diff --git a/fitnesstrax/core/src/bin/legacy-importer.rs b/fitnesstrax/core/src/bin/legacy-importer.rs index 1f29c99..77b6423 100644 --- a/fitnesstrax/core/src/bin/legacy-importer.rs +++ b/fitnesstrax/core/src/bin/legacy-importer.rs @@ -14,19 +14,21 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with FitnessTrax. If not, see . */ -use std::{ - fs::File, - io::{BufRead, BufReader, Read}, - fmt, - str::FromStr, -}; use chrono::SecondsFormat; use chrono_tz::Etc::UTC; -use emseries::{Record, Series, Timestamp, RecordId}; -use ft_core::TraxRecord; -use serde::{Deserialize, Serialize, Deserializer, Serializer, de::{self, Visitor}}; - - +use dimensioned::si; +use emseries::{Record, RecordId, Series, Timestamp}; +use ft_core::{self, DurationWorkout, DurationWorkoutActivity, SetRepActivity, TraxRecord}; +use serde::{ + de::{self, Visitor}, + Deserialize, Deserializer, Serialize, Serializer, +}; +use std::{ + fmt, + fs::File, + io::{BufRead, BufReader, Read}, + str::FromStr, +}; /// This is a wrapper around date time objects, using timezones from the chroon-tz database and /// providing string representation and parsing of the form " ", i.e., @@ -116,11 +118,19 @@ impl<'de> Deserialize<'de> for DateTimeTz { } } - #[derive(Clone, Debug, Serialize, Deserialize)] struct Steps { date: DateTimeTz, - steps: u64, + steps: u32, +} + +impl From for ft_core::Steps { + fn from(s: Steps) -> Self { + Self { + date: s.date.0.naive_utc().date(), + count: s.steps, + } + } } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -129,13 +139,34 @@ struct Weight { weight: f64, } +impl From for ft_core::Weight { + fn from(w: Weight) -> Self { + Self { + date: w.date.0.naive_utc().date(), + weight: w.weight * si::KG, + } + } +} + #[derive(Clone, Debug, Serialize, Deserialize)] enum TDActivity { Cycling, - Running, - Walking, - Swimming, Rowing, + Running, + Swimming, + Walking, +} + +impl From for ft_core::TimeDistanceActivity { + fn from(activity: TDActivity) -> Self { + match activity { + TDActivity::Cycling => ft_core::TimeDistanceActivity::Biking, + TDActivity::Rowing => ft_core::TimeDistanceActivity::Rowing, + TDActivity::Running => ft_core::TimeDistanceActivity::Running, + TDActivity::Swimming => ft_core::TimeDistanceActivity::Swimming, + TDActivity::Walking => ft_core::TimeDistanceActivity::Walking, + } + } } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -147,25 +178,63 @@ struct TimeDistance { duration: Option, } +impl From for ft_core::TimeDistance { + fn from(td: TimeDistance) -> Self { + Self { + datetime: td.date.0.fixed_offset(), + activity: td.activity.into(), + comments: td.comments, + distance: td.distance.map(|d| d * si::M), + duration: td.duration.map(|d| d * si::S), + } + } +} + #[derive(Clone, Debug, Serialize, Deserialize)] enum SRActivity { Pushups, Situps, } +impl From for SetRepActivity { + fn from(activity: SRActivity) -> Self { + match activity { + SRActivity::Pushups => Self::Pushups, + SRActivity::Situps => Self::Situps, + } + } +} + #[derive(Clone, Debug, Serialize, Deserialize)] struct SetRep { date: DateTimeTz, activity: SRActivity, - sets: Vec, + sets: Vec, comments: Option, } +impl From for ft_core::SetRep { + fn from(sr: SetRep) -> Self { + Self { + date: sr.date.0.naive_utc().date(), + activity: sr.activity.into(), + sets: sr.sets, + comments: sr.comments, + } + } +} + #[derive(Clone, Debug, Serialize, Deserialize)] enum RDActivity { MartialArts, } +impl From for DurationWorkoutActivity { + fn from(_: RDActivity) -> Self { + Self::MartialArts + } +} + #[derive(Clone, Debug, Serialize, Deserialize)] struct RepDuration { date: DateTimeTz, @@ -173,13 +242,24 @@ struct RepDuration { sets: Vec, } +impl From for DurationWorkout { + fn from(rd: RepDuration) -> Self { + Self { + datetime: rd.date.0.fixed_offset(), + activity: rd.activity.into(), + duration: rd.sets.into_iter().map(|d| d * si::S).next(), + comments: None, + } + } +} + #[derive(Clone, Debug, Serialize, Deserialize)] enum LegacyData { - Steps(Steps), - Weight(Weight), - TimeDistance(TimeDistance), - SetRep(SetRep), RepDuration(RepDuration), + SetRep(SetRep), + Steps(Steps), + TimeDistance(TimeDistance), + Weight(Weight), } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -188,11 +268,39 @@ struct LegacyRecord { data: LegacyData, } +impl From for Record { + fn from(record: LegacyRecord) -> Self { + match record.data { + LegacyData::RepDuration(rd) => Record { + id: record.id, + data: TraxRecord::DurationWorkout(rd.into()), + }, + LegacyData::SetRep(sr) => Record { + id: record.id, + data: TraxRecord::SetRep(sr.into()), + }, + LegacyData::Steps(s) => Record { + id: record.id, + data: TraxRecord::Steps(s.into()), + }, + LegacyData::TimeDistance(td) => Record { + id: record.id, + data: TraxRecord::TimeDistance(td.into()), + }, + LegacyData::Weight(weight) => Record { + id: record.id, + data: TraxRecord::Weight(weight.into()), + }, + } + } +} + fn main() { let mut args = std::env::args(); let _ = args.next().unwrap(); let input_filename = args.next().unwrap(); println!("input filename: {}", input_filename); + // let output: Series = Series::open_file("import.fitnesstrax").unwrap(); let input_file = File::open(input_filename).unwrap(); let mut buf_reader = BufReader::new(input_file); @@ -211,7 +319,8 @@ fn main() { Ok(0) => std::process::exit(0), Ok(_) => { let record = serde_json::from_str::(&line).unwrap(); - println!("[{}] {:?}", count, record); + let record: Record = record.into(); + println!("{}", serde_json::to_string(&record).unwrap()); count += 1; } } diff --git a/fitnesstrax/core/src/lib.rs b/fitnesstrax/core/src/lib.rs index f579628..15f4871 100644 --- a/fitnesstrax/core/src/lib.rs +++ b/fitnesstrax/core/src/lib.rs @@ -2,5 +2,7 @@ mod legacy; mod types; pub use types::{ - Steps, TimeDistance, TimeDistanceActivity, TraxRecord, Weight, TIME_DISTANCE_ACTIVITIES, + SetRep, SetRepActivity, Steps, TimeDistance, TimeDistanceActivity, TraxRecord, Weight, + DurationWorkoutActivity, DurationWorkout, + SET_REP_ACTIVITIES, TIME_DISTANCE_ACTIVITIES, }; diff --git a/fitnesstrax/core/src/types.rs b/fitnesstrax/core/src/types.rs index d2ef95c..97a9d69 100644 --- a/fitnesstrax/core/src/types.rs +++ b/fitnesstrax/core/src/types.rs @@ -19,17 +19,38 @@ use dimensioned::si; use emseries::{Recordable, Timestamp}; use serde::{Deserialize, Serialize}; +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub enum SetRepActivity { + Pushups, + Situps, +} + +pub const SET_REP_ACTIVITIES: [SetRepActivity; 2] = + [SetRepActivity::Pushups, SetRepActivity::Situps]; + /// SetRep represents workouts like pushups or situps, which involve doing a "set" of a number of /// actions, resting, and then doing another set. -#[allow(dead_code)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct SetRep { /// I assume that a set/rep workout is only done once in a day. - date: NaiveDate, + pub date: NaiveDate, + /// The activity involved + pub activity: SetRepActivity, /// Each set entry represents the number of times that the action was performed in a set. So, a /// pushup workout that involved five sets would have five entries. Each entry would be x /// number of pushups. A viable workout would be something like [6, 6, 4, 4, 5]. - sets: Vec, - comments: Option, + pub sets: Vec, + pub comments: Option, +} + +impl Recordable for SetRep { + fn timestamp(&self) -> Timestamp { + Timestamp::Date(self.date) + } + + fn tags(&self) -> Vec { + vec![] + } } /// The number of steps one takes in a single day. @@ -120,11 +141,45 @@ impl Recordable for Weight { } } +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub enum DurationWorkoutActivity { + MartialArts, + Yoga, +} + +pub const DURATION_WORKOUT_ACTIVITIES: [DurationWorkoutActivity; 2] = [ + DurationWorkoutActivity::MartialArts, + DurationWorkoutActivity::Yoga, +]; + +/// Generic workouts for which only duration really matters. This is for things +/// such as Martial Arts or Yoga, which are activities done for an amount of +/// time, but with no other details. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct DurationWorkout { + pub datetime: DateTime, + pub activity: DurationWorkoutActivity, + pub duration: Option>, + pub comments: Option, +} + +impl Recordable for DurationWorkout { + fn timestamp(&self) -> Timestamp { + Timestamp::DateTime(self.datetime) + } + + fn tags(&self) -> Vec { + vec![] + } +} + /// The unified data structure for all records that are part of the app. #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub enum TraxRecord { - TimeDistance(TimeDistance), + DurationWorkout(DurationWorkout), + SetRep(SetRep), Steps(Steps), + TimeDistance(TimeDistance), Weight(Weight), } @@ -164,8 +219,10 @@ impl Recordable for TraxRecord { fn timestamp(&self) -> Timestamp { match self { TraxRecord::TimeDistance(rec) => Timestamp::DateTime(rec.datetime), + TraxRecord::SetRep(rec) => rec.timestamp(), TraxRecord::Steps(rec) => rec.timestamp(), TraxRecord::Weight(rec) => rec.timestamp(), + TraxRecord::DurationWorkout(rec) => rec.timestamp(), } }