"""Backfill workout active_calories / steps from notes via regex.""" from __future__ import annotations import re import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parents[1])) from sqlalchemy import select from app.db.base import SessionLocal from app.db.models import WorkoutLog ACTIVE_PATTERNS = [ re.compile(r"(?:active|активн(?:ые|ых)?)\s*(?:калор|kcal|ккал)[^\d]*(\d+(?:[.,]\d+)?)", re.I), re.compile(r"(\d+(?:[.,]\d+)?)\s*(?:ккал|kcal)\s*(?:active|актив)", re.I), re.compile(r"актив[^\d]{0,20}(\d+(?:[.,]\d+)?)", re.I), ] TOTAL_PATTERNS = [ re.compile(r"(?:total|всего|сожжено|burned)[^\d]*(\d+(?:[.,]\d+)?)\s*(?:ккал|kcal)", re.I), re.compile(r"(\d+(?:[.,]\d+)?)\s*(?:ккал|kcal)\s*(?:total|всего)", re.I), ] STEPS_PATTERNS = [ re.compile(r"(?:шаг|step)s?\s*[:\-]?\s*(\d+)", re.I), re.compile(r"(\d+)\s*(?:шаг|steps)", re.I), ] def _first(patterns: list[re.Pattern[str]], text: str, *, as_int: bool = False): for pat in patterns: m = pat.search(text) if not m: continue raw = m.group(1).replace(",", ".") return int(float(raw)) if as_int else float(raw) return None def main() -> None: db = SessionLocal() updated = 0 try: rows = db.scalars(select(WorkoutLog)).all() for row in rows: text = f"{row.title or ''}\n{row.notes or ''}" changed = False if row.active_calories is None: val = _first(ACTIVE_PATTERNS, text) if val is not None: row.active_calories = float(val) changed = True if row.total_calories is None: val = _first(TOTAL_PATTERNS, text) if val is not None: row.total_calories = float(val) changed = True if row.steps is None: val = _first(STEPS_PATTERNS, text, as_int=True) if val is not None: row.steps = int(val) changed = True if changed: updated += 1 db.commit() print(f"updated {updated} workout rows") finally: db.close() if __name__ == "__main__": main()