skill-calorie 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analysis/anomaly/common.d.ts +53 -0
- package/dist/analysis/anomaly/common.d.ts.map +1 -0
- package/dist/analysis/anomaly/common.js +69 -0
- package/dist/analysis/anomaly/common.js.map +1 -0
- package/dist/analysis/anomaly/diet.d.ts +8 -0
- package/dist/analysis/anomaly/diet.d.ts.map +1 -0
- package/dist/analysis/anomaly/diet.js +95 -0
- package/dist/analysis/anomaly/diet.js.map +1 -0
- package/dist/analysis/anomaly/exercise.d.ts +9 -0
- package/dist/analysis/anomaly/exercise.d.ts.map +1 -0
- package/dist/analysis/anomaly/exercise.js +116 -0
- package/dist/analysis/anomaly/exercise.js.map +1 -0
- package/dist/analysis/anomaly/index.d.ts +19 -0
- package/dist/analysis/anomaly/index.d.ts.map +1 -0
- package/dist/analysis/anomaly/index.js +51 -0
- package/dist/analysis/anomaly/index.js.map +1 -0
- package/dist/analysis/anomaly/overall.d.ts +13 -0
- package/dist/analysis/anomaly/overall.d.ts.map +1 -0
- package/dist/analysis/anomaly/overall.js +128 -0
- package/dist/analysis/anomaly/overall.js.map +1 -0
- package/dist/analysis/anomaly/overall2.d.ts +5 -0
- package/dist/analysis/anomaly/overall2.d.ts.map +1 -0
- package/dist/analysis/anomaly/overall2.js +94 -0
- package/dist/analysis/anomaly/overall2.js.map +1 -0
- package/dist/analysis/anomaly/weight.d.ts +8 -0
- package/dist/analysis/anomaly/weight.d.ts.map +1 -0
- package/dist/analysis/anomaly/weight.js +106 -0
- package/dist/analysis/anomaly/weight.js.map +1 -0
- package/dist/analysis/anomaly/weight2.d.ts +4 -0
- package/dist/analysis/anomaly/weight2.d.ts.map +1 -0
- package/dist/analysis/anomaly/weight2.js +59 -0
- package/dist/analysis/anomaly/weight2.js.map +1 -0
- package/dist/analysis/contraindications.d.ts +63 -0
- package/dist/analysis/contraindications.d.ts.map +1 -0
- package/dist/analysis/contraindications.js +121 -0
- package/dist/analysis/contraindications.js.map +1 -0
- package/dist/analysis/cross.d.ts +75 -0
- package/dist/analysis/cross.d.ts.map +1 -0
- package/dist/analysis/cross.js +283 -0
- package/dist/analysis/cross.js.map +1 -0
- package/dist/analysis/dashboard.d.ts +18 -0
- package/dist/analysis/dashboard.d.ts.map +1 -0
- package/dist/analysis/dashboard.js +48 -0
- package/dist/analysis/dashboard.js.map +1 -0
- package/dist/analysis/deficit.d.ts +44 -0
- package/dist/analysis/deficit.d.ts.map +1 -0
- package/dist/analysis/deficit.js +54 -0
- package/dist/analysis/deficit.js.map +1 -0
- package/dist/analysis/diet.d.ts +76 -0
- package/dist/analysis/diet.d.ts.map +1 -0
- package/dist/analysis/diet.js +164 -0
- package/dist/analysis/diet.js.map +1 -0
- package/dist/analysis/exercise.d.ts +98 -0
- package/dist/analysis/exercise.d.ts.map +1 -0
- package/dist/analysis/exercise.js +298 -0
- package/dist/analysis/exercise.js.map +1 -0
- package/dist/analysis/exerciseReview.d.ts +36 -0
- package/dist/analysis/exerciseReview.d.ts.map +1 -0
- package/dist/analysis/exerciseReview.js +50 -0
- package/dist/analysis/exerciseReview.js.map +1 -0
- package/dist/analysis/precision.d.ts +15 -0
- package/dist/analysis/precision.d.ts.map +1 -0
- package/dist/analysis/precision.js +38 -0
- package/dist/analysis/precision.js.map +1 -0
- package/dist/analysis/result.d.ts +15 -0
- package/dist/analysis/result.d.ts.map +1 -0
- package/dist/analysis/result.js +13 -0
- package/dist/analysis/result.js.map +1 -0
- package/dist/analysis/review.d.ts +118 -0
- package/dist/analysis/review.d.ts.map +1 -0
- package/dist/analysis/review.js +195 -0
- package/dist/analysis/review.js.map +1 -0
- package/dist/analysis/series.d.ts +42 -0
- package/dist/analysis/series.d.ts.map +1 -0
- package/dist/analysis/series.js +127 -0
- package/dist/analysis/series.js.map +1 -0
- package/dist/analysis/simulate.d.ts +43 -0
- package/dist/analysis/simulate.d.ts.map +1 -0
- package/dist/analysis/simulate.js +109 -0
- package/dist/analysis/simulate.js.map +1 -0
- package/dist/analysis/simulate2.d.ts +65 -0
- package/dist/analysis/simulate2.d.ts.map +1 -0
- package/dist/analysis/simulate2.js +173 -0
- package/dist/analysis/simulate2.js.map +1 -0
- package/dist/analysis/trend.d.ts +39 -0
- package/dist/analysis/trend.d.ts.map +1 -0
- package/dist/analysis/trend.js +48 -0
- package/dist/analysis/trend.js.map +1 -0
- package/dist/analysis/utils.d.ts +17 -0
- package/dist/analysis/utils.d.ts.map +1 -0
- package/dist/analysis/utils.js +52 -0
- package/dist/analysis/utils.js.map +1 -0
- package/dist/analysis/volatility.d.ts +43 -0
- package/dist/analysis/volatility.d.ts.map +1 -0
- package/dist/analysis/volatility.js +96 -0
- package/dist/analysis/volatility.js.map +1 -0
- package/dist/analysis/weight.d.ts +86 -0
- package/dist/analysis/weight.d.ts.map +1 -0
- package/dist/analysis/weight.js +219 -0
- package/dist/analysis/weight.js.map +1 -0
- package/dist/analysis/weightCompare.d.ts +63 -0
- package/dist/analysis/weightCompare.d.ts.map +1 -0
- package/dist/analysis/weightCompare.js +212 -0
- package/dist/analysis/weightCompare.js.map +1 -0
- package/dist/analysis/weightCompare2.d.ts +63 -0
- package/dist/analysis/weightCompare2.d.ts.map +1 -0
- package/dist/analysis/weightCompare2.js +211 -0
- package/dist/analysis/weightCompare2.js.map +1 -0
- package/dist/analysis/weightCompare3.d.ts +13 -0
- package/dist/analysis/weightCompare3.d.ts.map +1 -0
- package/dist/analysis/weightCompare3.js +199 -0
- package/dist/analysis/weightCompare3.js.map +1 -0
- package/dist/cli/cmd_read.d.ts +8 -0
- package/dist/cli/cmd_read.d.ts.map +1 -0
- package/dist/cli/cmd_read.js +719 -0
- package/dist/cli/cmd_read.js.map +1 -0
- package/dist/cli/keys.d.ts +333 -0
- package/dist/cli/keys.d.ts.map +1 -0
- package/dist/cli/keys.js +103 -0
- package/dist/cli/keys.js.map +1 -0
- package/dist/cli/write.d.ts +24 -0
- package/dist/cli/write.d.ts.map +1 -0
- package/dist/cli/write.js +832 -0
- package/dist/cli/write.js.map +1 -0
- package/dist/fetch/audit.d.ts +25 -0
- package/dist/fetch/audit.d.ts.map +1 -0
- package/dist/fetch/audit.js +46 -0
- package/dist/fetch/audit.js.map +1 -0
- package/dist/fetch/batch.d.ts +68 -0
- package/dist/fetch/batch.d.ts.map +1 -0
- package/dist/fetch/batch.js +183 -0
- package/dist/fetch/batch.js.map +1 -0
- package/dist/fetch/body.d.ts +89 -0
- package/dist/fetch/body.d.ts.map +1 -0
- package/dist/fetch/body.js +217 -0
- package/dist/fetch/body.js.map +1 -0
- package/dist/fetch/cli.d.ts +3 -0
- package/dist/fetch/cli.d.ts.map +1 -0
- package/dist/fetch/cli.js +195 -0
- package/dist/fetch/cli.js.map +1 -0
- package/dist/fetch/cross-skill.d.ts +29 -0
- package/dist/fetch/cross-skill.d.ts.map +1 -0
- package/dist/fetch/cross-skill.js +42 -0
- package/dist/fetch/cross-skill.js.map +1 -0
- package/dist/fetch/db.d.ts +5 -0
- package/dist/fetch/db.d.ts.map +1 -0
- package/dist/fetch/db.js +4 -0
- package/dist/fetch/db.js.map +1 -0
- package/dist/fetch/diet.d.ts +174 -0
- package/dist/fetch/diet.d.ts.map +1 -0
- package/dist/fetch/diet.js +239 -0
- package/dist/fetch/diet.js.map +1 -0
- package/dist/fetch/errors.d.ts +5 -0
- package/dist/fetch/errors.d.ts.map +1 -0
- package/dist/fetch/errors.js +8 -0
- package/dist/fetch/errors.js.map +1 -0
- package/dist/fetch/exercise.d.ts +72 -0
- package/dist/fetch/exercise.d.ts.map +1 -0
- package/dist/fetch/exercise.js +270 -0
- package/dist/fetch/exercise.js.map +1 -0
- package/dist/fetch/goal.d.ts +48 -0
- package/dist/fetch/goal.d.ts.map +1 -0
- package/dist/fetch/goal.js +73 -0
- package/dist/fetch/goal.js.map +1 -0
- package/dist/fetch/goalHistory.d.ts +24 -0
- package/dist/fetch/goalHistory.d.ts.map +1 -0
- package/dist/fetch/goalHistory.js +51 -0
- package/dist/fetch/goalHistory.js.map +1 -0
- package/dist/fetch/history.d.ts +28 -0
- package/dist/fetch/history.d.ts.map +1 -0
- package/dist/fetch/history.js +29 -0
- package/dist/fetch/history.js.map +1 -0
- package/dist/fetch/index.d.ts +23 -0
- package/dist/fetch/index.d.ts.map +1 -0
- package/dist/fetch/index.js +16 -0
- package/dist/fetch/index.js.map +1 -0
- package/dist/fetch/nutritionGoal.d.ts +115 -0
- package/dist/fetch/nutritionGoal.d.ts.map +1 -0
- package/dist/fetch/nutritionGoal.js +154 -0
- package/dist/fetch/nutritionGoal.js.map +1 -0
- package/dist/fetch/photos.d.ts +76 -0
- package/dist/fetch/photos.d.ts.map +1 -0
- package/dist/fetch/photos.js +193 -0
- package/dist/fetch/photos.js.map +1 -0
- package/dist/fetch/plan.d.ts +145 -0
- package/dist/fetch/plan.d.ts.map +1 -0
- package/dist/fetch/plan.js +258 -0
- package/dist/fetch/plan.js.map +1 -0
- package/dist/fetch/products.d.ts +62 -0
- package/dist/fetch/products.d.ts.map +1 -0
- package/dist/fetch/products.js +73 -0
- package/dist/fetch/products.js.map +1 -0
- package/dist/fetch/profile.d.ts +46 -0
- package/dist/fetch/profile.d.ts.map +1 -0
- package/dist/fetch/profile.js +105 -0
- package/dist/fetch/profile.js.map +1 -0
- package/dist/fetch/shapes.d.ts +41 -0
- package/dist/fetch/shapes.d.ts.map +1 -0
- package/dist/fetch/shapes.js +38 -0
- package/dist/fetch/shapes.js.map +1 -0
- package/dist/fetch/validate.d.ts +11 -0
- package/dist/fetch/validate.d.ts.map +1 -0
- package/dist/fetch/validate.js +64 -0
- package/dist/fetch/validate.js.map +1 -0
- package/dist/fetch/weight.d.ts +109 -0
- package/dist/fetch/weight.d.ts.map +1 -0
- package/dist/fetch/weight.js +210 -0
- package/dist/fetch/weight.js.map +1 -0
- package/dist/fetch/xunji-catalog.d.ts +17 -0
- package/dist/fetch/xunji-catalog.d.ts.map +1 -0
- package/dist/fetch/xunji-catalog.js +57 -0
- package/dist/fetch/xunji-catalog.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/kcal.d.ts +21 -0
- package/dist/kcal.d.ts.map +1 -0
- package/dist/kcal.js +27 -0
- package/dist/kcal.js.map +1 -0
- package/dist/migrate/migrate.d.ts +71 -0
- package/dist/migrate/migrate.d.ts.map +1 -0
- package/dist/migrate/migrate.js +450 -0
- package/dist/migrate/migrate.js.map +1 -0
- package/dist/paths.d.ts +6 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +34 -0
- package/dist/paths.js.map +1 -0
- package/dist/render/analysisPlate.d.ts +49 -0
- package/dist/render/analysisPlate.d.ts.map +1 -0
- package/dist/render/analysisPlate.js +149 -0
- package/dist/render/analysisPlate.js.map +1 -0
- package/dist/render/bodyPlate.d.ts +55 -0
- package/dist/render/bodyPlate.d.ts.map +1 -0
- package/dist/render/bodyPlate.js +97 -0
- package/dist/render/bodyPlate.js.map +1 -0
- package/dist/render/diet.d.ts +47 -0
- package/dist/render/diet.d.ts.map +1 -0
- package/dist/render/diet.js +94 -0
- package/dist/render/diet.js.map +1 -0
- package/dist/render/envelope.d.ts +60 -0
- package/dist/render/envelope.d.ts.map +1 -0
- package/dist/render/envelope.js +111 -0
- package/dist/render/envelope.js.map +1 -0
- package/dist/render/errors.d.ts +10 -0
- package/dist/render/errors.d.ts.map +1 -0
- package/dist/render/errors.js +9 -0
- package/dist/render/errors.js.map +1 -0
- package/dist/render/exercise.d.ts +20 -0
- package/dist/render/exercise.d.ts.map +1 -0
- package/dist/render/exercise.js +32 -0
- package/dist/render/exercise.js.map +1 -0
- package/dist/render/goal.d.ts +24 -0
- package/dist/render/goal.d.ts.map +1 -0
- package/dist/render/goal.js +23 -0
- package/dist/render/goal.js.map +1 -0
- package/dist/render/goalExtra.d.ts +45 -0
- package/dist/render/goalExtra.d.ts.map +1 -0
- package/dist/render/goalExtra.js +94 -0
- package/dist/render/goalExtra.js.map +1 -0
- package/dist/render/goalPlate.d.ts +66 -0
- package/dist/render/goalPlate.d.ts.map +1 -0
- package/dist/render/goalPlate.js +110 -0
- package/dist/render/goalPlate.js.map +1 -0
- package/dist/render/health.d.ts +21 -0
- package/dist/render/health.d.ts.map +1 -0
- package/dist/render/health.js +41 -0
- package/dist/render/health.js.map +1 -0
- package/dist/render/help.d.ts +18 -0
- package/dist/render/help.d.ts.map +1 -0
- package/dist/render/help.js +58 -0
- package/dist/render/help.js.map +1 -0
- package/dist/render/home.d.ts +30 -0
- package/dist/render/home.d.ts.map +1 -0
- package/dist/render/home.js +73 -0
- package/dist/render/home.js.map +1 -0
- package/dist/render/html.d.ts +66 -0
- package/dist/render/html.d.ts.map +1 -0
- package/dist/render/html.js +510 -0
- package/dist/render/html.js.map +1 -0
- package/dist/render/index.d.ts +47 -0
- package/dist/render/index.d.ts.map +1 -0
- package/dist/render/index.js +26 -0
- package/dist/render/index.js.map +1 -0
- package/dist/render/insightPlate.d.ts +55 -0
- package/dist/render/insightPlate.d.ts.map +1 -0
- package/dist/render/insightPlate.js +97 -0
- package/dist/render/insightPlate.js.map +1 -0
- package/dist/render/library.d.ts +32 -0
- package/dist/render/library.d.ts.map +1 -0
- package/dist/render/library.js +54 -0
- package/dist/render/library.js.map +1 -0
- package/dist/render/photo.d.ts +101 -0
- package/dist/render/photo.d.ts.map +1 -0
- package/dist/render/photo.js +241 -0
- package/dist/render/photo.js.map +1 -0
- package/dist/render/planPlate.d.ts +40 -0
- package/dist/render/planPlate.d.ts.map +1 -0
- package/dist/render/planPlate.js +72 -0
- package/dist/render/planPlate.js.map +1 -0
- package/dist/render/profilePlate.d.ts +17 -0
- package/dist/render/profilePlate.d.ts.map +1 -0
- package/dist/render/profilePlate.js +18 -0
- package/dist/render/profilePlate.js.map +1 -0
- package/dist/render/ranking.d.ts +22 -0
- package/dist/render/ranking.d.ts.map +1 -0
- package/dist/render/ranking.js +63 -0
- package/dist/render/ranking.js.map +1 -0
- package/dist/render/receipt.d.ts +71 -0
- package/dist/render/receipt.d.ts.map +1 -0
- package/dist/render/receipt.js +65 -0
- package/dist/render/receipt.js.map +1 -0
- package/dist/render/weightPlate.d.ts +54 -0
- package/dist/render/weightPlate.d.ts.map +1 -0
- package/dist/render/weightPlate.js +110 -0
- package/dist/render/weightPlate.js.map +1 -0
- package/dist/schema.d.ts +25 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +281 -0
- package/dist/schema.js.map +1 -0
- package/dist/triggers/help-lookup.d.ts +28 -0
- package/dist/triggers/help-lookup.d.ts.map +1 -0
- package/dist/triggers/help-lookup.js +163 -0
- package/dist/triggers/help-lookup.js.map +1 -0
- package/dist/triggers/index.d.ts +24 -0
- package/dist/triggers/index.d.ts.map +1 -0
- package/dist/triggers/index.js +78 -0
- package/dist/triggers/index.js.map +1 -0
- package/dist/triggers/scene-01-home.d.ts +4 -0
- package/dist/triggers/scene-01-home.d.ts.map +1 -0
- package/dist/triggers/scene-01-home.js +13 -0
- package/dist/triggers/scene-01-home.js.map +1 -0
- package/dist/triggers/scene-02-diet.d.ts +4 -0
- package/dist/triggers/scene-02-diet.d.ts.map +1 -0
- package/dist/triggers/scene-02-diet.js +74 -0
- package/dist/triggers/scene-02-diet.js.map +1 -0
- package/dist/triggers/scene-03-weight.d.ts +4 -0
- package/dist/triggers/scene-03-weight.d.ts.map +1 -0
- package/dist/triggers/scene-03-weight.js +62 -0
- package/dist/triggers/scene-03-weight.js.map +1 -0
- package/dist/triggers/scene-04-exercise.d.ts +4 -0
- package/dist/triggers/scene-04-exercise.d.ts.map +1 -0
- package/dist/triggers/scene-04-exercise.js +43 -0
- package/dist/triggers/scene-04-exercise.js.map +1 -0
- package/dist/triggers/scene-05-workout.d.ts +4 -0
- package/dist/triggers/scene-05-workout.d.ts.map +1 -0
- package/dist/triggers/scene-05-workout.js +36 -0
- package/dist/triggers/scene-05-workout.js.map +1 -0
- package/dist/triggers/scene-06-goal.d.ts +4 -0
- package/dist/triggers/scene-06-goal.d.ts.map +1 -0
- package/dist/triggers/scene-06-goal.js +29 -0
- package/dist/triggers/scene-06-goal.js.map +1 -0
- package/dist/triggers/scene-07-profile.d.ts +4 -0
- package/dist/triggers/scene-07-profile.d.ts.map +1 -0
- package/dist/triggers/scene-07-profile.js +8 -0
- package/dist/triggers/scene-07-profile.js.map +1 -0
- package/dist/triggers/scene-08-body.d.ts +4 -0
- package/dist/triggers/scene-08-body.d.ts.map +1 -0
- package/dist/triggers/scene-08-body.js +17 -0
- package/dist/triggers/scene-08-body.js.map +1 -0
- package/dist/triggers/scene-09-photo.d.ts +4 -0
- package/dist/triggers/scene-09-photo.d.ts.map +1 -0
- package/dist/triggers/scene-09-photo.js +14 -0
- package/dist/triggers/scene-09-photo.js.map +1 -0
- package/dist/triggers/scene-10-analysis.d.ts +4 -0
- package/dist/triggers/scene-10-analysis.d.ts.map +1 -0
- package/dist/triggers/scene-10-analysis.js +180 -0
- package/dist/triggers/scene-10-analysis.js.map +1 -0
- package/dist/triggers/types.d.ts +66 -0
- package/dist/triggers/types.d.ts.map +1 -0
- package/dist/triggers/types.js +3 -0
- package/dist/triggers/types.js.map +1 -0
- package/package.json +35 -0
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
import { FetchError } from '../fetch/errors.js';
|
|
2
|
+
import { getActivityFactor, parseDate, shiftISODate } from './utils.js';
|
|
3
|
+
import { ok, rejection } from './result.js';
|
|
4
|
+
const round = (n) => Math.round(n);
|
|
5
|
+
function rangeOf(start, end) {
|
|
6
|
+
const s = parseDate(start);
|
|
7
|
+
if (!s)
|
|
8
|
+
throw new FetchError('起始日期非法: ' + String(start));
|
|
9
|
+
return [s, parseDate(end ?? undefined) ?? s];
|
|
10
|
+
}
|
|
11
|
+
function daysBetween(d1, d2) {
|
|
12
|
+
const a = Date.parse(d1 + 'T12:00:00Z');
|
|
13
|
+
const b = Date.parse(d2 + 'T12:00:00Z');
|
|
14
|
+
if (Number.isNaN(a) || Number.isNaN(b))
|
|
15
|
+
throw new FetchError('日期非法: ' + d1 + ' ~ ' + d2);
|
|
16
|
+
return Math.round((b - a) / 86400000);
|
|
17
|
+
}
|
|
18
|
+
function latestWeightKg(db) {
|
|
19
|
+
const row = db.prepare('SELECT weight_kg FROM weight_log ORDER BY date DESC LIMIT 1').get();
|
|
20
|
+
return row?.weight_kg ?? 70;
|
|
21
|
+
}
|
|
22
|
+
function profileActivityLevel(db) {
|
|
23
|
+
const row = db.prepare('SELECT activity_level FROM user_profile WHERE id = 1').get();
|
|
24
|
+
return row?.activity_level ?? 'moderate';
|
|
25
|
+
}
|
|
26
|
+
export function exerciseTrend(db, startDate, endDate) {
|
|
27
|
+
const [start, end] = rangeOf(startDate, endDate);
|
|
28
|
+
const raw = db.prepare('SELECT date AS d, exercise_type AS t, duration_minutes AS dur, calories_burned AS k FROM exercise_log WHERE date >= ? AND date <= ? ORDER BY date ASC').all(start, end);
|
|
29
|
+
const rows = raw.map((r) => [r.d, r.t, r.dur, r.k]);
|
|
30
|
+
if (rows.length === 0)
|
|
31
|
+
return rejection('无运动记录(' + start + ' ~ ' + end + ')');
|
|
32
|
+
const span = daysBetween(start, end) + 1;
|
|
33
|
+
const dates = [...new Set(rows.map((r) => r[0]))].sort();
|
|
34
|
+
const totalCal = rows.reduce((a, r) => a + (r[3] ?? 0), 0);
|
|
35
|
+
const totalDur = rows.reduce((a, r) => a + (r[2] ?? 0), 0);
|
|
36
|
+
const durationMissing = rows.every((r) => r[2] === null || r[2] === undefined);
|
|
37
|
+
let longestStreak = 1;
|
|
38
|
+
let cur = 1;
|
|
39
|
+
for (let i = 1; i < dates.length; i++) {
|
|
40
|
+
if (daysBetween(dates[i - 1], dates[i]) === 1) {
|
|
41
|
+
cur += 1;
|
|
42
|
+
longestStreak = Math.max(longestStreak, cur);
|
|
43
|
+
}
|
|
44
|
+
else
|
|
45
|
+
cur = 1;
|
|
46
|
+
}
|
|
47
|
+
let maxGap;
|
|
48
|
+
if (dates.length >= 2) {
|
|
49
|
+
maxGap = 0;
|
|
50
|
+
for (let i = 1; i < dates.length; i++)
|
|
51
|
+
maxGap = Math.max(maxGap, daysBetween(dates[i - 1], dates[i]) - 1);
|
|
52
|
+
}
|
|
53
|
+
else
|
|
54
|
+
maxGap = span - 1;
|
|
55
|
+
return ok({
|
|
56
|
+
daysWithExercise: dates.length,
|
|
57
|
+
totalDays: span,
|
|
58
|
+
coveragePct: round((dates.length / span) * 100),
|
|
59
|
+
totalCalories: totalCal,
|
|
60
|
+
totalMinutes: totalDur,
|
|
61
|
+
avgCaloriesPerDay: round(totalCal / span),
|
|
62
|
+
avgMinutesPerDay: durationMissing ? null : round(totalDur / span),
|
|
63
|
+
longestStreakDays: longestStreak,
|
|
64
|
+
longestRestDays: maxGap,
|
|
65
|
+
alert: maxGap >= 7 ? '建议动起来' : null,
|
|
66
|
+
durationMissing,
|
|
67
|
+
}, '运动 ' + dates.length + '/' + span + ' 天,总消耗 ' + totalCal + ' 卡');
|
|
68
|
+
}
|
|
69
|
+
export function exerciseTypeBreakdown(db, startDate, endDate) {
|
|
70
|
+
const [start, end] = rangeOf(startDate, endDate);
|
|
71
|
+
const raw = db.prepare('SELECT exercise_type AS t, SUM(calories_burned) AS k, COUNT(*) AS n, SUM(duration_minutes) AS dur FROM exercise_log WHERE date >= ? AND date <= ? GROUP BY exercise_type ORDER BY SUM(calories_burned) DESC').all(start, end);
|
|
72
|
+
const rows = raw.map((r) => [r.t, r.k, r.n, r.dur]);
|
|
73
|
+
if (rows.length === 0)
|
|
74
|
+
return rejection('无运动记录(' + start + ' ~ ' + end + ')');
|
|
75
|
+
const totalCal = rows.reduce((a, r) => a + (r[1] ?? 0), 0);
|
|
76
|
+
const totalCnt = rows.reduce((a, r) => a + r[2], 0);
|
|
77
|
+
const totalDur = rows.reduce((a, r) => a + (r[3] ?? 0), 0);
|
|
78
|
+
const types = rows.map((r) => ({
|
|
79
|
+
type: r[0],
|
|
80
|
+
calories: r[1] ?? 0,
|
|
81
|
+
count: r[2],
|
|
82
|
+
minutes: r[3] ?? 0,
|
|
83
|
+
calPct: totalCal === 0 ? 0 : round(((r[1] ?? 0) / totalCal) * 100),
|
|
84
|
+
countPct: totalCnt === 0 ? 0 : round((r[2] / totalCnt) * 100),
|
|
85
|
+
minutesPct: totalDur === 0 ? 0 : round(((r[3] ?? 0) / totalDur) * 100),
|
|
86
|
+
}));
|
|
87
|
+
return ok({ totalCalories: totalCal, totalCount: totalCnt, totalMinutes: totalDur, types }, types.length + ' 种运动类型');
|
|
88
|
+
}
|
|
89
|
+
export function exerciseDeficitContribution(db, startDate, endDate) {
|
|
90
|
+
const [start, end] = rangeOf(startDate, endDate);
|
|
91
|
+
const exRow = db.prepare('SELECT SUM(calories_burned) AS v FROM exercise_log WHERE date >= ? AND date <= ?').get(start, end);
|
|
92
|
+
const inRow = db.prepare('SELECT SUM(calories) AS v FROM food_log WHERE date >= ? AND date <= ?').get(start, end);
|
|
93
|
+
const totalEx = exRow.v ?? 0;
|
|
94
|
+
const totalIntake = inRow.v ?? 0;
|
|
95
|
+
const span = daysBetween(start, end) + 1;
|
|
96
|
+
const currentWeight = latestWeightKg(db);
|
|
97
|
+
const bmr = currentWeight * 24 * getActivityFactor(profileActivityLevel(db));
|
|
98
|
+
const dietDeficit = bmr * span - totalIntake;
|
|
99
|
+
const totalDeficit = bmr * span - totalIntake + totalEx;
|
|
100
|
+
let dietPct;
|
|
101
|
+
let exPct;
|
|
102
|
+
if (totalDeficit === 0) {
|
|
103
|
+
dietPct = 50;
|
|
104
|
+
exPct = 50;
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
dietPct = (Math.abs(dietDeficit) / Math.abs(totalDeficit)) * 100;
|
|
108
|
+
exPct = (totalEx / Math.abs(totalDeficit)) * 100;
|
|
109
|
+
}
|
|
110
|
+
const evaluation = exPct < 15 ? '运动贡献偏低,建议增加运动比例' : exPct > 25 ? '运动贡献较高' : '运动贡献适中';
|
|
111
|
+
return ok({
|
|
112
|
+
dietDeficit: round(dietDeficit),
|
|
113
|
+
dietContribPct: round(dietPct),
|
|
114
|
+
exerciseDeficit: round(totalEx),
|
|
115
|
+
exerciseContribPct: round(exPct),
|
|
116
|
+
evaluation,
|
|
117
|
+
bmr: round(bmr),
|
|
118
|
+
currentWeight: Math.round(currentWeight * 10) / 10,
|
|
119
|
+
}, '运动贡献 ' + round(exPct) + '% — ' + evaluation);
|
|
120
|
+
}
|
|
121
|
+
function calcPlanWeek(targetISO, config) {
|
|
122
|
+
if (!config)
|
|
123
|
+
return null;
|
|
124
|
+
const diff = daysBetween(config.start_date, targetISO);
|
|
125
|
+
if (diff < 0)
|
|
126
|
+
return null;
|
|
127
|
+
const realWeek = Math.floor(diff / 7) + 1;
|
|
128
|
+
return ((realWeek - 1) % config.total_weeks) + 1;
|
|
129
|
+
}
|
|
130
|
+
function getDayPlan(db, targetISO) {
|
|
131
|
+
const cfg = db.prepare('SELECT total_weeks, start_date FROM workout_plan_config WHERE id = 1').get();
|
|
132
|
+
const config = cfg ? { total_weeks: cfg.total_weeks, start_date: cfg.start_date } : null;
|
|
133
|
+
const week = calcPlanWeek(targetISO, config);
|
|
134
|
+
const dow = new Date(targetISO + 'T12:00:00Z').getUTCDay();
|
|
135
|
+
const isoDow = dow === 0 ? 7 : dow;
|
|
136
|
+
if (week === null && config !== null) {
|
|
137
|
+
return { plan_week: null, config, sessions: [], unstarted: true };
|
|
138
|
+
}
|
|
139
|
+
const rows = db.prepare('SELECT session_label, time_start, time_end, is_rest_day, total_sets, movements FROM workout_plans WHERE week_number = ? AND day_of_week = ? ORDER BY session_index').all(week, isoDow);
|
|
140
|
+
return {
|
|
141
|
+
plan_week: week,
|
|
142
|
+
config,
|
|
143
|
+
sessions: rows.map((r) => ({ session_label: r[0], total_sets: r[4], movements: JSON.parse(r[5] || '[]') })),
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
function isoWeekKey(iso) {
|
|
147
|
+
const d = new Date(iso + 'T12:00:00Z');
|
|
148
|
+
const day = (d.getUTCDay() + 6) % 7;
|
|
149
|
+
const thursday = new Date(d.getTime() + (3 - day) * 86400000);
|
|
150
|
+
const year = thursday.getUTCFullYear();
|
|
151
|
+
const jan4 = new Date(Date.UTC(year, 0, 4));
|
|
152
|
+
const week = 1 + Math.round(((thursday.getTime() - jan4.getTime()) / 86400000 - 3 + ((jan4.getUTCDay() + 6) % 7)) / 7);
|
|
153
|
+
return year + '-W' + String(week).padStart(2, '0');
|
|
154
|
+
}
|
|
155
|
+
function weekLabel(mondayISO) {
|
|
156
|
+
const mon = new Date(mondayISO + 'T12:00:00Z');
|
|
157
|
+
const sun = new Date(mon.getTime() + 6 * 86400000);
|
|
158
|
+
const m1 = mon.getUTCMonth() + 1;
|
|
159
|
+
const d1 = mon.getUTCDate();
|
|
160
|
+
const m2 = sun.getUTCMonth() + 1;
|
|
161
|
+
const d2 = sun.getUTCDate();
|
|
162
|
+
return m1 === m2 ? m1 + '/' + d1 + '~' + d2 : m1 + '/' + d1 + '~' + m2 + '/' + d2;
|
|
163
|
+
}
|
|
164
|
+
export function exerciseReview(db, startDate, endDate) {
|
|
165
|
+
const [start, end] = rangeOf(startDate, endDate);
|
|
166
|
+
const results = new Map();
|
|
167
|
+
const volPlanWeek = new Map();
|
|
168
|
+
const volActualWeek = new Map();
|
|
169
|
+
const volPlanMov = new Map();
|
|
170
|
+
const volActualMov = new Map();
|
|
171
|
+
const volMovTotal = new Map();
|
|
172
|
+
const volWeeks = [];
|
|
173
|
+
const weekMonday = new Map();
|
|
174
|
+
const addVol = (m, k, v) => { m.set(k, (m.get(k) ?? 0) + v); };
|
|
175
|
+
for (let d = start; d <= end; d = shiftISODate(d, 1)) {
|
|
176
|
+
const wk = isoWeekKey(d);
|
|
177
|
+
if (volWeeks[volWeeks.length - 1] !== wk) {
|
|
178
|
+
volWeeks.push(wk);
|
|
179
|
+
weekMonday.set(wk, shiftISODate(d, -(((new Date(d + 'T12:00:00Z').getUTCDay() + 6) % 7))));
|
|
180
|
+
}
|
|
181
|
+
const plan = getDayPlan(db, d);
|
|
182
|
+
const planSessions = plan.sessions;
|
|
183
|
+
const planTotalSets = planSessions.reduce((a, s) => a + (s.total_sets ?? 0), 0);
|
|
184
|
+
const sessionLabels = planSessions.map((s) => s.session_label ?? '');
|
|
185
|
+
for (const s of planSessions) {
|
|
186
|
+
for (const m of s.movements ?? []) {
|
|
187
|
+
const name = m.name ?? '';
|
|
188
|
+
for (const st of m.sets ?? []) {
|
|
189
|
+
const w = Number(st.weight ?? 0);
|
|
190
|
+
const reps = Math.trunc(Number(st.reps ?? 0));
|
|
191
|
+
if (!(w > 0) || !(reps > 0))
|
|
192
|
+
continue;
|
|
193
|
+
const unit = st.unit ?? '';
|
|
194
|
+
if (unit && unit !== 'kg')
|
|
195
|
+
continue;
|
|
196
|
+
const ton = w * reps;
|
|
197
|
+
addVol(volPlanWeek, wk, ton);
|
|
198
|
+
addVol(volPlanMov, wk + '\u0000' + name, ton);
|
|
199
|
+
const t = volMovTotal.get(name) ?? { plan: 0, actual: 0 };
|
|
200
|
+
t.plan += ton;
|
|
201
|
+
volMovTotal.set(name, t);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
const exRaw = db.prepare('SELECT exercise_type AS t, duration_minutes AS dur, calories_burned AS k, reps AS r, load_kg AS l FROM exercise_log WHERE date = ? ORDER BY exercise_type').all(d);
|
|
206
|
+
const exRows = exRaw.map((r) => [r.t, r.dur, r.k, r.r, r.l]);
|
|
207
|
+
const actual = new Map();
|
|
208
|
+
for (const r of exRows) {
|
|
209
|
+
const etype = r[0];
|
|
210
|
+
const a = actual.get(etype) ?? { sets: 0, calories: 0, minutes: 0 };
|
|
211
|
+
a.sets += 1;
|
|
212
|
+
a.calories += r[2] ?? 0;
|
|
213
|
+
a.minutes += r[1] ?? 0;
|
|
214
|
+
actual.set(etype, a);
|
|
215
|
+
const load = r[4] ?? 0;
|
|
216
|
+
const reps = r[3] ?? 0;
|
|
217
|
+
if (load > 0 && reps > 0) {
|
|
218
|
+
const ton = load * reps;
|
|
219
|
+
addVol(volActualWeek, wk, ton);
|
|
220
|
+
addVol(volActualMov, wk + '\u0000' + etype, ton);
|
|
221
|
+
const t = volMovTotal.get(etype) ?? { plan: 0, actual: 0 };
|
|
222
|
+
t.actual += ton;
|
|
223
|
+
volMovTotal.set(etype, t);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
const actualTotalSets = [...actual.values()].reduce((a, v) => a + v.sets, 0);
|
|
227
|
+
const anomalies = [];
|
|
228
|
+
let note = null;
|
|
229
|
+
let completionRate = null;
|
|
230
|
+
if (plan.unstarted) {
|
|
231
|
+
note = '计划尚未开始(起始 ' + plan.config.start_date + ')';
|
|
232
|
+
}
|
|
233
|
+
else if (planTotalSets === 0 && actualTotalSets === 0) {
|
|
234
|
+
note = '休息日 / 无计划无实绩';
|
|
235
|
+
}
|
|
236
|
+
else if (planTotalSets === 0 && actualTotalSets > 0) {
|
|
237
|
+
note = '计划休息但实做了 ' + actualTotalSets + ' 组';
|
|
238
|
+
anomalies.push({ type: 'rest_but_done', msg: '⚠️ ' + note });
|
|
239
|
+
}
|
|
240
|
+
else if (planTotalSets > 0 && actualTotalSets === 0) {
|
|
241
|
+
note = '计划有训练但完全未做';
|
|
242
|
+
completionRate = 0.0;
|
|
243
|
+
anomalies.push({ type: 'no_actual', msg: '❌ ' + note });
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
completionRate = (actualTotalSets / planTotalSets) * 100;
|
|
247
|
+
if (completionRate < 50)
|
|
248
|
+
anomalies.push({ type: 'low_completion', msg: '⚠️ 完成率仅 ' + Math.round(completionRate) + '%(实做 ' + actualTotalSets + '/' + planTotalSets + ' 组数)' });
|
|
249
|
+
else if (completionRate > 130)
|
|
250
|
+
anomalies.push({ type: 'over_completion', msg: '⚠️ 超额完成 (' + Math.round(completionRate) + '%)(' + actualTotalSets + '/' + planTotalSets + ' 组数)' });
|
|
251
|
+
}
|
|
252
|
+
results.set(d, {
|
|
253
|
+
date: d,
|
|
254
|
+
planWeek: plan.plan_week,
|
|
255
|
+
sessions: sessionLabels,
|
|
256
|
+
planTotalSets,
|
|
257
|
+
actualTotalSets,
|
|
258
|
+
caloriesBurned: [...actual.values()].reduce((a, v) => a + v.calories, 0),
|
|
259
|
+
completionRate,
|
|
260
|
+
anomalies,
|
|
261
|
+
note,
|
|
262
|
+
isRestDay: planTotalSets === 0 && actualTotalSets === 0,
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
const bySeverityCount = { low_completion: 0, over_completion: 0, no_actual: 0, rest_but_done: 0 };
|
|
266
|
+
for (const r of results.values()) {
|
|
267
|
+
for (const a of r.anomalies) {
|
|
268
|
+
if (a.type in bySeverityCount)
|
|
269
|
+
bySeverityCount[a.type] = bySeverityCount[a.type] + 1;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
const wlabel = (wk) => weekLabel(weekMonday.get(wk));
|
|
273
|
+
const volumeWeeks = volWeeks.map((wk) => ({ week: wk, label: wlabel(wk), plan: round(volPlanWeek.get(wk) ?? 0), actual: round(volActualWeek.get(wk) ?? 0), actualSets: 0 }));
|
|
274
|
+
const topNames = [...volMovTotal.keys()].sort((a, b) => {
|
|
275
|
+
const ta = volMovTotal.get(a);
|
|
276
|
+
const tb = volMovTotal.get(b);
|
|
277
|
+
return tb.plan + tb.actual - (ta.plan + ta.actual);
|
|
278
|
+
}).slice(0, 4);
|
|
279
|
+
const volumeMovements = topNames.map((name) => {
|
|
280
|
+
const t = volMovTotal.get(name);
|
|
281
|
+
return { name, planTotal: round(t.plan), actualTotal: round(t.actual), weeks: volWeeks.map((wk) => ({ week: wk, label: wlabel(wk), plan: round(volPlanMov.get(wk + '\u0000' + name) ?? 0), actual: round(volActualMov.get(wk + '\u0000' + name) ?? 0) })) };
|
|
282
|
+
});
|
|
283
|
+
const totalActualTon = [...volActualWeek.values()].reduce((a, b) => a + b, 0);
|
|
284
|
+
const days = [...results.values()];
|
|
285
|
+
const data = {};
|
|
286
|
+
for (const [k, v] of results)
|
|
287
|
+
data[k] = v;
|
|
288
|
+
data.__meta__ = {
|
|
289
|
+
bySeverityCount,
|
|
290
|
+
totalDays: days.length,
|
|
291
|
+
trainDays: days.filter((r) => !r.isRestDay).length,
|
|
292
|
+
restDays: days.filter((r) => r.isRestDay).length,
|
|
293
|
+
totalCalories: days.reduce((a, r) => a + (r.caloriesBurned ?? 0), 0),
|
|
294
|
+
volume: { weeks: volumeWeeks, movements: volumeMovements, totalPlan: round([...volPlanWeek.values()].reduce((a, b) => a + b, 0)), totalActual: round(totalActualTon), hasActual: totalActualTon > 0 },
|
|
295
|
+
};
|
|
296
|
+
return ok(data, '训练复盘 ' + days.length + ' 天');
|
|
297
|
+
}
|
|
298
|
+
//# sourceMappingURL=exercise.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exercise.js","sourceRoot":"","sources":["../../src/analysis/exercise.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** T7 #26 · 运动复核口径(对照老家 exercise_review CLI 范围语义 + T3 取数 + T5 衍生)。
|
|
2
|
+
*
|
|
3
|
+
* 老家 exercise_review.py 仅参数排期外壳,复核计算主体在 review_engine;
|
|
4
|
+
* TS 以 T3 listWindow 为行源、MET 口径为估算源,产出运动复核摘要(T8 渲染消费)。
|
|
5
|
+
* 软删除排除;缺失阻断不返空。
|
|
6
|
+
*/
|
|
7
|
+
import type { DatabaseSync } from 'node:sqlite';
|
|
8
|
+
export interface ExerciseTypeStat {
|
|
9
|
+
type: string;
|
|
10
|
+
sessions: number;
|
|
11
|
+
burned: number;
|
|
12
|
+
minutes: number;
|
|
13
|
+
}
|
|
14
|
+
export interface ExerciseReview {
|
|
15
|
+
start: string;
|
|
16
|
+
end: string;
|
|
17
|
+
days: number;
|
|
18
|
+
sessions: number;
|
|
19
|
+
activeDays: number;
|
|
20
|
+
totalBurned: number;
|
|
21
|
+
totalMinutes: number;
|
|
22
|
+
avgBurnedPerSession: number;
|
|
23
|
+
avgBurnedPerDay: number;
|
|
24
|
+
byCategory: Record<string, {
|
|
25
|
+
sessions: number;
|
|
26
|
+
burned: number;
|
|
27
|
+
}>;
|
|
28
|
+
byType: ExerciseTypeStat[];
|
|
29
|
+
estimatedCheck: {
|
|
30
|
+
reported: number;
|
|
31
|
+
estimated: number;
|
|
32
|
+
deviationPct: number | null;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export declare function buildExerciseReview(db: DatabaseSync, start: string, end: string, bodyWeightKg?: number): ExerciseReview;
|
|
36
|
+
//# sourceMappingURL=exerciseReview.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exerciseReview.d.ts","sourceRoot":"","sources":["../../src/analysis/exerciseReview.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAKhD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;CACjE;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IACzC,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IACrC,WAAW,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAC1C,mBAAmB,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAC;IACrD,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjE,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B,cAAc,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;CACtF;AAED,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,YAAY,SAAK,GAAG,cAAc,CA4CnH"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { FetchError } from '../fetch/errors.js';
|
|
2
|
+
import { round2 } from '../kcal.js';
|
|
3
|
+
import { estimateCaloriesMet, inferCategory, listWindow } from '../fetch/exercise.js';
|
|
4
|
+
export function buildExerciseReview(db, start, end, bodyWeightKg = 70) {
|
|
5
|
+
const rows = listWindow(db, start, end);
|
|
6
|
+
if (!rows.length)
|
|
7
|
+
throw new FetchError(`无运动记录:${start} ~ ${end}`);
|
|
8
|
+
const daySet = new Set();
|
|
9
|
+
let totalBurned = 0, totalMinutes = 0;
|
|
10
|
+
const byCategory = {};
|
|
11
|
+
const byType = new Map();
|
|
12
|
+
let estSum = 0;
|
|
13
|
+
for (const r of rows) {
|
|
14
|
+
const date = String(r.date);
|
|
15
|
+
const type = String(r.exercise_type ?? '未知');
|
|
16
|
+
const burned = Number(r.calories_burned ?? 0);
|
|
17
|
+
const minutes = Number(r.duration_minutes ?? 0);
|
|
18
|
+
daySet.add(date);
|
|
19
|
+
totalBurned += burned;
|
|
20
|
+
totalMinutes += minutes;
|
|
21
|
+
const cat = inferCategory(type);
|
|
22
|
+
const c = byCategory[cat] ?? { sessions: 0, burned: 0 };
|
|
23
|
+
c.sessions++;
|
|
24
|
+
c.burned += burned;
|
|
25
|
+
byCategory[cat] = c;
|
|
26
|
+
const t = byType.get(type) ?? { type, sessions: 0, burned: 0, minutes: 0 };
|
|
27
|
+
t.sessions++;
|
|
28
|
+
t.burned += burned;
|
|
29
|
+
t.minutes += minutes;
|
|
30
|
+
byType.set(type, t);
|
|
31
|
+
const [est] = estimateCaloriesMet(type, bodyWeightKg, minutes || undefined, 3);
|
|
32
|
+
estSum += est;
|
|
33
|
+
}
|
|
34
|
+
const sessions = rows.length;
|
|
35
|
+
const days = daySet.size;
|
|
36
|
+
const deviationPct = estSum > 0 ? round2(Math.abs(totalBurned - estSum) / estSum) : null;
|
|
37
|
+
return {
|
|
38
|
+
start, end, days,
|
|
39
|
+
sessions,
|
|
40
|
+
activeDays: days,
|
|
41
|
+
totalBurned: round2(totalBurned),
|
|
42
|
+
totalMinutes,
|
|
43
|
+
avgBurnedPerSession: round2(totalBurned / sessions),
|
|
44
|
+
avgBurnedPerDay: round2(totalBurned / days),
|
|
45
|
+
byCategory,
|
|
46
|
+
byType: [...byType.values()].sort((a, b) => b.burned - a.burned),
|
|
47
|
+
estimatedCheck: { reported: round2(totalBurned), estimated: round2(estSum), deviationPct },
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=exerciseReview.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exerciseReview.js","sourceRoot":"","sources":["../../src/analysis/exerciseReview.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export declare const PRECISION_SUMMARY_FIELDS: string[];
|
|
2
|
+
export declare const PRECISION_SERIES_KEYS: string[];
|
|
3
|
+
export interface LeakReport {
|
|
4
|
+
path: string;
|
|
5
|
+
value: number;
|
|
6
|
+
}
|
|
7
|
+
export declare function findLeaks(data: {
|
|
8
|
+
summary?: Record<string, unknown>;
|
|
9
|
+
series?: Record<string, unknown>[];
|
|
10
|
+
}): LeakReport[];
|
|
11
|
+
export declare function assertNoLeak(data: {
|
|
12
|
+
summary?: Record<string, unknown>;
|
|
13
|
+
series?: Record<string, unknown>[];
|
|
14
|
+
}): void;
|
|
15
|
+
//# sourceMappingURL=precision.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"precision.d.ts","sourceRoot":"","sources":["../../src/analysis/precision.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,wBAAwB,UAIpC,CAAC;AAEF,eAAO,MAAM,qBAAqB,UAAc,CAAC;AAEjD,MAAM,WAAW,UAAU;IAAG,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE;AAE3D,wBAAgB,SAAS,CAAC,IAAI,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;CAAE,GAAG,UAAU,EAAE,CAevH;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;CAAE,GAAG,IAAI,CAOlH"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/** T7 #26 · 精度守卫(对照老家 check_decimal_precision.py 白名单与规则)。
|
|
2
|
+
*
|
|
3
|
+
* 白名单:summary.trend_value/start_avg/end_avg/weekday_avg/weekend_avg/avg/total_calorie/avg_calorie、
|
|
4
|
+
* series[*].calorie/items[*].calorie。规则:round(2) 后差 ≤ 1e-9,否则抛错。
|
|
5
|
+
* T8-T10 渲染序列化前调 assertNoLeak;T1 kcal.isClean 为单值版。
|
|
6
|
+
*/
|
|
7
|
+
import { isClean } from '../kcal.js';
|
|
8
|
+
export const PRECISION_SUMMARY_FIELDS = [
|
|
9
|
+
'trend_value', 'trendValue', 'start_avg', 'startAvg', 'end_avg', 'endAvg',
|
|
10
|
+
'weekday_avg', 'weekdayAvg', 'weekend_avg', 'weekendAvg', 'weekend_diff', 'weekendDiff',
|
|
11
|
+
'avg', 'total_calorie', 'totalCalorie', 'avg_calorie', 'avgCalorie',
|
|
12
|
+
];
|
|
13
|
+
export const PRECISION_SERIES_KEYS = ['calorie'];
|
|
14
|
+
export function findLeaks(data) {
|
|
15
|
+
const leaks = [];
|
|
16
|
+
const summary = data.summary ?? {};
|
|
17
|
+
for (const f of PRECISION_SUMMARY_FIELDS) {
|
|
18
|
+
const v = summary[f];
|
|
19
|
+
if (typeof v === 'number' && !isClean(v))
|
|
20
|
+
leaks.push({ path: `summary.${f}`, value: v });
|
|
21
|
+
}
|
|
22
|
+
for (let i = 0; i < (data.series ?? []).length; i++) {
|
|
23
|
+
const item = data.series[i];
|
|
24
|
+
for (const k of PRECISION_SERIES_KEYS) {
|
|
25
|
+
const v = item[k];
|
|
26
|
+
if (typeof v === 'number' && !isClean(v))
|
|
27
|
+
leaks.push({ path: `series[${i}].${k}`, value: v });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return leaks;
|
|
31
|
+
}
|
|
32
|
+
export function assertNoLeak(data) {
|
|
33
|
+
const leaks = findLeaks(data);
|
|
34
|
+
if (leaks.length) {
|
|
35
|
+
throw new Error('[calorie] 小数精度泄漏: ' + leaks.map((l) => `${l.path}=${l.value}`).join(', '));
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=precision.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"precision.js","sourceRoot":"","sources":["../../src/analysis/precision.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAErC,MAAM,CAAC,MAAM,wBAAwB,GAAG;IACtC,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ;IACzE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,cAAc,EAAE,aAAa;IACvF,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,aAAa,EAAE,YAAY;CACpE,CAAC;AAEF,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,SAAS,CAAC,CAAC;AAIjD,MAAM,UAAU,SAAS,CAAC,IAA+E;IACvG,MAAM,KAAK,GAAiB,EAAE,CAAC;IAC/B,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC;IACnC,KAAK,MAAM,CAAC,IAAI,wBAAwB,EAAE,CAAC;QACzC,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QACrB,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;YAAE,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,WAAW,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;IAC3F,CAAC;IACD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACpD,MAAM,IAAI,GAAI,IAAI,CAAC,MAAoC,CAAC,CAAC,CAA4B,CAAC;QACtF,KAAK,MAAM,CAAC,IAAI,qBAAqB,EAAE,CAAC;YACtC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;gBAAE,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;QAChG,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAA+E;IAC1G,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAC9B,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CACb,oBAAoB,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAC3E,CAAC;IACJ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** T5 #24 · 分析结果包络(对照老家 as_dict=True 形状)。
|
|
2
|
+
*
|
|
3
|
+
* 有规则口径 → {status:'ok', data, message};无数据 → {status:'error', data:null,
|
|
4
|
+
* message} 明确缺失阻断(验收:无规则 rejection 有明确缺失阻断,不返空)。
|
|
5
|
+
* 输入非法(坏日期/坏枚举)→ 抛 FetchError。L6 开放式分析留 AI,不在此建模。
|
|
6
|
+
*/
|
|
7
|
+
export type AnalysisStatus = 'ok' | 'error';
|
|
8
|
+
export interface AnalysisResult<T> {
|
|
9
|
+
status: AnalysisStatus;
|
|
10
|
+
data: T | null;
|
|
11
|
+
message: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function ok<T>(data: T, message: string): AnalysisResult<T>;
|
|
14
|
+
export declare function rejection<T = never>(message: string): AnalysisResult<T>;
|
|
15
|
+
//# sourceMappingURL=result.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result.d.ts","sourceRoot":"","sources":["../../src/analysis/result.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,MAAM,cAAc,GAAG,IAAI,GAAG,OAAO,CAAC;AAE5C,MAAM,WAAW,cAAc,CAAC,CAAC;IAC/B,MAAM,EAAE,cAAc,CAAC;IACvB,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,cAAc,CAAC,CAAC,CAAC,CAEjE;AAED,wBAAgB,SAAS,CAAC,CAAC,GAAG,KAAK,EAAE,OAAO,EAAE,MAAM,GAAG,cAAc,CAAC,CAAC,CAAC,CAEvE"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** T5 #24 · 分析结果包络(对照老家 as_dict=True 形状)。
|
|
2
|
+
*
|
|
3
|
+
* 有规则口径 → {status:'ok', data, message};无数据 → {status:'error', data:null,
|
|
4
|
+
* message} 明确缺失阻断(验收:无规则 rejection 有明确缺失阻断,不返空)。
|
|
5
|
+
* 输入非法(坏日期/坏枚举)→ 抛 FetchError。L6 开放式分析留 AI,不在此建模。
|
|
6
|
+
*/
|
|
7
|
+
export function ok(data, message) {
|
|
8
|
+
return { status: 'ok', data, message };
|
|
9
|
+
}
|
|
10
|
+
export function rejection(message) {
|
|
11
|
+
return { status: 'error', data: null, message };
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=result.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result.js","sourceRoot":"","sources":["../../src/analysis/result.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAUH,MAAM,UAAU,EAAE,CAAI,IAAO,EAAE,OAAe;IAC5C,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,SAAS,CAAY,OAAe;IAClD,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,IAAS,EAAE,OAAO,EAAE,CAAC;AACvD,CAAC"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/** T5 #24 · 周复盘引擎取数+衍生(对照老家 scripts/review_engine.py 数据段)。
|
|
2
|
+
*
|
|
3
|
+
* parseRange / query5dims / derive(含 complete_days 今日污染隔离、缺口、
|
|
4
|
+
* 营养达标率、异常天规则)。_render_weight_trend_svg 与 extract_summary 属
|
|
5
|
+
* 渲染/HTML 层,归 T8-T10,本票 weightTrendSvg 回 null 并在 meta 注明(待裁决)。
|
|
6
|
+
* today 可注入(默认 UTC 日)。
|
|
7
|
+
*/
|
|
8
|
+
import type { DatabaseSync } from 'node:sqlite';
|
|
9
|
+
export type RangeType = 'day' | 'week' | 'month' | 'year';
|
|
10
|
+
export declare function parseRange(rangeArg?: string | null, rangeType?: RangeType, today?: string): [string, string];
|
|
11
|
+
export interface DayIntake {
|
|
12
|
+
date: string;
|
|
13
|
+
totalCalorie: number;
|
|
14
|
+
totalProtein: number;
|
|
15
|
+
totalCarbs: number;
|
|
16
|
+
totalFat: number;
|
|
17
|
+
mealCount: number;
|
|
18
|
+
}
|
|
19
|
+
export interface DayBurn {
|
|
20
|
+
date: string;
|
|
21
|
+
totalBurned: number;
|
|
22
|
+
totalMinutes: number | null;
|
|
23
|
+
types: string | null;
|
|
24
|
+
categories: string | null;
|
|
25
|
+
}
|
|
26
|
+
export interface WeightLogPoint {
|
|
27
|
+
date: string;
|
|
28
|
+
weightKg: number;
|
|
29
|
+
}
|
|
30
|
+
export interface FitnessSession {
|
|
31
|
+
date: string;
|
|
32
|
+
weekNumber: number;
|
|
33
|
+
dayOfWeek: number;
|
|
34
|
+
sessionLabel: string;
|
|
35
|
+
isRestDay: number;
|
|
36
|
+
totalSets: number | null;
|
|
37
|
+
movements: unknown[];
|
|
38
|
+
}
|
|
39
|
+
export interface TopFood {
|
|
40
|
+
name: string;
|
|
41
|
+
totalCal: number;
|
|
42
|
+
cnt: number;
|
|
43
|
+
avgCalPerMeal: number;
|
|
44
|
+
}
|
|
45
|
+
export interface FiveDims {
|
|
46
|
+
range: {
|
|
47
|
+
start: string;
|
|
48
|
+
end: string;
|
|
49
|
+
days: number;
|
|
50
|
+
};
|
|
51
|
+
dailyIntake: DayIntake[];
|
|
52
|
+
dailyBurn: DayBurn[];
|
|
53
|
+
weightLogs: WeightLogPoint[];
|
|
54
|
+
fitnessPlan: FitnessSession[] | null;
|
|
55
|
+
userProfile: {
|
|
56
|
+
heightCm: number | null;
|
|
57
|
+
age: number;
|
|
58
|
+
gender: string;
|
|
59
|
+
activityLevel: string;
|
|
60
|
+
};
|
|
61
|
+
nutritionTargets: Record<string, unknown>;
|
|
62
|
+
topFoods: TopFood[];
|
|
63
|
+
}
|
|
64
|
+
export declare function query5dims(db: DatabaseSync, start: string, end: string): FiveDims;
|
|
65
|
+
export interface NutritionMatch {
|
|
66
|
+
daysCount: number;
|
|
67
|
+
matchedDays: number;
|
|
68
|
+
matchRatePct: number;
|
|
69
|
+
summary: string;
|
|
70
|
+
}
|
|
71
|
+
export declare function nutritionMatchRate(dailyIntake: DayIntake[], targets: Record<string, unknown>): NutritionMatch;
|
|
72
|
+
export interface AnomalyDay {
|
|
73
|
+
date: string | null;
|
|
74
|
+
type: string;
|
|
75
|
+
value: number;
|
|
76
|
+
target: number;
|
|
77
|
+
diff: string;
|
|
78
|
+
severity: string;
|
|
79
|
+
}
|
|
80
|
+
export interface DerivedReview extends FiveDims {
|
|
81
|
+
todayPartial: {
|
|
82
|
+
intake: DayIntake | null;
|
|
83
|
+
burn: DayBurn | null;
|
|
84
|
+
};
|
|
85
|
+
completeDaysCount: number;
|
|
86
|
+
tdee: number;
|
|
87
|
+
intakeSummary: {
|
|
88
|
+
daysCount: number;
|
|
89
|
+
isPartial?: boolean;
|
|
90
|
+
avgCalorie: number;
|
|
91
|
+
avgProtein: number;
|
|
92
|
+
avgCarbs: number;
|
|
93
|
+
avgFat: number;
|
|
94
|
+
};
|
|
95
|
+
burnSummary: {
|
|
96
|
+
daysCount: number;
|
|
97
|
+
isPartial?: boolean;
|
|
98
|
+
totalBurned: number;
|
|
99
|
+
avgBurned: number;
|
|
100
|
+
totalMinutes: number;
|
|
101
|
+
};
|
|
102
|
+
macroRatio: {
|
|
103
|
+
proteinPct: number;
|
|
104
|
+
carbsPct: number;
|
|
105
|
+
fatPct: number;
|
|
106
|
+
};
|
|
107
|
+
weeklyDeficit: number;
|
|
108
|
+
avgDailyDeficit: number;
|
|
109
|
+
theoreticalWeightLoss: number;
|
|
110
|
+
nutritionMatch: NutritionMatch;
|
|
111
|
+
weightTrendSvg: null;
|
|
112
|
+
weightTrendMeta: {
|
|
113
|
+
deferred: string;
|
|
114
|
+
};
|
|
115
|
+
anomalyDays: AnomalyDay[];
|
|
116
|
+
}
|
|
117
|
+
export declare function deriveReview(db: DatabaseSync, dims: FiveDims, today?: string): DerivedReview;
|
|
118
|
+
//# sourceMappingURL=review.d.ts.map
|