feat: разбор и хранение тренировок и состояния разума

- секции `workouts` и `stateOfMind` покрыты разбором: тренировка лежит одной
  строкой вместе с маршрутом и внутренними рядами, запись — по ключу `род + id`;
  миграция 00007 заводит обе таблицы и возвращает в очередь `partial`-доставки
  с этими ключами
- сущность заменяется целиком, но условно: приехавшая побеждает, если не теряет
  содержания сохранённой (множество ключей и длины верхнеуровневых массивов), а
  при равном содержании выигрывает версия из более поздней доставки ЖУРНАЛА —
  «побеждает приехавшая» было бы функцией порядка свёртки, и живая витрина
  расходилась бы с пересборкой молча
- отпечаток витрины покрывает тренировки и записи и снимается одним снимком
  базы; отчёт `reindex` считает «было и стало» по каждой единице хранения
This commit is contained in:
av
2026-08-02 13:05:16 +03:00
parent c28de9796e
commit f8200f7f80
47 changed files with 5817 additions and 301 deletions
+21
View File
@@ -12,6 +12,19 @@
переставлены с сохранением формы), даты (сдвинуты на постоянную величину),
имена устройств.
Для сущностей с собственным `id` вычищается дополнительно: сами
идентификаторы (псевдо-UUID той же формы), метки RFC 3339 и `route[].timestamp`
(сдвигаются, как и прочие даты), а также словарные значения `stateOfMind`
`kind`, `valenceClassification`, `labels`, `associations`. Последнее не
перестраховка: это измерение душевного состояния, самое чувствительное, что
есть в потоке. Значения подменяются другими кодами из того же словаря HealthKit,
поэтому форма (snake_case, строка против массива строк) сохраняется, а смысл —
нет. Побочный эффект подмены: в `labels` могут появиться повторы, которых HAE не
шлёт; разбору это безразлично.
Координаты маршрута вычищаются как обычные числа — они не отличаются от прочих
измерений и после подмены указывают в никуда.
| Файл | Что проверяет |
|---|---|
| `minute.json` | минутная доставка; плотные метрики минутные, одна (`apple_stand_hour`) часовая — классификация **по метрике**, а не по доставке; редкие наследуют минутный слой; суточная сводка сна |
@@ -20,7 +33,12 @@
| `mixed.json` | одна доставка с минутными, посекундными и часовыми метриками — перенастройка автоматизации |
| `heartbeat_series.json` | точка с `heartbeatSeries`: третий формат времени, серия проходит исходными байтами |
| `sparse_sleep.json` | доставка **без плотных метрик** (заголовок `Default` не спасает) и поэпизодный сон с задвоенной меткой — интервальная идентичность |
| `workout_route.json` | уличная тренировка с маршрутом и внутренними рядами; ряды урезаны `--limit`, форма точки маршрута сохранена |
| `workout_indoor.json` | тренировка без маршрута: набор полей зависит от типа (`temperature`, `humidity`, `intensity` вместо `route`, `avgSpeed`, `flightsClimbed`) |
| `state_of_mind.json` | `stateOfMind`: RFC 3339 в UTC, коды вместо переводов, поля `source` нет вовсе |
| `uncovered_sections.json` | одна доставка со всеми родами секций сразу — покрытыми и непокрытыми. Рукотворная: живой поток шлёт по одной секции за раз |
| `handmade_edge.json` | случаи, которых живой поток не даёт (см. ниже) |
| `handmade_entities.json` | краевые случаи сущностей: пустой, отсутствующий и слишком длинный `id`; неразбираемая метка и её отсутствие; неразбираемый `end`; нечисловая и отсутствующая длительность; две версии одного `id` в одном теле; элемент, не являющийся объектом |
`handmade_edge.json` собран руками, скриптом не воспроизводится:
@@ -43,3 +61,6 @@ python3 tmp/research/fixtures.py --list
python3 tmp/research/fixtures.py <файл из data/raw> --metrics a,b --limit 14 \
--out internal/hae/testdata/<имя>.json
```
`--limit` режет и внутренние ряды тренировки (маршрут, пульс, энергия): фикстуре
нужна форма точки ряда, а не 593 её экземпляра.
+110
View File
@@ -0,0 +1,110 @@
{
"_comment": "Рукотворная фикстура: краевые случаи сущностей с собственным id, которых живой поток не даёт. Скриптом tmp/research/fixtures.py не порождается, значения выдуманы целиком.",
"data": {
"workouts": [
{
"id": "",
"name": "Пустой идентификатор",
"start": "2025-06-05 10:00:00 +0300",
"end": "2025-06-05 10:10:00 +0300",
"duration": 600
},
{
"name": "Идентификатора нет вовсе",
"start": "2025-06-05 10:00:00 +0300",
"end": "2025-06-05 10:10:00 +0300"
},
{
"id": "00000000-0000-4000-8000-000000000001",
"name": "Метка не разбирается",
"start": "вчера вечером",
"end": "2025-06-05 10:10:00 +0300"
},
{
"id": "00000000-0000-4000-8000-000000000002",
"name": "Метки нет вовсе",
"duration": 60
},
{
"id": "00000000-0000-4000-8000-000000000003",
"name": "Конец не разбирается",
"start": "2025-06-05 11:00:00 +0300",
"end": "никогда",
"duration": 61.5
},
{
"id": "00000000-0000-4000-8000-000000000004",
"name": "Длительность не число",
"start": "2025-06-05 12:00:00 +0300",
"end": "2025-06-05 12:01:00 +0300",
"duration": "минута"
},
{
"id": "00000000-0000-4000-8000-000000000005",
"name": "Длительности нет",
"start": "2025-06-05 13:00:00 +0300",
"end": "2025-06-05 13:01:00 +0300"
},
{
"id": "00000000-0000-4000-8000-000000000006",
"name": "Начало берётся из date",
"date": "2025-06-05 14:00:00 +0300",
"duration": 30
},
{
"id": "00000000-0000-4000-8000-000000000007",
"name": "Две версии в одном теле, первая",
"start": "2025-06-05 15:00:00 +0300",
"end": "2025-06-05 15:30:00 +0300",
"duration": 1800,
"totalEnergy": {"qty": 100.5, "units": "kJ"}
},
{
"id": "00000000-0000-4000-8000-000000000007",
"name": "Две версии в одном теле, вторая",
"start": "2025-06-05 15:00:00 +0300",
"end": "2025-06-05 15:30:00 +0300",
"duration": 1800,
"totalEnergy": {"qty": 110.5, "units": "kJ"}
},
{
"id": "00000000-0000-4000-8000-000000000008-и-ещё-очень-длинный-хвост-который-заведомо-выходит-за-предел-длины-идентификатора-принятый-разбором-сущностей",
"name": "Идентификатор длиннее предела",
"start": "2025-06-05 16:00:00 +0300",
"end": "2025-06-05 16:01:00 +0300"
},
"не объект вовсе",
{
"id": "00000000-0000-4000-8000-000000000009",
"name": "Незнакомое поле и дословные литералы",
"start": "2025-06-05 17:00:00 +0300",
"end": "2025-06-05 17:05:00 +0300",
"duration": 300,
"невиданноеПоле": {"вложенное": [1.0, 9007199254740993, 0.123456789012345678]},
"isIndoor": false
}
],
"stateOfMind": [
{
"id": "00000000-0000-4000-8000-00000000000a",
"kind": "momentary_emotion",
"start": "2025-06-05T18:00:00Z",
"end": "2025-06-05T18:00:00Z",
"valence": 0.5,
"valenceClassification": "pleasant",
"labels": ["calm"],
"associations": ["hobbies"]
},
{
"id": "00000000-0000-4000-8000-00000000000b",
"kind": "daily_mood",
"start": "2025-06-05 21:00:00 +0300",
"end": "2025-06-06 21:00:00 +0300",
"valence": -0.25,
"valenceClassification": "slightly_unpleasant",
"labels": [],
"associations": []
}
]
}
}
+36
View File
@@ -0,0 +1,36 @@
{
"data": {
"stateOfMind": [
{
"associations": [
"fitness"
],
"valenceClassification": "slightly_unpleasant",
"valence": 0.021376147736425923,
"end": "2025-06-05T18:03:51Z",
"labels": [
"peaceful",
"peaceful"
],
"kind": "momentary_emotion",
"id": "50244944-3582-4508-7804-525425479700",
"start": "2025-06-05T18:03:51Z"
},
{
"kind": "daily_mood",
"end": "2025-06-05T18:03:17Z",
"id": "87868435-7832-4736-7633-078876535422",
"associations": [
"hobbies"
],
"start": "2025-06-05T18:03:17Z",
"valence": 0.12057562819279189,
"valenceClassification": "slightly_unpleasant",
"labels": [
"relieved",
"relieved"
]
}
]
}
}
+90 -15
View File
@@ -5,16 +5,56 @@
"name": "step_count",
"units": "count",
"data": [
{"date": "2025-06-05 09:00:00 +0300", "qty": 41.0, "source": "Device A"},
{"date": "2025-06-05 09:01:00 +0300", "qty": 17.0, "source": "Device A"},
{"date": "2025-06-05 09:02:00 +0300", "qty": 82.0, "source": "Device A"},
{"date": "2025-06-05 09:03:00 +0300", "qty": 5.0, "source": "Device A"},
{"date": "2025-06-05 09:04:00 +0300", "qty": 63.0, "source": "Device A"},
{"date": "2025-06-05 09:05:00 +0300", "qty": 28.0, "source": "Device A"},
{"date": "2025-06-05 09:06:00 +0300", "qty": 94.0, "source": "Device A"},
{"date": "2025-06-05 09:07:00 +0300", "qty": 12.0, "source": "Device A"},
{"date": "2025-06-05 09:08:00 +0300", "qty": 71.0, "source": "Device A"},
{"date": "2025-06-05 09:09:00 +0300", "qty": 36.0, "source": "Device A"}
{
"date": "2025-06-05 09:00:00 +0300",
"qty": 41.0,
"source": "Device A"
},
{
"date": "2025-06-05 09:01:00 +0300",
"qty": 17.0,
"source": "Device A"
},
{
"date": "2025-06-05 09:02:00 +0300",
"qty": 82.0,
"source": "Device A"
},
{
"date": "2025-06-05 09:03:00 +0300",
"qty": 5.0,
"source": "Device A"
},
{
"date": "2025-06-05 09:04:00 +0300",
"qty": 63.0,
"source": "Device A"
},
{
"date": "2025-06-05 09:05:00 +0300",
"qty": 28.0,
"source": "Device A"
},
{
"date": "2025-06-05 09:06:00 +0300",
"qty": 94.0,
"source": "Device A"
},
{
"date": "2025-06-05 09:07:00 +0300",
"qty": 12.0,
"source": "Device A"
},
{
"date": "2025-06-05 09:08:00 +0300",
"qty": 71.0,
"source": "Device A"
},
{
"date": "2025-06-05 09:09:00 +0300",
"qty": 36.0,
"source": "Device A"
}
]
}
],
@@ -26,11 +66,28 @@
"end": "2025-06-05 08:30:00 +0300",
"duration": 1800,
"heartRateData": [
{"date": "2025-06-05 08:00:07 +0300", "Min": 91.0, "Avg": 94.5, "Max": 98.0, "units": "count/min"},
{"date": "2025-06-05 08:00:21 +0300", "Min": 93.0, "Avg": 95.5, "Max": 99.0, "units": "count/min"}
{
"date": "2025-06-05 08:00:07 +0300",
"Min": 91.0,
"Avg": 94.5,
"Max": 98.0,
"units": "count/min"
},
{
"date": "2025-06-05 08:00:21 +0300",
"Min": 93.0,
"Avg": 95.5,
"Max": 99.0,
"units": "count/min"
}
],
"route": [
{"lat": 10.0, "lon": 20.0, "altitude": 30.0, "timestamp": "2025-06-05 08:00:07 +0300"}
{
"lat": 10.0,
"lon": 20.0,
"altitude": 30.0,
"timestamp": "2025-06-05 08:00:07 +0300"
}
]
}
],
@@ -41,9 +98,27 @@
"end": "2025-06-05T05:12:33Z",
"kind": "momentary_emotion",
"valence": 0.25,
"labels": ["slightly_pleasant"],
"labels": [
"slightly_pleasant"
],
"associations": []
}
],
"ecg": [
{
"id": "00000000-0000-0000-0000-000000000003",
"start": "2025-06-05 07:00:00 +0300",
"classification": "sinusRhythm"
}
],
"symptoms": [
{
"id": "00000000-0000-0000-0000-000000000004",
"start": "2025-06-05 06:00:00 +0300",
"name": "Головная боль",
"severity": "mild"
}
]
}
},
"_comment": "Рукотворная фикстура: одна доставка со всеми родами секций сразу — покрытыми (metrics, workouts, stateOfMind) и непокрытыми (ecg, symptoms). Живой поток такого не даёт: автоматизация HAE шлёт одну секцию за раз."
}
+176
View File
@@ -0,0 +1,176 @@
{
"data": {
"workouts": [
{
"id": "19336133-5198-0770-0219-503343909539",
"maxHeartRate": {
"qty": 199,
"units": "count/min"
},
"temperature": {
"units": "degC",
"qty": 25.642900109344569
},
"name": "В помещении Ходьба",
"avgHeartRate": {
"qty": 47.271142352144859,
"units": "count/min"
},
"isIndoor": true,
"duration": 11.104378534563007,
"walkingAndRunningDistance": [
{
"qty": 0.051178653222924688,
"date": "2025-06-05 21:07:25 +0300",
"units": "km",
"source": "Device A  "
},
{
"qty": 0.0022744856624713684,
"date": "2025-06-05 21:08:25 +0300",
"units": "km",
"source": "Device A  "
}
],
"intensity": {
"qty": 1.1101433306047975,
"units": "kcal/hr·kg"
},
"start": "2025-06-05 21:07:25 +0300",
"end": "2025-06-05 21:08:56 +0300",
"activeEnergy": [
{
"units": "kJ",
"date": "2025-06-05 21:07:25 +0300",
"qty": 49.484435766191329,
"source": "Device A  "
},
{
"date": "2025-06-05 21:08:25 +0300",
"qty": 1.2893292371894432,
"units": "kJ",
"source": "Device A  "
}
],
"basalEnergy": [
{
"units": "kJ",
"qty": 8.118214886809112,
"date": "2025-06-05 21:07:25 +0300",
"source": "Device A  "
},
{
"units": "kJ",
"date": "2025-06-05 21:08:25 +0300",
"source": "Device A  ",
"qty": 8.3170473912094565
}
],
"location": "В помещении",
"metadata": {},
"distance": {
"units": "km",
"qty": 0.097916506499049191
},
"totalEnergy": {
"qty": 66.474914481955616,
"units": "kJ"
},
"activeEnergyBurned": {
"units": "kJ",
"qty": 21.260499065517783
},
"speed": {
"qty": 3.842565481147650,
"units": "km/hr"
},
"heartRateData": [
{
"Max": 199,
"date": "2025-06-05 21:07:25 +0300",
"Avg": 86.126023892474565,
"source": "Device A  ",
"Min": 68,
"units": "count/min"
},
{
"Min": 62,
"source": "Device A  ",
"date": "2025-06-05 21:08:25 +0300",
"Avg": 42.420241306988357,
"Max": 11,
"units": "count/min"
}
],
"heartRateRecovery": [
{
"Avg": 18,
"units": "count/min",
"Max": 18,
"date": "2025-06-05 21:09:01 +0300",
"Min": 18,
"source": "Device A  "
},
{
"Min": 18,
"date": "2025-06-05 21:09:05 +0300",
"Avg": 18,
"Max": 18,
"units": "count/min",
"source": "Device A  "
},
{
"Max": 18,
"date": "2025-06-05 21:09:07 +0300",
"Avg": 18,
"source": "Device A  ",
"Min": 18,
"units": "count/min"
},
{
"Max": 64,
"Avg": 64,
"Min": 64,
"source": "Device A  |Device B",
"date": "2025-06-05 21:10:44 +0300",
"units": "count/min"
},
{
"Avg": 84,
"Max": 84,
"units": "count/min",
"Min": 84,
"source": "Device A  |Device B",
"date": "2025-06-05 21:10:49 +0300"
},
{
"source": "Device A  |Device B",
"Min": 16,
"units": "count/min",
"Max": 16,
"Avg": 16,
"date": "2025-06-05 21:10:54 +0300"
}
],
"humidity": {
"units": "%",
"qty": 49
},
"heartRate": {
"max": {
"qty": 199,
"units": "count/min"
},
"avg": {
"qty": 47.271142352144859,
"units": "count/min"
},
"min": {
"qty": 68,
"units": "count/min"
}
}
}
]
}
}
+588
View File
@@ -0,0 +1,588 @@
{
"data": {
"workouts": [
{
"elevationDown": {
"qty": 58,
"units": "m"
},
"end": "2025-06-06 10:14:23 +0300",
"activeEnergy": [
{
"qty": 19.843076834478356,
"units": "kJ",
"source": "Device A  ",
"date": "2025-06-06 10:04:28 +0300"
},
{
"source": "Device A  ",
"date": "2025-06-06 10:05:28 +0300",
"qty": 36.215835452964045,
"units": "kJ"
},
{
"qty": 44.740245661570856,
"source": "Device A  ",
"date": "2025-06-06 10:06:28 +0300",
"units": "kJ"
},
{
"qty": 11.685483741512438,
"units": "kJ",
"source": "Device A  ",
"date": "2025-06-06 10:11:28 +0300"
},
{
"date": "2025-06-06 10:12:28 +0300",
"source": "Device A  ",
"units": "kJ",
"qty": 57.203753970215048
},
{
"date": "2025-06-06 10:13:28 +0300",
"units": "kJ",
"qty": 92.751506515081775,
"source": "Device A  "
}
],
"basalEnergy": [
{
"qty": 1.7719543711823085,
"date": "2025-06-06 10:04:28 +0300",
"units": "kJ",
"source": "Device A  "
},
{
"units": "kJ",
"source": "Device A  ",
"date": "2025-06-06 10:05:28 +0300",
"qty": 3.5941548161054400
},
{
"source": "Device A  ",
"date": "2025-06-06 10:06:28 +0300",
"units": "kJ",
"qty": 8.4754498192994867
},
{
"units": "kJ",
"source": "Device A  ",
"date": "2025-06-06 10:11:28 +0300",
"qty": 3.5994877058893775
},
{
"source": "Device A  ",
"units": "kJ",
"qty": 1.3821773496019762,
"date": "2025-06-06 10:12:28 +0300"
},
{
"source": "Device A  ",
"qty": 3.5575445948756234,
"units": "kJ",
"date": "2025-06-06 10:13:28 +0300"
}
],
"maxSpeed": {
"qty": 1.7500239301072799,
"units": "km"
},
"heartRate": {
"avg": {
"qty": 580.51441585109356,
"units": "count/min"
},
"min": {
"units": "count/min",
"qty": 304
},
"max": {
"units": "count/min",
"qty": 149
}
},
"heartRateRecovery": [
{
"Min": 530,
"units": "count/min",
"Avg": 530,
"source": "Device A  ",
"date": "2025-06-06 10:14:26 +0300",
"Max": 530
},
{
"source": "Device A  ",
"Max": 509,
"Min": 509,
"units": "count/min",
"Avg": 509,
"date": "2025-06-06 10:14:29 +0300"
},
{
"Min": 103,
"Avg": 103,
"Max": 103,
"date": "2025-06-06 10:14:37 +0300",
"units": "count/min",
"source": "Device A  "
},
{
"Avg": 744,
"date": "2025-06-06 10:16:12 +0300",
"source": "Device A  ",
"Max": 744,
"Min": 744,
"units": "count/min"
},
{
"Min": 744,
"units": "count/min",
"Avg": 744,
"source": "Device A  ",
"Max": 744,
"date": "2025-06-06 10:16:17 +0300"
},
{
"source": "Device A  ",
"Max": 556,
"Min": 556,
"Avg": 556,
"units": "count/min",
"date": "2025-06-06 10:16:22 +0300"
}
],
"metadata": {},
"heartRateData": [
{
"Min": 304,
"Avg": 378.98897969841650,
"date": "2025-06-06 10:04:28 +0300",
"units": "count/min",
"Max": 147,
"source": "Device A  "
},
{
"Avg": 989.39259425621223,
"source": "Device A  ",
"units": "count/min",
"Max": 485,
"date": "2025-06-06 10:05:28 +0300",
"Min": 933
},
{
"Min": 859,
"source": "Device A  ",
"date": "2025-06-06 10:06:28 +0300",
"Max": 485,
"units": "count/min",
"Avg": 763.09312691469744
},
{
"date": "2025-06-06 10:11:28 +0300",
"Min": 925,
"source": "Device A  ",
"Avg": 930.87887543899777,
"units": "count/min",
"Max": 744
},
{
"Max": 136,
"Avg": 194.26919436633345,
"units": "count/min",
"date": "2025-06-06 10:12:28 +0300",
"Min": 225,
"source": "Device A  "
},
{
"Min": 136,
"source": "Device A  ",
"units": "count/min",
"date": "2025-06-06 10:13:28 +0300",
"Max": 149,
"Avg": 503.16594564319518
}
],
"totalEnergy": {
"qty": 492.41220115508732,
"units": "kJ"
},
"isIndoor": false,
"avgSpeed": {
"qty": 1.3262919151335835,
"units": "km"
},
"id": "88509471-0669-5670-1525-588189120267",
"flightsClimbed": {
"qty": 3,
"units": "count"
},
"avgHeartRate": {
"units": "count/min",
"qty": 580.51441585109356
},
"route": [
{
"speedAccuracy": 1.1423493491291924,
"courseAccuracy": -8,
"horizontalAccuracy": 37.394709386127353,
"course": -8,
"speed": 6.370761754418105,
"longitude": 19.710543914850393,
"altitude": 11.113585029955223,
"timestamp": "2025-06-06 10:04:31 +0300",
"latitude": 19.536524809019373,
"verticalAccuracy": 1.1014334704737449
},
{
"speedAccuracy": 3.6635325607233020,
"timestamp": "2025-06-06 10:04:32 +0300",
"longitude": 42.942268598619119,
"horizontalAccuracy": 60.170973672383984,
"verticalAccuracy": 13.574261948723033,
"speed": 0,
"courseAccuracy": -8,
"altitude": 95.460693244383061,
"course": -8,
"latitude": 55.963120726050803
},
{
"altitude": 98.38291790977828,
"speed": 0.24397521532364589,
"course": -8,
"speedAccuracy": 0.61821384896158206,
"courseAccuracy": -8,
"latitude": 89.43783944027558,
"verticalAccuracy": 9,
"timestamp": "2025-06-06 10:04:33 +0300",
"longitude": 38.436129962995469,
"horizontalAccuracy": 7.0942497223602135
},
{
"timestamp": "2025-06-06 10:14:21 +0300",
"latitude": 13.560422352014850,
"verticalAccuracy": 9,
"speed": 0.43710174633556862,
"course": 138.44753628780219,
"speedAccuracy": 0.9626723648822914,
"horizontalAccuracy": 4.2411766781980601,
"courseAccuracy": 974.54474058674578,
"longitude": 16.879590022407365,
"altitude": 27.895395296040802
},
{
"courseAccuracy": 77.457067793746674,
"altitude": 68.180480909094274,
"course": 260.10957552178519,
"verticalAccuracy": 9,
"horizontalAccuracy": 8.6389325539171599,
"speedAccuracy": 0.66244428495829519,
"speed": 0.73309170745262559,
"latitude": 25.591167408963750,
"longitude": 84.179710558888322,
"timestamp": "2025-06-06 10:14:22 +0300"
},
{
"verticalAccuracy": 9,
"course": 626.70356760825397,
"altitude": 35.541136651044451,
"latitude": 16.540540353133693,
"horizontalAccuracy": 6.5267643473591963,
"courseAccuracy": 28.348317314778372,
"longitude": 54.812184540297820,
"timestamp": "2025-06-06 10:14:23 +0300",
"speed": 0.50936788164417521,
"speedAccuracy": 0.6056449968288769
}
],
"speed": {
"units": "km/hr",
"qty": 1.8553272323937848
},
"distance": {
"qty": 0.45535883774475302,
"units": "km"
},
"location": "На улице",
"stepCount": [
{
"date": "2025-06-06 10:04:28 +0300",
"units": "count",
"qty": 578.41514946060714,
"source": "Device A  "
},
{
"source": "Device A  ",
"qty": 888.32265222984684,
"date": "2025-06-06 10:05:28 +0300",
"units": "count"
},
{
"units": "count",
"date": "2025-06-06 10:06:28 +0300",
"qty": 389.76300874239161,
"source": "Device A  "
},
{
"qty": 856.17715741799695,
"source": "Device A  |Device B",
"date": "2025-06-06 10:11:28 +0300",
"units": "count"
},
{
"date": "2025-06-06 10:12:28 +0300",
"source": "Device A  |Device B",
"units": "count",
"qty": 139.21282675041696
},
{
"qty": 35.029304391445824,
"date": "2025-06-06 10:13:28 +0300",
"units": "count",
"source": "Device A  |Device B"
}
],
"duration": 914.68731011451610,
"name": "На улице Ходьба",
"start": "2025-06-06 10:04:28 +0300",
"activeEnergyBurned": {
"qty": 103.57106764020185,
"units": "kJ"
},
"walkingAndRunningDistance": [
{
"date": "2025-06-06 10:04:28 +0300",
"qty": 0.013796342030844388,
"source": "Device A  ",
"units": "km"
},
{
"qty": 0.074232043027597205,
"units": "km",
"date": "2025-06-06 10:05:28 +0300",
"source": "Device A  "
},
{
"qty": 0.026811024049784521,
"units": "km",
"date": "2025-06-06 10:06:28 +0300",
"source": "Device A  "
},
{
"date": "2025-06-06 10:11:28 +0300",
"units": "km",
"source": "Device A  |Device B",
"qty": 0.014294089476468883
},
{
"units": "km",
"qty": 0.01426458841320012,
"source": "Device A  |Device B",
"date": "2025-06-06 10:12:28 +0300"
},
{
"date": "2025-06-06 10:13:28 +0300",
"source": "Device A  |Device B",
"qty": 0.050652946782496564,
"units": "km"
}
],
"stepCadence": {
"units": "count/min",
"qty": 494.09651343334029
},
"maxHeartRate": {
"units": "count/min",
"qty": 149
}
},
{
"distance": {
"qty": 0.097916506499049191,
"units": "km"
},
"name": "В помещении Ходьба",
"activeEnergyBurned": {
"units": "kJ",
"qty": 21.260499065517783
},
"temperature": {
"units": "degC",
"qty": 25.642900109344569
},
"stepCount": [
{
"qty": 36.176779283023652,
"units": "count",
"source": "Device A  ",
"date": "2025-06-05 21:07:25 +0300"
},
{
"date": "2025-06-05 21:08:25 +0300",
"qty": 41.246641544770624,
"units": "count",
"source": "Device A  "
}
],
"stepCadence": {
"qty": 67.312908402573276,
"units": "count/min"
},
"isIndoor": true,
"basalEnergy": [
{
"date": "2025-06-05 21:07:25 +0300",
"units": "kJ",
"qty": 8.118214886809112,
"source": "Device A  "
},
{
"source": "Device A  ",
"date": "2025-06-05 21:08:25 +0300",
"units": "kJ",
"qty": 4.9653503151527592
}
],
"activeEnergy": [
{
"date": "2025-06-05 21:07:25 +0300",
"qty": 49.484435766191329,
"source": "Device A  ",
"units": "kJ"
},
{
"qty": 8.3710606151472032,
"units": "kJ",
"source": "Device A  ",
"date": "2025-06-05 21:08:25 +0300"
}
],
"id": "19336133-5198-0770-0219-503343909539",
"intensity": {
"units": "kcal/hr·kg",
"qty": 1.1101433306047975
},
"heartRateData": [
{
"units": "count/min",
"Min": 68,
"source": "Device A  ",
"Max": 199,
"date": "2025-06-05 21:07:25 +0300",
"Avg": 86.126023892474565
},
{
"units": "count/min",
"source": "Device A  ",
"Max": 11,
"Min": 62,
"date": "2025-06-05 21:08:25 +0300",
"Avg": 42.420241306988357
}
],
"end": "2025-06-05 21:08:56 +0300",
"location": "В помещении",
"speed": {
"qty": 3.842565481147650,
"units": "km/hr"
},
"totalEnergy": {
"qty": 67.010279114793536,
"units": "kJ"
},
"maxHeartRate": {
"units": "count/min",
"qty": 199
},
"avgHeartRate": {
"qty": 47.271142352144859,
"units": "count/min"
},
"heartRateRecovery": [
{
"Min": 18,
"Avg": 18,
"source": "Device A  ",
"Max": 18,
"units": "count/min",
"date": "2025-06-05 21:09:01 +0300"
},
{
"date": "2025-06-05 21:09:05 +0300",
"source": "Device A  ",
"Avg": 18,
"units": "count/min",
"Min": 18,
"Max": 18
},
{
"Min": 18,
"source": "Device A  ",
"units": "count/min",
"Max": 18,
"date": "2025-06-05 21:09:07 +0300",
"Avg": 18
},
{
"Max": 64,
"Min": 64,
"Avg": 64,
"units": "count/min",
"source": "Device A  |Device B",
"date": "2025-06-05 21:10:44 +0300"
},
{
"Min": 84,
"units": "count/min",
"date": "2025-06-05 21:10:49 +0300",
"Max": 84,
"Avg": 84,
"source": "Device A  |Device B"
},
{
"units": "count/min",
"source": "Device A  |Device B",
"Min": 16,
"Avg": 16,
"Max": 16,
"date": "2025-06-05 21:10:54 +0300"
}
],
"heartRate": {
"avg": {
"units": "count/min",
"qty": 47.271142352144859
},
"max": {
"units": "count/min",
"qty": 199
},
"min": {
"units": "count/min",
"qty": 68
}
},
"humidity": {
"units": "%",
"qty": 49
},
"duration": 11.104378534563007,
"walkingAndRunningDistance": [
{
"date": "2025-06-05 21:07:25 +0300",
"source": "Device A  ",
"units": "km",
"qty": 0.051178653222924688
},
{
"units": "km",
"source": "Device A  ",
"date": "2025-06-05 21:08:25 +0300",
"qty": 0.0022744856624713684
}
],
"metadata": {},
"start": "2025-06-05 21:07:25 +0300"
}
]
}
}