Skip to content

feat(data): health-domain import/upsert support and CLI hygiene fixes - #333

Merged
liujuanjuan1984 merged 11 commits into
mainfrom
feat/body-measurement-import
Aug 28, 2026
Merged

feat(data): health-domain import/upsert support and CLI hygiene fixes#333
liujuanjuan1984 merged 11 commits into
mainfrom
feat/body-measurement-import

Conversation

@liujuanjuan1984

@liujuanjuan1984 liujuanjuan1984 commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

TL;DR

将 body-measurement、menstrual、menstrual-factor 与 sleep 纳入 data export/import/batch-*,为 body measurement 提供按 measured_at 的幂等 upsert 与 add --replace-existing,并通过活动行唯一索引、savepoint 与并发 winner 重试收敛重复写入风险。本轮独立审查进一步修正 active-row 匹配、非法 Decimal、menstrual-factor batch-update 缺失、唯一冲突错误处理、迁移覆盖、help 语义和 uv 升级文档偏差。

Closes #332

变更内容

1. 健康域 data namespace 支持

  • data export/import/batch-update/batch-delete 支持 body-measurement、menstrual、menstrual-factor 与 sleep。
  • 自然键 upsert 支持 body-measurement.measured_atmenstrual.log_datemenstrual-factor.name,仅匹配 active row。
  • menstrual snapshot 携带 factor_names,bundle 先写入全部 base row、再恢复关系;单资源导入要求相关 factor 已存在。
  • 补齐 menstrual-factor rename service 与 batch-update 映射,避免 parser 暴露合法 target 但运行时拒绝。

2. body measurement 幂等性与并发安全

  • add --replace-existing 在相同 measured_at active row 存在时更新,否则新增;省略的可选指标保持不变。
  • body_measurements 新增 active-row 部分唯一索引(SQLite/PostgreSQL),迁移保留最近更新记录并软删除旧重复。
  • service upsert 使用 savepoint;并发 writer 先插入时回退更新 winner。
  • 通用 natural-key import 在唯一冲突后仅当同 key active winner 已出现时重试更新;其他约束错误仍按行失败。
  • 普通 create/update 的时间冲突转换为 domain validation error,batch-update 的 IntegrityError 转换为行级失败。

3. Decimal 与 snapshot 稳健性

  • body-measurement CLI 数值参数保持原始字符串,服务层统一转 Decimal,避免先经过二进制 float。
  • 拒绝非法、NaN、Infinity 和量化后小于 0.01 kg 的体重;snapshot Decimal 解析同样拒绝非法或非有限值。
  • Decimal 列以 JSON number 导出,并通过 Decimal(str(value)) 恢复 canonical 数值。

4. CLI help 与统一校验文案

  • 去除同屏重复 help 文案并新增 duplicate lint,接入 pre-commit。
  • 修复多处重复 show 示例,并修正 update --measured-at 错误声称“省略时默认为当前时间”的语义。
  • --replace-existing 增加 help 示例。
  • 统一 CLI、service 与 Web API 的日期范围配对错误文案。

5. 文档与测试

  • README(en/zh)依据 uv 官方行为补充 upgrade 路径:普通安装使用 uv tool upgrade lifeos-cli;精确 pin 需重新 install @latest,并按需保留 extras。
  • 新增 SQLite migration roundtrip,验证历史重复软删除、部分唯一索引与 downgrade。
  • 增加 active-only natural-key、并发 winner retry、非法 Decimal、唯一冲突、menstrual-factor batch-update 与 help 语义回归覆盖。
  • 横向覆盖 Area、Vision、Person、Habit、BodyMeasurement、MenstrualDay、MenstrualFactor,验证同键 soft-deleted history 不参与 natural-key 匹配或 ambiguity 判定。

关联

验证

  • 本地 bash ./scripts/doctor.sh 通过:lint、mypy、dead-code、943 个非集成测试、77% 覆盖率、pip-audit 与 package build 均通过。
  • uv run python scripts/audit_cli_help.py --check-duplicates 在 English 与 Simplified Chinese locale 下均通过。
  • 新增 migration roundtrip 与全 natural-key soft-delete 回归测试通过。
  • 本机未配置 LIFEOS_TEST_DATABASE_URL,PostgreSQL CLI integration 按脚本约定跳过;推送 bbeb435 后的新一轮 GitHub Actions 6/6 checks 全部通过,包括 Python 3.11–3.14、Default Toolchain 与 PostgreSQL CLI Integration。

…h operations

