From 7a6e902fdd6b428a29fea624777d307c5eaf2916 Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Wed, 27 Dec 2023 21:49:44 -0500 Subject: [PATCH] Create placeholders in the historical view for days that are unpopulated. --- fitnesstrax/app/src/components/day.rs | 2 + fitnesstrax/app/src/views/historical_view.rs | 132 +++++++++++++++---- 2 files changed, 112 insertions(+), 22 deletions(-) diff --git a/fitnesstrax/app/src/components/day.rs b/fitnesstrax/app/src/components/day.rs index 690d9d3..66364cb 100644 --- a/fitnesstrax/app/src/components/day.rs +++ b/fitnesstrax/app/src/components/day.rs @@ -90,12 +90,14 @@ impl DaySummary { *self.imp().weight.borrow_mut() = Some(label); } + /* self.append( >k::Label::builder() .halign(gtk::Align::Start) .label("15km of biking in 60 minutes") .build(), ); + */ } } diff --git a/fitnesstrax/app/src/views/historical_view.rs b/fitnesstrax/app/src/views/historical_view.rs index 962fef2..4111127 100644 --- a/fitnesstrax/app/src/views/historical_view.rs +++ b/fitnesstrax/app/src/views/historical_view.rs @@ -15,7 +15,8 @@ You should have received a copy of the GNU General Public License along with Fit */ use crate::components::DaySummary; -use emseries::{Record, Recordable, Timestamp}; +use chrono::{Duration, Local, NaiveDate}; +use emseries::Record; use ft_core::TraxRecord; use glib::Object; use gtk::{prelude::*, subclass::prelude::*}; @@ -24,7 +25,9 @@ use std::{cell::RefCell, collections::HashMap, rc::Rc}; /// The historical view will show a window into the main database. It will show some version of /// daily summaries, daily details, and will provide all functions the user may need for editing /// records. -pub struct HistoricalViewPrivate {} +pub struct HistoricalViewPrivate { + time_window: RefCell, +} #[glib::object_subclass] impl ObjectSubclass for HistoricalViewPrivate { @@ -33,7 +36,9 @@ impl ObjectSubclass for HistoricalViewPrivate { type ParentType = gtk::Box; fn new() -> Self { - Self {} + Self { + time_window: RefCell::new(DayInterval::default()), + } } } @@ -54,10 +59,11 @@ impl HistoricalView { s.set_orientation(gtk::Orientation::Vertical); s.set_css_classes(&["historical"]); - let day_records: GroupedRecords = GroupedRecords::from(records); + let grouped_records = + GroupedRecords::new((*s.imp().time_window.borrow()).clone()).with_data(records); - let model = gio::ListStore::new::(); - model.extend_from_slice(&day_records.0); + let mut model = gio::ListStore::new::(); + model.extend(grouped_records.items()); let factory = gtk::SignalListItemFactory::new(); factory.connect_setup(move |_, list_item| { @@ -149,23 +155,105 @@ impl DayRecords { } } -struct GroupedRecords(Vec); +struct GroupedRecords { + interval: DayInterval, + data: HashMap, +} -impl From>> for GroupedRecords { - fn from(records: Vec>) -> GroupedRecords { - GroupedRecords( - records - .into_iter() - .fold(HashMap::new(), |mut acc, rec| { - acc.entry(rec.date()) - .and_modify(|entry: &mut DayRecords| (*entry).add_record(rec.clone())) - .or_insert(DayRecords::new(rec.date(), vec![rec])); - acc - }) - .values() - .cloned() - .collect::>(), - ) +impl GroupedRecords { + fn new(interval: DayInterval) -> Self { + let mut s = Self { + interval: interval.clone(), + data: HashMap::new(), + }; + interval.days().for_each(|date| { + let _ = s.data.insert(date, DayRecords::new(date, vec![])); + }); + s + } + + fn with_data(mut self, records: Vec>) -> Self { + records.into_iter().for_each(|record| { + self.data + .entry(record.date()) + .and_modify(|entry: &mut DayRecords| (*entry).add_record(record.clone())) + .or_insert(DayRecords::new(record.date(), vec![record])); + }); + + self + } + + fn items<'a>(&'a self) -> impl Iterator + 'a { + /* + GroupedRecordIterator { + interval: self.interval.clone(), + data: &self.data, + } + */ + self.interval.days().map(|date| { + self.data + .get(&date) + .map(|rec| rec.clone()) + .unwrap_or(DayRecords::new(date, vec![])) + }) + } +} + +/* +struct GroupedRecordIterator<'a> { + interval: DayInterval, + data: &'a HashMap, +} + +impl <'a> Iterator for GroupedRecordIterator<'a> { + type Item = &'a DayRecords; + + fn next(&mut self) -> Option<&'a DayRecords> { + + } +} +*/ + +#[derive(Clone, Debug, PartialEq, Eq)] +struct DayInterval { + start: NaiveDate, + end: NaiveDate, +} + +impl Default for DayInterval { + fn default() -> Self { + Self { + start: (Local::now() - Duration::days(7)).date_naive(), + end: Local::now().date_naive(), + } + } +} + +impl DayInterval { + fn days(&self) -> impl Iterator { + DayIterator { + current: self.start.clone(), + end: self.end.clone(), + } + } +} + +struct DayIterator { + current: NaiveDate, + end: NaiveDate, +} + +impl Iterator for DayIterator { + type Item = NaiveDate; + + fn next(&mut self) -> Option { + if self.current <= self.end { + let val = self.current.clone(); + self.current += Duration::days(1); + Some(val) + } else { + None + } } }