Skip to content

Commit 5d865ad

Browse files
Copilotgfauredev
andauthored
refactor(analytics): address code review - extract log_matches closure, simplify maps, add axis/mode docs
Agent-Logs-Url: https://github.com/gfauredev/LogOut/sessions/07d5a767-7b0e-4202-acdb-85424116769d Co-authored-by: gfauredev <19304085+gfauredev@users.noreply.github.com>
1 parent dcd5c81 commit 5d865ad

3 files changed

Lines changed: 35 additions & 47 deletions

File tree

src/components/analytics/chart.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,8 @@ pub fn ChartView(data: SeriesData, colors: Vec<&'static str>) -> Element {
244244
for i in 0..5_usize {
245245
if let Some((unit, _, min_y, max_y)) = axis_data[i] {
246246
{
247+
// Axis side: odd indices (Reps=1, Duration=3) → right axis;
248+
// even indices (Weight=0, Distance=2, Volume=4) → left axis.
247249
let is_right = i % 2 == 1;
248250
let x_pos = if is_right { left_pad + chart_width } else { left_pad };
249251
let (ct, cb) = if i < 2 {

src/components/analytics/mod.rs

Lines changed: 29 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -76,18 +76,17 @@ pub fn Analytics() -> Element {
7676
let is_weighted = log.weight_hg.0 > 0;
7777
if is_weighted {
7878
maps[0].insert(log.exercise_id.clone(), name.clone());
79-
}
80-
if log.reps.is_some() && !is_weighted {
81-
maps[1].insert(log.exercise_id.clone(), name.clone());
82-
}
83-
if log.distance_m.is_some() && !is_weighted {
84-
maps[2].insert(log.exercise_id.clone(), name.clone());
85-
}
86-
if !is_weighted {
79+
if log.reps.is_some() {
80+
maps[4].insert(log.exercise_id.clone(), name.clone());
81+
}
82+
} else {
8783
maps[3].insert(log.exercise_id.clone(), name.clone());
88-
}
89-
if is_weighted && log.reps.is_some() {
90-
maps[4].insert(log.exercise_id.clone(), name.clone());
84+
if log.reps.is_some() {
85+
maps[1].insert(log.exercise_id.clone(), name.clone());
86+
}
87+
if log.distance_m.is_some() {
88+
maps[2].insert(log.exercise_id.clone(), name.clone());
89+
}
9190
}
9291
}
9392
}
@@ -109,24 +108,27 @@ pub fn Analytics() -> Element {
109108
.filter_map(|(i, (metric, opt_id))| opt_id.as_ref().map(|id| (i, *metric, id.clone())))
110109
.map(|(i, metric, exercise_id)| {
111110
let mut points = Vec::new();
111+
// Returns true if a log entry should contribute to this metric's series.
112+
// Weight: weighted sets only. Reps/Distance/Duration: non-weighted sets.
113+
let log_matches = |log: &crate::models::ExerciseLog| -> bool {
114+
if log.exercise_id != exercise_id {
115+
return false;
116+
}
117+
let w = log.weight_hg.0 > 0;
118+
match metric {
119+
Metric::Weight => w,
120+
Metric::Reps | Metric::Distance | Metric::Duration => !w,
121+
Metric::Volume => false,
122+
}
123+
};
112124
match mode {
113125
AnalyticsMode::Set => {
114126
for session in &sessions {
115127
for log in &session.exercise_logs {
116-
if log.exercise_id == exercise_id {
117-
let is_weighted = log.weight_hg.0 > 0;
118-
let include = match metric {
119-
Metric::Weight => is_weighted,
120-
Metric::Reps | Metric::Distance | Metric::Duration => {
121-
!is_weighted
122-
}
123-
Metric::Volume => false,
124-
};
125-
if include {
126-
if let Some(value) = metric.extract_value(log) {
127-
#[allow(clippy::cast_precision_loss)]
128-
points.push((log.start_time as f64, value));
129-
}
128+
if log_matches(log) {
129+
if let Some(value) = metric.extract_value(log) {
130+
#[allow(clippy::cast_precision_loss)]
131+
points.push((log.start_time as f64, value));
130132
}
131133
}
132134
}
@@ -137,17 +139,7 @@ pub fn Analytics() -> Element {
137139
let values: Vec<f64> = session
138140
.exercise_logs
139141
.iter()
140-
.filter(|log| {
141-
if log.exercise_id != exercise_id {
142-
return false;
143-
}
144-
let w = log.weight_hg.0 > 0;
145-
match metric {
146-
Metric::Weight => w,
147-
Metric::Reps | Metric::Distance | Metric::Duration => !w,
148-
Metric::Volume => false,
149-
}
150-
})
142+
.filter(|log| log_matches(log))
151143
.filter_map(|log| metric.extract_value(log))
152144
.collect();
153145
if !values.is_empty() {
@@ -163,17 +155,7 @@ pub fn Analytics() -> Element {
163155
let values: Vec<f64> = session
164156
.exercise_logs
165157
.iter()
166-
.filter(|log| {
167-
if log.exercise_id != exercise_id {
168-
return false;
169-
}
170-
let w = log.weight_hg.0 > 0;
171-
match metric {
172-
Metric::Weight => w,
173-
Metric::Reps | Metric::Distance | Metric::Duration => !w,
174-
Metric::Volume => false,
175-
}
176-
})
158+
.filter(|log| log_matches(log))
177159
.filter_map(|log| metric.extract_value(log))
178160
.collect();
179161
if !values.is_empty() {

src/models/analytics.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ const DURATION_HOURS_MINS_THRESHOLD: f64 = 180.0;
66

77
/// Global aggregation mode for the Analytics view.
88
/// Applies uniformly to all exercise series and to the Volume chart.
9+
///
10+
/// Note: in [`AnalyticsMode::SessionTotal`] mode, [`crate::models::analytics::Metric::Weight`]
11+
/// shows the **maximum** weight per session rather than the sum, because summing weights
12+
/// across sets is not a meaningful fitness metric.
913
#[derive(Clone, Copy, PartialEq, Debug, Default, serde::Serialize, serde::Deserialize)]
1014
pub enum AnalyticsMode {
1115
/// One data point per set (timestamp = set start time).

0 commit comments

Comments
 (0)