use chrono::{DateTime, Utc};
use serde_derive::{Deserialize, Serialize};
use std::error::Error;
use std::io::{BufReader, Read};
use chrono;
use serde_json;
use serde_xml_rs;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum Intensity {
Active,
Resting,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum TriggerMethod {
Manual,
Distance,
Location,
Time,
HeartRate,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum CoursePointType {
Generic,
Summit,
Valley,
Water,
Food,
Danger,
Left,
Right,
Straight,
FirstAid,
FourthCategory,
ThirdCategory,
SecondCategory,
FirstCategory,
HorsCategory,
Sprint,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum BuildType {
Internal,
Alpha,
Beta,
Release,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum SpeedType {
Pace,
Speed,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
pub struct Version {
#[serde(rename = "VersionMajor")]
pub version_major: u16,
#[serde(rename = "VersionMinor")]
pub version_minor: u16,
#[serde(rename = "BuildMajor")]
pub build_major: Option<u16>,
#[serde(rename = "BuildMinor")]
pub build_minor: Option<u16>,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
pub struct AbstractSource {}
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
pub struct NameKeyReference {}
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
pub struct Courses {
#[serde(rename = "CourseFolder")]
pub folder: Option<CourseFolder>,
#[serde(rename = "Extensions")]
pub extensions: Option<Extensions>,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
pub struct CourseFolder {
#[serde(rename = "Folder")]
pub folder: Box<Option<CourseFolder>>,
#[serde(rename = "Notes")]
pub notes: Option<String>,
#[serde(rename = "CourseNameRef")]
pub course_name_ref: Option<NameKeyReference>,
#[serde(rename = "Extensions")]
pub extensions: Option<Extensions>,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
pub struct Course {
#[serde(rename = "CourseLap")]
pub lap: Option<CourseLap>,
#[serde(rename = "Track")]
pub tracks: Option<Vec<Track>>,
#[serde(rename = "Notes")]
pub notes: Option<String>,
#[serde(rename = "CoursePoint")]
pub course_point: Option<CoursePoint>,
#[serde(rename = "Creator")]
pub creator: Option<AbstractSource>,
#[serde(rename = "Extensions")]
pub extensions: Option<Extensions>,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
pub struct CourseLap {
#[serde(rename = "TotalTimeSeconds")]
pub total_time_seconds: f64,
#[serde(rename = "DistanceMeters")]
pub distance_meters: f64,
#[serde(rename = "BeginPosition")]
pub begin_position: Option<Position>,
#[serde(rename = "BeginAltitudeMeters")]
pub begin_altitude_meters: Option<f64>,
#[serde(rename = "EndPosition")]
pub end_position: Option<Position>,
#[serde(rename = "EndAltitudeMeters")]
pub end_altitude_meters: f64,
#[serde(rename = "AverageHeartRateBpm")]
pub average_heart_rate: Option<f64>,
#[serde(rename = "MaximumHeartRate")]
pub maximum_heart_rate: Option<f64>,
#[serde(rename = "Intensity")]
pub intensity: Option<Intensity>,
#[serde(rename = "Cadence")]
pub cadence: Option<u8>,
#[serde(rename = "Extensions")]
pub extensions: Option<Extensions>,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
pub struct CoursePointName {
pub token: u8,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct CoursePoint {
#[serde(rename = "Name")]
pub name: Option<CoursePointName>,
#[serde(rename = "Time")]
pub time: DateTime<Utc>,
#[serde(rename = "Position")]
pub position: Option<Position>,
#[serde(rename = "AltitudeMeters")]
pub altitude_meters: Option<f64>,
#[serde(rename = "PointType")]
pub point_type: Option<CoursePointType>,
#[serde(rename = "Notes")]
pub notes: Option<String>,
#[serde(rename = "Extensions")]
pub extensions: Option<Extensions>,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
pub struct HeartRate {
#[serde(rename = "Value")]
pub value: f64,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
pub struct Position {
#[serde(rename = "LatitudeDegrees")]
pub latitude: f64,
#[serde(rename = "LongitudeDegrees")]
pub longitude: f64,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Trackpoint {
#[serde(rename = "Time")]
pub time: DateTime<Utc>,
#[serde(rename = "Position")]
pub position: Option<Position>,
#[serde(rename = "AltitudeMeters")]
pub altitude_meters: Option<f64>,
#[serde(rename = "DistanceMeters")]
pub distance_meters: Option<f64>,
#[serde(rename = "HeartRateBpm")]
pub heart_rate: Option<HeartRate>,
#[serde(rename = "Cadence")]
pub cadence: Option<u8>,
#[serde(rename = "Extensions")]
pub extensions: Option<Extensions>,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
pub struct Track {
#[serde(rename = "Trackpoint")]
pub trackpoints: Vec<Trackpoint>,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
pub struct ActivityLap {
#[serde(rename = "TotalTimeSeconds")]
pub total_time_seconds: f64,
#[serde(rename = "DistanceMeters")]
pub distance_meters: f64,
#[serde(rename = "MaximumSpeed")]
pub maximum_speed: Option<f64>,
#[serde(rename = "Calories")]
pub calories: u16,
#[serde(rename = "AverageHeartRate")]
pub average_heart_rate: Option<f64>,
#[serde(rename = "MaximumHeartRate")]
pub maximum_heart_rate: Option<f64>,
#[serde(rename = "Intensity")]
pub intensity: Option<Intensity>,
#[serde(rename = "Cadence")]
pub cadence: Option<u8>,
#[serde(rename = "TriggerMethod")]
pub trigger_method: Option<TriggerMethod>,
#[serde(rename = "Track")]
pub tracks: Vec<Track>,
#[serde(rename = "Notes")]
pub notes: Option<String>,
#[serde(rename = "Extensions")]
pub extensions: Option<Extensions>,
}
impl ActivityLap {
pub fn calc_heartrates(&mut self) {
let mut max_hr = 0.0;
let mut avg_hr = 0.0;
let mut num_tp = 0;
for track in &self.tracks {
num_tp += track.trackpoints.len();
for point in &track.trackpoints {
if let Some(hr) = &point.heart_rate {
avg_hr += hr.value; if hr.value > max_hr {
max_hr = hr.value; }
} } }
if max_hr > 0.0 {
self.maximum_heart_rate = Some(max_hr);
}
if num_tp > 0 {
self.average_heart_rate = Some(avg_hr / num_tp as f64);
}
} }
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
pub struct Activity {
#[serde(rename = "Sport")]
pub sport: String,
#[serde(rename = "Id")]
pub id: String,
#[serde(rename = "Lap")]
pub laps: Vec<ActivityLap>,
#[serde(rename = "Notes")]
pub notes: Option<String>,
#[serde(rename = "Extensions")]
pub extensions: Option<Extensions>,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
pub struct Activities {
#[serde(rename = "Activity")]
pub activities: Vec<Activity>,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
pub struct History {}
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
pub struct Workouts {}
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
pub struct Ns3Tpx {
#[serde(rename = "Speed")]
pub speed: Option<f64>,
#[serde(rename = "Watts")]
pub watts: Option<u16>,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
pub struct Extensions {
#[serde(rename = "TPX")]
pub tpx: Option<Ns3Tpx>,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
pub struct Folders {
#[serde(rename = "History")]
pub history: Option<History>,
#[serde(rename = "Workouts")]
pub workouts: Option<Workouts>,
#[serde(rename = "Courses")]
pub courses: Option<Courses>,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
pub struct TrainingCenterDatabase {
#[serde(rename = "Activities")]
pub activities: Option<Activities>,
#[serde(rename = "Folders")]
pub folders: Option<Folders>,
#[serde(rename = "Courses")]
pub courses: Option<Courses>,
#[serde(rename = "Extensions")]
pub extensions: Option<Extensions>,
}
impl TrainingCenterDatabase {
pub fn from_file(filename: &str) -> Result<Self, serde_xml_rs::Error> {
let file = std::fs::File::open(filename).unwrap();
let mut reader = std::io::BufReader::new(file);
serde_xml_rs::from_reader(&mut reader)
}
pub fn calc_heartrates(&mut self) {
for act in 0..self.activities.as_ref().unwrap().activities.len() {
for lap in 0..self.activities.as_ref().unwrap().activities[act].laps.len() {
self.activities.as_mut().unwrap().activities[act].laps[lap].calc_heartrates();
}
}
}
pub fn export_json(&self, filename: &str) -> Result<(), Box<dyn Error>> {
serde_json::to_writer_pretty(
&std::fs::File::create(&std::path::PathBuf::from(filename))?,
&self,
)?;
Ok(())
}
}
pub fn read<R: Read>(
reader: &mut BufReader<R>,
) -> Result<TrainingCenterDatabase, serde_xml_rs::Error> {
serde_xml_rs::from_reader(reader)
}
pub fn read_file(filename: &str) -> Result<TrainingCenterDatabase, serde_xml_rs::Error> {
TrainingCenterDatabase::from_file(filename)
}