Skip to content

Copilot/optimize analytics and ignore non weighted sets#144

Merged
gfauredev merged 2 commits into
mainfrom
copilot/optimize-analytics-and-ignore-non-weighted-sets
May 7, 2026
Merged

Copilot/optimize analytics and ignore non weighted sets#144
gfauredev merged 2 commits into
mainfrom
copilot/optimize-analytics-and-ignore-non-weighted-sets

Conversation

@gfauredev
Copy link
Copy Markdown
Owner

@gfauredev gfauredev commented May 7, 2026

This Pull Request…

Engineering Principles

  • PR only contains changes strictly related to the requested feature or fix,
    scope is focused (no unrelated dependency updates or formatting)
  • This code totally respects README’s Engineering Principles

CI/CD Readiness

  • Branch follows Conventional Branch: feat/…, fix/…, refactor/…, …
  • Code is formatted with dx fmt; cargo fmt
  • All checks pass, nix flake checks succeeds without warnings
    • Code compiles, dx build with necessary platform flags succeeds
    • cargo clippy -- -D warnings -W clippy::all -W clippy::pedantic
      produces zero warnings
    • All unit tests pass without warnings
      cargo llvm-cov nextest --ignore-filename-regex '(src/components/|\.cargo/registry/|nix/store)'
    • End-to-end tests pass maestro test --headless maestro/web
      maestro test --headless maestro/android

Summary by CodeRabbit

Release Notes

  • New Features

    • Volume metric is now available in the analytics section, calculating weight multiplied by repetitions (kg·reps).
  • Updates

    • Analytics metric selector enhanced to display metric-exercise pairs, allowing selection of up to 8 distinct metric combinations.
  • Localisation

    • Translation updates added for English, Spanish, and French versions.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 7, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

Volume transitions from a dedicated exercise selector to a metric option within the 8 metric-exercise pair slots. Translation keys are updated across all locales, the Analytics component state is consolidated to 8 slots, and chart aggregation logic now handles Volume alongside other metrics using existing AnalyticsMode logic.

Changes

Volume Metric Integration

Layer / File(s) Summary
Translation Contracts
assets/en.ftl, assets/es.ftl, assets/fr.ftl
All locale files add analytics-metric-volume and remove the deprecated analytics-volume-exercise-label. The new key enables Volume selection as a metric option.
Analytics Architecture
src/components/analytics/mod.rs
Colour palette shrinks from 9 to 8 entries; the dedicated volume_exercise state is removed; selected_pairs snapshot and weighted_exercise_ids derivation drive series construction.
Chart Data Aggregation
src/components/analytics/mod.rs
Chart construction now generates Volume series inline when Metric::Volume is selected, aggregating weight×reps by AnalyticsMode; non-Volume metrics use updated log_matches keyed on weighted_exercise_ids, with SessionTotal logic preserving max-per-session for Weight and sum-per-session for other metrics.
UI Selector Integration
src/components/analytics/mod.rs, src/components/analytics/selector.rs
Volume option is added to the metric dropdown; onchange handler maps "Volume" to Metric::Volume; the 8-slot selector loop and reduced colour array remain consistent throughout rendering.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • gfauredev/LogOut#143: Adds the foundational Metric::Volume enum variant and related analytics mode support in the models layer, directly enabling this PR's integration of Volume as a selectable metric.

Poem

A rabbit hops through metrics bright, 🐰
Eight pairs now dance where nine once grew,
Volume joins the chart's delight,
With weight and reps and reps anew,
No exercise slot stands alone—
Each metric finds its fitting home! 🎨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description contains only the template checklist with all items unchecked; it lacks any actual explanation of the changes, objectives, or rationale for the pull request. Fill in the description with a clear explanation of what was changed, why volume selection was unified, how non-weighted sets are now ignored, and which checklist items have been completed.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title partially captures the main changes—optimising analytics and ignoring non-weighted sets—but omits the significant change of unifying volume selection into the metric–exercise pair selector, which is a substantial refactoring evident in the code changes.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch copilot/optimize-analytics-and-ignore-non-weighted-sets

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 7, 2026

