# Техническое задание

## Система синхронизации данных из Whoop, Garmin и Oura с личным дашбордом

**Заказчик:** частное лицо (владелец аккаунтов Whoop, Garmin, Oura)
**Документ:** редакция 2 от 07.08.2026
**Назначение документа:** передача подрядчику для оценки сроков, стоимости и последующей приёмки работ.

---

## 1. Цель и сценарий использования

### 1.1 Цель

Заказчик носит три устройства (Whoop, Garmin, Oura) и видит свои показатели в трёх разных мобильных приложениях. Данные нельзя сопоставить между собой, нельзя посмотреть динамику глубже, чем позволяет каждое приложение по отдельности, и они не принадлежат заказчику технически — они живут в облаках трёх вендоров.

Цель системы: **собрать данные всех трёх устройств в одно собственное хранилище заказчика и показать их в одном дашборде.**

### 1.2 Основной сценарий использования

> Утром заказчик открывает веб-страницу дашборда с телефона или ноутбука. На одном экране он видит за вчерашний день и за последние 30 дней: **сон** (длительность, фазы, эффективность), **нагрузку** (тренировки, шаги, калории, нагрузка дня), **восстановление** (готовность/recovery, ВСР/HRV, пульс покоя). Показатели трёх устройств за один и тот же день видны рядом и сравнимы между собой.

Дополнительные сценарии:

| № | Сценарий | Что должна уметь система |
|---|---|---|
| С-1 | Посмотреть вчерашний день | Данные за прошедшие сутки доступны в дашборде не позднее 12:00 текущего дня |
| С-2 | Посмотреть тренд за месяц/год | График любого показателя за произвольный период с начала истории |
| С-3 | Сравнить устройства | Один и тот же показатель (например, сон) от Whoop и Oura за одну дату — рядом |
| С-4 | Убедиться, что данные не потерялись | Видно, когда была последняя успешная синхронизация по каждому источнику |
| С-5 | Забрать данные с собой | Выгрузка всех накопленных данных в CSV/JSON одной кнопкой |

### 1.3 Кто пользуется системой

Один пользователь — заказчик. Многопользовательский режим, роли, приглашения и разделение доступа **не требуются** (см. раздел 5).

---

## 2. Источники данных

Раздел фиксирует технические условия, от которых зависят объём и стоимость работ. Все факты проверены по документации вендоров 07.08.2026, ссылки приведены.

### 2.1 Whoop

