From 16c0e847452629c2af9fa1dac7a9d83ae3846c62 Mon Sep 17 00:00:00 2001 From: Kjetil Orbekk Date: Fri, 7 Feb 2020 22:12:31 -0500 Subject: Properly display running entries as a table --- src/db.rs | 14 ++++++-- src/server.rs | 13 ++----- src/template.rs | 99 +++++++++++++++++++++++++++++++++++++-------------- templates/profile.hbs | 22 ++++++------ 4 files changed, 98 insertions(+), 50 deletions(-) diff --git a/src/db.rs b/src/db.rs index 198ce03..5b165c9 100644 --- a/src/db.rs +++ b/src/db.rs @@ -241,16 +241,24 @@ pub fn get_raw_data_keys(conn: &PgConnection) -> Result, Ok(rows) } -pub fn get_entries(conn: &PgConnection, username: &str) -> Result, Error> { +pub fn get_entries( + conn: &PgConnection, + username: &str, + entry_type: &str, +) -> Result, Error> { use crate::schema::entries; let r = entries::table - .filter(entries::username.eq(username)) + .filter( + entries::username + .eq(username) + .and(entries::entry_type.eq(entry_type)), + ) .get_results::(conn)?; Ok(r) } pub fn get_template( - conn: &PgConnection, + _conn: &PgConnection, entry_type: &str, ) -> Result { match entry_type { diff --git a/src/server.rs b/src/server.rs index 3d264a9..62ca2d1 100644 --- a/src/server.rs +++ b/src/server.rs @@ -77,16 +77,9 @@ fn profile(conn: Db, username: String) -> Result { let mut context = default_context(); context.insert("title".to_string(), json!(username)); - let entries = db::get_entries(&*conn, &username)?; - let headings = entries - .first() - .and_then(|e| e.payload.as_object()) - .map(|e| e.keys().collect::>()); - context.insert("headings".to_string(), json!(headings)); - context.insert( - "entries".to_string(), - json!(entries.into_iter().map(|e| e.payload).collect::>()), - ); + let entries = db::get_entries(&*conn, &username, "run")?; + let template = db::get_template(&*conn, "run")?; + context.insert("document".to_string(), template.apply(entries)?); Ok(Template::render("profile", context)) } diff --git a/src/template.rs b/src/template.rs index d469394..2810600 100644 --- a/src/template.rs +++ b/src/template.rs @@ -1,8 +1,10 @@ use crate::error::Error; +use crate::models; use serde::Deserialize; use serde::Serialize; use serde_json::to_value; use serde_json::Value as Json; +use chrono::DateTime; pub fn running_template() -> TemplateSpec { TemplateSpec::Table(vec![ @@ -10,7 +12,7 @@ pub fn running_template() -> TemplateSpec { display_name: "Date".to_string(), field: FieldSpec::App( Function::DisplayUnit(Unit::Timestamp, "date".to_string()), - vec![FieldSpec::Field("start_time".to_string())], + vec![FieldSpec::Field("start_timestamp".to_string())], ), }, Column { @@ -92,28 +94,51 @@ mod function { .map_err(|_| Error::TemplateError("convert to f64".to_string(), d.clone())) } - fn format_pace(d: &Json) -> Result { - let pace = really_f64(d)?; - let seconds_per_mile = (pace * METERS_PER_MILE).round() as i64; + fn format_duration(d: &Json, _opts: &str) -> Result { + let seconds = really_f64(d)? as i64; let mut hours = "".to_string(); - if seconds_per_mile > 3600 { - hours = format!("{}", seconds_per_mile / 3600); + if seconds > 3600 { + hours = format!("{}:", seconds / 3600); } Ok(json!(format!( "{}{:02}:{:02}", hours, - seconds_per_mile / 60, - seconds_per_mile % 60 + (seconds % 3600) / 60, + seconds % 60 ))) } + fn format_pace(d: &Json) -> Result { + let pace = really_f64(d)?; + format_duration(&json!(pace * METERS_PER_MILE), "") + } + fn format_distance(d: &Json, opts: &str) -> Result { Ok(json!(match opts { "miles" => format!("{:.1}", really_f64(d)? / METERS_PER_MILE), - _ => format!("{} m", really_f64(d)?) + _ => format!("{} m", really_f64(d)?), })) } + fn format_timestamp(d: &Json, opts: &str) -> Result { + fn format_timestamp(t: DateTime, opts: &str) -> Result { + let s = match opts { + "date" => t.format("%Y/%m/%d"), + _ => t.format("%c"), + }; + Ok(json!(format!("{}", s))) + } + + let t = d.as_str(); + match t { + None => Ok(json!(())), + Some(t) => { + format_timestamp(DateTime::parse_from_rfc3339(t)?.with_timezone(&chrono::Utc), opts) + } + } + + } + fn display_unit(u: Unit, opts: &str, params: &Vec, d: &Json) -> Result { if params.len() != 1 { Err(Error::TemplateError( @@ -123,9 +148,9 @@ mod function { } let d = eval(¶ms[0], &d)?; Ok(match u { - Unit::Timestamp => json!(format!("as_timestamp({:?})", d)), + Unit::Timestamp => format_timestamp(&d, &opts)?, Unit::Meters => format_distance(&d, &opts)?, - Unit::Seconds => json!(format!("as_duration({:?})", d)), + Unit::Seconds => format_duration(&d, &opts)?, Unit::Speed => json!(format!("as_speed({:?})", d)), Unit::Pace => format_pace(&d)?, }) @@ -173,7 +198,7 @@ mod function { #[test] fn eval_fn1() { - let d = json!({"x": 0.260976}); + let d = json!({"x": 2}); let app1 = |f| { println!("{:?}", f); @@ -184,9 +209,13 @@ mod function { .unwrap() }; assert_eq!( - json!("07:00"), + json!("53:00"), app1(Function::DisplayUnit(Unit::Pace, "".to_string())) ); + assert_eq!( + json!("00:02"), + app1(Function::DisplayUnit(Unit::Seconds, "".to_string())) + ); } #[test] @@ -198,10 +227,16 @@ mod function { assert_eq!( eval( - &FieldSpec::App(Function::Div, - vec!(FieldSpec::Field("time".to_string()), - FieldSpec::Field("distance".to_string()))), - &d).unwrap(), + &FieldSpec::App( + Function::Div, + vec!( + FieldSpec::Field("time".to_string()), + FieldSpec::Field("distance".to_string()) + ) + ), + &d + ) + .unwrap(), json!((time as f64) / distance) ) } @@ -218,24 +253,34 @@ mod table { } pub fn apply(columns: &Vec, d: &Json) -> Result { - let rows = d.as_array() + let rows = d + .as_array() .ok_or_else(|| Error::TemplateError("expected array".to_string(), d.clone()))?; Ok(DisplayTable { headings: columns.iter().map(|c| c.display_name.clone()).collect(), - rows: rows.iter().map(|d| { - columns.iter().map(|c| { - function::eval(&c.field, d) - }).collect::, _>>() - }).collect::, _>>()?, + rows: rows + .iter() + .map(|d| { + columns + .iter() + .map(|c| function::eval(&c.field, d)) + .collect::, _>>() + }) + .collect::, _>>()?, }) } } -pub fn apply(t: &TemplateSpec, d: &Json) -> Result { - Ok(match t { - &TemplateSpec::Table(ref c) => to_value(table::apply(c, d)?)?, - }) +impl TemplateSpec { + pub fn apply(self: &TemplateSpec, entries: Vec) -> Result { + info!("Applying template\n {:#?}", self); + info!("Sample entry\n {:#?}", entries.first()); + let d = Json::Array(entries.into_iter().map(|e| e.payload).collect()); + Ok(match self { + &TemplateSpec::Table(ref c) => to_value(table::apply(c, &d)?)?, + }) + } } #[cfg(test)] diff --git a/templates/profile.hbs b/templates/profile.hbs index 240c443..c7e75e9 100644 --- a/templates/profile.hbs +++ b/templates/profile.hbs @@ -1,22 +1,24 @@ -{{#*inline "page"}} -{{#if user}} +{{#*inline "page"~}} +{{#if user~}}

Profile for {{ user }}

-{{/if}} +{{/if~}} +{{#with document ~}} - {{#each headings}} + {{#each headings ~}} - {{/each}} + {{/each ~}} - {{#each entries}} + {{#each rows ~}} - {{#each this}} + {{#each this ~}} - {{/each}} + {{/each ~}} - {{/each}} + {{/each ~}}
{{ this }}
{{ this }}
-{{/inline}} +{{/with~}} +{{/inline~}} {{~> (parent)~}} -- cgit v1.2.3