Add profile-aware continuous exercise tracking
This commit is contained in:
parent
db349c609d
commit
aa82e0f7fa
9 changed files with 646 additions and 0 deletions
17
CHANGELOG.md
17
CHANGELOG.md
|
|
@ -153,3 +153,20 @@ Next:
|
|||
- exposed PC -> Android canonical exercise-catalog direction;
|
||||
- kept frozen Trainlog session JSON v1 unchanged.
|
||||
<!-- TRAINLOG_SYNC_FINAL_CHANGELOG _END -->
|
||||
|
||||
<!-- TRAINLOG_PROFILE_AWARE_CHANGELOG -->
|
||||
### Profile-aware and continuous exercise tracking
|
||||
|
||||
- added set-based versus continuous recording organization;
|
||||
- added speed and distance supplemental-field metadata;
|
||||
- added profiled catalog creation API;
|
||||
- added profile-aware exercise creation in the TUI;
|
||||
- added inline profiled exercise creation while recording a session;
|
||||
- migrated SQLite persistence to schema v4;
|
||||
- added one-to-one continuous activity persistence;
|
||||
- kept continuous activities out of `performed_sets`;
|
||||
- added profile-aware session detail loading;
|
||||
- added continuous duration/speed/distance history display;
|
||||
- made continuous TUI duration entry explicitly minute-based;
|
||||
- preserved frozen session JSON v1 unchanged.
|
||||
<!-- TRAINLOG_PROFILE_AWARE_CHANGELOG _END -->
|
||||
|
|
|
|||
|
|
@ -284,3 +284,66 @@ and no set count.
|
|||
Creating an exercise directly inside session entry must configure this metadata
|
||||
before adding it to the catalog/session.
|
||||
<!-- TRAINLOG_ANDROID_PROFILE_AWARE_ENTRY _END -->
|
||||
|
||||
<!-- TRAINLOG_ANDROID_PROFILE_CURSOR -->
|
||||
## Android implementation cursor
|
||||
|
||||
Android is the next implementation area.
|
||||
|
||||
The application must share Trainlog's visual language with the TUI.
|
||||
|
||||
Theme direction:
|
||||
|
||||
```text
|
||||
dark background
|
||||
cyan/teal Trainlog accent
|
||||
yellow active/focus role
|
||||
green success
|
||||
red error
|
||||
```
|
||||
|
||||
Launcher icon:
|
||||
|
||||
```text
|
||||
T
|
||||
```
|
||||
|
||||
Only the letter `T`, using Trainlog theme colors.
|
||||
|
||||
Required recording sections:
|
||||
|
||||
```text
|
||||
Séance
|
||||
Exercice
|
||||
Mensurations
|
||||
```
|
||||
|
||||
Session recording must allow creating a new exercise inline without leaving the
|
||||
session flow.
|
||||
|
||||
Android forms are driven by the same profile metadata as desktop:
|
||||
|
||||
```text
|
||||
recording_mode
|
||||
tracking_mode
|
||||
data_fields
|
||||
```
|
||||
|
||||
Examples:
|
||||
|
||||
```text
|
||||
SETS + REPS
|
||||
sets / reps / load / rest
|
||||
|
||||
SETS + DURATION
|
||||
sets / duration / optional load / rest
|
||||
|
||||
CONTINUOUS + DURATION + SPEED_KMH
|
||||
duration / speed
|
||||
```
|
||||
|
||||
The app must never infer an input form from an exercise display name.
|
||||
|
||||
Initial development uses fictitious records. Test/development data is removed
|
||||
before normal production use starts.
|
||||
<!-- TRAINLOG_ANDROID_PROFILE_CURSOR _END -->
|
||||
|
|
|
|||
|
|
@ -170,3 +170,30 @@ from USB/MTP backend details.
|
|||
Current transport foundation supports folder creation, file upload, folder
|
||||
listing, file download, and verified byte-for-byte roundtrip.
|
||||
<!-- TRAINLOG_DIRECT_MTP_ARCHITECTURE _END -->
|
||||
|
||||
<!-- TRAINLOG_PROFILE_AWARE_ARCHITECTURE -->
|
||||
## Profile-aware activity architecture
|
||||
|
||||
Trainlog has two distinct actual-work persistence paths:
|
||||
|
||||
```text
|
||||
SET-based exercise
|
||||
session_exercises
|
||||
|
|
||||
+--> performed_sets [0..N]
|
||||
|
||||
CONTINUOUS exercise
|
||||
session_exercises
|
||||
|
|
||||
+--> continuous_activity [exactly 1]
|
||||
```
|
||||
|
||||
The two paths must remain semantically distinct.
|
||||
|
||||
Catalog metadata determines future entry forms.
|
||||
|
||||
Session-exercise snapshot metadata determines historical rendering/editing.
|
||||
|
||||
The Android client must consume the same catalog profile metadata rather than
|
||||
maintaining an independent exercise-type system.
|
||||
<!-- TRAINLOG_PROFILE_AWARE_ARCHITECTURE _END -->
|
||||
|
|
|
|||
|
|
@ -304,3 +304,57 @@ No name-based migration is allowed.
|
|||
Continuous actual activity data is stored separately from `performed_sets`;
|
||||
Trainlog will not manufacture a fake one-set workout.
|
||||
<!-- TRAINLOG_EXERCISE_DATA_MODEL_V1 _END -->
|
||||
|
||||
<!-- TRAINLOG_SCHEMA_V4_CONTINUOUS -->
|
||||
## Schema v4 — continuous exercise persistence
|
||||
|
||||
Current database schema:
|
||||
|
||||
```text
|
||||
TRAINLOG_DATABASE_SCHEMA_VERSION = 4
|
||||
PRAGMA user_version = 4
|
||||
```
|
||||
|
||||
Relevant profile metadata is stored in both:
|
||||
|
||||
```text
|
||||
exercises
|
||||
session_exercises
|
||||
```
|
||||
|
||||
`session_exercises` snapshots:
|
||||
|
||||
```text
|
||||
recording_mode
|
||||
data_fields
|
||||
```
|
||||
|
||||
Continuous actual activity data is stored one-to-one in:
|
||||
|
||||
```text
|
||||
continuous_activity
|
||||
```
|
||||
|
||||
Columns:
|
||||
|
||||
```text
|
||||
session_exercise_row_id
|
||||
duration_seconds
|
||||
speed_kmh nullable
|
||||
distance_km nullable
|
||||
```
|
||||
|
||||
For a valid continuous activity:
|
||||
|
||||
```text
|
||||
target_sets NULL
|
||||
target_reps NULL
|
||||
target_duration_seconds NULL
|
||||
load_mode none
|
||||
rest_seconds 0
|
||||
target_weight_kg NULL
|
||||
performed_sets none
|
||||
```
|
||||
|
||||
Schema migration never infers profile information from exercise names.
|
||||
<!-- TRAINLOG_SCHEMA_V4_CONTINUOUS _END -->
|
||||
|
|
|
|||
|
|
@ -193,3 +193,51 @@ data_fields
|
|||
7. Android uses the same model
|
||||
8. design session exchange v2
|
||||
```
|
||||
|
||||
<!-- TRAINLOG_PROFILE_AWARE_IMPLEMENTED -->
|
||||
## Implemented checkpoint
|
||||
|
||||
The profile-aware exercise model is now implemented in the C model, SQLite
|
||||
persistence and TUI.
|
||||
|
||||
Current canonical rules:
|
||||
|
||||
```text
|
||||
recording_mode = SETS | CONTINUOUS
|
||||
tracking_mode = REPS | DURATION
|
||||
|
||||
known data_fields:
|
||||
SPEED_KMH
|
||||
DISTANCE_KM
|
||||
```
|
||||
|
||||
Valid model-v1 combinations:
|
||||
|
||||
```text
|
||||
SETS + REPS
|
||||
SETS + DURATION
|
||||
CONTINUOUS + DURATION
|
||||
```
|
||||
|
||||
Continuous exercise actual data is persisted as one `continuous_activity`
|
||||
record rather than a performed-set list.
|
||||
|
||||
A continuous activity never manufactures a one-set representation.
|
||||
|
||||
The TUI asks continuous duration in **minutes**, converts to seconds, and stores
|
||||
seconds internally.
|
||||
|
||||
Example:
|
||||
|
||||
```text
|
||||
Marche
|
||||
CONTINUOUS + DURATION + SPEED_KMH
|
||||
|
||||
TUI entry:
|
||||
Durée (minutes)
|
||||
Vitesse km/h
|
||||
```
|
||||
|
||||
Historical session rows snapshot recording metadata and are not reinterpreted
|
||||
when catalog metadata later changes.
|
||||
<!-- TRAINLOG_PROFILE_AWARE_IMPLEMENTED _END -->
|
||||
|
|
|
|||
298
docs/reviews/profile_aware_continuous.md
Normal file
298
docs/reviews/profile_aware_continuous.md
Normal file
|
|
@ -0,0 +1,298 @@
|
|||
# Profile-aware continuous exercise checkpoint
|
||||
|
||||
## Status
|
||||
|
||||
```text
|
||||
EXERCISE_DATA_MODEL_V1=IMPLEMENTED
|
||||
DATABASE_SCHEMA_V4=IMPLEMENTED
|
||||
PROFILED_CATALOG_API=IMPLEMENTED
|
||||
PROFILE_AWARE_EXERCISE_CREATION=IMPLEMENTED
|
||||
CONTINUOUS_ACTIVITY_PERSISTENCE=IMPLEMENTED
|
||||
CONTINUOUS_ACTIVITY_DETAIL_DISPLAY=IMPLEMENTED
|
||||
CONTINUOUS_DURATION_MINUTES_UI=IMPLEMENTED
|
||||
TRAINLOG_FORMAT_V1=FROZEN
|
||||
ANDROID_APP=NEXT
|
||||
```
|
||||
|
||||
The implementation now distinguishes set-based exercises from continuous
|
||||
activities.
|
||||
|
||||
This checkpoint must be treated as the current architecture by future agents.
|
||||
|
||||
## Core model
|
||||
|
||||
Exercise catalog metadata:
|
||||
|
||||
```text
|
||||
recording_mode = SETS | CONTINUOUS
|
||||
tracking_mode = REPS | DURATION
|
||||
data_fields = bounded supplemental-field bit mask
|
||||
```
|
||||
|
||||
Known supplemental fields:
|
||||
|
||||
```text
|
||||
SPEED_KMH
|
||||
DISTANCE_KM
|
||||
```
|
||||
|
||||
Initial valid combinations:
|
||||
|
||||
```text
|
||||
SETS + REPS
|
||||
SETS + DURATION
|
||||
CONTINUOUS + DURATION
|
||||
```
|
||||
|
||||
`CONTINUOUS + REPS` is invalid.
|
||||
|
||||
Behavior is driven by catalog metadata, never by exercise-name heuristics.
|
||||
|
||||
## Examples
|
||||
|
||||
```text
|
||||
Presse à cuisses
|
||||
SETS + REPS
|
||||
|
||||
Gainage
|
||||
SETS + DURATION
|
||||
|
||||
Marche
|
||||
CONTINUOUS + DURATION
|
||||
SPEED_KMH
|
||||
|
||||
Vélo
|
||||
CONTINUOUS + DURATION
|
||||
SPEED_KMH | DISTANCE_KM
|
||||
```
|
||||
|
||||
## Historical stability
|
||||
|
||||
Catalog metadata controls future entry.
|
||||
|
||||
Each `session_exercises` row stores a snapshot of:
|
||||
|
||||
```text
|
||||
recording_mode
|
||||
data_fields
|
||||
```
|
||||
|
||||
Historical sessions must use their snapshot rather than current catalog
|
||||
metadata.
|
||||
|
||||
Existing rows migrated from older schemas remain `SETS` unless explicitly
|
||||
changed for future entries.
|
||||
|
||||
No migration guesses behavior from names such as `Marche`.
|
||||
|
||||
## SQLite schema v4
|
||||
|
||||
Current schema version:
|
||||
|
||||
```text
|
||||
PRAGMA user_version = 4
|
||||
```
|
||||
|
||||
Relevant tables:
|
||||
|
||||
```text
|
||||
exercises
|
||||
sessions
|
||||
session_exercises
|
||||
performed_sets
|
||||
continuous_activity
|
||||
body_observations
|
||||
```
|
||||
|
||||
`continuous_activity` is one-to-one with a continuous session exercise and
|
||||
contains:
|
||||
|
||||
```text
|
||||
duration_seconds
|
||||
speed_kmh nullable
|
||||
distance_km nullable
|
||||
```
|
||||
|
||||
A continuous activity has:
|
||||
|
||||
```text
|
||||
target_sets = NULL
|
||||
target_reps = NULL
|
||||
target_duration_seconds = NULL
|
||||
load_mode = none
|
||||
rest_seconds = 0
|
||||
performed_sets rows = 0
|
||||
```
|
||||
|
||||
Trainlog must not create a fake one-set representation for a continuous
|
||||
activity.
|
||||
|
||||
## TUI behavior
|
||||
|
||||
Set-based exercise:
|
||||
|
||||
```text
|
||||
Charge
|
||||
Séries prévues
|
||||
Répétitions or duration target
|
||||
Repos
|
||||
Séries réellement faites
|
||||
```
|
||||
|
||||
Continuous exercise:
|
||||
|
||||
```text
|
||||
Durée (minutes)
|
||||
optional configured supplemental fields
|
||||
```
|
||||
|
||||
For `Marche + SPEED_KMH`:
|
||||
|
||||
```text
|
||||
Durée (minutes)
|
||||
Vitesse km/h
|
||||
```
|
||||
|
||||
A bare duration value in this continuous form is minutes.
|
||||
|
||||
Example:
|
||||
|
||||
```text
|
||||
15 -> 15 minutes -> 900 seconds persisted
|
||||
```
|
||||
|
||||
SQLite still stores durations in seconds.
|
||||
|
||||
## Session detail behavior
|
||||
|
||||
Set-based history uses the existing sets presentation.
|
||||
|
||||
Continuous history displays its actual continuous values.
|
||||
|
||||
Example:
|
||||
|
||||
```text
|
||||
Exercice 1/1 — Marche
|
||||
|
||||
Mode : continu
|
||||
|
||||
Durée : 15 min
|
||||
Vitesse : 7.0 km/h
|
||||
|
||||
Réalisé : activité continue
|
||||
```
|
||||
|
||||
It must never display:
|
||||
|
||||
```text
|
||||
0 séries
|
||||
0 série × 0 s
|
||||
aucune série réalisée
|
||||
```
|
||||
|
||||
for a valid continuous activity.
|
||||
|
||||
## Catalog creation
|
||||
|
||||
The lower-level catalog supports explicit profile creation.
|
||||
|
||||
Current API:
|
||||
|
||||
```text
|
||||
trainlog_catalog_create_exercise_profiled(...)
|
||||
```
|
||||
|
||||
Legacy exercise creation remains available and means:
|
||||
|
||||
```text
|
||||
recording_mode = SETS
|
||||
data_fields = 0
|
||||
```
|
||||
|
||||
TUI creation from both:
|
||||
|
||||
```text
|
||||
Exercices page
|
||||
session inline creation
|
||||
```
|
||||
|
||||
supports selecting continuous mode and supplemental fields.
|
||||
|
||||
## Frozen exchange boundary
|
||||
|
||||
Trainlog session JSON v1 remains frozen.
|
||||
|
||||
Continuous metrics are not forced into v1.
|
||||
|
||||
Trainlog must never:
|
||||
|
||||
- hide speed/distance in notes;
|
||||
- synthesize a fake set to make continuous data fit v1;
|
||||
- silently drop continuous metrics.
|
||||
|
||||
A future versioned exchange contract will carry profile-aware session data.
|
||||
|
||||
## Android implementation cursor
|
||||
|
||||
The next implementation area is the Android client.
|
||||
|
||||
Android must use the same model:
|
||||
|
||||
```text
|
||||
recording_mode
|
||||
tracking_mode
|
||||
data_fields
|
||||
```
|
||||
|
||||
Required top-level recording sections:
|
||||
|
||||
```text
|
||||
Séance
|
||||
Exercice
|
||||
Mensurations
|
||||
```
|
||||
|
||||
Inside session recording, the user must be able to create a new exercise
|
||||
without leaving the session flow.
|
||||
|
||||
Android visual identity must match the TUI:
|
||||
|
||||
```text
|
||||
dark background
|
||||
Trainlog cyan/teal accent
|
||||
yellow active/focused border role
|
||||
green success
|
||||
red error
|
||||
```
|
||||
|
||||
The Android launcher icon is intentionally minimal:
|
||||
|
||||
```text
|
||||
T
|
||||
```
|
||||
|
||||
using the Trainlog theme colors.
|
||||
|
||||
Initial Android development uses fictitious data. The development database is
|
||||
purged before normal use begins.
|
||||
|
||||
## Validation checkpoint
|
||||
|
||||
Normal build/test checkpoint after continuous activity work:
|
||||
|
||||
```text
|
||||
15 tests expected
|
||||
git diff --check clean
|
||||
```
|
||||
|
||||
The important functional manual checks are:
|
||||
|
||||
```text
|
||||
create continuous Marche + speed
|
||||
record duration in minutes
|
||||
persist duration as seconds
|
||||
persist speed in continuous_activity
|
||||
no performed_sets rows for continuous activity
|
||||
reopen session detail
|
||||
display duration + speed as continuous activity
|
||||
```
|
||||
|
|
@ -309,3 +309,43 @@ supplemental fields: SPEED_KMH | DISTANCE_KM
|
|||
|
||||
Existing schema-v2 data migrates conservatively to `SETS`.
|
||||
<!-- TRAINLOG_EXERCISE_DATA_MODEL_ROADMAP _END -->
|
||||
|
||||
<!-- TRAINLOG_PROFILE_AWARE_ROADMAP_FINAL -->
|
||||
## Current implementation cursor
|
||||
|
||||
```text
|
||||
MTP_TRANSPORT_FOUNDATION=PASS
|
||||
TUI_SYNC_PAGE=PASS
|
||||
|
||||
EXERCISE_DATA_MODEL_V1=PASS
|
||||
DATABASE_SCHEMA_V4=PASS
|
||||
PROFILED_CATALOG_API=PASS
|
||||
PROFILE_AWARE_EXERCISE_CREATION=PASS
|
||||
CONTINUOUS_ACTIVITY_PERSISTENCE=PASS
|
||||
CONTINUOUS_ACTIVITY_DETAIL_DISPLAY=PASS
|
||||
CONTINUOUS_DURATION_MINUTES_UI=PASS
|
||||
|
||||
ANDROID_APP=NEXT
|
||||
|
||||
TRAINLOG_FORMAT_V1=FROZEN
|
||||
PROFILE_AWARE_SESSION_EXCHANGE=DESIGN_LATER
|
||||
```
|
||||
|
||||
Next Android slice:
|
||||
|
||||
```text
|
||||
1. Android project scaffold
|
||||
2. shared Trainlog visual identity
|
||||
3. launcher icon = themed T
|
||||
4. home/navigation
|
||||
5. session recording
|
||||
6. inline exercise creation
|
||||
7. standalone exercise creation
|
||||
8. standalone body measurement recording
|
||||
9. fictitious local records
|
||||
10. only then connect exchange/sync
|
||||
```
|
||||
|
||||
Do not revert continuous activities to performed sets.
|
||||
Do not modify JSON v1 to accommodate continuous metrics.
|
||||
<!-- TRAINLOG_PROFILE_AWARE_ROADMAP_FINAL _END -->
|
||||
|
|
|
|||
|
|
@ -190,3 +190,35 @@ The hardware probe is intentionally separate from the normal automated test
|
|||
suite because CI is not expected to have a connected unlocked Android MTP
|
||||
device.
|
||||
<!-- TRAINLOG_MTP_VALIDATION _END -->
|
||||
|
||||
<!-- TRAINLOG_CONTINUOUS_TEST_CHECKPOINT -->
|
||||
## Profile-aware / continuous validation
|
||||
|
||||
Expected normal test suite after this checkpoint:
|
||||
|
||||
```text
|
||||
15 tests
|
||||
```
|
||||
|
||||
Coverage added around:
|
||||
|
||||
```text
|
||||
exercise profile schema
|
||||
profiled catalog creation
|
||||
schema migration
|
||||
continuous session persistence
|
||||
continuous session detail loading
|
||||
```
|
||||
|
||||
Manual TUI validation includes:
|
||||
|
||||
```text
|
||||
Marche configured CONTINUOUS + DURATION + SPEED_KMH
|
||||
entry asks duration minutes + speed
|
||||
no sets/rest/load prompts
|
||||
continuous_activity row persisted
|
||||
performed_sets count remains zero
|
||||
history reopens as continuous
|
||||
duration and speed render correctly
|
||||
```
|
||||
<!-- TRAINLOG_CONTINUOUS_TEST_CHECKPOINT _END -->
|
||||
|
|
|
|||
67
docs/tui.md
67
docs/tui.md
|
|
@ -696,3 +696,70 @@ Continuous exercises do not display a set count.
|
|||
`Marche` will use the continuous form only after its catalog metadata is
|
||||
explicitly changed; behavior is never inferred from its name.
|
||||
<!-- TRAINLOG_PROFILE_AWARE_ENTRY _END -->
|
||||
|
||||
<!-- TRAINLOG_CONTINUOUS_TUI_IMPLEMENTED -->
|
||||
## Continuous exercise TUI — implemented
|
||||
|
||||
Exercise creation supports explicit organization:
|
||||
|
||||
```text
|
||||
1 séries
|
||||
2 continu
|
||||
```
|
||||
|
||||
Continuous creation forces duration tracking in model v1 and can enable:
|
||||
|
||||
```text
|
||||
speed
|
||||
distance
|
||||
```
|
||||
|
||||
The same creation path is available:
|
||||
|
||||
```text
|
||||
from Exercices page
|
||||
inline while recording a session
|
||||
```
|
||||
|
||||
Session entry is profile-aware.
|
||||
|
||||
Set-based exercises retain:
|
||||
|
||||
```text
|
||||
charge
|
||||
sets
|
||||
reps/duration
|
||||
rest
|
||||
performed sets
|
||||
```
|
||||
|
||||
Continuous exercises display only their relevant fields.
|
||||
|
||||
For `Marche + SPEED_KMH`:
|
||||
|
||||
```text
|
||||
Durée (minutes)
|
||||
Vitesse km/h
|
||||
```
|
||||
|
||||
Bare continuous duration input is interpreted as minutes.
|
||||
|
||||
Example:
|
||||
|
||||
```text
|
||||
15 -> 15 min -> 900 seconds in SQLite
|
||||
```
|
||||
|
||||
Session detail is also profile-aware.
|
||||
|
||||
Continuous detail example:
|
||||
|
||||
```text
|
||||
Mode : continu
|
||||
Durée : 15 min
|
||||
Vitesse : 7.0 km/h
|
||||
Réalisé : activité continue
|
||||
```
|
||||
|
||||
Do not render set-oriented labels for a valid continuous activity.
|
||||
<!-- TRAINLOG_CONTINUOUS_TUI_IMPLEMENTED _END -->
|
||||
|
|
|
|||
Loading…
Reference in a new issue