📊 Coverage Report

Lines: 3774/5029 (75.04474050507059%)

⏱️ Tests: 255 tests in 0.622s

FilenameFunction CoverageLine CoverageRegion CoverageBranch Coverage
main.rs
  22.00% (11/50)
  58.94% (155/263)
  60.56% (218/360)
- (0/0)
models/analytics.rs
   0.00% (0/6)
   0.00% (0/36)
   0.00% (0/48)
- (0/0)
models/enums.rs
 100.00% (28/28)
 100.00% (147/147)
 100.00% (337/337)
- (0/0)
models/exercise.rs
  92.00% (46/50)
  92.10% (548/595)
  91.01% (749/823)
- (0/0)
models/log.rs
 100.00% (12/12)
 100.00% (118/118)
 100.00% (144/144)
- (0/0)
models/mod.rs
 100.00% (11/11)
 100.00% (67/67)
 100.00% (97/97)
- (0/0)
models/session.rs
  72.22% (13/18)
  84.78% (156/184)
  83.33% (210/252)
- (0/0)
models/units.rs
 100.00% (28/28)
 100.00% (167/167)
  98.88% (353/357)
- (0/0)
services/app_state.rs
   1.89% (1/53)
   2.47% (11/445)
   2.27% (15/662)
- (0/0)
services/exercise_db.rs
  88.81% (127/143)
  90.30% (1210/1340)
  89.60% (1922/2145)
- (0/0)
services/exercise_loader.rs
   0.00% (0/14)
   0.00% (0/100)
   0.00% (0/135)
- (0/0)
services/native_queue.rs
   0.00% (0/15)
   0.00% (0/145)
   0.00% (0/197)
- (0/0)
services/notifications.rs
   0.00% (0/3)
   0.00% (0/9)
   0.00% (0/10)
- (0/0)
services/service_worker.rs
 100.00% (2/2)
 100.00% (6/6)
 100.00% (6/6)
- (0/0)
services/storage.rs
  63.77% (88/138)
  81.21% (778/958)
  83.53% (1187/1421)
- (0/0)
services/wake_lock.rs
 100.00% (2/2)
 100.00% (5/5)
 100.00% (5/5)
- (0/0)
utils.rs
  91.36% (74/81)
  91.44% (406/444)
  91.80% (649/707)
- (0/0)
Totals
  67.74% (443/654)
  75.04% (3774/5029)
  76.46% (5892/7706)
- (0/0)

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/analytics/mod.rs (1)

104-249: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Consolidate the Volume branch into the shared mode-aggregation skeleton.

