- backend: NestJS service with exercise/plan data - miniprogram: WeChat mini program client - exclude node_modules, dist, runtime data-store, local env
75 lines
3.6 KiB
JavaScript
75 lines
3.6 KiB
JavaScript
/**
|
||
* 数据分析脚本:扫描 exercises.json,输出各维度的去重取值与数量,
|
||
* 并报告哪些取值尚未被中文标签映射覆盖(便于补全 labels.ts)。
|
||
*
|
||
* 用法: node scripts/analyze.mjs [path-to-exercises.json]
|
||
*/
|
||
import * as fs from 'fs';
|
||
import * as path from 'path';
|
||
import { fileURLToPath } from 'url';
|
||
|
||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||
const DATA = process.argv[2] || path.join(__dirname, '..', 'src', 'exercises', 'data', 'exercises.json');
|
||
|
||
// 复用 TS 标签映射(用 require 不行,这里用一份 JS 镜像做缺失检测)
|
||
const BODY_PART_LABELS = {
|
||
back: '背部', cardio: '有氧心肺', chest: '胸部', 'lower arms': '前臂',
|
||
'lower legs': '小腿', neck: '颈部', shoulders: '肩部', 'upper arms': '上臂',
|
||
'upper legs': '大腿', waist: '腰腹',
|
||
};
|
||
const EQUIPMENT_LABELS = {
|
||
'body weight': '自重', dumbbell: '哑铃', barbell: '杠铃', kettlebell: '壶铃', cable: '钢索',
|
||
machine: '固定器械', band: '弹力带', 'exercise ball': '健身球', 'medicine ball': '药球',
|
||
'ez bar': 'EZ曲杆', 'foam roll': '泡沫轴', treadmill: '跑步机', 'stationary bike': '固定单车',
|
||
rope: '战绳', tire: '轮胎', skier: '滑雪训练器', 'sled machine': '雪橇机', roller: '滚轮',
|
||
'elliptical machine': '椭圆机', 'olympic barbell': '奥杆', 'decline bench': '下斜凳',
|
||
'flat bench': '平板凳', 'pull-up bar': '单杠', 'stability ball': '瑞士球', trx: 'TRX悬挂带',
|
||
'assistance machine': '辅助器械', 'bosu ball': 'BOSU半球', 'wheel roller': '健腹轮',
|
||
none: '无器械', other: '其他',
|
||
};
|
||
const MUSCLE_LABELS = {
|
||
biceps: '肱二头肌', triceps: '肱三头肌', forearms: '前臂', abs: '腹肌', abdominals: '腹肌',
|
||
obliques: '腹斜肌', pectorals: '胸大肌', chest: '胸部', quadriceps: '股四头肌',
|
||
hamstrings: '腘绳肌', glutes: '臀大肌', calves: '小腿', traps: '斜方肌', 'upper traps': '上斜方肌',
|
||
lats: '背阔肌', 'lower back': '下背', delts: '三角肌', shoulders: '肩部', hips: '髋部',
|
||
adductors: '内收肌', neck: '颈部', 'serratus anterior': '前锯肌', 'spinal erectors': '竖脊肌',
|
||
'hip flexors': '髋屈肌', 'middle back': '中背',
|
||
};
|
||
|
||
function tally(arr, keyFn) {
|
||
const m = new Map();
|
||
for (const x of arr) {
|
||
const k = keyFn(x);
|
||
if (!k) continue;
|
||
if (Array.isArray(k)) k.forEach((v) => m.set(v, (m.get(v) || 0) + 1));
|
||
else m.set(k, (m.get(k) || 0) + 1);
|
||
}
|
||
return m;
|
||
}
|
||
|
||
function report(name, map, labels) {
|
||
const missing = [];
|
||
const rows = Array.from(map.entries()).sort((a, b) => b[1] - a[1]);
|
||
console.log(`\n=== ${name} (${rows.length} 种) ===`);
|
||
for (const [v, c] of rows) {
|
||
const has = labels[v] ? '' : ' ⚠ 缺标签';
|
||
if (!labels[v]) missing.push(v);
|
||
console.log(` ${v.padEnd(22)} ${String(c).padStart(5)} ${labels[v] || ''}${has}`);
|
||
}
|
||
return missing;
|
||
}
|
||
|
||
const raw = JSON.parse(fs.readFileSync(DATA, 'utf-8'));
|
||
console.log(`总动作数: ${raw.length}`);
|
||
|
||
const eqMiss = report('EQUIPMENT', tally(raw, (e) => e.equipment), EQUIPMENT_LABELS);
|
||
const tMiss = report('TARGET', tally(raw, (e) => e.target), MUSCLE_LABELS);
|
||
const mgMiss = report('MUSCLE_GROUP', tally(raw, (e) => e.muscle_group), MUSCLE_LABELS);
|
||
const smMiss = report('SECONDARY_MUSCLES', tally(raw, (e) => e.secondary_muscles), MUSCLE_LABELS);
|
||
|
||
console.log('\n=== 缺失中文标签汇总(需补全 labels.ts)===');
|
||
console.log('equipment :', eqMiss.join(', ') || '(无)');
|
||
console.log('target :', tMiss.join(', ') || '(无)');
|
||
console.log('muscle_group:', mgMiss.join(', ') || '(无)');
|
||
console.log('secondary :', smMiss.join(', ') || '(无)');
|