Add body-measurement to the canonical data namespace with measured-at natural-key upsert, batch update/delete, and a --replace-existing add path for idempotent scale or external-app sync. CLI numeric input stays a string until the domain service converts it with Decimal(str(value)), and snapshot rows round-trip Decimal columns exactly.
Remove the measured-at note that repeated the --measured-at option help, trim the shared date-range note so it no longer restates the --date option help, and make show examples distinct. Add a help_audit lint that flags exact repeats and verbatim option-text copies within one command screen, wired into pre-commit.
Share one DATE_RANGE_TOGETHER_MESSAGE constant between the CLI date argument resolver, the body-measurement and habit services, and the web routers so the wording cannot drift.
Explain uv tool upgrade for unpinned installs and reinstall with @latest for exact-version pins, in both the English and Simplified Chinese READMEs.
Add a partial unique index on body_measurements.measured_at for active rows, mirroring the menstrual day/factor pattern, and soft-delete pre-existing duplicates in the migration. Make the upsert race-safe: on a concurrent-writer IntegrityError the savepoint rolls back and the winner's record is updated instead of raising.
Add menstrual, menstrual-factor, and sleep to data export/import/batch-* with log_date and name natural-key upserts (both DB-enforced by existing partial unique indexes). Menstrual day snapshots carry factor_names and import resolves them against existing active factors. Health domains now share the same machine-oriented import/export contract as body-measurement.
Catch IntegrityError per row in single-resource imports so duplicate natural keys (for example menstrual log_date or factor name) become row-level failures, and surface bundle-import constraint violations as a clean CLI error instead of a traceback.
@liujuanjuan1984 liujuanjuan1984 changed the title feat(data): body-measurement import/upsert and CLI help hygiene fixes feat(data): health-domain import/upsert support and CLI hygiene fixes Aug 28, 2026
@liujuanjuan1984

Copy link
Copy Markdown
Collaborator Author

本轮独立代码审查已完成并在 d7d61e2 收敛以下问题:

  • natural-key 查询未过滤 deleted_at,会把迁移产生的 soft-deleted body measurement history 计入匹配并导致 ambiguous;现仅匹配 active row。
  • data import --mode upsert 在并发唯一冲突后未回退更新 winner,与 issue 的原子 upsert 目标不一致;现仅在同自然键 active winner 确实出现时重试。
  • CLI 改收原始字符串后,非法 Decimal/NaN/Infinity 会抛未处理异常;现统一转为 domain/data validation error,并拒绝量化后为 0 kg 的输入。
  • data batch-update 暴露了 menstrual-factor target,但没有 update operation;现补齐 rename service、唯一性校验与映射。
  • body measurement create/update 与 batch-update 的唯一冲突可能 traceback/500;现转换为 domain 或行级失败。
  • 新增唯一索引迁移此前没有 migration roundtrip;现覆盖 dedupe、部分唯一索引与 downgrade。
  • update --measured-at help 错写“省略时默认为当前时间”,--replace-existing 也缺少示例;现已修正。
  • README 对 uv tool upgrade 的 extras 指引不够准确;现按 uv 官方“保留安装约束/设置”的行为修正。

本地 bash ./scripts/doctor.sh 已通过:942 个非集成测试、77.17% 覆盖率、lint/mypy/dead-code、pip-audit 与构建均通过。本机未配置 PostgreSQL 测试 URL,等待本次推送触发的 GitHub Actions PostgreSQL job 最终确认。

@liujuanjuan1984

Copy link
Copy Markdown
Collaborator Author

修正提交 d7d61e2 的新一轮 GitHub Actions 已全部通过(6/6):Default Toolchain、Python 3.11/3.12/3.13/3.14 runtime matrix、PostgreSQL CLI Integration 均为 success。当前独立审查未留下未解决的 blocking finding;PR 保持 Draft,是否转 Ready 由维护者决定。

@liujuanjuan1984

Copy link
Copy Markdown
Collaborator Author

TL;DR

已完成其它排重对象的软删横向审计;未发现新的生产代码遗漏。此前修正的通用 natural-key 查询会统一排除 deleted_at,本轮补齐全资源回归,防止未来仅 body-measurement 保持正确而其它对象回退。

审计结论

  • 检查了所有带业务唯一性的 soft-delete 模型及对应 service 冲突查询。Area、Tag、MenstrualDay、MenstrualFactor、BodyMeasurement、HabitAction、TimelogTemplate、FinanceTree、FinanceAsset、FinanceSnapshotEntry、FinanceRateSnapshotEntry 均使用 active-row partial unique index,显式排重查询也只检查 active row。
  • Vision、Person、Habit 的自然键查询同样由通用 active-row 条件保护;其中 Habit 同名 active row 属于既有允许行为,导入会保留歧义保护,不会任意选择一条记录。
  • FinanceTreeNode 的唯一 path 不是 partial index,但 path 由 node UUID 构成,软删后新节点不会复用旧 path,因此不构成重建冲突。

新增回归

  • 对全部 7 类 natural-key 资源构造“同键 soft-deleted history + active row”场景:Area、Vision、Person、Habit、BodyMeasurement、MenstrualDay、MenstrualFactor。
  • 逐项验证 upsert 只匹配 active row,不会把历史记录计入 ambiguity,也不会匹配或复活 soft-deleted history。

验证

  • uv run pytest tests/test_data_ops.py -q:35 passed。
  • bash ./scripts/doctor.sh:通过;943 个非集成测试、lint、mypy、dead-code、pip-audit 与 package build 均通过。
  • 本机未配置 LIFEOS_TEST_DATABASE_URL,PostgreSQL CLI integration 按仓库约定跳过,等待本次推送触发的远端检查补齐。

提交:bbeb435 test(data): cover soft-delete natural-key matching

@liujuanjuan1984
liujuanjuan1984 marked this pull request as ready for review August 28, 2026 06:28
@liujuanjuan1984
liujuanjuan1984 merged commit ae7181f into main Aug 28, 2026
6 checks passed
@liujuanjuan1984
liujuanjuan1984 deleted the feat/body-measurement-import branch August 28, 2026 06:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

身体测量(body-measurement)批量化/幂等导入路径与 CLI 卫生修复

1 participant