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,149 @@
|
|
|
1
|
+
import { MEAL_WINDOWS, inferMealType, listMeals } from '../fetch/diet.js';
|
|
2
|
+
import { buildSeries, resolveWindow, seriesCount } from '../analysis/series.js';
|
|
3
|
+
import { analyzePair, PAIRS } from '../analysis/cross.js';
|
|
4
|
+
import { buildDeficitData } from '../analysis/deficit.js';
|
|
5
|
+
import { buildTrendData } from '../analysis/trend.js';
|
|
6
|
+
import { dietCalorieTrend, dietDeficitAnalysis, dietMacroRatio } from '../analysis/diet.js';
|
|
7
|
+
import { FetchError } from '../fetch/errors.js';
|
|
8
|
+
import { round2 } from '../kcal.js';
|
|
9
|
+
import { CalorieRenderError } from './errors.js';
|
|
10
|
+
import { MEAL_BUCKETS } from './diet.js';
|
|
11
|
+
export const COMBINED_PAIRS = Object.keys(PAIRS);
|
|
12
|
+
function assertDate(s) {
|
|
13
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(s) || Number.isNaN(Date.parse(s + 'T12:00:00Z'))) {
|
|
14
|
+
throw new CalorieRenderError('bad-input', '日期非法: ' + String(s));
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/** 组合分析盘(combined_analysis):pair + 窗口别名,series→analyzePair 直通。 */
|
|
18
|
+
export function buildCombinedAnalysis(db, pair, window = '30d', start, end, today) {
|
|
19
|
+
if (!(pair in PAIRS))
|
|
20
|
+
throw new CalorieRenderError('bad-input', '未知配对 ' + String(pair) + ',可选: ' + Object.keys(PAIRS).join(', '));
|
|
21
|
+
let s;
|
|
22
|
+
let e;
|
|
23
|
+
if (start && end) {
|
|
24
|
+
assertDate(start);
|
|
25
|
+
assertDate(end);
|
|
26
|
+
if (start > end)
|
|
27
|
+
throw new CalorieRenderError('bad-input', 'start 不得晚于 end');
|
|
28
|
+
s = start;
|
|
29
|
+
e = end;
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
try {
|
|
33
|
+
const [rs, re] = resolveWindow(window, start ?? null, end ?? null, today);
|
|
34
|
+
s = rs;
|
|
35
|
+
e = re;
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
if (err instanceof FetchError)
|
|
39
|
+
throw new CalorieRenderError('bad-input', err.message);
|
|
40
|
+
throw err;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const series = buildSeries(db, s, e);
|
|
44
|
+
let analysis;
|
|
45
|
+
try {
|
|
46
|
+
analysis = analyzePair(series, pair, db, window);
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
if (err instanceof FetchError)
|
|
50
|
+
throw new CalorieRenderError('bad-input', err.message);
|
|
51
|
+
throw err;
|
|
52
|
+
}
|
|
53
|
+
if (analysis.aCount === 0 && analysis.bCount === 0) {
|
|
54
|
+
throw new CalorieRenderError('missing-data', '组合无数据(' + pair + ',' + s + ' ~ ' + e + ')');
|
|
55
|
+
}
|
|
56
|
+
return { pair, window, start: s, end: e, series, analysis };
|
|
57
|
+
}
|
|
58
|
+
/** 缺口盘(calorie_deficit):T7 薄壳直通,空窗转 missing-data。 */
|
|
59
|
+
export function buildDeficitPlate(db, start, end) {
|
|
60
|
+
assertDate(start);
|
|
61
|
+
assertDate(end);
|
|
62
|
+
if (start > end)
|
|
63
|
+
throw new CalorieRenderError('bad-input', 'start 不得晚于 end');
|
|
64
|
+
try {
|
|
65
|
+
return buildDeficitData(db, start, end);
|
|
66
|
+
}
|
|
67
|
+
catch (e) {
|
|
68
|
+
if (e instanceof FetchError)
|
|
69
|
+
throw new CalorieRenderError('missing-data', e.message);
|
|
70
|
+
throw e;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/** 趋势盘(render_analysis 趋势段):T7 薄壳直通。 */
|
|
74
|
+
export function buildTrendPlate(db, start, end) {
|
|
75
|
+
assertDate(start);
|
|
76
|
+
assertDate(end);
|
|
77
|
+
if (start > end)
|
|
78
|
+
throw new CalorieRenderError('bad-input', 'start 不得晚于 end');
|
|
79
|
+
try {
|
|
80
|
+
return buildTrendData(db, start, end);
|
|
81
|
+
}
|
|
82
|
+
catch (e) {
|
|
83
|
+
if (e instanceof FetchError)
|
|
84
|
+
throw new CalorieRenderError('missing-data', e.message);
|
|
85
|
+
throw e;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function assertMealWindows() {
|
|
89
|
+
for (const k of ['早餐', '午餐', '下午茶', '晚餐', '夜宵', '加餐']) {
|
|
90
|
+
if (!MEAL_WINDOWS[k])
|
|
91
|
+
throw new CalorieRenderError('missing-data', 'MEAL_WINDOWS 缺餐别: ' + k);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function bucketOf(time) {
|
|
95
|
+
if (!time)
|
|
96
|
+
return '其他';
|
|
97
|
+
const m = inferMealType(time);
|
|
98
|
+
if (m === '早餐' || m === '午餐' || m === '晚餐')
|
|
99
|
+
return m;
|
|
100
|
+
if (m === '下午茶' || m === '夜宵')
|
|
101
|
+
return '加餐';
|
|
102
|
+
return '其他';
|
|
103
|
+
}
|
|
104
|
+
/** 饮食复盘盘(diet_review/nutrition_ratio):T5 三件套 + 按餐桶汇总(MEAL_WINDOWS 同源)。 */
|
|
105
|
+
export function buildDietReview(db, start, end) {
|
|
106
|
+
assertDate(start);
|
|
107
|
+
assertDate(end);
|
|
108
|
+
if (start > end)
|
|
109
|
+
throw new CalorieRenderError('bad-input', 'start 不得晚于 end');
|
|
110
|
+
assertMealWindows();
|
|
111
|
+
const trend = dietCalorieTrend(db, start, end);
|
|
112
|
+
const macro = dietMacroRatio(db, start, end);
|
|
113
|
+
const deficit = dietDeficitAnalysis(db, start, end);
|
|
114
|
+
if (trend.status !== 'ok' && macro.status !== 'ok' && deficit.status !== 'ok') {
|
|
115
|
+
throw new CalorieRenderError('missing-data', '无饮食记录(' + start + ' ~ ' + end + ')');
|
|
116
|
+
}
|
|
117
|
+
const series = buildSeries(db, start, end);
|
|
118
|
+
const loggedDays = seriesCount(series, 'calories');
|
|
119
|
+
if (loggedDays === 0)
|
|
120
|
+
throw new CalorieRenderError('missing-data', '无饮食记录(' + start + ' ~ ' + end + ')');
|
|
121
|
+
const agg = new Map();
|
|
122
|
+
for (const d of series) {
|
|
123
|
+
let rows;
|
|
124
|
+
try {
|
|
125
|
+
rows = listMeals(db, d.date).filter((r) => r.food_name !== '💧水');
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
for (const r of rows) {
|
|
131
|
+
const b = bucketOf(r.time);
|
|
132
|
+
if (b === '其他')
|
|
133
|
+
continue;
|
|
134
|
+
let e = agg.get(b);
|
|
135
|
+
if (!e) {
|
|
136
|
+
e = { days: new Set(), total: 0 };
|
|
137
|
+
agg.set(b, e);
|
|
138
|
+
}
|
|
139
|
+
e.days.add(d.date);
|
|
140
|
+
e.total += r.calories;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
const byMeal = MEAL_BUCKETS.map((meal) => {
|
|
144
|
+
const e = agg.get(meal);
|
|
145
|
+
return { meal, days: e ? e.days.size : 0, totalCalories: e ? round2(e.total) : 0 };
|
|
146
|
+
});
|
|
147
|
+
return { start, end, trend, macro, deficit, loggedDays, byMeal };
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=analysisPlate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analysisPlate.js","sourceRoot":"","sources":["../../src/render/analysisPlate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/** #41 · 身体细节读链 render 数据(体成分看/围度看)。
|
|
2
|
+
*
|
|
3
|
+
* 数据源全复用 fetch/body.ts,不自算:
|
|
4
|
+
* 体成分=listCompositions + trendComposition(source 缺省取 latestSource);
|
|
5
|
+
* 围度=listMeasurements + trendMeasurement(metric 可选)。
|
|
6
|
+
* 录链(composition-add/remove、measure-add/remove)已由 #40 写键承接,本票只补看链。
|
|
7
|
+
* 空库一律 missing-data,不返空数组冒充正常。
|
|
8
|
+
*/
|
|
9
|
+
import type { DatabaseSync } from 'node:sqlite';
|
|
10
|
+
export interface BodyCompositionView {
|
|
11
|
+
source: string | null;
|
|
12
|
+
items: Record<string, unknown>[];
|
|
13
|
+
total: number;
|
|
14
|
+
trend: {
|
|
15
|
+
date: string;
|
|
16
|
+
avgPct: number;
|
|
17
|
+
n: number;
|
|
18
|
+
}[];
|
|
19
|
+
latestPct: number | null;
|
|
20
|
+
}
|
|
21
|
+
export declare function buildBodyCompositionView(db: DatabaseSync, opts?: {
|
|
22
|
+
days?: number;
|
|
23
|
+
source?: string;
|
|
24
|
+
limit?: number;
|
|
25
|
+
}): BodyCompositionView;
|
|
26
|
+
export declare function buildBodyCompositionCompare(db: DatabaseSync, fromDate: string, toDate: string, source?: string): Record<string, unknown>;
|
|
27
|
+
export interface BodyMeasureView {
|
|
28
|
+
metric: string | null;
|
|
29
|
+
items: Record<string, unknown>[];
|
|
30
|
+
total: number;
|
|
31
|
+
trend: {
|
|
32
|
+
date: string;
|
|
33
|
+
avgVal: number;
|
|
34
|
+
n: number;
|
|
35
|
+
}[];
|
|
36
|
+
latestVal: number | null;
|
|
37
|
+
}
|
|
38
|
+
export declare function buildBodyMeasureView(db: DatabaseSync, opts?: {
|
|
39
|
+
metric?: string;
|
|
40
|
+
days?: number;
|
|
41
|
+
limit?: number;
|
|
42
|
+
dateFrom?: string;
|
|
43
|
+
dateTo?: string;
|
|
44
|
+
}): BodyMeasureView;
|
|
45
|
+
export declare function buildBodyMeasureCompare(db: DatabaseSync, date1: string, date2: string): {
|
|
46
|
+
date1: string;
|
|
47
|
+
date2: string;
|
|
48
|
+
deltas: Record<string, {
|
|
49
|
+
before: number;
|
|
50
|
+
after: number;
|
|
51
|
+
delta: number;
|
|
52
|
+
}>;
|
|
53
|
+
nCompared: number;
|
|
54
|
+
};
|
|
55
|
+
//# sourceMappingURL=bodyPlate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bodyPlate.d.ts","sourceRoot":"","sources":["../../src/render/bodyPlate.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAKhD,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACrD,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,wBAAgB,wBAAwB,CACtC,EAAE,EAAE,YAAY,EAChB,IAAI,GAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAC5D,mBAAmB,CAsBrB;AAED,wBAAgB,2BAA2B,CACzC,EAAE,EAAE,YAAY,EAChB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,GACd,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAUzB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACrD,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,wBAAgB,oBAAoB,CAClC,EAAE,EAAE,YAAY,EAChB,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,GAChG,eAAe,CAiCjB;AAED,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG;IACvF,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;CAC3H,CAUA"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { compareCompositions, compareMeasurements, listCompositions, listMeasurements, trendComposition, trendMeasurement } from '../fetch/body.js';
|
|
2
|
+
import { FetchError } from '../fetch/errors.js';
|
|
3
|
+
import { CalorieRenderError } from './errors.js';
|
|
4
|
+
export function buildBodyCompositionView(db, opts = {}) {
|
|
5
|
+
const days = opts.days ?? 90;
|
|
6
|
+
if (!Number.isInteger(days) || days < 1 || days > 365) {
|
|
7
|
+
throw new CalorieRenderError('bad-input', 'days 须为 1..365 整数');
|
|
8
|
+
}
|
|
9
|
+
const limit = opts.limit ?? 20;
|
|
10
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 100) {
|
|
11
|
+
throw new CalorieRenderError('bad-input', 'limit 须为 1..100 整数');
|
|
12
|
+
}
|
|
13
|
+
try {
|
|
14
|
+
const items = listCompositions(db, { days, source: opts.source, limit });
|
|
15
|
+
if (items.length === 0)
|
|
16
|
+
throw new CalorieRenderError('missing-data', '无体成分记录(近' + days + '天)');
|
|
17
|
+
const trend = trendComposition(db, days, opts.source);
|
|
18
|
+
const first = items[0];
|
|
19
|
+
const latestPct = typeof first?.body_fat_pct === 'number' ? first.body_fat_pct : null;
|
|
20
|
+
const source = typeof first?.source === 'string' ? first.source : (opts.source ?? null);
|
|
21
|
+
return { source, items, total: items.length, trend, latestPct };
|
|
22
|
+
}
|
|
23
|
+
catch (e) {
|
|
24
|
+
if (e instanceof CalorieRenderError)
|
|
25
|
+
throw e;
|
|
26
|
+
if (e instanceof FetchError)
|
|
27
|
+
throw new CalorieRenderError('missing-data', e.message);
|
|
28
|
+
throw e;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export function buildBodyCompositionCompare(db, fromDate, toDate, source) {
|
|
32
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(fromDate) || !/^\d{4}-\d{2}-\d{2}$/.test(toDate)) {
|
|
33
|
+
throw new CalorieRenderError('bad-input', '日期非法:' + fromDate + ' ~ ' + toDate);
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
return compareCompositions(db, fromDate, toDate, source);
|
|
37
|
+
}
|
|
38
|
+
catch (e) {
|
|
39
|
+
if (e instanceof FetchError)
|
|
40
|
+
throw new CalorieRenderError('missing-data', e.message);
|
|
41
|
+
throw e;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export function buildBodyMeasureView(db, opts = {}) {
|
|
45
|
+
const days = opts.days ?? 90;
|
|
46
|
+
if (!Number.isInteger(days) || days < 1 || days > 365) {
|
|
47
|
+
throw new CalorieRenderError('bad-input', 'days 须为 1..365 整数');
|
|
48
|
+
}
|
|
49
|
+
const limit = opts.limit ?? 20;
|
|
50
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 100) {
|
|
51
|
+
throw new CalorieRenderError('bad-input', 'limit 须为 1..100 整数');
|
|
52
|
+
}
|
|
53
|
+
if (opts.dateFrom && !/^\d{4}-\d{2}-\d{2}$/.test(opts.dateFrom))
|
|
54
|
+
throw new CalorieRenderError('bad-input', 'dateFrom 非法:' + opts.dateFrom);
|
|
55
|
+
if (opts.dateTo && !/^\d{4}-\d{2}-\d{2}$/.test(opts.dateTo))
|
|
56
|
+
throw new CalorieRenderError('bad-input', 'dateTo 非法:' + opts.dateTo);
|
|
57
|
+
try {
|
|
58
|
+
const items = listMeasurements(db, {
|
|
59
|
+
metric: opts.metric,
|
|
60
|
+
days: opts.dateFrom && opts.dateTo ? undefined : days,
|
|
61
|
+
dateFrom: opts.dateFrom,
|
|
62
|
+
dateTo: opts.dateTo,
|
|
63
|
+
limit,
|
|
64
|
+
});
|
|
65
|
+
if (items.length === 0)
|
|
66
|
+
throw new CalorieRenderError('missing-data', '无围度记录');
|
|
67
|
+
let trend = [];
|
|
68
|
+
let latestVal = null;
|
|
69
|
+
if (opts.metric) {
|
|
70
|
+
trend = trendMeasurement(db, opts.metric, days);
|
|
71
|
+
const v = items[0][opts.metric];
|
|
72
|
+
latestVal = typeof v === 'number' ? v : null;
|
|
73
|
+
}
|
|
74
|
+
return { metric: opts.metric ?? null, items, total: items.length, trend, latestVal };
|
|
75
|
+
}
|
|
76
|
+
catch (e) {
|
|
77
|
+
if (e instanceof CalorieRenderError)
|
|
78
|
+
throw e;
|
|
79
|
+
if (e instanceof FetchError)
|
|
80
|
+
throw new CalorieRenderError('missing-data', e.message);
|
|
81
|
+
throw e;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
export function buildBodyMeasureCompare(db, date1, date2) {
|
|
85
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(date1) || !/^\d{4}-\d{2}-\d{2}$/.test(date2)) {
|
|
86
|
+
throw new CalorieRenderError('bad-input', '日期非法:' + date1 + ' ~ ' + date2);
|
|
87
|
+
}
|
|
88
|
+
try {
|
|
89
|
+
return compareMeasurements(db, date1, date2);
|
|
90
|
+
}
|
|
91
|
+
catch (e) {
|
|
92
|
+
if (e instanceof FetchError)
|
|
93
|
+
throw new CalorieRenderError('missing-data', e.message);
|
|
94
|
+
throw e;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=bodyPlate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bodyPlate.js","sourceRoot":"","sources":["../../src/render/bodyPlate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/** T8 #27 · 饮食视图数据(diet_overview + meal_distribution 对应)。
|
|
2
|
+
*
|
|
3
|
+
* 死规矩 1:餐别窗口跟 diet.ts MEAL_WINDOWS(已合入方向,老家旧口径作废)。
|
|
4
|
+
* 本文件不硬编码任何小时区间:单条归类走 inferMealType(与 MEAL_WINDOWS 同源),
|
|
5
|
+
* 加餐聚合 = 下午茶 + 夜宵(diet.ts 注释口径),窗口存在性断言 MEAL_WINDOWS。
|
|
6
|
+
* 死规矩 3:日数列用 T5 series(buildSeries/seriesSum/seriesAvg),不自造 SUM。
|
|
7
|
+
* 饮食总览趋势段用 T7 buildTrendData(同样以 series 为源)。空窗即 missing-data。
|
|
8
|
+
*/
|
|
9
|
+
import type { DatabaseSync } from 'node:sqlite';
|
|
10
|
+
import type { DaySeries } from '../analysis/series.js';
|
|
11
|
+
import type { TrendData } from '../analysis/trend.js';
|
|
12
|
+
/** 四桶分布(触发词 meal_dist_all 口径):早餐/午餐/晚餐/加餐(加餐=下午茶+夜宵)。 */
|
|
13
|
+
export declare const MEAL_BUCKETS: readonly ["早餐", "午餐", "晚餐", "加餐"];
|
|
14
|
+
export type MealBucket = (typeof MEAL_BUCKETS)[number];
|
|
15
|
+
export interface MealSlice {
|
|
16
|
+
meal: MealBucket;
|
|
17
|
+
count: number;
|
|
18
|
+
calories: number;
|
|
19
|
+
pct: number;
|
|
20
|
+
}
|
|
21
|
+
export interface MealDistribution {
|
|
22
|
+
date: string;
|
|
23
|
+
totalCalories: number;
|
|
24
|
+
slices: MealSlice[];
|
|
25
|
+
/** 明细 5 类(早/午/下午茶/晚/夜宵)+ 加餐聚合,供模板展开。 */
|
|
26
|
+
detail: {
|
|
27
|
+
meal: string;
|
|
28
|
+
count: number;
|
|
29
|
+
calories: number;
|
|
30
|
+
}[];
|
|
31
|
+
}
|
|
32
|
+
/** C4 #43 · 空日零分布(窗内有数但尾日无记录时回零,不掀整窗 missing)。 */
|
|
33
|
+
export declare function zeroMealDistribution(date: string): MealDistribution;
|
|
34
|
+
export declare function buildMealDistribution(db: DatabaseSync, date: string): MealDistribution;
|
|
35
|
+
export interface DietOverview {
|
|
36
|
+
start: string;
|
|
37
|
+
end: string;
|
|
38
|
+
days: number;
|
|
39
|
+
loggedDays: number;
|
|
40
|
+
totalCalories: number;
|
|
41
|
+
avgCalories: number | null;
|
|
42
|
+
calorieGoal: number;
|
|
43
|
+
trend: TrendData;
|
|
44
|
+
series: DaySeries[];
|
|
45
|
+
}
|
|
46
|
+
export declare function buildDietOverview(db: DatabaseSync, start: string, end: string): DietOverview;
|
|
47
|
+
//# sourceMappingURL=diet.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diet.d.ts","sourceRoot":"","sources":["../../src/render/diet.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAEvD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAItD,yDAAyD;AACzD,eAAO,MAAM,YAAY,mCAAoC,CAAC;AAC9D,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;AAEvD,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,wCAAwC;IACxC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC7D;AAiBD,kDAAkD;AAClD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,CAQnE;AAED,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,GAAG,gBAAgB,CAgCtF;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,SAAS,CAAC;IACjB,MAAM,EAAE,SAAS,EAAE,CAAC;CACrB;AAED,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,YAAY,CAoB5F"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { MEAL_WINDOWS, inferMealType, listMeals } from '../fetch/diet.js';
|
|
2
|
+
import { buildSeries, seriesAvg, seriesCount, seriesSum } from '../analysis/series.js';
|
|
3
|
+
import { buildTrendData } from '../analysis/trend.js';
|
|
4
|
+
import { round2 } from '../kcal.js';
|
|
5
|
+
import { CalorieRenderError } from './errors.js';
|
|
6
|
+
/** 四桶分布(触发词 meal_dist_all 口径):早餐/午餐/晚餐/加餐(加餐=下午茶+夜宵)。 */
|
|
7
|
+
export const MEAL_BUCKETS = ['早餐', '午餐', '晚餐', '加餐'];
|
|
8
|
+
function assertMealWindows() {
|
|
9
|
+
for (const k of ['早餐', '午餐', '下午茶', '晚餐', '夜宵', '加餐']) {
|
|
10
|
+
if (!MEAL_WINDOWS[k])
|
|
11
|
+
throw new CalorieRenderError('missing-data', 'MEAL_WINDOWS 缺餐别: ' + k);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/** 单条时间归桶:先 inferMealType(MEAL_WINDOWS 同源),再把下午茶/夜宵并入加餐。 */
|
|
15
|
+
function bucketOf(time) {
|
|
16
|
+
if (!time)
|
|
17
|
+
return '其他';
|
|
18
|
+
const m = inferMealType(time);
|
|
19
|
+
if (m === '早餐' || m === '午餐' || m === '晚餐')
|
|
20
|
+
return m;
|
|
21
|
+
if (m === '下午茶' || m === '夜宵')
|
|
22
|
+
return '加餐';
|
|
23
|
+
return '其他';
|
|
24
|
+
}
|
|
25
|
+
/** C4 #43 · 空日零分布(窗内有数但尾日无记录时回零,不掀整窗 missing)。 */
|
|
26
|
+
export function zeroMealDistribution(date) {
|
|
27
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(date))
|
|
28
|
+
throw new CalorieRenderError('bad-input', '日期非法: ' + date);
|
|
29
|
+
return {
|
|
30
|
+
date,
|
|
31
|
+
totalCalories: 0,
|
|
32
|
+
slices: MEAL_BUCKETS.map((meal) => ({ meal, count: 0, calories: 0, pct: 0 })),
|
|
33
|
+
detail: [],
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export function buildMealDistribution(db, date) {
|
|
37
|
+
assertMealWindows();
|
|
38
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(date))
|
|
39
|
+
throw new CalorieRenderError('bad-input', '日期非法: ' + date);
|
|
40
|
+
const rows = listMeals(db, date).filter((r) => r.food_name !== '💧水');
|
|
41
|
+
// 日总量以 T5 series 为准(死规矩 3),分组小计仅做桶内累加,分母不自造。
|
|
42
|
+
const series = buildSeries(db, date, date);
|
|
43
|
+
const totalCalories = series[0]?.calories ?? 0;
|
|
44
|
+
if (rows.length === 0 || totalCalories === 0) {
|
|
45
|
+
throw new CalorieRenderError('missing-data', '无饮食记录(' + date + ')');
|
|
46
|
+
}
|
|
47
|
+
const byBucket = new Map();
|
|
48
|
+
const byDetail = new Map();
|
|
49
|
+
for (const r of rows) {
|
|
50
|
+
const b = bucketOf(r.time);
|
|
51
|
+
if (b === '其他')
|
|
52
|
+
continue;
|
|
53
|
+
const e = byBucket.get(b) ?? { count: 0, calories: 0 };
|
|
54
|
+
e.count += 1;
|
|
55
|
+
e.calories += r.calories;
|
|
56
|
+
byBucket.set(b, e);
|
|
57
|
+
const dm = inferMealType(String(r.time ?? ''));
|
|
58
|
+
const d = byDetail.get(dm) ?? { count: 0, calories: 0 };
|
|
59
|
+
d.count += 1;
|
|
60
|
+
d.calories += r.calories;
|
|
61
|
+
byDetail.set(dm, d);
|
|
62
|
+
}
|
|
63
|
+
const total = totalCalories;
|
|
64
|
+
const slices = MEAL_BUCKETS.map((meal) => {
|
|
65
|
+
const e = byBucket.get(meal) ?? { count: 0, calories: 0 };
|
|
66
|
+
return { meal, count: e.count, calories: round2(e.calories), pct: total ? round2((e.calories / total) * 100) : 0 };
|
|
67
|
+
});
|
|
68
|
+
const detail = [...byDetail.entries()].map(([meal, e]) => ({ meal, count: e.count, calories: round2(e.calories) }));
|
|
69
|
+
return { date, totalCalories, slices, detail };
|
|
70
|
+
}
|
|
71
|
+
export function buildDietOverview(db, start, end) {
|
|
72
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(start) || !/^\d{4}-\d{2}-\d{2}$/.test(end)) {
|
|
73
|
+
throw new CalorieRenderError('bad-input', '起止日期非法: ' + start + ' ~ ' + end);
|
|
74
|
+
}
|
|
75
|
+
if (start > end)
|
|
76
|
+
throw new CalorieRenderError('bad-input', 'start 不得晚于 end');
|
|
77
|
+
const series = buildSeries(db, start, end);
|
|
78
|
+
const loggedDays = seriesCount(series, 'calories');
|
|
79
|
+
if (loggedDays === 0)
|
|
80
|
+
throw new CalorieRenderError('missing-data', '无饮食记录(' + start + ' ~ ' + end + ')');
|
|
81
|
+
const trend = buildTrendData(db, start, end);
|
|
82
|
+
return {
|
|
83
|
+
start,
|
|
84
|
+
end,
|
|
85
|
+
days: series.length,
|
|
86
|
+
loggedDays,
|
|
87
|
+
totalCalories: seriesSum(series, 'calories'),
|
|
88
|
+
avgCalories: seriesAvg(series, 'calories'),
|
|
89
|
+
calorieGoal: series[0]?.calorieGoal ?? 1800,
|
|
90
|
+
trend,
|
|
91
|
+
series,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=diet.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diet.js","sourceRoot":"","sources":["../../src/render/diet.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAEvF,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACpC,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEjD,yDAAyD;AACzD,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAU,CAAC;AAkB9D,SAAS,iBAAiB;IACxB,KAAK,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;QACtD,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC;YAAE,MAAM,IAAI,kBAAkB,CAAC,cAAc,EAAE,oBAAoB,GAAG,CAAC,CAAC,CAAC;IAC/F,CAAC;AACH,CAAC;AAED,4DAA4D;AAC5D,SAAS,QAAQ,CAAC,IAAmB;IACnC,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,MAAM,CAAC,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IAC9B,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,IAAI;QAAE,OAAO,CAAC,CAAC;IACrD,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAC3C,OAAO,IAAI,CAAC;AACd,CAAC;AAED,kDAAkD;AAClD,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,MAAM,IAAI,kBAAkB,CAAC,WAAW,EAAE,QAAQ,GAAG,IAAI,CAAC,CAAC;IAClG,OAAO;QACL,IAAI;QACJ,aAAa,EAAE,CAAC;QAChB,MAAM,EAAE,YAAY,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QAC7E,MAAM,EAAE,EAAE;KACX,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,EAAgB,EAAE,IAAY;IAClE,iBAAiB,EAAE,CAAC;IACpB,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,MAAM,IAAI,kBAAkB,CAAC,WAAW,EAAE,QAAQ,GAAG,IAAI,CAAC,CAAC;IAClG,MAAM,IAAI,GAAG,SAAS,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,KAAK,CAAC,CAAC;IACtE,6CAA6C;IAC7C,MAAM,MAAM,GAAG,WAAW,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC3C,MAAM,aAAa,GAAG,MAAM,CAAC,CAAC,CAAC,EAAE,QAAQ,IAAI,CAAC,CAAC;IAC/C,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,aAAa,KAAK,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,kBAAkB,CAAC,cAAc,EAAE,QAAQ,GAAG,IAAI,GAAG,GAAG,CAAC,CAAC;IACtE,CAAC;IACD,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA+C,CAAC;IACxE,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA+C,CAAC;IACxE,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QACrB,MAAM,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC3B,IAAI,CAAC,KAAK,IAAI;YAAE,SAAS;QACzB,MAAM,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;QACvD,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;QACb,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,QAAQ,CAAC;QACzB,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACnB,MAAM,EAAE,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC;QAC/C,MAAM,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;QACxD,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;QACb,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,QAAQ,CAAC;QACzB,QAAQ,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IACtB,CAAC;IACD,MAAM,KAAK,GAAG,aAAa,CAAC;IAC5B,MAAM,MAAM,GAAgB,YAAY,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACpD,MAAM,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;QAC1D,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACrH,CAAC,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,CAAC,GAAG,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;IACpH,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AACjD,CAAC;AAcD,MAAM,UAAU,iBAAiB,CAAC,EAAgB,EAAE,KAAa,EAAE,GAAW;IAC5E,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QAC3E,MAAM,IAAI,kBAAkB,CAAC,WAAW,EAAE,UAAU,GAAG,KAAK,GAAG,KAAK,GAAG,GAAG,CAAC,CAAC;IAC9E,CAAC;IACD,IAAI,KAAK,GAAG,GAAG;QAAE,MAAM,IAAI,kBAAkB,CAAC,WAAW,EAAE,gBAAgB,CAAC,CAAC;IAC7E,MAAM,MAAM,GAAG,WAAW,CAAC,EAAE,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;IAC3C,MAAM,UAAU,GAAG,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACnD,IAAI,UAAU,KAAK,CAAC;QAAE,MAAM,IAAI,kBAAkB,CAAC,cAAc,EAAE,QAAQ,GAAG,KAAK,GAAG,KAAK,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC;IACzG,MAAM,KAAK,GAAG,cAAc,CAAC,EAAE,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;IAC7C,OAAO;QACL,KAAK;QACL,GAAG;QACH,IAAI,EAAE,MAAM,CAAC,MAAM;QACnB,UAAU;QACV,aAAa,EAAE,SAAS,CAAC,MAAM,EAAE,UAAU,CAAC;QAC5C,WAAW,EAAE,SAAS,CAAC,MAAM,EAAE,UAAU,CAAC;QAC1C,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,WAAW,IAAI,IAAI;QAC3C,KAAK;QACL,MAAM;KACP,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/** T8 #27 + T9 #28 + T10 #29 + #41 · 主视图 + 目标分析盘 + 身体照片 + 读链补齐 envelope 键表(T11 出口复用同一命名;仅 type-only 消费 link-core)。
|
|
2
|
+
*
|
|
3
|
+
* T8 四主视图 key 另立 view 域:calorie.view_home / calorie.view_diet / calorie.view_exercise / calorie.view_goal(一律 stat)。
|
|
4
|
+
* T9 目标分析盘:goal_config / goal_recommend / goal_weight / goal_progress / goal_status / combined / deficit / diet_review / health / ranking / library / search(一律 stat)。
|
|
5
|
+
* T10 照片视图 key 挂 calorie.photo_*(gallery/compare/viewer/gif/help/receipt 六键,就近形状)。
|
|
6
|
+
*/
|
|
7
|
+
import type { EnvelopeShape } from 'base-link-core';
|
|
8
|
+
export declare const VIEW_KEYS: {
|
|
9
|
+
readonly home: string;
|
|
10
|
+
readonly diet: string;
|
|
11
|
+
readonly exercise: string;
|
|
12
|
+
readonly goal: string;
|
|
13
|
+
readonly goalConfig: string;
|
|
14
|
+
readonly goalRecommend: string;
|
|
15
|
+
readonly goalWeight: string;
|
|
16
|
+
readonly goalProgress: string;
|
|
17
|
+
readonly goalStatus: string;
|
|
18
|
+
readonly combined: string;
|
|
19
|
+
readonly deficit: string;
|
|
20
|
+
readonly dietReview: string;
|
|
21
|
+
readonly health: string;
|
|
22
|
+
readonly ranking: string;
|
|
23
|
+
readonly library: string;
|
|
24
|
+
readonly search: string;
|
|
25
|
+
readonly weight: string;
|
|
26
|
+
readonly weightHistory: string;
|
|
27
|
+
readonly weightCompare: string;
|
|
28
|
+
readonly weightReview: string;
|
|
29
|
+
readonly volatility: string;
|
|
30
|
+
readonly bodyComposition: string;
|
|
31
|
+
readonly bodyMeasure: string;
|
|
32
|
+
readonly plan: string;
|
|
33
|
+
readonly planWizard: string;
|
|
34
|
+
readonly exerciseGoal: string;
|
|
35
|
+
readonly goalExpiring: string;
|
|
36
|
+
readonly goalPredict: string;
|
|
37
|
+
readonly goalVsActual: string;
|
|
38
|
+
readonly predict: string;
|
|
39
|
+
readonly anomaly: string;
|
|
40
|
+
readonly contraindication: string;
|
|
41
|
+
readonly dedupe: string;
|
|
42
|
+
readonly profile: string;
|
|
43
|
+
};
|
|
44
|
+
export type ViewName = keyof typeof VIEW_KEYS;
|
|
45
|
+
export declare const VIEW_SHAPES: Record<ViewName, EnvelopeShape>;
|
|
46
|
+
export declare function viewShapeFor(key: string): EnvelopeShape;
|
|
47
|
+
/** stat 载荷守卫:metrics 须全 number(envelope 全字段前置校验,坏载荷不进渲染)。 */
|
|
48
|
+
export declare function assertStatMetrics(metrics: Record<string, unknown>): asserts metrics is Record<string, number>;
|
|
49
|
+
export declare const PHOTO_VIEW_KEYS: {
|
|
50
|
+
readonly gallery: string;
|
|
51
|
+
readonly viewer: string;
|
|
52
|
+
readonly compare: string;
|
|
53
|
+
readonly receipt: string;
|
|
54
|
+
readonly gif: string;
|
|
55
|
+
readonly help: string;
|
|
56
|
+
};
|
|
57
|
+
export type PhotoViewName = keyof typeof PHOTO_VIEW_KEYS;
|
|
58
|
+
export declare const PHOTO_VIEW_SHAPES: Record<PhotoViewName, EnvelopeShape>;
|
|
59
|
+
export declare function photoShapeFor(key: string): EnvelopeShape;
|
|
60
|
+
//# sourceMappingURL=envelope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"envelope.d.ts","sourceRoot":"","sources":["../../src/render/envelope.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAIpD,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmCZ,CAAC;AACX,MAAM,MAAM,QAAQ,GAAG,MAAM,OAAO,SAAS,CAAC;AAE9C,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,QAAQ,EAAE,aAAa,CAmCvD,CAAC;AAEF,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,CAIvD;AAED,6DAA6D;AAC7D,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAM7G;AAED,eAAO,MAAM,eAAe;;;;;;;CAOlB,CAAC;AACX,MAAM,MAAM,aAAa,GAAG,MAAM,OAAO,eAAe,CAAC;AAEzD,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,aAAa,EAAE,aAAa,CAOlE,CAAC;AAEF,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,CAIxD"}
|