187 lines
7.0 KiB
Python
187 lines
7.0 KiB
Python
#!/usr/bin/env python3
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
from sqlalchemy.orm import Session
|
|
import database
|
|
import models
|
|
import schemas
|
|
from datetime import datetime
|
|
|
|
router = APIRouter(prefix="/sync", tags=["sync"])
|
|
|
|
def get_current_timestamp():
|
|
return datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%fZ')
|
|
|
|
@router.post("/", response_model=schemas.SyncResponse)
|
|
def sync_data(payload: schemas.SyncPayload, db: Session = Depends(database.get_db)):
|
|
"""
|
|
Endpoint для синхронизации офлайн-очереди с клиента.
|
|
Использует sync_id для идемпотентности — если запись с таким sync_id уже есть, пропускаем.
|
|
"""
|
|
timestamp = get_current_timestamp()
|
|
created = {
|
|
"clients": 0,
|
|
"workout_types": 0,
|
|
"exercises": 0,
|
|
"workouts": 0,
|
|
"metric_types": 0,
|
|
"body_metrics": 0
|
|
}
|
|
updated = {k: 0 for k in created}
|
|
|
|
# Синхронизация клиентов
|
|
for client_data in payload.clients or []:
|
|
existing = db.query(models.Client).filter(models.Client.sync_id == client_data.sync_id).first()
|
|
if not existing:
|
|
db_client = models.Client(
|
|
**client_data.dict(),
|
|
created_at=timestamp,
|
|
updated_at=timestamp
|
|
)
|
|
db.add(db_client)
|
|
created["clients"] += 1
|
|
|
|
# Синхронизация типов тренировок
|
|
for wt_data in payload.workout_types or []:
|
|
existing = db.query(models.WorkoutType).filter(models.WorkoutType.sync_id == wt_data.sync_id).first()
|
|
if not existing:
|
|
db_wt = models.WorkoutType(
|
|
**wt_data.dict(),
|
|
created_at=timestamp
|
|
)
|
|
db.add(db_wt)
|
|
created["workout_types"] += 1
|
|
|
|
# Синхронизация упражнений
|
|
for ex_data in payload.exercises or []:
|
|
existing = db.query(models.Exercise).filter(models.Exercise.sync_id == ex_data.sync_id).first()
|
|
if not existing:
|
|
db_ex = models.Exercise(
|
|
**ex_data.dict(),
|
|
created_at=timestamp,
|
|
updated_at=timestamp
|
|
)
|
|
db.add(db_ex)
|
|
created["exercises"] += 1
|
|
|
|
# Синхронизация типов замеров
|
|
for mt_data in payload.metric_types or []:
|
|
existing = db.query(models.MetricType).filter(models.MetricType.sync_id == mt_data.sync_id).first()
|
|
if not existing:
|
|
db_mt = models.MetricType(
|
|
**mt_data.dict(),
|
|
created_at=timestamp
|
|
)
|
|
db.add(db_mt)
|
|
created["metric_types"] += 1
|
|
|
|
# Синхронизация тренировок (с упражнениями и подходами)
|
|
for workout_data in payload.workouts or []:
|
|
existing = db.query(models.Workout).filter(models.Workout.sync_id == workout_data.sync_id).first()
|
|
if not existing:
|
|
db_workout = models.Workout(
|
|
sync_id=workout_data.sync_id,
|
|
client_id=workout_data.client_id,
|
|
workout_type_id=workout_data.workout_type_id,
|
|
start_time=workout_data.start_time,
|
|
end_time=workout_data.end_time,
|
|
notes=workout_data.notes,
|
|
created_at=timestamp,
|
|
updated_at=timestamp
|
|
)
|
|
db.add(db_workout)
|
|
db.flush()
|
|
|
|
# Упражнения
|
|
for we_data in workout_data.workout_exercises or []:
|
|
db_we = models.WorkoutExercise(
|
|
sync_id=we_data.sync_id,
|
|
workout_id=db_workout.id,
|
|
exercise_id=we_data.exercise_id,
|
|
exercise_name=we_data.exercise_name,
|
|
sort_order=we_data.sort_order,
|
|
created_at=timestamp
|
|
)
|
|
db.add(db_we)
|
|
db.flush()
|
|
|
|
# Подходы
|
|
for set_data in we_data.sets or []:
|
|
db_set = models.Set(
|
|
sync_id=set_data.sync_id,
|
|
workout_exercise_id=db_we.id,
|
|
is_dropset=set_data.is_dropset,
|
|
weight=set_data.weight,
|
|
reps=set_data.reps,
|
|
rating=set_data.rating,
|
|
notes=set_data.notes,
|
|
sort_order=set_data.sort_order,
|
|
timestamp=set_data.timestamp,
|
|
created_at=timestamp
|
|
)
|
|
db.add(db_set)
|
|
db.flush()
|
|
|
|
# Дроп-сет элементы
|
|
if set_data.is_dropset:
|
|
for item_data in set_data.dropset_items or []:
|
|
db_item = models.DropsetItem(
|
|
set_id=db_set.id,
|
|
weight=item_data.weight,
|
|
reps=item_data.reps,
|
|
sort_order=item_data.sort_order
|
|
)
|
|
db.add(db_item)
|
|
|
|
# Кардио
|
|
for cardio_data in workout_data.cardio_sessions or []:
|
|
db_cardio = models.Cardio(
|
|
sync_id=cardio_data.sync_id,
|
|
workout_id=db_workout.id,
|
|
cardio_type=cardio_data.cardio_type,
|
|
purpose=cardio_data.purpose,
|
|
duration=cardio_data.duration,
|
|
distance=cardio_data.distance,
|
|
speed=cardio_data.speed,
|
|
incline=cardio_data.incline,
|
|
resistance=cardio_data.resistance,
|
|
notes=cardio_data.notes,
|
|
sort_order=cardio_data.sort_order,
|
|
created_at=timestamp
|
|
)
|
|
db.add(db_cardio)
|
|
|
|
created["workouts"] += 1
|
|
|
|
# Синхронизация замеров тела
|
|
for metric_data in payload.body_metrics or []:
|
|
existing = db.query(models.BodyMetric).filter(models.BodyMetric.sync_id == metric_data.sync_id).first()
|
|
if not existing:
|
|
db_metric = models.BodyMetric(
|
|
sync_id=metric_data.sync_id,
|
|
client_id=metric_data.client_id,
|
|
metric_date=metric_data.metric_date,
|
|
notes=metric_data.notes,
|
|
created_at=timestamp
|
|
)
|
|
db.add(db_metric)
|
|
db.flush()
|
|
|
|
for value_data in metric_data.values or []:
|
|
db_value = models.BodyMetricValue(
|
|
body_metric_id=db_metric.id,
|
|
metric_type_id=value_data.metric_type_id,
|
|
value=value_data.value
|
|
)
|
|
db.add(db_value)
|
|
|
|
created["body_metrics"] += 1
|
|
|
|
db.commit()
|
|
|
|
return schemas.SyncResponse(
|
|
status="success",
|
|
created=created,
|
|
updated=updated
|
|
)
|