| Параметр | Значение |
|---|---|
| Тип доступа | Публичный OAuth 2.0 API для личных аккаунтов, регистрация приложения самостоятельная |
| Авторизация | OAuth 2.0, обязательно запросить scope `offline` — иначе не будет refresh-токена и доступ отвалится через час |
| Данные | Cycle (нагрузка дня, strain), Recovery (recovery %, HRV, RHR), Sleep (фазы, эффективность), Workout |
| Лимиты | 100 запросов в минуту и 10 000 запросов в сутки на клиента ([WHOOP Rate Limiting](https://developer.whoop.com/docs/developing/rate-limiting/), проверено 07.08.2026) |
| Вебхуки | Есть, версия v2; v1 отключены — использовать только v2 |
| Риск | Низкий. Доступ получается самостоятельно, без одобрения вендора |

### 2.2 Oura

| Параметр | Значение |
|---|---|
| Тип доступа | Публичный API v2, база `https://api.ouraring.com/v2` |
| Авторизация | **Только OAuth 2.0.** Personal Access Token (PAT) объявлены устаревшими в декабре 2025, новые не выдаются — проектировать на PAT нельзя |
| Данные | `usercollection/daily_sleep`, `daily_readiness`, `daily_activity`, `daily_spo2`, `heartrate`, `workout`, `session`, `tag` |
| Лимиты | Двухуровневые: на токен и на приложение. Ранее документированное значение 5000 запросов / 5 минут устарело — подрядчик обязан свериться с живой документацией на старте |
| Вебхуки | Есть: `/v2/webhook/subscription`, подтверждение подписки challenge-запросом, подпись `x-oura-signature` по client secret |
| Особенность | Данные за ночь обрабатываются к середине утра. Синхронизацию планировать на 07:00–10:00 по местному времени заказчика |
| Риск | Низкий. Ограничение: свежезарегистрированное приложение обслуживает до 10 пользователей до одобрения Oura — для одного заказчика достаточно |

Источник: [Oura API v2 Documentation](https://api.ouraring.com/v2/docs), проверено 07.08.2026.

### 2.3 Garmin — главный риск проекта

**У Garmin нет открытого потребительского API.** Доступ к Garmin Health API выдаётся только через Garmin Developer Program по заявке, и программа ориентирована на корпоративное оздоровление, популяционное здоровье и мониторинг пациентов — не на личные проекты ([Garmin Health API](https://developer.garmin.com/gc-developer-program/health-api/), проверено 07.08.2026). Одобрение заявки от частного лица не гарантировано и по срокам не прогнозируется.

Варианты решения, из которых заказчик выбирает **до начала этапа 1**:

| Вариант | Как работает | Плюсы | Минусы |
|---|---|---|---|
| **G-1. Заявка в Garmin Developer Program** | Подать заявку на Health API, при одобрении — полноценная интеграция (push или ping/pull) | Легально, надёжно, есть вебхуки, полный набор данных | Одобрение не гарантировано; срок ответа неизвестен; может потребоваться юрлицо и описание продукта |
| **G-2. Экспорт из Garmin Connect по запросу пользователя** | Заказчик периодически запрашивает выгрузку своих данных и кладёт файл в систему; система разбирает файл | Работает всегда, ничьего одобрения не нужно, легально | Не автоматически: требует ручного действия заказчика; данные приходят с задержкой; формат выгрузки может меняться |
| **G-3. Файлы FIT с устройства** | Часы подключаются кабелем к компьютеру, папка `Activity`/`Monitor` копируется в систему, система парсит FIT | Самые полные исходные данные, без посредников | Ручное подключение часов; нет данных, считаемых только на сервере Garmin (Body Battery, оценка сна Connect) |
| **G-4. Неофициальные библиотеки к Garmin Connect** | Логин под учётной записью заказчика через сторонний клиент | Автоматически, быстро в реализации | **Нарушает пользовательское соглашение Garmin**; риск блокировки аккаунта; ломается при любом изменении на стороне Garmin; в промышленную эксплуатацию не рекомендуется |
| **G-5. Платный агрегатор** (Terra, Vital, Rook и т.п.) | Один API вместо трёх, включая Garmin | Быстрый старт, снимает вопрос с Garmin | Платная подписка; данные о здоровье проходят через третью сторону; зависимость от посредника |

**Требование к подрядчику:** архитектура коннекторов делается единообразной, чтобы источник Garmin можно было заменить с одного варианта на другой (например, G-2 → G-1 после одобрения заявки) **без переделки хранилища и дашборда**. Формат хранения от способа получения данных не зависит.

> **Решение заказчика по Garmin не принято.** До его принятия работы по этапу 1 ведутся по Whoop и Oura, коннектор Garmin реализуется вторым подходом после выбора варианта.

---

## 3. Состав работ

Работы разбиты на два этапа. Этап 2 начинается после приёмки этапа 1.

### 3.1 Этап 1 — синхронизация данных

**Результат этапа:** данные трёх (или двух — до решения по Garmin) источников автоматически, без участия человека, попадают в хранилище заказчика и накапливаются там.

#### 3.1.1 Коннекторы к источникам

| ID | Требование |
|---|---|
| Ф-1.1 | Подключение аккаунта по OAuth 2.0: заказчик один раз проходит авторизацию в браузере, система сохраняет access- и refresh-токены |
| Ф-1.2 | Автоматическое обновление access-токена по refresh-токену без участия заказчика |
| Ф-1.3 | Первичная загрузка всей доступной истории по каждому источнику (сколько отдаёт API — не менее чем за 2 года, если данные есть) |
| Ф-1.4 | Инкрементальная догрузка новых данных с момента последней успешной синхронизации |
| Ф-1.5 | Повторная загрузка за период с перезаписью (данные вендоров задним числом уточняются: например, оценка сна может пересчитаться) |
| Ф-1.6 | Соблюдение лимитов API источника, ожидание и повтор при ответе `429`, экспоненциальная задержка между повторами |
| Ф-1.7 | Приём вебхуков от Whoop (v2) и Oura с проверкой подписи; событие вебхука запускает догрузку соответствующей записи |
| Ф-1.8 | Идемпотентность: повторная обработка одного и того же события или периода не создаёт дублей в хранилище |
| Ф-1.9 | Коннектор Garmin по варианту, выбранному заказчиком (раздел 2.3) |

#### 3.1.2 Хранилище

| ID | Требование |
|---|---|
| Ф-2.1 | Реляционная СУБД (рекомендуется PostgreSQL), развёрнутая под контролем заказчика |
| Ф-2.2 | Сырые ответы API сохраняются как есть (raw-слой) — чтобы при изменении логики обработки можно было пересчитать витрины без повторного обращения к вендорам |
| Ф-2.3 | Нормализованный слой: единая схема для показателей сна, нагрузки и восстановления, независимая от источника; у каждой записи проставлены источник, дата (в местном времени заказчика), момент загрузки |
| Ф-2.4 | Обязательный минимальный состав нормализованных показателей: длительность и фазы сна, эффективность сна, время отхода ко сну и подъёма, ВСР (HRV), пульс покоя, показатель восстановления/готовности, дневная нагрузка, шаги, калории, тренировки (тип, длительность, пульс) |
| Ф-2.5 | Данные, отсутствующие у источника, хранятся как пустые, а не как нули |
| Ф-2.6 | Схема хранилища описана в документации: таблицы, поля, единицы измерения, откуда взято каждое поле |

#### 3.1.3 Расписание и надёжность

| ID | Требование |
|---|---|
| Ф-3.1 | Автоматический запуск синхронизации по расписанию: не реже раза в сутки, для Oura — в окне 07:00–10:00 по местному времени заказчика |
| Ф-3.2 | Расписание меняется настройкой, без правки исходного кода |
| Ф-3.3 | Ручной запуск синхронизации по любому источнику и за любой период |
| Ф-3.4 | Сбой одного источника не останавливает синхронизацию остальных |
| Ф-3.5 | Автоматический повтор неудавшейся синхронизации (не менее 3 попыток с нарастающей паузой) |
| Ф-3.6 | Журнал синхронизаций: по каждому запуску — источник, период, время начала и окончания, число загруженных записей, результат, текст ошибки при сбое |
| Ф-3.7 | Уведомление заказчику (e-mail или Telegram — канал согласуется на старте) при сбое всех попыток по источнику и при отсутствии новых данных от источника более 48 часов |

### 3.2 Этап 2 — веб-дашборд

**Результат этапа:** заказчик открывает веб-страницу и видит свои данные.

| ID | Требование |
|---|---|
| Ф-4.1 | Веб-интерфейс, работающий в браузере на компьютере и на телефоне (адаптивная вёрстка) |
| Ф-4.2 | Вход по паролю (или иному согласованному способу аутентификации); без входа данные недоступны |
| Ф-4.3 | Главный экран: сон, нагрузка, восстановление за вчерашний день по каждому источнику + сводка за 7 дней |
| Ф-4.4 | Графики динамики по каждому показателю с выбором периода: 7 дней, 30 дней, 90 дней, год, произвольный диапазон |
| Ф-4.5 | Экран сравнения источников: один показатель за одну дату от разных устройств рядом |
| Ф-4.6 | Список тренировок с деталями (дата, тип, длительность, пульс, источник) |
| Ф-4.7 | Индикатор состояния синхронизации: по каждому источнику — время последней успешной синхронизации и признак ошибки |
| Ф-4.8 | Выгрузка данных в CSV и JSON за выбранный период |
| Ф-4.9 | Кнопка ручного запуска синхронизации из интерфейса |
| Ф-4.10 | Дашборд открывается и остаётся работоспособным при пустых данных по какому-либо источнику (например, Garmin не подключён) |

---

## 4. Нефункциональные требования

### 4.1 Персональные данные о здоровье

| ID | Требование |
|---|---|
| НФ-1.1 | Все данные системы — это данные о состоянии здоровья одного человека. Обращение с ними: доступ только заказчику, передача третьим лицам не допускается |
| НФ-1.2 | Данные размещаются только на инфраструктуре, подконтрольной заказчику: его VPS/сервер или его аккаунт в облаке. Использование учётных записей подрядчика для промышленного размещения не допускается |
| НФ-1.3 | Обмен с браузером и с API вендоров — только по HTTPS/TLS |
| НФ-1.4 | Токены доступа, пароли и client secret хранятся в зашифрованном виде или в менеджере секретов; в исходном коде, конфигурационных файлах репозитория и логах их быть не должно |
| НФ-1.5 | По завершении работ подрядчик удаляет все копии данных заказчика со своих машин и письменно это подтверждает |
| НФ-1.6 | Функция полного удаления данных и отзыва доступа к аккаунтам вендоров по требованию заказчика |

### 4.2 Доступ

| ID | Требование |
|---|---|
| НФ-2.1 | Единственный пользователь — заказчик. Открытая регистрация отсутствует |
| НФ-2.2 | Административные интерфейсы (БД, панель управления задачами, если есть) не доступны из интернета без аутентификации |
| НФ-2.3 | Пароли хранятся в виде хешей современным алгоритмом (bcrypt/argon2), не в открытом виде |

### 4.3 Резервное копирование

| ID | Требование |
|---|---|
| НФ-3.1 | Автоматическая резервная копия базы данных не реже раза в сутки |
| НФ-3.2 | Глубина хранения копий — не менее 30 дней |
| НФ-3.3 | Копии хранятся отдельно от основного сервера (другой диск/хранилище/аккаунт) в зашифрованном виде |
| НФ-3.4 | Восстановление из копии проверено практикой и описано пошагово в инструкции |

### 4.4 Логи и мониторинг

| ID | Требование |
|---|---|
| НФ-4.1 | Журнал работы системы: запуски синхронизаций, ошибки, обращения к API источников, входы в дашборд. Глубина хранения — не менее 90 дней |
| НФ-4.2 | В логах не должно быть токенов, паролей и полного содержимого данных о здоровье |
| НФ-4.3 | Отдельный признак «здоровья» системы, доступный в дашборде: по каждому источнику — время последней успешной синхронизации |
| НФ-4.4 | Уведомление заказчику при сбое синхронизации (см. Ф-3.7) с указанием источника и причины |
| НФ-4.5 | Автоматический перезапуск сервисов после перезагрузки сервера |

### 4.5 Прочее

| ID | Требование |
|---|---|
| НФ-5.1 | Страница дашборда загружается не дольше 3 секунд на периоде 30 дней при накопленной истории в 2 года |
| НФ-5.2 | Система работает на одном сервере начального уровня (ориентир: 2 vCPU / 4 ГБ RAM / 40 ГБ диска) |
| НФ-5.3 | Развёртывание контейнерами (Docker/Docker Compose) — воспроизводимо одной командой |
| НФ-5.4 | Язык интерфейса — русский; единицы измерения метрические; время — местное время заказчика |

---

## 5. Что НЕ входит в объём работ

Явно исключено из настоящего ТЗ. Любой из пунктов может стать отдельным этапом, но в текущую оценку и цену не входит:

1. Многопользовательский режим, регистрация, роли, приглашения, шаринг данных.
2. Мобильное приложение (iOS/Android). Дашборд — только веб-страница в браузере.
3. Аналитика, рекомендации, ИИ-советы, тренерские планы, предсказание перетренированности.
4. Интеграции с любыми источниками, кроме Whoop, Garmin, Oura (Apple Health, Google Fit, Strava, Withings, весы, глюкометры и прочее).
5. Двусторонняя синхронизация — система только читает данные, ничего не записывает обратно в сервисы вендоров.
6. Обратное заполнение данных за периоды, которые API вендора не отдаёт.
7. Медицинская интерпретация показателей, диагностика, любые заключения о здоровье.
8. Сертификация и формальная аттестация по медицинским стандартам и регламентам обработки данных о здоровье.
9. Экспорт в сторонние сервисы (Notion, Google Sheets, Obsidian и т.п.).
10. Круглосуточная техническая поддержка и SLA после сдачи работ — сопровождение оговаривается отдельным договором.
11. Оплата подписок и тарифов вендоров/агрегаторов (если по Garmin будет выбран вариант G-5) — оплачивает заказчик отдельно.
12. Приобретение и оплата сервера/домена — на стороне заказчика.

---

## 6. Требования к передаче результата

Работа считается переданной, когда подрядчик передал заказчику всё перечисленное:

| ID | Что передаётся | В каком виде |
|---|---|---|
| П-1 | Исходный код | Git-репозиторий, принадлежащий заказчику, с историей коммитов. Права на код переходят заказчику |
| П-2 | Инструкция по развёртыванию | Пошаговый документ: от чистого сервера до работающей системы, включая регистрацию приложений в Whoop/Oura и получение ключей |
| П-3 | Конфигурация | Файл-образец переменных окружения со всеми параметрами и пояснениями (без реальных значений секретов) |
| П-4 | Описание хранилища | Схема БД: таблицы, поля, типы, единицы измерения, источник каждого поля |
| П-5 | Руководство пользователя | Как подключить аккаунт, запустить синхронизацию вручную, выгрузить данные, что делать при ошибке синхронизации |
| П-6 | Инструкция по резервному копированию и восстановлению | Как настроено, где лежат копии, как восстановиться — с подтверждением, что проверялось на практике |
| П-7 | Список известных ограничений | Что не реализовано и почему, какие данные каким источником не отдаются |
| П-8 | Демонстрация | Онлайн-показ работающей системы на сервере заказчика: полный проход по критериям приёмки раздела 7 |
| П-9 | Подтверждение удаления данных | Письменное подтверждение (НФ-1.5) |

Документация — на русском языке, в формате Markdown или PDF, в том же репозитории.

---

## 7. Критерии приёмки

Каждый критерий — действие с наблюдаемым результатом. Проверка проводится заказчиком или в его присутствии на сервере заказчика. Критерий считается выполненным, только если результат наблюдается фактически.

### 7.1 Приёмка этапа 1 — синхронизация

| № | Действие | Наблюдаемый результат |
|---|---|---|
| ПР-1.1 | Развернуть систему на чистом сервере, следуя только переданной инструкции, не задавая вопросов подрядчику | Система запустилась и работает |
| ПР-1.2 | Пройти OAuth-подключение аккаунта Whoop через интерфейс системы | Подключение завершилось успехом, в системе виден статус «подключено» |
| ПР-1.3 | То же для Oura | То же |
| ПР-1.4 | То же для Garmin по выбранному варианту (раздел 2.3) | Данные Garmin попали в хранилище выбранным способом |
| ПР-1.5 | Запустить первичную загрузку истории | В базе появились записи, самая ранняя дата соответствует глубине истории, отданной API |
| ПР-1.6 | Открыть базу и запросить данные за произвольный день | Возвращаются записи по сну, нагрузке и восстановлению по каждому подключённому источнику, единицы измерения соответствуют документации |
| ПР-1.7 | Не трогать систему сутки | На следующий день в базе есть данные за прошедший день по каждому источнику, они появились без ручного вмешательства |
| ПР-1.8 | Запустить синхронизацию повторно за тот же период | Количество записей не выросло, дубли не появились (проверка Ф-1.8) |
| ПР-1.9 | Отключить сеть/подставить неверный токен для одного источника и запустить синхронизацию | Остальные источники синхронизировались; в журнале — запись об ошибке с указанием источника и причины; заказчику пришло уведомление |
| ПР-1.10 | Восстановить доступ и запустить синхронизацию | Пропущенный период догрузился, пробела в данных нет |
| ПР-1.11 | Дождаться истечения access-токена (более часа после подключения) и запустить синхронизацию | Синхронизация прошла успешно, повторная авторизация в браузере не потребовалась (проверка Ф-1.2) |
| ПР-1.12 | Открыть журнал синхронизаций | По каждому запуску видны источник, период, время, число записей и результат |
| ПР-1.13 | Найти в журнале и в файлах конфигурации репозитория токен или пароль | Не находится ни одного (проверка НФ-1.4, НФ-4.2) |
| ПР-1.14 | Выполнить восстановление базы из резервной копии по инструкции на пустом сервере | База восстановилась, данные на месте, система работает |
| ПР-1.15 | Перезагрузить сервер | Система поднялась сама, синхронизация по расписанию продолжилась |

### 7.2 Приёмка этапа 2 — дашборд

| № | Действие | Наблюдаемый результат |
|---|---|---|
| ПР-2.1 | Открыть адрес дашборда в браузере, не выполняя вход | Данные не показаны, предложен вход |
| ПР-2.2 | Войти под своей учётной записью | Открылся главный экран |
| ПР-2.3 | Посмотреть главный экран утром следующего дня | Виден вчерашний сон, нагрузка и восстановление по каждому подключённому источнику; данные соответствуют приложениям вендоров |
| ПР-2.4 | Переключить период на 30 дней и на год | Графики перестроились, страница отрисовалась не дольше 3 секунд (проверка НФ-5.1) |
| ПР-2.5 | Открыть экран сравнения источников за одну дату | Показатели Whoop и Oura за эту дату видны рядом |
| ПР-2.6 | Открыть список тренировок | Тренировки видны с датой, типом, длительностью и источником |
| ПР-2.7 | Посмотреть индикатор синхронизации | По каждому источнику видно время последней успешной синхронизации |
| ПР-2.8 | Нажать ручной запуск синхронизации | Синхронизация запустилась, по завершении время последней синхронизации обновилось на экране |
| ПР-2.9 | Выгрузить данные за 30 дней в CSV | Файл скачался, открывается в Excel/Numbers, содержит данные за указанный период |
| ПР-2.10 | Открыть дашборд на телефоне | Все экраны читаемы и работоспособны без горизонтальной прокрутки |
| ПР-2.11 | Открыть дашборд при неподключённом источнике | Страница открылась, по этому источнику показано «нет данных», остальные данные видны |
| ПР-2.12 | Пройти по всем пунктам передачи результата (раздел 6) | Каждый пункт П-1…П-9 передан фактически |

### 7.3 Условия итоговой приёмки

Работы принимаются, когда выполнены **все** критерии соответствующего этапа. Невыполненный критерий фиксируется письменно с указанием причины; работы принимаются после устранения либо, по решению заказчика, критерий снимается с явной отметкой в акте.

---

## 8. Решения, которые заказчик принимает до начала работ

| № | Вопрос | Почему это важно |
|---|---|---|
| Р-1 | **Вариант по Garmin** (G-1…G-5 из раздела 2.3) | Определяет объём и стоимость коннектора Garmin, а в случае G-5 — ежемесячные платежи |
| Р-2 | **Где размещается система**: собственный VPS у российского или зарубежного хостинга / домашний мини-сервер | Влияет на стоимость владения и на доступность API вендоров с этого сервера. Рекомендация подрядчика — VPS начального уровня (НФ-5.2) под учётной записью заказчика |
| Р-3 | **Канал уведомлений о сбоях**: e-mail или Telegram | Требование Ф-3.7 |
| Р-4 | **Бюджет и сроки по этапам** | Этап 2 запускается после приёмки этапа 1 |

---

## Приложение А. Проверенные источники

| Источник | Ссылка | Дата проверки |
|---|---|---|
| WHOOP — лимиты запросов | https://developer.whoop.com/docs/developing/rate-limiting/ | 07.08.2026 |
| WHOOP — OAuth | https://developer.whoop.com/docs/developing/oauth/ | 07.08.2026 |
| Oura — документация API v2 | https://api.ouraring.com/v2/docs | 07.08.2026 |
| Garmin — Health API и условия доступа | https://developer.garmin.com/gc-developer-program/health-api/ | 07.08.2026 |

Оценка сроков и стоимости — зона ответственности подрядчика; настоящее ТЗ цен и сроков не содержит.