trainlog/tui/include/trainlog/statistics.h

191 lines
6.4 KiB
C

/*
* Trainlog statistics interface.
*
* Declares the module boundary and ownership contract; implementation and persistence remain in their owning modules.
*/
#ifndef TRAINLOG_STATISTICS_H
#define TRAINLOG_STATISTICS_H
/**
* @file statistics.h
* @brief Auditable read models for the desktop statistics centre.
*/
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "trainlog/database.h"
#define TRAINLOG_STATISTICS_BUCKET_MAX 128U
#define TRAINLOG_STATISTICS_ZONE_MAX 64U
#define TRAINLOG_STATISTICS_SERIES_MAX 256U
typedef enum TrainlogStatisticsWindow {
TRAINLOG_STATISTICS_7_DAYS = 0,
TRAINLOG_STATISTICS_30_DAYS,
TRAINLOG_STATISTICS_ALL
} TrainlogStatisticsWindow;
typedef enum TrainlogStatisticsBucketKind {
TRAINLOG_STATISTICS_BUCKET_7_DAYS = 0,
TRAINLOG_STATISTICS_BUCKET_CALENDAR_MONTH
} TrainlogStatisticsBucketKind;
typedef struct TrainlogStatisticsBucket {
int64_t timestamp;
int64_t end_timestamp;
char label[24];
size_t sessions;
size_t sets;
uint64_t repetitions;
double loaded_volume_kg;
size_t immediate_feedback_count;
} TrainlogStatisticsBucket;
typedef struct TrainlogStatisticsSummary {
size_t sessions;
size_t distinct_exercises;
size_t occurrences;
size_t sets;
uint64_t repetitions;
uint64_t activity_duration_seconds;
uint64_t session_duration_seconds;
size_t sessions_with_duration;
double loaded_volume_kg;
size_t active_days;
size_t active_weeks;
int64_t latest_session_timestamp;
size_t days_since_last_session;
size_t immediate_feedback_count;
size_t followup_count;
char top_feedback_exercise[TRAINLOG_NAME_MAX + 1U];
size_t top_feedback_count;
size_t planned_actual_occurrences;
uint64_t planned_sets;
uint64_t actual_sets_for_plans;
uint64_t planned_repetitions;
uint64_t actual_repetitions_for_plans;
TrainlogStatisticsBucket buckets[TRAINLOG_STATISTICS_BUCKET_MAX];
size_t bucket_count;
bool buckets_partial;
TrainlogStatisticsBucketKind bucket_kind;
} TrainlogStatisticsSummary;
typedef struct TrainlogZoneStatistics {
char zone_id[TRAINLOG_ID_MAX + 1U];
char label[TRAINLOG_NAME_MAX + 1U];
size_t sessions_any;
size_t primary_7;
size_t secondary_7;
size_t primary_30;
size_t secondary_30;
size_t distinct_exercises;
char latest_primary[TRAINLOG_TIMESTAMP_MAX + 1U];
char latest_secondary[TRAINLOG_TIMESTAMP_MAX + 1U];
} TrainlogZoneStatistics;
typedef enum TrainlogExerciseSeriesKind {
TRAINLOG_EXERCISE_SERIES_LOAD = 0,
TRAINLOG_EXERCISE_SERIES_VOLUME,
TRAINLOG_EXERCISE_SERIES_REPETITIONS,
TRAINLOG_EXERCISE_SERIES_SETS,
TRAINLOG_EXERCISE_SERIES_MAX,
TRAINLOG_EXERCISE_SERIES_COUNT
} TrainlogExerciseSeriesKind;
typedef struct TrainlogStatisticPoint {
int64_t timestamp;
char timestamp_label[TRAINLOG_TIMESTAMP_MAX + 1U];
double value;
} TrainlogStatisticPoint;
typedef struct TrainlogMaxListItem {
char exercise_id[TRAINLOG_ID_MAX + 1U];
double latest_max_kg;
char latest_at[TRAINLOG_TIMESTAMP_MAX + 1U];
} TrainlogMaxListItem;
typedef struct TrainlogExerciseListStatistic {
char exercise_id[TRAINLOG_ID_MAX + 1U];
size_t occurrences;
char latest_at[TRAINLOG_TIMESTAMP_MAX + 1U];
} TrainlogExerciseListStatistic;
typedef struct TrainlogExerciseStatistics {
char exercise_id[TRAINLOG_ID_MAX + 1U];
char name[TRAINLOG_NAME_MAX + 1U];
TrainlogTrackingMode tracking_mode;
TrainlogRecordingMode recording_mode;
char load_semantics[32];
size_t occurrences;
size_t sessions;
size_t sets;
uint64_t repetitions;
uint64_t duration_seconds;
double loaded_volume_kg;
double highest_load_kg;
bool has_highest_load;
int highest_repetitions;
double highest_repetitions_load_kg;
bool has_repetitions_at_load;
char first_at[TRAINLOG_TIMESTAMP_MAX + 1U];
char last_at[TRAINLOG_TIMESTAMP_MAX + 1U];
size_t frequency_7;
size_t frequency_30;
size_t max_count;
double latest_max_kg;
double previous_max_kg;
double best_max_kg;
char latest_max_at[TRAINLOG_TIMESTAMP_MAX + 1U];
bool has_latest_max;
bool has_previous_max;
size_t immediate_feedback_count;
uint64_t planned_sets;
uint64_t actual_sets_for_plans;
uint64_t planned_repetitions;
uint64_t actual_repetitions_for_plans;
double latest_planned_weight_kg;
double latest_actual_weight_kg;
bool has_latest_planned_actual_weight;
TrainlogStatisticPoint series[TRAINLOG_EXERCISE_SERIES_COUNT]
[TRAINLOG_STATISTICS_SERIES_MAX];
size_t series_count[TRAINLOG_EXERCISE_SERIES_COUNT];
bool series_partial[TRAINLOG_EXERCISE_SERIES_COUNT];
} TrainlogExerciseStatistics;
/**
* Load selected-period global statistics and bounded comparison buckets.
*
* CONTRACT: now_utc is the inclusive observation cutoff. Summary totals cover
* the current selected calendar period, while graph buckets compare consecutive
* calendar periods. Seven-day buckets are Monday-Sunday weeks and 30-day mode
* uses Gregorian calendar months; all-history aggregation is adaptive.
* Only the graph series can be bounded/partial. The
* database and output are borrowed and no SQLite resource escapes this call.
*/
TrainlogStatus trainlog_statistics_load_summary(
TrainlogDatabase *database, TrainlogStatisticsWindow window,
int64_t now_utc, TrainlogStatisticsSummary *output);
/* BODY ZONES are counted only from persisted primary/secondary relations. */
TrainlogStatus trainlog_statistics_load_zones(
TrainlogDatabase *database, int64_t now_utc,
TrainlogZoneStatistics *output, size_t capacity, size_t *output_count,
bool *output_partial);
/* Exact canonical exercise_id lookup; display names never select history. */
TrainlogStatus trainlog_statistics_load_exercise(
TrainlogDatabase *database, const char *exercise_id, int64_t now_utc,
TrainlogExerciseStatistics *output);
/* One bounded query for the default MAX catalogue surface; newest explicit
* result wins per stable exercise_id and exercises without MAX are absent. */
TrainlogStatus trainlog_statistics_list_maxima(TrainlogDatabase *database,
TrainlogMaxListItem *output, size_t capacity, size_t *output_count,
bool *output_partial);
TrainlogStatus trainlog_statistics_list_exercises(TrainlogDatabase *database,
TrainlogExerciseListStatistic *output, size_t capacity,
size_t *output_count, bool *output_partial);
#endif