feat: 补齐 Garmin 未同步的数据,并各自配上界面
审计 57 个接口后,把账号里真有数据却从未入库的部分补上。全部走同步模块, 界面只读本地库。 新增数据 - 体重与身体成分(体脂率/肌肉量/体水分/骨量/内脏脂肪/代谢年龄) - 血压(接口通,账号暂无记录) - 跑步成绩预测(5 公里 / 10 公里 / 半马 / 全马) - 爬坡分、饮水量、出汗量 → health_data 新增七列 - 全天曲线:心率 / 压力 / 身体电量 / 呼吸 / 血氧 - 挑战赛(徽章挑战与好友挑战,与一次性的徽章不同,有周期和进度) - 已配对设备 新增界面 - /body/ 身体成分:体重大数字 + BMI 分级 + 体脂肌肉曲线 + 血压表格 - /race/ 成绩预测:四个距离的预测成绩与配速,以及预测随时间的变化 - /challenges/ 挑战赛:按类型筛选,有目标的显示进度条 - /devices/ 已配对设备 - 每日页新增「全天曲线」,这是存日内采样的主要目的 - 健康页新增「身体成分」分组与「更多」入口,运动页加挑战赛与成绩预测入口 同步开销 - 日内曲线每天五个请求,14 天以内的同步顺带拉,更长的历史交给后台 「补齐详细数据」,否则一年的同步会多出约 1800 个请求 - 原来的「补齐运动详情」扩展为统一的补齐任务,分阶段上报进度 日内采样抽稀到每天 240 点:手机图表分辨不出更多,只会把行撑大。 全量 446 项测试通过。 Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
100
backend/db.py
100
backend/db.py
@@ -188,6 +188,97 @@ CREATE TABLE IF NOT EXISTS activity_details (
|
||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
);
|
||||
|
||||
-- Weight and body composition, one row per measurement day.
|
||||
-- Separate from health_data because it arrives from the scale rather than the
|
||||
-- watch, on its own irregular schedule — most days simply have no row.
|
||||
CREATE TABLE IF NOT EXISTS body_composition (
|
||||
id VARCHAR(96) PRIMARY KEY,
|
||||
user_id VARCHAR(64) NOT NULL,
|
||||
date DATE NOT NULL,
|
||||
weight_kg DOUBLE,
|
||||
bmi DOUBLE,
|
||||
body_fat_pct DOUBLE,
|
||||
body_water_pct DOUBLE,
|
||||
bone_mass_kg DOUBLE,
|
||||
muscle_mass_kg DOUBLE,
|
||||
physique_rating DOUBLE,
|
||||
visceral_fat DOUBLE,
|
||||
metabolic_age DOUBLE,
|
||||
source VARCHAR(32),
|
||||
UNIQUE(user_id, date),
|
||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
);
|
||||
|
||||
-- Blood pressure readings. Manually entered in Garmin Connect, so there may
|
||||
-- be none at all; the table exists so that there is somewhere to put them.
|
||||
CREATE TABLE IF NOT EXISTS blood_pressure (
|
||||
id VARCHAR(96) PRIMARY KEY,
|
||||
user_id VARCHAR(64) NOT NULL,
|
||||
measured_at DATETIME NOT NULL,
|
||||
systolic INT,
|
||||
diastolic INT,
|
||||
pulse INT,
|
||||
note TEXT,
|
||||
UNIQUE(user_id, measured_at),
|
||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
);
|
||||
|
||||
-- Garmin's predicted race times, in seconds. One row per day it recalculates.
|
||||
CREATE TABLE IF NOT EXISTS race_predictions (
|
||||
id VARCHAR(96) PRIMARY KEY,
|
||||
user_id VARCHAR(64) NOT NULL,
|
||||
date DATE NOT NULL,
|
||||
time_5k INT,
|
||||
time_10k INT,
|
||||
time_half INT,
|
||||
time_marathon INT,
|
||||
UNIQUE(user_id, date),
|
||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
);
|
||||
|
||||
-- Within-day sample series: heart rate, stress, body battery, respiration,
|
||||
-- SpO2. One generic table rather than five near-identical ones — they differ
|
||||
-- only in what the numbers mean, and the daily screen reads them the same way.
|
||||
CREATE TABLE IF NOT EXISTS daily_series (
|
||||
id VARCHAR(96) PRIMARY KEY,
|
||||
user_id VARCHAR(64) NOT NULL,
|
||||
date DATE NOT NULL,
|
||||
kind VARCHAR(32) NOT NULL,
|
||||
payload MEDIUMTEXT,
|
||||
fetched_at DATETIME,
|
||||
UNIQUE(user_id, date, kind),
|
||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
);
|
||||
|
||||
-- Badge challenges and ad-hoc challenges. Distinct from `badges`: a badge is
|
||||
-- earned once, a challenge has a period, a target and a standing.
|
||||
CREATE TABLE IF NOT EXISTS challenges (
|
||||
id VARCHAR(96) PRIMARY KEY,
|
||||
user_id VARCHAR(64) NOT NULL,
|
||||
challenge_uuid VARCHAR(96),
|
||||
kind VARCHAR(32),
|
||||
name VARCHAR(255),
|
||||
status VARCHAR(64),
|
||||
start_date DATE,
|
||||
end_date DATE,
|
||||
payload MEDIUMTEXT,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
);
|
||||
|
||||
-- Paired devices, so the app can say which watch a number came from.
|
||||
CREATE TABLE IF NOT EXISTS devices (
|
||||
id VARCHAR(96) PRIMARY KEY,
|
||||
user_id VARCHAR(64) NOT NULL,
|
||||
device_id VARCHAR(96),
|
||||
name VARCHAR(255),
|
||||
model VARCHAR(255),
|
||||
serial VARCHAR(96),
|
||||
software_version VARCHAR(64),
|
||||
last_used_at DATETIME,
|
||||
payload MEDIUMTEXT,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
);
|
||||
|
||||
-- One cached LLM answer per user. Generating one takes minutes against a
|
||||
-- large reasoning model, which is far too slow to sit in a page load, so the
|
||||
-- result is stored and reused until the underlying data changes.
|
||||
@@ -349,6 +440,15 @@ MIGRATIONS = {
|
||||
("training_readiness", "INT"),
|
||||
("vo2max", "DOUBLE"),
|
||||
("endurance_score", "INT"),
|
||||
# hill score, hydration and weight — daily scalars that were being
|
||||
# fetched from Garmin by nothing at all until now
|
||||
("hill_score", "INT"),
|
||||
("hydration_ml", "INT"),
|
||||
("hydration_goal_ml", "INT"),
|
||||
("sweat_loss_ml", "INT"),
|
||||
("weight_kg", "DOUBLE"),
|
||||
("body_fat_pct", "DOUBLE"),
|
||||
("bmi", "DOUBLE"),
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user