Files
healthlog/docs/tasks/items/categorical-value-dictionary.md
T
av 3df42afeca tasks: разобраны вопросы и набран спринт 2026-08-03
- открытых вопросов не осталось: три решения владельца доведены до берущегося
  вида, по entity-without-parsed-label принято хранить с NULL-меткой после Read API
- unseen-sections-check сжата до остатка — активная проверка появления секции;
  разбор невиденных секций из неё вынут, вслепую он не пишется
- спринт под целью parsing-and-storage: categorical-value-dictionary и
  unseen-sections-check, обеим написаны критерии приёмки с оракулами
2026-08-03 17:47:41 +03:00

5.2 KiB
Raw Blame History

Словарь категориальных значений → коды HealthKit

  • Секция: ядро
  • Зачем: Фазы сна и типы тренировок приходят строками русской локали — с экспортом Apple их не сверить
  • Теги: goal:parsing-and-storage

HAE отдаёт перечислимые значения строками локали телефона: «БДГ», «Сидячий образ жизни», «В помещении Ходьба». Родной экспорт Apple при этом говорит кодами (HKCategoryValueSleepAnalysisAsleepREM) — источники несопоставимы (находка 37).

Три следствия, и третье решающее: клиент угадывает словарь; смена языка телефона молча расколет историю; сверить покрытие экспортом нечем — а на этой сверке стоит устаревание нижнего слоя.

Решение (вариант «б»): строка хранится дословно, рядом кладётся выведенный код. Словарь ключуется парой (локаль, строка), локаль берётся из Accept-Language. Незнакомая строка → пустой код, а не догадка.

Словарь фаз сна уже выведен сопоставлением потока с экспортом за тот же период (находка 43) — составлять руками не нужно:

Основная → AsleepCore    Бодрствование → Awake    БДГ → AsleepREM
Глубокий → AsleepDeep    В кровати → InBed        Во сне → AsleepUnspecified

Тем же способом добираются heart_rate.context и типы тренировок.

Осложнение, всплывшее на истории экспортов: коды тоже не вечны. Одни и те же записи сна приезжают как …Asleep в экспорте 2021 года и как …AsleepUnspecified в экспорте 2026-го: Apple переименовала значение и переписывает историю при выгрузке (находка 43). Значит словарь должен переживать переименование самих кодов, иначе после обновления iOS история расколется вторично — уже на «стабильной» стороне. Простейшее решение: хранить код как есть, а эквивалентность старых и новых имён держать отдельной таблицей синонимов.

stateOfMind в словаре не нуждается — он и так шлёт коды HealthKit.

Критерии приёмки

  • фаза сна из потока («БДГ») и из экспорта Apple (HKCategoryValueSleepAnalysisAsleepREM) за один период сопоставляются напрямую — оракул: запрос на живой базе за период с известным перекрытием, ноль несопоставимых строк
  • строка сохранена дословно, код лежит рядом отдельным полем — оракул: тест разбора на реальном пакете HAE из internal/hae/testdata
  • незнакомая строка даёт пустой код, разбор не падает, а событие попадает в счётчик — оракул: тест на выдуманной фазе сна плюс проверка счётчика
  • переименование кода самой Apple (…Asleep…AsleepUnspecified, находка 43) не раскалывает историю — оракул: тест на паре синонимов, обе формы сходятся в один код
  • повторный прогон живого архива даёт то же состояние — оракул: task verify:archive

Критерий «/stats показывает строки без кода» снят при переоценке 2026-08-03: /stats ещё нет (stats-endpoint), и вешать приёмку на несуществующий оракул значит либо блокировать задачу, либо принять её непроверенной. Наблюдаемость закрывается счётчиком; показ в /stats — строка задачи наблюдаемости, а не этой.

Рамки

Схема трогается: у категориального значения появляется поле кода, плюс таблица синонимов. Дословную строку не заменяем и не нормализуем — инвариант «точки хранятся дословно». Пересборка обязана оставаться детерминированной, оракул тот же task verify:archive.