trainlog/tui/include/trainlog/database.h

346 lines
9.3 KiB
C

#ifndef TRAINLOG_DATABASE_H
#define TRAINLOG_DATABASE_H
/**
* @file database.h
* @brief SQLite persistence API for the Trainlog TUI core.
*/
#include <stddef.h>
#include "trainlog/model.h"
#include "trainlog/status.h"
#define TRAINLOG_DATABASE_SCHEMA_VERSION 4
typedef struct TrainlogDatabase TrainlogDatabase;
TrainlogStatus trainlog_database_open(
const char *path,
TrainlogDatabase **output_database
);
void trainlog_database_close(TrainlogDatabase *database);
TrainlogStatus trainlog_database_schema_version(
TrainlogDatabase *database,
int *output_version
);
TrainlogStatus trainlog_database_foreign_keys_enabled(
TrainlogDatabase *database,
int *output_enabled
);
TrainlogStatus trainlog_database_begin(TrainlogDatabase *database);
TrainlogStatus trainlog_database_commit(TrainlogDatabase *database);
TrainlogStatus trainlog_database_rollback(TrainlogDatabase *database);
/**
* @brief Insert one already-normalized canonical exercise row.
*
* Unicode normalization belongs to catalog.c. This lower-level API owns the
* final SQLite uniqueness barrier.
*/
TrainlogStatus trainlog_database_insert_exercise(
TrainlogDatabase *database,
const char *exercise_id,
const char *name,
const char *normalized_name,
TrainlogTrackingMode tracking_mode
);
TrainlogStatus trainlog_database_insert_exercise_profiled(
TrainlogDatabase *database,
const char *exercise_id,
const char *name,
const char *normalized_name,
TrainlogTrackingMode tracking_mode,
TrainlogRecordingMode recording_mode,
TrainlogExerciseDataFields data_fields
);
TrainlogStatus trainlog_database_exercise_count(
TrainlogDatabase *database,
size_t *output_count
);
TrainlogStatus trainlog_database_list_exercises(
TrainlogDatabase *database,
TrainlogExercise *output,
size_t capacity,
size_t *output_count
);
TrainlogStatus trainlog_database_insert_session(
TrainlogDatabase *database,
const TrainlogSessionInput *session
);
TrainlogStatus trainlog_database_session_count(
TrainlogDatabase *database,
size_t *output_count
);
TrainlogStatus trainlog_database_list_sessions(
TrainlogDatabase *database,
TrainlogSessionSummary *output,
size_t capacity,
size_t *output_count
);
TrainlogStatus trainlog_database_insert_body_observation(
TrainlogDatabase *database,
const TrainlogBodyObservationInput *observation
);
TrainlogStatus trainlog_database_list_weight_points(
TrainlogDatabase *database,
TrainlogWeightPoint *output,
size_t capacity,
size_t *output_count
);
/* TRAINLOG_SESSION_DETAILS_API */
#define TRAINLOG_SET_SUMMARY_MAX 1024U
typedef struct TrainlogPersistedExerciseDetail {
char name[TRAINLOG_NAME_MAX + 1U];
TrainlogTrackingMode tracking_mode;
TrainlogRecordingMode recording_mode;
TrainlogExerciseDataFields data_fields;
TrainlogLoadMode load_mode;
int rest_seconds;
int target_sets;
int target_reps;
int target_duration_seconds;
int has_target_weight;
double target_weight_kg;
int continuous_duration_seconds;
int has_continuous_speed;
double continuous_speed_kmh;
int has_continuous_distance;
double continuous_distance_km;
size_t actual_set_count;
char actual_summary[TRAINLOG_SET_SUMMARY_MAX + 1U];
} TrainlogPersistedExerciseDetail;
/**
* @brief Load one session header plus ordered exercise details.
*
* The function is read-only and allocates nothing.
*/
TrainlogStatus trainlog_database_get_session_details(
TrainlogDatabase *database,
const char *session_id,
TrainlogSessionSummary *output_session,
TrainlogPersistedExerciseDetail *output_exercises,
size_t exercise_capacity,
size_t *output_exercise_count
);
/* TRAINLOG_BODY_METRIC_HISTORY_API */
typedef enum TrainlogBodyMetric {
TRAINLOG_BODY_METRIC_WEIGHT = 0,
TRAINLOG_BODY_METRIC_NECK,
TRAINLOG_BODY_METRIC_SHOULDERS,
TRAINLOG_BODY_METRIC_CHEST,
TRAINLOG_BODY_METRIC_WAIST,
TRAINLOG_BODY_METRIC_HIPS,
TRAINLOG_BODY_METRIC_LEFT_ARM,
TRAINLOG_BODY_METRIC_RIGHT_ARM,
TRAINLOG_BODY_METRIC_LEFT_FOREARM,
TRAINLOG_BODY_METRIC_RIGHT_FOREARM,
TRAINLOG_BODY_METRIC_LEFT_THIGH,
TRAINLOG_BODY_METRIC_RIGHT_THIGH,
TRAINLOG_BODY_METRIC_LEFT_CALF,
TRAINLOG_BODY_METRIC_RIGHT_CALF,
TRAINLOG_BODY_METRIC_COUNT
} TrainlogBodyMetric;
typedef struct TrainlogBodyMetricPoint {
char observed_at[TRAINLOG_TIMESTAMP_MAX + 1U];
double value;
} TrainlogBodyMetricPoint;
typedef struct TrainlogBodyPairPoint {
bool found;
char observed_at[TRAINLOG_TIMESTAMP_MAX + 1U];
double left_value;
double right_value;
} TrainlogBodyPairPoint;
TrainlogStatus trainlog_database_list_body_metric_points(
TrainlogDatabase *database,
TrainlogBodyMetric metric,
TrainlogBodyMetricPoint *output,
size_t capacity,
size_t *output_count
);
TrainlogStatus trainlog_database_latest_body_pair(
TrainlogDatabase *database,
TrainlogBodyMetric left_metric,
TrainlogBodyMetric right_metric,
TrainlogBodyPairPoint *output
);
/* TRAINLOG_EXERCISE_PERFORMANCE_API */
typedef struct TrainlogExercisePerformancePoint {
char session_id[TRAINLOG_ID_MAX + 1U];
char started_at[TRAINLOG_TIMESTAMP_MAX + 1U];
TrainlogTrackingMode tracking_mode;
TrainlogLoadMode load_mode;
size_t actual_set_count;
int has_performance;
int metric_value;
int has_weight;
double weight_kg;
} TrainlogExercisePerformancePoint;
/**
* @brief Read newest-first per-session representative performance.
*
* Representative-set semantics:
*
* - no load: greatest successful reps/duration;
* - external load: greatest load, then greatest reps/duration;
* - assistance: lowest assistance, then greatest reps/duration.
*
* A zero-repetition failed attempt is never promoted to representative
* performance. This API does not create or infer a measured maximum.
*/
TrainlogStatus trainlog_database_list_exercise_performance(
TrainlogDatabase *database,
const char *exercise_id,
TrainlogExercisePerformancePoint *output,
size_t capacity,
size_t *output_count
);
/* TRAINLOG_SESSION_EDIT_API */
typedef struct TrainlogEditableExerciseRecord {
char exercise_id[TRAINLOG_ID_MAX + 1U];
char name[TRAINLOG_NAME_MAX + 1U];
TrainlogTrackingMode tracking_mode;
TrainlogLoadMode load_mode;
int rest_seconds;
int target_sets;
int target_reps;
int target_duration_seconds;
int has_target_weight;
double target_weight_kg;
char notes[TRAINLOG_NOTE_MAX + 1U];
size_t set_offset;
size_t set_count;
} TrainlogEditableExerciseRecord;
/**
* @brief Load a complete persisted session into caller-owned editable buffers.
*
* The function never truncates a session silently. If either caller capacity is
* too small it returns TRAINLOG_STATUS_INVALID_ARGUMENT and the outputs must not
* be used.
*/
TrainlogStatus trainlog_database_load_session_editable(
TrainlogDatabase *database,
const char *session_id,
TrainlogSessionSummary *output_session,
TrainlogEditableExerciseRecord *output_exercises,
size_t exercise_capacity,
size_t *output_exercise_count,
TrainlogSetInput *output_sets,
size_t set_capacity,
size_t *output_set_count
);
/**
* @brief Replace only the exercise/set contents of an existing session.
*
* The session row itself is preserved, so session_id, timestamps, session type,
* session notes and body_observations.session_row_id remain attached to the
* same row. The replacement is atomic: any error rolls the whole operation
* back.
*/
TrainlogStatus trainlog_database_replace_session_exercises(
TrainlogDatabase *database,
const char *session_id,
const TrainlogSessionExerciseInput *exercises,
size_t exercise_count
);
/* TRAINLOG_BODY_OBSERVATION_RECORD_API */
typedef struct TrainlogBodyObservationRecord {
char observation_id[TRAINLOG_ID_MAX + 1U];
char observed_at[TRAINLOG_TIMESTAMP_MAX + 1U];
char session_id[TRAINLOG_ID_MAX + 1U];
bool has_body_weight;
double body_weight_kg;
bool has_neck;
double neck_cm;
bool has_shoulders;
double shoulders_cm;
bool has_chest;
double chest_cm;
bool has_waist;
double waist_cm;
bool has_hips;
double hips_cm;
bool has_left_arm;
double left_arm_cm;
bool has_right_arm;
double right_arm_cm;
bool has_left_forearm;
double left_forearm_cm;
bool has_right_forearm;
double right_forearm_cm;
bool has_left_thigh;
double left_thigh_cm;
bool has_right_thigh;
double right_thigh_cm;
bool has_left_calf;
double left_calf_cm;
bool has_right_calf;
double right_calf_cm;
char notes[TRAINLOG_NOTE_MAX + 1U];
} TrainlogBodyObservationRecord;
TrainlogStatus trainlog_database_list_body_observations(
TrainlogDatabase *database,
TrainlogBodyObservationRecord *output,
size_t capacity,
size_t *output_count
);
TrainlogStatus trainlog_database_get_body_observation(
TrainlogDatabase *database,
const char *observation_id,
TrainlogBodyObservationRecord *output
);
/**
* @brief Correct metric values of one existing observation.
*
* observation_id identifies the stable row. observed_at and session linkage
* remain unchanged. At least one body metric must remain present.
*/
TrainlogStatus trainlog_database_update_body_observation(
TrainlogDatabase *database,
const TrainlogBodyObservationInput *observation
);
#endif