import os from dotenv import load_dotenv from pydantic import BaseModel # Load .env first (if present) load_dotenv(dotenv_path=".env", override=False) # Also load .env-example.txt as a fallback for local dev (does not override) load_dotenv(dotenv_path=".env-example.txt", override=False) class AppSettings(BaseModel): # Raw pieces db_host: str | None = os.getenv("DB_HOST") db_port: str | None = os.getenv("DB_PORT") db_name: str | None = os.getenv("DB_NAME") db_user: str | None = os.getenv("DB_USER") db_password: str | None = os.getenv("DB_PASSWORD") db_echo: bool = os.getenv("DB_ECHO", "false").lower() == "true" # Optional complete URL (takes precedence if set) database_url_env: str | None = os.getenv("DATABASE_URL") app_secret: str = os.getenv("APP_SECRET", "change_me") background_image_url: str | None = os.getenv("BACKGROUND_IMAGE_URL") # SMTP / Email settings smtp_host: str | None = os.getenv("SMTP_HOST") smtp_port: int | None = int(os.getenv("SMTP_PORT", "587")) smtp_user: str | None = os.getenv("SMTP_USER") smtp_password: str | None = os.getenv("SMTP_PASSWORD") smtp_use_tls: bool = os.getenv("SMTP_USE_TLS", "true").lower() == "true" smtp_from_email: str | None = os.getenv("SMTP_FROM_EMAIL") smtp_from_name: str = os.getenv("SMTP_FROM_NAME", "Workolik Team") # Default recipients for automated reports (comma-separated) report_recipients: str | None = os.getenv("REPORT_RECIPIENTS") @property def database_url(self) -> str: if self.database_url_env: # Normalize asyncpg to psycopg2 if needed if self.database_url_env.startswith("postgresql+asyncpg://"): return self.database_url_env.replace( "postgresql+asyncpg://", "postgresql+psycopg2://", 1 ) return self.database_url_env # Build from parts if all([self.db_host, self.db_port, self.db_name, self.db_user, self.db_password]): return ( f"postgresql+psycopg2://{self.db_user}:{self.db_password}" f"@{self.db_host}:{self.db_port}/{self.db_name}" ) # Fallback empty (will error at runtime if used) return "" # Fixed mapping of stores to tenant IDs and division codes # Used by analytics and data pages to scope queries per store STORES = [ {"label": "Porters Liquor Claremont - PC", "code": "PC", "tenant_id": 1}, {"label": "Porters Iluka - IP", "code": "IP", "tenant_id": 2}, {"label": "Cellarbrations at Morris Place - ML", "code": "ML", "tenant_id": 3}, {"label": "Cellarbrations at Lynwood - CL", "code": "CL4", "tenant_id": 4}, {"label": "Cellarbrations at Nicholson Road - NL", "code": "NL", "tenant_id": 5}, {"label": "Cellarbrations at Treeby - CL ", "code": "CL6", "tenant_id": 6}, {"label": "The Bottle-O Rossmoyne - RC", "code": "RC", "tenant_id": 7}, {"label": "Porters Liquor Piara Waters - PL", "code": "PL", "tenant_id": 8}, ] # Helper map for quick lookups by code (supports variants like CL-4 → CL4) STORE_CODE_TO_TENANT_ID: dict[str, int] = { "PC": 1, "IP": 2, "ML": 3, "CL4": 4, "CL-4": 4, "CL_4": 4, "CL": 4, # default CL → 4 "NL": 5, "NL5": 5, "NL-5": 5, "CL6": 6, "CL-6": 6, "CL_6": 6, "RC": 7, "PL": 8, }