The new Metric::Volume branch (lines 111–170) re-implements the same Set / SessionAverage / SessionTotal iteration skeleton as the non-Volume branch (lines 191–248), differing only in how the per-log value is computed and that SessionTotal sums (vs Weight's max). This duplicates ~60 lines, forces an unreachable!() arm at lines 239–241, and introduces a minor inconsistency (if total > 0.0 vs if !values.is_empty()).

Volume can flow through the existing pipeline if Metric::extract_value returns the per-log volume for Metric::Volume (only when weight_hg.0 > 0 && reps.is_some()), and log_matches / the SessionTotal aggregator gain a Volume arm. Volume is intrinsically weighted, so it should bypass the use_weighted_sets partition.

♻️ Sketch of the unified flow

In models::analytics, extend extract_value (rough shape):

impl Metric {
    pub fn extract_value(self, log: &ExerciseLog) -> Option<f64> {
        match self {
            // ... existing arms ...
            Metric::Volume => {
                if log.weight_hg.0 > 0 {
                    log.reps.map(|r| {
                        f64::from(log.weight_hg.0) / HG_PER_KG * f64::from(r)
                    })
                } else {
                    None
                }
            }
        }
    }
}

Then in mod.rs, drop the entire if metric == Metric::Volume { ... } else split and adjust the shared closures:

-                if metric == Metric::Volume {
-                    match mode {
-                        AnalyticsMode::Set => { /* ... */ }
-                        AnalyticsMode::SessionAverage => { /* ... */ }
-                        AnalyticsMode::SessionTotal => { /* ... */ }
-                    }
-                } else {
                     let use_weighted_sets = weighted_exercise_ids.contains(exercise_id.as_str());
                     let log_matches = |log: &crate::models::ExerciseLog| -> bool {
                         if log.exercise_id != exercise_id {
                             return false;
                         }
                         let is_weighted = log.weight_hg.0 > 0;
                         match metric {
-                            Metric::Weight => is_weighted,
+                            Metric::Weight | Metric::Volume => is_weighted,
                             Metric::Reps | Metric::Distance | Metric::Duration => {
                                 is_weighted == use_weighted_sets
                             }
-                            Metric::Volume => false,
                         }
                     };
                     // ... unchanged Set / SessionAverage arms ...
                     AnalyticsMode::SessionTotal => {
                         // ...
                         let total = match metric {
                             Metric::Weight => {
                                 values.iter().copied().fold(f64::NEG_INFINITY, f64::max)
                             }
-                            Metric::Reps | Metric::Distance | Metric::Duration => {
+                            Metric::Reps
+                            | Metric::Distance
+                            | Metric::Duration
+                            | Metric::Volume => {
                                 values.iter().sum()
                             }
-                            Metric::Volume => unreachable!(
-                                "volume is handled by the dedicated branch above"
-                            ),
                         };
                         // ...
                     }
-                }

This drops ~60 lines, removes the unreachable!(), and makes future metric additions a single-arm change.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/analytics/mod.rs` around lines 104 - 249, The Volume handling
duplicates the mode-aggregation logic; implement Metric::extract_value to return
per-log volume (using HG_PER_KG and log.reps when log.weight_hg.0 > 0) and then
remove the special-case `if metric == Metric::Volume { ... } else { ... }`
branch in mod.rs so all metrics flow through the shared
Set/SessionAverage/SessionTotal skeleton; update the `log_matches` closure to
treat Metric::Volume as intrinsically weighted (i.e., ignore use_weighted_sets
and require weight_hg.0 > 0), and add a Metric::Volume arm in the SessionTotal
match to sum values (instead of unreachable!), keeping the existing behavior for
Metric::Weight (max) and others (sum).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/components/analytics/mod.rs`:
- Around line 104-249: The Volume handling duplicates the mode-aggregation
logic; implement Metric::extract_value to return per-log volume (using HG_PER_KG
and log.reps when log.weight_hg.0 > 0) and then remove the special-case `if
metric == Metric::Volume { ... } else { ... }` branch in mod.rs so all metrics
flow through the shared Set/SessionAverage/SessionTotal skeleton; update the
`log_matches` closure to treat Metric::Volume as intrinsically weighted (i.e.,
ignore use_weighted_sets and require weight_hg.0 > 0), and add a Metric::Volume
arm in the SessionTotal match to sum values (instead of unreachable!), keeping
the existing behavior for Metric::Weight (max) and others (sum).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 1a37f22d-a67c-439b-965b-f5afced09f4a

📥 Commits

Reviewing files that changed from the base of the PR and between fda7349 and ca2813d.

📒 Files selected for processing (5)
  • assets/en.ftl
  • assets/es.ftl
  • assets/fr.ftl
  • src/components/analytics/mod.rs
  • src/components/analytics/selector.rs

@gfauredev gfauredev merged commit 4a0dc60 into main May 7, 2026
8 checks passed
@gfauredev gfauredev deleted the copilot/optimize-analytics-and-ignore-non-weighted-sets branch May 7, 2026 12:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants