From 279810f7d704a65789a99513e0d8b9b8ed38cbd8 Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Sun, 21 Jan 2024 10:50:18 -0500 Subject: [PATCH] Show a summary of the day's biking stats when there is one --- fitnesstrax/app/src/components/day.rs | 10 ++- fitnesstrax/app/src/components/mod.rs | 2 +- .../app/src/components/time_distance.rs | 22 ++++++ fitnesstrax/app/src/view_models/day_detail.rs | 74 ++++++++++++++++++- fitnesstrax/core/src/types.rs | 8 ++ 5 files changed, 112 insertions(+), 4 deletions(-) diff --git a/fitnesstrax/app/src/components/day.rs b/fitnesstrax/app/src/components/day.rs index 15eb9ce..ec9c929 100644 --- a/fitnesstrax/app/src/components/day.rs +++ b/fitnesstrax/app/src/components/day.rs @@ -17,9 +17,13 @@ You should have received a copy of the GNU General Public License along with Fit // use chrono::NaiveDate; // use ft_core::TraxRecord; use crate::{ - components::{steps_editor, weight_field, ActionGroup, Steps, WeightLabel}, + components::{ + steps_editor, time_distance_summary, weight_field, ActionGroup, Steps, WeightLabel, + }, view_models::DayDetailViewModel, }; +use dimensioned::si; +use ft_core::{RecordType, TraxRecord}; use glib::Object; use gtk::{prelude::*, subclass::prelude::*}; use std::cell::RefCell; @@ -96,8 +100,10 @@ impl DaySummary { label.set_label(&format!("{} steps", s)); } row.append(&label); - self.append(&row); + + let biking_summary = view_model.biking_summary(); + time_distance_summary(biking_summary.0, biking_summary.1).map(|label| self.append(&label)); } } diff --git a/fitnesstrax/app/src/components/mod.rs b/fitnesstrax/app/src/components/mod.rs index b6c0fe3..cdaa1b6 100644 --- a/fitnesstrax/app/src/components/mod.rs +++ b/fitnesstrax/app/src/components/mod.rs @@ -30,7 +30,7 @@ mod text_entry; pub use text_entry::{weight_field, TextEntry}; mod time_distance; -pub use time_distance::TimeDistanceView; +pub use time_distance::{time_distance_summary, TimeDistanceView}; mod weight; pub use weight::WeightLabel; diff --git a/fitnesstrax/app/src/components/time_distance.rs b/fitnesstrax/app/src/components/time_distance.rs index ac1ad9c..53c0881 100644 --- a/fitnesstrax/app/src/components/time_distance.rs +++ b/fitnesstrax/app/src/components/time_distance.rs @@ -17,11 +17,33 @@ You should have received a copy of the GNU General Public License along with Fit // use crate::components::{EditView, ParseError, TextEntry}; // use chrono::{Local, NaiveDate}; // use dimensioned::si; +use dimensioned::si; use ft_core::{RecordType, TimeDistance}; use glib::Object; use gtk::{prelude::*, subclass::prelude::*}; use std::cell::RefCell; +pub fn time_distance_summary( + distance: si::Meter, + duration: si::Second, +) -> Option { + let text = match (distance > si::M, duration > si::S) { + (true, true) => Some(format!( + "{} kilometers of biking in {} minutes", + distance.value_unsafe / 1000., + duration.value_unsafe / 60. + )), + (true, false) => Some(format!( + "{} kilometers of biking", + distance.value_unsafe / 1000. + )), + (false, true) => Some(format!("{} seconds of biking", duration.value_unsafe / 60.)), + (false, false) => None, + }; + + text.map(|text| gtk::Label::new(Some(&text))) +} + #[derive(Default)] pub struct TimeDistanceViewPrivate { #[allow(unused)] diff --git a/fitnesstrax/app/src/view_models/day_detail.rs b/fitnesstrax/app/src/view_models/day_detail.rs index cf8ac0e..f0d359d 100644 --- a/fitnesstrax/app/src/view_models/day_detail.rs +++ b/fitnesstrax/app/src/view_models/day_detail.rs @@ -15,8 +15,9 @@ You should have received a copy of the GNU General Public License along with Fit */ use crate::{app::App, types::WeightFormatter}; +use dimensioned::si; use emseries::{Record, RecordId, Recordable}; -use ft_core::TraxRecord; +use ft_core::{TimeDistance, TraxRecord}; use std::{ collections::HashMap, ops::Deref, @@ -43,6 +44,15 @@ impl RecordState { } } + fn data(&self) -> Option<&Record> { + match self { + RecordState::Original(ref r) => Some(&r), + RecordState::New(ref r) => None, + RecordState::Updated(ref r) => Some(&r), + RecordState::Deleted(ref r) => Some(&r), + } + } + fn with_value(self, value: T) -> RecordState { match self { RecordState::Original(r) => RecordState::Updated(Record { data: value, ..r }), @@ -164,6 +174,40 @@ impl DayDetailViewModel { *record = Some(new_record); } + pub fn biking_summary(&self) -> (si::Meter, si::Second) { + self.records.read().unwrap().iter().fold( + (0. * si::M, 0. * si::S), + |(acc_distance, acc_duration), (_, record)| match record.data() { + Some(Record { + data: + TraxRecord::BikeRide(TimeDistance { + distance, duration, .. + }), + .. + }) => ( + distance + .map(|distance| acc_distance + distance) + .unwrap_or(acc_distance), + (duration + .map(|duration| acc_duration + duration) + .unwrap_or(acc_duration)), + ), + + _ => (acc_distance, acc_duration), + }, + ) + } + + pub fn records(&self) -> Vec> { + let read_lock = self.records.read().unwrap(); + read_lock + .iter() + .map(|(_, record_state)| record_state.data()) + .filter_map(|r| r) + .cloned() + .collect::>>() + } + pub fn save(&self) { glib::spawn_future({ let s = self.clone(); @@ -234,3 +278,31 @@ impl DayDetailViewModel { unimplemented!(); } } + +/* +struct SavedRecordIterator<'a> { + read_lock: RwLockReadGuard<'a, HashMap>>, + iter: Box> + 'a>, +} + +impl<'a> SavedRecordIterator<'a> { + fn new(records: Arc>>>) -> Self { + let read_lock = records.read().unwrap(); + let iter = read_lock + .iter() + .map(|(_, record_state)| record_state.data()) + .filter_map(|r| r); + Self { + read_lock, + iter: Box::new(iter), + } + } +} + +impl<'a> Iterator for SavedRecordIterator<'a> { + type Item = &'a Record; + fn next(&mut self) -> Option { + None + } +} +*/ diff --git a/fitnesstrax/core/src/types.rs b/fitnesstrax/core/src/types.rs index b0959f7..e61959e 100644 --- a/fitnesstrax/core/src/types.rs +++ b/fitnesstrax/core/src/types.rs @@ -118,6 +118,14 @@ impl TraxRecord { pub fn is_steps(&self) -> bool { matches!(self, TraxRecord::Steps(_)) } + + pub fn is_bike_ride(&self) -> bool { + matches!(self, TraxRecord::BikeRide(_)) + } + + pub fn is_run(&self) -> bool { + matches!(self, TraxRecord::Run(_)) + } } impl Recordable for TraxRecord {