Files
autoayuno-api/models.py
Carlos Narro 21f5682aac telemetría: instalaciones únicas con id anónimo de instalación
Nueva tabla telemetry_active(install_id, day) (create_all la crea). add_telemetry
hace upsert (install_id, day) si viene install_id. El summary añade installsTotal
(únicas de siempre), installsActive (en el periodo) y dauToday. Tests: dos ids
únicos + uno duplicado → 2 únicas.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 14:42:02 +02:00

98 lines
3.9 KiB
Python

"""Modelos de la base de datos: alimentos y recetas del catálogo compartido."""
from sqlalchemy import Column, Float, Integer, String, JSON, DateTime, func
from db import Base
class Food(Base):
__tablename__ = "foods"
id = Column(String, primary_key=True)
name = Column(String, nullable=False, index=True)
kcal_100g = Column(Float, nullable=False)
zone = Column(String, default="otros")
portions = Column(JSON, default=list) # [{"name","grams"}]
barcode = Column(String, nullable=True)
source = Column(String, default="user") # off | user
off_id = Column(String, nullable=True, index=True)
author_token = Column(String, nullable=True)
created_at = Column(DateTime, server_default=func.now())
def to_app(self) -> dict:
"""Serializa a la forma que espera la app (Food.fromJson)."""
return {
"id": self.id,
"name": self.name,
"kcalPer100g": self.kcal_100g,
"zone": self.zone or "otros",
"portions": self.portions or [],
"origin": "remote",
}
class Recipe(Base):
__tablename__ = "recipes"
id = Column(String, primary_key=True)
name = Column(String, nullable=False, index=True)
servings = Column(Integer, default=1)
items = Column(JSON, default=list) # [{"food": {...}, "grams", "portionLabel"}]
category = Column(String, default="otros")
prep_minutes = Column(Integer, nullable=True)
steps = Column(JSON, default=list) # ["paso 1", "paso 2"]
notes = Column(String, nullable=True)
author_id = Column(String, nullable=True, index=True)
author_name = Column(String, nullable=True)
parent_id = Column(String, nullable=True)
parent_author_name = Column(String, nullable=True)
root_id = Column(String, nullable=True)
root_author_name = Column(String, nullable=True)
visibility = Column(String, default="public", index=True)
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
author_token = Column(String, nullable=True)
created_at = Column(DateTime, server_default=func.now())
def to_app(self) -> dict:
return {
"id": self.id,
"name": self.name,
"servings": self.servings,
"items": self.items or [],
"category": self.category or "otros",
"prepMinutes": self.prep_minutes,
"steps": self.steps or [],
"notes": self.notes,
"authorId": self.author_id,
"authorName": self.author_name,
"parentId": self.parent_id,
"parentAuthorName": self.parent_author_name,
"rootId": self.root_id,
"rootAuthorName": self.root_author_name,
"visibility": self.visibility or "public",
}
class TelemetryDaily(Base):
"""Contadores de uso ANÓNIMOS y agregados, por día y módulo. NO hay
identificador de usuario ni contenido de salud: solo cuántas instalaciones
tienen cada módulo activo (`active`) y cuántas veces lo abren (`opens`). La
fila especial module='__submissions__' cuenta los envíos del día (≈ nº de
instalaciones activas que ceden estadística)."""
__tablename__ = "telemetry_daily"
day = Column(String, primary_key=True) # 'YYYY-MM-DD'
module = Column(String, primary_key=True) # nombre del módulo/vista o '__submissions__'
active = Column(Integer, default=0)
opens = Column(Integer, default=0)
seconds = Column(Integer, default=0) # tiempo en pantalla acumulado
class TelemetryActive(Base):
"""Presencia (instalación, día) para contar instalaciones ÚNICAS, DAU/MAU y
retención. El install_id es un identificador anónimo de la instalación (un
número aleatorio, sin nada personal)."""
__tablename__ = "telemetry_active"
install_id = Column(String, primary_key=True)
day = Column(String, primary_key=True) # 'YYYY-MM-DD'