workout-tracker / tracker /config.py
ronheichman's picture
Deploy workout tracker
c5700c6 verified
Raw
History Blame Contribute Delete
3.65 kB
from __future__ import annotations
from pathlib import Path
from pydantic import BaseModel, Field, model_validator
from pydantic_settings import BaseSettings, PydanticBaseSettingsSource, SettingsConfigDict, YamlConfigSettingsSource
from pydantic_settings.sources import EnvSettingsSource
from tracker.constants import DisplayDistanceUnit, DisplayWeightUnit
_TRACKER_REPO_ROOT = Path(__file__).resolve().parent.parent
_CONFIG_YAML_PATH = _TRACKER_REPO_ROOT / "config.yaml"
class DashboardLogin(BaseModel):
"""Small, explicit dashboard account for HF/demo deployments."""
label: str
email: str = Field(min_length=1)
password: str = Field(min_length=1)
role: str | None = None
notes: list[str] = Field(default_factory=list)
class TrackerConfig(BaseSettings):
"""Tracker app configuration: config.yaml first, then TRACKER_* environment overrides (HF Secrets)."""
model_config = SettingsConfigDict(env_prefix="TRACKER_", extra="ignore")
parser_data_dir: str
tracker_db_path: str
google_credentials_path: str
google_token_path: str
default_calendar_id: str
chores_calendar_id: str | None = None
workout_calendar_id: str | None = None
tracker_app_url: str = "http://127.0.0.1:8020"
host: str
port: int
active_program_id: int | None = None
agent_profile_path: str = "agent_profile.yaml"
agent_api_key: str | None = None
dashboard_password: str | None = None
dashboard_logins: list[DashboardLogin] = Field(default_factory=list)
session_secret: str | None = None
display_weight_unit: DisplayWeightUnit = DisplayWeightUnit.KG
display_distance_unit: DisplayDistanceUnit = DisplayDistanceUnit.KM
calibration_dumbbell_bench_lb_per_hand: float | None = None
calibration_dumbbell_squat_lb_per_hand: float | None = None
dumbbell_weight_quantum_lb: float = 5.0
@model_validator(mode="after")
def validate_dashboard_session(self) -> TrackerConfig:
if self.dashboard_password or self.dashboard_logins:
if not self.session_secret or len(self.session_secret) < 16:
msg = (
"session_secret must be set to at least 16 characters when "
"dashboard_password or dashboard_logins is set"
)
raise ValueError(msg)
return self
@property
def user_db_path(self) -> Path:
return Path(self.parser_data_dir) / "user.db"
@property
def query_db_path(self) -> Path:
return Path(self.parser_data_dir) / "web_query.db"
@property
def cache_db_path(self) -> Path:
return Path(self.parser_data_dir) / "web_cache.db"
@property
def tracker_db_full_path(self) -> Path:
path = Path(self.tracker_db_path)
return path if path.is_absolute() else _TRACKER_REPO_ROOT / path
@property
def agent_profile_resolved_path(self) -> Path:
path = Path(self.agent_profile_path)
return path if path.is_absolute() else _TRACKER_REPO_ROOT / path
@classmethod
def settings_customise_sources(
cls,
settings_cls: type[BaseSettings],
init_settings: PydanticBaseSettingsSource,
env_settings: PydanticBaseSettingsSource,
dotenv_settings: PydanticBaseSettingsSource,
file_secret_settings: PydanticBaseSettingsSource,
) -> tuple[PydanticBaseSettingsSource, ...]:
return (
init_settings,
EnvSettingsSource(settings_cls),
YamlConfigSettingsSource(settings_cls, yaml_file=str(_CONFIG_YAML_PATH)),
)
def load_config() -> TrackerConfig:
return TrackerConfig()