Calculate calories burned by running separately
This commit is contained in:
parent
a4578c004f
commit
17a773a5b5
26
Cargo.lock
generated
26
Cargo.lock
generated
@ -287,7 +287,7 @@ dependencies = [
|
|||||||
"bitflags 2.6.0",
|
"bitflags 2.6.0",
|
||||||
"cexpr",
|
"cexpr",
|
||||||
"clang-sys",
|
"clang-sys",
|
||||||
"itertools",
|
"itertools 0.12.1",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"lazycell",
|
"lazycell",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@ -484,6 +484,7 @@ dependencies = [
|
|||||||
"axum",
|
"axum",
|
||||||
"chrono",
|
"chrono",
|
||||||
"image 0.25.2",
|
"image 0.25.2",
|
||||||
|
"interp",
|
||||||
"mysql",
|
"mysql",
|
||||||
"mysql_common",
|
"mysql_common",
|
||||||
"plotters",
|
"plotters",
|
||||||
@ -1527,7 +1528,7 @@ dependencies = [
|
|||||||
"approx",
|
"approx",
|
||||||
"getrandom",
|
"getrandom",
|
||||||
"image 0.25.2",
|
"image 0.25.2",
|
||||||
"itertools",
|
"itertools 0.12.1",
|
||||||
"nalgebra",
|
"nalgebra",
|
||||||
"num",
|
"num",
|
||||||
"rand",
|
"rand",
|
||||||
@ -1551,6 +1552,16 @@ dependencies = [
|
|||||||
"hashbrown 0.14.5",
|
"hashbrown 0.14.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "interp"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bbf8add5dd9442596f3c5f504fd4fbbdf6266ff2337a51f6430dfeff6fd3a4de"
|
||||||
|
dependencies = [
|
||||||
|
"itertools 0.13.0",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "interpolate_name"
|
name = "interpolate_name"
|
||||||
version = "0.2.4"
|
version = "0.2.4"
|
||||||
@ -1580,6 +1591,15 @@ dependencies = [
|
|||||||
"either",
|
"either",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.11"
|
version = "1.0.11"
|
||||||
@ -2497,7 +2517,7 @@ dependencies = [
|
|||||||
"built",
|
"built",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"interpolate_name",
|
"interpolate_name",
|
||||||
"itertools",
|
"itertools 0.12.1",
|
||||||
"libc",
|
"libc",
|
||||||
"libfuzzer-sys",
|
"libfuzzer-sys",
|
||||||
"log",
|
"log",
|
||||||
|
@ -18,3 +18,4 @@ time = "0.3.36"
|
|||||||
tokio = { version = "1.39.2", features = ["macros", "rt-multi-thread"] }
|
tokio = { version = "1.39.2", features = ["macros", "rt-multi-thread"] }
|
||||||
tower-http = { version = "0.5.2", features = ["fs"] }
|
tower-http = { version = "0.5.2", features = ["fs"] }
|
||||||
shared = { path = "../shared", features = ["sql"] }
|
shared = { path = "../shared", features = ["sql"] }
|
||||||
|
interp = "2.0.1"
|
||||||
|
@ -19,9 +19,7 @@ use mysql::*;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
mod math;
|
mod math;
|
||||||
mod my_structs;
|
mod my_structs;
|
||||||
use my_structs::MyError;
|
use my_structs::{MyError, Purchase, Running, Weight};
|
||||||
use my_structs::Purchase;
|
|
||||||
use my_structs::Weight;
|
|
||||||
mod plotting;
|
mod plotting;
|
||||||
// use crate::plotting::Plotter;
|
// use crate::plotting::Plotter;
|
||||||
use std::{thread, time};
|
use std::{thread, time};
|
||||||
@ -147,12 +145,14 @@ async fn svg_weight_loss(State(state): State<Arc<AppState>>) -> Result<impl Into
|
|||||||
let nutrition_val_promise = Nutrition::get_nutrition_hashmap(state.sql_pool.clone());
|
let nutrition_val_promise = Nutrition::get_nutrition_hashmap(state.sql_pool.clone());
|
||||||
let purchases_val_promise = Purchase::get_purchase_rows(state.sql_pool.clone());
|
let purchases_val_promise = Purchase::get_purchase_rows(state.sql_pool.clone());
|
||||||
let weight_val_promise = Weight::get_weight_rows(state.sql_pool.clone());
|
let weight_val_promise = Weight::get_weight_rows(state.sql_pool.clone());
|
||||||
|
let running_val_promise = Running::get_running_rows(state.sql_pool.clone());
|
||||||
|
|
||||||
let nutrition_val = nutrition_val_promise.await?;
|
let nutrition_val = nutrition_val_promise.await?;
|
||||||
let purchases_val = purchases_val_promise.await?;
|
let purchases_val = purchases_val_promise.await?;
|
||||||
let weight_val = weight_val_promise.await?;
|
let weight_val = weight_val_promise.await?;
|
||||||
|
let running_val = running_val_promise.await?;
|
||||||
|
|
||||||
let plotfun = math::plot_weight_loss(nutrition_val, purchases_val, weight_val);
|
let plotfun = math::plot_weight_loss(nutrition_val, purchases_val, weight_val, running_val);
|
||||||
Ok(plotting::SVGPlotter::plot(640, 480, plotfun))
|
Ok(plotting::SVGPlotter::plot(640, 480, plotfun))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ use crate::my_structs;
|
|||||||
use crate::plotting;
|
use crate::plotting;
|
||||||
// use chrono::TimeZone;
|
// use chrono::TimeZone;
|
||||||
use chrono::{DateTime, NaiveDate, Utc};
|
use chrono::{DateTime, NaiveDate, Utc};
|
||||||
|
use interp::{interp_slice, InterpMode};
|
||||||
use plotters::prelude::*;
|
use plotters::prelude::*;
|
||||||
use shared::structs::Nutrition;
|
use shared::structs::Nutrition;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@ -158,13 +159,42 @@ pub fn plot_weight_loss(
|
|||||||
nutrition_map: HashMap<u32, Nutrition>,
|
nutrition_map: HashMap<u32, Nutrition>,
|
||||||
purchases: Vec<my_structs::Purchase>,
|
purchases: Vec<my_structs::Purchase>,
|
||||||
weight: Vec<my_structs::Weight>,
|
weight: Vec<my_structs::Weight>,
|
||||||
|
running: Vec<my_structs::Running>,
|
||||||
) -> plotting::SVGPlotFun {
|
) -> plotting::SVGPlotFun {
|
||||||
let (mindate, calorie_days) = calculate_calories_per_day(nutrition_map, purchases);
|
let (mindate, calorie_days) = calculate_calories_per_day(nutrition_map, purchases);
|
||||||
let calories_burned_per_day: f32 = 2200.;
|
let calories_burned_per_day: f32 = 2140.;
|
||||||
|
|
||||||
|
// Based on the Caloie Burned by Distance Calculator
|
||||||
|
// https://www.calculator.net/calories-burned-calculator.html
|
||||||
|
// if running between 5 minutes per km, and 7 minutes per km,
|
||||||
|
// we get the approximate formula: kcal = (0.94*kg + 10.01)*km
|
||||||
|
let weight_dates: Vec<f32> = weight
|
||||||
|
.iter()
|
||||||
|
.map(|w| (w.datetime.date() - mindate).whole_days() as f32)
|
||||||
|
.collect();
|
||||||
|
let weight_values: Vec<f32> = weight.iter().map(|w| w.weight).collect();
|
||||||
|
let weight_days = interp_slice(
|
||||||
|
&weight_dates,
|
||||||
|
&weight_values,
|
||||||
|
&(0..calorie_days.len())
|
||||||
|
.map(|x| x as f32)
|
||||||
|
.collect::<Vec<f32>>(),
|
||||||
|
&InterpMode::FirstLast,
|
||||||
|
);
|
||||||
|
let mut running_days: Vec<f32> = vec![0.; calorie_days.len()];
|
||||||
|
running.into_iter().for_each(|r| {
|
||||||
|
let idx: usize = (r.datetime.date() - mindate)
|
||||||
|
.whole_days()
|
||||||
|
.try_into()
|
||||||
|
.unwrap();
|
||||||
|
running_days[idx] += (0.94 * weight_days[idx] + 10.01) * r.distance;
|
||||||
|
});
|
||||||
|
|
||||||
let deficit_days: Vec<f32> = calorie_days
|
let deficit_days: Vec<f32> = calorie_days
|
||||||
.iter()
|
.iter()
|
||||||
.scan(0., |deficit, &calories| {
|
.enumerate()
|
||||||
*deficit += calories - calories_burned_per_day;
|
.scan(0., |deficit, (i, &calories)| {
|
||||||
|
*deficit += calories - calories_burned_per_day - running_days[i];
|
||||||
Some(*deficit)
|
Some(*deficit)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
@ -230,7 +260,9 @@ pub fn plot_weight_loss(
|
|||||||
mindate_chrono
|
mindate_chrono
|
||||||
.checked_add_days(chrono::Days::new(i.try_into().unwrap()))
|
.checked_add_days(chrono::Days::new(i.try_into().unwrap()))
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
*cal / 7000. + weight[0].weight + 1.,
|
*cal / 7000. + (weight[0].weight + 1.), // the non-calories values is an
|
||||||
|
// offset to make the two plotted
|
||||||
|
// lines fit
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
RED.stroke_width(3),
|
RED.stroke_width(3),
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
pub use self::myerror::MyError;
|
pub use self::myerror::MyError;
|
||||||
pub use self::purchases::Purchase;
|
pub use self::purchases::Purchase;
|
||||||
|
pub use self::running::Running;
|
||||||
pub use self::weight::Weight;
|
pub use self::weight::Weight;
|
||||||
mod myerror;
|
mod myerror;
|
||||||
mod purchases;
|
mod purchases;
|
||||||
|
mod running;
|
||||||
mod weight;
|
mod weight;
|
||||||
|
56
server/src/my_structs/running.rs
Normal file
56
server/src/my_structs/running.rs
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
use mysql::prelude::*;
|
||||||
|
use mysql::*;
|
||||||
|
use mysql_common::frunk::{hlist_pat, HList};
|
||||||
|
use struct_field_names_as_array::FieldNamesAsArray;
|
||||||
|
use time::{PrimitiveDateTime, Time};
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[derive(Debug, PartialEq, FieldNamesAsArray)]
|
||||||
|
pub struct Running {
|
||||||
|
id: u32,
|
||||||
|
pub distance: f32,
|
||||||
|
pub time: Time,
|
||||||
|
pub datetime: PrimitiveDateTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
type RowType = HList!(u32, f32, Time, PrimitiveDateTime);
|
||||||
|
|
||||||
|
impl Running {
|
||||||
|
fn get_sql_fields() -> String {
|
||||||
|
Running::FIELD_NAMES_AS_ARRAY
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.intersperse(", ")
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn query_map_helper() -> (String, impl Fn(RowType) -> Running) {
|
||||||
|
let sql_query = format!("SELECT {} from running", Self::get_sql_fields());
|
||||||
|
println!("{}", sql_query);
|
||||||
|
|
||||||
|
let construction_closure = |row: RowType| {
|
||||||
|
let hlist_pat![id, distance, time, datetime] = row;
|
||||||
|
Running {
|
||||||
|
id,
|
||||||
|
distance,
|
||||||
|
time,
|
||||||
|
datetime,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
(sql_query, construction_closure)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_running_rows(sql_pool: Pool) -> Result<Vec<Running>> {
|
||||||
|
let mut conn = sql_pool
|
||||||
|
.get_conn()
|
||||||
|
.expect("Cannot establish database connection");
|
||||||
|
|
||||||
|
let (running_query, running_closure) = Self::query_map_helper();
|
||||||
|
let running_val: Vec<Running> = conn
|
||||||
|
.query_map(running_query, running_closure)
|
||||||
|
.expect("Data in database doesn't match the Running class");
|
||||||
|
|
||||||
|
Ok(running_val)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user