From d807a50f7742d9109f053521da8250aa2dc1da97 Mon Sep 17 00:00:00 2001 From: zwitschi Date: Sun, 9 Nov 2025 16:49:27 +0100 Subject: [PATCH 001/109] v2 init --- .prettierrc | 8 - config/setup_production.env.example | 35 - config/setup_staging.env.example | 11 - config/setup_test.env.example | 14 - models/__init__.py | 10 - models/application_setting.py | 38 - models/capex.py | 71 -- models/consumption.py | 22 - models/currency.py | 24 - models/distribution.py | 14 - models/equipment.py | 17 - models/maintenance.py | 23 - models/opex.py | 63 -- models/parameters.py | 29 - models/production_output.py | 24 - models/role.py | 13 - models/scenario.py | 36 - models/simulation_result.py | 14 - models/theme_setting.py | 15 - models/user.py | 23 - requirements-test.txt | 4 +- requirements.txt | 2 +- routes/consumption.py | 52 -- routes/costs.py | 121 --- routes/currencies.py | 193 ---- routes/dependencies.py | 13 - routes/distributions.py | 38 - routes/equipment.py | 38 - routes/maintenance.py | 91 -- routes/parameters.py | 90 -- routes/production.py | 56 -- routes/reporting.py | 73 -- routes/scenarios.py | 42 - routes/settings.py | 110 --- routes/simulations.py | 126 --- routes/ui.py | 784 ---------------- routes/users.py | 107 --- schemas/user.py | 41 - scripts/backfill_currency.py | 157 ---- scripts/check_docs_links.py | 50 -- scripts/format_docs_md.py | 92 -- scripts/migrations/000_base.sql | 189 ---- scripts/seed_data.py | 268 ------ scripts/setup_database.py | 1233 -------------------------- services/reporting.py | 79 -- services/security.py | 59 -- services/settings.py | 230 ----- services/simulation.py | 144 --- templates/Dashboard.html | 94 -- templates/ParameterInput.html | 51 -- templates/ScenarioForm.html | 53 -- templates/consumption.html | 76 -- templates/costs.html | 129 --- templates/currencies.html | 131 --- templates/equipment.html | 78 -- templates/maintenance.html | 111 --- templates/production.html | 97 -- templates/reporting.html | 41 - templates/settings.html | 26 - templates/simulations.html | 41 - templates/theme_settings.html | 125 --- tests/__init__.py | 0 tests/e2e/conftest.py | 170 ---- tests/e2e/test_consumption.py | 50 -- tests/e2e/test_costs.py | 63 -- tests/e2e/test_currencies.py | 135 --- tests/e2e/test_dashboard.py | 17 - tests/e2e/test_equipment.py | 45 - tests/e2e/test_maintenance.py | 58 -- tests/e2e/test_production.py | 48 - tests/e2e/test_reporting.py | 9 - tests/e2e/test_scenarios.py | 43 - tests/e2e/test_smoke.py | 85 -- tests/unit/__init__.py | 0 tests/unit/conftest.py | 266 ------ tests/unit/test_auth.py | 231 ----- tests/unit/test_consumption.py | 77 -- tests/unit/test_costs.py | 123 --- tests/unit/test_currencies.py | 125 --- tests/unit/test_currency_workflow.py | 75 -- tests/unit/test_distribution.py | 71 -- tests/unit/test_equipment.py | 77 -- tests/unit/test_maintenance.py | 125 --- tests/unit/test_parameters.py | 126 --- tests/unit/test_production.py | 82 -- tests/unit/test_reporting.py | 123 --- tests/unit/test_router_validation.py | 94 -- tests/unit/test_scenario.py | 45 - tests/unit/test_seed_data.py | 46 - tests/unit/test_settings_routes.py | 53 -- tests/unit/test_settings_service.py | 149 ---- tests/unit/test_setup_database.py | 547 ------------ tests/unit/test_simulation.py | 232 ----- tests/unit/test_theme_settings.py | 56 -- tests/unit/test_ui_routes.py | 179 ---- tests/unit/test_validation.py | 28 - 96 files changed, 3 insertions(+), 9689 deletions(-) delete mode 100644 .prettierrc delete mode 100644 config/setup_production.env.example delete mode 100644 config/setup_staging.env.example delete mode 100644 config/setup_test.env.example delete mode 100644 models/__init__.py delete mode 100644 models/application_setting.py delete mode 100644 models/capex.py delete mode 100644 models/consumption.py delete mode 100644 models/currency.py delete mode 100644 models/distribution.py delete mode 100644 models/equipment.py delete mode 100644 models/maintenance.py delete mode 100644 models/opex.py delete mode 100644 models/parameters.py delete mode 100644 models/production_output.py delete mode 100644 models/role.py delete mode 100644 models/scenario.py delete mode 100644 models/simulation_result.py delete mode 100644 models/theme_setting.py delete mode 100644 models/user.py delete mode 100644 routes/consumption.py delete mode 100644 routes/costs.py delete mode 100644 routes/currencies.py delete mode 100644 routes/dependencies.py delete mode 100644 routes/distributions.py delete mode 100644 routes/equipment.py delete mode 100644 routes/maintenance.py delete mode 100644 routes/parameters.py delete mode 100644 routes/production.py delete mode 100644 routes/reporting.py delete mode 100644 routes/scenarios.py delete mode 100644 routes/settings.py delete mode 100644 routes/simulations.py delete mode 100644 routes/ui.py delete mode 100644 routes/users.py delete mode 100644 schemas/user.py delete mode 100644 scripts/backfill_currency.py delete mode 100644 scripts/check_docs_links.py delete mode 100644 scripts/format_docs_md.py delete mode 100644 scripts/migrations/000_base.sql delete mode 100644 scripts/seed_data.py delete mode 100644 scripts/setup_database.py delete mode 100644 services/reporting.py delete mode 100644 services/security.py delete mode 100644 services/settings.py delete mode 100644 services/simulation.py delete mode 100644 templates/Dashboard.html delete mode 100644 templates/ParameterInput.html delete mode 100644 templates/ScenarioForm.html delete mode 100644 templates/consumption.html delete mode 100644 templates/costs.html delete mode 100644 templates/currencies.html delete mode 100644 templates/equipment.html delete mode 100644 templates/maintenance.html delete mode 100644 templates/production.html delete mode 100644 templates/reporting.html delete mode 100644 templates/settings.html delete mode 100644 templates/simulations.html delete mode 100644 templates/theme_settings.html delete mode 100644 tests/__init__.py delete mode 100644 tests/e2e/conftest.py delete mode 100644 tests/e2e/test_consumption.py delete mode 100644 tests/e2e/test_costs.py delete mode 100644 tests/e2e/test_currencies.py delete mode 100644 tests/e2e/test_dashboard.py delete mode 100644 tests/e2e/test_equipment.py delete mode 100644 tests/e2e/test_maintenance.py delete mode 100644 tests/e2e/test_production.py delete mode 100644 tests/e2e/test_reporting.py delete mode 100644 tests/e2e/test_scenarios.py delete mode 100644 tests/e2e/test_smoke.py delete mode 100644 tests/unit/__init__.py delete mode 100644 tests/unit/conftest.py delete mode 100644 tests/unit/test_auth.py delete mode 100644 tests/unit/test_consumption.py delete mode 100644 tests/unit/test_costs.py delete mode 100644 tests/unit/test_currencies.py delete mode 100644 tests/unit/test_currency_workflow.py delete mode 100644 tests/unit/test_distribution.py delete mode 100644 tests/unit/test_equipment.py delete mode 100644 tests/unit/test_maintenance.py delete mode 100644 tests/unit/test_parameters.py delete mode 100644 tests/unit/test_production.py delete mode 100644 tests/unit/test_reporting.py delete mode 100644 tests/unit/test_router_validation.py delete mode 100644 tests/unit/test_scenario.py delete mode 100644 tests/unit/test_seed_data.py delete mode 100644 tests/unit/test_settings_routes.py delete mode 100644 tests/unit/test_settings_service.py delete mode 100644 tests/unit/test_setup_database.py delete mode 100644 tests/unit/test_simulation.py delete mode 100644 tests/unit/test_theme_settings.py delete mode 100644 tests/unit/test_ui_routes.py delete mode 100644 tests/unit/test_validation.py diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index 0ca3806..0000000 --- a/.prettierrc +++ /dev/null @@ -1,8 +0,0 @@ -{ - "semi": true, - "singleQuote": true, - "trailingComma": "es5", - "printWidth": 80, - "tabWidth": 2, - "useTabs": false -} diff --git a/config/setup_production.env.example b/config/setup_production.env.example deleted file mode 100644 index fefd6f2..0000000 --- a/config/setup_production.env.example +++ /dev/null @@ -1,35 +0,0 @@ -# Copy this file to config/setup_production.env and replace values with production secrets - -# Container image and runtime configuration -CALMINER_IMAGE=registry.example.com/calminer/api:latest -CALMINER_DOMAIN=calminer.example.com -TRAEFIK_ACME_EMAIL=ops@example.com -CALMINER_API_PORT=8000 -UVICORN_WORKERS=4 -UVICORN_LOG_LEVEL=info -CALMINER_NETWORK=calminer_backend -API_LIMIT_CPUS=1.0 -API_LIMIT_MEMORY=1g -API_RESERVATION_MEMORY=512m -TRAEFIK_LIMIT_CPUS=0.5 -TRAEFIK_LIMIT_MEMORY=512m -POSTGRES_LIMIT_CPUS=1.0 -POSTGRES_LIMIT_MEMORY=2g -POSTGRES_RESERVATION_MEMORY=1g - -# Application database connection -DATABASE_DRIVER=postgresql+psycopg2 -DATABASE_HOST=production-db.internal -DATABASE_PORT=5432 -DATABASE_NAME=calminer -DATABASE_USER=calminer_app -DATABASE_PASSWORD=ChangeMe123! -DATABASE_SCHEMA=public - -# Optional consolidated SQLAlchemy URL (overrides granular settings when set) -# DATABASE_URL=postgresql+psycopg2://calminer_app:ChangeMe123!@production-db.internal:5432/calminer - -# Superuser credentials used by scripts/setup_database.py for migrations/seed data -DATABASE_SUPERUSER=postgres -DATABASE_SUPERUSER_PASSWORD=ChangeMeSuper123! -DATABASE_SUPERUSER_DB=postgres diff --git a/config/setup_staging.env.example b/config/setup_staging.env.example deleted file mode 100644 index a166e1f..0000000 --- a/config/setup_staging.env.example +++ /dev/null @@ -1,11 +0,0 @@ -# Sample environment configuration for staging deployment -DATABASE_HOST=staging-db.internal -DATABASE_PORT=5432 -DATABASE_NAME=calminer_staging -DATABASE_USER=calminer_app -DATABASE_PASSWORD= - -# Admin connection used for provisioning database and roles -DATABASE_SUPERUSER=postgres -DATABASE_SUPERUSER_PASSWORD= -DATABASE_SUPERUSER_DB=postgres diff --git a/config/setup_test.env.example b/config/setup_test.env.example deleted file mode 100644 index 2228373..0000000 --- a/config/setup_test.env.example +++ /dev/null @@ -1,14 +0,0 @@ -# Sample environment configuration for running scripts/setup_database.py against a test instance -DATABASE_DRIVER=postgresql -DATABASE_HOST=postgres -DATABASE_PORT=5432 -DATABASE_NAME=calminer_test -DATABASE_USER=calminer_test -DATABASE_PASSWORD= -# optional: specify schema if different from 'public' -#DATABASE_SCHEMA=public - -# Admin connection used for provisioning database and roles -DATABASE_SUPERUSER=postgres -DATABASE_SUPERUSER_PASSWORD= -DATABASE_SUPERUSER_DB=postgres diff --git a/models/__init__.py b/models/__init__.py deleted file mode 100644 index a46e508..0000000 --- a/models/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -""" -models package initializer. Import key models so they're registered -with the shared Base.metadata when the package is imported by tests. -""" - -from . import application_setting # noqa: F401 -from . import currency # noqa: F401 -from . import role # noqa: F401 -from . import user # noqa: F401 -from . import theme_setting # noqa: F401 diff --git a/models/application_setting.py b/models/application_setting.py deleted file mode 100644 index ed98160..0000000 --- a/models/application_setting.py +++ /dev/null @@ -1,38 +0,0 @@ -from datetime import datetime -from typing import Optional - -from sqlalchemy import Boolean, DateTime, String, Text -from sqlalchemy.orm import Mapped, mapped_column -from sqlalchemy.sql import func - -from config.database import Base - - -class ApplicationSetting(Base): - __tablename__ = "application_setting" - - id: Mapped[int] = mapped_column(primary_key=True, index=True) - key: Mapped[str] = mapped_column(String(128), unique=True, nullable=False) - value: Mapped[str] = mapped_column(Text, nullable=False) - value_type: Mapped[str] = mapped_column( - String(32), nullable=False, default="string" - ) - category: Mapped[str] = mapped_column( - String(32), nullable=False, default="general" - ) - description: Mapped[Optional[str]] = mapped_column(Text, nullable=True) - is_editable: Mapped[bool] = mapped_column( - Boolean, nullable=False, default=True - ) - created_at: Mapped[datetime] = mapped_column( - DateTime(timezone=True), server_default=func.now(), nullable=False - ) - updated_at: Mapped[datetime] = mapped_column( - DateTime(timezone=True), - server_default=func.now(), - onupdate=func.now(), - nullable=False, - ) - - def __repr__(self) -> str: - return f"" diff --git a/models/capex.py b/models/capex.py deleted file mode 100644 index 68b6749..0000000 --- a/models/capex.py +++ /dev/null @@ -1,71 +0,0 @@ -from sqlalchemy import event, text -from sqlalchemy import Column, Integer, Float, String, ForeignKey -from sqlalchemy.orm import relationship -from config.database import Base - - -class Capex(Base): - __tablename__ = "capex" - - id = Column(Integer, primary_key=True, index=True) - scenario_id = Column(Integer, ForeignKey("scenario.id"), nullable=False) - amount = Column(Float, nullable=False) - description = Column(String, nullable=True) - currency_id = Column(Integer, ForeignKey("currency.id"), nullable=False) - - scenario = relationship("Scenario", back_populates="capex_items") - currency = relationship("Currency", back_populates="capex_items") - - def __repr__(self): - return ( - f"" - ) - - @property - def currency_code(self) -> str: - return self.currency.code if self.currency else None - - @currency_code.setter - def currency_code(self, value: str) -> None: - # store pending code so application code or migrations can pick it up - setattr( - self, "_currency_code_pending", (value or "USD").strip().upper() - ) - - -# SQLAlchemy event handlers to ensure currency_id is set before insert/update - - -def _resolve_currency(mapper, connection, target): - # If currency_id already set, nothing to do - if getattr(target, "currency_id", None): - return - code = getattr(target, "_currency_code_pending", None) or "USD" - # Try to find existing currency id - row = connection.execute( - text("SELECT id FROM currency WHERE code = :code"), {"code": code} - ).fetchone() - if row: - cid = row[0] - else: - # Insert new currency and attempt to get lastrowid - res = connection.execute( - text( - "INSERT INTO currency (code, name, symbol, is_active) VALUES (:code, :name, :symbol, :active)" - ), - {"code": code, "name": code, "symbol": None, "active": True}, - ) - try: - cid = res.lastrowid - except Exception: - # fallback: select after insert - cid = connection.execute( - text("SELECT id FROM currency WHERE code = :code"), - {"code": code}, - ).scalar() - target.currency_id = cid - - -event.listen(Capex, "before_insert", _resolve_currency) -event.listen(Capex, "before_update", _resolve_currency) diff --git a/models/consumption.py b/models/consumption.py deleted file mode 100644 index c5239bc..0000000 --- a/models/consumption.py +++ /dev/null @@ -1,22 +0,0 @@ -from sqlalchemy import Column, Integer, Float, String, ForeignKey -from sqlalchemy.orm import relationship -from config.database import Base - - -class Consumption(Base): - __tablename__ = "consumption" - - id = Column(Integer, primary_key=True, index=True) - scenario_id = Column(Integer, ForeignKey("scenario.id"), nullable=False) - amount = Column(Float, nullable=False) - description = Column(String, nullable=True) - unit_name = Column(String(64), nullable=True) - unit_symbol = Column(String(16), nullable=True) - - scenario = relationship("Scenario", back_populates="consumption_items") - - def __repr__(self): - return ( - f"" - ) diff --git a/models/currency.py b/models/currency.py deleted file mode 100644 index de95abd..0000000 --- a/models/currency.py +++ /dev/null @@ -1,24 +0,0 @@ -from sqlalchemy import Column, Integer, String, Boolean -from sqlalchemy.orm import relationship -from config.database import Base - - -class Currency(Base): - __tablename__ = "currency" - - id = Column(Integer, primary_key=True, index=True) - code = Column(String(3), nullable=False, unique=True, index=True) - name = Column(String(128), nullable=False) - symbol = Column(String(8), nullable=True) - is_active = Column(Boolean, nullable=False, default=True) - - # reverse relationships (optional) - capex_items = relationship( - "Capex", back_populates="currency", lazy="select" - ) - opex_items = relationship("Opex", back_populates="currency", lazy="select") - - def __repr__(self): - return ( - f"" - ) diff --git a/models/distribution.py b/models/distribution.py deleted file mode 100644 index 9f9832a..0000000 --- a/models/distribution.py +++ /dev/null @@ -1,14 +0,0 @@ -from sqlalchemy import Column, Integer, String, JSON -from config.database import Base - - -class Distribution(Base): - __tablename__ = "distribution" - - id = Column(Integer, primary_key=True, index=True) - name = Column(String, nullable=False) - distribution_type = Column(String, nullable=False) - parameters = Column(JSON, nullable=True) - - def __repr__(self): - return f"" diff --git a/models/equipment.py b/models/equipment.py deleted file mode 100644 index e431891..0000000 --- a/models/equipment.py +++ /dev/null @@ -1,17 +0,0 @@ -from sqlalchemy import Column, Integer, String, ForeignKey -from sqlalchemy.orm import relationship -from config.database import Base - - -class Equipment(Base): - __tablename__ = "equipment" - - id = Column(Integer, primary_key=True, index=True) - scenario_id = Column(Integer, ForeignKey("scenario.id"), nullable=False) - name = Column(String, nullable=False) - description = Column(String, nullable=True) - - scenario = relationship("Scenario", back_populates="equipment_items") - - def __repr__(self): - return f"" diff --git a/models/maintenance.py b/models/maintenance.py deleted file mode 100644 index 43a7aea..0000000 --- a/models/maintenance.py +++ /dev/null @@ -1,23 +0,0 @@ -from sqlalchemy import Column, Date, Float, ForeignKey, Integer, String -from sqlalchemy.orm import relationship -from config.database import Base - - -class Maintenance(Base): - __tablename__ = "maintenance" - - id = Column(Integer, primary_key=True, index=True) - equipment_id = Column(Integer, ForeignKey("equipment.id"), nullable=False) - scenario_id = Column(Integer, ForeignKey("scenario.id"), nullable=False) - maintenance_date = Column(Date, nullable=False) - description = Column(String, nullable=True) - cost = Column(Float, nullable=False) - - equipment = relationship("Equipment") - scenario = relationship("Scenario", back_populates="maintenance_items") - - def __repr__(self) -> str: - return ( - f"" - ) diff --git a/models/opex.py b/models/opex.py deleted file mode 100644 index 5c0e703..0000000 --- a/models/opex.py +++ /dev/null @@ -1,63 +0,0 @@ -from sqlalchemy import event, text -from sqlalchemy import Column, Integer, Float, String, ForeignKey -from sqlalchemy.orm import relationship -from config.database import Base - - -class Opex(Base): - __tablename__ = "opex" - - id = Column(Integer, primary_key=True, index=True) - scenario_id = Column(Integer, ForeignKey("scenario.id"), nullable=False) - amount = Column(Float, nullable=False) - description = Column(String, nullable=True) - currency_id = Column(Integer, ForeignKey("currency.id"), nullable=False) - - scenario = relationship("Scenario", back_populates="opex_items") - currency = relationship("Currency", back_populates="opex_items") - - def __repr__(self): - return ( - f"" - ) - - @property - def currency_code(self) -> str: - return self.currency.code if self.currency else None - - @currency_code.setter - def currency_code(self, value: str) -> None: - setattr( - self, "_currency_code_pending", (value or "USD").strip().upper() - ) - - -def _resolve_currency_opex(mapper, connection, target): - if getattr(target, "currency_id", None): - return - code = getattr(target, "_currency_code_pending", None) or "USD" - row = connection.execute( - text("SELECT id FROM currency WHERE code = :code"), {"code": code} - ).fetchone() - if row: - cid = row[0] - else: - res = connection.execute( - text( - "INSERT INTO currency (code, name, symbol, is_active) VALUES (:code, :name, :symbol, :active)" - ), - {"code": code, "name": code, "symbol": None, "active": True}, - ) - try: - cid = res.lastrowid - except Exception: - cid = connection.execute( - text("SELECT id FROM currency WHERE code = :code"), - {"code": code}, - ).scalar() - target.currency_id = cid - - -event.listen(Opex, "before_insert", _resolve_currency_opex) -event.listen(Opex, "before_update", _resolve_currency_opex) diff --git a/models/parameters.py b/models/parameters.py deleted file mode 100644 index 822a011..0000000 --- a/models/parameters.py +++ /dev/null @@ -1,29 +0,0 @@ -from typing import Any, Dict, Optional - -from sqlalchemy import ForeignKey, JSON -from sqlalchemy.orm import Mapped, mapped_column, relationship -from config.database import Base - - -class Parameter(Base): - __tablename__ = "parameter" - - id: Mapped[int] = mapped_column(primary_key=True, index=True) - scenario_id: Mapped[int] = mapped_column( - ForeignKey("scenario.id"), nullable=False - ) - name: Mapped[str] = mapped_column(nullable=False) - value: Mapped[float] = mapped_column(nullable=False) - distribution_id: Mapped[Optional[int]] = mapped_column( - ForeignKey("distribution.id"), nullable=True - ) - distribution_type: Mapped[Optional[str]] = mapped_column(nullable=True) - distribution_parameters: Mapped[Optional[Dict[str, Any]]] = mapped_column( - JSON, nullable=True - ) - - scenario = relationship("Scenario", back_populates="parameters") - distribution = relationship("Distribution") - - def __repr__(self): - return f"" diff --git a/models/production_output.py b/models/production_output.py deleted file mode 100644 index fde7cb8..0000000 --- a/models/production_output.py +++ /dev/null @@ -1,24 +0,0 @@ -from sqlalchemy import Column, Integer, Float, String, ForeignKey -from sqlalchemy.orm import relationship -from config.database import Base - - -class ProductionOutput(Base): - __tablename__ = "production_output" - - id = Column(Integer, primary_key=True, index=True) - scenario_id = Column(Integer, ForeignKey("scenario.id"), nullable=False) - amount = Column(Float, nullable=False) - description = Column(String, nullable=True) - unit_name = Column(String(64), nullable=True) - unit_symbol = Column(String(16), nullable=True) - - scenario = relationship( - "Scenario", back_populates="production_output_items" - ) - - def __repr__(self): - return ( - f"" - ) diff --git a/models/role.py b/models/role.py deleted file mode 100644 index 3351908..0000000 --- a/models/role.py +++ /dev/null @@ -1,13 +0,0 @@ -from sqlalchemy import Column, Integer, String -from sqlalchemy.orm import relationship - -from config.database import Base - - -class Role(Base): - __tablename__ = "roles" - - id = Column(Integer, primary_key=True, index=True) - name = Column(String, unique=True, index=True) - - users = relationship("User", back_populates="role") diff --git a/models/scenario.py b/models/scenario.py deleted file mode 100644 index 66d4fd2..0000000 --- a/models/scenario.py +++ /dev/null @@ -1,36 +0,0 @@ -from sqlalchemy import Column, Integer, String, DateTime, func -from sqlalchemy.orm import relationship -from models.simulation_result import SimulationResult -from models.capex import Capex -from models.opex import Opex -from models.consumption import Consumption -from models.production_output import ProductionOutput -from models.equipment import Equipment -from models.maintenance import Maintenance -from config.database import Base - - -class Scenario(Base): - __tablename__ = "scenario" - - id = Column(Integer, primary_key=True, index=True) - name = Column(String, unique=True, nullable=False) - description = Column(String) - created_at = Column(DateTime(timezone=True), server_default=func.now()) - updated_at = Column(DateTime(timezone=True), onupdate=func.now()) - parameters = relationship("Parameter", back_populates="scenario") - simulation_results = relationship( - SimulationResult, back_populates="scenario" - ) - capex_items = relationship(Capex, back_populates="scenario") - opex_items = relationship(Opex, back_populates="scenario") - consumption_items = relationship(Consumption, back_populates="scenario") - production_output_items = relationship( - ProductionOutput, back_populates="scenario" - ) - equipment_items = relationship(Equipment, back_populates="scenario") - maintenance_items = relationship(Maintenance, back_populates="scenario") - - # relationships can be defined later - def __repr__(self): - return f"" diff --git a/models/simulation_result.py b/models/simulation_result.py deleted file mode 100644 index c5edac7..0000000 --- a/models/simulation_result.py +++ /dev/null @@ -1,14 +0,0 @@ -from sqlalchemy import Column, Integer, Float, ForeignKey -from sqlalchemy.orm import relationship -from config.database import Base - - -class SimulationResult(Base): - __tablename__ = "simulation_result" - - id = Column(Integer, primary_key=True, index=True) - scenario_id = Column(Integer, ForeignKey("scenario.id"), nullable=False) - iteration = Column(Integer, nullable=False) - result = Column(Float, nullable=False) - - scenario = relationship("Scenario", back_populates="simulation_results") diff --git a/models/theme_setting.py b/models/theme_setting.py deleted file mode 100644 index 1e20c64..0000000 --- a/models/theme_setting.py +++ /dev/null @@ -1,15 +0,0 @@ -from sqlalchemy import Column, Integer, String - -from config.database import Base - - -class ThemeSetting(Base): - __tablename__ = "theme_settings" - - id = Column(Integer, primary_key=True, index=True) - theme_name = Column(String, unique=True, index=True) - primary_color = Column(String) - secondary_color = Column(String) - accent_color = Column(String) - background_color = Column(String) - text_color = Column(String) diff --git a/models/user.py b/models/user.py deleted file mode 100644 index 5ee8654..0000000 --- a/models/user.py +++ /dev/null @@ -1,23 +0,0 @@ -from sqlalchemy import Column, Integer, String, ForeignKey -from sqlalchemy.orm import relationship - -from config.database import Base -from services.security import get_password_hash, verify_password - - -class User(Base): - __tablename__ = "users" - - id = Column(Integer, primary_key=True, index=True) - username = Column(String, unique=True, index=True) - email = Column(String, unique=True, index=True) - hashed_password = Column(String) - role_id = Column(Integer, ForeignKey("roles.id")) - - role = relationship("Role", back_populates="users") - - def set_password(self, password: str): - self.hashed_password = get_password_hash(password) - - def check_password(self, password: str) -> bool: - return verify_password(password, str(self.hashed_password)) diff --git a/requirements-test.txt b/requirements-test.txt index b2ac481..1e96b46 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,7 +1,7 @@ -playwright pytest pytest-cov pytest-httpx -pytest-playwright python-jose ruff +black +mypy \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 0f27fee..e07bb5c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ fastapi -pydantic>=2.0,<3.0 +pydantic uvicorn sqlalchemy psycopg2-binary diff --git a/routes/consumption.py b/routes/consumption.py deleted file mode 100644 index e03785d..0000000 --- a/routes/consumption.py +++ /dev/null @@ -1,52 +0,0 @@ -from typing import List, Optional - -from fastapi import APIRouter, Depends, status -from pydantic import BaseModel, ConfigDict, PositiveFloat, field_validator -from sqlalchemy.orm import Session - -from models.consumption import Consumption -from routes.dependencies import get_db - - -router = APIRouter(prefix="/api/consumption", tags=["Consumption"]) - - -class ConsumptionBase(BaseModel): - scenario_id: int - amount: PositiveFloat - description: Optional[str] = None - unit_name: Optional[str] = None - unit_symbol: Optional[str] = None - - @field_validator("unit_name", "unit_symbol") - @classmethod - def _normalize_text(cls, value: Optional[str]) -> Optional[str]: - if value is None: - return None - stripped = value.strip() - return stripped or None - - -class ConsumptionCreate(ConsumptionBase): - pass - - -class ConsumptionRead(ConsumptionBase): - id: int - model_config = ConfigDict(from_attributes=True) - - -@router.post( - "/", response_model=ConsumptionRead, status_code=status.HTTP_201_CREATED -) -def create_consumption(item: ConsumptionCreate, db: Session = Depends(get_db)): - db_item = Consumption(**item.model_dump()) - db.add(db_item) - db.commit() - db.refresh(db_item) - return db_item - - -@router.get("/", response_model=List[ConsumptionRead]) -def list_consumption(db: Session = Depends(get_db)): - return db.query(Consumption).all() diff --git a/routes/costs.py b/routes/costs.py deleted file mode 100644 index e22f18a..0000000 --- a/routes/costs.py +++ /dev/null @@ -1,121 +0,0 @@ -from typing import List, Optional - -from fastapi import APIRouter, Depends -from pydantic import BaseModel, ConfigDict, field_validator -from sqlalchemy.orm import Session - -from models.capex import Capex -from models.opex import Opex -from routes.dependencies import get_db - -router = APIRouter(prefix="/api/costs", tags=["Costs"]) -# Pydantic schemas for CAPEX and OPEX - - -class _CostBase(BaseModel): - scenario_id: int - amount: float - description: Optional[str] = None - currency_code: Optional[str] = "USD" - currency_id: Optional[int] = None - - @field_validator("currency_code") - @classmethod - def _normalize_currency(cls, value: Optional[str]) -> str: - code = (value or "USD").strip().upper() - return code[:3] if len(code) > 3 else code - - -class CapexCreate(_CostBase): - pass - - -class CapexRead(_CostBase): - id: int - # use from_attributes so Pydantic reads attributes off SQLAlchemy model - model_config = ConfigDict(from_attributes=True) - - # optionally include nested currency info - currency: Optional["CurrencyRead"] = None - - -class OpexCreate(_CostBase): - pass - - -class OpexRead(_CostBase): - id: int - model_config = ConfigDict(from_attributes=True) - currency: Optional["CurrencyRead"] = None - - -class CurrencyRead(BaseModel): - id: int - code: str - name: Optional[str] = None - symbol: Optional[str] = None - is_active: Optional[bool] = True - - model_config = ConfigDict(from_attributes=True) - - -# forward refs -CapexRead.model_rebuild() -OpexRead.model_rebuild() - - -# Capex endpoints -@router.post("/capex", response_model=CapexRead) -def create_capex(item: CapexCreate, db: Session = Depends(get_db)): - payload = item.model_dump() - # Prefer explicit currency_id if supplied - cid = payload.get("currency_id") - if not cid: - code = (payload.pop("currency_code", "USD") or "USD").strip().upper() - currency_cls = __import__( - "models.currency", fromlist=["Currency"] - ).Currency - currency = db.query(currency_cls).filter_by(code=code).one_or_none() - if currency is None: - currency = currency_cls(code=code, name=code, symbol=None) - db.add(currency) - db.flush() - payload["currency_id"] = currency.id - db_item = Capex(**payload) - db.add(db_item) - db.commit() - db.refresh(db_item) - return db_item - - -@router.get("/capex", response_model=List[CapexRead]) -def list_capex(db: Session = Depends(get_db)): - return db.query(Capex).all() - - -# Opex endpoints -@router.post("/opex", response_model=OpexRead) -def create_opex(item: OpexCreate, db: Session = Depends(get_db)): - payload = item.model_dump() - cid = payload.get("currency_id") - if not cid: - code = (payload.pop("currency_code", "USD") or "USD").strip().upper() - currency_cls = __import__( - "models.currency", fromlist=["Currency"] - ).Currency - currency = db.query(currency_cls).filter_by(code=code).one_or_none() - if currency is None: - currency = currency_cls(code=code, name=code, symbol=None) - db.add(currency) - db.flush() - payload["currency_id"] = currency.id - db_item = Opex(**payload) - db.add(db_item) - db.commit() - db.refresh(db_item) - return db_item - - -@router.get("/opex", response_model=List[OpexRead]) -def list_opex(db: Session = Depends(get_db)): - return db.query(Opex).all() diff --git a/routes/currencies.py b/routes/currencies.py deleted file mode 100644 index 8899366..0000000 --- a/routes/currencies.py +++ /dev/null @@ -1,193 +0,0 @@ -from typing import List, Optional - -from fastapi import APIRouter, Depends, HTTPException, Query, status -from pydantic import BaseModel, ConfigDict, Field, field_validator -from sqlalchemy.orm import Session -from sqlalchemy.exc import IntegrityError - -from models.currency import Currency -from routes.dependencies import get_db - -router = APIRouter(prefix="/api/currencies", tags=["Currencies"]) - - -DEFAULT_CURRENCY_CODE = "USD" -DEFAULT_CURRENCY_NAME = "US Dollar" -DEFAULT_CURRENCY_SYMBOL = "$" - - -class CurrencyBase(BaseModel): - name: str = Field(..., min_length=1, max_length=128) - symbol: Optional[str] = Field(default=None, max_length=8) - - @staticmethod - def _normalize_symbol(value: Optional[str]) -> Optional[str]: - if value is None: - return None - value = value.strip() - return value or None - - @field_validator("name") - @classmethod - def _strip_name(cls, value: str) -> str: - return value.strip() - - @field_validator("symbol") - @classmethod - def _strip_symbol(cls, value: Optional[str]) -> Optional[str]: - return cls._normalize_symbol(value) - - -class CurrencyCreate(CurrencyBase): - code: str = Field(..., min_length=3, max_length=3) - is_active: bool = True - - @field_validator("code") - @classmethod - def _normalize_code(cls, value: str) -> str: - return value.strip().upper() - - -class CurrencyUpdate(CurrencyBase): - is_active: Optional[bool] = None - - -class CurrencyActivation(BaseModel): - is_active: bool - - -class CurrencyRead(CurrencyBase): - id: int - code: str - is_active: bool - - model_config = ConfigDict(from_attributes=True) - - -def _ensure_default_currency(db: Session) -> Currency: - existing = ( - db.query(Currency) - .filter(Currency.code == DEFAULT_CURRENCY_CODE) - .one_or_none() - ) - if existing: - return existing - - default_currency = Currency( - code=DEFAULT_CURRENCY_CODE, - name=DEFAULT_CURRENCY_NAME, - symbol=DEFAULT_CURRENCY_SYMBOL, - is_active=True, - ) - db.add(default_currency) - try: - db.commit() - except IntegrityError: - db.rollback() - existing = ( - db.query(Currency) - .filter(Currency.code == DEFAULT_CURRENCY_CODE) - .one() - ) - return existing - db.refresh(default_currency) - return default_currency - - -def _get_currency_or_404(db: Session, code: str) -> Currency: - normalized = code.strip().upper() - currency = ( - db.query(Currency).filter(Currency.code == normalized).one_or_none() - ) - if currency is None: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="Currency not found" - ) - return currency - - -@router.get("/", response_model=List[CurrencyRead]) -def list_currencies( - include_inactive: bool = Query( - False, description="Include inactive currencies" - ), - db: Session = Depends(get_db), -): - _ensure_default_currency(db) - query = db.query(Currency) - if not include_inactive: - query = query.filter(Currency.is_active.is_(True)) - currencies = query.order_by(Currency.code).all() - return currencies - - -@router.post( - "/", response_model=CurrencyRead, status_code=status.HTTP_201_CREATED -) -def create_currency(payload: CurrencyCreate, db: Session = Depends(get_db)): - code = payload.code - existing = db.query(Currency).filter(Currency.code == code).one_or_none() - if existing is not None: - raise HTTPException( - status_code=status.HTTP_409_CONFLICT, - detail=f"Currency '{code}' already exists", - ) - - currency = Currency( - code=code, - name=payload.name, - symbol=CurrencyBase._normalize_symbol(payload.symbol), - is_active=payload.is_active, - ) - db.add(currency) - db.commit() - db.refresh(currency) - return currency - - -@router.put("/{code}", response_model=CurrencyRead) -def update_currency( - code: str, payload: CurrencyUpdate, db: Session = Depends(get_db) -): - currency = _get_currency_or_404(db, code) - - if payload.name is not None: - setattr(currency, "name", payload.name) - if payload.symbol is not None or payload.symbol == "": - setattr( - currency, - "symbol", - CurrencyBase._normalize_symbol(payload.symbol), - ) - if payload.is_active is not None: - code_value = getattr(currency, "code") - if code_value == DEFAULT_CURRENCY_CODE and payload.is_active is False: - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail="The default currency cannot be deactivated.", - ) - setattr(currency, "is_active", payload.is_active) - - db.add(currency) - db.commit() - db.refresh(currency) - return currency - - -@router.patch("/{code}/activation", response_model=CurrencyRead) -def toggle_currency_activation( - code: str, body: CurrencyActivation, db: Session = Depends(get_db) -): - currency = _get_currency_or_404(db, code) - code_value = getattr(currency, "code") - if code_value == DEFAULT_CURRENCY_CODE and body.is_active is False: - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail="The default currency cannot be deactivated.", - ) - - setattr(currency, "is_active", body.is_active) - db.add(currency) - db.commit() - db.refresh(currency) - return currency diff --git a/routes/dependencies.py b/routes/dependencies.py deleted file mode 100644 index 0afc871..0000000 --- a/routes/dependencies.py +++ /dev/null @@ -1,13 +0,0 @@ -from collections.abc import Generator - -from sqlalchemy.orm import Session - -from config.database import SessionLocal - - -def get_db() -> Generator[Session, None, None]: - db = SessionLocal() - try: - yield db - finally: - db.close() diff --git a/routes/distributions.py b/routes/distributions.py deleted file mode 100644 index 34a0cc8..0000000 --- a/routes/distributions.py +++ /dev/null @@ -1,38 +0,0 @@ -from typing import Dict, List - -from fastapi import APIRouter, Depends -from pydantic import BaseModel, ConfigDict -from sqlalchemy.orm import Session - -from models.distribution import Distribution -from routes.dependencies import get_db - -router = APIRouter(prefix="/api/distributions", tags=["Distributions"]) - - -class DistributionCreate(BaseModel): - name: str - distribution_type: str - parameters: Dict[str, float | int] - - -class DistributionRead(DistributionCreate): - id: int - model_config = ConfigDict(from_attributes=True) - - -@router.post("/", response_model=DistributionRead) -async def create_distribution( - dist: DistributionCreate, db: Session = Depends(get_db) -): - db_dist = Distribution(**dist.model_dump()) - db.add(db_dist) - db.commit() - db.refresh(db_dist) - return db_dist - - -@router.get("/", response_model=List[DistributionRead]) -async def list_distributions(db: Session = Depends(get_db)): - dists = db.query(Distribution).all() - return dists diff --git a/routes/equipment.py b/routes/equipment.py deleted file mode 100644 index a5800a9..0000000 --- a/routes/equipment.py +++ /dev/null @@ -1,38 +0,0 @@ -from typing import List, Optional - -from fastapi import APIRouter, Depends -from pydantic import BaseModel, ConfigDict -from sqlalchemy.orm import Session - -from models.equipment import Equipment -from routes.dependencies import get_db - -router = APIRouter(prefix="/api/equipment", tags=["Equipment"]) -# Pydantic schemas - - -class EquipmentCreate(BaseModel): - scenario_id: int - name: str - description: Optional[str] = None - - -class EquipmentRead(EquipmentCreate): - id: int - model_config = ConfigDict(from_attributes=True) - - -@router.post("/", response_model=EquipmentRead) -async def create_equipment( - item: EquipmentCreate, db: Session = Depends(get_db) -): - db_item = Equipment(**item.model_dump()) - db.add(db_item) - db.commit() - db.refresh(db_item) - return db_item - - -@router.get("/", response_model=List[EquipmentRead]) -async def list_equipment(db: Session = Depends(get_db)): - return db.query(Equipment).all() diff --git a/routes/maintenance.py b/routes/maintenance.py deleted file mode 100644 index 93683fd..0000000 --- a/routes/maintenance.py +++ /dev/null @@ -1,91 +0,0 @@ -from datetime import date -from typing import List, Optional - -from fastapi import APIRouter, Depends, HTTPException, status -from pydantic import BaseModel, ConfigDict, PositiveFloat -from sqlalchemy.orm import Session - -from models.maintenance import Maintenance -from routes.dependencies import get_db - - -router = APIRouter(prefix="/api/maintenance", tags=["Maintenance"]) - - -class MaintenanceBase(BaseModel): - equipment_id: int - scenario_id: int - maintenance_date: date - description: Optional[str] = None - cost: PositiveFloat - - -class MaintenanceCreate(MaintenanceBase): - pass - - -class MaintenanceUpdate(MaintenanceBase): - pass - - -class MaintenanceRead(MaintenanceBase): - id: int - model_config = ConfigDict(from_attributes=True) - - -def _get_maintenance_or_404(db: Session, maintenance_id: int) -> Maintenance: - maintenance = ( - db.query(Maintenance).filter(Maintenance.id == maintenance_id).first() - ) - if maintenance is None: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, - detail=f"Maintenance record {maintenance_id} not found", - ) - return maintenance - - -@router.post( - "/", response_model=MaintenanceRead, status_code=status.HTTP_201_CREATED -) -def create_maintenance( - maintenance: MaintenanceCreate, db: Session = Depends(get_db) -): - db_maintenance = Maintenance(**maintenance.model_dump()) - db.add(db_maintenance) - db.commit() - db.refresh(db_maintenance) - return db_maintenance - - -@router.get("/", response_model=List[MaintenanceRead]) -def list_maintenance( - skip: int = 0, limit: int = 100, db: Session = Depends(get_db) -): - return db.query(Maintenance).offset(skip).limit(limit).all() - - -@router.get("/{maintenance_id}", response_model=MaintenanceRead) -def get_maintenance(maintenance_id: int, db: Session = Depends(get_db)): - return _get_maintenance_or_404(db, maintenance_id) - - -@router.put("/{maintenance_id}", response_model=MaintenanceRead) -def update_maintenance( - maintenance_id: int, - payload: MaintenanceUpdate, - db: Session = Depends(get_db), -): - db_maintenance = _get_maintenance_or_404(db, maintenance_id) - for field, value in payload.model_dump().items(): - setattr(db_maintenance, field, value) - db.commit() - db.refresh(db_maintenance) - return db_maintenance - - -@router.delete("/{maintenance_id}", status_code=status.HTTP_204_NO_CONTENT) -def delete_maintenance(maintenance_id: int, db: Session = Depends(get_db)): - db_maintenance = _get_maintenance_or_404(db, maintenance_id) - db.delete(db_maintenance) - db.commit() diff --git a/routes/parameters.py b/routes/parameters.py deleted file mode 100644 index 59f09c8..0000000 --- a/routes/parameters.py +++ /dev/null @@ -1,90 +0,0 @@ -from typing import Any, Dict, List, Optional - -from fastapi import APIRouter, Depends, HTTPException -from pydantic import BaseModel, ConfigDict, field_validator -from sqlalchemy.orm import Session - -from models.distribution import Distribution -from models.parameters import Parameter -from models.scenario import Scenario -from routes.dependencies import get_db - -router = APIRouter(prefix="/api/parameters", tags=["parameters"]) - - -class ParameterCreate(BaseModel): - scenario_id: int - name: str - value: float - distribution_id: Optional[int] = None - distribution_type: Optional[str] = None - distribution_parameters: Optional[Dict[str, Any]] = None - - @field_validator("distribution_type") - @classmethod - def normalize_type(cls, value: Optional[str]) -> Optional[str]: - if value is None: - return value - normalized = value.strip().lower() - if not normalized: - return None - if normalized not in {"normal", "uniform", "triangular"}: - raise ValueError( - "distribution_type must be normal, uniform, or triangular" - ) - return normalized - - @field_validator("distribution_parameters") - @classmethod - def empty_dict_to_none( - cls, value: Optional[Dict[str, Any]] - ) -> Optional[Dict[str, Any]]: - if value is None: - return None - return value or None - - -class ParameterRead(ParameterCreate): - id: int - model_config = ConfigDict(from_attributes=True) - - -@router.post("/", response_model=ParameterRead) -def create_parameter(param: ParameterCreate, db: Session = Depends(get_db)): - scen = db.query(Scenario).filter(Scenario.id == param.scenario_id).first() - if not scen: - raise HTTPException(status_code=404, detail="Scenario not found") - distribution_id = param.distribution_id - distribution_type = param.distribution_type - distribution_parameters = param.distribution_parameters - - if distribution_id is not None: - distribution = ( - db.query(Distribution) - .filter(Distribution.id == distribution_id) - .first() - ) - if not distribution: - raise HTTPException( - status_code=404, detail="Distribution not found" - ) - distribution_type = distribution.distribution_type - distribution_parameters = distribution.parameters or None - - new_param = Parameter( - scenario_id=param.scenario_id, - name=param.name, - value=param.value, - distribution_id=distribution_id, - distribution_type=distribution_type, - distribution_parameters=distribution_parameters, - ) - db.add(new_param) - db.commit() - db.refresh(new_param) - return new_param - - -@router.get("/", response_model=List[ParameterRead]) -def list_parameters(db: Session = Depends(get_db)): - return db.query(Parameter).all() diff --git a/routes/production.py b/routes/production.py deleted file mode 100644 index ad4a059..0000000 --- a/routes/production.py +++ /dev/null @@ -1,56 +0,0 @@ -from typing import List, Optional - -from fastapi import APIRouter, Depends, status -from pydantic import BaseModel, ConfigDict, PositiveFloat, field_validator -from sqlalchemy.orm import Session - -from models.production_output import ProductionOutput -from routes.dependencies import get_db - - -router = APIRouter(prefix="/api/production", tags=["Production"]) - - -class ProductionOutputBase(BaseModel): - scenario_id: int - amount: PositiveFloat - description: Optional[str] = None - unit_name: Optional[str] = None - unit_symbol: Optional[str] = None - - @field_validator("unit_name", "unit_symbol") - @classmethod - def _normalize_text(cls, value: Optional[str]) -> Optional[str]: - if value is None: - return None - stripped = value.strip() - return stripped or None - - -class ProductionOutputCreate(ProductionOutputBase): - pass - - -class ProductionOutputRead(ProductionOutputBase): - id: int - model_config = ConfigDict(from_attributes=True) - - -@router.post( - "/", - response_model=ProductionOutputRead, - status_code=status.HTTP_201_CREATED, -) -def create_production( - item: ProductionOutputCreate, db: Session = Depends(get_db) -): - db_item = ProductionOutput(**item.model_dump()) - db.add(db_item) - db.commit() - db.refresh(db_item) - return db_item - - -@router.get("/", response_model=List[ProductionOutputRead]) -def list_production(db: Session = Depends(get_db)): - return db.query(ProductionOutput).all() diff --git a/routes/reporting.py b/routes/reporting.py deleted file mode 100644 index 09a9417..0000000 --- a/routes/reporting.py +++ /dev/null @@ -1,73 +0,0 @@ -from typing import Any, Dict, List, cast - -from fastapi import APIRouter, HTTPException, Request, status -from pydantic import BaseModel - -from services.reporting import generate_report - - -router = APIRouter(prefix="/api/reporting", tags=["Reporting"]) - - -def _validate_payload(payload: Any) -> List[Dict[str, float]]: - if not isinstance(payload, list): - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail="Invalid input format", - ) - - typed_payload = cast(List[Any], payload) - - validated: List[Dict[str, float]] = [] - for index, item in enumerate(typed_payload): - if not isinstance(item, dict): - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail=f"Entry at index {index} must be an object", - ) - value = cast(Dict[str, Any], item).get("result") - if not isinstance(value, (int, float)): - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail=f"Entry at index {index} must include numeric 'result'", - ) - validated.append({"result": float(value)}) - return validated - - -class ReportSummary(BaseModel): - count: int - mean: float - median: float - min: float - max: float - std_dev: float - variance: float - percentile_10: float - percentile_90: float - percentile_5: float - percentile_95: float - value_at_risk_95: float - expected_shortfall_95: float - - -@router.post("/summary", response_model=ReportSummary) -async def summary_report(request: Request): - payload = await request.json() - validated_payload = _validate_payload(payload) - summary = generate_report(validated_payload) - return ReportSummary( - count=int(summary["count"]), - mean=float(summary["mean"]), - median=float(summary["median"]), - min=float(summary["min"]), - max=float(summary["max"]), - std_dev=float(summary["std_dev"]), - variance=float(summary["variance"]), - percentile_10=float(summary["percentile_10"]), - percentile_90=float(summary["percentile_90"]), - percentile_5=float(summary["percentile_5"]), - percentile_95=float(summary["percentile_95"]), - value_at_risk_95=float(summary["value_at_risk_95"]), - expected_shortfall_95=float(summary["expected_shortfall_95"]), - ) diff --git a/routes/scenarios.py b/routes/scenarios.py deleted file mode 100644 index 4454f74..0000000 --- a/routes/scenarios.py +++ /dev/null @@ -1,42 +0,0 @@ -from datetime import datetime -from typing import Optional - -from fastapi import APIRouter, Depends, HTTPException -from pydantic import BaseModel, ConfigDict -from sqlalchemy.orm import Session - -from models.scenario import Scenario -from routes.dependencies import get_db - -router = APIRouter(prefix="/api/scenarios", tags=["scenarios"]) - -# Pydantic schemas - - -class ScenarioCreate(BaseModel): - name: str - description: Optional[str] = None - - -class ScenarioRead(ScenarioCreate): - id: int - created_at: datetime - updated_at: Optional[datetime] = None - model_config = ConfigDict(from_attributes=True) - - -@router.post("/", response_model=ScenarioRead) -def create_scenario(scenario: ScenarioCreate, db: Session = Depends(get_db)): - db_s = db.query(Scenario).filter(Scenario.name == scenario.name).first() - if db_s: - raise HTTPException(status_code=400, detail="Scenario already exists") - new_s = Scenario(name=scenario.name, description=scenario.description) - db.add(new_s) - db.commit() - db.refresh(new_s) - return new_s - - -@router.get("/", response_model=list[ScenarioRead]) -def list_scenarios(db: Session = Depends(get_db)): - return db.query(Scenario).all() diff --git a/routes/settings.py b/routes/settings.py deleted file mode 100644 index ed06fb5..0000000 --- a/routes/settings.py +++ /dev/null @@ -1,110 +0,0 @@ -from typing import Dict, List - -from fastapi import APIRouter, Depends, HTTPException, status -from pydantic import BaseModel, Field, model_validator -from sqlalchemy.orm import Session - -from routes.dependencies import get_db -from services.settings import ( - CSS_COLOR_DEFAULTS, - get_css_color_settings, - list_css_env_override_rows, - read_css_color_env_overrides, - update_css_color_settings, - get_theme_settings, - save_theme_settings, -) - -router = APIRouter(prefix="/api/settings", tags=["Settings"]) - - -class CSSSettingsPayload(BaseModel): - variables: Dict[str, str] = Field(default_factory=dict) - - @model_validator(mode="after") - def _validate_allowed_keys(self) -> "CSSSettingsPayload": - invalid = set(self.variables.keys()) - set(CSS_COLOR_DEFAULTS.keys()) - if invalid: - invalid_keys = ", ".join(sorted(invalid)) - raise ValueError( - f"Unsupported CSS variables: {invalid_keys}." - " Accepted keys align with the default theme variables." - ) - return self - - -class EnvOverride(BaseModel): - css_key: str - env_var: str - value: str - - -class CSSSettingsResponse(BaseModel): - variables: Dict[str, str] - env_overrides: Dict[str, str] = Field(default_factory=dict) - env_sources: List[EnvOverride] = Field(default_factory=list) - - -@router.get("/css", response_model=CSSSettingsResponse) -def read_css_settings(db: Session = Depends(get_db)) -> CSSSettingsResponse: - try: - values = get_css_color_settings(db) - env_overrides = read_css_color_env_overrides() - env_sources = [ - EnvOverride(**row) for row in list_css_env_override_rows() - ] - except ValueError as exc: - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=str(exc), - ) from exc - return CSSSettingsResponse( - variables=values, - env_overrides=env_overrides, - env_sources=env_sources, - ) - - -@router.put( - "/css", response_model=CSSSettingsResponse, status_code=status.HTTP_200_OK -) -def update_css_settings( - payload: CSSSettingsPayload, db: Session = Depends(get_db) -) -> CSSSettingsResponse: - try: - values = update_css_color_settings(db, payload.variables) - env_overrides = read_css_color_env_overrides() - env_sources = [ - EnvOverride(**row) for row in list_css_env_override_rows() - ] - except ValueError as exc: - raise HTTPException( - status_code=status.HTTP_422_UNPROCESSABLE_CONTENT, - detail=str(exc), - ) from exc - return CSSSettingsResponse( - variables=values, - env_overrides=env_overrides, - env_sources=env_sources, - ) - - -class ThemeSettings(BaseModel): - theme_name: str - primary_color: str - secondary_color: str - accent_color: str - background_color: str - text_color: str - - -@router.post("/theme") -async def update_theme(theme_data: ThemeSettings, db: Session = Depends(get_db)): - data_dict = theme_data.model_dump() - save_theme_settings(db, data_dict) - return {"message": "Theme updated", "theme": data_dict} - - -@router.get("/theme") -async def get_theme(db: Session = Depends(get_db)): - return get_theme_settings(db) diff --git a/routes/simulations.py b/routes/simulations.py deleted file mode 100644 index 5500805..0000000 --- a/routes/simulations.py +++ /dev/null @@ -1,126 +0,0 @@ -from typing import Dict, List, Optional - -from fastapi import APIRouter, Depends, HTTPException, status -from pydantic import BaseModel, PositiveInt -from sqlalchemy.orm import Session - -from models.parameters import Parameter -from models.scenario import Scenario -from models.simulation_result import SimulationResult -from routes.dependencies import get_db -from services.reporting import generate_report -from services.simulation import run_simulation - -router = APIRouter(prefix="/api/simulations", tags=["Simulations"]) - - -class SimulationParameterInput(BaseModel): - name: str - value: float - distribution: Optional[str] = "normal" - std_dev: Optional[float] = None - min: Optional[float] = None - max: Optional[float] = None - mode: Optional[float] = None - - -class SimulationRunRequest(BaseModel): - scenario_id: int - iterations: PositiveInt = 1000 - parameters: Optional[List[SimulationParameterInput]] = None - seed: Optional[int] = None - - -class SimulationResultItem(BaseModel): - iteration: int - result: float - - -class SimulationRunResponse(BaseModel): - scenario_id: int - iterations: int - results: List[SimulationResultItem] - summary: Dict[str, float | int] - - -def _load_parameters( - db: Session, scenario_id: int -) -> List[SimulationParameterInput]: - db_params = ( - db.query(Parameter) - .filter(Parameter.scenario_id == scenario_id) - .order_by(Parameter.id) - .all() - ) - return [ - SimulationParameterInput( - name=item.name, - value=item.value, - ) - for item in db_params - ] - - -@router.post("/run", response_model=SimulationRunResponse) -async def simulate( - payload: SimulationRunRequest, db: Session = Depends(get_db) -): - scenario = ( - db.query(Scenario).filter(Scenario.id == payload.scenario_id).first() - ) - if scenario is None: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, - detail="Scenario not found", - ) - - parameters = payload.parameters or _load_parameters(db, payload.scenario_id) - if not parameters: - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail="No parameters provided", - ) - - raw_results = run_simulation( - [param.model_dump(exclude_none=True) for param in parameters], - iterations=payload.iterations, - seed=payload.seed, - ) - - if not raw_results: - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail="Simulation produced no results", - ) - - # Persist results (replace existing values for scenario) - db.query(SimulationResult).filter( - SimulationResult.scenario_id == payload.scenario_id - ).delete() - db.bulk_save_objects( - [ - SimulationResult( - scenario_id=payload.scenario_id, - iteration=item["iteration"], - result=item["result"], - ) - for item in raw_results - ] - ) - db.commit() - - summary = generate_report(raw_results) - - response = SimulationRunResponse( - scenario_id=payload.scenario_id, - iterations=payload.iterations, - results=[ - SimulationResultItem( - iteration=int(item["iteration"]), - result=float(item["result"]), - ) - for item in raw_results - ], - summary=summary, - ) - return response diff --git a/routes/ui.py b/routes/ui.py deleted file mode 100644 index e690dba..0000000 --- a/routes/ui.py +++ /dev/null @@ -1,784 +0,0 @@ -from collections import defaultdict -from datetime import datetime, timezone -from typing import Any, Dict, Optional - -from fastapi import APIRouter, Depends, Request -from fastapi.responses import HTMLResponse, JSONResponse -from fastapi.templating import Jinja2Templates -from sqlalchemy.orm import Session - -from models.capex import Capex -from models.consumption import Consumption -from models.equipment import Equipment -from models.maintenance import Maintenance -from models.opex import Opex -from models.parameters import Parameter -from models.production_output import ProductionOutput -from models.scenario import Scenario -from models.simulation_result import SimulationResult -from routes.dependencies import get_db -from services.reporting import generate_report -from models.currency import Currency -from routes.currencies import DEFAULT_CURRENCY_CODE, _ensure_default_currency -from services.settings import ( - CSS_COLOR_DEFAULTS, - get_css_color_settings, - list_css_env_override_rows, - read_css_color_env_overrides, -) - - -CURRENCY_CHOICES: list[Dict[str, Any]] = [ - {"id": "USD", "name": "US Dollar (USD)"}, - {"id": "EUR", "name": "Euro (EUR)"}, - {"id": "CLP", "name": "Chilean Peso (CLP)"}, - {"id": "RMB", "name": "Chinese Yuan (RMB)"}, - {"id": "GBP", "name": "British Pound (GBP)"}, - {"id": "CAD", "name": "Canadian Dollar (CAD)"}, - {"id": "AUD", "name": "Australian Dollar (AUD)"}, -] - -MEASUREMENT_UNITS: list[Dict[str, Any]] = [ - {"id": "tonnes", "name": "Tonnes", "symbol": "t"}, - {"id": "kilograms", "name": "Kilograms", "symbol": "kg"}, - {"id": "pounds", "name": "Pounds", "symbol": "lb"}, - {"id": "liters", "name": "Liters", "symbol": "L"}, - {"id": "cubic_meters", "name": "Cubic Meters", "symbol": "m3"}, - {"id": "kilowatt_hours", "name": "Kilowatt Hours", "symbol": "kWh"}, -] - -router = APIRouter() - -# Set up Jinja2 templates directory -templates = Jinja2Templates(directory="templates") - - -def _context( - request: Request, extra: Optional[Dict[str, Any]] = None -) -> Dict[str, Any]: - payload: Dict[str, Any] = { - "request": request, - "current_year": datetime.now(timezone.utc).year, - } - if extra: - payload.update(extra) - return payload - - -def _render( - request: Request, - template_name: str, - extra: Optional[Dict[str, Any]] = None, -): - context = _context(request, extra) - return templates.TemplateResponse(request, template_name, context) - - -def _format_currency(value: float) -> str: - return f"${value:,.2f}" - - -def _format_decimal(value: float) -> str: - return f"{value:,.2f}" - - -def _format_int(value: int) -> str: - return f"{value:,}" - - -def _load_scenarios(db: Session) -> Dict[str, Any]: - scenarios: list[Dict[str, Any]] = [ - { - "id": item.id, - "name": item.name, - "description": item.description, - } - for item in db.query(Scenario).order_by(Scenario.name).all() - ] - return {"scenarios": scenarios} - - -def _load_parameters(db: Session) -> Dict[str, Any]: - grouped: defaultdict[int, list[Dict[str, Any]]] = defaultdict(list) - for param in db.query(Parameter).order_by( - Parameter.scenario_id, Parameter.id - ): - grouped[param.scenario_id].append( - { - "id": param.id, - "name": param.name, - "value": param.value, - "distribution_type": param.distribution_type, - "distribution_parameters": param.distribution_parameters, - } - ) - return {"parameters_by_scenario": dict(grouped)} - - -def _load_costs(db: Session) -> Dict[str, Any]: - capex_grouped: defaultdict[int, list[Dict[str, Any]]] = defaultdict(list) - for capex in db.query(Capex).order_by(Capex.scenario_id, Capex.id).all(): - capex_grouped[int(getattr(capex, "scenario_id"))].append( - { - "id": int(getattr(capex, "id")), - "scenario_id": int(getattr(capex, "scenario_id")), - "amount": float(getattr(capex, "amount", 0.0)), - "description": getattr(capex, "description", "") or "", - "currency_code": getattr(capex, "currency_code", "USD") - or "USD", - } - ) - - opex_grouped: defaultdict[int, list[Dict[str, Any]]] = defaultdict(list) - for opex in db.query(Opex).order_by(Opex.scenario_id, Opex.id).all(): - opex_grouped[int(getattr(opex, "scenario_id"))].append( - { - "id": int(getattr(opex, "id")), - "scenario_id": int(getattr(opex, "scenario_id")), - "amount": float(getattr(opex, "amount", 0.0)), - "description": getattr(opex, "description", "") or "", - "currency_code": getattr(opex, "currency_code", "USD") or "USD", - } - ) - - return { - "capex_by_scenario": dict(capex_grouped), - "opex_by_scenario": dict(opex_grouped), - } - - -def _load_currencies(db: Session) -> Dict[str, Any]: - items: list[Dict[str, Any]] = [] - for c in ( - db.query(Currency) - .filter_by(is_active=True) - .order_by(Currency.code) - .all() - ): - items.append( - {"id": c.code, "name": f"{c.name} ({c.code})", "symbol": c.symbol} - ) - if not items: - items.append({"id": "USD", "name": "US Dollar (USD)", "symbol": "$"}) - return {"currency_options": items} - - -def _load_currency_settings(db: Session) -> Dict[str, Any]: - _ensure_default_currency(db) - records = db.query(Currency).order_by(Currency.code).all() - currencies: list[Dict[str, Any]] = [] - for record in records: - code_value = getattr(record, "code") - currencies.append( - { - "id": int(getattr(record, "id")), - "code": code_value, - "name": getattr(record, "name"), - "symbol": getattr(record, "symbol"), - "is_active": bool(getattr(record, "is_active", True)), - "is_default": code_value == DEFAULT_CURRENCY_CODE, - } - ) - - active_count = sum(1 for item in currencies if item["is_active"]) - inactive_count = len(currencies) - active_count - - return { - "currencies": currencies, - "currency_stats": { - "total": len(currencies), - "active": active_count, - "inactive": inactive_count, - }, - "default_currency_code": DEFAULT_CURRENCY_CODE, - "currency_api_base": "/api/currencies", - } - - -def _load_css_settings(db: Session) -> Dict[str, Any]: - variables = get_css_color_settings(db) - env_overrides = read_css_color_env_overrides() - env_rows = list_css_env_override_rows() - env_meta = {row["css_key"]: row for row in env_rows} - return { - "css_variables": variables, - "css_defaults": CSS_COLOR_DEFAULTS, - "css_env_overrides": env_overrides, - "css_env_override_rows": env_rows, - "css_env_override_meta": env_meta, - } - - -def _load_consumption(db: Session) -> Dict[str, Any]: - grouped: defaultdict[int, list[Dict[str, Any]]] = defaultdict(list) - for record in ( - db.query(Consumption) - .order_by(Consumption.scenario_id, Consumption.id) - .all() - ): - record_id = int(getattr(record, "id")) - scenario_id = int(getattr(record, "scenario_id")) - amount_value = float(getattr(record, "amount", 0.0)) - description = getattr(record, "description", "") or "" - unit_name = getattr(record, "unit_name", None) - unit_symbol = getattr(record, "unit_symbol", None) - grouped[scenario_id].append( - { - "id": record_id, - "scenario_id": scenario_id, - "amount": amount_value, - "description": description, - "unit_name": unit_name, - "unit_symbol": unit_symbol, - } - ) - return {"consumption_by_scenario": dict(grouped)} - - -def _load_production(db: Session) -> Dict[str, Any]: - grouped: defaultdict[int, list[Dict[str, Any]]] = defaultdict(list) - for record in ( - db.query(ProductionOutput) - .order_by(ProductionOutput.scenario_id, ProductionOutput.id) - .all() - ): - record_id = int(getattr(record, "id")) - scenario_id = int(getattr(record, "scenario_id")) - amount_value = float(getattr(record, "amount", 0.0)) - description = getattr(record, "description", "") or "" - unit_name = getattr(record, "unit_name", None) - unit_symbol = getattr(record, "unit_symbol", None) - grouped[scenario_id].append( - { - "id": record_id, - "scenario_id": scenario_id, - "amount": amount_value, - "description": description, - "unit_name": unit_name, - "unit_symbol": unit_symbol, - } - ) - return {"production_by_scenario": dict(grouped)} - - -def _load_equipment(db: Session) -> Dict[str, Any]: - grouped: defaultdict[int, list[Dict[str, Any]]] = defaultdict(list) - for record in ( - db.query(Equipment).order_by(Equipment.scenario_id, Equipment.id).all() - ): - record_id = int(getattr(record, "id")) - scenario_id = int(getattr(record, "scenario_id")) - name_value = getattr(record, "name", "") or "" - description = getattr(record, "description", "") or "" - grouped[scenario_id].append( - { - "id": record_id, - "scenario_id": scenario_id, - "name": name_value, - "description": description, - } - ) - return {"equipment_by_scenario": dict(grouped)} - - -def _load_maintenance(db: Session) -> Dict[str, Any]: - grouped: defaultdict[int, list[Dict[str, Any]]] = defaultdict(list) - for record in ( - db.query(Maintenance) - .order_by(Maintenance.scenario_id, Maintenance.maintenance_date) - .all() - ): - record_id = int(getattr(record, "id")) - scenario_id = int(getattr(record, "scenario_id")) - equipment_id = int(getattr(record, "equipment_id")) - equipment_obj = getattr(record, "equipment", None) - equipment_name = ( - getattr(equipment_obj, "name", "") if equipment_obj else "" - ) - maintenance_date = getattr(record, "maintenance_date", None) - cost_value = float(getattr(record, "cost", 0.0)) - description = getattr(record, "description", "") or "" - - grouped[scenario_id].append( - { - "id": record_id, - "scenario_id": scenario_id, - "equipment_id": equipment_id, - "equipment_name": equipment_name, - "maintenance_date": ( - maintenance_date.isoformat() if maintenance_date else "" - ), - "cost": cost_value, - "description": description, - } - ) - return {"maintenance_by_scenario": dict(grouped)} - - -def _load_simulations(db: Session) -> Dict[str, Any]: - scenarios: list[Dict[str, Any]] = [ - { - "id": item.id, - "name": item.name, - } - for item in db.query(Scenario).order_by(Scenario.name).all() - ] - - results_grouped: defaultdict[int, list[Dict[str, Any]]] = defaultdict(list) - for record in ( - db.query(SimulationResult) - .order_by(SimulationResult.scenario_id, SimulationResult.iteration) - .all() - ): - scenario_id = int(getattr(record, "scenario_id")) - results_grouped[scenario_id].append( - { - "iteration": int(getattr(record, "iteration")), - "result": float(getattr(record, "result", 0.0)), - } - ) - - runs: list[Dict[str, Any]] = [] - sample_limit = 20 - for item in scenarios: - scenario_id = int(item["id"]) - scenario_results = results_grouped.get(scenario_id, []) - summary = ( - generate_report(scenario_results) - if scenario_results - else generate_report([]) - ) - runs.append( - { - "scenario_id": scenario_id, - "scenario_name": item["name"], - "iterations": int(summary.get("count", 0)), - "summary": summary, - "sample_results": scenario_results[:sample_limit], - } - ) - - return { - "simulation_scenarios": scenarios, - "simulation_runs": runs, - } - - -def _load_reporting(db: Session) -> Dict[str, Any]: - scenarios = _load_scenarios(db)["scenarios"] - runs = _load_simulations(db)["simulation_runs"] - - summaries: list[Dict[str, Any]] = [] - runs_by_scenario = {run["scenario_id"]: run for run in runs} - - for scenario in scenarios: - scenario_id = scenario["id"] - run = runs_by_scenario.get(scenario_id) - summary = run["summary"] if run else generate_report([]) - summaries.append( - { - "scenario_id": scenario_id, - "scenario_name": scenario["name"], - "summary": summary, - "iterations": run["iterations"] if run else 0, - } - ) - - return { - "report_summaries": summaries, - } - - -def _load_dashboard(db: Session) -> Dict[str, Any]: - scenarios = _load_scenarios(db)["scenarios"] - parameters_by_scenario = _load_parameters(db)["parameters_by_scenario"] - costs_context = _load_costs(db) - capex_by_scenario = costs_context["capex_by_scenario"] - opex_by_scenario = costs_context["opex_by_scenario"] - consumption_by_scenario = _load_consumption(db)["consumption_by_scenario"] - production_by_scenario = _load_production(db)["production_by_scenario"] - equipment_by_scenario = _load_equipment(db)["equipment_by_scenario"] - maintenance_by_scenario = _load_maintenance(db)["maintenance_by_scenario"] - simulation_context = _load_simulations(db) - simulation_runs = simulation_context["simulation_runs"] - - runs_by_scenario = {run["scenario_id"]: run for run in simulation_runs} - - def sum_amounts( - grouped: Dict[int, list[Dict[str, Any]]], field: str = "amount" - ) -> float: - total = 0.0 - for items in grouped.values(): - for item in items: - value = item.get(field, 0.0) - if isinstance(value, (int, float)): - total += float(value) - return total - - total_capex = sum_amounts(capex_by_scenario) - total_opex = sum_amounts(opex_by_scenario) - total_consumption = sum_amounts(consumption_by_scenario) - total_production = sum_amounts(production_by_scenario) - total_maintenance_cost = sum_amounts(maintenance_by_scenario, field="cost") - - total_parameters = sum( - len(items) for items in parameters_by_scenario.values() - ) - total_equipment = sum( - len(items) for items in equipment_by_scenario.values() - ) - total_maintenance_events = sum( - len(items) for items in maintenance_by_scenario.values() - ) - total_simulation_iterations = sum( - run["iterations"] for run in simulation_runs - ) - - scenario_rows: list[Dict[str, Any]] = [] - scenario_labels: list[str] = [] - scenario_capex: list[float] = [] - scenario_opex: list[float] = [] - activity_labels: list[str] = [] - activity_production: list[float] = [] - activity_consumption: list[float] = [] - - for scenario in scenarios: - scenario_id = scenario["id"] - scenario_name = scenario["name"] - param_count = len(parameters_by_scenario.get(scenario_id, [])) - equipment_count = len(equipment_by_scenario.get(scenario_id, [])) - maintenance_count = len(maintenance_by_scenario.get(scenario_id, [])) - - capex_total = sum( - float(item.get("amount", 0.0)) - for item in capex_by_scenario.get(scenario_id, []) - ) - opex_total = sum( - float(item.get("amount", 0.0)) - for item in opex_by_scenario.get(scenario_id, []) - ) - consumption_total = sum( - float(item.get("amount", 0.0)) - for item in consumption_by_scenario.get(scenario_id, []) - ) - production_total = sum( - float(item.get("amount", 0.0)) - for item in production_by_scenario.get(scenario_id, []) - ) - - run = runs_by_scenario.get(scenario_id) - summary = run["summary"] if run else generate_report([]) - iterations = run["iterations"] if run else 0 - mean_value = float(summary.get("mean", 0.0)) - - scenario_rows.append( - { - "scenario_name": scenario_name, - "parameter_count": param_count, - "parameter_display": _format_int(param_count), - "equipment_count": equipment_count, - "equipment_display": _format_int(equipment_count), - "capex_total": capex_total, - "capex_display": _format_currency(capex_total), - "opex_total": opex_total, - "opex_display": _format_currency(opex_total), - "production_total": production_total, - "production_display": _format_decimal(production_total), - "consumption_total": consumption_total, - "consumption_display": _format_decimal(consumption_total), - "maintenance_count": maintenance_count, - "maintenance_display": _format_int(maintenance_count), - "iterations": iterations, - "iterations_display": _format_int(iterations), - "simulation_mean": mean_value, - "simulation_mean_display": _format_decimal(mean_value), - } - ) - - scenario_labels.append(scenario_name) - scenario_capex.append(capex_total) - scenario_opex.append(opex_total) - - activity_labels.append(scenario_name) - activity_production.append(production_total) - activity_consumption.append(consumption_total) - - scenario_rows.sort(key=lambda row: row["scenario_name"].lower()) - - all_simulation_results = [ - {"result": float(getattr(item, "result", 0.0))} - for item in db.query(SimulationResult).all() - ] - overall_report = generate_report(all_simulation_results) - - overall_report_metrics = [ - { - "label": "Runs", - "value": _format_int(int(overall_report.get("count", 0))), - }, - { - "label": "Mean", - "value": _format_decimal(float(overall_report.get("mean", 0.0))), - }, - { - "label": "Median", - "value": _format_decimal(float(overall_report.get("median", 0.0))), - }, - { - "label": "Std Dev", - "value": _format_decimal(float(overall_report.get("std_dev", 0.0))), - }, - { - "label": "95th Percentile", - "value": _format_decimal( - float(overall_report.get("percentile_95", 0.0)) - ), - }, - { - "label": "VaR (95%)", - "value": _format_decimal( - float(overall_report.get("value_at_risk_95", 0.0)) - ), - }, - { - "label": "Expected Shortfall (95%)", - "value": _format_decimal( - float(overall_report.get("expected_shortfall_95", 0.0)) - ), - }, - ] - - recent_simulations: list[Dict[str, Any]] = [ - { - "scenario_name": run["scenario_name"], - "iterations": run["iterations"], - "iterations_display": _format_int(run["iterations"]), - "mean_display": _format_decimal( - float(run["summary"].get("mean", 0.0)) - ), - "p95_display": _format_decimal( - float(run["summary"].get("percentile_95", 0.0)) - ), - } - for run in simulation_runs - if run["iterations"] > 0 - ] - recent_simulations.sort(key=lambda item: item["iterations"], reverse=True) - recent_simulations = recent_simulations[:5] - - upcoming_maintenance: list[Dict[str, Any]] = [] - for record in ( - db.query(Maintenance) - .order_by(Maintenance.maintenance_date.asc()) - .limit(5) - .all() - ): - maintenance_date = getattr(record, "maintenance_date", None) - upcoming_maintenance.append( - { - "scenario_name": getattr( - getattr(record, "scenario", None), "name", "Unknown" - ), - "equipment_name": getattr( - getattr(record, "equipment", None), "name", "Unknown" - ), - "date_display": ( - maintenance_date.strftime("%Y-%m-%d") - if maintenance_date - else "—" - ), - "cost_display": _format_currency( - float(getattr(record, "cost", 0.0)) - ), - "description": getattr(record, "description", "") or "—", - } - ) - - cost_chart_has_data = any(value > 0 for value in scenario_capex) or any( - value > 0 for value in scenario_opex - ) - activity_chart_has_data = any( - value > 0 for value in activity_production - ) or any(value > 0 for value in activity_consumption) - - scenario_cost_chart: Dict[str, list[Any]] = { - "labels": scenario_labels, - "capex": scenario_capex, - "opex": scenario_opex, - } - scenario_activity_chart: Dict[str, list[Any]] = { - "labels": activity_labels, - "production": activity_production, - "consumption": activity_consumption, - } - - summary_metrics = [ - {"label": "Active Scenarios", "value": _format_int(len(scenarios))}, - {"label": "Parameters", "value": _format_int(total_parameters)}, - {"label": "CAPEX Total", "value": _format_currency(total_capex)}, - {"label": "OPEX Total", "value": _format_currency(total_opex)}, - {"label": "Equipment Assets", "value": _format_int(total_equipment)}, - { - "label": "Maintenance Events", - "value": _format_int(total_maintenance_events), - }, - {"label": "Consumption", "value": _format_decimal(total_consumption)}, - {"label": "Production", "value": _format_decimal(total_production)}, - { - "label": "Simulation Iterations", - "value": _format_int(total_simulation_iterations), - }, - { - "label": "Maintenance Cost", - "value": _format_currency(total_maintenance_cost), - }, - ] - - return { - "summary_metrics": summary_metrics, - "scenario_rows": scenario_rows, - "overall_report_metrics": overall_report_metrics, - "recent_simulations": recent_simulations, - "upcoming_maintenance": upcoming_maintenance, - "scenario_cost_chart": scenario_cost_chart, - "scenario_activity_chart": scenario_activity_chart, - "cost_chart_has_data": cost_chart_has_data, - "activity_chart_has_data": activity_chart_has_data, - "report_available": overall_report.get("count", 0) > 0, - } - - -@router.get("/", response_class=HTMLResponse) -async def dashboard_root(request: Request, db: Session = Depends(get_db)): - """Render the primary dashboard landing page.""" - return _render(request, "Dashboard.html", _load_dashboard(db)) - - -@router.get("/ui/dashboard", response_class=HTMLResponse) -async def dashboard(request: Request, db: Session = Depends(get_db)): - """Render the legacy dashboard route for backward compatibility.""" - return _render(request, "Dashboard.html", _load_dashboard(db)) - - -@router.get("/ui/dashboard/data", response_class=JSONResponse) -async def dashboard_data(db: Session = Depends(get_db)) -> JSONResponse: - """Expose dashboard aggregates as JSON for client-side refreshes.""" - return JSONResponse(_load_dashboard(db)) - - -@router.get("/ui/scenarios", response_class=HTMLResponse) -async def scenario_form(request: Request, db: Session = Depends(get_db)): - """Render the scenario creation form.""" - context = _load_scenarios(db) - return _render(request, "ScenarioForm.html", context) - - -@router.get("/ui/parameters", response_class=HTMLResponse) -async def parameter_form(request: Request, db: Session = Depends(get_db)): - """Render the parameter input form.""" - context: Dict[str, Any] = {} - context.update(_load_scenarios(db)) - context.update(_load_parameters(db)) - return _render(request, "ParameterInput.html", context) - - -@router.get("/ui/costs", response_class=HTMLResponse) -async def costs_view(request: Request, db: Session = Depends(get_db)): - """Render the costs view with CAPEX and OPEX data.""" - context: Dict[str, Any] = {} - context.update(_load_scenarios(db)) - context.update(_load_costs(db)) - context.update(_load_currencies(db)) - return _render(request, "costs.html", context) - - -@router.get("/ui/consumption", response_class=HTMLResponse) -async def consumption_view(request: Request, db: Session = Depends(get_db)): - """Render the consumption view with scenario consumption data.""" - context: Dict[str, Any] = {} - context.update(_load_scenarios(db)) - context.update(_load_consumption(db)) - context["unit_options"] = MEASUREMENT_UNITS - return _render(request, "consumption.html", context) - - -@router.get("/ui/production", response_class=HTMLResponse) -async def production_view(request: Request, db: Session = Depends(get_db)): - """Render the production view with scenario production data.""" - context: Dict[str, Any] = {} - context.update(_load_scenarios(db)) - context.update(_load_production(db)) - context["unit_options"] = MEASUREMENT_UNITS - return _render(request, "production.html", context) - - -@router.get("/ui/equipment", response_class=HTMLResponse) -async def equipment_view(request: Request, db: Session = Depends(get_db)): - """Render the equipment view with scenario equipment data.""" - context: Dict[str, Any] = {} - context.update(_load_scenarios(db)) - context.update(_load_equipment(db)) - return _render(request, "equipment.html", context) - - -@router.get("/ui/maintenance", response_class=HTMLResponse) -async def maintenance_view(request: Request, db: Session = Depends(get_db)): - """Render the maintenance view with scenario maintenance data.""" - context: Dict[str, Any] = {} - context.update(_load_scenarios(db)) - context.update(_load_equipment(db)) - context.update(_load_maintenance(db)) - return _render(request, "maintenance.html", context) - - -@router.get("/ui/simulations", response_class=HTMLResponse) -async def simulations_view(request: Request, db: Session = Depends(get_db)): - """Render the simulations view with scenario information and recent runs.""" - return _render(request, "simulations.html", _load_simulations(db)) - - -@router.get("/ui/reporting", response_class=HTMLResponse) -async def reporting_view(request: Request, db: Session = Depends(get_db)): - """Render the reporting view with scenario KPI summaries.""" - return _render(request, "reporting.html", _load_reporting(db)) - - -@router.get("/ui/settings", response_class=HTMLResponse) -async def settings_view(request: Request, db: Session = Depends(get_db)): - """Render the settings landing page.""" - context = _load_css_settings(db) - return _render(request, "settings.html", context) - - -@router.get("/ui/currencies", response_class=HTMLResponse) -async def currencies_view(request: Request, db: Session = Depends(get_db)): - """Render the currency administration page with full currency context.""" - context = _load_currency_settings(db) - return _render(request, "currencies.html", context) - - -@router.get("/login", response_class=HTMLResponse) -async def login_page(request: Request): - return _render(request, "login.html") - - -@router.get("/register", response_class=HTMLResponse) -async def register_page(request: Request): - return _render(request, "register.html") - - -@router.get("/profile", response_class=HTMLResponse) -async def profile_page(request: Request): - return _render(request, "profile.html") - - -@router.get("/forgot-password", response_class=HTMLResponse) -async def forgot_password_page(request: Request): - return _render(request, "forgot_password.html") - - -@router.get("/theme-settings", response_class=HTMLResponse) -async def theme_settings_page(request: Request, db: Session = Depends(get_db)): - """Render the theme settings page.""" - context = _load_css_settings(db) - return _render(request, "theme_settings.html", context) diff --git a/routes/users.py b/routes/users.py deleted file mode 100644 index 5de7092..0000000 --- a/routes/users.py +++ /dev/null @@ -1,107 +0,0 @@ -from fastapi import APIRouter, Depends, HTTPException, status -from sqlalchemy.orm import Session - -from config.database import get_db -from models.user import User -from services.security import create_access_token, get_current_user -from schemas.user import ( - PasswordReset, - PasswordResetRequest, - UserCreate, - UserInDB, - UserLogin, - UserUpdate, -) - -router = APIRouter(prefix="/users", tags=["users"]) - - -@router.post("/register", response_model=UserInDB, status_code=status.HTTP_201_CREATED) -async def register_user(user: UserCreate, db: Session = Depends(get_db)): - db_user = db.query(User).filter(User.username == user.username).first() - if db_user: - raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, - detail="Username already registered") - db_user = db.query(User).filter(User.email == user.email).first() - if db_user: - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, detail="Email already registered") - - # Get or create default role - from models.role import Role - default_role = db.query(Role).filter(Role.name == "user").first() - if not default_role: - default_role = Role(name="user") - db.add(default_role) - db.commit() - db.refresh(default_role) - - new_user = User(username=user.username, email=user.email, - role_id=default_role.id) - new_user.set_password(user.password) - db.add(new_user) - db.commit() - db.refresh(new_user) - return new_user - - -@router.post("/login") -async def login_user(user: UserLogin, db: Session = Depends(get_db)): - db_user = db.query(User).filter(User.username == user.username).first() - if not db_user or not db_user.check_password(user.password): - raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, - detail="Incorrect username or password") - access_token = create_access_token(subject=db_user.username) - return {"access_token": access_token, "token_type": "bearer"} - - -@router.get("/me") -async def read_users_me(current_user: User = Depends(get_current_user)): - return current_user - - -@router.put("/me", response_model=UserInDB) -async def update_user_me(user_update: UserUpdate, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)): - if user_update.username and user_update.username != current_user.username: - existing_user = db.query(User).filter( - User.username == user_update.username).first() - if existing_user: - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, detail="Username already taken") - setattr(current_user, "username", user_update.username) - - if user_update.email and user_update.email != current_user.email: - existing_user = db.query(User).filter( - User.email == user_update.email).first() - if existing_user: - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, detail="Email already registered") - setattr(current_user, "email", user_update.email) - - if user_update.password: - current_user.set_password(user_update.password) - - db.add(current_user) - db.commit() - db.refresh(current_user) - return current_user - - -@router.post("/forgot-password") -async def forgot_password(request: PasswordResetRequest): - # In a real application, this would send an email with a reset token - return {"message": "Password reset email sent (not really)"} - - -@router.post("/reset-password") -async def reset_password(request: PasswordReset, db: Session = Depends(get_db)): - # In a real application, the token would be verified - user = db.query(User).filter(User.username == - request.token).first() # Use token as username for test - if not user: - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid token or user") - user.set_password(request.new_password) - db.add(user) - db.commit() - return {"message": "Password has been reset successfully"} diff --git a/schemas/user.py b/schemas/user.py deleted file mode 100644 index fafce5b..0000000 --- a/schemas/user.py +++ /dev/null @@ -1,41 +0,0 @@ -from pydantic import BaseModel, ConfigDict - - -class UserCreate(BaseModel): - username: str - email: str - password: str - - -class UserInDB(BaseModel): - id: int - username: str - email: str - role_id: int - - model_config = ConfigDict(from_attributes=True) - - -class UserLogin(BaseModel): - username: str - password: str - - -class UserUpdate(BaseModel): - username: str | None = None - email: str | None = None - password: str | None = None - - -class PasswordResetRequest(BaseModel): - email: str - - -class PasswordReset(BaseModel): - token: str - new_password: str - - -class Token(BaseModel): - access_token: str - token_type: str diff --git a/scripts/backfill_currency.py b/scripts/backfill_currency.py deleted file mode 100644 index 4651021..0000000 --- a/scripts/backfill_currency.py +++ /dev/null @@ -1,157 +0,0 @@ -""" -Backfill script to populate currency_id for capex and opex rows using existing currency_code. - -Usage: - python scripts/backfill_currency.py --dry-run - python scripts/backfill_currency.py --create-missing - -This script is intentionally cautious: it defaults to dry-run mode and will refuse to run -if database connection settings are missing. It supports creating missing currency rows when `--create-missing` -is provided. Always run against a development/staging database first. -""" - -from __future__ import annotations -import argparse -import importlib -import sys -from pathlib import Path - -from sqlalchemy import text, create_engine - - -PROJECT_ROOT = Path(__file__).resolve().parent.parent -if str(PROJECT_ROOT) not in sys.path: - sys.path.insert(0, str(PROJECT_ROOT)) - - -def load_database_url() -> str: - try: - db_module = importlib.import_module("config.database") - except RuntimeError as exc: - raise RuntimeError( - "Database configuration missing: set DATABASE_URL or provide granular " - "variables (DATABASE_DRIVER, DATABASE_HOST, DATABASE_PORT, DATABASE_USER, " - "DATABASE_PASSWORD, DATABASE_NAME, optional DATABASE_SCHEMA)." - ) from exc - - return getattr(db_module, "DATABASE_URL") - - -def backfill( - db_url: str, dry_run: bool = True, create_missing: bool = False -) -> None: - engine = create_engine(db_url) - with engine.begin() as conn: - # Ensure currency table exists - if db_url.startswith("sqlite:"): - conn.execute( - text( - "SELECT name FROM sqlite_master WHERE type='table' AND name='currency';" - ) - ) - else: - conn.execute(text("SELECT to_regclass('public.currency');")) - # Note: we don't strictly depend on the above - we assume migration was already applied - - # Helper: find or create currency by code - def find_currency_id(code: str): - r = conn.execute( - text("SELECT id FROM currency WHERE code = :code"), - {"code": code}, - ).fetchone() - if r: - return r[0] - if create_missing: - # insert and return id - conn.execute( - text( - "INSERT INTO currency (code, name, symbol, is_active) VALUES (:c, :n, NULL, TRUE)" - ), - {"c": code, "n": code}, - ) - r2 = conn.execute( - text("SELECT id FROM currency WHERE code = :code"), - {"code": code}, - ).fetchone() - if not r2: - raise RuntimeError( - f"Unable to determine currency ID for '{code}' after insert" - ) - return r2[0] - return None - - # Process tables capex and opex - for table in ("capex", "opex"): - # Check if currency_id column exists - try: - cols = ( - conn.execute( - text( - f"SELECT 1 FROM information_schema.columns WHERE table_name = '{table}' AND column_name = 'currency_id'" - ) - ) - if not db_url.startswith("sqlite:") - else [(1,)] - ) - except Exception: - cols = [(1,)] - - if not cols: - print(f"Skipping {table}: no currency_id column found") - continue - - # Find rows where currency_id IS NULL but currency_code exists - rows = conn.execute( - text( - f"SELECT id, currency_code FROM {table} WHERE currency_id IS NULL OR currency_id = ''" - ) - ) - changed = 0 - for r in rows: - rid = r[0] - code = (r[1] or "USD").strip().upper() - cid = find_currency_id(code) - if cid is None: - print( - f"Row {table}:{rid} has unknown currency code '{code}' and create_missing=False; skipping" - ) - continue - if dry_run: - print( - f"[DRY RUN] Would set {table}.currency_id = {cid} for row id={rid} (code={code})" - ) - else: - conn.execute( - text( - f"UPDATE {table} SET currency_id = :cid WHERE id = :rid" - ), - {"cid": cid, "rid": rid}, - ) - changed += 1 - - print(f"{table}: processed, changed={changed} (dry_run={dry_run})") - - -def main() -> None: - parser = argparse.ArgumentParser( - description="Backfill currency_id from currency_code for capex/opex tables" - ) - parser.add_argument( - "--dry-run", - action="store_true", - default=True, - help="Show actions without writing", - ) - parser.add_argument( - "--create-missing", - action="store_true", - help="Create missing currency rows in the currency table", - ) - args = parser.parse_args() - - db = load_database_url() - backfill(db, dry_run=args.dry_run, create_missing=args.create_missing) - - -if __name__ == "__main__": - main() diff --git a/scripts/check_docs_links.py b/scripts/check_docs_links.py deleted file mode 100644 index aebc1fe..0000000 --- a/scripts/check_docs_links.py +++ /dev/null @@ -1,50 +0,0 @@ -"""Simple Markdown link checker for local docs/ files. - -Checks only local file links (relative paths) and reports missing targets. - -Run from the repository root using the project's Python environment. -""" - -import re -from pathlib import Path - -ROOT = Path(__file__).resolve().parent.parent -DOCS = ROOT / "docs" - -MD_LINK_RE = re.compile(r"\[([^\]]+)\]\(([^)]+)\)") - -errors = [] - -for md in DOCS.rglob("*.md"): - text = md.read_text(encoding="utf-8") - for m in MD_LINK_RE.finditer(text): - label, target = m.groups() - # skip URLs - if ( - target.startswith("http://") - or target.startswith("https://") - or target.startswith("#") - ): - continue - # strip anchors - target_path = target.split("#")[0] - # if link is to a directory index, allow - candidate = (md.parent / target_path).resolve() - if candidate.exists(): - continue - # check common implicit index: target/ -> target/README.md or target/index.md - candidate_dir = md.parent / target_path - if candidate_dir.is_dir(): - if (candidate_dir / "README.md").exists() or ( - candidate_dir / "index.md" - ).exists(): - continue - errors.append((str(md.relative_to(ROOT)), target, label)) - -if errors: - print("Broken local links found:") - for src, tgt, label in errors: - print(f"- {src} -> {tgt} ({label})") - exit(2) - -print("No broken local links detected.") diff --git a/scripts/format_docs_md.py b/scripts/format_docs_md.py deleted file mode 100644 index 5e1e856..0000000 --- a/scripts/format_docs_md.py +++ /dev/null @@ -1,92 +0,0 @@ -"""Lightweight Markdown formatter: normalizes first-line H1, adds code-fence language hints for common shebangs, trims trailing whitespace. - -This is intentionally small and non-destructive; it touches only files under docs/ and makes safe changes. -""" - -import re -from pathlib import Path - -DOCS = Path(__file__).resolve().parents[1] / "docs" - -CODE_LANG_HINTS = { - "powershell": ("powershell",), - "bash": ("bash", "sh"), - "sql": ("sql",), - "python": ("python",), -} - - -def add_code_fence_language(match): - fence = match.group(0) - inner = match.group(1) - # If language already present, return unchanged - if fence.startswith("```") and len(fence.splitlines()[0].strip()) > 3: - return fence - # Try to infer language from the code content - code = inner.strip().splitlines()[0] if inner.strip() else "" - lang = "" - if ( - code.startswith("$") - or code.startswith("PS") - or code.lower().startswith("powershell") - ): - lang = "powershell" - elif ( - code.startswith("#") - or code.startswith("import") - or code.startswith("from") - ): - lang = "python" - elif re.match(r"^(select|insert|update|create)\b", code.strip(), re.I): - lang = "sql" - elif ( - code.startswith("git") - or code.startswith("./") - or code.startswith("sudo") - ): - lang = "bash" - if lang: - return f"```{lang}\n{inner}\n```" - return fence - - -def normalize_file(path: Path): - text = path.read_text(encoding="utf-8") - orig = text - # Trim trailing whitespace and ensure single trailing newline - text = "\n".join(line.rstrip() for line in text.splitlines()) + "\n" - # Ensure first non-empty line is H1 - lines = text.splitlines() - for i, ln in enumerate(lines): - if ln.strip(): - if not ln.startswith("#"): - lines[i] = "# " + ln - break - text = "\n".join(lines) + "\n" - # Add basic code fence languages where missing (simple heuristic) - text = re.sub(r"```\n([\s\S]*?)\n```", add_code_fence_language, text) - if text != orig: - path.write_text(text, encoding="utf-8") - return True - return False - - -def main(): - changed = [] - for p in DOCS.rglob("*.md"): - if p.is_file(): - try: - if normalize_file(p): - changed.append(str(p.relative_to(Path.cwd()))) - except Exception as e: - print(f"Failed to format {p}: {e}") - if changed: - print("Formatted files:") - for c in changed: - print(" -", c) - else: - print("No formatting changes required.") - - -if __name__ == "__main__": - main() diff --git a/scripts/migrations/000_base.sql b/scripts/migrations/000_base.sql deleted file mode 100644 index 11f9358..0000000 --- a/scripts/migrations/000_base.sql +++ /dev/null @@ -1,189 +0,0 @@ --- Baseline migration for CalMiner database schema --- Date: 2025-10-25 --- Purpose: Consolidate foundational tables and reference data - -BEGIN; - --- Currency reference table -CREATE TABLE IF NOT EXISTS currency ( - id SERIAL PRIMARY KEY, - code VARCHAR(3) NOT NULL UNIQUE, - name VARCHAR(128) NOT NULL, - symbol VARCHAR(8), - is_active BOOLEAN NOT NULL DEFAULT TRUE -); - -INSERT INTO currency (code, name, symbol, is_active) -VALUES - ('USD', 'United States Dollar', 'USD$', TRUE), - ('EUR', 'Euro', 'EUR', TRUE), - ('CLP', 'Chilean Peso', 'CLP$', TRUE), - ('RMB', 'Chinese Yuan', 'RMB', TRUE), - ('GBP', 'British Pound', 'GBP', TRUE), - ('CAD', 'Canadian Dollar', 'CAD$', TRUE), - ('AUD', 'Australian Dollar', 'AUD$', TRUE) -ON CONFLICT (code) DO UPDATE -SET name = EXCLUDED.name, - symbol = EXCLUDED.symbol, - is_active = EXCLUDED.is_active; - --- Application-level settings table -CREATE TABLE IF NOT EXISTS application_setting ( - id SERIAL PRIMARY KEY, - key VARCHAR(128) NOT NULL UNIQUE, - value TEXT NOT NULL, - value_type VARCHAR(32) NOT NULL DEFAULT 'string', - category VARCHAR(32) NOT NULL DEFAULT 'general', - description TEXT, - is_editable BOOLEAN NOT NULL DEFAULT TRUE, - created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), - updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() -); - -CREATE UNIQUE INDEX IF NOT EXISTS ux_application_setting_key - ON application_setting (key); - -CREATE INDEX IF NOT EXISTS ix_application_setting_category - ON application_setting (category); - --- Measurement unit reference table -CREATE TABLE IF NOT EXISTS measurement_unit ( - id SERIAL PRIMARY KEY, - code VARCHAR(64) NOT NULL UNIQUE, - name VARCHAR(128) NOT NULL, - symbol VARCHAR(16), - unit_type VARCHAR(32) NOT NULL, - is_active BOOLEAN NOT NULL DEFAULT TRUE, - created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), - updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() -); - -INSERT INTO measurement_unit (code, name, symbol, unit_type, is_active) -VALUES - ('tonnes', 'Tonnes', 't', 'mass', TRUE), - ('kilograms', 'Kilograms', 'kg', 'mass', TRUE), - ('pounds', 'Pounds', 'lb', 'mass', TRUE), - ('liters', 'Liters', 'L', 'volume', TRUE), - ('cubic_meters', 'Cubic Meters', 'm3', 'volume', TRUE), - ('kilowatt_hours', 'Kilowatt Hours', 'kWh', 'energy', TRUE) -ON CONFLICT (code) DO UPDATE -SET name = EXCLUDED.name, - symbol = EXCLUDED.symbol, - unit_type = EXCLUDED.unit_type, - is_active = EXCLUDED.is_active; - --- Consumption and production measurement metadata -ALTER TABLE consumption - ADD COLUMN IF NOT EXISTS unit_name VARCHAR(64); -ALTER TABLE consumption - ADD COLUMN IF NOT EXISTS unit_symbol VARCHAR(16); - -ALTER TABLE production_output - ADD COLUMN IF NOT EXISTS unit_name VARCHAR(64); -ALTER TABLE production_output - ADD COLUMN IF NOT EXISTS unit_symbol VARCHAR(16); - --- Currency integration for CAPEX and OPEX -ALTER TABLE capex - ADD COLUMN IF NOT EXISTS currency_id INTEGER; -ALTER TABLE opex - ADD COLUMN IF NOT EXISTS currency_id INTEGER; - -DO $$ -DECLARE - usd_id INTEGER; -BEGIN - -- Ensure currency_id columns align with legacy currency_code values when present - IF EXISTS ( - SELECT 1 FROM information_schema.columns - WHERE table_name = 'capex' AND column_name = 'currency_code' - ) THEN - UPDATE capex AS c - SET currency_id = cur.id - FROM currency AS cur - WHERE c.currency_code = cur.code - AND (c.currency_id IS DISTINCT FROM cur.id); - END IF; - - IF EXISTS ( - SELECT 1 FROM information_schema.columns - WHERE table_name = 'opex' AND column_name = 'currency_code' - ) THEN - UPDATE opex AS o - SET currency_id = cur.id - FROM currency AS cur - WHERE o.currency_code = cur.code - AND (o.currency_id IS DISTINCT FROM cur.id); - END IF; - - SELECT id INTO usd_id FROM currency WHERE code = 'USD'; - IF usd_id IS NOT NULL THEN - UPDATE capex SET currency_id = usd_id WHERE currency_id IS NULL; - UPDATE opex SET currency_id = usd_id WHERE currency_id IS NULL; - END IF; -END $$; - -ALTER TABLE capex - ALTER COLUMN currency_id SET NOT NULL; -ALTER TABLE opex - ALTER COLUMN currency_id SET NOT NULL; - -DO $$ -BEGIN - IF NOT EXISTS ( - SELECT 1 FROM information_schema.table_constraints - WHERE table_schema = current_schema() - AND table_name = 'capex' - AND constraint_name = 'fk_capex_currency' - ) THEN - ALTER TABLE capex - ADD CONSTRAINT fk_capex_currency FOREIGN KEY (currency_id) - REFERENCES currency (id) ON DELETE RESTRICT; - END IF; - - IF NOT EXISTS ( - SELECT 1 FROM information_schema.table_constraints - WHERE table_schema = current_schema() - AND table_name = 'opex' - AND constraint_name = 'fk_opex_currency' - ) THEN - ALTER TABLE opex - ADD CONSTRAINT fk_opex_currency FOREIGN KEY (currency_id) - REFERENCES currency (id) ON DELETE RESTRICT; - END IF; -END $$; - -ALTER TABLE capex - DROP COLUMN IF EXISTS currency_code; -ALTER TABLE opex - DROP COLUMN IF EXISTS currency_code; - --- Role-based access control tables -CREATE TABLE IF NOT EXISTS roles ( - id SERIAL PRIMARY KEY, - name VARCHAR(255) UNIQUE NOT NULL -); - -CREATE TABLE IF NOT EXISTS users ( - id SERIAL PRIMARY KEY, - username VARCHAR(255) UNIQUE NOT NULL, - email VARCHAR(255) UNIQUE NOT NULL, - hashed_password VARCHAR(255) NOT NULL, - role_id INTEGER NOT NULL REFERENCES roles (id) ON DELETE RESTRICT -); - -CREATE INDEX IF NOT EXISTS ix_users_username ON users (username); -CREATE INDEX IF NOT EXISTS ix_users_email ON users (email); - --- Theme settings configuration table -CREATE TABLE IF NOT EXISTS theme_settings ( - id SERIAL PRIMARY KEY, - theme_name VARCHAR(255) UNIQUE NOT NULL, - primary_color VARCHAR(7) NOT NULL, - secondary_color VARCHAR(7) NOT NULL, - accent_color VARCHAR(7) NOT NULL, - background_color VARCHAR(7) NOT NULL, - text_color VARCHAR(7) NOT NULL -); - -COMMIT; diff --git a/scripts/seed_data.py b/scripts/seed_data.py deleted file mode 100644 index b762d04..0000000 --- a/scripts/seed_data.py +++ /dev/null @@ -1,268 +0,0 @@ -"""Seed baseline data for CalMiner in an idempotent manner. - -Usage examples --------------- - -```powershell -# Use existing environment variables (or load from setup_test.env.example) -python scripts/seed_data.py --currencies --units --defaults - -# Dry-run to preview actions -python scripts/seed_data.py --currencies --dry-run -``` -""" - -from __future__ import annotations - -import argparse -import logging -from typing import Optional - -import psycopg2 -from psycopg2 import errors -from psycopg2.extras import execute_values - -from scripts.setup_database import DatabaseConfig - - -logger = logging.getLogger(__name__) - -CURRENCY_SEEDS = ( - ("USD", "United States Dollar", "USD$", True), - ("EUR", "Euro", "EUR", True), - ("CLP", "Chilean Peso", "CLP$", True), - ("RMB", "Chinese Yuan", "RMB", True), - ("GBP", "British Pound", "GBP", True), - ("CAD", "Canadian Dollar", "CAD$", True), - ("AUD", "Australian Dollar", "AUD$", True), -) - -MEASUREMENT_UNIT_SEEDS = ( - ("tonnes", "Tonnes", "t", "mass", True), - ("kilograms", "Kilograms", "kg", "mass", True), - ("pounds", "Pounds", "lb", "mass", True), - ("liters", "Liters", "L", "volume", True), - ("cubic_meters", "Cubic Meters", "m3", "volume", True), - ("kilowatt_hours", "Kilowatt Hours", "kWh", "energy", True), -) - -THEME_SETTING_SEEDS = ( - ("--color-background", "#f4f5f7", "color", - "theme", "CSS variable --color-background", True), - ("--color-surface", "#ffffff", "color", - "theme", "CSS variable --color-surface", True), - ("--color-text-primary", "#2a1f33", "color", - "theme", "CSS variable --color-text-primary", True), - ("--color-text-secondary", "#624769", "color", - "theme", "CSS variable --color-text-secondary", True), - ("--color-text-muted", "#64748b", "color", - "theme", "CSS variable --color-text-muted", True), - ("--color-text-subtle", "#94a3b8", "color", - "theme", "CSS variable --color-text-subtle", True), - ("--color-text-invert", "#ffffff", "color", - "theme", "CSS variable --color-text-invert", True), - ("--color-text-dark", "#0f172a", "color", - "theme", "CSS variable --color-text-dark", True), - ("--color-text-strong", "#111827", "color", - "theme", "CSS variable --color-text-strong", True), - ("--color-primary", "#5f320d", "color", - "theme", "CSS variable --color-primary", True), - ("--color-primary-strong", "#7e4c13", "color", - "theme", "CSS variable --color-primary-strong", True), - ("--color-primary-stronger", "#837c15", "color", - "theme", "CSS variable --color-primary-stronger", True), - ("--color-accent", "#bff838", "color", - "theme", "CSS variable --color-accent", True), - ("--color-border", "#e2e8f0", "color", - "theme", "CSS variable --color-border", True), - ("--color-border-strong", "#cbd5e1", "color", - "theme", "CSS variable --color-border-strong", True), - ("--color-highlight", "#eef2ff", "color", - "theme", "CSS variable --color-highlight", True), - ("--color-panel-shadow", "rgba(15, 23, 42, 0.08)", "color", - "theme", "CSS variable --color-panel-shadow", True), - ("--color-panel-shadow-deep", "rgba(15, 23, 42, 0.12)", "color", - "theme", "CSS variable --color-panel-shadow-deep", True), - ("--color-surface-alt", "#f8fafc", "color", - "theme", "CSS variable --color-surface-alt", True), - ("--color-success", "#047857", "color", - "theme", "CSS variable --color-success", True), - ("--color-error", "#b91c1c", "color", - "theme", "CSS variable --color-error", True), -) - - -def parse_args() -> argparse.Namespace: - parser = argparse.ArgumentParser(description="Seed baseline CalMiner data") - parser.add_argument( - "--currencies", action="store_true", help="Seed currency table" - ) - parser.add_argument("--units", action="store_true", help="Seed unit table") - parser.add_argument( - "--theme", action="store_true", help="Seed theme settings" - ) - parser.add_argument( - "--defaults", action="store_true", help="Seed default records" - ) - parser.add_argument( - "--dry-run", action="store_true", help="Print actions without executing" - ) - parser.add_argument( - "--verbose", - "-v", - action="count", - default=0, - help="Increase logging verbosity", - ) - return parser.parse_args() - - -def _configure_logging(args: argparse.Namespace) -> None: - level = logging.WARNING - (10 * min(args.verbose, 2)) - logging.basicConfig( - level=max(level, logging.INFO), format="%(levelname)s %(message)s" - ) - - -def main() -> None: - args = parse_args() - run_with_namespace(args) - - -def run_with_namespace( - args: argparse.Namespace, - *, - config: Optional[DatabaseConfig] = None, -) -> None: - if not hasattr(args, "verbose"): - args.verbose = 0 - if not hasattr(args, "dry_run"): - args.dry_run = False - - _configure_logging(args) - - currencies = bool(getattr(args, "currencies", False)) - units = bool(getattr(args, "units", False)) - theme = bool(getattr(args, "theme", False)) - defaults = bool(getattr(args, "defaults", False)) - dry_run = bool(getattr(args, "dry_run", False)) - - if not any((currencies, units, theme, defaults)): - logger.info("No seeding options provided; exiting") - return - - config = config or DatabaseConfig.from_env() - - with psycopg2.connect(config.application_dsn()) as conn: - conn.autocommit = True - with conn.cursor() as cursor: - if currencies: - _seed_currencies(cursor, dry_run=dry_run) - if units: - _seed_units(cursor, dry_run=dry_run) - if theme: - _seed_theme(cursor, dry_run=dry_run) - if defaults: - _seed_defaults(cursor, dry_run=dry_run) - - -def _seed_currencies(cursor, *, dry_run: bool) -> None: - logger.info("Seeding currency table (%d rows)", len(CURRENCY_SEEDS)) - if dry_run: - for code, name, symbol, active in CURRENCY_SEEDS: - logger.info("Dry run: would upsert currency %s (%s)", code, name) - return - - execute_values( - cursor, - """ - INSERT INTO currency (code, name, symbol, is_active) - VALUES %s - ON CONFLICT (code) DO UPDATE - SET name = EXCLUDED.name, - symbol = EXCLUDED.symbol, - is_active = EXCLUDED.is_active - """, - CURRENCY_SEEDS, - ) - logger.info("Currency seed complete") - - -def _seed_units(cursor, *, dry_run: bool) -> None: - total = len(MEASUREMENT_UNIT_SEEDS) - logger.info("Seeding measurement_unit table (%d rows)", total) - if dry_run: - for code, name, symbol, unit_type, _ in MEASUREMENT_UNIT_SEEDS: - logger.info( - "Dry run: would upsert measurement unit %s (%s - %s)", - code, - name, - unit_type, - ) - return - - try: - execute_values( - cursor, - """ - INSERT INTO measurement_unit (code, name, symbol, unit_type, is_active) - VALUES %s - ON CONFLICT (code) DO UPDATE - SET name = EXCLUDED.name, - symbol = EXCLUDED.symbol, - unit_type = EXCLUDED.unit_type, - is_active = EXCLUDED.is_active - """, - MEASUREMENT_UNIT_SEEDS, - ) - except errors.UndefinedTable: - logger.warning( - "measurement_unit table does not exist; skipping unit seeding." - ) - cursor.connection.rollback() - return - - logger.info("Measurement unit seed complete") - - -def _seed_theme(cursor, *, dry_run: bool) -> None: - logger.info("Seeding theme settings (%d rows)", len(THEME_SETTING_SEEDS)) - if dry_run: - for key, value, _, _, _, _ in THEME_SETTING_SEEDS: - logger.info( - "Dry run: would upsert theme setting %s = %s", key, value) - return - - try: - execute_values( - cursor, - """ - INSERT INTO application_setting (key, value, value_type, category, description, is_editable) - VALUES %s - ON CONFLICT (key) DO UPDATE - SET value = EXCLUDED.value, - value_type = EXCLUDED.value_type, - category = EXCLUDED.category, - description = EXCLUDED.description, - is_editable = EXCLUDED.is_editable - """, - THEME_SETTING_SEEDS, - ) - except errors.UndefinedTable: - logger.warning( - "application_setting table does not exist; skipping theme seeding." - ) - cursor.connection.rollback() - return - - logger.info("Theme settings seed complete") - - -def _seed_defaults(cursor, *, dry_run: bool) -> None: - logger.info("Seeding default records") - _seed_theme(cursor, dry_run=dry_run) - logger.info("Default records seed complete") - - -if __name__ == "__main__": - main() diff --git a/scripts/setup_database.py b/scripts/setup_database.py deleted file mode 100644 index 918d1e6..0000000 --- a/scripts/setup_database.py +++ /dev/null @@ -1,1233 +0,0 @@ -"""Utilities to bootstrap the CalMiner PostgreSQL database. - -This script is designed to be idempotent. Each step checks the existing -state before attempting to modify it so repeated executions are safe. - -Environment variables (with defaults) used when establishing connections: - -* ``DATABASE_DRIVER`` (``postgresql``) -* ``DATABASE_HOST`` (required) -* ``DATABASE_PORT`` (``5432``) -* ``DATABASE_NAME`` (required) -* ``DATABASE_USER`` (required) -* ``DATABASE_PASSWORD`` (optional, required for password auth) -* ``DATABASE_SCHEMA`` (``public``) -* ``DATABASE_ADMIN_URL`` (overrides individual admin settings) -* ``DATABASE_SUPERUSER`` (falls back to ``DATABASE_USER`` or ``postgres``) -* ``DATABASE_SUPERUSER_PASSWORD`` (falls back to ``DATABASE_PASSWORD``) -* ``DATABASE_SUPERUSER_DB`` (``postgres``) - -Set ``DATABASE_URL`` if other parts of the application rely on a single -connection string; this script will still honor the granular inputs above. -""" - -from __future__ import annotations -from config.database import Base -import argparse -import importlib -import logging -import os -import pkgutil -import sys -from dataclasses import dataclass -from pathlib import Path -from typing import Callable, Optional, cast -from urllib.parse import quote_plus, urlencode -import psycopg2 -from psycopg2 import errors -from psycopg2 import sql -from psycopg2 import extensions -from psycopg2.extensions import connection as PGConnection, parse_dsn -from dotenv import load_dotenv -from sqlalchemy import create_engine, inspect - -ROOT_DIR = Path(__file__).resolve().parents[1] -if str(ROOT_DIR) not in sys.path: - sys.path.insert(0, str(ROOT_DIR)) - - -logger = logging.getLogger(__name__) - -SCRIPTS_DIR = Path(__file__).resolve().parent -DEFAULT_MIGRATIONS_DIR = SCRIPTS_DIR / "migrations" -MIGRATIONS_TABLE = "schema_migrations" - - -@dataclass(slots=True) -class DatabaseConfig: - """Configuration required to manage the application database.""" - - driver: str - host: str - port: int - database: str - user: str - password: Optional[str] - schema: Optional[str] - - admin_user: str - admin_password: Optional[str] - admin_database: str = "postgres" - - @classmethod - def from_env( - cls, - overrides: Optional[dict[str, Optional[str]]] = None, - ) -> "DatabaseConfig": - load_dotenv() - - override_map: dict[str, Optional[str]] = dict(overrides or {}) - - def _get(name: str, default: Optional[str] = None) -> Optional[str]: - if name in override_map and override_map[name] is not None: - return override_map[name] - env_value = os.getenv(name) - if env_value is not None: - return env_value - return default - - driver = _get("DATABASE_DRIVER", "postgresql") - host = _get("DATABASE_HOST") - port_value = _get("DATABASE_PORT", "5432") - database = _get("DATABASE_NAME") - user = _get("DATABASE_USER") - password = _get("DATABASE_PASSWORD") - schema = _get("DATABASE_SCHEMA", "public") - - try: - port = int(port_value) if port_value is not None else 5432 - except ValueError as exc: - raise RuntimeError( - "Invalid DATABASE_PORT value: expected integer, got" - f" '{port_value}'" - ) from exc - - admin_url = _get("DATABASE_ADMIN_URL") - if admin_url: - admin_conninfo = parse_dsn(admin_url) - admin_user = admin_conninfo.get("user") or user or "postgres" - admin_password = admin_conninfo.get("password") - admin_database = admin_conninfo.get("dbname") or "postgres" - host = admin_conninfo.get("host") or host - port = int(admin_conninfo.get("port") or port) - else: - admin_user = _get("DATABASE_SUPERUSER", user or "postgres") - admin_password = _get("DATABASE_SUPERUSER_PASSWORD", password) - admin_database = _get("DATABASE_SUPERUSER_DB", "postgres") - - missing = [ - name - for name, value in ( - ("DATABASE_HOST", host), - ("DATABASE_NAME", database), - ("DATABASE_USER", user), - ) - if not value - ] - if missing: - raise RuntimeError( - "Missing required database configuration: " + - ", ".join(missing) - ) - - host = cast(str, host) - database = cast(str, database) - user = cast(str, user) - driver = cast(str, driver) - admin_user = cast(str, admin_user) - admin_database = cast(str, admin_database) - - return cls( - driver=driver, - host=host, - port=port, - database=database, - user=user, - password=password, - schema=schema, - admin_user=admin_user, - admin_password=admin_password, - admin_database=admin_database, - ) - - def admin_dsn(self, database: Optional[str] = None) -> str: - target_db = database or self.admin_database - return self._compose_url( - user=self.admin_user, - password=self.admin_password, - database=target_db, - schema=None, - ) - - def application_dsn(self) -> str: - """Return a SQLAlchemy URL for connecting as the application role.""" - - return self._compose_url( - user=self.user, - password=self.password, - database=self.database, - schema=self.schema, - ) - - def _compose_url( - self, - *, - user: Optional[str], - password: Optional[str], - database: str, - schema: Optional[str], - ) -> str: - auth = "" - if user: - encoded_user = quote_plus(user) - if password: - encoded_pass = quote_plus(password) - auth = f"{encoded_user}:{encoded_pass}@" - else: - auth = f"{encoded_user}@" - - host = self.host - if ":" in host and not host.startswith("["): - host = f"[{host}]" - - host_port = host - if self.port: - host_port = f"{host}:{self.port}" - - url = f"{self.driver}://{auth}{host_port}/{database}" - - params = {} - if schema and schema.strip() and schema != "public": - params["options"] = f"-csearch_path={schema}" - - if params: - url = f"{url}?{urlencode(params, quote_via=quote_plus)}" - - return url - - -class DatabaseSetup: - """Encapsulates the full setup workflow.""" - - def __init__( - self, config: DatabaseConfig, *, dry_run: bool = False - ) -> None: - self.config = config - self.dry_run = dry_run - self._models_loaded = False - self._rollback_actions: list[tuple[str, Callable[[], None]]] = [] - - def _register_rollback( - self, label: str, action: Callable[[], None] - ) -> None: - if self.dry_run: - return - self._rollback_actions.append((label, action)) - - def execute_rollbacks(self) -> None: - if not self._rollback_actions: - logger.info("No rollback actions registered; nothing to undo.") - return - - logger.warning( - "Attempting rollback of %d action(s)", len(self._rollback_actions) - ) - for label, action in reversed(self._rollback_actions): - try: - logger.warning("Rollback step: %s", label) - action() - except Exception: - logger.exception("Rollback action '%s' failed", label) - self._rollback_actions.clear() - - def clear_rollbacks(self) -> None: - self._rollback_actions.clear() - - def _describe_connection(self, user: str, database: str) -> str: - return f"{user}@{self.config.host}:{self.config.port}/{database}" - - def validate_admin_connection(self) -> None: - descriptor = self._describe_connection( - self.config.admin_user, self.config.admin_database - ) - logger.info("[CONNECT] Validating admin connection (%s)", descriptor) - try: - with self._admin_connection(self.config.admin_database) as conn: - with conn.cursor() as cursor: - cursor.execute("SELECT 1") - except psycopg2.Error as exc: - raise RuntimeError( - "Unable to connect with admin credentials. " - "Check DATABASE_ADMIN_URL or DATABASE_SUPERUSER settings." - f" Target: {descriptor}" - ) from exc - logger.info("[CONNECT] Admin connection verified (%s)", descriptor) - - def validate_application_connection(self) -> None: - descriptor = self._describe_connection( - self.config.user, self.config.database - ) - logger.info( - "[CONNECT] Validating application connection (%s)", descriptor) - try: - with self._application_connection() as conn: - with conn.cursor() as cursor: - cursor.execute("SELECT 1") - except psycopg2.Error as exc: - raise RuntimeError( - "Unable to connect using application credentials. " - "Ensure the role exists and credentials are correct. " - f"Target: {descriptor}" - ) from exc - logger.info( - "[CONNECT] Application connection verified (%s)", descriptor) - - def ensure_database(self) -> None: - """Create the target database when it does not already exist.""" - - logger.info("Ensuring database '%s' exists", self.config.database) - try: - conn = self._admin_connection(self.config.admin_database) - except RuntimeError: - logger.error( - "Could not connect to admin database '%s' while creating '%s'.", - self.config.admin_database, - self.config.database, - ) - raise - try: - conn.autocommit = True - conn.set_isolation_level(extensions.ISOLATION_LEVEL_AUTOCOMMIT) - cursor = conn.cursor() - try: - try: - cursor.execute( - "SELECT 1 FROM pg_database WHERE datname = %s", - (self.config.database,), - ) - except psycopg2.Error as exc: - message = ( - "Unable to inspect existing databases while ensuring '%s'." - " Verify admin permissions." - ) % self.config.database - logger.error(message) - raise RuntimeError(message) from exc - - exists = cursor.fetchone() is not None - if exists: - logger.info( - "Database '%s' already present", self.config.database - ) - return - - if self.dry_run: - logger.info( - "Dry run: would create database '%s'. Run without --dry-run to proceed.", - self.config.database, - ) - return - - try: - cursor.execute( - sql.SQL("CREATE DATABASE {} ENCODING 'UTF8'").format( - sql.Identifier(self.config.database) - ) - ) - except psycopg2.Error as exc: - message = ( - "Failed to create database '%s'. Rerun with --dry-run for diagnostics" - ) % self.config.database - logger.error(message) - raise RuntimeError(message) from exc - else: - rollback_label = f"drop database {self.config.database}" - self._register_rollback( - rollback_label, - lambda db=self.config.database: self._drop_database( - db), - ) - logger.info("Created database '%s'", self.config.database) - finally: - cursor.close() - finally: - conn.close() - - def ensure_role(self) -> None: - """Create the application role and assign privileges when missing.""" - - logger.info("Ensuring role '%s' exists", self.config.user) - try: - admin_conn = self._admin_connection(self.config.admin_database) - except RuntimeError: - logger.error( - "Unable to connect with admin credentials while ensuring role '%s'", - self.config.user, - ) - raise - - with admin_conn as conn: - conn.autocommit = True - with conn.cursor() as cursor: - try: - cursor.execute( - "SELECT 1 FROM pg_roles WHERE rolname = %s", - (self.config.user,), - ) - except psycopg2.Error as exc: - message = ( - "Unable to inspect existing roles while ensuring role '%s'." - " Verify admin permissions." - ) % self.config.user - logger.error(message) - raise RuntimeError(message) from exc - role_exists = cursor.fetchone() is not None - if not role_exists: - logger.info("Creating role '%s'", self.config.user) - if self.dry_run: - logger.info( - "Dry run: would create role '%s'. Run without --dry-run to apply.", - self.config.user, - ) - return - try: - if self.config.password: - cursor.execute( - sql.SQL( - "CREATE ROLE {} WITH LOGIN PASSWORD %s" - ).format(sql.Identifier(self.config.user)), - (self.config.password,), - ) - else: - cursor.execute( - sql.SQL("CREATE ROLE {} WITH LOGIN").format( - sql.Identifier(self.config.user) - ) - ) - except psycopg2.Error as exc: - message = ( - "Failed to create role '%s'. Review admin privileges and rerun." - ) % self.config.user - logger.error(message) - raise RuntimeError(message) from exc - else: - rollback_label = f"drop role {self.config.user}" - self._register_rollback( - rollback_label, - lambda role=self.config.user: self._drop_role( - role), - ) - else: - logger.info("Role '%s' already present", self.config.user) - - try: - role_conn = self._admin_connection(self.config.database) - except RuntimeError: - logger.error( - "Unable to connect to application database '%s' while granting privileges to role '%s'", - self.config.database, - self.config.user, - ) - raise - - if self.dry_run: - logger.info( - "Dry run: would grant privileges on schema/database to role '%s'.", - self.config.user, - ) - return - - with role_conn as conn: - conn.autocommit = True - with conn.cursor() as cursor: - schema_name = self.config.schema or "public" - schema_identifier = sql.Identifier(schema_name) - role_identifier = sql.Identifier(self.config.user) - - try: - cursor.execute( - sql.SQL("GRANT CONNECT ON DATABASE {} TO {}").format( - sql.Identifier(self.config.database), - role_identifier, - ) - ) - cursor.execute( - sql.SQL("GRANT USAGE ON SCHEMA {} TO {}").format( - schema_identifier, - role_identifier, - ) - ) - cursor.execute( - sql.SQL("GRANT CREATE ON SCHEMA {} TO {}").format( - schema_identifier, - role_identifier, - ) - ) - cursor.execute( - sql.SQL( - "GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA {} TO {}" - ).format( - schema_identifier, - role_identifier, - ) - ) - cursor.execute( - sql.SQL( - "GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA {} TO {}" - ).format( - schema_identifier, - role_identifier, - ) - ) - cursor.execute( - sql.SQL( - "ALTER DEFAULT PRIVILEGES IN SCHEMA {} GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO {}" - ).format( - schema_identifier, - role_identifier, - ) - ) - cursor.execute( - sql.SQL( - "ALTER DEFAULT PRIVILEGES IN SCHEMA {} GRANT USAGE, SELECT ON SEQUENCES TO {}" - ).format( - schema_identifier, - role_identifier, - ) - ) - except psycopg2.Error as exc: - message = ( - "Failed to grant privileges to role '%s' in schema '%s'." - " Rerun with --dry-run for more context." - ) % (self.config.user, schema_name) - logger.error(message) - raise RuntimeError(message) from exc - logger.info( - "Granted privileges on schema '%s' to role '%s'", - schema_name, - self.config.user, - ) - rollback_label = f"revoke privileges for {self.config.user}" - self._register_rollback( - rollback_label, - lambda schema=schema_name: self._revoke_role_privileges( - schema_name=schema - ), - ) - - def ensure_schema(self) -> None: - """Create the configured schema when it does not exist.""" - - schema_name = self.config.schema - if not schema_name or schema_name == "public": - logger.info("Using default schema 'public'; nothing to ensure") - return - - logger.info("Ensuring schema '%s' exists", schema_name) - with self._admin_connection(self.config.database) as conn: - conn.autocommit = True - with conn.cursor() as cursor: - cursor.execute( - sql.SQL( - "SELECT 1 FROM information_schema.schemata WHERE schema_name = %s" - ), - (schema_name,), - ) - exists = cursor.fetchone() is not None - if not exists: - if self.dry_run: - logger.info( - "Dry run: would create schema '%s'", - schema_name, - ) - else: - cursor.execute( - sql.SQL("CREATE SCHEMA {}").format( - sql.Identifier(schema_name) - ) - ) - logger.info("Created schema '%s'", schema_name) - try: - if self.dry_run: - logger.info( - "Dry run: would set schema '%s' owner to '%s'", - schema_name, - self.config.user, - ) - else: - cursor.execute( - sql.SQL("ALTER SCHEMA {} OWNER TO {}").format( - sql.Identifier(schema_name), - sql.Identifier(self.config.user), - ) - ) - except errors.UndefinedObject: - logger.warning( - "Role '%s' not found when assigning ownership to schema '%s'." - " Run --ensure-role after creating the schema.", - self.config.user, - schema_name, - ) - - def application_role_exists(self) -> bool: - try: - with self._admin_connection(self.config.admin_database) as conn: - with conn.cursor() as cursor: - try: - cursor.execute( - "SELECT 1 FROM pg_roles WHERE rolname = %s", - (self.config.user,), - ) - except psycopg2.Error as exc: - message = ( - "Unable to inspect existing roles while checking for role '%s'." - " Verify admin permissions." - ) % self.config.user - logger.error(message) - raise RuntimeError(message) from exc - return cursor.fetchone() is not None - except RuntimeError: - raise - - def _connect(self, dsn: str, descriptor: str) -> PGConnection: - try: - return psycopg2.connect(dsn) - except psycopg2.Error as exc: - raise RuntimeError( - f"Unable to establish connection. Target: {descriptor}" - ) from exc - - def _admin_connection(self, database: Optional[str] = None) -> PGConnection: - target_db = database or self.config.admin_database - dsn = self.config.admin_dsn(database) - descriptor = self._describe_connection( - self.config.admin_user, target_db - ) - return self._connect(dsn, descriptor) - - def _application_connection(self) -> PGConnection: - dsn = self.config.application_dsn() - descriptor = self._describe_connection( - self.config.user, self.config.database - ) - return self._connect(dsn, descriptor) - - def initialize_schema(self) -> None: - """Create database objects from SQLAlchemy metadata if missing.""" - - self._ensure_models_loaded() - logger.info("Ensuring SQLAlchemy metadata is reflected in database") - engine = create_engine(self.config.application_dsn(), future=True) - try: - inspector = inspect(engine) - existing_tables = set( - inspector.get_table_names(schema=self.config.schema) - ) - metadata_tables = set(Base.metadata.tables.keys()) - missing_tables = sorted(metadata_tables - existing_tables) - - if missing_tables: - logger.info("Pending tables: %s", ", ".join(missing_tables)) - else: - logger.info("All tables already exist") - - if self.dry_run: - if missing_tables: - logger.info("Dry run: skipping creation of pending tables") - return - - Base.metadata.create_all(bind=engine, checkfirst=True) - finally: - engine.dispose() - - logger.info("Schema initialization complete") - - def _ensure_models_loaded(self) -> None: - if self._models_loaded: - return - - package = importlib.import_module("models") - for module_info in pkgutil.iter_modules(package.__path__): - importlib.import_module(f"{package.__name__}.{module_info.name}") - self._models_loaded = True - - def run_migrations( - self, migrations_dir: Optional[Path | str] = None - ) -> None: - """Execute pending SQL migrations in chronological order.""" - - directory = ( - Path(migrations_dir) - if migrations_dir is not None - else DEFAULT_MIGRATIONS_DIR - ) - directory = directory.resolve() - - if not directory.exists(): - logger.warning("Migrations directory '%s' not found", directory) - return - - migration_files = sorted(directory.glob("*.sql")) - if not migration_files: - logger.info("No migration scripts found in '%s'", directory) - return - - baseline_name = "000_base.sql" - baseline_path = directory / baseline_name - - schema_name = self.config.schema or "public" - - with self._application_connection() as conn: - conn.autocommit = True - with conn.cursor() as cursor: - table_exists = self._migrations_table_exists( - cursor, schema_name - ) - if not table_exists: - if self.dry_run: - logger.info( - "Dry run: would create migration history table %s.%s", - schema_name, - MIGRATIONS_TABLE, - ) - applied: set[str] = set() - else: - self._create_migrations_table(cursor, schema_name) - logger.info( - "Created migration history table %s.%s", - schema_name, - MIGRATIONS_TABLE, - ) - applied = set() - else: - applied = self._fetch_applied_migrations( - cursor, schema_name - ) - - self._handle_baseline_migration( - cursor, schema_name, baseline_path, baseline_name, migration_files, applied - ) - - pending = [ - path for path in migration_files if path.name not in applied - ] - - if not pending: - logger.info("No pending migrations") - return - - logger.info( - "Pending migrations: %s", - ", ".join(path.name for path in pending), - ) - - if self.dry_run: - logger.info("Dry run: skipping migration execution") - return - - for path in pending: - self._apply_migration_file(cursor, schema_name, path) - - logger.info("Applied %d migrations", len(pending)) - - def _handle_baseline_migration( - self, - cursor: extensions.cursor, - schema_name: str, - baseline_path: Path, - baseline_name: str, - migration_files: list[Path], - applied: set[str], - ) -> None: - if baseline_path.exists() and baseline_name not in applied: - if self.dry_run: - logger.info( - "Dry run: baseline migration '%s' pending; would apply and mark legacy files", - baseline_name, - ) - else: - logger.info( - "[MIGRATE] Baseline migration '%s' pending; applying and marking older migrations", - baseline_name, - ) - try: - baseline_applied = self._apply_migration_file( - cursor, schema_name, baseline_path - ) - except Exception: - logger.error( - "Failed while applying baseline migration '%s'." - " Review the migration contents and rerun with --dry-run for diagnostics.", - baseline_name, - exc_info=True, - ) - raise - applied.add(baseline_applied) - self._mark_legacy_migrations_as_applied( - cursor, schema_name, migration_files, baseline_name, applied - ) - - def _mark_legacy_migrations_as_applied( - self, - cursor: extensions.cursor, - schema_name: str, - migration_files: list[Path], - baseline_name: str, - applied: set[str], - ) -> None: - legacy_files = [ - path - for path in migration_files - if path.name != baseline_name - ] - for legacy in legacy_files: - if legacy.name not in applied: - try: - cursor.execute( - sql.SQL( - "INSERT INTO {} (filename, applied_at) VALUES (%s, NOW())" - ).format( - sql.Identifier( - schema_name, - MIGRATIONS_TABLE, - ) - ), - (legacy.name,), - ) - except Exception: - logger.error( - "Unable to record legacy migration '%s' after baseline application." - " Check schema_migrations table in schema '%s' for partial state.", - legacy.name, - schema_name, - exc_info=True, - ) - raise - applied.add(legacy.name) - logger.info( - "Marked legacy migration '%s' as applied via baseline", - legacy.name, - ) - - def _apply_migration_file( - self, - cursor, - schema_name: str, - path: Path, - ) -> str: - logger.info("Applying migration '%s'", path.name) - sql_text = path.read_text(encoding="utf-8") - try: - cursor.execute(sql_text) - cursor.execute( - sql.SQL( - "INSERT INTO {} (filename, applied_at) VALUES (%s, NOW())" - ).format(sql.Identifier(schema_name, MIGRATIONS_TABLE)), - (path.name,), - ) - return path.name - except Exception: - logger.exception("Failed to apply migration '%s'", path.name) - raise - - def _migrations_table_exists(self, cursor, schema_name: str) -> bool: - cursor.execute( - """ - SELECT 1 - FROM information_schema.tables - WHERE table_schema = %s AND table_name = %s - """, - (schema_name, MIGRATIONS_TABLE), - ) - return cursor.fetchone() is not None - - def _create_migrations_table(self, cursor, schema_name: str) -> None: - cursor.execute( - sql.SQL( - "CREATE TABLE IF NOT EXISTS {} (" - "filename TEXT PRIMARY KEY," - "applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW()" - ")" - ).format(sql.Identifier(schema_name, MIGRATIONS_TABLE)) - ) - - def _fetch_applied_migrations(self, cursor, schema_name: str) -> set[str]: - cursor.execute( - sql.SQL("SELECT filename FROM {} ORDER BY filename").format( - sql.Identifier(schema_name, MIGRATIONS_TABLE) - ) - ) - return {row[0] for row in cursor.fetchall()} - - def seed_baseline_data(self, *, dry_run: bool) -> None: - """Seed reference data such as currencies.""" - - from scripts import seed_data - - seed_args = argparse.Namespace( - currencies=True, - units=True, - theme=True, - defaults=False, - dry_run=dry_run, - verbose=0, - ) - try: - seed_data.run_with_namespace(seed_args, config=self.config) - except Exception: - logger.error( - "[SEED] Failed during baseline data seeding. " - "Review seed_data.py and rerun with --dry-run for diagnostics.", - exc_info=True, - ) - raise - - if dry_run: - logger.info("[SEED] Dry run: skipped seed verification") - return - - expected_currencies = { - code for code, *_ in getattr(seed_data, "CURRENCY_SEEDS", ()) - } - expected_units = { - code - for code, *_ in getattr(seed_data, "MEASUREMENT_UNIT_SEEDS", ()) - } - self._verify_seeded_data( - expected_currency_codes=expected_currencies, - expected_unit_codes=expected_units, - ) - - def _verify_seeded_data( - self, - *, - expected_currency_codes: set[str], - expected_unit_codes: set[str], - ) -> None: - if not expected_currency_codes and not expected_unit_codes: - logger.info("No seed datasets configured for verification") - return - - with self._application_connection() as conn: - with conn.cursor() as cursor: - if expected_currency_codes: - cursor.execute( - "SELECT code, is_active FROM currency WHERE code = ANY(%s)", - (list(expected_currency_codes),), - ) - rows = cursor.fetchall() - found_codes = {row[0] for row in rows} - missing_codes = sorted( - expected_currency_codes - found_codes - ) - if missing_codes: - message = ( - "Missing expected currencies after seeding: %s. " - "Run scripts/seed_data.py --currencies to restore them." - ) % ", ".join(missing_codes) - logger.error(message) - raise RuntimeError(message) - - logger.info( - "[VERIFY] Verified %d seeded currencies present", - len(found_codes), - ) - - default_status = next( - (row[1] for row in rows if row[0] == "USD"), None - ) - if default_status is False: - message = ( - "Default currency 'USD' is inactive after seeding. " - "Reactivate it or rerun the seeding command." - ) - logger.error(message) - raise RuntimeError(message) - elif default_status is None: - message = ( - "Default currency 'USD' not found after seeding. " - "Ensure baseline migration 000_base.sql ran successfully." - ) - logger.error(message) - raise RuntimeError(message) - else: - logger.info( - "[VERIFY] Verified default currency 'USD' active") - - if expected_unit_codes: - try: - cursor.execute( - "SELECT code, is_active FROM measurement_unit WHERE code = ANY(%s)", - (list(expected_unit_codes),), - ) - except errors.UndefinedTable: - conn.rollback() - message = ( - "measurement_unit table not found during seed verification. " - "Ensure baseline migration 000_base.sql has been applied." - ) - logger.error(message) - raise RuntimeError(message) - else: - rows = cursor.fetchall() - found_units = {row[0] for row in rows} - missing_units = sorted( - expected_unit_codes - found_units - ) - if missing_units: - message = ( - "Missing expected measurement units after seeding: %s. " - "Run scripts/seed_data.py --units to restore them." - ) % ", ".join(missing_units) - logger.error(message) - raise RuntimeError(message) - - inactive_units = sorted( - row[0] for row in rows if not bool(row[1]) - ) - if inactive_units: - message = ( - "Measurement units inactive after seeding: %s. " - "Reactivate them or rerun unit seeding." - ) % ", ".join(inactive_units) - logger.error(message) - raise RuntimeError(message) - - logger.info( - "Verified %d measurement units present", - len(found_units), - ) - - logger.info("Seed verification complete") - - def _drop_database(self, database: str) -> None: - logger.warning("Rollback: dropping database '%s'", database) - with self._admin_connection(self.config.admin_database) as conn: - conn.autocommit = True - with conn.cursor() as cursor: - cursor.execute( - "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = %s", - (database,), - ) - cursor.execute( - sql.SQL("DROP DATABASE IF EXISTS {}").format( - sql.Identifier(database) - ) - ) - - def _drop_role(self, role: str) -> None: - logger.warning("Rollback: dropping role '%s'", role) - with self._admin_connection(self.config.admin_database) as conn: - conn.autocommit = True - with conn.cursor() as cursor: - cursor.execute( - sql.SQL("DROP ROLE IF EXISTS {}").format( - sql.Identifier(role) - ) - ) - - def _revoke_role_privileges(self, *, schema_name: str) -> None: - logger.warning( - "Rollback: revoking privileges on schema '%s' for role '%s'", - schema_name, - self.config.user, - ) - with self._admin_connection(self.config.database) as conn: - conn.autocommit = True - with conn.cursor() as cursor: - cursor.execute( - sql.SQL( - "REVOKE ALL PRIVILEGES ON ALL TABLES IN SCHEMA {} FROM {}" - ).format( - sql.Identifier(schema_name), - sql.Identifier(self.config.user), - ) - ) - cursor.execute( - sql.SQL( - "REVOKE ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA {} FROM {}" - ).format( - sql.Identifier(schema_name), - sql.Identifier(self.config.user), - ) - ) - cursor.execute( - sql.SQL( - "ALTER DEFAULT PRIVILEGES IN SCHEMA {} REVOKE SELECT, INSERT, UPDATE, DELETE ON TABLES FROM {}" - ).format( - sql.Identifier(schema_name), - sql.Identifier(self.config.user), - ) - ) - cursor.execute( - sql.SQL( - "ALTER DEFAULT PRIVILEGES IN SCHEMA {} REVOKE USAGE, SELECT ON SEQUENCES FROM {}" - ).format( - sql.Identifier(schema_name), - sql.Identifier(self.config.user), - ) - ) - - -def parse_args() -> argparse.Namespace: - parser = argparse.ArgumentParser(description="Bootstrap CalMiner database") - parser.add_argument( - "--ensure-database", - action="store_true", - help="Create the application database when it does not already exist.", - ) - parser.add_argument( - "--ensure-role", - action="store_true", - help="Create the application role and grant necessary privileges.", - ) - parser.add_argument( - "--ensure-schema", - action="store_true", - help="Create the configured schema if it does not exist.", - ) - parser.add_argument( - "--initialize-schema", - action="store_true", - help="Create missing tables based on SQLAlchemy models.", - ) - parser.add_argument( - "--run-migrations", - action="store_true", - help="Execute pending SQL migrations.", - ) - parser.add_argument( - "--seed-data", - action="store_true", - help="Seed baseline reference data (currencies, etc.).", - ) - parser.add_argument( - "--migrations-dir", - default=None, - help="Override the default migrations directory.", - ) - parser.add_argument("--db-driver", help="Override DATABASE_DRIVER") - parser.add_argument("--db-host", help="Override DATABASE_HOST") - parser.add_argument("--db-port", type=int, help="Override DATABASE_PORT") - parser.add_argument("--db-name", help="Override DATABASE_NAME") - parser.add_argument("--db-user", help="Override DATABASE_USER") - parser.add_argument("--db-password", help="Override DATABASE_PASSWORD") - parser.add_argument("--db-schema", help="Override DATABASE_SCHEMA") - parser.add_argument( - "--admin-url", - help="Override DATABASE_ADMIN_URL for administrative operations", - ) - parser.add_argument( - "--admin-user", help="Override DATABASE_SUPERUSER for admin ops" - ) - parser.add_argument( - "--admin-password", - help="Override DATABASE_SUPERUSER_PASSWORD for admin ops", - ) - parser.add_argument( - "--admin-db", - help="Override DATABASE_SUPERUSER_DB for admin ops", - ) - parser.add_argument( - "--dry-run", - action="store_true", - help="Log actions without applying changes.", - ) - parser.add_argument( - "--verbose", - "-v", - action="count", - default=0, - help="Increase logging verbosity", - ) - return parser.parse_args() - - -def main() -> None: - args = parse_args() - level = logging.WARNING - (10 * min(args.verbose, 2)) - logging.basicConfig( - level=max(level, logging.INFO), format="%(levelname)s %(message)s" - ) - - override_args: dict[str, Optional[str]] = { - "DATABASE_DRIVER": args.db_driver, - "DATABASE_HOST": args.db_host, - "DATABASE_NAME": args.db_name, - "DATABASE_USER": args.db_user, - "DATABASE_PASSWORD": args.db_password, - "DATABASE_SCHEMA": args.db_schema, - "DATABASE_ADMIN_URL": args.admin_url, - "DATABASE_SUPERUSER": args.admin_user, - "DATABASE_SUPERUSER_PASSWORD": args.admin_password, - "DATABASE_SUPERUSER_DB": args.admin_db, - } - if args.db_port is not None: - override_args["DATABASE_PORT"] = str(args.db_port) - - config = DatabaseConfig.from_env(overrides=override_args) - setup = DatabaseSetup(config, dry_run=args.dry_run) - - admin_tasks_requested = ( - args.ensure_database or args.ensure_role or args.ensure_schema - ) - if admin_tasks_requested: - setup.validate_admin_connection() - - app_validated = False - - def ensure_application_connection_for(operation: str) -> bool: - nonlocal app_validated - if app_validated: - return True - if setup.dry_run and not setup.application_role_exists(): - logger.info( - "Dry run: skipping %s because application role '%s' does not exist yet.", - operation, - setup.config.user, - ) - return False - setup.validate_application_connection() - app_validated = True - return True - - should_run_migrations = args.run_migrations - auto_run_migrations_reason: Optional[str] = None - if args.seed_data and not should_run_migrations: - should_run_migrations = True - auto_run_migrations_reason = "Seed data requested without explicit --run-migrations; applying migrations first." - - try: - if args.ensure_database: - setup.ensure_database() - if args.ensure_role: - setup.ensure_role() - if args.ensure_schema: - setup.ensure_schema() - - if args.initialize_schema: - if ensure_application_connection_for( - "SQLAlchemy schema initialization" - ): - setup.initialize_schema() - if should_run_migrations: - if ensure_application_connection_for("migration execution"): - if auto_run_migrations_reason: - logger.info(auto_run_migrations_reason) - migrations_path = ( - Path(args.migrations_dir) if args.migrations_dir else None - ) - setup.run_migrations(migrations_path) - if args.seed_data: - if ensure_application_connection_for("baseline data seeding"): - setup.seed_baseline_data(dry_run=args.dry_run) - except Exception: - if not setup.dry_run: - setup.execute_rollbacks() - raise - finally: - if not setup.dry_run: - setup.clear_rollbacks() - - -if __name__ == "__main__": - main() diff --git a/services/reporting.py b/services/reporting.py deleted file mode 100644 index 98387d6..0000000 --- a/services/reporting.py +++ /dev/null @@ -1,79 +0,0 @@ -from statistics import mean, median, pstdev -from typing import Any, Dict, Iterable, List, Mapping, Union, cast - - -def _extract_results(simulation_results: Iterable[object]) -> List[float]: - values: List[float] = [] - for item in simulation_results: - if not isinstance(item, Mapping): - continue - mapping_item = cast(Mapping[str, Any], item) - value = mapping_item.get("result") - if isinstance(value, (int, float)): - values.append(float(value)) - return values - - -def _percentile(values: List[float], percentile: float) -> float: - if not values: - return 0.0 - sorted_values = sorted(values) - if len(sorted_values) == 1: - return sorted_values[0] - index = (percentile / 100) * (len(sorted_values) - 1) - lower = int(index) - upper = min(lower + 1, len(sorted_values) - 1) - weight = index - lower - return sorted_values[lower] * (1 - weight) + sorted_values[upper] * weight - - -def generate_report( - simulation_results: List[Dict[str, float]], -) -> Dict[str, Union[float, int]]: - """Aggregate basic statistics for simulation outputs.""" - - values = _extract_results(simulation_results) - - if not values: - return { - "count": 0, - "mean": 0.0, - "median": 0.0, - "min": 0.0, - "max": 0.0, - "std_dev": 0.0, - "variance": 0.0, - "percentile_10": 0.0, - "percentile_90": 0.0, - "percentile_5": 0.0, - "percentile_95": 0.0, - "value_at_risk_95": 0.0, - "expected_shortfall_95": 0.0, - } - - summary: Dict[str, Union[float, int]] = { - "count": len(values), - "mean": mean(values), - "median": median(values), - "min": min(values), - "max": max(values), - "percentile_10": _percentile(values, 10), - "percentile_90": _percentile(values, 90), - "percentile_5": _percentile(values, 5), - "percentile_95": _percentile(values, 95), - } - - std_dev = pstdev(values) if len(values) > 1 else 0.0 - summary["std_dev"] = std_dev - summary["variance"] = std_dev**2 - - var_95 = summary["percentile_5"] - summary["value_at_risk_95"] = var_95 - - tail_values = [value for value in values if value <= var_95] - if tail_values: - summary["expected_shortfall_95"] = mean(tail_values) - else: - summary["expected_shortfall_95"] = var_95 - - return summary diff --git a/services/security.py b/services/security.py deleted file mode 100644 index 24782c5..0000000 --- a/services/security.py +++ /dev/null @@ -1,59 +0,0 @@ -from datetime import datetime, timedelta -from typing import Any, Union - -from fastapi import HTTPException, status, Depends -from fastapi.security import OAuth2PasswordBearer -from jose import jwt, JWTError -from passlib.context import CryptContext -from sqlalchemy.orm import Session - -from config.database import get_db - - -ACCESS_TOKEN_EXPIRE_MINUTES = 30 -SECRET_KEY = "your-secret-key" # Change this in production -ALGORITHM = "HS256" - -pwd_context = CryptContext(schemes=["pbkdf2_sha256"], deprecated="auto") - -oauth2_scheme = OAuth2PasswordBearer(tokenUrl="users/login") - - -def create_access_token( - subject: Union[str, Any], expires_delta: Union[timedelta, None] = None -) -> str: - if expires_delta: - expire = datetime.utcnow() + expires_delta - else: - expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) - to_encode = {"exp": expire, "sub": str(subject)} - encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) - return encoded_jwt - - -def verify_password(plain_password: str, hashed_password: str) -> bool: - return pwd_context.verify(plain_password, hashed_password) - - -def get_password_hash(password: str) -> str: - return pwd_context.hash(password) - - -async def get_current_user(token: str = Depends(oauth2_scheme), db: Session = Depends(get_db)): - from models.user import User - credentials_exception = HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, - detail="Could not validate credentials", - headers={"WWW-Authenticate": "Bearer"}, - ) - try: - payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) - username = payload.get("sub") - if username is None: - raise credentials_exception - except JWTError: - raise credentials_exception - user = db.query(User).filter(User.username == username).first() - if user is None: - raise credentials_exception - return user diff --git a/services/settings.py b/services/settings.py deleted file mode 100644 index 51b49ac..0000000 --- a/services/settings.py +++ /dev/null @@ -1,230 +0,0 @@ -from __future__ import annotations - -import os -import re -from typing import Dict, Mapping - -from sqlalchemy.orm import Session - -from models.application_setting import ApplicationSetting -from models.theme_setting import ThemeSetting # Import ThemeSetting model - -CSS_COLOR_CATEGORY = "theme" -CSS_COLOR_VALUE_TYPE = "color" -CSS_ENV_PREFIX = "CALMINER_THEME_" - -CSS_COLOR_DEFAULTS: Dict[str, str] = { - "--color-background": "#f4f5f7", - "--color-surface": "#ffffff", - "--color-text-primary": "#2a1f33", - "--color-text-secondary": "#624769", - "--color-text-muted": "#64748b", - "--color-text-subtle": "#94a3b8", - "--color-text-invert": "#ffffff", - "--color-text-dark": "#0f172a", - "--color-text-strong": "#111827", - "--color-primary": "#5f320d", - "--color-primary-strong": "#7e4c13", - "--color-primary-stronger": "#837c15", - "--color-accent": "#bff838", - "--color-border": "#e2e8f0", - "--color-border-strong": "#cbd5e1", - "--color-highlight": "#eef2ff", - "--color-panel-shadow": "rgba(15, 23, 42, 0.08)", - "--color-panel-shadow-deep": "rgba(15, 23, 42, 0.12)", - "--color-surface-alt": "#f8fafc", - "--color-success": "#047857", - "--color-error": "#b91c1c", -} - -_COLOR_VALUE_PATTERN = re.compile( - r"^(#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})|rgba?\([^)]+\)|hsla?\([^)]+\))$", - re.IGNORECASE, -) - - -def ensure_css_color_settings(db: Session) -> Dict[str, ApplicationSetting]: - """Ensure the CSS color defaults exist in the settings table.""" - - existing = ( - db.query(ApplicationSetting) - .filter(ApplicationSetting.key.in_(CSS_COLOR_DEFAULTS.keys())) - .all() - ) - by_key = {setting.key: setting for setting in existing} - - created = False - for key, default_value in CSS_COLOR_DEFAULTS.items(): - if key in by_key: - continue - setting = ApplicationSetting( - key=key, - value=default_value, - value_type=CSS_COLOR_VALUE_TYPE, - category=CSS_COLOR_CATEGORY, - description=f"CSS variable {key}", - is_editable=True, - ) - db.add(setting) - by_key[key] = setting - created = True - - if created: - db.commit() - for key, setting in by_key.items(): - db.refresh(setting) - - return by_key - - -def get_css_color_settings(db: Session) -> Dict[str, str]: - """Return CSS color variables, filling missing values with defaults.""" - - settings = ensure_css_color_settings(db) - values: Dict[str, str] = { - key: settings[key].value if key in settings else default - for key, default in CSS_COLOR_DEFAULTS.items() - } - - env_overrides = read_css_color_env_overrides(os.environ) - if env_overrides: - values.update(env_overrides) - - return values - - -def update_css_color_settings( - db: Session, updates: Mapping[str, str] -) -> Dict[str, str]: - """Persist provided CSS color overrides and return the final values.""" - - if not updates: - return get_css_color_settings(db) - - invalid_keys = sorted(set(updates.keys()) - set(CSS_COLOR_DEFAULTS.keys())) - if invalid_keys: - invalid_list = ", ".join(invalid_keys) - raise ValueError(f"Unsupported CSS variables: {invalid_list}") - - normalized: Dict[str, str] = {} - for key, value in updates.items(): - normalized[key] = _normalize_color_value(value) - - settings = ensure_css_color_settings(db) - changed = False - - for key, value in normalized.items(): - setting = settings[key] - if setting.value != value: - setting.value = value - changed = True - if setting.value_type != CSS_COLOR_VALUE_TYPE: - setting.value_type = CSS_COLOR_VALUE_TYPE - changed = True - if setting.category != CSS_COLOR_CATEGORY: - setting.category = CSS_COLOR_CATEGORY - changed = True - if not setting.is_editable: - setting.is_editable = True - changed = True - - if changed: - db.commit() - for key in normalized.keys(): - db.refresh(settings[key]) - - return get_css_color_settings(db) - - -def read_css_color_env_overrides( - env: Mapping[str, str] | None = None, -) -> Dict[str, str]: - """Return validated CSS overrides sourced from environment variables.""" - - if env is None: - env = os.environ - - overrides: Dict[str, str] = {} - for css_key in CSS_COLOR_DEFAULTS.keys(): - env_name = css_key_to_env_var(css_key) - raw_value = env.get(env_name) - if raw_value is None: - continue - overrides[css_key] = _normalize_color_value(raw_value) - - return overrides - - -def _normalize_color_value(value: str) -> str: - if not isinstance(value, str): - raise ValueError("Color value must be a string") - trimmed = value.strip() - if not trimmed: - raise ValueError("Color value cannot be empty") - if not _COLOR_VALUE_PATTERN.match(trimmed): - raise ValueError( - "Color value must be a hex code or an rgb/rgba/hsl/hsla expression" - ) - _validate_functional_color(trimmed) - return trimmed - - -def _validate_functional_color(value: str) -> None: - lowered = value.lower() - if lowered.startswith("rgb(") or lowered.startswith("hsl("): - _ensure_component_count(value, expected=3) - elif lowered.startswith("rgba(") or lowered.startswith("hsla("): - _ensure_component_count(value, expected=4) - - -def _ensure_component_count(value: str, expected: int) -> None: - if not value.endswith(")"): - raise ValueError( - "Color function expressions must end with a closing parenthesis" - ) - inner = value[value.index("(") + 1: -1] - parts = [segment.strip() for segment in inner.split(",")] - if len(parts) != expected: - raise ValueError( - "Color function expressions must provide the expected number of components" - ) - if any(not component for component in parts): - raise ValueError("Color function components cannot be empty") - - -def css_key_to_env_var(css_key: str) -> str: - sanitized = css_key.lstrip("-").replace("-", "_").upper() - return f"{CSS_ENV_PREFIX}{sanitized}" - - -def list_css_env_override_rows( - env: Mapping[str, str] | None = None, -) -> list[Dict[str, str]]: - overrides = read_css_color_env_overrides(env) - rows: list[Dict[str, str]] = [] - for css_key, value in overrides.items(): - rows.append( - { - "css_key": css_key, - "env_var": css_key_to_env_var(css_key), - "value": value, - } - ) - return rows - - -def save_theme_settings(db: Session, theme_data: dict): - theme = db.query(ThemeSetting).first() or ThemeSetting() - for key, value in theme_data.items(): - setattr(theme, key, value) - db.add(theme) - db.commit() - db.refresh(theme) - return theme - - -def get_theme_settings(db: Session): - theme = db.query(ThemeSetting).first() - if theme: - return {c.name: getattr(theme, c.name) for c in theme.__table__.columns} - return {} diff --git a/services/simulation.py b/services/simulation.py deleted file mode 100644 index 6c8ffe1..0000000 --- a/services/simulation.py +++ /dev/null @@ -1,144 +0,0 @@ -from __future__ import annotations - -from dataclasses import dataclass -from random import Random -from typing import Dict, List, Literal, Optional, Sequence - - -DEFAULT_STD_DEV_RATIO = 0.1 -DEFAULT_UNIFORM_SPAN_RATIO = 0.15 -DistributionType = Literal["normal", "uniform", "triangular"] - - -@dataclass -class SimulationParameter: - name: str - base_value: float - distribution: DistributionType - std_dev: Optional[float] = None - minimum: Optional[float] = None - maximum: Optional[float] = None - mode: Optional[float] = None - - -def _ensure_positive_span(span: float, fallback: float) -> float: - return span if span and span > 0 else fallback - - -def _compile_parameters( - parameters: Sequence[Dict[str, float]], -) -> List[SimulationParameter]: - compiled: List[SimulationParameter] = [] - for index, item in enumerate(parameters): - if "value" not in item: - raise ValueError(f"Parameter at index {index} must include 'value'") - name = str(item.get("name", f"param_{index}")) - base_value = float(item["value"]) - distribution = str(item.get("distribution", "normal")).lower() - if distribution not in {"normal", "uniform", "triangular"}: - raise ValueError( - f"Parameter '{name}' has unsupported distribution '{distribution}'" - ) - - span_default = abs(base_value) * DEFAULT_UNIFORM_SPAN_RATIO or 1.0 - - if distribution == "normal": - std_dev = item.get("std_dev") - std_dev_value = ( - float(std_dev) - if std_dev is not None - else abs(base_value) * DEFAULT_STD_DEV_RATIO or 1.0 - ) - compiled.append( - SimulationParameter( - name=name, - base_value=base_value, - distribution="normal", - std_dev=_ensure_positive_span(std_dev_value, 1.0), - ) - ) - continue - - minimum = item.get("min") - maximum = item.get("max") - if minimum is None or maximum is None: - minimum = base_value - span_default - maximum = base_value + span_default - minimum = float(minimum) - maximum = float(maximum) - if minimum >= maximum: - raise ValueError( - f"Parameter '{name}' requires 'min' < 'max' for {distribution} distribution" - ) - - if distribution == "uniform": - compiled.append( - SimulationParameter( - name=name, - base_value=base_value, - distribution="uniform", - minimum=minimum, - maximum=maximum, - ) - ) - else: # triangular - mode = item.get("mode") - if mode is None: - mode = base_value - mode_value = float(mode) - if not (minimum <= mode_value <= maximum): - raise ValueError( - f"Parameter '{name}' mode must be within min/max bounds for triangular distribution" - ) - compiled.append( - SimulationParameter( - name=name, - base_value=base_value, - distribution="triangular", - minimum=minimum, - maximum=maximum, - mode=mode_value, - ) - ) - return compiled - - -def _sample_parameter(rng: Random, param: SimulationParameter) -> float: - if param.distribution == "normal": - assert param.std_dev is not None - return rng.normalvariate(param.base_value, param.std_dev) - if param.distribution == "uniform": - assert param.minimum is not None and param.maximum is not None - return rng.uniform(param.minimum, param.maximum) - # triangular - assert ( - param.minimum is not None - and param.maximum is not None - and param.mode is not None - ) - return rng.triangular(param.minimum, param.maximum, param.mode) - - -def run_simulation( - parameters: Sequence[Dict[str, float]], - iterations: int = 1000, - seed: Optional[int] = None, -) -> List[Dict[str, float]]: - """Run a lightweight Monte Carlo simulation using configurable distributions.""" - - if iterations <= 0: - return [] - - compiled_params = _compile_parameters(parameters) - if not compiled_params: - return [] - - rng = Random(seed) - results: List[Dict[str, float]] = [] - for iteration in range(1, iterations + 1): - total = 0.0 - for param in compiled_params: - sample = _sample_parameter(rng, param) - total += sample - results.append({"iteration": iteration, "result": total}) - return results diff --git a/templates/Dashboard.html b/templates/Dashboard.html deleted file mode 100644 index ef2dfcb..0000000 --- a/templates/Dashboard.html +++ /dev/null @@ -1,94 +0,0 @@ -{% extends "base.html" %} {% block title %}Dashboard · CalMiner{% endblock %} {% -block content %} -
-
-

Operations Overview

-

- Unified insight across scenarios, costs, production, maintenance, and - simulations. -

-
-
- -
-
- - - -
-
- {% for metric in summary_metrics %} -
- {{ metric.label }} - {{ metric.value }} -
- {% endfor %} -
-

- 0 %} hidden{% endif %}> Add project inputs to populate summary metrics. -

-
- -
-
-
-
-

Scenario Cost Mix

-

CAPEX vs OPEX totals per scenario

-
-
- -

- Add CAPEX or OPEX entries to display this chart. -

-
-
-
-
-

Production vs Consumption

-

Throughput comparison by scenario

-
-
- -
-
-{% endblock %} {% block scripts %} {{ super() }} - - -{% endblock %} diff --git a/templates/ParameterInput.html b/templates/ParameterInput.html deleted file mode 100644 index 10f30b1..0000000 --- a/templates/ParameterInput.html +++ /dev/null @@ -1,51 +0,0 @@ -{% extends "base.html" %} {% block title %}Process Parameters · CalMiner{% -endblock %} {% block content %} -
-

Scenario Parameters

- {% if scenarios %} -
- - - - -
- -
- - - - - - - - - - -
ParameterValueDistributionDetails
-
- {% else %} -

- No scenarios available. Create a scenario before - adding parameters. -

- {% endif %} -
-{% endblock %} {% block scripts %} {{ super() }} - - -{% endblock %} diff --git a/templates/ScenarioForm.html b/templates/ScenarioForm.html deleted file mode 100644 index fc5e6ab..0000000 --- a/templates/ScenarioForm.html +++ /dev/null @@ -1,53 +0,0 @@ -{% extends "base.html" %} {% block title %}Scenario Management · CalMiner{% -endblock %} {% block content %} -
-

Create a New Scenario

-
- - - -
- -
- {% if scenarios %} - - - - - - - - - {% for scenario in scenarios %} - - - - - {% endfor %} - -
NameDescription
{{ scenario.name }}{{ scenario.description or "—" }}
- {% else %} -

- No scenarios yet. Create one to get started. -

- - - - - - - - - - {% endif %} -
-
-{% endblock %} {% block scripts %} {{ super() }} - -{% endblock %} diff --git a/templates/consumption.html b/templates/consumption.html deleted file mode 100644 index 63c1198..0000000 --- a/templates/consumption.html +++ /dev/null @@ -1,76 +0,0 @@ -{% extends "base.html" %} {% from "partials/components.html" import -select_field, feedback, empty_state, table_container with context %} {% block -title %}Consumption · CalMiner{% endblock %} {% block content %} -
-

Consumption Tracking

-
- {{ select_field( "Scenario filter", "consumption-scenario-filter", - options=scenarios, placeholder="Select a scenario" ) }} -
- {{ empty_state( "consumption-empty", "Choose a scenario to review its - consumption records." ) }} {% call table_container( - "consumption-table-wrapper", hidden=True, aria_label="Scenario consumption - records" ) %} - - - Amount - Description - - - - {% endcall %} -
- -
-

Add Consumption Record

- {% if scenarios %} -
- {{ select_field( "Scenario", "consumption-form-scenario", - name="scenario_id", options=scenarios, required=True, placeholder="Select a - scenario", placeholder_disabled=True ) }} - - - - - -
- {{ feedback("consumption-feedback") }} {% else %} -

- Create a scenario before adding consumption records. -

- {% endif %} -
- -{% endblock %} {% block scripts %} {{ super() }} - - -{% endblock %} diff --git a/templates/costs.html b/templates/costs.html deleted file mode 100644 index 417de97..0000000 --- a/templates/costs.html +++ /dev/null @@ -1,129 +0,0 @@ -{% extends "base.html" %} {% from "partials/components.html" import -select_field, feedback, empty_state, table_container with context %} {% block -title %}Costs · CalMiner{% endblock %} {% block content %} -
-

Cost Overview

- {% if scenarios %} -
- {{ select_field( "Scenario filter", "costs-scenario-filter", - options=scenarios, placeholder="Select a scenario" ) }} -
- {% else %} {{ empty_state( "costs-scenario-empty", "Create a scenario to - review cost information." ) }} {% endif %} {{ empty_state( "costs-empty", - "Choose a scenario to review CAPEX and OPEX details." ) }} - - -
- -
-

Add CAPEX Entry

- {% if scenarios %} -
- {{ select_field( "Scenario", "capex-form-scenario", name="scenario_id", - options=scenarios, required=True, placeholder="Select a scenario", - placeholder_disabled=True ) }} {{ select_field( "Currency", - "capex-form-currency", name="currency_code", options=currency_options, - required=True, placeholder="Select currency", placeholder_disabled=True, - value_attr="id", label_attr="name" ) }} - - - -
- {{ feedback("capex-feedback") }} {% else %} {{ empty_state( - "capex-form-empty", "Create a scenario before adding CAPEX entries." ) }} {% - endif %} -
- -
-

Add OPEX Entry

- {% if scenarios %} -
- {{ select_field( "Scenario", "opex-form-scenario", name="scenario_id", - options=scenarios, required=True, placeholder="Select a scenario", - placeholder_disabled=True ) }} {{ select_field( "Currency", - "opex-form-currency", name="currency_code", options=currency_options, - required=True, placeholder="Select currency", placeholder_disabled=True, - value_attr="id", label_attr="name" ) }} - - - -
- {{ feedback("opex-feedback") }} {% else %} {{ empty_state( "opex-form-empty", - "Create a scenario before adding OPEX entries." ) }} {% endif %} -
- -{% endblock %} {% block scripts %} {{ super() }} - - -{% endblock %} diff --git a/templates/currencies.html b/templates/currencies.html deleted file mode 100644 index 6c99515..0000000 --- a/templates/currencies.html +++ /dev/null @@ -1,131 +0,0 @@ -{% extends "base.html" %} -{% from "partials/components.html" import select_field, feedback, empty_state, table_container with context %} - -{% block title %}Currencies · CalMiner{% endblock %} - -{% block content %} -
-
-
-

Currency Overview

-

- Current availability of currencies for project inputs. -

-
-
- - {% if currency_stats %} -
-
- Total Currencies - {{ currency_stats.total }} -
-
- Active - {{ currency_stats.active }} -
-
- Inactive - {{ currency_stats.inactive }} -
-
- {% else %} {{ empty_state("currencies-overview-empty", "No currency data - available yet.") }} {% endif %} {% call table_container( - "currencies-table-container", aria_label="Configured currencies", - heading="Configured Currencies" ) %} - - - Code - Name - Symbol - Status - Actions - - - - {% endcall %} {{ empty_state( "currencies-table-empty", "No currencies - configured yet.", hidden=currencies|length > 0 ) }} -
- -
-
-
-

Manage Currencies

-

- Create new currencies or update existing configurations inline. -

-
-
- - {% set status_options = [ {"id": "true", "name": "Active"}, {"id": "false", - "name": "Inactive"} ] %} - -
- {{ select_field( "Currency to update (leave blank for new)", - "currency-form-existing", name="existing_code", options=currencies, - placeholder="Create a new currency", value_attr="code", label_attr="name" ) - }} - - - - - - - - {{ select_field( "Status", "currency-form-status", name="is_active", - options=status_options, include_blank=False ) }} - -
- - -
-
- {{ feedback("currency-form-feedback") }} -
-{% endblock %} {% block scripts %} {{ super() }} - - -{% endblock %} diff --git a/templates/equipment.html b/templates/equipment.html deleted file mode 100644 index ed02537..0000000 --- a/templates/equipment.html +++ /dev/null @@ -1,78 +0,0 @@ -{% extends "base.html" %} {% block title %}Equipment · CalMiner{% endblock %} {% -block content %} -
-

Equipment Inventory

- {% if scenarios %} -
- -
- {% else %} -

- Create a scenario to view equipment inventory. -

- {% endif %} -
- Choose a scenario to review the equipment list. -
- -
- -
-

Add Equipment

- {% if scenarios %} -
- - - - -
- - {% else %} -

- Create a scenario before managing equipment. -

- {% endif %} -
- -{% endblock %} {% block scripts %} {{ super() }} - - -{% endblock %} diff --git a/templates/maintenance.html b/templates/maintenance.html deleted file mode 100644 index 51b0449..0000000 --- a/templates/maintenance.html +++ /dev/null @@ -1,111 +0,0 @@ -{% extends "base.html" %} {% block title %}Maintenance · CalMiner{% endblock %} -{% block content %} -
-

Maintenance Schedule

- {% if scenarios %} -
- -
- {% else %} -

- Create a scenario to view maintenance entries. -

- {% endif %} -
- Choose a scenario to review upcoming or completed maintenance. -
- -
- -
-

Add Maintenance Entry

- {% if scenarios %} -
- - - - - - - -
- - {% else %} -

- Create a scenario before managing maintenance - entries. -

- {% endif %} -
- -{% endblock %} {% block scripts %} {{ super() }} - - -{% endblock %} diff --git a/templates/production.html b/templates/production.html deleted file mode 100644 index 1de5b25..0000000 --- a/templates/production.html +++ /dev/null @@ -1,97 +0,0 @@ -{% extends "base.html" %} {% block title %}Production · CalMiner{% endblock %} -{% block content %} -
-

Production Output

- {% if scenarios %} -
- -
- {% else %} -

- Create a scenario to view production output data. -

- {% endif %} -
- Choose a scenario to review its production output. -
- -
- -
-

Add Production Output

- {% if scenarios %} -
- - - - - - -
- - {% else %} -

- Create a scenario before adding production output. -

- {% endif %} -
- -{% endblock %} {% block scripts %} {{ super() }} - - -{% endblock %} diff --git a/templates/reporting.html b/templates/reporting.html deleted file mode 100644 index aa9a635..0000000 --- a/templates/reporting.html +++ /dev/null @@ -1,41 +0,0 @@ -{% extends "base.html" %} {% block title %}Reporting · CalMiner{% endblock %} {% -block content %} -
-

Scenario KPI Summary

-
- -
- - - - - -
- -{% endblock %} {% block scripts %} {{ super() }} - - -{% endblock %} diff --git a/templates/settings.html b/templates/settings.html deleted file mode 100644 index 1fcbc21..0000000 --- a/templates/settings.html +++ /dev/null @@ -1,26 +0,0 @@ -{% extends "base.html" %} {% block title %}Settings · CalMiner{% endblock %} {% -block content %} - -
-
-

Currency Management

-

- Manage available currencies, symbols, and default selections from the - Currency Management page. -

- Go to Currency Management -
- -
-{% endblock %} diff --git a/templates/simulations.html b/templates/simulations.html deleted file mode 100644 index 4e96743..0000000 --- a/templates/simulations.html +++ /dev/null @@ -1,41 +0,0 @@ -{% extends "base.html" %} {% block title %}Simulations · CalMiner{% endblock %} -{% block content %} -
-

Monte Carlo Simulations

- {% if simulation_scenarios %} -
- -
- {% else %} -

Create a scenario before running simulations.

- {% endif %} - -
-

Scenario Run History

- - - - - - - - - - - {% endblock %} {% block scripts %} {{ super() }} - - - {% endblock %} diff --git a/templates/theme_settings.html b/templates/theme_settings.html deleted file mode 100644 index 72cecf4..0000000 --- a/templates/theme_settings.html +++ /dev/null @@ -1,125 +0,0 @@ -{% extends "base.html" %} {% block title %}Theme Settings · CalMiner{% endblock -%} {% block content %} - - -
-
-
-

Theme Colors

-

- Update global CSS variables to customize CalMiner's appearance. -

-
-
-
- {% for key, value in css_variables.items() %} {% set env_meta = - css_env_override_meta.get(key) %} - - {% endfor %} - -
- - -
- - {% from "partials/components.html" import feedback with context %} {{ - feedback("theme-settings-feedback") }} -
- -
-
-
-

Environment Overrides

-

- The following CSS variables are controlled via environment variables and - take precedence over database values. -

-
-
- {% if css_env_override_rows %} -
-
ScenarioIterationsMean Result
- - - - - - - - - {% for row in css_env_override_rows %} - - - - - - {% endfor %} - -
CSS VariableEnvironment VariableValue
{{ row.css_key }}{{ row.env_var }}{{ row.value }}
-
- {% else %} -

No environment overrides configured.

- {% endif %} -
-{% endblock %} {% block scripts %} {{ super() }} - - -{% endblock %} diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/e2e/conftest.py b/tests/e2e/conftest.py deleted file mode 100644 index 6ced399..0000000 --- a/tests/e2e/conftest.py +++ /dev/null @@ -1,170 +0,0 @@ -import os -import subprocess -import time -from typing import Dict, Generator - -import pytest - -# type: ignore[import] -from playwright.sync_api import Browser, Page, Playwright, sync_playwright - -import httpx -from sqlalchemy.engine import make_url - -# Use a different port for the test server to avoid conflicts -TEST_PORT = 8001 -BASE_URL = f"http://localhost:{TEST_PORT}" - - -@pytest.fixture(scope="session", autouse=True) -def live_server() -> Generator[str, None, None]: - """Launch a live test server in a separate process.""" - env = _prepare_database_environment(os.environ.copy()) - - process = subprocess.Popen( - [ - "uvicorn", - "main:app", - "--host", - "127.0.0.1", - f"--port={TEST_PORT}", - ], - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL, - env=env, - ) - - deadline = time.perf_counter() + 30 - last_error: Exception | None = None - while time.perf_counter() < deadline: - if process.poll() is not None: - raise RuntimeError("uvicorn server exited before becoming ready") - try: - response = httpx.get(BASE_URL, timeout=1.0, trust_env=False) - if response.status_code < 500: - break - except Exception as exc: # noqa: BLE001 - last_error = exc - time.sleep(0.5) - else: - process.terminate() - process.wait(timeout=5) - raise TimeoutError( - "Timed out waiting for uvicorn test server to start" - ) from last_error - - try: - yield BASE_URL - finally: - if process.poll() is None: - process.terminate() - try: - process.wait(timeout=5) - except subprocess.TimeoutExpired: - process.kill() - process.wait(timeout=5) - - -@pytest.fixture(scope="session", autouse=True) -def seed_default_currencies(live_server: str) -> None: - """Ensure a baseline set of currencies exists for UI flows.""" - - seeds = [ - {"code": "EUR", "name": "Euro", "symbol": "EUR", "is_active": True}, - { - "code": "CLP", - "name": "Chilean Peso", - "symbol": "CLP$", - "is_active": True, - }, - ] - - with httpx.Client( - base_url=live_server, timeout=5.0, trust_env=False - ) as client: - try: - response = client.get("/api/currencies/?include_inactive=true") - response.raise_for_status() - existing_codes = { - str(item.get("code")) - for item in response.json() - if isinstance(item, dict) and item.get("code") - } - except httpx.HTTPError as exc: # noqa: BLE001 - raise RuntimeError("Failed to read existing currencies") from exc - - for payload in seeds: - if payload["code"] in existing_codes: - continue - try: - create_response = client.post("/api/currencies/", json=payload) - except httpx.HTTPError as exc: # noqa: BLE001 - raise RuntimeError("Failed to seed currencies") from exc - - if create_response.status_code == 409: - continue - create_response.raise_for_status() - - -@pytest.fixture(scope="session") -def playwright_instance() -> Generator[Playwright, None, None]: - """Provide a Playwright instance for the test session.""" - with sync_playwright() as p: - yield p - - -@pytest.fixture(scope="session") -def browser( - playwright_instance: Playwright, -) -> Generator[Browser, None, None]: - """Provide a browser instance for the test session.""" - browser = playwright_instance.chromium.launch() - yield browser - browser.close() - - -@pytest.fixture() -def page(browser: Browser, live_server: str) -> Generator[Page, None, None]: - """Provide a new page for each test.""" - page = browser.new_page(base_url=live_server) - page.goto("/") - page.wait_for_load_state("networkidle") - yield page - page.close() - - -def _prepare_database_environment(env: Dict[str, str]) -> Dict[str, str]: - """Ensure granular database env vars are available for the app under test.""" - - required = ( - "DATABASE_HOST", - "DATABASE_USER", - "DATABASE_NAME", - "DATABASE_PASSWORD", - ) - if all(env.get(key) for key in required): - return env - - legacy_url = env.get("DATABASE_URL") - if not legacy_url: - return env - - url = make_url(legacy_url) - env.setdefault("DATABASE_DRIVER", url.drivername) - if url.host: - env.setdefault("DATABASE_HOST", url.host) - if url.port: - env.setdefault("DATABASE_PORT", str(url.port)) - if url.username: - env.setdefault("DATABASE_USER", url.username) - if url.password: - env.setdefault("DATABASE_PASSWORD", url.password) - if url.database: - env.setdefault("DATABASE_NAME", url.database) - - query_options = dict(url.query) if url.query else {} - options = query_options.get("options") - if isinstance(options, str) and "search_path=" in options: - env.setdefault("DATABASE_SCHEMA", options.split("search_path=")[-1]) - - return env diff --git a/tests/e2e/test_consumption.py b/tests/e2e/test_consumption.py deleted file mode 100644 index 685db93..0000000 --- a/tests/e2e/test_consumption.py +++ /dev/null @@ -1,50 +0,0 @@ -from uuid import uuid4 - -from playwright.sync_api import Page, expect - - -def test_consumption_form_loads(page: Page): - """Verify the consumption form page loads correctly.""" - page.goto("/ui/consumption") - expect(page).to_have_title("Consumption · CalMiner") - expect( - page.locator("h2:has-text('Add Consumption Record')") - ).to_be_visible() - - -def test_create_consumption_item(page: Page): - """Test creating a new consumption item through the UI.""" - # First, create a scenario to associate the consumption with. - page.goto("/ui/scenarios") - scenario_name = f"Consumption Test Scenario {uuid4()}" - page.fill("input[name='name']", scenario_name) - page.click("button[type='submit']") - with page.expect_response("**/api/scenarios/"): - pass # Wait for the scenario to be created - - # Now, navigate to the consumption page and add an item. - page.goto("/ui/consumption") - - # Create a consumption item. - consumption_desc = "Diesel for generators" - page.select_option("#consumption-form-scenario", label=scenario_name) - page.fill("textarea[name='description']", consumption_desc) - page.fill("input[name='amount']", "5000") - page.click("button[type='submit']") - - with page.expect_response("**/api/consumption/") as response_info: - pass - assert response_info.value.status == 201 - - # Verify the new item appears in the table. - page.select_option("#consumption-scenario-filter", label=scenario_name) - expect( - page.locator("#consumption-table-body tr").filter( - has_text=consumption_desc - ) - ).to_be_visible() - - # Verify the feedback message. - expect(page.locator("#consumption-feedback")).to_have_text( - "Consumption record saved." - ) diff --git a/tests/e2e/test_costs.py b/tests/e2e/test_costs.py deleted file mode 100644 index c49439a..0000000 --- a/tests/e2e/test_costs.py +++ /dev/null @@ -1,63 +0,0 @@ -from uuid import uuid4 - -from playwright.sync_api import Page, expect - - -def test_costs_form_loads(page: Page): - """Verify the costs form page loads correctly.""" - page.goto("/ui/costs") - expect(page).to_have_title("Costs · CalMiner") - expect(page.locator("h2:has-text('Add CAPEX Entry')")).to_be_visible() - - -def test_create_capex_and_opex_items(page: Page): - """Test creating new CAPEX and OPEX items through the UI.""" - # First, create a scenario to associate the costs with. - page.goto("/ui/scenarios") - scenario_name = f"Cost Test Scenario {uuid4()}" - page.fill("input[name='name']", scenario_name) - page.click("button[type='submit']") - with page.expect_response("**/api/scenarios/"): - pass # Wait for the scenario to be created - - # Now, navigate to the costs page and add CAPEX and OPEX items. - page.goto("/ui/costs") - - # Create a CAPEX item. - capex_desc = "Initial drilling equipment" - page.select_option("#capex-form-scenario", label=scenario_name) - page.fill("#capex-form-description", capex_desc) - page.fill("#capex-form-amount", "150000") - page.click("#capex-form button[type='submit']") - - with page.expect_response("**/api/costs/capex") as response_info: - pass - assert response_info.value.status == 200 - - # Create an OPEX item. - opex_desc = "Monthly fuel costs" - page.select_option("#opex-form-scenario", label=scenario_name) - page.fill("#opex-form-description", opex_desc) - page.fill("#opex-form-amount", "25000") - page.click("#opex-form button[type='submit']") - - with page.expect_response("**/api/costs/opex") as response_info: - pass - assert response_info.value.status == 200 - - # Verify the new items appear in their respective tables. - page.select_option("#costs-scenario-filter", label=scenario_name) - expect( - page.locator("#capex-table-body tr").filter(has_text=capex_desc) - ).to_be_visible() - expect( - page.locator("#opex-table-body tr").filter(has_text=opex_desc) - ).to_be_visible() - - # Verify the feedback messages. - expect(page.locator("#capex-feedback")).to_have_text( - "Entry saved successfully." - ) - expect(page.locator("#opex-feedback")).to_have_text( - "Entry saved successfully." - ) diff --git a/tests/e2e/test_currencies.py b/tests/e2e/test_currencies.py deleted file mode 100644 index 4b7f8d0..0000000 --- a/tests/e2e/test_currencies.py +++ /dev/null @@ -1,135 +0,0 @@ -import random -import string - -from playwright.sync_api import Page, expect - - -def _unique_currency_code(existing: set[str]) -> str: - """Generate a unique three-letter code not present in *existing*.""" - alphabet = string.ascii_uppercase - for _ in range(100): - candidate = "".join(random.choices(alphabet, k=3)) - if candidate not in existing and candidate != "USD": - return candidate - raise AssertionError( - "Unable to generate a unique currency code for the test run." - ) - - -def _metric_value(page: Page, element_id: str) -> int: - locator = page.locator(f"#{element_id}") - expect(locator).to_be_visible() - return int(locator.inner_text().strip()) - - -def _expect_feedback(page: Page, expected_text: str) -> None: - page.wait_for_function( - "expected => {" - " const el = document.getElementById('currency-form-feedback');" - " if (!el) return false;" - " const text = (el.textContent || '').trim();" - " return !el.classList.contains('hidden') && text === expected;" - "}", - arg=expected_text, - ) - feedback = page.locator("#currency-form-feedback") - expect(feedback).to_have_text(expected_text) - - -def test_currency_workflow_create_update_toggle(page: Page) -> None: - """Exercise create, update, and toggle flows on the currency settings page.""" - page.goto("/ui/currencies") - expect(page).to_have_title("Currencies · CalMiner") - expect(page.locator("h2:has-text('Currency Overview')")).to_be_visible() - - code_cells = page.locator("#currencies-table-body tr td:nth-child(1)") - existing_codes = { - text.strip().upper() for text in code_cells.all_inner_texts() - } - - total_before = _metric_value(page, "currency-metric-total") - active_before = _metric_value(page, "currency-metric-active") - inactive_before = _metric_value(page, "currency-metric-inactive") - - new_code = _unique_currency_code(existing_codes) - new_name = f"Test Currency {new_code}" - new_symbol = new_code[0] - - page.fill("#currency-form-code", new_code) - page.fill("#currency-form-name", new_name) - page.fill("#currency-form-symbol", new_symbol) - page.select_option("#currency-form-status", "true") - - with page.expect_response("**/api/currencies/") as create_info: - page.click("button[type='submit']") - create_response = create_info.value - assert create_response.status == 201 - - _expect_feedback(page, "Currency created successfully.") - - page.wait_for_function( - "expected => Number(document.getElementById('currency-metric-total').textContent.trim()) === expected", - arg=total_before + 1, - ) - page.wait_for_function( - "expected => Number(document.getElementById('currency-metric-active').textContent.trim()) === expected", - arg=active_before + 1, - ) - - row = page.locator("#currencies-table-body tr").filter(has_text=new_code) - expect(row).to_be_visible() - expect(row.locator("td").nth(3)).to_have_text("Active") - - # Switch to update mode using the existing currency option. - page.select_option("#currency-form-existing", new_code) - updated_name = f"{new_name} Updated" - updated_symbol = f"{new_symbol}$" - page.fill("#currency-form-name", updated_name) - page.fill("#currency-form-symbol", updated_symbol) - page.select_option("#currency-form-status", "false") - - with page.expect_response(f"**/api/currencies/{new_code}") as update_info: - page.click("button[type='submit']") - update_response = update_info.value - assert update_response.status == 200 - - _expect_feedback(page, "Currency updated successfully.") - - page.wait_for_function( - "expected => Number(document.getElementById('currency-metric-active').textContent.trim()) === expected", - arg=active_before, - ) - page.wait_for_function( - "expected => Number(document.getElementById('currency-metric-inactive').textContent.trim()) === expected", - arg=inactive_before + 1, - ) - - expect(row.locator("td").nth(1)).to_have_text(updated_name) - expect(row.locator("td").nth(2)).to_have_text(updated_symbol) - expect(row.locator("td").nth(3)).to_contain_text("Inactive") - - toggle_button = row.locator("button[data-action='toggle']") - expect(toggle_button).to_have_text("Activate") - - with page.expect_response( - f"**/api/currencies/{new_code}/activation" - ) as toggle_info: - toggle_button.click() - toggle_response = toggle_info.value - assert toggle_response.status == 200 - - page.wait_for_function( - "expected => Number(document.getElementById('currency-metric-active').textContent.trim()) === expected", - arg=active_before + 1, - ) - page.wait_for_function( - "expected => Number(document.getElementById('currency-metric-inactive').textContent.trim()) === expected", - arg=inactive_before, - ) - - _expect_feedback(page, f"Currency {new_code} activated.") - - expect(row.locator("td").nth(3)).to_contain_text("Active") - expect(row.locator("button[data-action='toggle']")).to_have_text( - "Deactivate" - ) diff --git a/tests/e2e/test_dashboard.py b/tests/e2e/test_dashboard.py deleted file mode 100644 index 198b04d..0000000 --- a/tests/e2e/test_dashboard.py +++ /dev/null @@ -1,17 +0,0 @@ -from playwright.sync_api import Page, expect - - -def test_dashboard_loads_and_has_title(page: Page): - """Verify the dashboard page loads and the title is correct.""" - expect(page).to_have_title("Dashboard · CalMiner") - - -def test_dashboard_shows_summary_metrics_panel(page: Page): - """Check that the summary metrics panel is visible.""" - expect(page.locator("h2:has-text('Operations Overview')")).to_be_visible() - - -def test_dashboard_renders_cost_chart(page: Page): - """Ensure the scenario cost chart canvas is present.""" - expect(page.locator("#cost-chart")).to_be_attached() - expect(page.locator("#cost-chart-empty")).to_be_visible() diff --git a/tests/e2e/test_equipment.py b/tests/e2e/test_equipment.py deleted file mode 100644 index f507a6e..0000000 --- a/tests/e2e/test_equipment.py +++ /dev/null @@ -1,45 +0,0 @@ -from uuid import uuid4 - -from playwright.sync_api import Page, expect - - -def test_equipment_form_loads(page: Page): - """Verify the equipment form page loads correctly.""" - page.goto("/ui/equipment") - expect(page).to_have_title("Equipment · CalMiner") - expect(page.locator("h2:has-text('Add Equipment')")).to_be_visible() - - -def test_create_equipment_item(page: Page): - """Test creating a new equipment item through the UI.""" - # First, create a scenario to associate the equipment with. - page.goto("/ui/scenarios") - scenario_name = f"Equipment Test Scenario {uuid4()}" - page.fill("input[name='name']", scenario_name) - page.click("button[type='submit']") - with page.expect_response("**/api/scenarios/"): - pass # Wait for the scenario to be created - - # Now, navigate to the equipment page and add an item. - page.goto("/ui/equipment") - - # Create an equipment item. - equipment_name = "Haul Truck HT-05" - equipment_desc = "Primary haul truck for ore transport." - page.select_option("#equipment-form-scenario", label=scenario_name) - page.fill("#equipment-form-name", equipment_name) - page.fill("#equipment-form-description", equipment_desc) - page.click("button[type='submit']") - - with page.expect_response("**/api/equipment/") as response_info: - pass - assert response_info.value.status == 200 - - # Verify the new item appears in the table. - page.select_option("#equipment-scenario-filter", label=scenario_name) - expect( - page.locator("#equipment-table-body tr").filter(has_text=equipment_name) - ).to_be_visible() - - # Verify the feedback message. - expect(page.locator("#equipment-feedback")).to_have_text("Equipment saved.") diff --git a/tests/e2e/test_maintenance.py b/tests/e2e/test_maintenance.py deleted file mode 100644 index fb9a403..0000000 --- a/tests/e2e/test_maintenance.py +++ /dev/null @@ -1,58 +0,0 @@ -from uuid import uuid4 - -from playwright.sync_api import Page, expect - - -def test_maintenance_form_loads(page: Page): - """Verify the maintenance form page loads correctly.""" - page.goto("/ui/maintenance") - expect(page).to_have_title("Maintenance · CalMiner") - expect(page.locator("h2:has-text('Add Maintenance Entry')")).to_be_visible() - - -def test_create_maintenance_item(page: Page): - """Test creating a new maintenance item through the UI.""" - # First, create a scenario and an equipment item. - page.goto("/ui/scenarios") - scenario_name = f"Maintenance Test Scenario {uuid4()}" - page.fill("input[name='name']", scenario_name) - page.click("button[type='submit']") - with page.expect_response("**/api/scenarios/"): - pass - - page.goto("/ui/equipment") - equipment_name = f"Excavator EX-12 {uuid4()}" - page.select_option("#equipment-form-scenario", label=scenario_name) - page.fill("#equipment-form-name", equipment_name) - page.click("button[type='submit']") - with page.expect_response("**/api/equipment/"): - pass - - # Now, navigate to the maintenance page and add an item. - page.goto("/ui/maintenance") - - # Create a maintenance item. - maintenance_desc = "Scheduled engine overhaul" - page.select_option("#maintenance-form-scenario", label=scenario_name) - page.select_option("#maintenance-form-equipment", label=equipment_name) - page.fill("#maintenance-form-date", "2025-12-01") - page.fill("#maintenance-form-description", maintenance_desc) - page.fill("#maintenance-form-cost", "12000") - page.click("button[type='submit']") - - with page.expect_response("**/api/maintenance/") as response_info: - pass - assert response_info.value.status == 201 - - # Verify the new item appears in the table. - page.select_option("#maintenance-scenario-filter", label=scenario_name) - expect( - page.locator("#maintenance-table-body tr").filter( - has_text=maintenance_desc - ) - ).to_be_visible() - - # Verify the feedback message. - expect(page.locator("#maintenance-feedback")).to_have_text( - "Maintenance entry saved." - ) diff --git a/tests/e2e/test_production.py b/tests/e2e/test_production.py deleted file mode 100644 index 72a63ba..0000000 --- a/tests/e2e/test_production.py +++ /dev/null @@ -1,48 +0,0 @@ -from uuid import uuid4 - -from playwright.sync_api import Page, expect - - -def test_production_form_loads(page: Page): - """Verify the production form page loads correctly.""" - page.goto("/ui/production") - expect(page).to_have_title("Production · CalMiner") - expect(page.locator("h2:has-text('Add Production Output')")).to_be_visible() - - -def test_create_production_item(page: Page): - """Test creating a new production item through the UI.""" - # First, create a scenario to associate the production with. - page.goto("/ui/scenarios") - scenario_name = f"Production Test Scenario {uuid4()}" - page.fill("input[name='name']", scenario_name) - page.click("button[type='submit']") - with page.expect_response("**/api/scenarios/"): - pass # Wait for the scenario to be created - - # Now, navigate to the production page and add an item. - page.goto("/ui/production") - - # Create a production item. - production_desc = "Ore extracted - Grade A" - page.select_option("#production-form-scenario", label=scenario_name) - page.fill("#production-form-description", production_desc) - page.fill("#production-form-amount", "1500") - page.click("button[type='submit']") - - with page.expect_response("**/api/production/") as response_info: - pass - assert response_info.value.status == 201 - - # Verify the new item appears in the table. - page.select_option("#production-scenario-filter", label=scenario_name) - expect( - page.locator("#production-table-body tr").filter( - has_text=production_desc - ) - ).to_be_visible() - - # Verify the feedback message. - expect(page.locator("#production-feedback")).to_have_text( - "Production output saved." - ) diff --git a/tests/e2e/test_reporting.py b/tests/e2e/test_reporting.py deleted file mode 100644 index 04cee12..0000000 --- a/tests/e2e/test_reporting.py +++ /dev/null @@ -1,9 +0,0 @@ -from playwright.sync_api import Page, expect - - -def test_reporting_view_loads(page: Page): - """Verify the reporting view page loads correctly.""" - page.get_by_role("link", name="Reporting").click() - expect(page).to_have_url("http://localhost:8001/ui/reporting") - expect(page).to_have_title("Reporting · CalMiner") - expect(page.locator("h2:has-text('Scenario KPI Summary')")).to_be_visible() diff --git a/tests/e2e/test_scenarios.py b/tests/e2e/test_scenarios.py deleted file mode 100644 index 04f37ea..0000000 --- a/tests/e2e/test_scenarios.py +++ /dev/null @@ -1,43 +0,0 @@ -from uuid import uuid4 - -from playwright.sync_api import Page, expect - - -def test_scenario_form_loads(page: Page): - """Verify the scenario form page loads correctly.""" - page.goto("/ui/scenarios") - expect(page).to_have_url( - "http://localhost:8001/ui/scenarios" - ) # Updated port - expect(page.locator("h2:has-text('Create a New Scenario')")).to_be_visible() - - -def test_create_new_scenario(page: Page): - """Test creating a new scenario via the UI form.""" - page.goto("/ui/scenarios") - - scenario_name = f"E2E Test Scenario {uuid4()}" - scenario_desc = "A scenario created during an end-to-end test." - - page.fill("input[name='name']", scenario_name) - page.fill("input[name='description']", scenario_desc) - - # Expect a network response from the POST request after clicking the submit button. - with page.expect_response("**/api/scenarios/") as response_info: - page.click("button[type='submit']") - - response = response_info.value - assert response.status == 200 - - # After a successful submission, the new scenario should be visible in the table. - # The table is dynamically updated, so we might need to wait for it to appear. - new_row = page.locator(f"tr:has-text('{scenario_name}')") - expect(new_row).to_be_visible() - expect(new_row.locator("td").nth(1)).to_have_text(scenario_desc) - - # Verify the feedback message. - feedback = page.locator("#feedback") - expect(feedback).to_be_visible() - expect(feedback).to_have_text( - f'Scenario "{scenario_name}" created successfully.' - ) diff --git a/tests/e2e/test_smoke.py b/tests/e2e/test_smoke.py deleted file mode 100644 index a9f0b23..0000000 --- a/tests/e2e/test_smoke.py +++ /dev/null @@ -1,85 +0,0 @@ -import pytest -from playwright.sync_api import Page, expect - -# A list of UI routes to check, with their URL, expected title, and a key heading text. -UI_ROUTES = [ - ("/", "Dashboard · CalMiner", "Operations Overview"), - ("/ui/dashboard", "Dashboard · CalMiner", "Operations Overview"), - ( - "/ui/scenarios", - "Scenario Management · CalMiner", - "Create a New Scenario", - ), - ("/ui/parameters", "Process Parameters · CalMiner", "Scenario Parameters"), - ("/ui/settings", "Settings · CalMiner", "Settings"), - ("/ui/costs", "Costs · CalMiner", "Cost Overview"), - ("/ui/consumption", "Consumption · CalMiner", "Consumption Tracking"), - ("/ui/production", "Production · CalMiner", "Production Output"), - ("/ui/equipment", "Equipment · CalMiner", "Equipment Inventory"), - ("/ui/maintenance", "Maintenance · CalMiner", "Maintenance Schedule"), - ("/ui/simulations", "Simulations · CalMiner", "Monte Carlo Simulations"), - ("/ui/reporting", "Reporting · CalMiner", "Scenario KPI Summary"), - ("/ui/currencies", "Currencies · CalMiner", "Currency Overview"), -] - - -@pytest.mark.parametrize("url, title, heading", UI_ROUTES) -def test_ui_pages_load_correctly( - page: Page, url: str, title: str, heading: str -): - """Verify that all UI pages load with the correct title and a visible heading.""" - page.goto(url) - expect(page).to_have_title(title) - # The app uses a mix of h1 and h2 for main page headings. - heading_locator = page.locator( - f"h1:has-text('{heading}'), h2:has-text('{heading}')" - ) - expect(heading_locator.first).to_be_visible() - - -def test_settings_theme_form_interaction(page: Page): - page.goto("/theme-settings") - expect(page).to_have_title("Theme Settings · CalMiner") - - env_rows = page.locator("#theme-env-overrides tbody tr") - disabled_inputs = page.locator( - "#theme-settings-form input.color-value-input[disabled]" - ) - env_row_count = env_rows.count() - disabled_count = disabled_inputs.count() - assert disabled_count == env_row_count - - color_input = page.locator( - "#theme-settings-form input[name='--color-primary']" - ) - expect(color_input).to_be_visible() - expect(color_input).to_be_enabled() - - original_value = color_input.input_value() - candidate_values = ("#114455", "#225566") - new_value = ( - candidate_values[0] - if original_value != candidate_values[0] - else candidate_values[1] - ) - - color_input.fill(new_value) - page.click("#theme-settings-form button[type='submit']") - - feedback = page.locator("#theme-settings-feedback") - expect(feedback).to_contain_text("updated successfully") - - computed_color = page.evaluate( - "() => getComputedStyle(document.documentElement).getPropertyValue('--color-primary').trim()" - ) - assert computed_color.lower() == new_value.lower() - - page.reload() - expect(color_input).to_have_value(new_value) - - color_input.fill(original_value) - page.click("#theme-settings-form button[type='submit']") - expect(feedback).to_contain_text("updated successfully") - - page.reload() - expect(color_input).to_have_value(original_value) diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py deleted file mode 100644 index 00d8401..0000000 --- a/tests/unit/conftest.py +++ /dev/null @@ -1,266 +0,0 @@ -from datetime import date -from typing import Any, Dict, Generator -from uuid import uuid4 - -import pytest -from fastapi.testclient import TestClient -from sqlalchemy import create_engine -from sqlalchemy.orm import Session, sessionmaker -from sqlalchemy.pool import StaticPool - -from config.database import Base -from main import app -from models.capex import Capex -from models.consumption import Consumption -from models.equipment import Equipment -from models.maintenance import Maintenance -from models.opex import Opex -from models.parameters import Parameter -from models.production_output import ProductionOutput -from models.scenario import Scenario -from models.simulation_result import SimulationResult - -SQLALCHEMY_TEST_URL = "sqlite:///:memory:" -engine = create_engine( - SQLALCHEMY_TEST_URL, - connect_args={"check_same_thread": False}, - poolclass=StaticPool, -) -TestingSessionLocal = sessionmaker( - autocommit=False, autoflush=False, bind=engine -) - - -@pytest.fixture(scope="session", autouse=True) -def setup_database() -> Generator[None, None, None]: - # Ensure all model metadata is registered before creating tables - from models import ( - application_setting, - capex, - consumption, - currency, - distribution, - equipment, - maintenance, - opex, - parameters, - production_output, - role, - scenario, - simulation_result, - theme_setting, - user, - ) # noqa: F401 - imported for side effects - - _ = ( - capex, - consumption, - currency, - distribution, - equipment, - maintenance, - application_setting, - opex, - parameters, - production_output, - role, - scenario, - simulation_result, - theme_setting, - user, - ) - - Base.metadata.create_all(bind=engine) - yield - Base.metadata.drop_all(bind=engine) - - -@pytest.fixture() -def db_session() -> Generator[Session, None, None]: - Base.metadata.drop_all(bind=engine) - Base.metadata.create_all(bind=engine) - session = TestingSessionLocal() - try: - yield session - finally: - session.rollback() - session.close() - - -@pytest.fixture() -def api_client(db_session: Session) -> Generator[TestClient, None, None]: - def override_get_db(): - try: - yield db_session - finally: - pass - - from routes.dependencies import get_db - - app.dependency_overrides[get_db] = override_get_db - - with TestClient(app) as client: - yield client - - app.dependency_overrides.pop(get_db, None) - - -@pytest.fixture() -def seeded_ui_data( - db_session: Session, -) -> Generator[Dict[str, Any], None, None]: - """Populate a scenario with representative related records for UI tests.""" - scenario_name = f"Scenario Alpha {uuid4()}" - scenario = Scenario(name=scenario_name, description="Seeded UI scenario") - db_session.add(scenario) - db_session.flush() - - parameter = Parameter( - scenario_id=scenario.id, - name="Ore Grade", - value=1.5, - distribution_type="normal", - distribution_parameters={"mean": 1.5, "std_dev": 0.1}, - ) - capex = Capex( - scenario_id=scenario.id, - amount=1_000_000.0, - description="Drill purchase", - currency_code="USD", - ) - opex = Opex( - scenario_id=scenario.id, - amount=250_000.0, - description="Fuel spend", - currency_code="USD", - ) - consumption = Consumption( - scenario_id=scenario.id, - amount=1_200.0, - description="Diesel (L)", - unit_name="Liters", - unit_symbol="L", - ) - production = ProductionOutput( - scenario_id=scenario.id, - amount=800.0, - description="Ore (tonnes)", - unit_name="Tonnes", - unit_symbol="t", - ) - equipment = Equipment( - scenario_id=scenario.id, - name="Excavator 42", - description="Primary loader", - ) - db_session.add_all( - [parameter, capex, opex, consumption, production, equipment] - ) - db_session.flush() - - maintenance = Maintenance( - scenario_id=scenario.id, - equipment_id=equipment.id, - maintenance_date=date(2025, 1, 15), - description="Hydraulic service", - cost=15_000.0, - ) - simulation_results = [ - SimulationResult( - scenario_id=scenario.id, - iteration=index, - result=value, - ) - for index, value in enumerate( - (950_000.0, 975_000.0, 990_000.0), start=1 - ) - ] - - db_session.add(maintenance) - db_session.add_all(simulation_results) - db_session.commit() - - try: - yield { - "scenario": scenario, - "equipment": equipment, - "simulation_results": simulation_results, - } - finally: - db_session.query(SimulationResult).filter_by( - scenario_id=scenario.id - ).delete() - db_session.query(Maintenance).filter_by( - scenario_id=scenario.id - ).delete() - db_session.query(Equipment).filter_by(id=equipment.id).delete() - db_session.query(ProductionOutput).filter_by( - scenario_id=scenario.id - ).delete() - db_session.query(Consumption).filter_by( - scenario_id=scenario.id - ).delete() - db_session.query(Opex).filter_by(scenario_id=scenario.id).delete() - db_session.query(Capex).filter_by(scenario_id=scenario.id).delete() - db_session.query(Parameter).filter_by(scenario_id=scenario.id).delete() - db_session.query(Scenario).filter_by(id=scenario.id).delete() - db_session.commit() - - -@pytest.fixture() -def invalid_request_payloads( - db_session: Session, -) -> Generator[Dict[str, Any], None, None]: - """Provide reusable invalid request bodies for exercising validation branches.""" - duplicate_name = f"Scenario Duplicate {uuid4()}" - existing = Scenario( - name=duplicate_name, - description="Existing scenario for duplicate checks", - ) - db_session.add(existing) - db_session.commit() - - payloads: Dict[str, Any] = { - "existing_scenario": existing, - "scenario_duplicate": { - "name": duplicate_name, - "description": "Second scenario should fail with duplicate name", - }, - "parameter_missing_scenario": { - "scenario_id": existing.id + 99, - "name": "Invalid Parameter", - "value": 1.0, - }, - "parameter_invalid_distribution": { - "scenario_id": existing.id, - "name": "Weird Dist", - "value": 2.5, - "distribution_type": "invalid", - }, - "simulation_unknown_scenario": { - "scenario_id": existing.id + 99, - "iterations": 10, - "parameters": [ - {"name": "grade", "value": 1.2, "distribution": "normal"} - ], - }, - "simulation_missing_parameters": { - "scenario_id": existing.id, - "iterations": 5, - "parameters": [], - }, - "reporting_non_list_payload": {"result": 10.0}, - "reporting_missing_result": [{"value": 12.0}], - "maintenance_negative_cost": { - "equipment_id": 1, - "scenario_id": existing.id, - "maintenance_date": "2025-01-15", - "cost": -500.0, - }, - } - - try: - yield payloads - finally: - db_session.query(Scenario).filter_by(id=existing.id).delete() - db_session.commit() diff --git a/tests/unit/test_auth.py b/tests/unit/test_auth.py deleted file mode 100644 index f4c0251..0000000 --- a/tests/unit/test_auth.py +++ /dev/null @@ -1,231 +0,0 @@ -from services.security import get_password_hash, verify_password - - -def test_password_hashing(): - password = "testpassword" - hashed_password = get_password_hash(password) - assert verify_password(password, hashed_password) - assert not verify_password("wrongpassword", hashed_password) - - -def test_register_user(api_client): - response = api_client.post( - "/users/register", - json={ - "username": "testuser", - "email": "test@example.com", - "password": "testpassword", - }, - ) - assert response.status_code == 201 - data = response.json() - assert data["username"] == "testuser" - assert data["email"] == "test@example.com" - assert "id" in data - assert "role_id" in data - - response = api_client.post( - "/users/register", - json={ - "username": "testuser", - "email": "another@example.com", - "password": "testpassword", - }, - ) - assert response.status_code == 400 - assert response.json() == {"detail": "Username already registered"} - - response = api_client.post( - "/users/register", - json={ - "username": "anotheruser", - "email": "test@example.com", - "password": "testpassword", - }, - ) - assert response.status_code == 400 - assert response.json() == {"detail": "Email already registered"} - - -def test_login_user(api_client): - # Register a user first - api_client.post( - "/users/register", - json={ - "username": "loginuser", - "email": "login@example.com", - "password": "loginpassword", - }, - ) - - response = api_client.post( - "/users/login", - json={"username": "loginuser", "password": "loginpassword"}, - ) - assert response.status_code == 200 - data = response.json() - assert "access_token" in data - assert data["token_type"] == "bearer" - - response = api_client.post( - "/users/login", - json={"username": "loginuser", "password": "wrongpassword"}, - ) - assert response.status_code == 401 - assert response.json() == {"detail": "Incorrect username or password"} - - response = api_client.post( - "/users/login", - json={"username": "nonexistent", "password": "password"}, - ) - assert response.status_code == 401 - assert response.json() == {"detail": "Incorrect username or password"} - - -def test_read_users_me(api_client): - # Register a user first - api_client.post( - "/users/register", - json={ - "username": "profileuser", - "email": "profile@example.com", - "password": "profilepassword", - }, - ) - # Login to get a token - login_response = api_client.post( - "/users/login", - json={"username": "profileuser", "password": "profilepassword"}, - ) - token = login_response.json()["access_token"] - - response = api_client.get( - "/users/me", headers={"Authorization": f"Bearer {token}"} - ) - assert response.status_code == 200 - data = response.json() - assert data["username"] == "profileuser" - assert data["email"] == "profile@example.com" - - -def test_update_users_me(api_client): - # Register a user first - api_client.post( - "/users/register", - json={ - "username": "updateuser", - "email": "update@example.com", - "password": "updatepassword", - }, - ) - # Login to get a token - login_response = api_client.post( - "/users/login", - json={"username": "updateuser", "password": "updatepassword"}, - ) - token = login_response.json()["access_token"] - - response = api_client.put( - "/users/me", - headers={"Authorization": f"Bearer {token}"}, - json={ - "username": "updateduser", - "email": "updated@example.com", - "password": "newpassword", - }, - ) - assert response.status_code == 200 - data = response.json() - assert data["username"] == "updateduser" - assert data["email"] == "updated@example.com" - - # Verify password change - response = api_client.post( - "/users/login", - json={"username": "updateduser", "password": "newpassword"}, - ) - assert response.status_code == 200 - token = response.json()["access_token"] - - # Test username already taken - api_client.post( - "/users/register", - json={ - "username": "anotherupdateuser", - "email": "anotherupdate@example.com", - "password": "password", - }, - ) - response = api_client.put( - "/users/me", - headers={"Authorization": f"Bearer {token}"}, - json={ - "username": "anotherupdateuser", - }, - ) - assert response.status_code == 400 - assert response.json() == {"detail": "Username already taken"} - - # Test email already registered - api_client.post( - "/users/register", - json={ - "username": "yetanotheruser", - "email": "yetanother@example.com", - "password": "password", - }, - ) - response = api_client.put( - "/users/me", - headers={"Authorization": f"Bearer {token}"}, - json={ - "email": "yetanother@example.com", - }, - ) - assert response.status_code == 400 - assert response.json() == {"detail": "Email already registered"} - - -def test_forgot_password(api_client): - response = api_client.post( - "/users/forgot-password", json={"email": "nonexistent@example.com"} - ) - assert response.status_code == 200 - assert response.json() == { - "message": "Password reset email sent (not really)"} - - -def test_reset_password(api_client): - # Register a user first - api_client.post( - "/users/register", - json={ - "username": "resetuser", - "email": "reset@example.com", - "password": "oldpassword", - }, - ) - - response = api_client.post( - "/users/reset-password", - json={ - "token": "resetuser", # Use username as token for test - "new_password": "newpassword", - }, - ) - assert response.status_code == 200 - assert response.json() == { - "message": "Password has been reset successfully"} - - # Verify password change - response = api_client.post( - "/users/login", - json={"username": "resetuser", "password": "newpassword"}, - ) - assert response.status_code == 200 - - response = api_client.post( - "/users/login", - json={"username": "resetuser", "password": "oldpassword"}, - ) - assert response.status_code == 401 diff --git a/tests/unit/test_consumption.py b/tests/unit/test_consumption.py deleted file mode 100644 index 9ea7bb3..0000000 --- a/tests/unit/test_consumption.py +++ /dev/null @@ -1,77 +0,0 @@ -from uuid import uuid4 - -import pytest -from fastapi.testclient import TestClient - - -@pytest.fixture -def client(api_client: TestClient) -> TestClient: - return api_client - - -def _create_scenario(client: TestClient) -> int: - payload = { - "name": f"Consumption Scenario {uuid4()}", - "description": "Scenario for consumption tests", - } - response = client.post("/api/scenarios/", json=payload) - assert response.status_code == 200 - return response.json()["id"] - - -def test_create_consumption(client: TestClient) -> None: - scenario_id = _create_scenario(client) - payload = { - "scenario_id": scenario_id, - "amount": 125.5, - "description": "Fuel usage baseline", - "unit_name": "Liters", - "unit_symbol": "L", - } - - response = client.post("/api/consumption/", json=payload) - assert response.status_code == 201 - body = response.json() - assert body["id"] > 0 - assert body["scenario_id"] == scenario_id - assert body["amount"] == pytest.approx(125.5) - assert body["description"] == "Fuel usage baseline" - assert body["unit_symbol"] == "L" - - -def test_list_consumption_returns_created_items(client: TestClient) -> None: - scenario_id = _create_scenario(client) - values = [50.0, 80.75] - for amount in values: - response = client.post( - "/api/consumption/", - json={ - "scenario_id": scenario_id, - "amount": amount, - "description": f"Consumption {amount}", - "unit_name": "Tonnes", - "unit_symbol": "t", - }, - ) - assert response.status_code == 201 - - list_response = client.get("/api/consumption/") - assert list_response.status_code == 200 - items = [ - item - for item in list_response.json() - if item["scenario_id"] == scenario_id - ] - assert {item["amount"] for item in items} == set(values) - - -def test_create_consumption_rejects_negative_amount(client: TestClient) -> None: - scenario_id = _create_scenario(client) - payload = { - "scenario_id": scenario_id, - "amount": -10, - "description": "Invalid negative amount", - } - - response = client.post("/api/consumption/", json=payload) - assert response.status_code == 422 diff --git a/tests/unit/test_costs.py b/tests/unit/test_costs.py deleted file mode 100644 index ae4059c..0000000 --- a/tests/unit/test_costs.py +++ /dev/null @@ -1,123 +0,0 @@ -from uuid import uuid4 - -from fastapi.testclient import TestClient - -from config.database import Base, engine -from main import app - - -def setup_module(module): - Base.metadata.drop_all(bind=engine) - Base.metadata.create_all(bind=engine) - - -def teardown_module(module): - Base.metadata.drop_all(bind=engine) - - -client = TestClient(app) - - -def _create_scenario() -> int: - payload = { - "name": f"CostScenario-{uuid4()}", - "description": "Cost tracking test scenario", - } - response = client.post("/api/scenarios/", json=payload) - assert response.status_code == 200 - return response.json()["id"] - - -def test_create_and_list_capex_and_opex(): - sid = _create_scenario() - - capex_payload = { - "scenario_id": sid, - "amount": 1000.0, - "description": "Initial capex", - "currency_code": "USD", - } - resp2 = client.post("/api/costs/capex", json=capex_payload) - assert resp2.status_code == 200 - capex = resp2.json() - assert capex["scenario_id"] == sid - assert capex["amount"] == 1000.0 - assert capex["currency_code"] == "USD" - - resp3 = client.get("/api/costs/capex") - assert resp3.status_code == 200 - data = resp3.json() - assert any( - item["amount"] == 1000.0 and item["scenario_id"] == sid for item in data - ) - - opex_payload = { - "scenario_id": sid, - "amount": 500.0, - "description": "Recurring opex", - "currency_code": "USD", - } - resp4 = client.post("/api/costs/opex", json=opex_payload) - assert resp4.status_code == 200 - opex = resp4.json() - assert opex["scenario_id"] == sid - assert opex["amount"] == 500.0 - assert opex["currency_code"] == "USD" - - resp5 = client.get("/api/costs/opex") - assert resp5.status_code == 200 - data_o = resp5.json() - assert any( - item["amount"] == 500.0 and item["scenario_id"] == sid - for item in data_o - ) - - -def test_multiple_capex_entries(): - sid = _create_scenario() - amounts = [250.0, 750.0] - for amount in amounts: - resp = client.post( - "/api/costs/capex", - json={ - "scenario_id": sid, - "amount": amount, - "description": f"Capex {amount}", - "currency_code": "EUR", - }, - ) - assert resp.status_code == 200 - - resp = client.get("/api/costs/capex") - assert resp.status_code == 200 - data = resp.json() - retrieved_amounts = [ - item["amount"] for item in data if item["scenario_id"] == sid - ] - for amount in amounts: - assert amount in retrieved_amounts - - -def test_multiple_opex_entries(): - sid = _create_scenario() - amounts = [120.0, 340.0] - for amount in amounts: - resp = client.post( - "/api/costs/opex", - json={ - "scenario_id": sid, - "amount": amount, - "description": f"Opex {amount}", - "currency_code": "CAD", - }, - ) - assert resp.status_code == 200 - - resp = client.get("/api/costs/opex") - assert resp.status_code == 200 - data = resp.json() - retrieved_amounts = [ - item["amount"] for item in data if item["scenario_id"] == sid - ] - for amount in amounts: - assert amount in retrieved_amounts diff --git a/tests/unit/test_currencies.py b/tests/unit/test_currencies.py deleted file mode 100644 index 044571e..0000000 --- a/tests/unit/test_currencies.py +++ /dev/null @@ -1,125 +0,0 @@ -from typing import Dict - -import pytest - -from models.currency import Currency - - -@pytest.fixture(autouse=True) -def _cleanup_currencies(db_session): - db_session.query(Currency).delete() - db_session.commit() - yield - db_session.query(Currency).delete() - db_session.commit() - - -def _assert_currency( - payload: Dict[str, object], - code: str, - name: str, - symbol: str | None, - is_active: bool, -) -> None: - assert payload["code"] == code - assert payload["name"] == name - assert payload["is_active"] is is_active - if symbol is None: - assert payload["symbol"] is None - else: - assert payload["symbol"] == symbol - - -def test_list_returns_default_currency(api_client, db_session): - response = api_client.get("/api/currencies/") - assert response.status_code == 200 - data = response.json() - assert any(item["code"] == "USD" for item in data) - - -def test_create_currency_success(api_client, db_session): - payload = {"code": "EUR", "name": "Euro", "symbol": "€", "is_active": True} - response = api_client.post("/api/currencies/", json=payload) - assert response.status_code == 201 - data = response.json() - _assert_currency(data, "EUR", "Euro", "€", True) - - stored = db_session.query(Currency).filter_by(code="EUR").one() - assert stored.name == "Euro" - assert stored.symbol == "€" - assert stored.is_active is True - - -def test_create_currency_conflict(api_client, db_session): - api_client.post( - "/api/currencies/", - json={ - "code": "CAD", - "name": "Canadian Dollar", - "symbol": "$", - "is_active": True, - }, - ) - duplicate = api_client.post( - "/api/currencies/", - json={ - "code": "CAD", - "name": "Canadian Dollar", - "symbol": "$", - "is_active": True, - }, - ) - assert duplicate.status_code == 409 - - -def test_update_currency_fields(api_client, db_session): - api_client.post( - "/api/currencies/", - json={ - "code": "GBP", - "name": "British Pound", - "symbol": "£", - "is_active": True, - }, - ) - - response = api_client.put( - "/api/currencies/GBP", - json={"name": "Pound Sterling", "symbol": "£", "is_active": False}, - ) - assert response.status_code == 200 - data = response.json() - _assert_currency(data, "GBP", "Pound Sterling", "£", False) - - -def test_toggle_currency_activation(api_client, db_session): - api_client.post( - "/api/currencies/", - json={ - "code": "AUD", - "name": "Australian Dollar", - "symbol": "A$", - "is_active": True, - }, - ) - - response = api_client.patch( - "/api/currencies/AUD/activation", - json={"is_active": False}, - ) - assert response.status_code == 200 - data = response.json() - _assert_currency(data, "AUD", "Australian Dollar", "A$", False) - - -def test_default_currency_cannot_be_deactivated(api_client, db_session): - api_client.get("/api/currencies/") - response = api_client.patch( - "/api/currencies/USD/activation", - json={"is_active": False}, - ) - assert response.status_code == 400 - assert ( - response.json()["detail"] - == "The default currency cannot be deactivated." - ) diff --git a/tests/unit/test_currency_workflow.py b/tests/unit/test_currency_workflow.py deleted file mode 100644 index f43809a..0000000 --- a/tests/unit/test_currency_workflow.py +++ /dev/null @@ -1,75 +0,0 @@ -from uuid import uuid4 - -import pytest - -from models.currency import Currency - - -@pytest.fixture -def seeded_currency(db_session): - currency = Currency(code="GBP", name="British Pound", symbol="GBP") - db_session.add(currency) - db_session.commit() - db_session.refresh(currency) - - try: - yield currency - finally: - db_session.delete(currency) - db_session.commit() - - -def _create_scenario(api_client): - payload = { - "name": f"CurrencyScenario-{uuid4()}", - "description": "Currency workflow scenario", - } - resp = api_client.post("/api/scenarios/", json=payload) - assert resp.status_code == 200 - return resp.json()["id"] - - -def test_create_capex_with_currency_code_and_list(api_client, seeded_currency): - sid = _create_scenario(api_client) - - payload = { - "scenario_id": sid, - "amount": 500.0, - "description": "Capex with GBP", - "currency_code": seeded_currency.code, - } - resp = api_client.post("/api/costs/capex", json=payload) - assert resp.status_code == 200 - data = resp.json() - assert ( - data.get("currency_code") == seeded_currency.code - or data.get("currency", {}).get("code") == seeded_currency.code - ) - - -def test_create_opex_with_currency_id(api_client, seeded_currency): - sid = _create_scenario(api_client) - - resp = api_client.get("/api/currencies/") - assert resp.status_code == 200 - currencies = resp.json() - assert any(c["id"] == seeded_currency.id for c in currencies) - - payload = { - "scenario_id": sid, - "amount": 120.0, - "description": "Opex with explicit id", - "currency_id": seeded_currency.id, - } - resp = api_client.post("/api/costs/opex", json=payload) - assert resp.status_code == 200 - data = resp.json() - assert data["currency_id"] == seeded_currency.id - - -def test_list_currencies_endpoint(api_client, seeded_currency): - resp = api_client.get("/api/currencies/") - assert resp.status_code == 200 - data = resp.json() - assert isinstance(data, list) - assert any(c["id"] == seeded_currency.id for c in data) diff --git a/tests/unit/test_distribution.py b/tests/unit/test_distribution.py deleted file mode 100644 index 1dbb98b..0000000 --- a/tests/unit/test_distribution.py +++ /dev/null @@ -1,71 +0,0 @@ -from uuid import uuid4 - -from fastapi.testclient import TestClient - -from config.database import Base, engine -from main import app - - -def setup_module(module): - Base.metadata.create_all(bind=engine) - - -def teardown_module(module): - Base.metadata.drop_all(bind=engine) - - -client = TestClient(app) - - -def test_create_and_list_distribution(): - dist_name = f"NormalDist-{uuid4()}" - payload = { - "name": dist_name, - "distribution_type": "normal", - "parameters": {"mu": 0, "sigma": 1}, - } - resp = client.post("/api/distributions/", json=payload) - assert resp.status_code == 200 - data = resp.json() - assert data["name"] == dist_name - - resp2 = client.get("/api/distributions/") - assert resp2.status_code == 200 - data2 = resp2.json() - assert any(d["name"] == dist_name for d in data2) - - -def test_duplicate_distribution_name_allowed(): - dist_name = f"DupDist-{uuid4()}" - payload = { - "name": dist_name, - "distribution_type": "uniform", - "parameters": {"min": 0, "max": 1}, - } - first = client.post("/api/distributions/", json=payload) - assert first.status_code == 200 - - duplicate = client.post("/api/distributions/", json=payload) - assert duplicate.status_code == 200 - - resp = client.get("/api/distributions/") - assert resp.status_code == 200 - matching = [item for item in resp.json() if item["name"] == dist_name] - assert len(matching) >= 2 - - -def test_list_distributions_returns_all(): - names = {f"ListDist-{uuid4()}" for _ in range(2)} - for name in names: - payload = { - "name": name, - "distribution_type": "triangular", - "parameters": {"min": 0, "max": 10, "mode": 5}, - } - resp = client.post("/api/distributions/", json=payload) - assert resp.status_code == 200 - - resp = client.get("/api/distributions/") - assert resp.status_code == 200 - found_names = {item["name"] for item in resp.json()} - assert names.issubset(found_names) diff --git a/tests/unit/test_equipment.py b/tests/unit/test_equipment.py deleted file mode 100644 index 2069b53..0000000 --- a/tests/unit/test_equipment.py +++ /dev/null @@ -1,77 +0,0 @@ -from uuid import uuid4 - -import pytest -from fastapi.testclient import TestClient - - -@pytest.fixture -def client(api_client: TestClient) -> TestClient: - return api_client - - -def _create_scenario(client: TestClient) -> int: - payload = { - "name": f"Equipment Scenario {uuid4()}", - "description": "Scenario for equipment tests", - } - response = client.post("/api/scenarios/", json=payload) - assert response.status_code == 200 - return response.json()["id"] - - -def test_create_equipment(client: TestClient) -> None: - scenario_id = _create_scenario(client) - payload = { - "scenario_id": scenario_id, - "name": "Excavator", - "description": "Heavy machinery", - } - - response = client.post("/api/equipment/", json=payload) - assert response.status_code == 200 - created = response.json() - assert created["id"] > 0 - assert created["scenario_id"] == scenario_id - assert created["name"] == "Excavator" - assert created["description"] == "Heavy machinery" - - -def test_list_equipment_filters_by_scenario(client: TestClient) -> None: - target_scenario = _create_scenario(client) - other_scenario = _create_scenario(client) - - for scenario_id, name in [ - (target_scenario, "Bulldozer"), - (target_scenario, "Loader"), - (other_scenario, "Conveyor"), - ]: - response = client.post( - "/api/equipment/", - json={ - "scenario_id": scenario_id, - "name": name, - "description": f"Equipment {name}", - }, - ) - assert response.status_code == 200 - - list_response = client.get("/api/equipment/") - assert list_response.status_code == 200 - items = [ - item - for item in list_response.json() - if item["scenario_id"] == target_scenario - ] - assert {item["name"] for item in items} == {"Bulldozer", "Loader"} - - -def test_create_equipment_requires_name(client: TestClient) -> None: - scenario_id = _create_scenario(client) - response = client.post( - "/api/equipment/", - json={ - "scenario_id": scenario_id, - "description": "Missing name", - }, - ) - assert response.status_code == 422 diff --git a/tests/unit/test_maintenance.py b/tests/unit/test_maintenance.py deleted file mode 100644 index 64e646c..0000000 --- a/tests/unit/test_maintenance.py +++ /dev/null @@ -1,125 +0,0 @@ -from uuid import uuid4 - -import pytest - -from fastapi.testclient import TestClient - - -@pytest.fixture -def client(api_client: TestClient) -> TestClient: - return api_client - - -def _create_scenario_and_equipment(client: TestClient): - scenario_payload = { - "name": f"Test Scenario {uuid4()}", - "description": "Scenario for maintenance tests", - } - scenario_response = client.post("/api/scenarios/", json=scenario_payload) - assert scenario_response.status_code == 200 - scenario_id = scenario_response.json()["id"] - - equipment_payload = { - "scenario_id": scenario_id, - "name": f"Test Equipment {uuid4()}", - "description": "Equipment linked to maintenance", - } - equipment_response = client.post("/api/equipment/", json=equipment_payload) - assert equipment_response.status_code == 200 - equipment_id = equipment_response.json()["id"] - return scenario_id, equipment_id - - -def _create_maintenance_payload( - equipment_id: int, scenario_id: int, description: str -): - return { - "equipment_id": equipment_id, - "scenario_id": scenario_id, - "maintenance_date": "2025-10-20", - "description": description, - "cost": 100.0, - } - - -def test_create_and_list_maintenance(client: TestClient): - scenario_id, equipment_id = _create_scenario_and_equipment(client) - payload = _create_maintenance_payload( - equipment_id, scenario_id, "Create maintenance" - ) - - response = client.post("/api/maintenance/", json=payload) - assert response.status_code == 201 - created = response.json() - assert created["equipment_id"] == equipment_id - assert created["scenario_id"] == scenario_id - assert created["description"] == "Create maintenance" - - list_response = client.get("/api/maintenance/") - assert list_response.status_code == 200 - items = list_response.json() - assert any(item["id"] == created["id"] for item in items) - - -def test_get_maintenance(client: TestClient): - scenario_id, equipment_id = _create_scenario_and_equipment(client) - payload = _create_maintenance_payload( - equipment_id, scenario_id, "Retrieve maintenance" - ) - create_response = client.post("/api/maintenance/", json=payload) - assert create_response.status_code == 201 - maintenance_id = create_response.json()["id"] - - response = client.get(f"/api/maintenance/{maintenance_id}") - assert response.status_code == 200 - data = response.json() - assert data["id"] == maintenance_id - assert data["equipment_id"] == equipment_id - assert data["description"] == "Retrieve maintenance" - - -def test_update_maintenance(client: TestClient): - scenario_id, equipment_id = _create_scenario_and_equipment(client) - create_response = client.post( - "/api/maintenance/", - json=_create_maintenance_payload( - equipment_id, scenario_id, "Maintenance before update" - ), - ) - assert create_response.status_code == 201 - maintenance_id = create_response.json()["id"] - - update_payload = { - "equipment_id": equipment_id, - "scenario_id": scenario_id, - "maintenance_date": "2025-11-01", - "description": "Maintenance after update", - "cost": 250.0, - } - - response = client.put( - f"/api/maintenance/{maintenance_id}", json=update_payload - ) - assert response.status_code == 200 - updated = response.json() - assert updated["maintenance_date"] == "2025-11-01" - assert updated["description"] == "Maintenance after update" - assert updated["cost"] == 250.0 - - -def test_delete_maintenance(client: TestClient): - scenario_id, equipment_id = _create_scenario_and_equipment(client) - create_response = client.post( - "/api/maintenance/", - json=_create_maintenance_payload( - equipment_id, scenario_id, "Delete maintenance" - ), - ) - assert create_response.status_code == 201 - maintenance_id = create_response.json()["id"] - - delete_response = client.delete(f"/api/maintenance/{maintenance_id}") - assert delete_response.status_code == 204 - - get_response = client.get(f"/api/maintenance/{maintenance_id}") - assert get_response.status_code == 404 diff --git a/tests/unit/test_parameters.py b/tests/unit/test_parameters.py deleted file mode 100644 index e1895e7..0000000 --- a/tests/unit/test_parameters.py +++ /dev/null @@ -1,126 +0,0 @@ -from typing import Any, Dict, List -from uuid import uuid4 - -from fastapi.testclient import TestClient - -from config.database import Base, engine -from main import app - - -def setup_module(module: object) -> None: - Base.metadata.create_all(bind=engine) - - -def teardown_module(module: object) -> None: - Base.metadata.drop_all(bind=engine) - - -def _create_scenario(name: str | None = None) -> int: - payload: Dict[str, Any] = { - "name": name or f"ParamScenario-{uuid4()}", - "description": "Parameter test scenario", - } - response = TestClient(app).post("/api/scenarios/", json=payload) - assert response.status_code == 200 - return response.json()["id"] - - -def _create_distribution() -> int: - payload: Dict[str, Any] = { - "name": f"NormalDist-{uuid4()}", - "distribution_type": "normal", - "parameters": {"mu": 10, "sigma": 2}, - } - response = TestClient(app).post("/api/distributions/", json=payload) - assert response.status_code == 200 - return response.json()["id"] - - -client = TestClient(app) - - -def test_create_and_list_parameter(): - scenario_id = _create_scenario() - distribution_id = _create_distribution() - parameter_payload: Dict[str, Any] = { - "scenario_id": scenario_id, - "name": f"param-{uuid4()}", - "value": 3.14, - "distribution_id": distribution_id, - } - - create_response = client.post("/api/parameters/", json=parameter_payload) - assert create_response.status_code == 200 - created = create_response.json() - assert created["scenario_id"] == scenario_id - assert created["name"] == parameter_payload["name"] - assert created["value"] == parameter_payload["value"] - assert created["distribution_id"] == distribution_id - assert created["distribution_type"] == "normal" - assert created["distribution_parameters"] == {"mu": 10, "sigma": 2} - - list_response = client.get("/api/parameters/") - assert list_response.status_code == 200 - params = list_response.json() - assert any(p["id"] == created["id"] for p in params) - - -def test_create_parameter_for_missing_scenario(): - payload: Dict[str, Any] = { - "scenario_id": 0, - "name": "invalid", - "value": 1.0, - } - response = client.post("/api/parameters/", json=payload) - assert response.status_code == 404 - assert response.json()["detail"] == "Scenario not found" - - -def test_multiple_parameters_listed(): - scenario_id = _create_scenario() - payloads: List[Dict[str, Any]] = [ - {"scenario_id": scenario_id, "name": f"alpha-{i}", "value": float(i)} - for i in range(2) - ] - - for payload in payloads: - resp = client.post("/api/parameters/", json=payload) - assert resp.status_code == 200 - - list_response = client.get("/api/parameters/") - assert list_response.status_code == 200 - names = {item["name"] for item in list_response.json()} - for payload in payloads: - assert payload["name"] in names - - -def test_parameter_inline_distribution_metadata(): - scenario_id = _create_scenario() - payload: Dict[str, Any] = { - "scenario_id": scenario_id, - "name": "inline-param", - "value": 7.5, - "distribution_type": "uniform", - "distribution_parameters": {"min": 5, "max": 10}, - } - - response = client.post("/api/parameters/", json=payload) - assert response.status_code == 200 - created = response.json() - assert created["distribution_id"] is None - assert created["distribution_type"] == "uniform" - assert created["distribution_parameters"] == {"min": 5, "max": 10} - - -def test_parameter_with_missing_distribution_reference(): - scenario_id = _create_scenario() - payload: Dict[str, Any] = { - "scenario_id": scenario_id, - "name": "missing-dist", - "value": 1.0, - "distribution_id": 9999, - } - - response = client.post("/api/parameters/", json=payload) - assert response.status_code == 404 - assert response.json()["detail"] == "Distribution not found" diff --git a/tests/unit/test_production.py b/tests/unit/test_production.py deleted file mode 100644 index 106721d..0000000 --- a/tests/unit/test_production.py +++ /dev/null @@ -1,82 +0,0 @@ -from uuid import uuid4 - -import pytest -from fastapi.testclient import TestClient - - -@pytest.fixture -def client(api_client: TestClient) -> TestClient: - return api_client - - -def _create_scenario(client: TestClient) -> int: - payload = { - "name": f"Production Scenario {uuid4()}", - "description": "Scenario for production tests", - } - response = client.post("/api/scenarios/", json=payload) - assert response.status_code == 200 - return response.json()["id"] - - -def test_create_production_record(client: TestClient) -> None: - scenario_id = _create_scenario(client) - payload: dict[str, any] = { - "scenario_id": scenario_id, - "amount": 475.25, - "description": "Daily output", - "unit_name": "Tonnes", - "unit_symbol": "t", - } - - response = client.post("/api/production/", json=payload) - assert response.status_code == 201 - created = response.json() - assert created["scenario_id"] == scenario_id - assert created["amount"] == pytest.approx(475.25) - assert created["description"] == "Daily output" - assert created["unit_symbol"] == "t" - - -def test_list_production_filters_by_scenario(client: TestClient) -> None: - target_scenario = _create_scenario(client) - other_scenario = _create_scenario(client) - - for scenario_id, amount in [ - (target_scenario, 100.0), - (target_scenario, 150.0), - (other_scenario, 200.0), - ]: - response = client.post( - "/api/production/", - json={ - "scenario_id": scenario_id, - "amount": amount, - "description": f"Output {amount}", - "unit_name": "Kilograms", - "unit_symbol": "kg", - }, - ) - assert response.status_code == 201 - - list_response = client.get("/api/production/") - assert list_response.status_code == 200 - items = [ - item - for item in list_response.json() - if item["scenario_id"] == target_scenario - ] - assert {item["amount"] for item in items} == {100.0, 150.0} - - -def test_create_production_rejects_negative_amount(client: TestClient) -> None: - scenario_id = _create_scenario(client) - response = client.post( - "/api/production/", - json={ - "scenario_id": scenario_id, - "amount": -5, - "description": "Invalid output", - }, - ) - assert response.status_code == 422 diff --git a/tests/unit/test_reporting.py b/tests/unit/test_reporting.py deleted file mode 100644 index 45adf38..0000000 --- a/tests/unit/test_reporting.py +++ /dev/null @@ -1,123 +0,0 @@ -import math -from typing import Any, Dict, List - -import pytest - -from fastapi.testclient import TestClient - -from services.reporting import generate_report - - -def test_generate_report_empty(): - report = generate_report([]) - assert report == { - "count": 0, - "mean": 0.0, - "median": 0.0, - "min": 0.0, - "max": 0.0, - "std_dev": 0.0, - "variance": 0.0, - "percentile_10": 0.0, - "percentile_90": 0.0, - "percentile_5": 0.0, - "percentile_95": 0.0, - "value_at_risk_95": 0.0, - "expected_shortfall_95": 0.0, - } - - -def test_generate_report_with_values(): - values: List[Dict[str, float]] = [ - {"iteration": 1, "result": 10.0}, - {"iteration": 2, "result": 20.0}, - {"iteration": 3, "result": 30.0}, - ] - report = generate_report(values) - assert report["count"] == 3 - assert math.isclose(float(report["mean"]), 20.0) - assert math.isclose(float(report["median"]), 20.0) - assert math.isclose(float(report["min"]), 10.0) - assert math.isclose(float(report["max"]), 30.0) - assert math.isclose(float(report["std_dev"]), 8.1649658, rel_tol=1e-6) - assert math.isclose(float(report["variance"]), 66.6666666, rel_tol=1e-6) - assert math.isclose(float(report["percentile_10"]), 12.0) - assert math.isclose(float(report["percentile_90"]), 28.0) - assert math.isclose(float(report["percentile_5"]), 11.0) - assert math.isclose(float(report["percentile_95"]), 29.0) - assert math.isclose(float(report["value_at_risk_95"]), 11.0) - assert math.isclose(float(report["expected_shortfall_95"]), 10.0) - - -def test_generate_report_single_value(): - report = generate_report( - [ - {"iteration": 1, "result": 42.0}, - ] - ) - assert report["count"] == 1 - assert report["std_dev"] == 0.0 - assert report["variance"] == 0.0 - assert report["percentile_10"] == 42.0 - assert report["expected_shortfall_95"] == 42.0 - - -def test_generate_report_ignores_invalid_entries(): - raw_values: List[Any] = [ - {"iteration": 1, "result": 10.0}, - "not-a-mapping", - {"iteration": 2}, - {"iteration": 3, "result": None}, - {"iteration": 4, "result": 20}, - ] - report = generate_report(raw_values) - assert report["count"] == 2 - assert math.isclose(float(report["mean"]), 15.0) - assert math.isclose(float(report["min"]), 10.0) - assert math.isclose(float(report["max"]), 20.0) - - -@pytest.fixture -def client(api_client: TestClient) -> TestClient: - return api_client - - -def test_reporting_endpoint_invalid_input(client: TestClient): - resp = client.post("/api/reporting/summary", json={}) - assert resp.status_code == 400 - assert resp.json()["detail"] == "Invalid input format" - - -def test_reporting_endpoint_success(client: TestClient): - input_data: List[Dict[str, float]] = [ - {"iteration": 1, "result": 10.0}, - {"iteration": 2, "result": 20.0}, - {"iteration": 3, "result": 30.0}, - ] - resp = client.post("/api/reporting/summary", json=input_data) - assert resp.status_code == 200 - data: Dict[str, Any] = resp.json() - assert data["count"] == 3 - assert math.isclose(float(data["mean"]), 20.0) - assert math.isclose(float(data["variance"]), 66.6666666, rel_tol=1e-6) - assert math.isclose(float(data["value_at_risk_95"]), 11.0) - assert math.isclose(float(data["expected_shortfall_95"]), 10.0) - - -validation_error_cases: List[tuple[List[Any], str]] = [ - (["not-a-dict"], "Entry at index 0 must be an object"), - ([{"iteration": 1}], "Entry at index 0 must include numeric 'result'"), - ( - [{"iteration": 1, "result": "bad"}], - "Entry at index 0 must include numeric 'result'", - ), -] - - -@pytest.mark.parametrize("payload,expected_detail", validation_error_cases) -def test_reporting_endpoint_validation_errors( - client: TestClient, payload: List[Any], expected_detail: str -): - resp = client.post("/api/reporting/summary", json=payload) - assert resp.status_code == 400 - assert resp.json()["detail"] == expected_detail diff --git a/tests/unit/test_router_validation.py b/tests/unit/test_router_validation.py deleted file mode 100644 index 4c81b73..0000000 --- a/tests/unit/test_router_validation.py +++ /dev/null @@ -1,94 +0,0 @@ -from typing import Any, Dict - -import pytest -from fastapi.testclient import TestClient - - -@pytest.mark.usefixtures("invalid_request_payloads") -def test_duplicate_scenario_returns_400( - api_client: TestClient, invalid_request_payloads: Dict[str, Any] -) -> None: - payload = invalid_request_payloads["scenario_duplicate"] - response = api_client.post("/api/scenarios/", json=payload) - assert response.status_code == 400 - body = response.json() - assert body["detail"] == "Scenario already exists" - - -@pytest.mark.usefixtures("invalid_request_payloads") -def test_parameter_create_missing_scenario_returns_404( - api_client: TestClient, invalid_request_payloads: Dict[str, Any] -) -> None: - payload = invalid_request_payloads["parameter_missing_scenario"] - response = api_client.post("/api/parameters/", json=payload) - assert response.status_code == 404 - assert response.json()["detail"] == "Scenario not found" - - -@pytest.mark.usefixtures("invalid_request_payloads") -def test_parameter_create_invalid_distribution_is_422( - api_client: TestClient, -) -> None: - response = api_client.post( - "/api/parameters/", - json={ - "scenario_id": 1, - "name": "Bad Dist", - "value": 2.0, - "distribution_type": "invalid", - }, - ) - assert response.status_code == 422 - errors = response.json()["detail"] - assert any("distribution_type" in err["loc"] for err in errors) - - -@pytest.mark.usefixtures("invalid_request_payloads") -def test_simulation_unknown_scenario_returns_404( - api_client: TestClient, invalid_request_payloads: Dict[str, Any] -) -> None: - payload = invalid_request_payloads["simulation_unknown_scenario"] - response = api_client.post("/api/simulations/run", json=payload) - assert response.status_code == 404 - assert response.json()["detail"] == "Scenario not found" - - -@pytest.mark.usefixtures("invalid_request_payloads") -def test_simulation_missing_parameters_returns_400( - api_client: TestClient, invalid_request_payloads: Dict[str, Any] -) -> None: - payload = invalid_request_payloads["simulation_missing_parameters"] - response = api_client.post("/api/simulations/run", json=payload) - assert response.status_code == 400 - assert response.json()["detail"] == "No parameters provided" - - -@pytest.mark.usefixtures("invalid_request_payloads") -def test_reporting_summary_rejects_non_list_payload( - api_client: TestClient, invalid_request_payloads: Dict[str, Any] -) -> None: - payload = invalid_request_payloads["reporting_non_list_payload"] - response = api_client.post("/api/reporting/summary", json=payload) - assert response.status_code == 400 - assert response.json()["detail"] == "Invalid input format" - - -@pytest.mark.usefixtures("invalid_request_payloads") -def test_reporting_summary_requires_result_field( - api_client: TestClient, invalid_request_payloads: Dict[str, Any] -) -> None: - payload = invalid_request_payloads["reporting_missing_result"] - response = api_client.post("/api/reporting/summary", json=payload) - assert response.status_code == 400 - assert "must include numeric 'result'" in response.json()["detail"] - - -@pytest.mark.usefixtures("invalid_request_payloads") -def test_maintenance_negative_cost_rejected_by_schema( - api_client: TestClient, invalid_request_payloads: Dict[str, Any] -) -> None: - payload = invalid_request_payloads["maintenance_negative_cost"] - response = api_client.post("/api/maintenance/", json=payload) - assert response.status_code == 422 - error_locations = [tuple(item["loc"]) for item in response.json()["detail"]] - assert ("body", "cost") in error_locations diff --git a/tests/unit/test_scenario.py b/tests/unit/test_scenario.py deleted file mode 100644 index fce4a28..0000000 --- a/tests/unit/test_scenario.py +++ /dev/null @@ -1,45 +0,0 @@ -from uuid import uuid4 - -from fastapi.testclient import TestClient - -from config.database import Base, engine -from main import app - - -def setup_module(module): - Base.metadata.create_all(bind=engine) - - -def teardown_module(module): - Base.metadata.drop_all(bind=engine) - - -client = TestClient(app) - - -def test_create_and_list_scenario(): - scenario_name = f"Scenario-{uuid4()}" - response = client.post( - "/api/scenarios/", - json={"name": scenario_name, "description": "Integration test"}, - ) - assert response.status_code == 200 - data = response.json() - assert data["name"] == scenario_name - - response2 = client.get("/api/scenarios/") - assert response2.status_code == 200 - data2 = response2.json() - assert any(s["name"] == scenario_name for s in data2) - - -def test_create_duplicate_scenario_rejected(): - scenario_name = f"Duplicate-{uuid4()}" - payload = {"name": scenario_name, "description": "Primary"} - - first_resp = client.post("/api/scenarios/", json=payload) - assert first_resp.status_code == 200 - - second_resp = client.post("/api/scenarios/", json=payload) - assert second_resp.status_code == 400 - assert second_resp.json()["detail"] == "Scenario already exists" diff --git a/tests/unit/test_seed_data.py b/tests/unit/test_seed_data.py deleted file mode 100644 index 87094b9..0000000 --- a/tests/unit/test_seed_data.py +++ /dev/null @@ -1,46 +0,0 @@ -import argparse -from unittest import mock - -import scripts.seed_data as seed_data -from scripts.seed_data import DatabaseConfig - - -def test_run_with_namespace_handles_missing_theme_flag_without_actions() -> None: - args = argparse.Namespace(currencies=False, units=False, defaults=False) - config = mock.create_autospec(DatabaseConfig) - config.application_dsn.return_value = "postgresql://example" - - with ( - mock.patch("scripts.seed_data._configure_logging") as configure_logging, - mock.patch("scripts.seed_data.psycopg2.connect") as connect_mock, - mock.patch.object(seed_data.logger, "info") as info_mock, - ): - seed_data.run_with_namespace(args, config=config) - - configure_logging.assert_called_once() - connect_mock.assert_not_called() - info_mock.assert_called_with("No seeding options provided; exiting") - - -def test_run_with_namespace_seeds_defaults_without_theme_flag() -> None: - args = argparse.Namespace( - currencies=False, units=False, defaults=True, dry_run=False) - config = mock.create_autospec(DatabaseConfig) - config.application_dsn.return_value = "postgresql://example" - - connection_mock = mock.MagicMock() - cursor_context = mock.MagicMock() - cursor_mock = mock.MagicMock() - connection_mock.__enter__.return_value = connection_mock - connection_mock.cursor.return_value = cursor_context - cursor_context.__enter__.return_value = cursor_mock - - with ( - mock.patch("scripts.seed_data._configure_logging"), - mock.patch("scripts.seed_data.psycopg2.connect", return_value=connection_mock) as connect_mock, - mock.patch("scripts.seed_data._seed_defaults") as seed_defaults, - ): - seed_data.run_with_namespace(args, config=config) - - connect_mock.assert_called_once_with(config.application_dsn()) - seed_defaults.assert_called_once_with(cursor_mock, dry_run=False) diff --git a/tests/unit/test_settings_routes.py b/tests/unit/test_settings_routes.py deleted file mode 100644 index 81a1aa9..0000000 --- a/tests/unit/test_settings_routes.py +++ /dev/null @@ -1,53 +0,0 @@ -import pytest -from fastapi.testclient import TestClient -from sqlalchemy.orm import Session - -from services import settings as settings_service - - -@pytest.mark.usefixtures("db_session") -def test_read_css_settings_reflects_env_overrides( - api_client: TestClient, monkeypatch: pytest.MonkeyPatch -) -> None: - env_var = settings_service.css_key_to_env_var("--color-background") - monkeypatch.setenv(env_var, "#123456") - - response = api_client.get("/api/settings/css") - assert response.status_code == 200 - body = response.json() - - assert body["variables"]["--color-background"] == "#123456" - assert body["env_overrides"]["--color-background"] == "#123456" - assert any( - source["env_var"] == env_var and source["value"] == "#123456" - for source in body["env_sources"] - ) - - -@pytest.mark.usefixtures("db_session") -def test_update_css_settings_persists_changes( - api_client: TestClient, db_session: Session -) -> None: - payload = {"variables": {"--color-primary": "#112233"}} - - response = api_client.put("/api/settings/css", json=payload) - assert response.status_code == 200 - body = response.json() - - assert body["variables"]["--color-primary"] == "#112233" - - persisted = settings_service.get_css_color_settings(db_session) - assert persisted["--color-primary"] == "#112233" - - -@pytest.mark.usefixtures("db_session") -def test_update_css_settings_invalid_value_returns_422( - api_client: TestClient, -) -> None: - response = api_client.put( - "/api/settings/css", - json={"variables": {"--color-primary": "not-a-color"}}, - ) - assert response.status_code == 422 - body = response.json() - assert "color" in body["detail"].lower() diff --git a/tests/unit/test_settings_service.py b/tests/unit/test_settings_service.py deleted file mode 100644 index 8066c06..0000000 --- a/tests/unit/test_settings_service.py +++ /dev/null @@ -1,149 +0,0 @@ -from types import SimpleNamespace -from typing import Dict - -import pytest - -from sqlalchemy.orm import Session - -from models.application_setting import ApplicationSetting -from services import settings as settings_service -from services.settings import CSS_COLOR_DEFAULTS - - -@pytest.fixture(name="clean_env") -def fixture_clean_env(monkeypatch: pytest.MonkeyPatch) -> Dict[str, str]: - """Provide an isolated environment mapping for tests.""" - - env: Dict[str, str] = {} - monkeypatch.setattr(settings_service, "os", SimpleNamespace(environ=env)) - return env - - -def test_css_key_to_env_var_formatting(): - assert ( - settings_service.css_key_to_env_var("--color-background") - == "CALMINER_THEME_COLOR_BACKGROUND" - ) - assert ( - settings_service.css_key_to_env_var("--color-primary-stronger") - == "CALMINER_THEME_COLOR_PRIMARY_STRONGER" - ) - - -@pytest.mark.parametrize( - "env_key,env_value", - [ - ("--color-background", "#ffffff"), - ("--color-primary", "rgb(10, 20, 30)"), - ("--color-accent", "rgba(1,2,3,0.5)"), - ("--color-text-secondary", "hsla(210, 40%, 40%, 1)"), - ], -) -def test_read_css_color_env_overrides_valid_values( - clean_env, env_key, env_value -): - env_var = settings_service.css_key_to_env_var(env_key) - clean_env[env_var] = env_value - - overrides = settings_service.read_css_color_env_overrides(clean_env) - assert overrides[env_key] == env_value - - -@pytest.mark.parametrize( - "invalid_value", - [ - "", # empty - "not-a-color", # arbitrary string - "#12", # short hex - "rgb(1,2)", # malformed rgb - ], -) -def test_read_css_color_env_overrides_invalid_values_raise( - clean_env, invalid_value -): - env_var = settings_service.css_key_to_env_var("--color-background") - clean_env[env_var] = invalid_value - - with pytest.raises(ValueError): - settings_service.read_css_color_env_overrides(clean_env) - - -def test_read_css_color_env_overrides_ignores_missing(clean_env): - overrides = settings_service.read_css_color_env_overrides(clean_env) - assert overrides == {} - - -def test_list_css_env_override_rows_returns_structured_data(clean_env): - clean_env[settings_service.css_key_to_env_var("--color-primary")] = ( - "#123456" - ) - rows = settings_service.list_css_env_override_rows(clean_env) - assert rows == [ - { - "css_key": "--color-primary", - "env_var": settings_service.css_key_to_env_var("--color-primary"), - "value": "#123456", - } - ] - - -def test_normalize_color_value_strips_and_validates(): - assert settings_service._normalize_color_value(" #abcdef ") == "#abcdef" - with pytest.raises(ValueError): - settings_service._normalize_color_value(123) # type: ignore[arg-type] - with pytest.raises(ValueError): - settings_service._normalize_color_value(" ") - with pytest.raises(ValueError): - settings_service._normalize_color_value("#12") - - -def test_ensure_css_color_settings_creates_defaults(db_session: Session): - settings_service.ensure_css_color_settings(db_session) - - stored = { - record.key: record.value - for record in db_session.query(ApplicationSetting).all() - } - assert set(stored.keys()) == set(CSS_COLOR_DEFAULTS.keys()) - assert stored == CSS_COLOR_DEFAULTS - - -def test_update_css_color_settings_persists_changes(db_session: Session): - settings_service.ensure_css_color_settings(db_session) - - updated = settings_service.update_css_color_settings( - db_session, - {"--color-background": "#000000", "--color-accent": "#abcdef"}, - ) - - assert updated["--color-background"] == "#000000" - assert updated["--color-accent"] == "#abcdef" - - stored = { - record.key: record.value - for record in db_session.query(ApplicationSetting).all() - } - assert stored["--color-background"] == "#000000" - assert stored["--color-accent"] == "#abcdef" - - -def test_get_css_color_settings_respects_env_overrides( - db_session: Session, clean_env: Dict[str, str] -): - settings_service.ensure_css_color_settings(db_session) - override_value = "#112233" - clean_env[settings_service.css_key_to_env_var("--color-background")] = ( - override_value - ) - - values = settings_service.get_css_color_settings(db_session) - - assert values["--color-background"] == override_value - - db_value = ( - db_session.query(ApplicationSetting) - .filter_by(key="--color-background") - .one() - .value - ) - assert db_value != override_value diff --git a/tests/unit/test_setup_database.py b/tests/unit/test_setup_database.py deleted file mode 100644 index efea513..0000000 --- a/tests/unit/test_setup_database.py +++ /dev/null @@ -1,547 +0,0 @@ -import argparse -from unittest import mock - -import psycopg2 -import pytest -from psycopg2 import errors as psycopg_errors - -import scripts.setup_database as setup_db_module - -from scripts import seed_data -from scripts.setup_database import DatabaseConfig, DatabaseSetup - - -@pytest.fixture() -def mock_config() -> DatabaseConfig: - return DatabaseConfig( - driver="postgresql", - host="localhost", - port=5432, - database="calminer_test", - user="calminer", - password="secret", - schema="public", - admin_user="postgres", - admin_password="secret", - ) - - -@pytest.fixture() -def setup_instance(mock_config: DatabaseConfig) -> DatabaseSetup: - return DatabaseSetup(mock_config, dry_run=True) - - -def test_seed_baseline_data_dry_run_skips_verification( - setup_instance: DatabaseSetup, -) -> None: - with ( - mock.patch("scripts.seed_data.run_with_namespace") as seed_run, - mock.patch.object(setup_instance, "_verify_seeded_data") as verify_mock, - ): - setup_instance.seed_baseline_data(dry_run=True) - - seed_run.assert_called_once() - namespace_arg = seed_run.call_args[0][0] - assert isinstance(namespace_arg, argparse.Namespace) - assert namespace_arg.dry_run is True - assert namespace_arg.currencies is True - assert namespace_arg.units is True - assert namespace_arg.theme is True - assert seed_run.call_args.kwargs["config"] is setup_instance.config - verify_mock.assert_not_called() - - -def test_seed_baseline_data_invokes_verification( - setup_instance: DatabaseSetup, -) -> None: - expected_currencies = {code for code, *_ in seed_data.CURRENCY_SEEDS} - expected_units = {code for code, *_ in seed_data.MEASUREMENT_UNIT_SEEDS} - - with ( - mock.patch("scripts.seed_data.run_with_namespace") as seed_run, - mock.patch.object(setup_instance, "_verify_seeded_data") as verify_mock, - ): - setup_instance.seed_baseline_data(dry_run=False) - - seed_run.assert_called_once() - namespace_arg = seed_run.call_args[0][0] - assert isinstance(namespace_arg, argparse.Namespace) - assert namespace_arg.dry_run is False - assert seed_run.call_args.kwargs["config"] is setup_instance.config - assert namespace_arg.theme is True - verify_mock.assert_called_once_with( - expected_currency_codes=expected_currencies, - expected_unit_codes=expected_units, - ) - - -def test_run_migrations_applies_baseline_when_missing( - mock_config: DatabaseConfig, tmp_path -) -> None: - setup_instance = DatabaseSetup(mock_config, dry_run=False) - - baseline = tmp_path / "000_base.sql" - baseline.write_text("SELECT 1;", encoding="utf-8") - other_migration = tmp_path / "20251022_add_other.sql" - other_migration.write_text("SELECT 2;", encoding="utf-8") - - migration_calls: list[str] = [] - - def capture_migration(cursor, schema_name: str, path): - migration_calls.append(path.name) - return path.name - - connection_mock = mock.MagicMock() - connection_mock.__enter__.return_value = connection_mock - cursor_context = mock.MagicMock() - cursor_mock = mock.MagicMock() - cursor_context.__enter__.return_value = cursor_mock - connection_mock.cursor.return_value = cursor_context - - with ( - mock.patch.object( - setup_instance, - "_application_connection", - return_value=connection_mock, - ), - mock.patch.object( - setup_instance, "_migrations_table_exists", return_value=True - ), - mock.patch.object( - setup_instance, "_fetch_applied_migrations", return_value=set() - ), - mock.patch.object( - setup_instance, - "_apply_migration_file", - side_effect=capture_migration, - ) as apply_mock, - ): - setup_instance.run_migrations(tmp_path) - - assert apply_mock.call_count == 1 - assert migration_calls == ["000_base.sql"] - legacy_marked = any( - call.args[1] == ("20251022_add_other.sql",) - for call in cursor_mock.execute.call_args_list - if len(call.args) == 2 - ) - assert legacy_marked - - -def test_run_migrations_noop_when_all_files_already_applied( - mock_config: DatabaseConfig, tmp_path -) -> None: - setup_instance = DatabaseSetup(mock_config, dry_run=False) - - baseline = tmp_path / "000_base.sql" - baseline.write_text("SELECT 1;", encoding="utf-8") - other_migration = tmp_path / "20251022_add_other.sql" - other_migration.write_text("SELECT 2;", encoding="utf-8") - - connection_mock, cursor_mock = _connection_with_cursor() - - with ( - mock.patch.object( - setup_instance, - "_application_connection", - return_value=connection_mock, - ), - mock.patch.object( - setup_instance, "_migrations_table_exists", return_value=True - ), - mock.patch.object( - setup_instance, - "_fetch_applied_migrations", - return_value={"000_base.sql", "20251022_add_other.sql"}, - ), - mock.patch.object( - setup_instance, "_apply_migration_file" - ) as apply_mock, - ): - setup_instance.run_migrations(tmp_path) - - apply_mock.assert_not_called() - cursor_mock.execute.assert_not_called() - - -def _connection_with_cursor() -> tuple[mock.MagicMock, mock.MagicMock]: - connection_mock = mock.MagicMock() - connection_mock.__enter__.return_value = connection_mock - cursor_context = mock.MagicMock() - cursor_mock = mock.MagicMock() - cursor_context.__enter__.return_value = cursor_mock - connection_mock.cursor.return_value = cursor_context - return connection_mock, cursor_mock - - -def test_verify_seeded_data_raises_when_currency_missing( - mock_config: DatabaseConfig, -) -> None: - setup_instance = DatabaseSetup(mock_config, dry_run=False) - connection_mock, cursor_mock = _connection_with_cursor() - cursor_mock.fetchall.return_value = [("USD", True)] - - with mock.patch.object( - setup_instance, "_application_connection", return_value=connection_mock - ): - with pytest.raises(RuntimeError) as exc: - setup_instance._verify_seeded_data( - expected_currency_codes={"USD", "EUR"}, - expected_unit_codes=set(), - ) - - assert "EUR" in str(exc.value) - - -def test_verify_seeded_data_raises_when_default_currency_inactive( - mock_config: DatabaseConfig, -) -> None: - setup_instance = DatabaseSetup(mock_config, dry_run=False) - connection_mock, cursor_mock = _connection_with_cursor() - cursor_mock.fetchall.return_value = [("USD", False)] - - with mock.patch.object( - setup_instance, "_application_connection", return_value=connection_mock - ): - with pytest.raises(RuntimeError) as exc: - setup_instance._verify_seeded_data( - expected_currency_codes={"USD"}, - expected_unit_codes=set(), - ) - - assert "inactive" in str(exc.value) - - -def test_verify_seeded_data_raises_when_units_missing( - mock_config: DatabaseConfig, -) -> None: - setup_instance = DatabaseSetup(mock_config, dry_run=False) - connection_mock, cursor_mock = _connection_with_cursor() - cursor_mock.fetchall.return_value = [("tonnes", True)] - - with mock.patch.object( - setup_instance, "_application_connection", return_value=connection_mock - ): - with pytest.raises(RuntimeError) as exc: - setup_instance._verify_seeded_data( - expected_currency_codes=set(), - expected_unit_codes={"tonnes", "liters"}, - ) - - assert "liters" in str(exc.value) - - -def test_verify_seeded_data_raises_when_measurement_table_missing( - mock_config: DatabaseConfig, -) -> None: - setup_instance = DatabaseSetup(mock_config, dry_run=False) - connection_mock, cursor_mock = _connection_with_cursor() - cursor_mock.execute.side_effect = psycopg_errors.UndefinedTable( - "relation does not exist" - ) - - with mock.patch.object( - setup_instance, "_application_connection", return_value=connection_mock - ): - with pytest.raises(RuntimeError) as exc: - setup_instance._verify_seeded_data( - expected_currency_codes=set(), - expected_unit_codes={"tonnes"}, - ) - - assert "measurement_unit" in str(exc.value) - connection_mock.rollback.assert_called_once() - - -def test_seed_baseline_data_rerun_uses_existing_records( - mock_config: DatabaseConfig, -) -> None: - setup_instance = DatabaseSetup(mock_config, dry_run=False) - - connection_mock, cursor_mock = _connection_with_cursor() - - currency_rows = [(code, True) for code, *_ in seed_data.CURRENCY_SEEDS] - unit_rows = [(code, True) for code, *_ in seed_data.MEASUREMENT_UNIT_SEEDS] - - cursor_mock.fetchall.side_effect = [ - currency_rows, - unit_rows, - currency_rows, - unit_rows, - ] - - with ( - mock.patch.object( - setup_instance, - "_application_connection", - return_value=connection_mock, - ), - mock.patch("scripts.seed_data.run_with_namespace") as seed_run, - ): - setup_instance.seed_baseline_data(dry_run=False) - setup_instance.seed_baseline_data(dry_run=False) - - assert seed_run.call_count == 2 - first_namespace = seed_run.call_args_list[0].args[0] - assert isinstance(first_namespace, argparse.Namespace) - assert first_namespace.dry_run is False - assert seed_run.call_args_list[0].kwargs["config"] is setup_instance.config - assert cursor_mock.execute.call_count == 4 - - -def test_ensure_database_raises_with_context( - mock_config: DatabaseConfig, -) -> None: - setup_instance = DatabaseSetup(mock_config, dry_run=False) - connection_mock = mock.MagicMock() - cursor_mock = mock.MagicMock() - cursor_mock.fetchone.return_value = None - cursor_mock.execute.side_effect = [None, psycopg2.Error("create_fail")] - connection_mock.cursor.return_value = cursor_mock - - with mock.patch.object( - setup_instance, "_admin_connection", return_value=connection_mock - ): - with pytest.raises(RuntimeError) as exc: - setup_instance.ensure_database() - - assert "Failed to create database" in str(exc.value) - - -def test_ensure_role_raises_with_context_during_creation( - mock_config: DatabaseConfig, -) -> None: - setup_instance = DatabaseSetup(mock_config, dry_run=False) - - admin_conn, admin_cursor = _connection_with_cursor() - admin_cursor.fetchone.return_value = None - admin_cursor.execute.side_effect = [None, psycopg2.Error("role_fail")] - - with mock.patch.object( - setup_instance, - "_admin_connection", - side_effect=[admin_conn], - ): - with pytest.raises(RuntimeError) as exc: - setup_instance.ensure_role() - - assert "Failed to create role" in str(exc.value) - - -def test_ensure_role_raises_with_context_during_privilege_grants( - mock_config: DatabaseConfig, -) -> None: - setup_instance = DatabaseSetup(mock_config, dry_run=False) - - admin_conn, admin_cursor = _connection_with_cursor() - admin_cursor.fetchone.return_value = (1,) - - privilege_conn, privilege_cursor = _connection_with_cursor() - privilege_cursor.execute.side_effect = [psycopg2.Error("grant_fail")] - - with mock.patch.object( - setup_instance, - "_admin_connection", - side_effect=[admin_conn, privilege_conn], - ): - with pytest.raises(RuntimeError) as exc: - setup_instance.ensure_role() - - assert "Failed to grant privileges" in str(exc.value) - - -def test_ensure_database_dry_run_skips_creation( - mock_config: DatabaseConfig, -) -> None: - setup_instance = DatabaseSetup(mock_config, dry_run=True) - - connection_mock = mock.MagicMock() - cursor_mock = mock.MagicMock() - cursor_mock.fetchone.return_value = None - connection_mock.cursor.return_value = cursor_mock - - with ( - mock.patch.object( - setup_instance, "_admin_connection", return_value=connection_mock - ), - mock.patch("scripts.setup_database.logger") as logger_mock, - ): - setup_instance.ensure_database() - - # expect only existence check, no create attempt - cursor_mock.execute.assert_called_once() - logger_mock.info.assert_any_call( - "Dry run: would create database '%s'. Run without --dry-run to proceed.", - mock_config.database, - ) - - -def test_ensure_role_dry_run_skips_creation_and_grants( - mock_config: DatabaseConfig, -) -> None: - setup_instance = DatabaseSetup(mock_config, dry_run=True) - - admin_conn, admin_cursor = _connection_with_cursor() - admin_cursor.fetchone.return_value = None - - with ( - mock.patch.object( - setup_instance, - "_admin_connection", - side_effect=[admin_conn], - ) as conn_mock, - mock.patch("scripts.setup_database.logger") as logger_mock, - ): - setup_instance.ensure_role() - - assert conn_mock.call_count == 1 - admin_cursor.execute.assert_called_once() - logger_mock.info.assert_any_call( - "Dry run: would create role '%s'. Run without --dry-run to apply.", - mock_config.user, - ) - - -def test_register_rollback_skipped_when_dry_run( - mock_config: DatabaseConfig, -) -> None: - setup_instance = DatabaseSetup(mock_config, dry_run=True) - setup_instance._register_rollback("noop", lambda: None) - assert setup_instance._rollback_actions == [] - - -def test_execute_rollbacks_runs_in_reverse_order( - mock_config: DatabaseConfig, -) -> None: - setup_instance = DatabaseSetup(mock_config, dry_run=False) - - calls: list[str] = [] - - def first_action() -> None: - calls.append("first") - - def second_action() -> None: - calls.append("second") - - setup_instance._register_rollback("first", first_action) - setup_instance._register_rollback("second", second_action) - - with mock.patch("scripts.setup_database.logger"): - setup_instance.execute_rollbacks() - - assert calls == ["second", "first"] - assert setup_instance._rollback_actions == [] - - -def test_ensure_database_registers_rollback_action( - mock_config: DatabaseConfig, -) -> None: - setup_instance = DatabaseSetup(mock_config, dry_run=False) - connection_mock = mock.MagicMock() - cursor_mock = mock.MagicMock() - cursor_mock.fetchone.return_value = None - connection_mock.cursor.return_value = cursor_mock - - with ( - mock.patch.object( - setup_instance, "_admin_connection", return_value=connection_mock - ), - mock.patch.object( - setup_instance, "_register_rollback" - ) as register_mock, - mock.patch.object(setup_instance, "_drop_database") as drop_mock, - ): - setup_instance.ensure_database() - register_mock.assert_called_once() - label, action = register_mock.call_args[0] - assert "drop database" in label - action() - drop_mock.assert_called_once_with(mock_config.database) - - -def test_ensure_role_registers_rollback_actions( - mock_config: DatabaseConfig, -) -> None: - setup_instance = DatabaseSetup(mock_config, dry_run=False) - - admin_conn, admin_cursor = _connection_with_cursor() - admin_cursor.fetchone.return_value = None - privilege_conn, privilege_cursor = _connection_with_cursor() - - with ( - mock.patch.object( - setup_instance, - "_admin_connection", - side_effect=[admin_conn, privilege_conn], - ), - mock.patch.object( - setup_instance, "_register_rollback" - ) as register_mock, - mock.patch.object(setup_instance, "_drop_role") as drop_mock, - mock.patch.object( - setup_instance, "_revoke_role_privileges" - ) as revoke_mock, - ): - setup_instance.ensure_role() - assert register_mock.call_count == 2 - drop_label, drop_action = register_mock.call_args_list[0][0] - revoke_label, revoke_action = register_mock.call_args_list[1][0] - - assert "drop role" in drop_label - assert "revoke privileges" in revoke_label - - drop_action() - drop_mock.assert_called_once_with(mock_config.user) - - revoke_action() - revoke_mock.assert_called_once() - - -def test_main_triggers_rollbacks_on_failure( - mock_config: DatabaseConfig, -) -> None: - args = argparse.Namespace( - ensure_database=True, - ensure_role=True, - ensure_schema=False, - initialize_schema=False, - run_migrations=False, - seed_data=False, - migrations_dir=None, - db_driver=None, - db_host=None, - db_port=None, - db_name=None, - db_user=None, - db_password=None, - db_schema=None, - admin_url=None, - admin_user=None, - admin_password=None, - admin_db=None, - dry_run=False, - verbose=0, - ) - - with ( - mock.patch.object(setup_db_module, "parse_args", return_value=args), - mock.patch.object( - setup_db_module.DatabaseConfig, "from_env", return_value=mock_config - ), - mock.patch.object(setup_db_module, "DatabaseSetup") as setup_cls, - ): - setup_instance = mock.MagicMock() - setup_instance.dry_run = False - setup_instance._rollback_actions = [ - ("drop role", mock.MagicMock()), - ] - setup_instance.ensure_database.side_effect = RuntimeError("boom") - setup_instance.execute_rollbacks = mock.MagicMock() - setup_instance.clear_rollbacks = mock.MagicMock() - setup_cls.return_value = setup_instance - - with pytest.raises(RuntimeError): - setup_db_module.main() - - setup_instance.execute_rollbacks.assert_called_once() - setup_instance.clear_rollbacks.assert_called_once() diff --git a/tests/unit/test_simulation.py b/tests/unit/test_simulation.py deleted file mode 100644 index b89febe..0000000 --- a/tests/unit/test_simulation.py +++ /dev/null @@ -1,232 +0,0 @@ -from math import isclose -from random import Random -from uuid import uuid4 - -import pytest -from fastapi.testclient import TestClient -from sqlalchemy.orm import Session - -from typing import Any, Dict, List - -from models.simulation_result import SimulationResult -from services.simulation import DEFAULT_UNIFORM_SPAN_RATIO, run_simulation - - -@pytest.fixture -def client(api_client: TestClient) -> TestClient: - return api_client - - -def test_run_simulation_function_generates_samples(): - params: List[Dict[str, Any]] = [ - { - "name": "grade", - "value": 1.8, - "distribution": "normal", - "std_dev": 0.2, - }, - { - "name": "recovery", - "value": 0.9, - "distribution": "uniform", - "min": 0.8, - "max": 0.95, - }, - ] - results = run_simulation(params, iterations=5, seed=123) - assert isinstance(results, list) - assert len(results) == 5 - assert results[0]["iteration"] == 1 - - -def test_run_simulation_with_zero_iterations_returns_empty(): - params: List[Dict[str, Any]] = [ - {"name": "grade", "value": 1.2, "distribution": "normal"} - ] - results = run_simulation(params, iterations=0) - assert results == [] - - -@pytest.mark.parametrize( - "parameter_payload,error_message", - [ - ( - {"name": "missing-value"}, - "Parameter at index 0 must include 'value'", - ), - ( - { - "name": "bad-dist", - "value": 1.0, - "distribution": "unsupported", - }, - "Parameter 'bad-dist' has unsupported distribution 'unsupported'", - ), - ( - { - "name": "uniform-range", - "value": 1.0, - "distribution": "uniform", - "min": 5, - "max": 5, - }, - "Parameter 'uniform-range' requires 'min' < 'max' for uniform distribution", - ), - ( - { - "name": "triangular-mode", - "value": 5.0, - "distribution": "triangular", - "min": 1, - "max": 3, - "mode": 5, - }, - "Parameter 'triangular-mode' mode must be within min/max bounds for triangular distribution", - ), - ], -) -def test_run_simulation_parameter_validation_errors( - parameter_payload: Dict[str, Any], error_message: str -) -> None: - with pytest.raises(ValueError) as exc: - run_simulation([parameter_payload]) - assert str(exc.value) == error_message - - -def test_run_simulation_normal_std_dev_fallback(): - params: List[Dict[str, Any]] = [ - { - "name": "std-dev-fallback", - "value": 10.0, - "distribution": "normal", - "std_dev": 0, - } - ] - results = run_simulation(params, iterations=3, seed=99) - assert len(results) == 3 - assert all("result" in entry for entry in results) - - -def test_run_simulation_triangular_sampling_path(): - params: List[Dict[str, Any]] = [ - {"name": "tri", "value": 10.0, "distribution": "triangular"} - ] - seed = 21 - iterations = 4 - results = run_simulation(params, iterations=iterations, seed=seed) - assert len(results) == iterations - span = 10.0 * DEFAULT_UNIFORM_SPAN_RATIO - rng = Random(seed) - expected_samples = [ - rng.triangular(10.0 - span, 10.0 + span, 10.0) - for _ in range(iterations) - ] - actual_samples = [entry["result"] for entry in results] - for actual, expected in zip(actual_samples, expected_samples): - assert isclose(actual, expected, rel_tol=1e-9) - - -def test_run_simulation_uniform_defaults_apply_bounds(): - params: List[Dict[str, Any]] = [ - {"name": "uniform-auto", "value": 200.0, "distribution": "uniform"} - ] - seed = 17 - iterations = 3 - results = run_simulation(params, iterations=iterations, seed=seed) - assert len(results) == iterations - span = 200.0 * DEFAULT_UNIFORM_SPAN_RATIO - rng = Random(seed) - expected_samples = [ - rng.uniform(200.0 - span, 200.0 + span) for _ in range(iterations) - ] - actual_samples = [entry["result"] for entry in results] - for actual, expected in zip(actual_samples, expected_samples): - assert isclose(actual, expected, rel_tol=1e-9) - - -def test_run_simulation_without_parameters_returns_empty(): - assert run_simulation([], iterations=5) == [] - - -def test_simulation_endpoint_no_params(client: TestClient): - scenario_payload: Dict[str, Any] = { - "name": f"NoParamScenario-{uuid4()}", - "description": "No parameters run", - } - scenario_resp = client.post("/api/scenarios/", json=scenario_payload) - assert scenario_resp.status_code == 200 - scenario_id = scenario_resp.json()["id"] - - resp = client.post( - "/api/simulations/run", - json={"scenario_id": scenario_id, "parameters": [], "iterations": 10}, - ) - assert resp.status_code == 400 - assert resp.json()["detail"] == "No parameters provided" - - -def test_simulation_endpoint_success(client: TestClient, db_session: Session): - scenario_payload: Dict[str, Any] = { - "name": f"SimScenario-{uuid4()}", - "description": "Simulation test", - } - scenario_resp = client.post("/api/scenarios/", json=scenario_payload) - assert scenario_resp.status_code == 200 - scenario_id = scenario_resp.json()["id"] - - params: List[Dict[str, Any]] = [ - { - "name": "param1", - "value": 2.5, - "distribution": "normal", - "std_dev": 0.5, - } - ] - payload: Dict[str, Any] = { - "scenario_id": scenario_id, - "parameters": params, - "iterations": 10, - "seed": 42, - } - - resp = client.post("/api/simulations/run", json=payload) - assert resp.status_code == 200 - data = resp.json() - assert data["scenario_id"] == scenario_id - assert len(data["results"]) == 10 - assert data["summary"]["count"] == 10 - - db_session.expire_all() - persisted = ( - db_session.query(SimulationResult) - .filter(SimulationResult.scenario_id == scenario_id) - .all() - ) - assert len(persisted) == 10 - - -def test_simulation_endpoint_uses_stored_parameters(client: TestClient): - scenario_payload: Dict[str, Any] = { - "name": f"StoredParams-{uuid4()}", - "description": "Stored parameter simulation", - } - scenario_resp = client.post("/api/scenarios/", json=scenario_payload) - assert scenario_resp.status_code == 200 - scenario_id = scenario_resp.json()["id"] - - parameter_payload: Dict[str, Any] = { - "scenario_id": scenario_id, - "name": "grade", - "value": 1.5, - } - param_resp = client.post("/api/parameters/", json=parameter_payload) - assert param_resp.status_code == 200 - - resp = client.post( - "/api/simulations/run", - json={"scenario_id": scenario_id, "iterations": 3, "seed": 7}, - ) - assert resp.status_code == 200 - data = resp.json() - assert data["summary"]["count"] == 3 - assert len(data["results"]) == 3 diff --git a/tests/unit/test_theme_settings.py b/tests/unit/test_theme_settings.py deleted file mode 100644 index e24f7ec..0000000 --- a/tests/unit/test_theme_settings.py +++ /dev/null @@ -1,56 +0,0 @@ -from sqlalchemy.orm import Session - -from services.settings import save_theme_settings, get_theme_settings - - -def test_save_theme_settings(db_session: Session): - theme_data = { - "theme_name": "dark", - "primary_color": "#000000", - "secondary_color": "#333333", - "accent_color": "#ff0000", - "background_color": "#1a1a1a", - "text_color": "#ffffff" - } - - saved_setting = save_theme_settings(db_session, theme_data) - assert str(saved_setting.theme_name) == "dark" - assert str(saved_setting.primary_color) == "#000000" - - -def test_get_theme_settings(db_session: Session): - # Create a theme setting first - theme_data = { - "theme_name": "light", - "primary_color": "#ffffff", - "secondary_color": "#cccccc", - "accent_color": "#0000ff", - "background_color": "#f0f0f0", - "text_color": "#000000" - } - save_theme_settings(db_session, theme_data) - - settings = get_theme_settings(db_session) - assert settings["theme_name"] == "light" - assert settings["primary_color"] == "#ffffff" - - -def test_theme_settings_api(api_client): - # Test API endpoint for saving theme settings - theme_data = { - "theme_name": "test_theme", - "primary_color": "#123456", - "secondary_color": "#789abc", - "accent_color": "#def012", - "background_color": "#345678", - "text_color": "#9abcde" - } - - response = api_client.post("/api/settings/theme", json=theme_data) - assert response.status_code == 200 - assert response.json()["theme"]["theme_name"] == "test_theme" - - # Test API endpoint for getting theme settings - response = api_client.get("/api/settings/theme") - assert response.status_code == 200 - assert response.json()["theme_name"] == "test_theme" diff --git a/tests/unit/test_ui_routes.py b/tests/unit/test_ui_routes.py deleted file mode 100644 index b0757d7..0000000 --- a/tests/unit/test_ui_routes.py +++ /dev/null @@ -1,179 +0,0 @@ -from typing import Any, Dict, cast - -import pytest -from fastapi.testclient import TestClient - -from models.scenario import Scenario -from services import settings as settings_service - - -def test_dashboard_route_provides_summary( - api_client: TestClient, seeded_ui_data: Dict[str, Any] -) -> None: - response = api_client.get("/ui/dashboard") - assert response.status_code == 200 - - template = getattr(response, "template", None) - assert template is not None - assert template.name == "Dashboard.html" - - context = cast(Dict[str, Any], getattr(response, "context", {})) - assert context.get("report_available") is True - - metric_labels = {item["label"] for item in context["summary_metrics"]} - assert { - "CAPEX Total", - "OPEX Total", - "Production", - "Simulation Iterations", - }.issubset(metric_labels) - - scenario = cast(Scenario, seeded_ui_data["scenario"]) - scenario_row = next( - row - for row in context["scenario_rows"] - if row["scenario_name"] == scenario.name - ) - assert scenario_row["iterations"] == 3 - assert scenario_row["simulation_mean_display"] == "971,666.67" - assert scenario_row["capex_display"] == "$1,000,000.00" - assert scenario_row["opex_display"] == "$250,000.00" - assert scenario_row["production_display"] == "800.00" - assert scenario_row["consumption_display"] == "1,200.00" - - -def test_scenarios_route_lists_seeded_scenario( - api_client: TestClient, seeded_ui_data: Dict[str, Any] -) -> None: - response = api_client.get("/ui/scenarios") - assert response.status_code == 200 - - template = getattr(response, "template", None) - assert template is not None - assert template.name == "ScenarioForm.html" - - context = cast(Dict[str, Any], getattr(response, "context", {})) - names = [item["name"] for item in context["scenarios"]] - scenario = cast(Scenario, seeded_ui_data["scenario"]) - assert scenario.name in names - - -def test_reporting_route_includes_summary( - api_client: TestClient, seeded_ui_data: Dict[str, Any] -) -> None: - response = api_client.get("/ui/reporting") - assert response.status_code == 200 - - template = getattr(response, "template", None) - assert template is not None - assert template.name == "reporting.html" - - context = cast(Dict[str, Any], getattr(response, "context", {})) - summaries = context["report_summaries"] - scenario = cast(Scenario, seeded_ui_data["scenario"]) - scenario_summary = next( - item for item in summaries if item["scenario_id"] == scenario.id - ) - assert scenario_summary["iterations"] == 3 - mean_value = float(scenario_summary["summary"]["mean"]) - assert abs(mean_value - 971_666.6666666666) < 1e-6 - - -def test_dashboard_data_endpoint_returns_aggregates( - api_client: TestClient, seeded_ui_data: Dict[str, Any] -) -> None: - response = api_client.get("/ui/dashboard/data") - assert response.status_code == 200 - - payload = response.json() - assert payload["report_available"] is True - - metric_map = { - item["label"]: item["value"] for item in payload["summary_metrics"] - } - assert metric_map["CAPEX Total"].startswith("$") - assert metric_map["Maintenance Cost"].startswith("$") - - scenario = cast(Scenario, seeded_ui_data["scenario"]) - scenario_rows = payload["scenario_rows"] - scenario_entry = next( - row for row in scenario_rows if row["scenario_name"] == scenario.name - ) - assert scenario_entry["capex_display"] == "$1,000,000.00" - assert scenario_entry["production_display"] == "800.00" - - labels = payload["scenario_cost_chart"]["labels"] - idx = labels.index(scenario.name) - assert payload["scenario_cost_chart"]["capex"][idx] == 1_000_000.0 - - activity_labels = payload["scenario_activity_chart"]["labels"] - activity_idx = activity_labels.index(scenario.name) - assert ( - payload["scenario_activity_chart"]["production"][activity_idx] == 800.0 - ) - - -@pytest.mark.parametrize( - ("path", "template_name"), - [ - ("/", "Dashboard.html"), - ("/ui/parameters", "ParameterInput.html"), - ("/ui/costs", "costs.html"), - ("/ui/consumption", "consumption.html"), - ("/ui/production", "production.html"), - ("/ui/equipment", "equipment.html"), - ("/ui/maintenance", "maintenance.html"), - ("/ui/simulations", "simulations.html"), - ], -) -def test_additional_ui_routes_render_templates( - api_client: TestClient, - seeded_ui_data: Dict[str, Any], - path: str, - template_name: str, -) -> None: - response = api_client.get(path) - assert response.status_code == 200 - - template = getattr(response, "template", None) - assert template is not None - assert template.name == template_name - - context = cast(Dict[str, Any], getattr(response, "context", {})) - assert context - - -def test_settings_route_provides_css_context( - api_client: TestClient, - monkeypatch: pytest.MonkeyPatch, -) -> None: - env_var = settings_service.css_key_to_env_var("--color-accent") - monkeypatch.setenv(env_var, "#abcdef") - - response = api_client.get("/ui/settings") - assert response.status_code == 200 - - template = getattr(response, "template", None) - assert template is not None - assert template.name == "settings.html" - - context = cast(Dict[str, Any], getattr(response, "context", {})) - assert "css_variables" in context - assert "css_defaults" in context - assert "css_env_overrides" in context - assert "css_env_override_rows" in context - assert "css_env_override_meta" in context - - assert context["css_variables"]["--color-accent"] == "#abcdef" - assert ( - context["css_defaults"]["--color-accent"] - == settings_service.CSS_COLOR_DEFAULTS["--color-accent"] - ) - assert context["css_env_overrides"]["--color-accent"] == "#abcdef" - - override_rows = context["css_env_override_rows"] - assert any(row["env_var"] == env_var for row in override_rows) - - meta = context["css_env_override_meta"]["--color-accent"] - assert meta["value"] == "#abcdef" - assert meta["env_var"] == env_var diff --git a/tests/unit/test_validation.py b/tests/unit/test_validation.py deleted file mode 100644 index 70473ae..0000000 --- a/tests/unit/test_validation.py +++ /dev/null @@ -1,28 +0,0 @@ -from uuid import uuid4 - -import pytest -from fastapi import HTTPException -from fastapi.testclient import TestClient - - -def test_validate_json_allows_valid_payload(api_client: TestClient) -> None: - payload = { - "name": f"ValidJSON-{uuid4()}", - "description": "Middleware should allow valid JSON.", - } - response = api_client.post("/api/scenarios/", json=payload) - assert response.status_code == 200 - data = response.json() - assert data["name"] == payload["name"] - - -def test_validate_json_rejects_invalid_payload(api_client: TestClient) -> None: - with pytest.raises(HTTPException) as exc_info: - api_client.post( - "/api/scenarios/", - content=b"{not valid json", - headers={"Content-Type": "application/json"}, - ) - - assert exc_info.value.status_code == 400 - assert exc_info.value.detail == "Invalid JSON payload" -- 2.39.5 From c6a0eb258820f4702f08f684ca6892f4e2664e28 Mon Sep 17 00:00:00 2001 From: zwitschi Date: Sun, 9 Nov 2025 16:49:46 +0100 Subject: [PATCH 002/109] v2 init --- static/js/consumption.js | 205 -------------- static/js/costs.js | 339 ----------------------- static/js/currencies.js | 537 ------------------------------------- static/js/dashboard.js | 289 -------------------- static/js/equipment.js | 145 ---------- static/js/maintenance.js | 243 ----------------- static/js/parameters.js | 124 --------- static/js/production.js | 204 -------------- static/js/reporting.js | 149 ---------- static/js/scenario-form.js | 78 ------ static/js/settings.js | 200 -------------- static/js/simulations.js | 354 ------------------------ 12 files changed, 2867 deletions(-) delete mode 100644 static/js/consumption.js delete mode 100644 static/js/costs.js delete mode 100644 static/js/currencies.js delete mode 100644 static/js/dashboard.js delete mode 100644 static/js/equipment.js delete mode 100644 static/js/maintenance.js delete mode 100644 static/js/parameters.js delete mode 100644 static/js/production.js delete mode 100644 static/js/reporting.js delete mode 100644 static/js/scenario-form.js delete mode 100644 static/js/settings.js delete mode 100644 static/js/simulations.js diff --git a/static/js/consumption.js b/static/js/consumption.js deleted file mode 100644 index 2866dd9..0000000 --- a/static/js/consumption.js +++ /dev/null @@ -1,205 +0,0 @@ -document.addEventListener("DOMContentLoaded", () => { - const dataElement = document.getElementById("consumption-data"); - let data = { scenarios: [], consumption: {}, unit_options: [] }; - - if (dataElement) { - try { - const parsed = JSON.parse(dataElement.textContent || "{}"); - if (parsed && typeof parsed === "object") { - data = { - scenarios: Array.isArray(parsed.scenarios) ? parsed.scenarios : [], - consumption: - parsed.consumption && typeof parsed.consumption === "object" - ? parsed.consumption - : {}, - unit_options: Array.isArray(parsed.unit_options) - ? parsed.unit_options - : [], - }; - } - } catch (error) { - console.error("Unable to parse consumption data", error); - } - } - - const consumptionByScenario = data.consumption; - const filterSelect = document.getElementById("consumption-scenario-filter"); - const tableWrapper = document.getElementById("consumption-table-wrapper"); - const tableBody = document.getElementById("consumption-table-body"); - const emptyState = document.getElementById("consumption-empty"); - const form = document.getElementById("consumption-form"); - const feedbackEl = document.getElementById("consumption-feedback"); - const unitSelect = document.getElementById("consumption-form-unit"); - const unitSymbolInput = document.getElementById( - "consumption-form-unit-symbol" - ); - - const showFeedback = (message, type = "success") => { - if (!feedbackEl) { - return; - } - feedbackEl.textContent = message; - feedbackEl.classList.remove("hidden", "success", "error"); - feedbackEl.classList.add(type); - }; - - const hideFeedback = () => { - if (!feedbackEl) { - return; - } - feedbackEl.classList.add("hidden"); - feedbackEl.textContent = ""; - }; - - const formatAmount = (value) => - Number(value).toLocaleString(undefined, { - minimumFractionDigits: 2, - maximumFractionDigits: 2, - }); - - const formatMeasurement = (amount, symbol, name) => { - if (symbol) { - return `${formatAmount(amount)} ${symbol}`; - } - if (name) { - return `${formatAmount(amount)} ${name}`; - } - return formatAmount(amount); - }; - - const renderConsumptionRows = (scenarioId) => { - if (!tableBody || !tableWrapper || !emptyState) { - return; - } - - const key = String(scenarioId); - const records = consumptionByScenario[key] || []; - - tableBody.innerHTML = ""; - - if (!records.length) { - emptyState.textContent = "No consumption records for this scenario yet."; - emptyState.classList.remove("hidden"); - tableWrapper.classList.add("hidden"); - return; - } - - emptyState.classList.add("hidden"); - tableWrapper.classList.remove("hidden"); - - records.forEach((record) => { - const row = document.createElement("tr"); - row.innerHTML = ` - ${formatMeasurement( - record.amount, - record.unit_symbol, - record.unit_name - )} - ${record.description || "—"} - `; - tableBody.appendChild(row); - }); - }; - - if (filterSelect) { - filterSelect.addEventListener("change", (event) => { - const value = event.target.value; - if (!value) { - if (emptyState && tableWrapper && tableBody) { - emptyState.textContent = - "Choose a scenario to review its consumption records."; - emptyState.classList.remove("hidden"); - tableWrapper.classList.add("hidden"); - tableBody.innerHTML = ""; - } - return; - } - renderConsumptionRows(value); - }); - } - - const submitConsumption = async (event) => { - event.preventDefault(); - hideFeedback(); - - if (!form) { - return; - } - - const formData = new FormData(form); - const scenarioId = formData.get("scenario_id"); - const unitName = formData.get("unit_name"); - const unitSymbol = formData.get("unit_symbol"); - const payload = { - scenario_id: scenarioId ? Number(scenarioId) : null, - amount: Number(formData.get("amount")), - description: formData.get("description") || null, - unit_name: unitName ? String(unitName) : null, - unit_symbol: unitSymbol ? String(unitSymbol) : null, - }; - - try { - const response = await fetch("/api/consumption/", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(payload), - }); - - if (!response.ok) { - const errorDetail = await response.json().catch(() => ({})); - throw new Error( - errorDetail.detail || "Unable to add consumption record." - ); - } - - const result = await response.json(); - const mapKey = String(result.scenario_id); - - if (!Array.isArray(consumptionByScenario[mapKey])) { - consumptionByScenario[mapKey] = []; - } - consumptionByScenario[mapKey].push(result); - - form.reset(); - syncUnitSelection(); - showFeedback("Consumption record saved.", "success"); - - if (filterSelect && filterSelect.value === String(result.scenario_id)) { - renderConsumptionRows(filterSelect.value); - } - } catch (error) { - showFeedback(error.message || "An unexpected error occurred.", "error"); - } - }; - - if (form) { - form.addEventListener("submit", submitConsumption); - } - - const syncUnitSelection = () => { - if (!unitSelect || !unitSymbolInput) { - return; - } - if (!unitSelect.value && unitSelect.options.length > 0) { - const firstOption = Array.from(unitSelect.options).find( - (option) => option.value - ); - if (firstOption) { - firstOption.selected = true; - } - } - const selectedOption = unitSelect.options[unitSelect.selectedIndex]; - unitSymbolInput.value = selectedOption - ? selectedOption.getAttribute("data-symbol") || "" - : ""; - }; - - if (unitSelect) { - unitSelect.addEventListener("change", syncUnitSelection); - syncUnitSelection(); - } - - if (filterSelect && filterSelect.value) { - renderConsumptionRows(filterSelect.value); - } -}); diff --git a/static/js/costs.js b/static/js/costs.js deleted file mode 100644 index d75139c..0000000 --- a/static/js/costs.js +++ /dev/null @@ -1,339 +0,0 @@ -document.addEventListener("DOMContentLoaded", () => { - const dataElement = document.getElementById("costs-payload"); - let capexByScenario = {}; - let opexByScenario = {}; - let currencyOptions = []; - - if (dataElement) { - try { - const parsed = JSON.parse(dataElement.textContent || "{}"); - if (parsed && typeof parsed === "object") { - if (parsed.capex && typeof parsed.capex === "object") { - capexByScenario = parsed.capex; - } - if (parsed.opex && typeof parsed.opex === "object") { - opexByScenario = parsed.opex; - } - if (Array.isArray(parsed.currency_options)) { - currencyOptions = parsed.currency_options; - } - } - } catch (error) { - console.error("Unable to parse cost data", error); - } - } - - const filterSelect = document.getElementById("costs-scenario-filter"); - const costsEmptyState = document.getElementById("costs-empty"); - const costsDataWrapper = document.getElementById("costs-data"); - const capexTableBody = document.getElementById("capex-table-body"); - const opexTableBody = document.getElementById("opex-table-body"); - const capexEmpty = document.getElementById("capex-empty"); - const opexEmpty = document.getElementById("opex-empty"); - const capexTotal = document.getElementById("capex-total"); - const opexTotal = document.getElementById("opex-total"); - const capexForm = document.getElementById("capex-form"); - const opexForm = document.getElementById("opex-form"); - const capexFeedback = document.getElementById("capex-feedback"); - const opexFeedback = document.getElementById("opex-feedback"); - const capexFormScenario = document.getElementById("capex-form-scenario"); - const opexFormScenario = document.getElementById("opex-form-scenario"); - const capexCurrencySelect = document.getElementById("capex-form-currency"); - const opexCurrencySelect = document.getElementById("opex-form-currency"); - - // If no currency options were injected server-side, fetch from API - const fetchCurrencyOptions = async () => { - try { - const resp = await fetch("/api/currencies/"); - if (!resp.ok) return; - const list = await resp.json(); - if (Array.isArray(list) && list.length) { - currencyOptions = list; - populateCurrencySelects(); - } - } catch (err) { - console.warn("Unable to fetch currency options", err); - } - }; - - const populateCurrencySelects = () => { - const selectElements = [capexCurrencySelect, opexCurrencySelect].filter(Boolean); - selectElements.forEach((sel) => { - if (!sel) return; - // Clear non-empty options except the empty placeholder - const placeholder = sel.querySelector("option[value='']"); - sel.innerHTML = ""; - if (placeholder) sel.appendChild(placeholder); - currencyOptions.forEach((opt) => { - const option = document.createElement("option"); - option.value = opt.id; - option.textContent = opt.name || opt.id; - sel.appendChild(option); - }); - }); - }; - - // populate from injected options first, then fetch to refresh - if (currencyOptions && currencyOptions.length) populateCurrencySelects(); - else fetchCurrencyOptions(); - - const showFeedback = (element, message, type = "success") => { - if (!element) { - return; - } - element.textContent = message; - element.classList.remove("hidden", "success", "error"); - element.classList.add(type); - }; - - const hideFeedback = (element) => { - if (!element) { - return; - } - element.classList.add("hidden"); - element.textContent = ""; - }; - - const formatAmount = (value) => - Number(value).toLocaleString(undefined, { - minimumFractionDigits: 2, - maximumFractionDigits: 2, - }); - - const formatCurrencyAmount = (value, currencyCode) => { - if (!currencyCode) { - return formatAmount(value); - } - try { - return new Intl.NumberFormat(undefined, { - style: "currency", - currency: currencyCode, - minimumFractionDigits: 2, - maximumFractionDigits: 2, - }).format(Number(value)); - } catch (error) { - return `${currencyCode} ${formatAmount(value)}`; - } - }; - - const sumAmount = (records) => - records.reduce((total, record) => total + Number(record.amount || 0), 0); - - const describeTotal = (records) => { - if (!records || records.length === 0) { - return "—"; - } - const total = sumAmount(records); - const currencyCodes = Array.from( - new Set( - records - .map((record) => (record.currency_code || "").trim().toUpperCase()) - .filter(Boolean) - ) - ); - - if (currencyCodes.length === 1) { - return formatCurrencyAmount(total, currencyCodes[0]); - } - return `${formatAmount(total)} (mixed)`; - }; - - const renderCostTables = (scenarioId) => { - if ( - !capexTableBody || - !opexTableBody || - !capexEmpty || - !opexEmpty || - !capexTotal || - !opexTotal - ) { - return; - } - - const capexRecords = capexByScenario[String(scenarioId)] || []; - const opexRecords = opexByScenario[String(scenarioId)] || []; - - capexTableBody.innerHTML = ""; - opexTableBody.innerHTML = ""; - - if (!capexRecords.length) { - capexEmpty.classList.remove("hidden"); - } else { - capexEmpty.classList.add("hidden"); - capexRecords.forEach((record) => { - const row = document.createElement("tr"); - row.innerHTML = ` - ${formatCurrencyAmount(record.amount, record.currency_code)} - ${record.description || "—"} - `; - capexTableBody.appendChild(row); - }); - } - - if (!opexRecords.length) { - opexEmpty.classList.remove("hidden"); - } else { - opexEmpty.classList.add("hidden"); - opexRecords.forEach((record) => { - const row = document.createElement("tr"); - row.innerHTML = ` - ${formatCurrencyAmount(record.amount, record.currency_code)} - ${record.description || "—"} - `; - opexTableBody.appendChild(row); - }); - } - - capexTotal.textContent = describeTotal(capexRecords); - opexTotal.textContent = describeTotal(opexRecords); - }; - - const toggleCostView = (show) => { - if ( - !costsEmptyState || - !costsDataWrapper || - !capexTableBody || - !opexTableBody - ) { - return; - } - - if (show) { - costsEmptyState.classList.add("hidden"); - costsDataWrapper.classList.remove("hidden"); - } else { - costsEmptyState.classList.remove("hidden"); - costsDataWrapper.classList.add("hidden"); - capexTableBody.innerHTML = ""; - opexTableBody.innerHTML = ""; - if (capexTotal) { - capexTotal.textContent = "—"; - } - if (opexTotal) { - opexTotal.textContent = "—"; - } - if (capexEmpty) { - capexEmpty.classList.add("hidden"); - } - if (opexEmpty) { - opexEmpty.classList.add("hidden"); - } - } - }; - - const syncFormSelections = (value) => { - if (capexFormScenario) { - capexFormScenario.value = value || ""; - } - if (opexFormScenario) { - opexFormScenario.value = value || ""; - } - }; - - const ensureCurrencySelection = (selectElement) => { - if (!selectElement || selectElement.value) { - return; - } - const firstOption = selectElement.querySelector( - "option[value]:not([value=''])" - ); - if (firstOption && firstOption.value) { - selectElement.value = firstOption.value; - } - }; - - if (filterSelect) { - filterSelect.addEventListener("change", (event) => { - const value = event.target.value; - if (!value) { - toggleCostView(false); - syncFormSelections(""); - return; - } - toggleCostView(true); - renderCostTables(value); - syncFormSelections(value); - }); - } - - const submitCostEntry = async (event, targetUrl, storageMap, feedbackEl) => { - event.preventDefault(); - hideFeedback(feedbackEl); - - const formData = new FormData(event.target); - const scenarioId = formData.get("scenario_id"); - const currencyCode = formData.get("currency_code"); - const payload = { - scenario_id: scenarioId ? Number(scenarioId) : null, - amount: Number(formData.get("amount")), - description: formData.get("description") || null, - currency_code: currencyCode ? String(currencyCode).toUpperCase() : null, - }; - - if (!payload.scenario_id) { - showFeedback(feedbackEl, "Select a scenario before submitting.", "error"); - return; - } - - if (!payload.currency_code) { - showFeedback(feedbackEl, "Choose a currency before submitting.", "error"); - return; - } - - try { - const response = await fetch(targetUrl, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(payload), - }); - - if (!response.ok) { - const errorDetail = await response.json().catch(() => ({})); - throw new Error(errorDetail.detail || "Unable to save cost entry."); - } - - const result = await response.json(); - const mapKey = String(result.scenario_id); - - if (!Array.isArray(storageMap[mapKey])) { - storageMap[mapKey] = []; - } - - storageMap[mapKey].push(result); - - event.target.reset(); - ensureCurrencySelection(event.target.querySelector("select[name='currency_code']")); - showFeedback(feedbackEl, "Entry saved successfully.", "success"); - - if (filterSelect && filterSelect.value === mapKey) { - renderCostTables(mapKey); - } - } catch (error) { - showFeedback( - feedbackEl, - error.message || "An unexpected error occurred.", - "error" - ); - } - }; - - if (capexForm) { - ensureCurrencySelection(capexCurrencySelect); - capexForm.addEventListener("submit", (event) => - submitCostEntry(event, "/api/costs/capex", capexByScenario, capexFeedback) - ); - } - - if (opexForm) { - ensureCurrencySelection(opexCurrencySelect); - opexForm.addEventListener("submit", (event) => - submitCostEntry(event, "/api/costs/opex", opexByScenario, opexFeedback) - ); - } - - if (filterSelect && filterSelect.value) { - toggleCostView(true); - renderCostTables(filterSelect.value); - syncFormSelections(filterSelect.value); - } -}); diff --git a/static/js/currencies.js b/static/js/currencies.js deleted file mode 100644 index 86557f7..0000000 --- a/static/js/currencies.js +++ /dev/null @@ -1,537 +0,0 @@ -document.addEventListener("DOMContentLoaded", () => { - const dataElement = document.getElementById("currencies-data"); - const editorSection = document.getElementById("currencies-editor"); - const tableBody = document.getElementById("currencies-table-body"); - const tableEmptyState = document.getElementById("currencies-table-empty"); - const metrics = { - total: document.getElementById("currency-metric-total"), - active: document.getElementById("currency-metric-active"), - inactive: document.getElementById("currency-metric-inactive"), - }; - - const form = document.getElementById("currency-form"); - const existingSelect = document.getElementById("currency-form-existing"); - const codeInput = document.getElementById("currency-form-code"); - const nameInput = document.getElementById("currency-form-name"); - const symbolInput = document.getElementById("currency-form-symbol"); - const statusSelect = document.getElementById("currency-form-status"); - const resetButton = document.getElementById("currency-form-reset"); - const feedbackElement = document.getElementById("currency-form-feedback"); - - const saveButton = form ? form.querySelector("button[type='submit']") : null; - - const uppercaseCode = (value) => - (value || "").toString().trim().toUpperCase(); - const normalizeSymbol = (value) => { - if (value === undefined || value === null) { - return null; - } - const trimmed = String(value).trim(); - return trimmed ? trimmed : null; - }; - - const normalizeApiBase = (value) => { - if (!value || typeof value !== "string") { - return "/api/currencies"; - } - return value.endsWith("/") ? value.slice(0, -1) : value; - }; - - let currencies = []; - let apiBase = "/api/currencies"; - let defaultCurrencyCode = "USD"; - - const buildCurrencyRecord = (record) => { - if (!record || typeof record !== "object") { - return null; - } - const code = uppercaseCode(record.code); - return { - id: record.id ?? null, - code, - name: record.name || "", - symbol: record.symbol || "", - is_active: Boolean(record.is_active), - is_default: code === defaultCurrencyCode, - }; - }; - - const findCurrencyIndex = (code) => { - return currencies.findIndex((item) => item.code === code); - }; - - const upsertCurrency = (record) => { - const normalized = buildCurrencyRecord(record); - if (!normalized) { - return null; - } - const existingIndex = findCurrencyIndex(normalized.code); - if (existingIndex >= 0) { - currencies[existingIndex] = normalized; - } else { - currencies.push(normalized); - } - currencies.sort((a, b) => a.code.localeCompare(b.code)); - return normalized; - }; - - const replaceCurrencyList = (records) => { - if (!Array.isArray(records)) { - return; - } - currencies = records - .map((record) => buildCurrencyRecord(record)) - .filter((record) => record !== null) - .sort((a, b) => a.code.localeCompare(b.code)); - }; - - const applyPayload = () => { - if (!dataElement) { - return; - } - try { - const parsed = JSON.parse(dataElement.textContent || "{}"); - if (parsed && typeof parsed === "object") { - if (parsed.default_currency_code) { - defaultCurrencyCode = uppercaseCode(parsed.default_currency_code); - } - if (parsed.currency_api_base) { - apiBase = normalizeApiBase(parsed.currency_api_base); - } - if (Array.isArray(parsed.currencies)) { - replaceCurrencyList(parsed.currencies); - } - } - } catch (error) { - console.error("Unable to parse currencies payload", error); - } - }; - - const showFeedback = (message, type = "success") => { - if (!feedbackElement) { - return; - } - feedbackElement.textContent = message; - feedbackElement.classList.remove("hidden", "success", "error"); - feedbackElement.classList.add(type); - }; - - const hideFeedback = () => { - if (!feedbackElement) { - return; - } - feedbackElement.classList.add("hidden"); - feedbackElement.classList.remove("success", "error"); - feedbackElement.textContent = ""; - }; - - const setButtonLoading = (button, isLoading) => { - if (!button) { - return; - } - button.disabled = isLoading; - button.classList.toggle("is-loading", isLoading); - }; - - const updateMetrics = () => { - const total = currencies.length; - const active = currencies.filter((item) => item.is_active).length; - const inactive = total - active; - if (metrics.total) { - metrics.total.textContent = String(total); - } - if (metrics.active) { - metrics.active.textContent = String(active); - } - if (metrics.inactive) { - metrics.inactive.textContent = String(inactive); - } - }; - - const renderExistingOptions = ( - selectedCode = existingSelect ? existingSelect.value : "" - ) => { - if (!existingSelect) { - return; - } - const placeholder = existingSelect.querySelector("option[value='']"); - const placeholderClone = placeholder ? placeholder.cloneNode(true) : null; - existingSelect.innerHTML = ""; - if (placeholderClone) { - existingSelect.appendChild(placeholderClone); - } - const fragment = document.createDocumentFragment(); - currencies.forEach((currency) => { - const option = document.createElement("option"); - option.value = currency.code; - option.textContent = currency.name - ? `${currency.name} (${currency.code})` - : currency.code; - if (selectedCode === currency.code) { - option.selected = true; - } - fragment.appendChild(option); - }); - existingSelect.appendChild(fragment); - if ( - selectedCode && - !currencies.some((item) => item.code === selectedCode) - ) { - existingSelect.value = ""; - } - }; - - const renderTable = () => { - if (!tableBody) { - return; - } - tableBody.innerHTML = ""; - if (!currencies.length) { - if (tableEmptyState) { - tableEmptyState.classList.remove("hidden"); - } - return; - } - if (tableEmptyState) { - tableEmptyState.classList.add("hidden"); - } - const fragment = document.createDocumentFragment(); - currencies.forEach((currency) => { - const row = document.createElement("tr"); - - const codeCell = document.createElement("td"); - codeCell.textContent = currency.code; - row.appendChild(codeCell); - - const nameCell = document.createElement("td"); - nameCell.textContent = currency.name || "—"; - row.appendChild(nameCell); - - const symbolCell = document.createElement("td"); - symbolCell.textContent = currency.symbol || "—"; - row.appendChild(symbolCell); - - const statusCell = document.createElement("td"); - statusCell.textContent = currency.is_active ? "Active" : "Inactive"; - if (currency.is_default) { - statusCell.textContent += " (Default)"; - } - row.appendChild(statusCell); - - const actionsCell = document.createElement("td"); - const editButton = document.createElement("button"); - editButton.type = "button"; - editButton.className = "btn"; - editButton.dataset.action = "edit"; - editButton.dataset.code = currency.code; - editButton.textContent = "Edit"; - editButton.style.marginRight = "0.5rem"; - - const toggleButton = document.createElement("button"); - toggleButton.type = "button"; - toggleButton.className = "btn"; - toggleButton.dataset.action = "toggle"; - toggleButton.dataset.code = currency.code; - toggleButton.textContent = currency.is_active ? "Deactivate" : "Activate"; - if (currency.is_default && currency.is_active) { - toggleButton.disabled = true; - toggleButton.title = "The default currency must remain active."; - } - - actionsCell.appendChild(editButton); - actionsCell.appendChild(toggleButton); - - row.appendChild(actionsCell); - fragment.appendChild(row); - }); - tableBody.appendChild(fragment); - }; - - const refreshUI = (selectedCode) => { - currencies.sort((a, b) => a.code.localeCompare(b.code)); - renderTable(); - renderExistingOptions(selectedCode); - updateMetrics(); - }; - - const findCurrency = (code) => - currencies.find((item) => item.code === code) || null; - - const setFormForCurrency = (currency) => { - if (!form || !codeInput || !nameInput || !symbolInput || !statusSelect) { - return; - } - if (!currency) { - form.reset(); - if (existingSelect) { - existingSelect.value = ""; - } - codeInput.readOnly = false; - codeInput.value = ""; - nameInput.value = ""; - symbolInput.value = ""; - statusSelect.disabled = false; - statusSelect.value = "true"; - statusSelect.title = ""; - return; - } - - if (existingSelect) { - existingSelect.value = currency.code; - } - codeInput.readOnly = true; - codeInput.value = currency.code; - nameInput.value = currency.name || ""; - symbolInput.value = currency.symbol || ""; - statusSelect.value = currency.is_active ? "true" : "false"; - if (currency.is_default) { - statusSelect.disabled = true; - statusSelect.value = "true"; - statusSelect.title = "The default currency must remain active."; - } else { - statusSelect.disabled = false; - statusSelect.title = ""; - } - }; - - const resetFormState = () => { - setFormForCurrency(null); - }; - - const parseError = async (response, fallbackMessage) => { - try { - const detail = await response.json(); - if (detail && typeof detail === "object" && detail.detail) { - return detail.detail; - } - } catch (error) { - // ignore JSON parse errors - } - return fallbackMessage; - }; - - const fetchCurrenciesFromApi = async () => { - const url = `${apiBase}/?include_inactive=true`; - try { - const response = await fetch(url); - if (!response.ok) { - return; - } - const list = await response.json(); - if (Array.isArray(list)) { - replaceCurrencyList(list); - refreshUI(existingSelect ? existingSelect.value : undefined); - } - } catch (error) { - console.warn("Unable to refresh currency list", error); - } - }; - - const handleSubmit = async (event) => { - event.preventDefault(); - hideFeedback(); - if (!form || !codeInput || !nameInput || !statusSelect) { - return; - } - - const editingCode = existingSelect - ? uppercaseCode(existingSelect.value) - : ""; - const codeValue = uppercaseCode(codeInput.value); - const nameValue = (nameInput.value || "").trim(); - const symbolValue = normalizeSymbol(symbolInput ? symbolInput.value : ""); - const isActive = statusSelect.value !== "false"; - - if (!nameValue) { - showFeedback("Provide a currency name.", "error"); - return; - } - - if (!editingCode) { - if (!codeValue || codeValue.length !== 3) { - showFeedback("Provide a three-letter currency code.", "error"); - return; - } - } - - const payload = editingCode - ? { - name: nameValue, - symbol: symbolValue, - is_active: isActive, - } - : { - code: codeValue, - name: nameValue, - symbol: symbolValue, - is_active: isActive, - }; - - const targetCode = editingCode || codeValue; - const url = editingCode - ? `${apiBase}/${encodeURIComponent(editingCode)}` - : `${apiBase}/`; - - setButtonLoading(saveButton, true); - try { - const response = await fetch(url, { - method: editingCode ? "PUT" : "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(payload), - }); - - if (!response.ok) { - const message = await parseError( - response, - editingCode - ? "Unable to update the currency." - : "Unable to create the currency." - ); - throw new Error(message); - } - - const result = await response.json(); - const updated = upsertCurrency(result); - defaultCurrencyCode = uppercaseCode(defaultCurrencyCode); - refreshUI(updated ? updated.code : targetCode); - - if (editingCode) { - showFeedback("Currency updated successfully."); - if (updated) { - setFormForCurrency(updated); - } - } else { - showFeedback("Currency created successfully."); - resetFormState(); - } - } catch (error) { - showFeedback(error.message || "An unexpected error occurred.", "error"); - } finally { - setButtonLoading(saveButton, false); - } - }; - - const handleToggle = async (code, button) => { - const record = findCurrency(code); - if (!record) { - return; - } - hideFeedback(); - const nextState = !record.is_active; - const url = `${apiBase}/${encodeURIComponent(code)}/activation`; - setButtonLoading(button, true); - try { - const response = await fetch(url, { - method: "PATCH", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ is_active: nextState }), - }); - - if (!response.ok) { - const message = await parseError( - response, - nextState - ? "Unable to activate the currency." - : "Unable to deactivate the currency." - ); - throw new Error(message); - } - - const result = await response.json(); - const updated = upsertCurrency(result); - refreshUI(updated ? updated.code : code); - if (existingSelect && existingSelect.value === code && updated) { - setFormForCurrency(updated); - } - const actionMessage = nextState - ? `Currency ${code} activated.` - : `Currency ${code} deactivated.`; - showFeedback(actionMessage); - } catch (error) { - showFeedback(error.message || "An unexpected error occurred.", "error"); - } finally { - setButtonLoading(button, false); - } - }; - - const handleTableClick = (event) => { - const button = event.target.closest("button[data-action]"); - if (!button) { - return; - } - const code = uppercaseCode(button.dataset.code); - const action = button.dataset.action; - if (!code || !action) { - return; - } - if (action === "edit") { - const currency = findCurrency(code); - if (currency) { - setFormForCurrency(currency); - hideFeedback(); - if (nameInput) { - nameInput.focus(); - } - } - } else if (action === "toggle") { - handleToggle(code, button); - } - }; - - applyPayload(); - if (editorSection && editorSection.dataset.defaultCode) { - defaultCurrencyCode = uppercaseCode(editorSection.dataset.defaultCode); - currencies = currencies.map((record) => { - return record - ? { - ...record, - is_default: record.code === defaultCurrencyCode, - } - : record; - }); - } - apiBase = normalizeApiBase(apiBase); - - refreshUI(); - - if (form) { - form.addEventListener("submit", handleSubmit); - } - - if (existingSelect) { - existingSelect.addEventListener("change", (event) => { - const selectedCode = uppercaseCode(event.target.value); - if (!selectedCode) { - hideFeedback(); - resetFormState(); - return; - } - const currency = findCurrency(selectedCode); - if (currency) { - setFormForCurrency(currency); - hideFeedback(); - } - }); - } - - if (resetButton) { - resetButton.addEventListener("click", (event) => { - event.preventDefault(); - hideFeedback(); - resetFormState(); - }); - } - - if (codeInput) { - codeInput.addEventListener("input", () => { - const value = uppercaseCode(codeInput.value).slice(0, 3); - codeInput.value = value; - }); - } - - if (tableBody) { - tableBody.addEventListener("click", handleTableClick); - } - - fetchCurrenciesFromApi(); -}); diff --git a/static/js/dashboard.js b/static/js/dashboard.js deleted file mode 100644 index 5cec954..0000000 --- a/static/js/dashboard.js +++ /dev/null @@ -1,289 +0,0 @@ -(() => { - const dataElement = document.getElementById("dashboard-data"); - if (!dataElement) { - return; - } - - let state = {}; - try { - state = JSON.parse(dataElement.textContent || "{}"); - } catch (error) { - console.error("Failed to parse dashboard data", error); - return; - } - - const statusElement = document.getElementById("dashboard-status"); - const summaryContainer = document.getElementById("summary-metrics"); - const summaryEmpty = document.getElementById("summary-empty"); - const scenarioTableBody = document.querySelector("#scenario-table tbody"); - const scenarioEmpty = document.getElementById("scenario-table-empty"); - const overallMetricsList = document.getElementById("overall-metrics"); - const overallMetricsEmpty = document.getElementById("overall-metrics-empty"); - const recentList = document.getElementById("recent-simulations"); - const recentEmpty = document.getElementById("recent-simulations-empty"); - const maintenanceList = document.getElementById("upcoming-maintenance"); - const maintenanceEmpty = document.getElementById( - "upcoming-maintenance-empty" - ); - const refreshButton = document.getElementById("refresh-dashboard"); - const costChartCanvas = document.getElementById("cost-chart"); - const costChartEmpty = document.getElementById("cost-chart-empty"); - const activityChartCanvas = document.getElementById("activity-chart"); - const activityChartEmpty = document.getElementById("activity-chart-empty"); - - let costChartInstance = null; - let activityChartInstance = null; - - const setStatus = (message, variant = "success") => { - if (!statusElement) { - return; - } - if (!message) { - statusElement.hidden = true; - statusElement.textContent = ""; - statusElement.classList.remove("success", "error"); - return; - } - statusElement.textContent = message; - statusElement.hidden = false; - statusElement.classList.toggle("success", variant === "success"); - statusElement.classList.toggle("error", variant !== "success"); - }; - - const renderSummaryMetrics = () => { - if (!summaryContainer || !summaryEmpty) { - return; - } - summaryContainer.innerHTML = ""; - const metrics = Array.isArray(state.summary_metrics) - ? state.summary_metrics - : []; - metrics.forEach((metric) => { - const card = document.createElement("article"); - card.className = "metric-card"; - card.innerHTML = ` - ${metric.label} - ${metric.value} - `; - summaryContainer.appendChild(card); - }); - summaryEmpty.hidden = metrics.length > 0; - }; - - const renderScenarioTable = () => { - if (!scenarioTableBody || !scenarioEmpty) { - return; - } - scenarioTableBody.innerHTML = ""; - const rows = Array.isArray(state.scenario_rows) ? state.scenario_rows : []; - rows.forEach((row) => { - const tr = document.createElement("tr"); - tr.innerHTML = ` - ${row.scenario_name} - ${row.parameter_display} - ${row.equipment_display} - ${row.capex_display} - ${row.opex_display} - ${row.production_display} - ${row.consumption_display} - ${row.maintenance_display} - ${row.iterations_display} - ${row.simulation_mean_display} - `; - scenarioTableBody.appendChild(tr); - }); - scenarioEmpty.hidden = rows.length > 0; - }; - - const renderOverallMetrics = () => { - if (!overallMetricsList || !overallMetricsEmpty) { - return; - } - overallMetricsList.innerHTML = ""; - const items = Array.isArray(state.overall_report_metrics) - ? state.overall_report_metrics - : []; - items.forEach((item) => { - const li = document.createElement("li"); - li.className = "metric-list-item"; - li.textContent = `${item.label}: ${item.value}`; - overallMetricsList.appendChild(li); - }); - overallMetricsEmpty.hidden = items.length > 0; - }; - - const renderRecentSimulations = () => { - if (!recentList || !recentEmpty) { - return; - } - recentList.innerHTML = ""; - const runs = Array.isArray(state.recent_simulations) - ? state.recent_simulations - : []; - runs.forEach((run) => { - const item = document.createElement("li"); - item.className = "metric-list-item"; - item.textContent = `${run.scenario_name} · ${run.iterations_display} iterations · ${run.mean_display}`; - recentList.appendChild(item); - }); - recentEmpty.hidden = runs.length > 0; - }; - - const renderMaintenanceReminders = () => { - if (!maintenanceList || !maintenanceEmpty) { - return; - } - maintenanceList.innerHTML = ""; - const items = Array.isArray(state.upcoming_maintenance) - ? state.upcoming_maintenance - : []; - items.forEach((item) => { - const li = document.createElement("li"); - li.innerHTML = ` - ${item.equipment_name} · ${item.scenario_name} - ${item.date_display} · ${item.cost_display} · ${item.description} - `; - maintenanceList.appendChild(li); - }); - maintenanceEmpty.hidden = items.length > 0; - }; - - const buildChartConfig = (dataset, overrides = {}) => ({ - type: dataset.type || "bar", - data: { - labels: dataset.labels || [], - datasets: dataset.datasets || [], - }, - options: Object.assign( - { - responsive: true, - maintainAspectRatio: false, - plugins: { - legend: { position: "top" }, - tooltip: { enabled: true }, - }, - scales: { - x: { stacked: dataset.stacked ?? false }, - y: { stacked: dataset.stacked ?? false, beginAtZero: true }, - }, - }, - overrides.options || {} - ), - }); - - const renderCharts = () => { - if (costChartInstance) { - costChartInstance.destroy(); - } - if (activityChartInstance) { - activityChartInstance.destroy(); - } - - const costData = state.scenario_cost_chart || {}; - const activityData = state.scenario_activity_chart || {}; - - if (costChartCanvas && state.cost_chart_has_data) { - costChartInstance = new Chart( - costChartCanvas, - buildChartConfig(costData, { - options: { - scales: { - y: { - beginAtZero: true, - ticks: { - callback: (value) => - typeof value === "number" - ? value.toLocaleString(undefined, { - maximumFractionDigits: 0, - }) - : value, - }, - }, - }, - }, - }) - ); - if (costChartEmpty) { - costChartEmpty.hidden = true; - } - costChartCanvas.classList.remove("hidden"); - } else if (costChartEmpty && costChartCanvas) { - costChartEmpty.hidden = false; - costChartCanvas.classList.add("hidden"); - } - - if (activityChartCanvas && state.activity_chart_has_data) { - activityChartInstance = new Chart( - activityChartCanvas, - buildChartConfig(activityData, { - options: { - scales: { - y: { - beginAtZero: true, - ticks: { - callback: (value) => - typeof value === "number" - ? value.toLocaleString(undefined, { - maximumFractionDigits: 0, - }) - : value, - }, - }, - }, - }, - }) - ); - if (activityChartEmpty) { - activityChartEmpty.hidden = true; - } - activityChartCanvas.classList.remove("hidden"); - } else if (activityChartEmpty && activityChartCanvas) { - activityChartEmpty.hidden = false; - activityChartCanvas.classList.add("hidden"); - } - }; - - const renderView = () => { - renderSummaryMetrics(); - renderScenarioTable(); - renderOverallMetrics(); - renderRecentSimulations(); - renderMaintenanceReminders(); - renderCharts(); - }; - - const refreshDashboard = async () => { - setStatus("Refreshing dashboard…", "success"); - if (refreshButton) { - refreshButton.classList.add("is-loading"); - } - - try { - const response = await fetch("/ui/dashboard/data", { - headers: { "X-Requested-With": "XMLHttpRequest" }, - }); - - if (!response.ok) { - throw new Error("Unable to refresh dashboard data."); - } - - const payload = await response.json(); - state = payload || {}; - renderView(); - setStatus("Dashboard updated.", "success"); - } catch (error) { - console.error(error); - setStatus(error.message || "Failed to refresh dashboard.", "error"); - } finally { - if (refreshButton) { - refreshButton.classList.remove("is-loading"); - } - } - }; - - renderView(); - - if (refreshButton) { - refreshButton.addEventListener("click", refreshDashboard); - } -})(); diff --git a/static/js/equipment.js b/static/js/equipment.js deleted file mode 100644 index cf2c56d..0000000 --- a/static/js/equipment.js +++ /dev/null @@ -1,145 +0,0 @@ -document.addEventListener("DOMContentLoaded", () => { - const dataElement = document.getElementById("equipment-data"); - let equipmentByScenario = {}; - - if (dataElement) { - try { - const parsed = JSON.parse(dataElement.textContent || "{}"); - if (parsed && typeof parsed === "object") { - if (parsed.equipment && typeof parsed.equipment === "object") { - equipmentByScenario = parsed.equipment; - } - } - } catch (error) { - console.error("Unable to parse equipment data", error); - } - } - - const filterSelect = document.getElementById("equipment-scenario-filter"); - const tableWrapper = document.getElementById("equipment-table-wrapper"); - const tableBody = document.getElementById("equipment-table-body"); - const emptyState = document.getElementById("equipment-empty"); - const form = document.getElementById("equipment-form"); - const feedbackEl = document.getElementById("equipment-feedback"); - - const showFeedback = (message, type = "success") => { - if (!feedbackEl) { - return; - } - feedbackEl.textContent = message; - feedbackEl.classList.remove("hidden", "success", "error"); - feedbackEl.classList.add(type); - }; - - const hideFeedback = () => { - if (!feedbackEl) { - return; - } - feedbackEl.classList.add("hidden"); - feedbackEl.textContent = ""; - }; - - const renderEquipmentRows = (scenarioId) => { - if (!tableBody || !tableWrapper || !emptyState) { - return; - } - - const key = String(scenarioId); - const records = equipmentByScenario[key] || []; - - tableBody.innerHTML = ""; - - if (!records.length) { - emptyState.textContent = "No equipment recorded for this scenario yet."; - emptyState.classList.remove("hidden"); - tableWrapper.classList.add("hidden"); - return; - } - - emptyState.classList.add("hidden"); - tableWrapper.classList.remove("hidden"); - - records.forEach((record) => { - const row = document.createElement("tr"); - row.innerHTML = ` - ${record.name || "—"} - ${record.description || "—"} - `; - tableBody.appendChild(row); - }); - }; - - if (filterSelect) { - filterSelect.addEventListener("change", (event) => { - const value = event.target.value; - if (!value) { - if (emptyState && tableWrapper && tableBody) { - emptyState.textContent = - "Choose a scenario to review the equipment list."; - emptyState.classList.remove("hidden"); - tableWrapper.classList.add("hidden"); - tableBody.innerHTML = ""; - } - return; - } - renderEquipmentRows(value); - }); - } - - const submitEquipment = async (event) => { - event.preventDefault(); - hideFeedback(); - - if (!form) { - return; - } - - const formData = new FormData(form); - const scenarioId = formData.get("scenario_id"); - const payload = { - scenario_id: scenarioId ? Number(scenarioId) : null, - name: formData.get("name"), - description: formData.get("description") || null, - }; - - try { - const response = await fetch("/api/equipment/", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(payload), - }); - - if (!response.ok) { - const errorDetail = await response.json().catch(() => ({})); - throw new Error( - errorDetail.detail || "Unable to add equipment record." - ); - } - - const result = await response.json(); - const mapKey = String(result.scenario_id); - - if (!Array.isArray(equipmentByScenario[mapKey])) { - equipmentByScenario[mapKey] = []; - } - equipmentByScenario[mapKey].push(result); - - form.reset(); - showFeedback("Equipment saved.", "success"); - - if (filterSelect && filterSelect.value === String(result.scenario_id)) { - renderEquipmentRows(filterSelect.value); - } - } catch (error) { - showFeedback(error.message || "An unexpected error occurred.", "error"); - } - }; - - if (form) { - form.addEventListener("submit", submitEquipment); - } - - if (filterSelect && filterSelect.value) { - renderEquipmentRows(filterSelect.value); - } -}); diff --git a/static/js/maintenance.js b/static/js/maintenance.js deleted file mode 100644 index 9ec5f4a..0000000 --- a/static/js/maintenance.js +++ /dev/null @@ -1,243 +0,0 @@ -document.addEventListener("DOMContentLoaded", () => { - const dataElement = document.getElementById("maintenance-data"); - let equipmentByScenario = {}; - let maintenanceByScenario = {}; - - if (dataElement) { - try { - const parsed = JSON.parse(dataElement.textContent || "{}"); - if (parsed && typeof parsed === "object") { - if (parsed.equipment && typeof parsed.equipment === "object") { - equipmentByScenario = parsed.equipment; - } - if (parsed.maintenance && typeof parsed.maintenance === "object") { - maintenanceByScenario = parsed.maintenance; - } - } - } catch (error) { - console.error("Unable to parse maintenance data", error); - } - } - - const filterSelect = document.getElementById("maintenance-scenario-filter"); - const tableWrapper = document.getElementById("maintenance-table-wrapper"); - const tableBody = document.getElementById("maintenance-table-body"); - const emptyState = document.getElementById("maintenance-empty"); - const form = document.getElementById("maintenance-form"); - const feedbackEl = document.getElementById("maintenance-feedback"); - const formScenarioSelect = document.getElementById( - "maintenance-form-scenario" - ); - const equipmentSelect = document.getElementById("maintenance-form-equipment"); - const equipmentEmptyState = document.getElementById( - "maintenance-equipment-empty" - ); - - const showFeedback = (message, type = "success") => { - if (!feedbackEl) { - return; - } - feedbackEl.textContent = message; - feedbackEl.classList.remove("hidden", "success", "error"); - feedbackEl.classList.add(type); - }; - - const hideFeedback = () => { - if (!feedbackEl) { - return; - } - feedbackEl.classList.add("hidden"); - feedbackEl.textContent = ""; - }; - - const formatCost = (value) => - Number(value).toLocaleString(undefined, { - minimumFractionDigits: 2, - maximumFractionDigits: 2, - }); - - const formatDate = (value) => { - if (!value) { - return "—"; - } - const parsed = new Date(value); - if (Number.isNaN(parsed.getTime())) { - return value; - } - return parsed.toLocaleDateString(); - }; - - const renderMaintenanceRows = (scenarioId) => { - if (!tableBody || !tableWrapper || !emptyState) { - return; - } - - const key = String(scenarioId); - const records = maintenanceByScenario[key] || []; - - tableBody.innerHTML = ""; - - if (!records.length) { - emptyState.textContent = - "No maintenance entries recorded for this scenario yet."; - emptyState.classList.remove("hidden"); - tableWrapper.classList.add("hidden"); - return; - } - - emptyState.classList.add("hidden"); - tableWrapper.classList.remove("hidden"); - - records.forEach((record) => { - const row = document.createElement("tr"); - row.innerHTML = ` - ${formatDate(record.maintenance_date)} - ${record.equipment_name || "—"} - ${formatCost(record.cost)} - ${record.description || "—"} - `; - tableBody.appendChild(row); - }); - }; - - const populateEquipmentOptions = (scenarioId) => { - if (!equipmentSelect) { - return; - } - - equipmentSelect.innerHTML = - ''; - equipmentSelect.disabled = true; - - if (equipmentEmptyState) { - equipmentEmptyState.classList.add("hidden"); - } - - if (!scenarioId) { - return; - } - - const list = equipmentByScenario[String(scenarioId)] || []; - if (!list.length) { - if (equipmentEmptyState) { - equipmentEmptyState.textContent = - "Add equipment for this scenario before scheduling maintenance."; - equipmentEmptyState.classList.remove("hidden"); - } - return; - } - - list.forEach((item) => { - const option = document.createElement("option"); - option.value = item.id; - option.textContent = item.name || `Equipment ${item.id}`; - equipmentSelect.appendChild(option); - }); - - equipmentSelect.disabled = false; - }; - - if (filterSelect) { - filterSelect.addEventListener("change", (event) => { - const value = event.target.value; - if (!value) { - if (emptyState && tableWrapper && tableBody) { - emptyState.textContent = - "Choose a scenario to review upcoming or completed maintenance."; - emptyState.classList.remove("hidden"); - tableWrapper.classList.add("hidden"); - tableBody.innerHTML = ""; - } - return; - } - renderMaintenanceRows(value); - }); - } - - if (formScenarioSelect) { - formScenarioSelect.addEventListener("change", (event) => { - const value = event.target.value; - populateEquipmentOptions(value); - }); - } - - const submitMaintenance = async (event) => { - event.preventDefault(); - hideFeedback(); - - if (!form) { - return; - } - - const formData = new FormData(form); - const scenarioId = formData.get("scenario_id"); - const equipmentId = formData.get("equipment_id"); - const payload = { - scenario_id: scenarioId ? Number(scenarioId) : null, - equipment_id: equipmentId ? Number(equipmentId) : null, - maintenance_date: formData.get("maintenance_date"), - cost: Number(formData.get("cost")), - description: formData.get("description") || null, - }; - - if (!payload.scenario_id || !payload.equipment_id) { - showFeedback( - "Select a scenario and equipment before submitting.", - "error" - ); - return; - } - - try { - const response = await fetch("/api/maintenance/", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(payload), - }); - - if (!response.ok) { - const errorDetail = await response.json().catch(() => ({})); - throw new Error( - errorDetail.detail || "Unable to add maintenance entry." - ); - } - - const result = await response.json(); - const mapKey = String(result.scenario_id); - - if (!Array.isArray(maintenanceByScenario[mapKey])) { - maintenanceByScenario[mapKey] = []; - } - - const equipmentList = equipmentByScenario[mapKey] || []; - const matchedEquipment = equipmentList.find( - (item) => Number(item.id) === Number(result.equipment_id) - ); - result.equipment_name = matchedEquipment ? matchedEquipment.name : ""; - - maintenanceByScenario[mapKey].push(result); - - form.reset(); - populateEquipmentOptions(null); - showFeedback("Maintenance entry saved.", "success"); - - if (filterSelect && filterSelect.value === String(result.scenario_id)) { - renderMaintenanceRows(filterSelect.value); - } - } catch (error) { - showFeedback(error.message || "An unexpected error occurred.", "error"); - } - }; - - if (form) { - form.addEventListener("submit", submitMaintenance); - } - - if (filterSelect && filterSelect.value) { - renderMaintenanceRows(filterSelect.value); - } - - if (formScenarioSelect && formScenarioSelect.value) { - populateEquipmentOptions(formScenarioSelect.value); - } -}); diff --git a/static/js/parameters.js b/static/js/parameters.js deleted file mode 100644 index b96d207..0000000 --- a/static/js/parameters.js +++ /dev/null @@ -1,124 +0,0 @@ -document.addEventListener("DOMContentLoaded", () => { - const dataElement = document.getElementById("parameters-data"); - let parametersByScenario = {}; - - if (dataElement) { - try { - const parsed = JSON.parse(dataElement.textContent || "{}"); - if (parsed && typeof parsed === "object") { - parametersByScenario = parsed; - } - } catch (error) { - console.error("Unable to parse parameter data", error); - } - } - - const form = document.getElementById("parameter-form"); - const scenarioSelect = /** @type {HTMLSelectElement | null} */ ( - document.getElementById("scenario_id") - ); - const nameInput = /** @type {HTMLInputElement | null} */ ( - document.getElementById("name") - ); - const valueInput = /** @type {HTMLInputElement | null} */ ( - document.getElementById("value") - ); - const feedback = document.getElementById("parameter-feedback"); - const tableBody = document.getElementById("parameter-table-body"); - - const setFeedback = (message, variant) => { - if (!feedback) { - return; - } - feedback.textContent = message; - feedback.classList.remove("success", "error"); - if (variant) { - feedback.classList.add(variant); - } - }; - - const renderTable = (scenarioId) => { - if (!tableBody) { - return; - } - tableBody.innerHTML = ""; - const rows = parametersByScenario[String(scenarioId)] || []; - if (!rows.length) { - const emptyRow = document.createElement("tr"); - emptyRow.id = "parameter-empty-state"; - emptyRow.innerHTML = - 'No parameters recorded for this scenario yet.'; - tableBody.appendChild(emptyRow); - return; - } - rows.forEach((row) => { - const tr = document.createElement("tr"); - tr.innerHTML = ` - ${row.name} - ${row.value} - ${row.distribution_type ?? "—"} - ${ - row.distribution_parameters - ? JSON.stringify(row.distribution_parameters) - : "—" - } - `; - tableBody.appendChild(tr); - }); - }; - - if (scenarioSelect) { - renderTable(scenarioSelect.value); - scenarioSelect.addEventListener("change", () => - renderTable(scenarioSelect.value) - ); - } - - if (!form || !scenarioSelect || !nameInput || !valueInput) { - return; - } - - form.addEventListener("submit", async (event) => { - event.preventDefault(); - - const scenarioId = scenarioSelect.value; - const payload = { - scenario_id: Number(scenarioId), - name: nameInput.value.trim(), - value: Number(valueInput.value), - }; - - if (!payload.name) { - setFeedback("Parameter name is required.", "error"); - return; - } - - if (!Number.isFinite(payload.value)) { - setFeedback("Enter a numeric value.", "error"); - return; - } - - const response = await fetch("/api/parameters/", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(payload), - }); - - if (!response.ok) { - const errorText = await response.text(); - setFeedback(`Error saving parameter: ${errorText}`, "error"); - return; - } - - const data = await response.json(); - const scenarioKey = String(scenarioId); - parametersByScenario[scenarioKey] = parametersByScenario[scenarioKey] || []; - parametersByScenario[scenarioKey].push(data); - - form.reset(); - scenarioSelect.value = scenarioKey; - renderTable(scenarioKey); - nameInput.focus(); - setFeedback("Parameter saved.", "success"); - }); -}); diff --git a/static/js/production.js b/static/js/production.js deleted file mode 100644 index 9d19a41..0000000 --- a/static/js/production.js +++ /dev/null @@ -1,204 +0,0 @@ -document.addEventListener("DOMContentLoaded", () => { - const dataElement = document.getElementById("production-data"); - let data = { scenarios: [], production: {}, unit_options: [] }; - - if (dataElement) { - try { - const parsed = JSON.parse(dataElement.textContent || "{}"); - if (parsed && typeof parsed === "object") { - data = { - scenarios: Array.isArray(parsed.scenarios) ? parsed.scenarios : [], - production: - parsed.production && typeof parsed.production === "object" - ? parsed.production - : {}, - unit_options: Array.isArray(parsed.unit_options) - ? parsed.unit_options - : [], - }; - } - } catch (error) { - console.error("Unable to parse production data", error); - } - } - - const productionByScenario = data.production; - const filterSelect = document.getElementById("production-scenario-filter"); - const tableWrapper = document.getElementById("production-table-wrapper"); - const tableBody = document.getElementById("production-table-body"); - const emptyState = document.getElementById("production-empty"); - const form = document.getElementById("production-form"); - const feedbackEl = document.getElementById("production-feedback"); - const unitSelect = document.getElementById("production-form-unit"); - const unitSymbolInput = document.getElementById("production-form-unit-symbol"); - - const showFeedback = (message, type = "success") => { - if (!feedbackEl) { - return; - } - feedbackEl.textContent = message; - feedbackEl.classList.remove("hidden", "success", "error"); - feedbackEl.classList.add(type); - }; - - const hideFeedback = () => { - if (!feedbackEl) { - return; - } - feedbackEl.classList.add("hidden"); - feedbackEl.textContent = ""; - }; - - const formatAmount = (value) => - Number(value).toLocaleString(undefined, { - minimumFractionDigits: 2, - maximumFractionDigits: 2, - }); - - const formatMeasurement = (amount, symbol, name) => { - if (symbol) { - return `${formatAmount(amount)} ${symbol}`; - } - if (name) { - return `${formatAmount(amount)} ${name}`; - } - return formatAmount(amount); - }; - - const renderProductionRows = (scenarioId) => { - if (!tableBody || !tableWrapper || !emptyState) { - return; - } - - const key = String(scenarioId); - const records = productionByScenario[key] || []; - - tableBody.innerHTML = ""; - - if (!records.length) { - emptyState.textContent = - "No production output recorded for this scenario yet."; - emptyState.classList.remove("hidden"); - tableWrapper.classList.add("hidden"); - return; - } - - emptyState.classList.add("hidden"); - tableWrapper.classList.remove("hidden"); - - records.forEach((record) => { - const row = document.createElement("tr"); - row.innerHTML = ` - ${formatMeasurement( - record.amount, - record.unit_symbol, - record.unit_name - )} - ${record.description || "—"} - `; - tableBody.appendChild(row); - }); - }; - - if (filterSelect) { - filterSelect.addEventListener("change", (event) => { - const value = event.target.value; - if (!value) { - if (emptyState && tableWrapper && tableBody) { - emptyState.textContent = - "Choose a scenario to review its production output."; - emptyState.classList.remove("hidden"); - tableWrapper.classList.add("hidden"); - tableBody.innerHTML = ""; - } - return; - } - renderProductionRows(value); - }); - } - - const submitProduction = async (event) => { - event.preventDefault(); - hideFeedback(); - - if (!form) { - return; - } - - const formData = new FormData(form); - const scenarioId = formData.get("scenario_id"); - const unitName = formData.get("unit_name"); - const unitSymbol = formData.get("unit_symbol"); - const payload = { - scenario_id: scenarioId ? Number(scenarioId) : null, - amount: Number(formData.get("amount")), - description: formData.get("description") || null, - unit_name: unitName ? String(unitName) : null, - unit_symbol: unitSymbol ? String(unitSymbol) : null, - }; - - try { - const response = await fetch("/api/production/", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(payload), - }); - - if (!response.ok) { - const errorDetail = await response.json().catch(() => ({})); - throw new Error( - errorDetail.detail || "Unable to add production output record." - ); - } - - const result = await response.json(); - const mapKey = String(result.scenario_id); - - if (!Array.isArray(productionByScenario[mapKey])) { - productionByScenario[mapKey] = []; - } - productionByScenario[mapKey].push(result); - - form.reset(); - syncUnitSelection(); - showFeedback("Production output saved.", "success"); - - if (filterSelect && filterSelect.value === String(result.scenario_id)) { - renderProductionRows(filterSelect.value); - } - } catch (error) { - showFeedback(error.message || "An unexpected error occurred.", "error"); - } - }; - - if (form) { - form.addEventListener("submit", submitProduction); - } - - const syncUnitSelection = () => { - if (!unitSelect || !unitSymbolInput) { - return; - } - if (!unitSelect.value && unitSelect.options.length > 0) { - const firstOption = Array.from(unitSelect.options).find( - (option) => option.value - ); - if (firstOption) { - firstOption.selected = true; - } - } - const selectedOption = unitSelect.options[unitSelect.selectedIndex]; - unitSymbolInput.value = selectedOption - ? selectedOption.getAttribute("data-symbol") || "" - : ""; - }; - - if (unitSelect) { - unitSelect.addEventListener("change", syncUnitSelection); - syncUnitSelection(); - } - - if (filterSelect && filterSelect.value) { - renderProductionRows(filterSelect.value); - } -}); diff --git a/static/js/reporting.js b/static/js/reporting.js deleted file mode 100644 index 3ca2f64..0000000 --- a/static/js/reporting.js +++ /dev/null @@ -1,149 +0,0 @@ -document.addEventListener("DOMContentLoaded", () => { - const dataElement = document.getElementById("reporting-data"); - let reportingSummaries = []; - - if (dataElement) { - try { - const parsed = JSON.parse(dataElement.textContent || "[]"); - if (Array.isArray(parsed)) { - reportingSummaries = parsed; - } - } catch (error) { - console.error("Unable to parse reporting data", error); - } - } - - const REPORT_FIELDS = [ - { key: "iterations", label: "Iterations", decimals: 0 }, - { key: "mean", label: "Mean Result", decimals: 2 }, - { key: "variance", label: "Variance", decimals: 2 }, - { key: "std_dev", label: "Std. Dev", decimals: 2 }, - { key: "percentile_5", label: "Percentile 5", decimals: 2 }, - { key: "percentile_95", label: "Percentile 95", decimals: 2 }, - { key: "value_at_risk_95", label: "Value at Risk (95%)", decimals: 2 }, - { - key: "expected_shortfall_95", - label: "Expected Shortfall (95%)", - decimals: 2, - }, - ]; - - const tableWrapper = document.getElementById("reporting-table-wrapper"); - const tableBody = document.getElementById("reporting-table-body"); - const emptyState = document.getElementById("reporting-empty"); - const refreshButton = document.getElementById("report-refresh"); - const feedbackEl = document.getElementById("report-feedback"); - - const formatNumber = (value, decimals = 2) => { - if (value === null || value === undefined || Number.isNaN(Number(value))) { - return "—"; - } - return Number(value).toLocaleString(undefined, { - minimumFractionDigits: decimals, - maximumFractionDigits: decimals, - }); - }; - - const showFeedback = (message, type = "success") => { - if (!feedbackEl) { - return; - } - feedbackEl.textContent = message; - feedbackEl.classList.remove("hidden", "success", "error"); - feedbackEl.classList.add(type); - }; - - const hideFeedback = () => { - if (!feedbackEl) { - return; - } - feedbackEl.classList.add("hidden"); - feedbackEl.textContent = ""; - }; - - const renderReportingTable = (summaryData) => { - if (!tableBody || !tableWrapper || !emptyState) { - return; - } - - tableBody.innerHTML = ""; - - if (!summaryData.length) { - emptyState.classList.remove("hidden"); - tableWrapper.classList.add("hidden"); - return; - } - - emptyState.classList.add("hidden"); - tableWrapper.classList.remove("hidden"); - - summaryData.forEach((entry) => { - const row = document.createElement("tr"); - const scenarioCell = document.createElement("td"); - scenarioCell.textContent = entry.scenario_name; - row.appendChild(scenarioCell); - - REPORT_FIELDS.forEach((field) => { - const cell = document.createElement("td"); - const source = field.key === "iterations" ? entry : entry.summary || {}; - cell.textContent = formatNumber(source[field.key], field.decimals); - row.appendChild(cell); - }); - - tableBody.appendChild(row); - }); - }; - - const refreshMetrics = async () => { - hideFeedback(); - showFeedback("Refreshing metrics…", "success"); - - try { - const response = await fetch("/ui/reporting", { - method: "GET", - headers: { "X-Requested-With": "XMLHttpRequest" }, - }); - - if (!response.ok) { - throw new Error("Unable to refresh reporting data."); - } - - const text = await response.text(); - const parser = new DOMParser(); - const doc = parser.parseFromString(text, "text/html"); - const newTable = doc.querySelector("#reporting-table-wrapper"); - const newFeedback = doc.querySelector("#report-feedback"); - - if (!newTable) { - throw new Error("Unexpected response while refreshing."); - } - - const newEmptyState = doc.querySelector("#reporting-empty"); - - if (emptyState && newEmptyState) { - emptyState.className = newEmptyState.className; - emptyState.textContent = newEmptyState.textContent; - } - - if (tableWrapper) { - tableWrapper.className = newTable.className; - tableWrapper.innerHTML = newTable.innerHTML; - } - - if (newFeedback && feedbackEl) { - feedbackEl.className = newFeedback.className; - feedbackEl.textContent = newFeedback.textContent; - } - - showFeedback("Metrics refreshed.", "success"); - } catch (error) { - showFeedback(error.message || "An unexpected error occurred.", "error"); - } - }; - - renderReportingTable(reportingSummaries); - - if (refreshButton) { - refreshButton.addEventListener("click", refreshMetrics); - } -}); diff --git a/static/js/scenario-form.js b/static/js/scenario-form.js deleted file mode 100644 index f27722a..0000000 --- a/static/js/scenario-form.js +++ /dev/null @@ -1,78 +0,0 @@ -document.addEventListener("DOMContentLoaded", () => { - const form = document.getElementById("scenario-form"); - if (!form) { - return; - } - - const nameInput = /** @type {HTMLInputElement | null} */ ( - document.getElementById("name") - ); - const descriptionInput = /** @type {HTMLInputElement | null} */ ( - document.getElementById("description") - ); - const table = document.getElementById("scenario-table"); - const tableBody = document.getElementById("scenario-table-body"); - const emptyState = document.getElementById("empty-state"); - - form.addEventListener("submit", async (event) => { - event.preventDefault(); - - if (!nameInput || !descriptionInput) { - return; - } - - const payload = { - name: nameInput.value.trim(), - description: descriptionInput.value.trim() || null, - }; - - if (!payload.name) { - return; - } - - const response = await fetch("/api/scenarios/", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(payload), - }); - - if (!response.ok) { - const errorText = await response.text(); - console.error("Scenario creation failed", errorText); - return; - } - - const data = await response.json(); - const row = document.createElement("tr"); - row.dataset.scenarioId = String(data.id); - row.innerHTML = ` - ${data.name} - ${data.description ?? "—"} - `; - - if (emptyState) { - emptyState.remove(); - } - - if (table) { - table.classList.remove("hidden"); - table.removeAttribute("aria-hidden"); - } - - if (tableBody) { - tableBody.appendChild(row); - } - - form.reset(); - nameInput.focus(); - - const feedback = document.getElementById("feedback"); - if (feedback) { - feedback.textContent = `Scenario "${data.name}" created successfully.`; - feedback.classList.remove("hidden"); - setTimeout(() => { - feedback.classList.add("hidden"); - }, 3000); - } - }); -}); diff --git a/static/js/settings.js b/static/js/settings.js deleted file mode 100644 index 8d7d5c6..0000000 --- a/static/js/settings.js +++ /dev/null @@ -1,200 +0,0 @@ -(function () { - const dataScript = document.getElementById("theme-settings-data"); - const form = document.getElementById("theme-settings-form"); - const feedbackEl = document.getElementById("theme-settings-feedback"); - const resetBtn = document.getElementById("theme-settings-reset"); - const panel = document.getElementById("theme-settings"); - - if (!dataScript || !form || !feedbackEl || !panel) { - return; - } - - const apiUrl = panel.getAttribute("data-api"); - if (!apiUrl) { - return; - } - - const parsed = JSON.parse(dataScript.textContent || "{}"); - const currentValues = { ...(parsed.variables || {}) }; - const defaultValues = parsed.defaults || {}; - let envOverrides = { ...(parsed.envOverrides || {}) }; - - const previewElements = new Map(); - const inputs = Array.from(form.querySelectorAll(".color-value-input")); - - inputs.forEach((input) => { - const key = input.name; - const field = input.closest(".color-form-field"); - const preview = field ? field.querySelector(".color-preview") : null; - if (preview) { - previewElements.set(input, preview); - } - - if (Object.prototype.hasOwnProperty.call(envOverrides, key)) { - const overrideValue = envOverrides[key]; - input.value = overrideValue; - input.disabled = true; - input.setAttribute("aria-disabled", "true"); - input.dataset.envOverride = "true"; - if (field) { - field.classList.add("is-env-override"); - } - if (preview) { - preview.style.background = overrideValue; - } - return; - } - - input.addEventListener("input", () => { - const previewEl = previewElements.get(input); - if (previewEl) { - previewEl.style.background = input.value || defaultValues[key] || ""; - } - }); - }); - - function setFeedback(message, type) { - feedbackEl.textContent = message; - feedbackEl.classList.remove("hidden", "success", "error"); - if (type) { - feedbackEl.classList.add(type); - } - } - - function clearFeedback() { - feedbackEl.textContent = ""; - feedbackEl.classList.add("hidden"); - feedbackEl.classList.remove("success", "error"); - } - - function updateRootVariables(values) { - if (!values) { - return; - } - const root = document.documentElement; - Object.entries(values).forEach(([key, value]) => { - if (typeof key === "string" && typeof value === "string") { - root.style.setProperty(key, value); - } - }); - } - - function resetTo(source) { - inputs.forEach((input) => { - const key = input.name; - if (input.disabled) { - const previewEl = previewElements.get(input); - const fallback = envOverrides[key] || currentValues[key]; - if (previewEl && fallback) { - previewEl.style.background = fallback; - } - return; - } - if (Object.prototype.hasOwnProperty.call(source, key)) { - input.value = source[key]; - const previewEl = previewElements.get(input); - if (previewEl) { - previewEl.style.background = source[key]; - } - } - }); - } - - // Initialize previews to current values after page load. - resetTo(currentValues); - - resetBtn?.addEventListener("click", () => { - resetTo(defaultValues); - clearFeedback(); - setFeedback("Reverted to default values. Submit to save.", "success"); - }); - - form.addEventListener("submit", async (event) => { - event.preventDefault(); - clearFeedback(); - - const payload = {}; - inputs.forEach((input) => { - if (input.disabled) { - return; - } - payload[input.name] = input.value.trim(); - }); - - try { - const response = await fetch(apiUrl, { - method: "PUT", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ variables: payload }), - }); - - if (!response.ok) { - let detail = "Unable to save theme settings."; - try { - const errorData = await response.json(); - if (errorData?.detail) { - detail = Array.isArray(errorData.detail) - ? errorData.detail.map((item) => item.msg || item).join("; ") - : errorData.detail; - } - } catch (parseError) { - // Ignore JSON parse errors and use default detail message. - } - setFeedback(detail, "error"); - return; - } - - const data = await response.json(); - const variables = data?.variables || {}; - const responseOverrides = data?.env_overrides || {}; - - Object.assign(currentValues, variables); - envOverrides = { ...responseOverrides }; - - inputs.forEach((input) => { - const key = input.name; - const field = input.closest(".color-form-field"); - const previewEl = previewElements.get(input); - const isOverride = Object.prototype.hasOwnProperty.call( - envOverrides, - key, - ); - - if (isOverride) { - const overrideValue = envOverrides[key]; - input.value = overrideValue; - if (!input.disabled) { - input.disabled = true; - input.setAttribute("aria-disabled", "true"); - } - if (field) { - field.classList.add("is-env-override"); - } - if (previewEl) { - previewEl.style.background = overrideValue; - } - } else if (input.disabled) { - input.disabled = false; - input.removeAttribute("aria-disabled"); - if (field) { - field.classList.remove("is-env-override"); - } - if ( - previewEl && - Object.prototype.hasOwnProperty.call(variables, key) - ) { - previewEl.style.background = variables[key]; - } - } - }); - - updateRootVariables(variables); - resetTo(variables); - setFeedback("Theme colors updated successfully.", "success"); - } catch (error) { - setFeedback("Network error: unable to save settings.", "error"); - } - }); -})(); diff --git a/static/js/simulations.js b/static/js/simulations.js deleted file mode 100644 index 9e9c8d6..0000000 --- a/static/js/simulations.js +++ /dev/null @@ -1,354 +0,0 @@ -document.addEventListener("DOMContentLoaded", () => { - const dataElement = document.getElementById("simulations-data"); - let simulationScenarios = []; - let initialRuns = []; - - if (dataElement) { - try { - const parsed = JSON.parse(dataElement.textContent || "{}"); - if (parsed && typeof parsed === "object") { - if (Array.isArray(parsed.scenarios)) { - simulationScenarios = parsed.scenarios; - } - if (Array.isArray(parsed.runs)) { - initialRuns = parsed.runs; - } - } - } catch (error) { - console.error("Unable to parse simulations data", error); - } - } - - const SUMMARY_FIELDS = [ - { key: "count", label: "Iterations", decimals: 0 }, - { key: "mean", label: "Mean Result", decimals: 2 }, - { key: "median", label: "Median Result", decimals: 2 }, - { key: "min", label: "Minimum", decimals: 2 }, - { key: "max", label: "Maximum", decimals: 2 }, - { key: "variance", label: "Variance", decimals: 2 }, - { key: "std_dev", label: "Standard Deviation", decimals: 2 }, - { key: "percentile_5", label: "Percentile 5", decimals: 2 }, - { key: "percentile_95", label: "Percentile 95", decimals: 2 }, - { key: "value_at_risk_95", label: "Value at Risk (95%)", decimals: 2 }, - { - key: "expected_shortfall_95", - label: "Expected Shortfall (95%)", - decimals: 2, - }, - ]; - const SAMPLE_RESULT_LIMIT = 20; - - const filterSelect = document.getElementById("simulations-scenario-filter"); - const overviewWrapper = document.getElementById( - "simulations-overview-wrapper" - ); - const overviewBody = document.getElementById("simulations-overview-body"); - const overviewEmpty = document.getElementById("simulations-overview-empty"); - const emptyState = document.getElementById("simulations-empty"); - const summaryWrapper = document.getElementById("simulations-summary-wrapper"); - const summaryBody = document.getElementById("simulations-summary-body"); - const summaryEmpty = document.getElementById("simulations-summary-empty"); - const resultsWrapper = document.getElementById("simulations-results-wrapper"); - const resultsBody = document.getElementById("simulations-results-body"); - const resultsEmpty = document.getElementById("simulations-results-empty"); - const simulationForm = document.getElementById("simulation-run-form"); - const simulationFeedback = document.getElementById("simulation-feedback"); - const formScenarioSelect = document.getElementById( - "simulation-form-scenario" - ); - - const simulationRunsMap = Object.create(null); - - const getScenarioName = (id) => { - const match = simulationScenarios.find( - (scenario) => String(scenario.id) === String(id) - ); - return match ? match.name : `Scenario ${id}`; - }; - - const formatNumber = (value, decimals = 2) => { - if (value === null || value === undefined || Number.isNaN(Number(value))) { - return "—"; - } - return Number(value).toLocaleString(undefined, { - minimumFractionDigits: decimals, - maximumFractionDigits: decimals, - }); - }; - - const showFeedback = (element, message, type = "success") => { - if (!element) { - return; - } - element.textContent = message; - element.classList.remove("hidden", "success", "error"); - element.classList.add(type); - }; - - const hideFeedback = (element) => { - if (!element) { - return; - } - element.classList.add("hidden"); - element.textContent = ""; - }; - - const initializeRunsMap = () => { - simulationScenarios.forEach((scenario) => { - const key = String(scenario.id); - simulationRunsMap[key] = { - scenario_id: scenario.id, - scenario_name: scenario.name, - iterations: 0, - summary: null, - sample_results: [], - }; - }); - - initialRuns.forEach((run) => { - const key = String(run.scenario_id); - simulationRunsMap[key] = { - scenario_id: run.scenario_id, - scenario_name: run.scenario_name || getScenarioName(key), - iterations: run.iterations || 0, - summary: run.summary || null, - sample_results: Array.isArray(run.sample_results) - ? run.sample_results - : [], - }; - }); - }; - - const renderOverviewTable = () => { - if (!overviewBody) { - return; - } - - overviewBody.innerHTML = ""; - - if (!simulationScenarios.length) { - if (overviewWrapper) { - overviewWrapper.classList.add("hidden"); - } - if (overviewEmpty) { - overviewEmpty.classList.remove("hidden"); - } - return; - } - - if (overviewWrapper) { - overviewWrapper.classList.remove("hidden"); - } - if (overviewEmpty) { - overviewEmpty.classList.add("hidden"); - } - - simulationScenarios.forEach((scenario) => { - const key = String(scenario.id); - const run = simulationRunsMap[key]; - const iterations = run && run.iterations ? run.iterations : 0; - const meanValue = - iterations && run && run.summary ? run.summary.mean : null; - - const row = document.createElement("tr"); - row.innerHTML = ` - ${scenario.name} - ${iterations || 0} - ${iterations ? formatNumber(meanValue) : "—"} - `; - overviewBody.appendChild(row); - }); - }; - - const renderScenarioDetails = (scenarioId) => { - if (!scenarioId) { - if (emptyState) { - emptyState.classList.remove("hidden"); - } - if (summaryWrapper) { - summaryWrapper.classList.add("hidden"); - } - if (summaryEmpty) { - summaryEmpty.classList.add("hidden"); - } - if (resultsWrapper) { - resultsWrapper.classList.add("hidden"); - } - if (resultsEmpty) { - resultsEmpty.classList.add("hidden"); - } - return; - } - - if (emptyState) { - emptyState.classList.add("hidden"); - } - - const run = simulationRunsMap[String(scenarioId)]; - const summary = run ? run.summary : null; - const samples = run ? run.sample_results || [] : []; - - if (!summary) { - if (summaryWrapper) { - summaryWrapper.classList.add("hidden"); - } - if (summaryEmpty) { - summaryEmpty.classList.remove("hidden"); - } - } else { - if (summaryWrapper) { - summaryWrapper.classList.remove("hidden"); - } - if (summaryEmpty) { - summaryEmpty.classList.add("hidden"); - } - - if (summaryBody) { - summaryBody.innerHTML = ""; - SUMMARY_FIELDS.forEach((field) => { - const row = document.createElement("tr"); - row.innerHTML = ` - ${field.label} - ${formatNumber(summary[field.key], field.decimals)} - `; - summaryBody.appendChild(row); - }); - } - } - - if (!samples.length) { - if (resultsWrapper) { - resultsWrapper.classList.add("hidden"); - } - if (resultsEmpty) { - resultsEmpty.classList.remove("hidden"); - } - } else { - if (resultsWrapper) { - resultsWrapper.classList.remove("hidden"); - } - if (resultsEmpty) { - resultsEmpty.classList.add("hidden"); - } - - if (resultsBody) { - resultsBody.innerHTML = ""; - samples.slice(0, SAMPLE_RESULT_LIMIT).forEach((item, index) => { - const row = document.createElement("tr"); - row.innerHTML = ` - ${index + 1} - ${formatNumber(item)} - `; - resultsBody.appendChild(row); - }); - } - } - }; - - const runSimulation = async (event) => { - event.preventDefault(); - hideFeedback(simulationFeedback); - - if (!simulationForm) { - return; - } - - const formData = new FormData(simulationForm); - const scenarioId = formData.get("scenario_id"); - const payload = { - scenario_id: scenarioId ? Number(scenarioId) : null, - iterations: Number(formData.get("iterations")), - seed: formData.get("seed") ? Number(formData.get("seed")) : null, - }; - - if (!payload.scenario_id) { - showFeedback( - simulationFeedback, - "Select a scenario before running a simulation.", - "error" - ); - return; - } - - try { - const response = await fetch("/api/simulations/", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(payload), - }); - - if (!response.ok) { - const errorDetail = await response.json().catch(() => ({})); - throw new Error(errorDetail.detail || "Unable to run simulation."); - } - - const result = await response.json(); - const mapKey = String(result.scenario_id); - const summary = - result.summary && typeof result.summary === "object" - ? result.summary - : null; - const iterations = - summary && typeof summary.count === "number" - ? summary.count - : payload.iterations || 0; - - simulationRunsMap[mapKey] = { - scenario_id: result.scenario_id, - scenario_name: getScenarioName(mapKey), - iterations, - summary, - sample_results: Array.isArray(result.sample_results) - ? result.sample_results - : [], - }; - - renderOverviewTable(); - renderScenarioDetails(mapKey); - - if (filterSelect) { - filterSelect.value = mapKey; - } - if (formScenarioSelect) { - formScenarioSelect.value = mapKey; - } - - simulationForm.reset(); - showFeedback(simulationFeedback, "Simulation completed.", "success"); - } catch (error) { - showFeedback( - simulationFeedback, - error.message || "An unexpected error occurred.", - "error" - ); - } - }; - - initializeRunsMap(); - renderOverviewTable(); - - if (filterSelect) { - filterSelect.addEventListener("change", (event) => { - const value = event.target.value; - renderScenarioDetails(value); - }); - } - - if (formScenarioSelect) { - formScenarioSelect.addEventListener("change", (event) => { - const value = event.target.value; - if (filterSelect) { - filterSelect.value = value; - } - renderScenarioDetails(value); - }); - } - - if (simulationForm) { - simulationForm.addEventListener("submit", runSimulation); - } - - if (filterSelect && filterSelect.value) { - renderScenarioDetails(filterSelect.value); - } -}); -- 2.39.5 From 203a5d08f26948e77df051e89bb739248cc5aa4b Mon Sep 17 00:00:00 2001 From: zwitschi Date: Sun, 9 Nov 2025 16:50:14 +0100 Subject: [PATCH 003/109] feat: add initial database models and changelog for financial inputs and projects --- changelog.md | 5 +++ main.py | 37 ++++-------------- models/__init__.py | 17 +++++++++ models/financial_input.py | 63 +++++++++++++++++++++++++++++++ models/project.py | 54 ++++++++++++++++++++++++++ models/scenario.py | 67 +++++++++++++++++++++++++++++++++ models/simulation_parameter.py | 69 ++++++++++++++++++++++++++++++++++ 7 files changed, 282 insertions(+), 30 deletions(-) create mode 100644 changelog.md create mode 100644 models/__init__.py create mode 100644 models/financial_input.py create mode 100644 models/project.py create mode 100644 models/scenario.py create mode 100644 models/simulation_parameter.py diff --git a/changelog.md b/changelog.md new file mode 100644 index 0000000..4cfec0c --- /dev/null +++ b/changelog.md @@ -0,0 +1,5 @@ +# Changelog + +## 2025-11-09 + +- Captured current implementation status, requirements coverage, missing features, and prioritized roadmap in `calminer-docs/implementation_status.md` to guide future development. diff --git a/main.py b/main.py index 858296d..e1cbc9a 100644 --- a/main.py +++ b/main.py @@ -1,25 +1,17 @@ -from routes.distributions import router as distributions_router -from routes.ui import router as ui_router -from routes.parameters import router as parameters_router from typing import Awaitable, Callable from fastapi import FastAPI, Request, Response from fastapi.staticfiles import StaticFiles from middleware.validation import validate_json from config.database import Base, engine -from routes.scenarios import router as scenarios_router -from routes.costs import router as costs_router -from routes.consumption import router as consumption_router -from routes.production import router as production_router -from routes.equipment import router as equipment_router -from routes.reporting import router as reporting_router -from routes.currencies import router as currencies_router -from routes.simulations import router as simulations_router -from routes.maintenance import router as maintenance_router -from routes.settings import router as settings_router -from routes.users import router as users_router +from models import ( + FinancialInput, + Project, + Scenario, + SimulationParameter, +) -# Initialize database schema +# Initialize database schema (imports above ensure models are registered) Base.metadata.create_all(bind=engine) app = FastAPI() @@ -39,18 +31,3 @@ async def health() -> dict[str, str]: app.mount("/static", StaticFiles(directory="static"), name="static") -# Include API routers -app.include_router(scenarios_router) -app.include_router(parameters_router) -app.include_router(distributions_router) -app.include_router(costs_router) -app.include_router(consumption_router) -app.include_router(simulations_router) -app.include_router(production_router) -app.include_router(equipment_router) -app.include_router(maintenance_router) -app.include_router(reporting_router) -app.include_router(currencies_router) -app.include_router(settings_router) -app.include_router(ui_router) -app.include_router(users_router) diff --git a/models/__init__.py b/models/__init__.py new file mode 100644 index 0000000..9b98963 --- /dev/null +++ b/models/__init__.py @@ -0,0 +1,17 @@ +"""Database models for the CalMiner domain.""" + +from .financial_input import FinancialCategory, FinancialInput +from .project import MiningOperationType, Project +from .scenario import Scenario, ScenarioStatus +from .simulation_parameter import DistributionType, SimulationParameter + +__all__ = [ + "FinancialCategory", + "FinancialInput", + "MiningOperationType", + "Project", + "Scenario", + "ScenarioStatus", + "DistributionType", + "SimulationParameter", +] diff --git a/models/financial_input.py b/models/financial_input.py new file mode 100644 index 0000000..092d77a --- /dev/null +++ b/models/financial_input.py @@ -0,0 +1,63 @@ +from __future__ import annotations + +from datetime import date, datetime +from enum import Enum +from typing import TYPE_CHECKING + +from sqlalchemy import ( + Date, + DateTime, + Enum as SQLEnum, + ForeignKey, + Integer, + Numeric, + String, + Text, +) +from sqlalchemy.orm import Mapped, mapped_column, relationship +from sqlalchemy.sql import func + +from config.database import Base + +if TYPE_CHECKING: # pragma: no cover + from .scenario import Scenario + + +class FinancialCategory(str, Enum): + """Enumeration of cost and revenue classifications.""" + + CAPITAL_EXPENDITURE = "capex" + OPERATING_EXPENDITURE = "opex" + REVENUE = "revenue" + CONTINGENCY = "contingency" + OTHER = "other" + + +class FinancialInput(Base): + """Line-item financial assumption attached to a scenario.""" + + __tablename__ = "financial_inputs" + + id: Mapped[int] = mapped_column(Integer, primary_key=True) + scenario_id: Mapped[int] = mapped_column( + ForeignKey("scenarios.id", ondelete="CASCADE"), nullable=False, index=True + ) + name: Mapped[str] = mapped_column(String(255), nullable=False) + category: Mapped[FinancialCategory] = mapped_column( + SQLEnum(FinancialCategory), nullable=False + ) + amount: Mapped[float] = mapped_column(Numeric(18, 2), nullable=False) + currency: Mapped[str | None] = mapped_column(String(3), nullable=True) + effective_date: Mapped[date | None] = mapped_column(Date, nullable=True) + notes: Mapped[str | None] = mapped_column(Text, nullable=True) + created_at: Mapped[datetime] = mapped_column( + DateTime(timezone=True), nullable=False, server_default=func.now() + ) + updated_at: Mapped[datetime] = mapped_column( + DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now() + ) + + scenario: Mapped["Scenario"] = relationship("Scenario", back_populates="financial_inputs") + + def __repr__(self) -> str: # pragma: no cover + return f"FinancialInput(id={self.id!r}, scenario_id={self.scenario_id!r}, name={self.name!r})" diff --git a/models/project.py b/models/project.py new file mode 100644 index 0000000..e12b3bc --- /dev/null +++ b/models/project.py @@ -0,0 +1,54 @@ +from __future__ import annotations + +from datetime import datetime +from enum import Enum +from typing import TYPE_CHECKING, List + +from sqlalchemy import DateTime, Enum as SQLEnum, Integer, String, Text +from sqlalchemy.orm import Mapped, mapped_column, relationship +from sqlalchemy.sql import func + +from config.database import Base + +if TYPE_CHECKING: # pragma: no cover + from .scenario import Scenario + + +class MiningOperationType(str, Enum): + """Supported mining operation categories.""" + + OPEN_PIT = "open_pit" + UNDERGROUND = "underground" + PLACER = "placer" + QUARRY = "quarry" + OTHER = "other" + + +class Project(Base): + """Top-level mining project grouping multiple scenarios.""" + + __tablename__ = "projects" + + id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True) + name: Mapped[str] = mapped_column(String(255), nullable=False, unique=True) + location: Mapped[str | None] = mapped_column(String(255), nullable=True) + operation_type: Mapped[MiningOperationType] = mapped_column( + SQLEnum(MiningOperationType), nullable=False, default=MiningOperationType.OTHER + ) + description: Mapped[str | None] = mapped_column(Text, nullable=True) + created_at: Mapped[datetime] = mapped_column( + DateTime(timezone=True), nullable=False, server_default=func.now() + ) + updated_at: Mapped[datetime] = mapped_column( + DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now() + ) + + scenarios: Mapped[List["Scenario"]] = relationship( + "Scenario", + back_populates="project", + cascade="all, delete-orphan", + passive_deletes=True, + ) + + def __repr__(self) -> str: # pragma: no cover - helpful for debugging + return f"Project(id={self.id!r}, name={self.name!r})" diff --git a/models/scenario.py b/models/scenario.py new file mode 100644 index 0000000..6198278 --- /dev/null +++ b/models/scenario.py @@ -0,0 +1,67 @@ +from __future__ import annotations + +from datetime import date, datetime +from enum import Enum +from typing import TYPE_CHECKING, List + +from sqlalchemy import Date, DateTime, Enum as SQLEnum, ForeignKey, Integer, Numeric, String, Text +from sqlalchemy.orm import Mapped, mapped_column, relationship +from sqlalchemy.sql import func + +from config.database import Base + +if TYPE_CHECKING: # pragma: no cover + from .financial_input import FinancialInput + from .project import Project + from .simulation_parameter import SimulationParameter + + +class ScenarioStatus(str, Enum): + """Lifecycle states for project scenarios.""" + + DRAFT = "draft" + ACTIVE = "active" + ARCHIVED = "archived" + + +class Scenario(Base): + """A specific configuration of assumptions for a project.""" + + __tablename__ = "scenarios" + + id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True) + project_id: Mapped[int] = mapped_column( + ForeignKey("projects.id", ondelete="CASCADE"), nullable=False, index=True + ) + name: Mapped[str] = mapped_column(String(255), nullable=False) + description: Mapped[str | None] = mapped_column(Text, nullable=True) + status: Mapped[ScenarioStatus] = mapped_column( + SQLEnum(ScenarioStatus), nullable=False, default=ScenarioStatus.DRAFT + ) + start_date: Mapped[date | None] = mapped_column(Date, nullable=True) + end_date: Mapped[date | None] = mapped_column(Date, nullable=True) + discount_rate: Mapped[float | None] = mapped_column(Numeric(5, 2), nullable=True) + currency: Mapped[str | None] = mapped_column(String(3), nullable=True) + created_at: Mapped[datetime] = mapped_column( + DateTime(timezone=True), nullable=False, server_default=func.now() + ) + updated_at: Mapped[datetime] = mapped_column( + DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now() + ) + + project: Mapped["Project"] = relationship("Project", back_populates="scenarios") + financial_inputs: Mapped[List["FinancialInput"]] = relationship( + "FinancialInput", + back_populates="scenario", + cascade="all, delete-orphan", + passive_deletes=True, + ) + simulation_parameters: Mapped[List["SimulationParameter"]] = relationship( + "SimulationParameter", + back_populates="scenario", + cascade="all, delete-orphan", + passive_deletes=True, + ) + + def __repr__(self) -> str: # pragma: no cover + return f"Scenario(id={self.id!r}, name={self.name!r}, project_id={self.project_id!r})" diff --git a/models/simulation_parameter.py b/models/simulation_parameter.py new file mode 100644 index 0000000..6e6f89c --- /dev/null +++ b/models/simulation_parameter.py @@ -0,0 +1,69 @@ +from __future__ import annotations + +from datetime import datetime +from enum import Enum +from typing import TYPE_CHECKING + +from sqlalchemy import ( + JSON, + DateTime, + Enum as SQLEnum, + ForeignKey, + Integer, + Numeric, + String, +) +from sqlalchemy.orm import Mapped, mapped_column, relationship +from sqlalchemy.sql import func + +from config.database import Base + +if TYPE_CHECKING: # pragma: no cover + from .scenario import Scenario + + +class DistributionType(str, Enum): + """Supported stochastic distribution families for simulations.""" + + NORMAL = "normal" + TRIANGULAR = "triangular" + UNIFORM = "uniform" + LOGNORMAL = "lognormal" + CUSTOM = "custom" + + +class SimulationParameter(Base): + """Probability distribution settings for scenario simulations.""" + + __tablename__ = "simulation_parameters" + + id: Mapped[int] = mapped_column(Integer, primary_key=True) + scenario_id: Mapped[int] = mapped_column( + ForeignKey("scenarios.id", ondelete="CASCADE"), nullable=False, index=True + ) + name: Mapped[str] = mapped_column(String(255), nullable=False) + distribution: Mapped[DistributionType] = mapped_column( + SQLEnum(DistributionType), nullable=False + ) + mean_value: Mapped[float | None] = mapped_column(Numeric(18, 4), nullable=True) + standard_deviation: Mapped[float | None] = mapped_column(Numeric(18, 4), nullable=True) + minimum_value: Mapped[float | None] = mapped_column(Numeric(18, 4), nullable=True) + maximum_value: Mapped[float | None] = mapped_column(Numeric(18, 4), nullable=True) + unit: Mapped[str | None] = mapped_column(String(32), nullable=True) + metadata: Mapped[dict | None] = mapped_column(JSON, nullable=True) + created_at: Mapped[datetime] = mapped_column( + DateTime(timezone=True), nullable=False, server_default=func.now() + ) + updated_at: Mapped[datetime] = mapped_column( + DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now() + ) + + scenario: Mapped["Scenario"] = relationship( + "Scenario", back_populates="simulation_parameters" + ) + + def __repr__(self) -> str: # pragma: no cover + return ( + f"SimulationParameter(id={self.id!r}, scenario_id={self.scenario_id!r}, " + f"name={self.name!r})" + ) -- 2.39.5 From 32a96a27c556f97783265b307f0b487ee084a651 Mon Sep 17 00:00:00 2001 From: zwitschi Date: Sun, 9 Nov 2025 16:54:46 +0100 Subject: [PATCH 004/109] feat: enhance database models with metadata and new resource types --- models/__init__.py | 20 ++++- models/financial_input.py | 25 ++++++ models/metadata.py | 146 +++++++++++++++++++++++++++++++++ models/project.py | 2 + models/scenario.py | 15 +++- models/simulation_parameter.py | 7 ++ 6 files changed, 213 insertions(+), 2 deletions(-) create mode 100644 models/metadata.py diff --git a/models/__init__.py b/models/__init__.py index 9b98963..55e89ac 100644 --- a/models/__init__.py +++ b/models/__init__.py @@ -1,6 +1,16 @@ -"""Database models for the CalMiner domain.""" +"""Database models and shared metadata for the CalMiner domain.""" from .financial_input import FinancialCategory, FinancialInput +from .metadata import ( + COST_BUCKET_METADATA, + RESOURCE_METADATA, + STOCHASTIC_VARIABLE_METADATA, + CostBucket, + ResourceDescriptor, + ResourceType, + StochasticVariable, + StochasticVariableDescriptor, +) from .project import MiningOperationType, Project from .scenario import Scenario, ScenarioStatus from .simulation_parameter import DistributionType, SimulationParameter @@ -14,4 +24,12 @@ __all__ = [ "ScenarioStatus", "DistributionType", "SimulationParameter", + "ResourceType", + "CostBucket", + "StochasticVariable", + "RESOURCE_METADATA", + "COST_BUCKET_METADATA", + "STOCHASTIC_VARIABLE_METADATA", + "ResourceDescriptor", + "StochasticVariableDescriptor", ] diff --git a/models/financial_input.py b/models/financial_input.py index 092d77a..eecdea2 100644 --- a/models/financial_input.py +++ b/models/financial_input.py @@ -4,6 +4,18 @@ from datetime import date, datetime from enum import Enum from typing import TYPE_CHECKING +from sqlalchemy import ( + Date, + DateTime, + Enum as SQLEnum, + ForeignKey, + Integer, + Numeric, + String, + Text, +) +from sqlalchemy.orm import Mapped, mapped_column, relationship, validates + from sqlalchemy import ( Date, DateTime, @@ -18,6 +30,7 @@ from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.sql import func from config.database import Base +from .metadata import CostBucket if TYPE_CHECKING: # pragma: no cover from .scenario import Scenario @@ -46,6 +59,9 @@ class FinancialInput(Base): category: Mapped[FinancialCategory] = mapped_column( SQLEnum(FinancialCategory), nullable=False ) + cost_bucket: Mapped[CostBucket | None] = mapped_column( + SQLEnum(CostBucket), nullable=True + ) amount: Mapped[float] = mapped_column(Numeric(18, 2), nullable=False) currency: Mapped[str | None] = mapped_column(String(3), nullable=True) effective_date: Mapped[date | None] = mapped_column(Date, nullable=True) @@ -59,5 +75,14 @@ class FinancialInput(Base): scenario: Mapped["Scenario"] = relationship("Scenario", back_populates="financial_inputs") + @validates("currency") + def _validate_currency(self, key: str, value: str | None) -> str | None: + if value is None: + return value + value = value.upper() + if len(value) != 3: + raise ValueError("Currency code must be a 3-letter ISO 4217 value") + return value + def __repr__(self) -> str: # pragma: no cover return f"FinancialInput(id={self.id!r}, scenario_id={self.scenario_id!r}, name={self.name!r})" diff --git a/models/metadata.py b/models/metadata.py new file mode 100644 index 0000000..7aedc2f --- /dev/null +++ b/models/metadata.py @@ -0,0 +1,146 @@ +from __future__ import annotations + +from dataclasses import dataclass +from enum import Enum + + +class ResourceType(str, Enum): + """Primary consumables and resources used in mining operations.""" + + DIESEL = "diesel" + ELECTRICITY = "electricity" + WATER = "water" + EXPLOSIVES = "explosives" + REAGENTS = "reagents" + LABOR = "labor" + EQUIPMENT_HOURS = "equipment_hours" + TAILINGS_CAPACITY = "tailings_capacity" + + +class CostBucket(str, Enum): + """Granular cost buckets aligned with project accounting.""" + + CAPITAL_INITIAL = "capital_initial" + CAPITAL_SUSTAINING = "capital_sustaining" + OPERATING_FIXED = "operating_fixed" + OPERATING_VARIABLE = "operating_variable" + MAINTENANCE = "maintenance" + RECLAMATION = "reclamation" + ROYALTIES = "royalties" + GENERAL_ADMIN = "general_admin" + + +class StochasticVariable(str, Enum): + """Domain variables that typically require probabilistic modelling.""" + + ORE_GRADE = "ore_grade" + RECOVERY_RATE = "recovery_rate" + METAL_PRICE = "metal_price" + OPERATING_COST = "operating_cost" + CAPITAL_COST = "capital_cost" + DISCOUNT_RATE = "discount_rate" + THROUGHPUT = "throughput" + + +@dataclass(frozen=True) +class ResourceDescriptor: + """Describes canonical metadata for a resource type.""" + + unit: str + description: str + + +RESOURCE_METADATA: dict[ResourceType, ResourceDescriptor] = { + ResourceType.DIESEL: ResourceDescriptor(unit="L", description="Diesel fuel consumption"), + ResourceType.ELECTRICITY: ResourceDescriptor(unit="kWh", description="Electrical power usage"), + ResourceType.WATER: ResourceDescriptor(unit="m3", description="Process and dust suppression water"), + ResourceType.EXPLOSIVES: ResourceDescriptor(unit="kg", description="Blasting agent consumption"), + ResourceType.REAGENTS: ResourceDescriptor(unit="kg", description="Processing reagents"), + ResourceType.LABOR: ResourceDescriptor(unit="hours", description="Direct labor hours"), + ResourceType.EQUIPMENT_HOURS: ResourceDescriptor(unit="hours", description="Mobile equipment operating hours"), + ResourceType.TAILINGS_CAPACITY: ResourceDescriptor(unit="m3", description="Tailings storage usage"), +} + + +@dataclass(frozen=True) +class CostBucketDescriptor: + """Describes reporting label and guidance for a cost bucket.""" + + label: str + description: str + + +COST_BUCKET_METADATA: dict[CostBucket, CostBucketDescriptor] = { + CostBucket.CAPITAL_INITIAL: CostBucketDescriptor( + label="Initial Capital", + description="Pre-production capital required to construct the mine", + ), + CostBucket.CAPITAL_SUSTAINING: CostBucketDescriptor( + label="Sustaining Capital", + description="Ongoing capital investments to maintain operations", + ), + CostBucket.OPERATING_FIXED: CostBucketDescriptor( + label="Fixed Operating", + description="Fixed operating costs independent of production rate", + ), + CostBucket.OPERATING_VARIABLE: CostBucketDescriptor( + label="Variable Operating", + description="Costs that scale with throughput or production", + ), + CostBucket.MAINTENANCE: CostBucketDescriptor( + label="Maintenance", + description="Maintenance and repair expenditures", + ), + CostBucket.RECLAMATION: CostBucketDescriptor( + label="Reclamation", + description="Mine closure and reclamation liabilities", + ), + CostBucket.ROYALTIES: CostBucketDescriptor( + label="Royalties", + description="Royalty and streaming obligations", + ), + CostBucket.GENERAL_ADMIN: CostBucketDescriptor( + label="G&A", + description="Corporate and site general and administrative costs", + ), +} + + +@dataclass(frozen=True) +class StochasticVariableDescriptor: + """Metadata describing how a stochastic variable is typically modelled.""" + + unit: str + description: str + + +STOCHASTIC_VARIABLE_METADATA: dict[StochasticVariable, StochasticVariableDescriptor] = { + StochasticVariable.ORE_GRADE: StochasticVariableDescriptor( + unit="g/t", + description="Head grade variability across the ore body", + ), + StochasticVariable.RECOVERY_RATE: StochasticVariableDescriptor( + unit="%", + description="Metallurgical recovery uncertainty", + ), + StochasticVariable.METAL_PRICE: StochasticVariableDescriptor( + unit="$/unit", + description="Commodity price fluctuations", + ), + StochasticVariable.OPERATING_COST: StochasticVariableDescriptor( + unit="$/t", + description="Operating cost per tonne volatility", + ), + StochasticVariable.CAPITAL_COST: StochasticVariableDescriptor( + unit="$", + description="Capital cost overrun/underrun potential", + ), + StochasticVariable.DISCOUNT_RATE: StochasticVariableDescriptor( + unit="%", + description="Discount rate sensitivity", + ), + StochasticVariable.THROUGHPUT: StochasticVariableDescriptor( + unit="t/d", + description="Plant throughput variability", + ), +} diff --git a/models/project.py b/models/project.py index e12b3bc..de6e0ff 100644 --- a/models/project.py +++ b/models/project.py @@ -19,8 +19,10 @@ class MiningOperationType(str, Enum): OPEN_PIT = "open_pit" UNDERGROUND = "underground" + IN_SITU_LEACH = "in_situ_leach" PLACER = "placer" QUARRY = "quarry" + MOUNTAINTOP_REMOVAL = "mountaintop_removal" OTHER = "other" diff --git a/models/scenario.py b/models/scenario.py index 6198278..b7f08b0 100644 --- a/models/scenario.py +++ b/models/scenario.py @@ -4,11 +4,21 @@ from datetime import date, datetime from enum import Enum from typing import TYPE_CHECKING, List -from sqlalchemy import Date, DateTime, Enum as SQLEnum, ForeignKey, Integer, Numeric, String, Text +from sqlalchemy import ( + Date, + DateTime, + Enum as SQLEnum, + ForeignKey, + Integer, + Numeric, + String, + Text, +) from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.sql import func from config.database import Base +from .metadata import ResourceType if TYPE_CHECKING: # pragma: no cover from .financial_input import FinancialInput @@ -42,6 +52,9 @@ class Scenario(Base): end_date: Mapped[date | None] = mapped_column(Date, nullable=True) discount_rate: Mapped[float | None] = mapped_column(Numeric(5, 2), nullable=True) currency: Mapped[str | None] = mapped_column(String(3), nullable=True) + primary_resource: Mapped[ResourceType | None] = mapped_column( + SQLEnum(ResourceType), nullable=True + ) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False, server_default=func.now() ) diff --git a/models/simulation_parameter.py b/models/simulation_parameter.py index 6e6f89c..e77b1b2 100644 --- a/models/simulation_parameter.py +++ b/models/simulation_parameter.py @@ -17,6 +17,7 @@ from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.sql import func from config.database import Base +from .metadata import ResourceType, StochasticVariable if TYPE_CHECKING: # pragma: no cover from .scenario import Scenario @@ -45,6 +46,12 @@ class SimulationParameter(Base): distribution: Mapped[DistributionType] = mapped_column( SQLEnum(DistributionType), nullable=False ) + variable: Mapped[StochasticVariable | None] = mapped_column( + SQLEnum(StochasticVariable), nullable=True + ) + resource_type: Mapped[ResourceType | None] = mapped_column( + SQLEnum(ResourceType), nullable=True + ) mean_value: Mapped[float | None] = mapped_column(Numeric(18, 4), nullable=True) standard_deviation: Mapped[float | None] = mapped_column(Numeric(18, 4), nullable=True) minimum_value: Mapped[float | None] = mapped_column(Numeric(18, 4), nullable=True) -- 2.39.5 From dc3ebfbba5452f59cd046726aa998fc2da376cce Mon Sep 17 00:00:00 2001 From: zwitschi Date: Sun, 9 Nov 2025 16:57:32 +0100 Subject: [PATCH 005/109] feat: add initial Alembic configuration files for database migrations --- alembic.ini | 35 +++++++++++++++++++++++ alembic/env.py | 63 ++++++++++++++++++++++++++++++++++++++++++ alembic/script.py.mako | 17 ++++++++++++ 3 files changed, 115 insertions(+) create mode 100644 alembic.ini create mode 100644 alembic/env.py create mode 100644 alembic/script.py.mako diff --git a/alembic.ini b/alembic.ini new file mode 100644 index 0000000..ae43771 --- /dev/null +++ b/alembic.ini @@ -0,0 +1,35 @@ +[alembic] +script_location = alembic +sqlalchemy.url = %(DATABASE_URL)s + +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s diff --git a/alembic/env.py b/alembic/env.py new file mode 100644 index 0000000..95ef1b9 --- /dev/null +++ b/alembic/env.py @@ -0,0 +1,63 @@ +from __future__ import annotations + +from logging.config import fileConfig +from typing import Iterable + +from alembic import context +from sqlalchemy import engine_from_config, pool + +from config.database import Base, DATABASE_URL +from models import * # noqa: F401,F403 - ensure models are imported for metadata registration + +# this is the Alembic Config object, which provides access to the values within the .ini file. +config = context.config + +if config.config_file_name is not None: + fileConfig(config.config_file_name) + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +config.set_main_option("sqlalchemy.url", DATABASE_URL) + +target_metadata = Base.metadata + + +def run_migrations_offline() -> None: + """Run migrations in 'offline' mode.""" + + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online() -> None: + """Run migrations in 'online' mode.""" + + connectable = engine_from_config( + config.get_section(config.config_ini_section, {}), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + with connectable.connect() as connection: + context.configure(connection=connection, target_metadata=target_metadata) + + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations() -> None: + if context.is_offline_mode(): + run_migrations_offline() + else: + run_migrations_online() + + +run_migrations() diff --git a/alembic/script.py.mako b/alembic/script.py.mako new file mode 100644 index 0000000..3966f7a --- /dev/null +++ b/alembic/script.py.mako @@ -0,0 +1,17 @@ +"""${message}""" + +revision = ${repr(revision)} +down_revision = ${repr(down_revision)} +branch_labels = ${repr(branch_labels)} +depends_on = ${repr(depends_on)} + +from alembic import op +import sqlalchemy as sa + + +def upgrade() -> None: + ${upgrades if upgrades else "pass"} + + +def downgrade() -> None: + ${downgrades if downgrades else "pass"} -- 2.39.5 From c6fdc2d923652bfaf73006724cdad6c3c3762997 Mon Sep 17 00:00:00 2001 From: zwitschi Date: Sun, 9 Nov 2025 16:57:58 +0100 Subject: [PATCH 006/109] feat: add initial schema and update changelog for database models --- .../versions/20251109_01_initial_schema.py | 195 ++++++++++++++++++ changelog.md | 1 + requirements-dev.txt | 2 + 3 files changed, 198 insertions(+) create mode 100644 alembic/versions/20251109_01_initial_schema.py create mode 100644 requirements-dev.txt diff --git a/alembic/versions/20251109_01_initial_schema.py b/alembic/versions/20251109_01_initial_schema.py new file mode 100644 index 0000000..020a56b --- /dev/null +++ b/alembic/versions/20251109_01_initial_schema.py @@ -0,0 +1,195 @@ +"""Initial domain schema""" + +from __future__ import annotations + +from alembic import op +import sqlalchemy as sa + +# revision identifiers, used by Alembic. +revision = "20251109_01" +down_revision = None +branch_labels = None +depends_on = None + + +mining_operation_type = sa.Enum( + "open_pit", + "underground", + "in_situ_leach", + "placer", + "quarry", + "mountaintop_removal", + "other", + name="miningoperationtype", +) + +scenario_status = sa.Enum( + "draft", + "active", + "archived", + name="scenariostatus", +) + +financial_category = sa.Enum( + "capex", + "opex", + "revenue", + "contingency", + "other", + name="financialcategory", +) + +cost_bucket = sa.Enum( + "capital_initial", + "capital_sustaining", + "operating_fixed", + "operating_variable", + "maintenance", + "reclamation", + "royalties", + "general_admin", + name="costbucket", +) + +distribution_type = sa.Enum( + "normal", + "triangular", + "uniform", + "lognormal", + "custom", + name="distributiontype", +) + +stochastic_variable = sa.Enum( + "ore_grade", + "recovery_rate", + "metal_price", + "operating_cost", + "capital_cost", + "discount_rate", + "throughput", + name="stochasticvariable", +) + +resource_type = sa.Enum( + "diesel", + "electricity", + "water", + "explosives", + "reagents", + "labor", + "equipment_hours", + "tailings_capacity", + name="resourcetype", +) + + +def upgrade() -> None: + bind = op.get_bind() + mining_operation_type.create(bind, checkfirst=True) + scenario_status.create(bind, checkfirst=True) + financial_category.create(bind, checkfirst=True) + cost_bucket.create(bind, checkfirst=True) + distribution_type.create(bind, checkfirst=True) + stochastic_variable.create(bind, checkfirst=True) + resource_type.create(bind, checkfirst=True) + + op.create_table( + "projects", + sa.Column("id", sa.Integer(), nullable=False), + sa.Column("name", sa.String(length=255), nullable=False), + sa.Column("location", sa.String(length=255), nullable=True), + sa.Column("operation_type", mining_operation_type, nullable=False), + sa.Column("description", sa.Text(), nullable=True), + sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), + sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), + sa.PrimaryKeyConstraint("id"), + sa.UniqueConstraint("name"), + ) + op.create_index(op.f("ix_projects_id"), "projects", ["id"], unique=False) + + op.create_table( + "scenarios", + sa.Column("id", sa.Integer(), nullable=False), + sa.Column("project_id", sa.Integer(), nullable=False), + sa.Column("name", sa.String(length=255), nullable=False), + sa.Column("description", sa.Text(), nullable=True), + sa.Column("status", scenario_status, nullable=False), + sa.Column("start_date", sa.Date(), nullable=True), + sa.Column("end_date", sa.Date(), nullable=True), + sa.Column("discount_rate", sa.Numeric(precision=5, scale=2), nullable=True), + sa.Column("currency", sa.String(length=3), nullable=True), + sa.Column("primary_resource", resource_type, nullable=True), + sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), + sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), + sa.ForeignKeyConstraint(["project_id"], ["projects.id"], ondelete="CASCADE"), + sa.PrimaryKeyConstraint("id"), + ) + op.create_index(op.f("ix_scenarios_id"), "scenarios", ["id"], unique=False) + op.create_index(op.f("ix_scenarios_project_id"), "scenarios", ["project_id"], unique=False) + + op.create_table( + "financial_inputs", + sa.Column("id", sa.Integer(), nullable=False), + sa.Column("scenario_id", sa.Integer(), nullable=False), + sa.Column("name", sa.String(length=255), nullable=False), + sa.Column("category", financial_category, nullable=False), + sa.Column("cost_bucket", cost_bucket, nullable=True), + sa.Column("amount", sa.Numeric(precision=18, scale=2), nullable=False), + sa.Column("currency", sa.String(length=3), nullable=True), + sa.Column("effective_date", sa.Date(), nullable=True), + sa.Column("notes", sa.Text(), nullable=True), + sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), + sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), + sa.ForeignKeyConstraint(["scenario_id"], ["scenarios.id"], ondelete="CASCADE"), + sa.PrimaryKeyConstraint("id"), + ) + op.create_index(op.f("ix_financial_inputs_id"), "financial_inputs", ["id"], unique=False) + op.create_index(op.f("ix_financial_inputs_scenario_id"), "financial_inputs", ["scenario_id"], unique=False) + + op.create_table( + "simulation_parameters", + sa.Column("id", sa.Integer(), nullable=False), + sa.Column("scenario_id", sa.Integer(), nullable=False), + sa.Column("name", sa.String(length=255), nullable=False), + sa.Column("distribution", distribution_type, nullable=False), + sa.Column("variable", stochastic_variable, nullable=True), + sa.Column("resource_type", resource_type, nullable=True), + sa.Column("mean_value", sa.Numeric(precision=18, scale=4), nullable=True), + sa.Column("standard_deviation", sa.Numeric(precision=18, scale=4), nullable=True), + sa.Column("minimum_value", sa.Numeric(precision=18, scale=4), nullable=True), + sa.Column("maximum_value", sa.Numeric(precision=18, scale=4), nullable=True), + sa.Column("unit", sa.String(length=32), nullable=True), + sa.Column("metadata", sa.JSON(), nullable=True), + sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), + sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), + sa.ForeignKeyConstraint(["scenario_id"], ["scenarios.id"], ondelete="CASCADE"), + sa.PrimaryKeyConstraint("id"), + ) + op.create_index(op.f("ix_simulation_parameters_id"), "simulation_parameters", ["id"], unique=False) + op.create_index(op.f("ix_simulation_parameters_scenario_id"), "simulation_parameters", ["scenario_id"], unique=False) + + +def downgrade() -> None: + op.drop_index(op.f("ix_simulation_parameters_scenario_id"), table_name="simulation_parameters") + op.drop_index(op.f("ix_simulation_parameters_id"), table_name="simulation_parameters") + op.drop_table("simulation_parameters") + + op.drop_index(op.f("ix_financial_inputs_scenario_id"), table_name="financial_inputs") + op.drop_index(op.f("ix_financial_inputs_id"), table_name="financial_inputs") + op.drop_table("financial_inputs") + + op.drop_index(op.f("ix_scenarios_project_id"), table_name="scenarios") + op.drop_index(op.f("ix_scenarios_id"), table_name="scenarios") + op.drop_table("scenarios") + + op.drop_index(op.f("ix_projects_id"), table_name="projects") + op.drop_table("projects") + + resource_type.drop(op.get_bind(), checkfirst=True) + stochastic_variable.drop(op.get_bind(), checkfirst=True) + distribution_type.drop(op.get_bind(), checkfirst=True) + cost_bucket.drop(op.get_bind(), checkfirst=True) + financial_category.drop(op.get_bind(), checkfirst=True) + scenario_status.drop(op.get_bind(), checkfirst=True) + mining_operation_type.drop(op.get_bind(), checkfirst=True) diff --git a/changelog.md b/changelog.md index 4cfec0c..1f5de9f 100644 --- a/changelog.md +++ b/changelog.md @@ -3,3 +3,4 @@ ## 2025-11-09 - Captured current implementation status, requirements coverage, missing features, and prioritized roadmap in `calminer-docs/implementation_status.md` to guide future development. +- Added core SQLAlchemy domain models, shared metadata descriptors, and Alembic migration setup (with initial schema snapshot) to establish the persistence layer foundation. diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..dffe6b7 --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,2 @@ +-r requirements.txt +alembic -- 2.39.5 From c69f933684238200f2766f71c9c6964687af9717 Mon Sep 17 00:00:00 2001 From: zwitschi Date: Sun, 9 Nov 2025 16:59:58 +0100 Subject: [PATCH 007/109] feat: implement repository and unit-of-work patterns for service layer operations --- changelog.md | 1 + services/exceptions.py | 9 +++ services/repositories.py | 146 +++++++++++++++++++++++++++++++++++++++ services/unit_of_work.py | 53 ++++++++++++++ 4 files changed, 209 insertions(+) create mode 100644 services/exceptions.py create mode 100644 services/repositories.py create mode 100644 services/unit_of_work.py diff --git a/changelog.md b/changelog.md index 1f5de9f..169fa8a 100644 --- a/changelog.md +++ b/changelog.md @@ -4,3 +4,4 @@ - Captured current implementation status, requirements coverage, missing features, and prioritized roadmap in `calminer-docs/implementation_status.md` to guide future development. - Added core SQLAlchemy domain models, shared metadata descriptors, and Alembic migration setup (with initial schema snapshot) to establish the persistence layer foundation. +- Introduced repository and unit-of-work helpers for projects, scenarios, financial inputs, and simulation parameters to support service-layer operations. diff --git a/services/exceptions.py b/services/exceptions.py new file mode 100644 index 0000000..6e58c29 --- /dev/null +++ b/services/exceptions.py @@ -0,0 +1,9 @@ +"""Domain-level exceptions for service and repository layers.""" + + +class EntityNotFoundError(Exception): + """Raised when a requested entity cannot be located.""" + + +class EntityConflictError(Exception): + """Raised when attempting to create or update an entity that violates uniqueness.""" diff --git a/services/repositories.py b/services/repositories.py new file mode 100644 index 0000000..bafff7b --- /dev/null +++ b/services/repositories.py @@ -0,0 +1,146 @@ +from __future__ import annotations + +from collections.abc import Iterable +from typing import Sequence + +from sqlalchemy import select +from sqlalchemy.exc import IntegrityError +from sqlalchemy.orm import Session, joinedload + +from models import FinancialInput, Project, Scenario, SimulationParameter +from services.exceptions import EntityConflictError, EntityNotFoundError + + +class ProjectRepository: + """Persistence operations for Project entities.""" + + def __init__(self, session: Session) -> None: + self.session = session + + def list(self) -> Sequence[Project]: + stmt = select(Project).order_by(Project.created_at) + return self.session.execute(stmt).scalars().all() + + def get(self, project_id: int, *, with_children: bool = False) -> Project: + stmt = select(Project).where(Project.id == project_id) + if with_children: + stmt = stmt.options(joinedload(Project.scenarios)) + project = self.session.execute(stmt).scalar_one_or_none() + if project is None: + raise EntityNotFoundError(f"Project {project_id} not found") + return project + + def create(self, project: Project) -> Project: + self.session.add(project) + try: + self.session.flush() + except IntegrityError as exc: # pragma: no cover - reliance on DB constraints + raise EntityConflictError("Project violates uniqueness constraints") from exc + return project + + def delete(self, project_id: int) -> None: + project = self.get(project_id) + self.session.delete(project) + + +class ScenarioRepository: + """Persistence operations for Scenario entities.""" + + def __init__(self, session: Session) -> None: + self.session = session + + def list_for_project(self, project_id: int) -> Sequence[Scenario]: + stmt = ( + select(Scenario) + .where(Scenario.project_id == project_id) + .order_by(Scenario.created_at) + ) + return self.session.execute(stmt).scalars().all() + + def get(self, scenario_id: int, *, with_children: bool = False) -> Scenario: + stmt = select(Scenario).where(Scenario.id == scenario_id) + if with_children: + stmt = stmt.options( + joinedload(Scenario.financial_inputs), + joinedload(Scenario.simulation_parameters), + ) + scenario = self.session.execute(stmt).scalar_one_or_none() + if scenario is None: + raise EntityNotFoundError(f"Scenario {scenario_id} not found") + return scenario + + def create(self, scenario: Scenario) -> Scenario: + self.session.add(scenario) + try: + self.session.flush() + except IntegrityError as exc: # pragma: no cover + raise EntityConflictError("Scenario violates constraints") from exc + return scenario + + def delete(self, scenario_id: int) -> None: + scenario = self.get(scenario_id) + self.session.delete(scenario) + + +class FinancialInputRepository: + """Persistence operations for FinancialInput entities.""" + + def __init__(self, session: Session) -> None: + self.session = session + + def list_for_scenario(self, scenario_id: int) -> Sequence[FinancialInput]: + stmt = ( + select(FinancialInput) + .where(FinancialInput.scenario_id == scenario_id) + .order_by(FinancialInput.created_at) + ) + return self.session.execute(stmt).scalars().all() + + def bulk_upsert(self, inputs: Iterable[FinancialInput]) -> Sequence[FinancialInput]: + entities = list(inputs) + self.session.add_all(entities) + try: + self.session.flush() + except IntegrityError as exc: # pragma: no cover + raise EntityConflictError("Financial input violates constraints") from exc + return entities + + def delete(self, input_id: int) -> None: + stmt = select(FinancialInput).where(FinancialInput.id == input_id) + entity = self.session.execute(stmt).scalar_one_or_none() + if entity is None: + raise EntityNotFoundError(f"Financial input {input_id} not found") + self.session.delete(entity) + + +class SimulationParameterRepository: + """Persistence operations for SimulationParameter entities.""" + + def __init__(self, session: Session) -> None: + self.session = session + + def list_for_scenario(self, scenario_id: int) -> Sequence[SimulationParameter]: + stmt = ( + select(SimulationParameter) + .where(SimulationParameter.scenario_id == scenario_id) + .order_by(SimulationParameter.created_at) + ) + return self.session.execute(stmt).scalars().all() + + def bulk_upsert( + self, parameters: Iterable[SimulationParameter] + ) -> Sequence[SimulationParameter]: + entities = list(parameters) + self.session.add_all(entities) + try: + self.session.flush() + except IntegrityError as exc: # pragma: no cover + raise EntityConflictError("Simulation parameter violates constraints") from exc + return entities + + def delete(self, parameter_id: int) -> None: + stmt = select(SimulationParameter).where(SimulationParameter.id == parameter_id) + entity = self.session.execute(stmt).scalar_one_or_none() + if entity is None: + raise EntityNotFoundError(f"Simulation parameter {parameter_id} not found") + self.session.delete(entity) diff --git a/services/unit_of_work.py b/services/unit_of_work.py new file mode 100644 index 0000000..6bb8cb8 --- /dev/null +++ b/services/unit_of_work.py @@ -0,0 +1,53 @@ +from __future__ import annotations + +from contextlib import AbstractContextManager +from typing import Callable + +from sqlalchemy.orm import Session + +from config.database import SessionLocal +from services.repositories import ( + FinancialInputRepository, + ProjectRepository, + ScenarioRepository, + SimulationParameterRepository, +) + + +class UnitOfWork(AbstractContextManager["UnitOfWork"]): + """Simple unit-of-work wrapper around SQLAlchemy sessions.""" + + def __init__(self, session_factory: Callable[[], Session] = SessionLocal) -> None: + self._session_factory = session_factory + self.session: Session | None = None + + def __enter__(self) -> "UnitOfWork": + self.session = self._session_factory() + self.projects = ProjectRepository(self.session) + self.scenarios = ScenarioRepository(self.session) + self.financial_inputs = FinancialInputRepository(self.session) + self.simulation_parameters = SimulationParameterRepository(self.session) + return self + + def __exit__(self, exc_type, exc_value, traceback) -> None: + assert self.session is not None + if exc_type is None: + self.session.commit() + else: + self.session.rollback() + self.session.close() + + def flush(self) -> None: + if not self.session: + raise RuntimeError("UnitOfWork session is not initialised") + self.session.flush() + + def commit(self) -> None: + if not self.session: + raise RuntimeError("UnitOfWork session is not initialised") + self.session.commit() + + def rollback(self) -> None: + if not self.session: + raise RuntimeError("UnitOfWork session is not initialised") + self.session.rollback() -- 2.39.5 From 8bf46b80c847ff8c2ca2c2e218f94afdd7521c97 Mon Sep 17 00:00:00 2001 From: zwitschi Date: Sun, 9 Nov 2025 17:17:42 +0100 Subject: [PATCH 008/109] feat: add pytest coverage for repository and unit-of-work behaviors --- changelog.md | 1 + tests/test_repositories.py | 156 +++++++++++++++++++++++++++++++++++++ 2 files changed, 157 insertions(+) create mode 100644 tests/test_repositories.py diff --git a/changelog.md b/changelog.md index 169fa8a..579efe5 100644 --- a/changelog.md +++ b/changelog.md @@ -5,3 +5,4 @@ - Captured current implementation status, requirements coverage, missing features, and prioritized roadmap in `calminer-docs/implementation_status.md` to guide future development. - Added core SQLAlchemy domain models, shared metadata descriptors, and Alembic migration setup (with initial schema snapshot) to establish the persistence layer foundation. - Introduced repository and unit-of-work helpers for projects, scenarios, financial inputs, and simulation parameters to support service-layer operations. +- Added SQLite-backed pytest coverage for repository and unit-of-work behaviours to validate persistence interactions. diff --git a/tests/test_repositories.py b/tests/test_repositories.py new file mode 100644 index 0000000..23ae50f --- /dev/null +++ b/tests/test_repositories.py @@ -0,0 +1,156 @@ +from __future__ import annotations + +from collections.abc import Iterator + +import pytest +from sqlalchemy import create_engine +from sqlalchemy.orm import Session, sessionmaker + +from config.database import Base +from models import ( + DistributionType, + FinancialCategory, + FinancialInput, + MiningOperationType, + Project, + Scenario, + ScenarioStatus, + SimulationParameter, + StochasticVariable, +) +from services.repositories import ( + FinancialInputRepository, + ProjectRepository, + ScenarioRepository, + SimulationParameterRepository, +) +from services.unit_of_work import UnitOfWork + + +@pytest.fixture() +def engine(): + engine = create_engine("sqlite:///:memory:", future=True) + Base.metadata.create_all(bind=engine) + try: + yield engine + finally: + Base.metadata.drop_all(bind=engine) + + +@pytest.fixture() +def session(engine) -> Iterator[Session]: + TestingSession = sessionmaker(bind=engine, expire_on_commit=False, future=True) + session = TestingSession() + try: + yield session + finally: + session.close() + + +def test_project_repository_create_and_list(session: Session) -> None: + repo = ProjectRepository(session) + project = Project(name="Project Alpha", operation_type=MiningOperationType.OPEN_PIT) + repo.create(project) + + projects = repo.list() + + assert len(projects) == 1 + assert projects[0].name == "Project Alpha" + + +def test_scenario_repository_get_with_children(session: Session) -> None: + project = Project(name="Project Beta", operation_type=MiningOperationType.UNDERGROUND) + scenario = Scenario(name="Scenario 1", project=project, status=ScenarioStatus.ACTIVE) + scenario.financial_inputs.append( + FinancialInput( + name="Lease Payment", + category=FinancialCategory.OPERATING_EXPENDITURE, + amount=10000, + currency="usd", + ) + ) + scenario.simulation_parameters.append( + SimulationParameter( + name="Copper Price", + distribution=DistributionType.NORMAL, + mean_value=3.5, + variable=StochasticVariable.METAL_PRICE, + ) + ) + + session.add(project) + session.flush() + + repo = ScenarioRepository(session) + retrieved = repo.get(scenario.id, with_children=True) + + assert retrieved.project.name == "Project Beta" + assert len(retrieved.financial_inputs) == 1 + assert retrieved.financial_inputs[0].currency == "USD" + assert len(retrieved.simulation_parameters) == 1 + assert ( + retrieved.simulation_parameters[0].variable + == StochasticVariable.METAL_PRICE + ) + + param_repo = SimulationParameterRepository(session) + params = param_repo.list_for_scenario(scenario.id) + assert len(params) == 1 + + +def test_financial_input_repository_bulk_upsert(session: Session) -> None: + project = Project(name="Project Gamma", operation_type=MiningOperationType.QUARRY) + scenario = Scenario(name="Scenario Bulk", project=project) + session.add(project) + session.flush() + + repo = FinancialInputRepository(session) + created = repo.bulk_upsert( + [ + FinancialInput( + scenario_id=scenario.id, + name="Explosives", + category=FinancialCategory.OPERATING_EXPENDITURE, + amount=5000, + currency="cad", + ), + FinancialInput( + scenario_id=scenario.id, + name="Equipment Lease", + category=FinancialCategory.OPERATING_EXPENDITURE, + amount=12000, + currency="cad", + ), + ] + ) + + assert len(created) == 2 + stored = repo.list_for_scenario(scenario.id) + assert len(stored) == 2 + assert all(item.currency == "CAD" for item in stored) + + +def test_unit_of_work_commit_and_rollback(engine) -> None: + TestingSession = sessionmaker(bind=engine, expire_on_commit=False, future=True) + + # Commit path + with UnitOfWork(session_factory=TestingSession) as uow: + uow.projects.create( + Project(name="Project Delta", operation_type=MiningOperationType.PLACER) + ) + + with TestingSession() as session: + projects = ProjectRepository(session).list() + assert len(projects) == 1 + + # Rollback path + with pytest.raises(RuntimeError): + with UnitOfWork(session_factory=TestingSession) as uow: + uow.projects.create( + Project(name="Project Epsilon", operation_type=MiningOperationType.OTHER) + ) + raise RuntimeError("trigger rollback") + + with TestingSession() as session: + projects = ProjectRepository(session).list() + assert len(projects) == 1 \ No newline at end of file -- 2.39.5 From 61b42b3041d5e2006c481d21468947500a710a5a Mon Sep 17 00:00:00 2001 From: zwitschi Date: Sun, 9 Nov 2025 17:23:10 +0100 Subject: [PATCH 009/109] feat: implement CRUD APIs for projects and scenarios with validated schemas --- .../versions/20251109_01_initial_schema.py | 77 ++++++++----- changelog.md | 1 + config/__init__.py | 1 + dependencies.py | 12 ++ main.py | 6 +- models/simulation_parameter.py | 14 ++- pyproject.toml | 3 + routes/__init__.py | 1 + routes/projects.py | 76 +++++++++++++ routes/scenarios.py | 103 ++++++++++++++++++ schemas/project.py | 37 +++++++ schemas/scenario.py | 66 +++++++++++ services/__init__.py | 1 + services/repositories.py | 20 +++- 14 files changed, 380 insertions(+), 38 deletions(-) create mode 100644 config/__init__.py create mode 100644 dependencies.py create mode 100644 routes/__init__.py create mode 100644 routes/projects.py create mode 100644 routes/scenarios.py create mode 100644 schemas/project.py create mode 100644 schemas/scenario.py create mode 100644 services/__init__.py diff --git a/alembic/versions/20251109_01_initial_schema.py b/alembic/versions/20251109_01_initial_schema.py index 020a56b..cf282d5 100644 --- a/alembic/versions/20251109_01_initial_schema.py +++ b/alembic/versions/20251109_01_initial_schema.py @@ -101,8 +101,10 @@ def upgrade() -> None: sa.Column("location", sa.String(length=255), nullable=True), sa.Column("operation_type", mining_operation_type, nullable=False), sa.Column("description", sa.Text(), nullable=True), - sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), - sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), + sa.Column("created_at", sa.DateTime(timezone=True), + server_default=sa.func.now(), nullable=False), + sa.Column("updated_at", sa.DateTime(timezone=True), + server_default=sa.func.now(), nullable=False), sa.PrimaryKeyConstraint("id"), sa.UniqueConstraint("name"), ) @@ -117,16 +119,21 @@ def upgrade() -> None: sa.Column("status", scenario_status, nullable=False), sa.Column("start_date", sa.Date(), nullable=True), sa.Column("end_date", sa.Date(), nullable=True), - sa.Column("discount_rate", sa.Numeric(precision=5, scale=2), nullable=True), + sa.Column("discount_rate", sa.Numeric( + precision=5, scale=2), nullable=True), sa.Column("currency", sa.String(length=3), nullable=True), sa.Column("primary_resource", resource_type, nullable=True), - sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), - sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), - sa.ForeignKeyConstraint(["project_id"], ["projects.id"], ondelete="CASCADE"), + sa.Column("created_at", sa.DateTime(timezone=True), + server_default=sa.func.now(), nullable=False), + sa.Column("updated_at", sa.DateTime(timezone=True), + server_default=sa.func.now(), nullable=False), + sa.ForeignKeyConstraint( + ["project_id"], ["projects.id"], ondelete="CASCADE"), sa.PrimaryKeyConstraint("id"), ) op.create_index(op.f("ix_scenarios_id"), "scenarios", ["id"], unique=False) - op.create_index(op.f("ix_scenarios_project_id"), "scenarios", ["project_id"], unique=False) + op.create_index(op.f("ix_scenarios_project_id"), + "scenarios", ["project_id"], unique=False) op.create_table( "financial_inputs", @@ -139,13 +146,18 @@ def upgrade() -> None: sa.Column("currency", sa.String(length=3), nullable=True), sa.Column("effective_date", sa.Date(), nullable=True), sa.Column("notes", sa.Text(), nullable=True), - sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), - sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), - sa.ForeignKeyConstraint(["scenario_id"], ["scenarios.id"], ondelete="CASCADE"), + sa.Column("created_at", sa.DateTime(timezone=True), + server_default=sa.func.now(), nullable=False), + sa.Column("updated_at", sa.DateTime(timezone=True), + server_default=sa.func.now(), nullable=False), + sa.ForeignKeyConstraint( + ["scenario_id"], ["scenarios.id"], ondelete="CASCADE"), sa.PrimaryKeyConstraint("id"), ) - op.create_index(op.f("ix_financial_inputs_id"), "financial_inputs", ["id"], unique=False) - op.create_index(op.f("ix_financial_inputs_scenario_id"), "financial_inputs", ["scenario_id"], unique=False) + op.create_index(op.f("ix_financial_inputs_id"), + "financial_inputs", ["id"], unique=False) + op.create_index(op.f("ix_financial_inputs_scenario_id"), + "financial_inputs", ["scenario_id"], unique=False) op.create_table( "simulation_parameters", @@ -155,28 +167,41 @@ def upgrade() -> None: sa.Column("distribution", distribution_type, nullable=False), sa.Column("variable", stochastic_variable, nullable=True), sa.Column("resource_type", resource_type, nullable=True), - sa.Column("mean_value", sa.Numeric(precision=18, scale=4), nullable=True), - sa.Column("standard_deviation", sa.Numeric(precision=18, scale=4), nullable=True), - sa.Column("minimum_value", sa.Numeric(precision=18, scale=4), nullable=True), - sa.Column("maximum_value", sa.Numeric(precision=18, scale=4), nullable=True), + sa.Column("mean_value", sa.Numeric( + precision=18, scale=4), nullable=True), + sa.Column("standard_deviation", sa.Numeric( + precision=18, scale=4), nullable=True), + sa.Column("minimum_value", sa.Numeric( + precision=18, scale=4), nullable=True), + sa.Column("maximum_value", sa.Numeric( + precision=18, scale=4), nullable=True), sa.Column("unit", sa.String(length=32), nullable=True), - sa.Column("metadata", sa.JSON(), nullable=True), - sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), - sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), - sa.ForeignKeyConstraint(["scenario_id"], ["scenarios.id"], ondelete="CASCADE"), + sa.Column("configuration", sa.JSON(), nullable=True), + sa.Column("created_at", sa.DateTime(timezone=True), + server_default=sa.func.now(), nullable=False), + sa.Column("updated_at", sa.DateTime(timezone=True), + server_default=sa.func.now(), nullable=False), + sa.ForeignKeyConstraint( + ["scenario_id"], ["scenarios.id"], ondelete="CASCADE"), sa.PrimaryKeyConstraint("id"), ) - op.create_index(op.f("ix_simulation_parameters_id"), "simulation_parameters", ["id"], unique=False) - op.create_index(op.f("ix_simulation_parameters_scenario_id"), "simulation_parameters", ["scenario_id"], unique=False) + op.create_index(op.f("ix_simulation_parameters_id"), + "simulation_parameters", ["id"], unique=False) + op.create_index(op.f("ix_simulation_parameters_scenario_id"), + "simulation_parameters", ["scenario_id"], unique=False) def downgrade() -> None: - op.drop_index(op.f("ix_simulation_parameters_scenario_id"), table_name="simulation_parameters") - op.drop_index(op.f("ix_simulation_parameters_id"), table_name="simulation_parameters") + op.drop_index(op.f("ix_simulation_parameters_scenario_id"), + table_name="simulation_parameters") + op.drop_index(op.f("ix_simulation_parameters_id"), + table_name="simulation_parameters") op.drop_table("simulation_parameters") - op.drop_index(op.f("ix_financial_inputs_scenario_id"), table_name="financial_inputs") - op.drop_index(op.f("ix_financial_inputs_id"), table_name="financial_inputs") + op.drop_index(op.f("ix_financial_inputs_scenario_id"), + table_name="financial_inputs") + op.drop_index(op.f("ix_financial_inputs_id"), + table_name="financial_inputs") op.drop_table("financial_inputs") op.drop_index(op.f("ix_scenarios_project_id"), table_name="scenarios") diff --git a/changelog.md b/changelog.md index 579efe5..a436c92 100644 --- a/changelog.md +++ b/changelog.md @@ -6,3 +6,4 @@ - Added core SQLAlchemy domain models, shared metadata descriptors, and Alembic migration setup (with initial schema snapshot) to establish the persistence layer foundation. - Introduced repository and unit-of-work helpers for projects, scenarios, financial inputs, and simulation parameters to support service-layer operations. - Added SQLite-backed pytest coverage for repository and unit-of-work behaviours to validate persistence interactions. +- Exposed project and scenario CRUD APIs with validated schemas and integrated them into the FastAPI application. diff --git a/config/__init__.py b/config/__init__.py new file mode 100644 index 0000000..56096f2 --- /dev/null +++ b/config/__init__.py @@ -0,0 +1 @@ +"""Configuration package.""" diff --git a/dependencies.py b/dependencies.py new file mode 100644 index 0000000..e492586 --- /dev/null +++ b/dependencies.py @@ -0,0 +1,12 @@ +from __future__ import annotations + +from collections.abc import Generator + +from services.unit_of_work import UnitOfWork + + +def get_unit_of_work() -> Generator[UnitOfWork, None, None]: + """FastAPI dependency yielding a unit-of-work instance.""" + + with UnitOfWork() as uow: + yield uow diff --git a/main.py b/main.py index e1cbc9a..4fd284f 100644 --- a/main.py +++ b/main.py @@ -10,6 +10,8 @@ from models import ( Scenario, SimulationParameter, ) +from routes.projects import router as projects_router +from routes.scenarios import router as scenarios_router # Initialize database schema (imports above ensure models are registered) Base.metadata.create_all(bind=engine) @@ -29,5 +31,7 @@ async def health() -> dict[str, str]: return {"status": "ok"} -app.mount("/static", StaticFiles(directory="static"), name="static") +app.include_router(projects_router) +app.include_router(scenarios_router) +app.mount("/static", StaticFiles(directory="static"), name="static") diff --git a/models/simulation_parameter.py b/models/simulation_parameter.py index e77b1b2..5b7ac16 100644 --- a/models/simulation_parameter.py +++ b/models/simulation_parameter.py @@ -52,12 +52,16 @@ class SimulationParameter(Base): resource_type: Mapped[ResourceType | None] = mapped_column( SQLEnum(ResourceType), nullable=True ) - mean_value: Mapped[float | None] = mapped_column(Numeric(18, 4), nullable=True) - standard_deviation: Mapped[float | None] = mapped_column(Numeric(18, 4), nullable=True) - minimum_value: Mapped[float | None] = mapped_column(Numeric(18, 4), nullable=True) - maximum_value: Mapped[float | None] = mapped_column(Numeric(18, 4), nullable=True) + mean_value: Mapped[float | None] = mapped_column( + Numeric(18, 4), nullable=True) + standard_deviation: Mapped[float | None] = mapped_column( + Numeric(18, 4), nullable=True) + minimum_value: Mapped[float | None] = mapped_column( + Numeric(18, 4), nullable=True) + maximum_value: Mapped[float | None] = mapped_column( + Numeric(18, 4), nullable=True) unit: Mapped[str | None] = mapped_column(String(32), nullable=True) - metadata: Mapped[dict | None] = mapped_column(JSON, nullable=True) + configuration: Mapped[dict | None] = mapped_column(JSON, nullable=True) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False, server_default=func.now() ) diff --git a/pyproject.toml b/pyproject.toml index 35be63b..de07a01 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,3 +14,6 @@ exclude = ''' )/ ''' +[tool.pytest.ini_options] +pythonpath = ["."] + diff --git a/routes/__init__.py b/routes/__init__.py new file mode 100644 index 0000000..3f06ec5 --- /dev/null +++ b/routes/__init__.py @@ -0,0 +1 @@ +"""API route registrations.""" \ No newline at end of file diff --git a/routes/projects.py b/routes/projects.py new file mode 100644 index 0000000..a362e1f --- /dev/null +++ b/routes/projects.py @@ -0,0 +1,76 @@ +from __future__ import annotations + +from typing import List + +from fastapi import APIRouter, Depends, HTTPException, status + +from dependencies import get_unit_of_work +from models import Project +from schemas.project import ProjectCreate, ProjectRead, ProjectUpdate +from services.exceptions import EntityConflictError, EntityNotFoundError +from services.unit_of_work import UnitOfWork + +router = APIRouter(prefix="/projects", tags=["Projects"]) + + +def _to_read_model(project: Project) -> ProjectRead: + return ProjectRead.model_validate(project) + + +@router.get("", response_model=List[ProjectRead]) +def list_projects(uow: UnitOfWork = Depends(get_unit_of_work)) -> List[ProjectRead]: + projects = uow.projects.list() + return [_to_read_model(project) for project in projects] + + +@router.post("", response_model=ProjectRead, status_code=status.HTTP_201_CREATED) +def create_project( + payload: ProjectCreate, uow: UnitOfWork = Depends(get_unit_of_work) +) -> ProjectRead: + project = Project(**payload.model_dump()) + try: + created = uow.projects.create(project) + except EntityConflictError as exc: + raise HTTPException( + status_code=status.HTTP_409_CONFLICT, detail=str(exc) + ) from exc + return _to_read_model(created) + + +@router.get("/{project_id}", response_model=ProjectRead) +def get_project(project_id: int, uow: UnitOfWork = Depends(get_unit_of_work)) -> ProjectRead: + try: + project = uow.projects.get(project_id) + except EntityNotFoundError as exc: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)) from exc + return _to_read_model(project) + + +@router.put("/{project_id}", response_model=ProjectRead) +def update_project( + project_id: int, + payload: ProjectUpdate, + uow: UnitOfWork = Depends(get_unit_of_work), +) -> ProjectRead: + try: + project = uow.projects.get(project_id) + except EntityNotFoundError as exc: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)) from exc + + update_data = payload.model_dump(exclude_unset=True) + for field, value in update_data.items(): + setattr(project, field, value) + + uow.flush() + return _to_read_model(project) + + +@router.delete("/{project_id}", status_code=status.HTTP_204_NO_CONTENT) +def delete_project(project_id: int, uow: UnitOfWork = Depends(get_unit_of_work)) -> None: + try: + uow.projects.delete(project_id) + except EntityNotFoundError as exc: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)) from exc diff --git a/routes/scenarios.py b/routes/scenarios.py new file mode 100644 index 0000000..c34d81d --- /dev/null +++ b/routes/scenarios.py @@ -0,0 +1,103 @@ +from __future__ import annotations + +from typing import List + +from fastapi import APIRouter, Depends, HTTPException, status + +from dependencies import get_unit_of_work +from models import Scenario +from schemas.scenario import ScenarioCreate, ScenarioRead, ScenarioUpdate +from services.exceptions import EntityConflictError, EntityNotFoundError +from services.unit_of_work import UnitOfWork + +router = APIRouter(tags=["Scenarios"]) + + +def _to_read_model(scenario: Scenario) -> ScenarioRead: + return ScenarioRead.model_validate(scenario) + + +@router.get( + "/projects/{project_id}/scenarios", + response_model=List[ScenarioRead], +) +def list_scenarios_for_project( + project_id: int, uow: UnitOfWork = Depends(get_unit_of_work) +) -> List[ScenarioRead]: + try: + uow.projects.get(project_id) + except EntityNotFoundError as exc: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)) from exc + + scenarios = uow.scenarios.list_for_project(project_id) + return [_to_read_model(scenario) for scenario in scenarios] + + +@router.post( + "/projects/{project_id}/scenarios", + response_model=ScenarioRead, + status_code=status.HTTP_201_CREATED, +) +def create_scenario_for_project( + project_id: int, + payload: ScenarioCreate, + uow: UnitOfWork = Depends(get_unit_of_work), +) -> ScenarioRead: + try: + uow.projects.get(project_id) + except EntityNotFoundError as exc: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)) from exc + + scenario = Scenario(project_id=project_id, **payload.model_dump()) + + try: + created = uow.scenarios.create(scenario) + except EntityConflictError as exc: + raise HTTPException( + status_code=status.HTTP_409_CONFLICT, detail=str(exc)) from exc + return _to_read_model(created) + + +@router.get("/scenarios/{scenario_id}", response_model=ScenarioRead) +def get_scenario( + scenario_id: int, uow: UnitOfWork = Depends(get_unit_of_work) +) -> ScenarioRead: + try: + scenario = uow.scenarios.get(scenario_id) + except EntityNotFoundError as exc: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)) from exc + return _to_read_model(scenario) + + +@router.put("/scenarios/{scenario_id}", response_model=ScenarioRead) +def update_scenario( + scenario_id: int, + payload: ScenarioUpdate, + uow: UnitOfWork = Depends(get_unit_of_work), +) -> ScenarioRead: + try: + scenario = uow.scenarios.get(scenario_id) + except EntityNotFoundError as exc: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)) from exc + + update_data = payload.model_dump(exclude_unset=True) + for field, value in update_data.items(): + setattr(scenario, field, value) + + uow.flush() + return _to_read_model(scenario) + + +@router.delete("/scenarios/{scenario_id}", status_code=status.HTTP_204_NO_CONTENT) +def delete_scenario( + scenario_id: int, uow: UnitOfWork = Depends(get_unit_of_work) +) -> None: + try: + uow.scenarios.delete(scenario_id) + except EntityNotFoundError as exc: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)) from exc diff --git a/schemas/project.py b/schemas/project.py new file mode 100644 index 0000000..1b0107d --- /dev/null +++ b/schemas/project.py @@ -0,0 +1,37 @@ +from __future__ import annotations + +from datetime import datetime + +from pydantic import BaseModel, ConfigDict + +from models import MiningOperationType + + +class ProjectBase(BaseModel): + name: str + location: str | None = None + operation_type: MiningOperationType + description: str | None = None + + model_config = ConfigDict(extra="forbid") + + +class ProjectCreate(ProjectBase): + pass + + +class ProjectUpdate(BaseModel): + name: str | None = None + location: str | None = None + operation_type: MiningOperationType | None = None + description: str | None = None + + model_config = ConfigDict(extra="forbid") + + +class ProjectRead(ProjectBase): + id: int + created_at: datetime + updated_at: datetime + + model_config = ConfigDict(from_attributes=True) diff --git a/schemas/scenario.py b/schemas/scenario.py new file mode 100644 index 0000000..6681cba --- /dev/null +++ b/schemas/scenario.py @@ -0,0 +1,66 @@ +from __future__ import annotations + +from datetime import date, datetime + +from pydantic import BaseModel, ConfigDict, field_validator + +from models import ResourceType, ScenarioStatus + + +class ScenarioBase(BaseModel): + name: str + description: str | None = None + status: ScenarioStatus = ScenarioStatus.DRAFT + start_date: date | None = None + end_date: date | None = None + discount_rate: float | None = None + currency: str | None = None + primary_resource: ResourceType | None = None + + model_config = ConfigDict(extra="forbid") + + @field_validator("currency") + @classmethod + def normalise_currency(cls, value: str | None) -> str | None: + if value is None: + return value + value = value.upper() + if len(value) != 3: + raise ValueError("Currency code must be a 3-letter ISO value") + return value + + +class ScenarioCreate(ScenarioBase): + pass + + +class ScenarioUpdate(BaseModel): + name: str | None = None + description: str | None = None + status: ScenarioStatus | None = None + start_date: date | None = None + end_date: date | None = None + discount_rate: float | None = None + currency: str | None = None + primary_resource: ResourceType | None = None + + model_config = ConfigDict(extra="forbid") + + @field_validator("currency") + @classmethod + def normalise_currency(cls, value: str | None) -> str | None: + if value is None: + return value + value = value.upper() + if len(value) != 3: + raise ValueError("Currency code must be a 3-letter ISO value") + return value + + +class ScenarioRead(ScenarioBase): + id: int + project_id: int + created_at: datetime + updated_at: datetime + + model_config = ConfigDict(from_attributes=True) diff --git a/services/__init__.py b/services/__init__.py new file mode 100644 index 0000000..1476ae9 --- /dev/null +++ b/services/__init__.py @@ -0,0 +1 @@ +"""Service layer utilities.""" diff --git a/services/repositories.py b/services/repositories.py index bafff7b..21c10da 100644 --- a/services/repositories.py +++ b/services/repositories.py @@ -35,7 +35,8 @@ class ProjectRepository: try: self.session.flush() except IntegrityError as exc: # pragma: no cover - reliance on DB constraints - raise EntityConflictError("Project violates uniqueness constraints") from exc + raise EntityConflictError( + "Project violates uniqueness constraints") from exc return project def delete(self, project_id: int) -> None: @@ -64,7 +65,10 @@ class ScenarioRepository: joinedload(Scenario.financial_inputs), joinedload(Scenario.simulation_parameters), ) - scenario = self.session.execute(stmt).scalar_one_or_none() + result = self.session.execute(stmt) + if with_children: + result = result.unique() + scenario = result.scalar_one_or_none() if scenario is None: raise EntityNotFoundError(f"Scenario {scenario_id} not found") return scenario @@ -102,7 +106,8 @@ class FinancialInputRepository: try: self.session.flush() except IntegrityError as exc: # pragma: no cover - raise EntityConflictError("Financial input violates constraints") from exc + raise EntityConflictError( + "Financial input violates constraints") from exc return entities def delete(self, input_id: int) -> None: @@ -135,12 +140,15 @@ class SimulationParameterRepository: try: self.session.flush() except IntegrityError as exc: # pragma: no cover - raise EntityConflictError("Simulation parameter violates constraints") from exc + raise EntityConflictError( + "Simulation parameter violates constraints") from exc return entities def delete(self, parameter_id: int) -> None: - stmt = select(SimulationParameter).where(SimulationParameter.id == parameter_id) + stmt = select(SimulationParameter).where( + SimulationParameter.id == parameter_id) entity = self.session.execute(stmt).scalar_one_or_none() if entity is None: - raise EntityNotFoundError(f"Simulation parameter {parameter_id} not found") + raise EntityNotFoundError( + f"Simulation parameter {parameter_id} not found") self.session.delete(entity) -- 2.39.5 From 191500aeb735643b3c4cae8ae2acfc507e2bde82 Mon Sep 17 00:00:00 2001 From: zwitschi Date: Sun, 9 Nov 2025 17:27:46 +0100 Subject: [PATCH 010/109] feat: add project and scenario templates for detailed views and forms --- templates/projects/detail.html | 78 ++++++++++++++++++++++ templates/projects/form.html | 52 +++++++++++++++ templates/projects/list.html | 42 ++++++++++++ templates/scenarios/detail.html | 112 ++++++++++++++++++++++++++++++++ templates/scenarios/form.html | 79 ++++++++++++++++++++++ 5 files changed, 363 insertions(+) create mode 100644 templates/projects/detail.html create mode 100644 templates/projects/form.html create mode 100644 templates/projects/list.html create mode 100644 templates/scenarios/detail.html create mode 100644 templates/scenarios/form.html diff --git a/templates/projects/detail.html b/templates/projects/detail.html new file mode 100644 index 0000000..e246a8e --- /dev/null +++ b/templates/projects/detail.html @@ -0,0 +1,78 @@ +{% extends "base.html" %} +{% block title %}{{ project.name }} · Project · CalMiner{% endblock %} + +{% block content %} + + + + +
+

Project Overview

+
+
+
Location
+
{{ project.location or '—' }}
+
+
+
Description
+
{{ project.description or 'No description provided.' }}
+
+
+
Created
+
{{ project.created_at.strftime('%Y-%m-%d %H:%M') }}
+
+
+
Updated
+
{{ project.updated_at.strftime('%Y-%m-%d %H:%M') }}
+
+
+
+ +
+
+

Scenarios

+ Add Scenario +
+ {% if scenarios %} + + + + + + + + + + + + {% for scenario in scenarios %} + + + + + + + + {% endfor %} + +
NameStatusCurrencyPrimary Resource
{{ scenario.name }}{{ scenario.status.value.title() }}{{ scenario.currency or '—' }}{{ scenario.primary_resource.value.replace('_', ' ') | title if scenario.primary_resource else '—' }} + View + Edit +
+ {% else %} +

No scenarios yet.

+ {% endif %} +
+{% endblock %} diff --git a/templates/projects/form.html b/templates/projects/form.html new file mode 100644 index 0000000..1b03c3b --- /dev/null +++ b/templates/projects/form.html @@ -0,0 +1,52 @@ +{% extends "base.html" %} +{% block title %}{% if project %}Edit {{ project.name }}{% else %}New Project{% endif %} · CalMiner{% endblock %} + +{% block content %} + + + + +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ Cancel + +
+
+{% endblock %} diff --git a/templates/projects/list.html b/templates/projects/list.html new file mode 100644 index 0000000..39703c5 --- /dev/null +++ b/templates/projects/list.html @@ -0,0 +1,42 @@ +{% extends "base.html" %} +{% block title %}Projects · CalMiner{% endblock %} + +{% block content %} + + + {% if projects %} + + + + + + + + + + + + {% for project in projects %} + + + + + + + + {% endfor %} + +
NameLocationTypeScenarios
{{ project.name }}{{ project.location or '—' }}{{ project.operation_type.value.replace('_', ' ') | title }}{{ project.scenario_count }} + View + Edit +
+ {% else %} +

No projects yet. Create your first project.

+ {% endif %} +{% endblock %} diff --git a/templates/scenarios/detail.html b/templates/scenarios/detail.html new file mode 100644 index 0000000..edb3466 --- /dev/null +++ b/templates/scenarios/detail.html @@ -0,0 +1,112 @@ +{% extends "base.html" %} +{% block title %}{{ scenario.name }} · Scenario · CalMiner{% endblock %} + +{% block content %} + + + + +
+

Scenario Details

+
+
+
Description
+
{{ scenario.description or 'No description provided.' }}
+
+
+
Timeline
+
+ {% if scenario.start_date %} + {{ scenario.start_date }} + {% else %} + — + {% endif %} + → + {% if scenario.end_date %} + {{ scenario.end_date }} + {% else %} + — + {% endif %} +
+
+
+
Discount Rate
+
{{ scenario.discount_rate or '—' }}
+
+
+
Currency
+
{{ scenario.currency or '—' }}
+
+
+
Primary Resource
+
{{ scenario.primary_resource.value.replace('_', ' ') | title if scenario.primary_resource else '—' }}
+
+
+
+ +
+

Financial Inputs

+ {% if financial_inputs %} + + + + + + + + + + + {% for item in financial_inputs %} + + + + + + + {% endfor %} + +
NameCategoryAmountCurrency
{{ item.name }}{{ item.category.value.title() }}{{ '{:,.2f}'.format(item.amount) }}{{ item.currency or '—' }}
+ {% else %} +

No financial inputs recorded.

+ {% endif %} +
+ +
+

Simulation Parameters

+ {% if simulation_parameters %} + + + + + + + + + + + {% for param in simulation_parameters %} + + + + + + + {% endfor %} + +
NameDistributionVariableResource
{{ param.name }}{{ param.distribution.value.title() }}{{ param.variable.value.replace('_', ' ') | title if param.variable else '—' }}{{ param.resource_type.value.replace('_', ' ') | title if param.resource_type else '—' }}
+ {% else %} +

No simulation parameters defined.

+ {% endif %} +
+{% endblock %} diff --git a/templates/scenarios/form.html b/templates/scenarios/form.html new file mode 100644 index 0000000..8b4947d --- /dev/null +++ b/templates/scenarios/form.html @@ -0,0 +1,79 @@ +{% extends "base.html" %} +{% block title %}{% if scenario %}Edit {{ scenario.name }}{% else %}New Scenario{% endif %} · CalMiner{% endblock %} + +{% block content %} + + + + +
+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ + +
+ +
+ Cancel + +
+
+{% endblock %} -- 2.39.5 From d36611606dc126fcaeabfd6c5d3b19ff6046158f Mon Sep 17 00:00:00 2001 From: zwitschi Date: Sun, 9 Nov 2025 17:32:23 +0100 Subject: [PATCH 011/109] feat: connect project and scenario routers to new Jinja2 views with forms and error handling --- changelog.md | 1 + routes/projects.py | 234 ++++++++++++++++++++++++++- routes/scenarios.py | 288 +++++++++++++++++++++++++++++++++- services/repositories.py | 6 +- templates/projects/form.html | 4 + templates/scenarios/form.html | 4 + 6 files changed, 531 insertions(+), 6 deletions(-) diff --git a/changelog.md b/changelog.md index a436c92..3f0f5e3 100644 --- a/changelog.md +++ b/changelog.md @@ -7,3 +7,4 @@ - Introduced repository and unit-of-work helpers for projects, scenarios, financial inputs, and simulation parameters to support service-layer operations. - Added SQLite-backed pytest coverage for repository and unit-of-work behaviours to validate persistence interactions. - Exposed project and scenario CRUD APIs with validated schemas and integrated them into the FastAPI application. +- Connected project and scenario routers to new Jinja2 list/detail/edit views with HTML forms and redirects. diff --git a/routes/projects.py b/routes/projects.py index a362e1f..fb937c6 100644 --- a/routes/projects.py +++ b/routes/projects.py @@ -2,21 +2,30 @@ from __future__ import annotations from typing import List -from fastapi import APIRouter, Depends, HTTPException, status +from fastapi import APIRouter, Depends, Form, HTTPException, Request, status +from fastapi.responses import HTMLResponse, RedirectResponse +from fastapi.templating import Jinja2Templates from dependencies import get_unit_of_work -from models import Project +from models import MiningOperationType, Project from schemas.project import ProjectCreate, ProjectRead, ProjectUpdate from services.exceptions import EntityConflictError, EntityNotFoundError from services.unit_of_work import UnitOfWork router = APIRouter(prefix="/projects", tags=["Projects"]) +templates = Jinja2Templates(directory="templates") def _to_read_model(project: Project) -> ProjectRead: return ProjectRead.model_validate(project) +def _operation_type_choices() -> list[tuple[str, str]]: + return [ + (op.value, op.name.replace("_", " ").title()) for op in MiningOperationType + ] + + @router.get("", response_model=List[ProjectRead]) def list_projects(uow: UnitOfWork = Depends(get_unit_of_work)) -> List[ProjectRead]: projects = uow.projects.list() @@ -74,3 +83,224 @@ def delete_project(project_id: int, uow: UnitOfWork = Depends(get_unit_of_work)) except EntityNotFoundError as exc: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)) from exc + + +@router.get( + "/ui", + response_class=HTMLResponse, + include_in_schema=False, + name="projects.project_list_page", +) +def project_list_page( + request: Request, uow: UnitOfWork = Depends(get_unit_of_work) +) -> HTMLResponse: + projects = uow.projects.list(with_children=True) + for project in projects: + setattr(project, "scenario_count", len(project.scenarios)) + return templates.TemplateResponse( + "projects/list.html", + { + "request": request, + "projects": projects, + }, + ) + + +@router.get( + "/create", + response_class=HTMLResponse, + include_in_schema=False, + name="projects.create_project_form", +) +def create_project_form(request: Request) -> HTMLResponse: + return templates.TemplateResponse( + "projects/form.html", + { + "request": request, + "project": None, + "operation_types": _operation_type_choices(), + "form_action": request.url_for("projects.create_project_submit"), + "cancel_url": request.url_for("projects.project_list_page"), + }, + ) + + +@router.post( + "/create", + include_in_schema=False, + name="projects.create_project_submit", +) +def create_project_submit( + request: Request, + name: str = Form(...), + location: str | None = Form(None), + operation_type: str = Form(...), + description: str | None = Form(None), + uow: UnitOfWork = Depends(get_unit_of_work), +): + def _normalise(value: str | None) -> str | None: + if value is None: + return None + value = value.strip() + return value or None + + try: + op_type = MiningOperationType(operation_type) + except ValueError as exc: + return templates.TemplateResponse( + "projects/form.html", + { + "request": request, + "project": None, + "operation_types": _operation_type_choices(), + "form_action": request.url_for("projects.create_project_submit"), + "cancel_url": request.url_for("projects.project_list_page"), + "error": "Invalid operation type.", + }, + status_code=status.HTTP_400_BAD_REQUEST, + ) + + project = Project( + name=name.strip(), + location=_normalise(location), + operation_type=op_type, + description=_normalise(description), + ) + try: + uow.projects.create(project) + except EntityConflictError as exc: + return templates.TemplateResponse( + "projects/form.html", + { + "request": request, + "project": project, + "operation_types": _operation_type_choices(), + "form_action": request.url_for("projects.create_project_submit"), + "cancel_url": request.url_for("projects.project_list_page"), + "error": "Project with this name already exists.", + }, + status_code=status.HTTP_409_CONFLICT, + ) + + return RedirectResponse( + request.url_for("projects.project_list_page"), + status_code=status.HTTP_303_SEE_OTHER, + ) + + +@router.get( + "/{project_id}/view", + response_class=HTMLResponse, + include_in_schema=False, + name="projects.view_project", +) +def view_project( + project_id: int, request: Request, uow: UnitOfWork = Depends(get_unit_of_work) +) -> HTMLResponse: + try: + project = uow.projects.get(project_id, with_children=True) + except EntityNotFoundError as exc: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail=str(exc) + ) from exc + + scenarios = sorted(project.scenarios, key=lambda s: s.created_at) + return templates.TemplateResponse( + "projects/detail.html", + { + "request": request, + "project": project, + "scenarios": scenarios, + }, + ) + + +@router.get( + "/{project_id}/edit", + response_class=HTMLResponse, + include_in_schema=False, + name="projects.edit_project_form", +) +def edit_project_form( + project_id: int, request: Request, uow: UnitOfWork = Depends(get_unit_of_work) +) -> HTMLResponse: + try: + project = uow.projects.get(project_id) + except EntityNotFoundError as exc: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail=str(exc) + ) from exc + + return templates.TemplateResponse( + "projects/form.html", + { + "request": request, + "project": project, + "operation_types": _operation_type_choices(), + "form_action": request.url_for( + "projects.edit_project_submit", project_id=project_id + ), + "cancel_url": request.url_for( + "projects.view_project", project_id=project_id + ), + }, + ) + + +@router.post( + "/{project_id}/edit", + include_in_schema=False, + name="projects.edit_project_submit", +) +def edit_project_submit( + project_id: int, + request: Request, + name: str = Form(...), + location: str | None = Form(None), + operation_type: str | None = Form(None), + description: str | None = Form(None), + uow: UnitOfWork = Depends(get_unit_of_work), +): + try: + project = uow.projects.get(project_id) + except EntityNotFoundError as exc: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail=str(exc) + ) from exc + + def _normalise(value: str | None) -> str | None: + if value is None: + return None + value = value.strip() + return value or None + + project.name = name.strip() + project.location = _normalise(location) + if operation_type: + try: + project.operation_type = MiningOperationType(operation_type) + except ValueError as exc: + return templates.TemplateResponse( + "projects/form.html", + { + "request": request, + "project": project, + "operation_types": _operation_type_choices(), + "form_action": request.url_for( + "projects.edit_project_submit", project_id=project_id + ), + "cancel_url": request.url_for( + "projects.view_project", project_id=project_id + ), + "error": "Invalid operation type.", + }, + status_code=status.HTTP_400_BAD_REQUEST, + ) + project.description = _normalise(description) + + uow.flush() + + return RedirectResponse( + request.url_for("projects.view_project", project_id=project_id), + status_code=status.HTTP_303_SEE_OTHER, + ) diff --git a/routes/scenarios.py b/routes/scenarios.py index c34d81d..465534d 100644 --- a/routes/scenarios.py +++ b/routes/scenarios.py @@ -1,22 +1,39 @@ from __future__ import annotations +from datetime import date from typing import List -from fastapi import APIRouter, Depends, HTTPException, status +from fastapi import APIRouter, Depends, Form, HTTPException, Request, status +from fastapi.responses import HTMLResponse, RedirectResponse +from fastapi.templating import Jinja2Templates from dependencies import get_unit_of_work -from models import Scenario +from models import ResourceType, Scenario, ScenarioStatus from schemas.scenario import ScenarioCreate, ScenarioRead, ScenarioUpdate from services.exceptions import EntityConflictError, EntityNotFoundError from services.unit_of_work import UnitOfWork router = APIRouter(tags=["Scenarios"]) +templates = Jinja2Templates(directory="templates") def _to_read_model(scenario: Scenario) -> ScenarioRead: return ScenarioRead.model_validate(scenario) +def _resource_type_choices() -> list[tuple[str, str]]: + return [ + (resource.value, resource.value.replace("_", " ").title()) + for resource in ResourceType + ] + + +def _scenario_status_choices() -> list[tuple[str, str]]: + return [ + (status.value, status.value.title()) for status in ScenarioStatus + ] + + @router.get( "/projects/{project_id}/scenarios", response_model=List[ScenarioRead], @@ -101,3 +118,270 @@ def delete_scenario( except EntityNotFoundError as exc: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)) from exc + + +def _normalise(value: str | None) -> str | None: + if value is None: + return None + value = value.strip() + return value or None + + +def _parse_date(value: str | None) -> date | None: + value = _normalise(value) + if not value: + return None + return date.fromisoformat(value) + + +def _parse_discount_rate(value: str | None) -> float | None: + value = _normalise(value) + if not value: + return None + try: + return float(value) + except ValueError: + return None + + +@router.get( + "/projects/{project_id}/scenarios/new", + response_class=HTMLResponse, + include_in_schema=False, + name="scenarios.create_scenario_form", +) +def create_scenario_form( + project_id: int, request: Request, uow: UnitOfWork = Depends(get_unit_of_work) +) -> HTMLResponse: + try: + project = uow.projects.get(project_id) + except EntityNotFoundError as exc: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail=str(exc) + ) from exc + + return templates.TemplateResponse( + "scenarios/form.html", + { + "request": request, + "project": project, + "scenario": None, + "scenario_statuses": _scenario_status_choices(), + "resource_types": _resource_type_choices(), + "form_action": request.url_for( + "scenarios.create_scenario_submit", project_id=project_id + ), + "cancel_url": request.url_for( + "projects.view_project", project_id=project_id + ), + }, + ) + + +@router.post( + "/projects/{project_id}/scenarios/new", + include_in_schema=False, + name="scenarios.create_scenario_submit", +) +def create_scenario_submit( + project_id: int, + request: Request, + name: str = Form(...), + description: str | None = Form(None), + status_value: str = Form(ScenarioStatus.DRAFT.value), + start_date: str | None = Form(None), + end_date: str | None = Form(None), + discount_rate: str | None = Form(None), + currency: str | None = Form(None), + primary_resource: str | None = Form(None), + uow: UnitOfWork = Depends(get_unit_of_work), +): + try: + project = uow.projects.get(project_id) + except EntityNotFoundError as exc: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail=str(exc) + ) from exc + + try: + status_enum = ScenarioStatus(status_value) + except ValueError: + status_enum = ScenarioStatus.DRAFT + + resource_enum = None + if primary_resource: + try: + resource_enum = ResourceType(primary_resource) + except ValueError: + resource_enum = None + + currency_value = _normalise(currency) + currency_value = currency_value.upper() if currency_value else None + + scenario = Scenario( + project_id=project_id, + name=name.strip(), + description=_normalise(description), + status=status_enum, + start_date=_parse_date(start_date), + end_date=_parse_date(end_date), + discount_rate=_parse_discount_rate(discount_rate), + currency=currency_value, + primary_resource=resource_enum, + ) + + try: + uow.scenarios.create(scenario) + except EntityConflictError as exc: + return templates.TemplateResponse( + "scenarios/form.html", + { + "request": request, + "project": project, + "scenario": scenario, + "scenario_statuses": _scenario_status_choices(), + "resource_types": _resource_type_choices(), + "form_action": request.url_for( + "scenarios.create_scenario_submit", project_id=project_id + ), + "cancel_url": request.url_for( + "projects.view_project", project_id=project_id + ), + "error": "Scenario could not be created.", + }, + status_code=status.HTTP_409_CONFLICT, + ) + + return RedirectResponse( + request.url_for("projects.view_project", project_id=project_id), + status_code=status.HTTP_303_SEE_OTHER, + ) + + +@router.get( + "/scenarios/{scenario_id}/view", + response_class=HTMLResponse, + include_in_schema=False, + name="scenarios.view_scenario", +) +def view_scenario( + scenario_id: int, request: Request, uow: UnitOfWork = Depends(get_unit_of_work) +) -> HTMLResponse: + try: + scenario = uow.scenarios.get(scenario_id, with_children=True) + except EntityNotFoundError as exc: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail=str(exc) + ) from exc + + project = uow.projects.get(scenario.project_id) + financial_inputs = sorted( + scenario.financial_inputs, key=lambda item: item.created_at + ) + simulation_parameters = sorted( + scenario.simulation_parameters, key=lambda item: item.created_at + ) + + return templates.TemplateResponse( + "scenarios/detail.html", + { + "request": request, + "project": project, + "scenario": scenario, + "financial_inputs": financial_inputs, + "simulation_parameters": simulation_parameters, + }, + ) + + +@router.get( + "/scenarios/{scenario_id}/edit", + response_class=HTMLResponse, + include_in_schema=False, + name="scenarios.edit_scenario_form", +) +def edit_scenario_form( + scenario_id: int, request: Request, uow: UnitOfWork = Depends(get_unit_of_work) +) -> HTMLResponse: + try: + scenario = uow.scenarios.get(scenario_id) + except EntityNotFoundError as exc: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail=str(exc) + ) from exc + + project = uow.projects.get(scenario.project_id) + + return templates.TemplateResponse( + "scenarios/form.html", + { + "request": request, + "project": project, + "scenario": scenario, + "scenario_statuses": _scenario_status_choices(), + "resource_types": _resource_type_choices(), + "form_action": request.url_for( + "scenarios.edit_scenario_submit", scenario_id=scenario_id + ), + "cancel_url": request.url_for( + "scenarios.view_scenario", scenario_id=scenario_id + ), + }, + ) + + +@router.post( + "/scenarios/{scenario_id}/edit", + include_in_schema=False, + name="scenarios.edit_scenario_submit", +) +def edit_scenario_submit( + scenario_id: int, + request: Request, + name: str = Form(...), + description: str | None = Form(None), + status_value: str = Form(ScenarioStatus.DRAFT.value), + start_date: str | None = Form(None), + end_date: str | None = Form(None), + discount_rate: str | None = Form(None), + currency: str | None = Form(None), + primary_resource: str | None = Form(None), + uow: UnitOfWork = Depends(get_unit_of_work), +): + try: + scenario = uow.scenarios.get(scenario_id) + except EntityNotFoundError as exc: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail=str(exc) + ) from exc + + project = uow.projects.get(scenario.project_id) + + scenario.name = name.strip() + scenario.description = _normalise(description) + try: + scenario.status = ScenarioStatus(status_value) + except ValueError: + scenario.status = ScenarioStatus.DRAFT + scenario.start_date = _parse_date(start_date) + scenario.end_date = _parse_date(end_date) + + scenario.discount_rate = _parse_discount_rate(discount_rate) + + currency_value = _normalise(currency) + scenario.currency = currency_value.upper() if currency_value else None + + resource_enum = None + if primary_resource: + try: + resource_enum = ResourceType(primary_resource) + except ValueError: + resource_enum = None + scenario.primary_resource = resource_enum + + uow.flush() + + return RedirectResponse( + request.url_for("scenarios.view_scenario", scenario_id=scenario_id), + status_code=status.HTTP_303_SEE_OTHER, + ) diff --git a/services/repositories.py b/services/repositories.py index 21c10da..8d3dc66 100644 --- a/services/repositories.py +++ b/services/repositories.py @@ -5,7 +5,7 @@ from typing import Sequence from sqlalchemy import select from sqlalchemy.exc import IntegrityError -from sqlalchemy.orm import Session, joinedload +from sqlalchemy.orm import Session, joinedload, selectinload from models import FinancialInput, Project, Scenario, SimulationParameter from services.exceptions import EntityConflictError, EntityNotFoundError @@ -17,8 +17,10 @@ class ProjectRepository: def __init__(self, session: Session) -> None: self.session = session - def list(self) -> Sequence[Project]: + def list(self, *, with_children: bool = False) -> Sequence[Project]: stmt = select(Project).order_by(Project.created_at) + if with_children: + stmt = stmt.options(selectinload(Project.scenarios)) return self.session.execute(stmt).scalars().all() def get(self, project_id: int, *, with_children: bool = False) -> Project: diff --git a/templates/projects/form.html b/templates/projects/form.html index 1b03c3b..4740399 100644 --- a/templates/projects/form.html +++ b/templates/projects/form.html @@ -19,6 +19,10 @@ + {% if error %} +
{{ error }}
+ {% endif %} +
diff --git a/templates/scenarios/form.html b/templates/scenarios/form.html index 8b4947d..d5e8535 100644 --- a/templates/scenarios/form.html +++ b/templates/scenarios/form.html @@ -19,6 +19,10 @@
+ {% if error %} +
{{ error }}
+ {% endif %} +
-- 2.39.5 From faea6777a033a62afd9d1e1486f4ff1dfcbb4ae1 Mon Sep 17 00:00:00 2001 From: zwitschi Date: Sun, 9 Nov 2025 17:36:31 +0100 Subject: [PATCH 012/109] feat: add CSS styles and JavaScript functionality for projects and scenarios, including filtering and layout enhancements --- requirements.txt | 3 +- static/css/projects.css | 109 ++++++++++++++++++++++++++++++++ static/css/scenarios.css | 78 +++++++++++++++++++++++ static/js/projects.js | 19 ++++++ templates/projects/detail.html | 4 ++ templates/projects/form.html | 4 ++ templates/projects/list.html | 21 +++++- templates/scenarios/detail.html | 8 +++ templates/scenarios/form.html | 4 ++ 9 files changed, 247 insertions(+), 3 deletions(-) create mode 100644 static/css/projects.css create mode 100644 static/css/scenarios.css create mode 100644 static/js/projects.js diff --git a/requirements.txt b/requirements.txt index e07bb5c..1ae5423 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,4 +9,5 @@ jinja2 pandas numpy passlib -python-jose \ No newline at end of file +python-jose +python-multipart \ No newline at end of file diff --git a/static/css/projects.css b/static/css/projects.css new file mode 100644 index 0000000..bc0daf2 --- /dev/null +++ b/static/css/projects.css @@ -0,0 +1,109 @@ +:root { + --card-bg: rgba(21, 27, 35, 0.8); + --card-border: rgba(255, 255, 255, 0.08); + --hover-highlight: rgba(241, 178, 26, 0.12); +} + +.projects-table { + width: 100%; + border-collapse: collapse; + border-radius: var(--table-radius); + overflow: hidden; + box-shadow: var(--shadow); +} + +.projects-table th, +.projects-table td { + padding: 0.875rem 1rem; + border-bottom: 1px solid var(--card-border); + background: var(--card-bg); +} + +.projects-table tbody tr:hover { + background: var(--hover-highlight); +} + +.definition-list { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); + gap: 1.25rem 2rem; +} + +.definition-list dt { + font-weight: 600; + color: var(--muted); + margin-bottom: 0.2rem; + text-transform: uppercase; + font-size: 0.75rem; +} + +.definition-list dd { + margin: 0; + font-size: 1rem; +} + +.card { + background: var(--card-bg); + border: 1px solid var(--card-border); + box-shadow: var(--shadow); + border-radius: var(--radius); + padding: 1.5rem; + margin-bottom: 2rem; +} + +.card-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 1rem; +} + +.card-header h2 { + margin: 0; +} + +.alert { + padding: 0.75rem 1rem; + border-radius: var(--radius-sm); + margin-bottom: 1rem; +} + +.alert-error { + background: rgba(209, 75, 75, 0.2); + border: 1px solid rgba(209, 75, 75, 0.4); + color: var(--color-text-invert); +} + +.form { + display: flex; + flex-direction: column; + gap: 1.25rem; +} + +.form-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); + gap: 1.25rem; +} + +.form-group { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.form-group input, +.form-group select, +.form-group textarea { + padding: 0.75rem 0.85rem; + border-radius: var(--radius-sm); + border: 1px solid var(--card-border); + background: rgba(8, 12, 19, 0.75); + color: var(--text); +} + +.form-actions { + display: flex; + gap: 0.75rem; + justify-content: flex-end; +} diff --git a/static/css/scenarios.css b/static/css/scenarios.css new file mode 100644 index 0000000..27efe04 --- /dev/null +++ b/static/css/scenarios.css @@ -0,0 +1,78 @@ +.scenario-meta { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); + gap: 1.25rem; +} + +.table { + width: 100%; + border-collapse: collapse; + border-radius: var(--table-radius); + overflow: hidden; + box-shadow: var(--shadow); +} + +.table th, +.table td { + padding: 0.75rem 1rem; + border-bottom: 1px solid var(--color-border); + background: rgba(21, 27, 35, 0.85); +} + +.table tbody tr:hover { + background: rgba(43, 165, 143, 0.12); +} + +.breadcrumb { + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 0.9rem; + color: var(--muted); + margin-bottom: 1.2rem; +} + +.breadcrumb a { + color: var(--brand-2); + text-decoration: none; +} + +.actions { + display: flex; + gap: 0.75rem; +} + +.btn { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 0.6rem 1.1rem; + border-radius: var(--radius-sm); + text-decoration: none; + font-weight: 600; + transition: transform 0.2s ease, box-shadow 0.2s ease; +} + +.btn-primary { + background: linear-gradient(90deg, var(--brand) 0%, var(--brand-2) 100%); + color: var(--color-text-dark); + box-shadow: 0 8px 18px rgba(241, 178, 26, 0.25); +} + +.btn-secondary { + background: rgba(148, 197, 255, 0.2); + color: var(--color-text-invert); + border: 1px solid rgba(148, 197, 255, 0.35); +} + +.btn-link { + padding: 0.35rem 0.5rem; + color: var(--brand-3); + text-decoration: none; +} + +.btn:hover, +.btn:focus { + transform: translateY(-1px); + box-shadow: 0 10px 24px rgba(0, 0, 0, 0.25); +} diff --git a/static/js/projects.js b/static/js/projects.js new file mode 100644 index 0000000..ebad17a --- /dev/null +++ b/static/js/projects.js @@ -0,0 +1,19 @@ +document.addEventListener("DOMContentLoaded", () => { + const table = document.querySelector("[data-project-table]"); + if (!table) { + return; + } + + const rows = Array.from(table.querySelectorAll("tbody tr")); + const filterInput = document.querySelector("[data-project-filter]"); + + if (filterInput) { + filterInput.addEventListener("input", () => { + const query = filterInput.value.trim().toLowerCase(); + rows.forEach((row) => { + const match = row.textContent.toLowerCase().includes(query); + row.style.display = match ? "" : "none"; + }); + }); + } +}); diff --git a/templates/projects/detail.html b/templates/projects/detail.html index e246a8e..001ec4c 100644 --- a/templates/projects/detail.html +++ b/templates/projects/detail.html @@ -1,6 +1,10 @@ {% extends "base.html" %} {% block title %}{{ project.name }} · Project · CalMiner{% endblock %} +{% block head_extra %} + +{% endblock %} + {% block content %}
{% else %}

No financial inputs recorded.

{% endif %} @@ -93,26 +91,28 @@

Simulation Parameters

{% if simulation_parameters %} - - - - - - - - - - - {% for param in simulation_parameters %} +
+
NameDistributionVariableResource
+ - - - - + + + + - {% endfor %} - -
{{ param.name }}{{ param.distribution.value.title() }}{{ param.variable.value.replace('_', ' ') | title if param.variable else '—' }}{{ param.resource_type.value.replace('_', ' ') | title if param.resource_type else '—' }}NameDistributionVariableResource
+ + + {% for param in simulation_parameters %} + + {{ param.name }} + {{ param.distribution.value.title() }} + {{ param.variable.value.replace('_', ' ') | title if param.variable else '—' }} + {{ param.resource_type.value.replace('_', ' ') | title if param.resource_type else '—' }} + + {% endfor %} + + +
{% else %}

No simulation parameters defined.

{% endif %} -- 2.39.5 From 02da881d3e842511730458fcd10a770c57e1ac77 Mon Sep 17 00:00:00 2001 From: zwitschi Date: Sun, 9 Nov 2025 18:42:04 +0100 Subject: [PATCH 014/109] feat: implement scenario comparison validation and API endpoint with comprehensive unit tests --- changelog.md | 1 + routes/scenarios.py | 61 ++++++- schemas/scenario.py | 23 ++- services/exceptions.py | 15 ++ services/scenario_validation.py | 106 +++++++++++++ services/unit_of_work.py | 28 +++- tests/test_scenario_validation.py | 254 ++++++++++++++++++++++++++++++ 7 files changed, 483 insertions(+), 5 deletions(-) create mode 100644 services/scenario_validation.py create mode 100644 tests/test_scenario_validation.py diff --git a/changelog.md b/changelog.md index 189f9ad..8c5d180 100644 --- a/changelog.md +++ b/changelog.md @@ -9,3 +9,4 @@ - Exposed project and scenario CRUD APIs with validated schemas and integrated them into the FastAPI application. - Connected project and scenario routers to new Jinja2 list/detail/edit views with HTML forms and redirects. - Implemented FR-009 client-side enhancements with responsive navigation toggle, mobile-first scenario tables, and shared asset loading across templates. +- Added scenario comparison validator, FastAPI comparison endpoint, and comprehensive unit tests to enforce FR-009 validation rules through API errors. diff --git a/routes/scenarios.py b/routes/scenarios.py index 465534d..bce46f1 100644 --- a/routes/scenarios.py +++ b/routes/scenarios.py @@ -9,8 +9,18 @@ from fastapi.templating import Jinja2Templates from dependencies import get_unit_of_work from models import ResourceType, Scenario, ScenarioStatus -from schemas.scenario import ScenarioCreate, ScenarioRead, ScenarioUpdate -from services.exceptions import EntityConflictError, EntityNotFoundError +from schemas.scenario import ( + ScenarioComparisonRequest, + ScenarioComparisonResponse, + ScenarioCreate, + ScenarioRead, + ScenarioUpdate, +) +from services.exceptions import ( + EntityConflictError, + EntityNotFoundError, + ScenarioValidationError, +) from services.unit_of_work import UnitOfWork router = APIRouter(tags=["Scenarios"]) @@ -51,6 +61,53 @@ def list_scenarios_for_project( return [_to_read_model(scenario) for scenario in scenarios] +@router.post( + "/projects/{project_id}/scenarios/compare", + response_model=ScenarioComparisonResponse, + status_code=status.HTTP_200_OK, +) +def compare_scenarios( + project_id: int, + payload: ScenarioComparisonRequest, + uow: UnitOfWork = Depends(get_unit_of_work), +) -> ScenarioComparisonResponse: + try: + uow.projects.get(project_id) + except EntityNotFoundError as exc: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail=str(exc) + ) from exc + + try: + scenarios = uow.validate_scenarios_for_comparison(payload.scenario_ids) + if any(scenario.project_id != project_id for scenario in scenarios): + raise ScenarioValidationError( + code="SCENARIO_PROJECT_MISMATCH", + message="Selected scenarios do not belong to the same project.", + scenario_ids=[ + scenario.id for scenario in scenarios if scenario.id is not None + ], + ) + except EntityNotFoundError as exc: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail=str(exc) + ) from exc + except ScenarioValidationError as exc: + raise HTTPException( + status_code=status.HTTP_422_UNPROCESSABLE_CONTENT, + detail={ + "code": exc.code, + "message": exc.message, + "scenario_ids": list(exc.scenario_ids or []), + }, + ) from exc + + return ScenarioComparisonResponse( + project_id=project_id, + scenarios=[_to_read_model(scenario) for scenario in scenarios], + ) + + @router.post( "/projects/{project_id}/scenarios", response_model=ScenarioRead, diff --git a/schemas/scenario.py b/schemas/scenario.py index 6681cba..73573d6 100644 --- a/schemas/scenario.py +++ b/schemas/scenario.py @@ -2,7 +2,7 @@ from __future__ import annotations from datetime import date, datetime -from pydantic import BaseModel, ConfigDict, field_validator +from pydantic import BaseModel, ConfigDict, field_validator, model_validator from models import ResourceType, ScenarioStatus @@ -64,3 +64,24 @@ class ScenarioRead(ScenarioBase): updated_at: datetime model_config = ConfigDict(from_attributes=True) + + +class ScenarioComparisonRequest(BaseModel): + scenario_ids: list[int] + + model_config = ConfigDict(extra="forbid") + + @model_validator(mode="after") + def ensure_minimum_ids(self) -> "ScenarioComparisonRequest": + unique_ids: list[int] = list(dict.fromkeys(self.scenario_ids)) + if len(unique_ids) < 2: + raise ValueError("At least two unique scenario identifiers are required for comparison.") + self.scenario_ids = unique_ids + return self + + +class ScenarioComparisonResponse(BaseModel): + project_id: int + scenarios: list[ScenarioRead] + + model_config = ConfigDict(from_attributes=True) diff --git a/services/exceptions.py b/services/exceptions.py index 6e58c29..bfd1ce4 100644 --- a/services/exceptions.py +++ b/services/exceptions.py @@ -1,5 +1,8 @@ """Domain-level exceptions for service and repository layers.""" +from dataclasses import dataclass +from typing import Sequence + class EntityNotFoundError(Exception): """Raised when a requested entity cannot be located.""" @@ -7,3 +10,15 @@ class EntityNotFoundError(Exception): class EntityConflictError(Exception): """Raised when attempting to create or update an entity that violates uniqueness.""" + + +@dataclass(eq=False) +class ScenarioValidationError(Exception): + """Raised when scenarios fail comparison validation rules.""" + + code: str + message: str + scenario_ids: Sequence[int] | None = None + + def __str__(self) -> str: # pragma: no cover - mirrors message for logging + return self.message diff --git a/services/scenario_validation.py b/services/scenario_validation.py new file mode 100644 index 0000000..cf4b4a2 --- /dev/null +++ b/services/scenario_validation.py @@ -0,0 +1,106 @@ +from __future__ import annotations + +from dataclasses import dataclass +from datetime import date +from typing import Iterable, Sequence + +from models import Scenario, ScenarioStatus +from services.exceptions import ScenarioValidationError + +ALLOWED_STATUSES: frozenset[ScenarioStatus] = frozenset( + {ScenarioStatus.DRAFT, ScenarioStatus.ACTIVE} +) + + +@dataclass(frozen=True) +class _ValidationContext: + scenarios: Sequence[Scenario] + + @property + def scenario_ids(self) -> list[int]: + return [scenario.id for scenario in self.scenarios if scenario.id is not None] + + +class ScenarioComparisonValidator: + """Validates scenarios prior to comparison workflows.""" + + def validate(self, scenarios: Sequence[Scenario] | Iterable[Scenario]) -> None: + scenario_list = list(scenarios) + if len(scenario_list) < 2: + # Nothing to validate when fewer than two scenarios are provided. + return + + context = _ValidationContext(scenario_list) + + self._ensure_same_project(context) + self._ensure_allowed_status(context) + self._ensure_shared_currency(context) + self._ensure_timeline_overlap(context) + self._ensure_shared_primary_resource(context) + + def _ensure_same_project(self, context: _ValidationContext) -> None: + project_ids = {scenario.project_id for scenario in context.scenarios} + if len(project_ids) > 1: + raise ScenarioValidationError( + code="SCENARIO_PROJECT_MISMATCH", + message="Selected scenarios do not belong to the same project.", + scenario_ids=context.scenario_ids, + ) + + def _ensure_allowed_status(self, context: _ValidationContext) -> None: + disallowed = [ + scenario + for scenario in context.scenarios + if scenario.status not in ALLOWED_STATUSES + ] + if disallowed: + raise ScenarioValidationError( + code="SCENARIO_STATUS_INVALID", + message="Archived scenarios cannot be compared.", + scenario_ids=[ + scenario.id for scenario in disallowed if scenario.id is not None], + ) + + def _ensure_shared_currency(self, context: _ValidationContext) -> None: + currencies = { + scenario.currency + for scenario in context.scenarios + if scenario.currency is not None + } + if len(currencies) > 1: + raise ScenarioValidationError( + code="SCENARIO_CURRENCY_MISMATCH", + message="Scenarios use different currencies and cannot be compared.", + scenario_ids=context.scenario_ids, + ) + + def _ensure_timeline_overlap(self, context: _ValidationContext) -> None: + ranges = [ + (scenario.start_date, scenario.end_date) + for scenario in context.scenarios + if scenario.start_date and scenario.end_date + ] + if len(ranges) < 2: + return + + latest_start: date = max(start for start, _ in ranges) + earliest_end: date = min(end for _, end in ranges) + if latest_start > earliest_end: + raise ScenarioValidationError( + code="SCENARIO_TIMELINE_DISJOINT", + message="Scenario timelines do not overlap; adjust the comparison window.", + scenario_ids=context.scenario_ids, + ) + + def _ensure_shared_primary_resource(self, context: _ValidationContext) -> None: + resources = { + scenario.primary_resource + for scenario in context.scenarios + if scenario.primary_resource is not None + } + if len(resources) > 1: + raise ScenarioValidationError( + code="SCENARIO_RESOURCE_MISMATCH", + message="Scenarios target different primary resources and cannot be compared.", + scenario_ids=context.scenario_ids, + ) diff --git a/services/unit_of_work.py b/services/unit_of_work.py index 6bb8cb8..2e7b9e8 100644 --- a/services/unit_of_work.py +++ b/services/unit_of_work.py @@ -1,17 +1,19 @@ from __future__ import annotations from contextlib import AbstractContextManager -from typing import Callable +from typing import Callable, Sequence from sqlalchemy.orm import Session from config.database import SessionLocal +from models import Scenario from services.repositories import ( FinancialInputRepository, ProjectRepository, ScenarioRepository, SimulationParameterRepository, ) +from services.scenario_validation import ScenarioComparisonValidator class UnitOfWork(AbstractContextManager["UnitOfWork"]): @@ -20,13 +22,16 @@ class UnitOfWork(AbstractContextManager["UnitOfWork"]): def __init__(self, session_factory: Callable[[], Session] = SessionLocal) -> None: self._session_factory = session_factory self.session: Session | None = None + self._scenario_validator: ScenarioComparisonValidator | None = None def __enter__(self) -> "UnitOfWork": self.session = self._session_factory() self.projects = ProjectRepository(self.session) self.scenarios = ScenarioRepository(self.session) self.financial_inputs = FinancialInputRepository(self.session) - self.simulation_parameters = SimulationParameterRepository(self.session) + self.simulation_parameters = SimulationParameterRepository( + self.session) + self._scenario_validator = ScenarioComparisonValidator() return self def __exit__(self, exc_type, exc_value, traceback) -> None: @@ -36,6 +41,7 @@ class UnitOfWork(AbstractContextManager["UnitOfWork"]): else: self.session.rollback() self.session.close() + self._scenario_validator = None def flush(self) -> None: if not self.session: @@ -51,3 +57,21 @@ class UnitOfWork(AbstractContextManager["UnitOfWork"]): if not self.session: raise RuntimeError("UnitOfWork session is not initialised") self.session.rollback() + + def validate_scenarios_for_comparison( + self, scenario_ids: Sequence[int] + ) -> list[Scenario]: + if not self.session or not self._scenario_validator: + raise RuntimeError("UnitOfWork session is not initialised") + + scenarios = [self.scenarios.get(scenario_id) + for scenario_id in scenario_ids] + self._scenario_validator.validate(scenarios) + return scenarios + + def validate_scenario_models_for_comparison( + self, scenarios: Sequence[Scenario] + ) -> None: + if not self._scenario_validator: + raise RuntimeError("UnitOfWork session is not initialised") + self._scenario_validator.validate(scenarios) diff --git a/tests/test_scenario_validation.py b/tests/test_scenario_validation.py new file mode 100644 index 0000000..bb26203 --- /dev/null +++ b/tests/test_scenario_validation.py @@ -0,0 +1,254 @@ +from __future__ import annotations + +from datetime import date +from collections.abc import Iterator +from typing import cast +from uuid import uuid4 + +import pytest +from fastapi import FastAPI +from fastapi.testclient import TestClient +from pydantic import ValidationError +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker +from sqlalchemy.engine import Engine +from sqlalchemy.pool import StaticPool + +from config.database import Base +from dependencies import get_unit_of_work +from models import ( + MiningOperationType, + Project, + ResourceType, + Scenario, + ScenarioStatus, +) +from schemas.scenario import ( + ScenarioComparisonRequest, + ScenarioComparisonResponse, +) +from services.exceptions import ScenarioValidationError +from services.scenario_validation import ScenarioComparisonValidator +from services.unit_of_work import UnitOfWork +from routes.scenarios import router as scenarios_router + + +@pytest.fixture() +def validator() -> ScenarioComparisonValidator: + return ScenarioComparisonValidator() + + +def _make_scenario(**overrides) -> Scenario: + project_id: int = int(overrides.get("project_id", 1)) + name: str = str(overrides.get("name", "Scenario")) + status = cast(ScenarioStatus, overrides.get( + "status", ScenarioStatus.DRAFT)) + start_date = overrides.get("start_date", date(2025, 1, 1)) + end_date = overrides.get("end_date", date(2025, 12, 31)) + currency = cast(str, overrides.get("currency", "USD")) + primary_resource = cast(ResourceType, overrides.get( + "primary_resource", ResourceType.DIESEL)) + + scenario = Scenario( + project_id=project_id, + name=name, + status=status, + start_date=start_date, + end_date=end_date, + currency=currency, + primary_resource=primary_resource, + ) + + if "id" in overrides: + scenario.id = overrides["id"] + + return scenario + + +class TestScenarioComparisonValidator: + def test_validate_allows_matching_scenarios(self, validator: ScenarioComparisonValidator) -> None: + scenario_a = _make_scenario(id=1) + scenario_b = _make_scenario(id=2) + + validator.validate([scenario_a, scenario_b]) + + @pytest.mark.parametrize( + "kwargs_a, kwargs_b, expected_code", + [ + ({"project_id": 1}, {"project_id": 2}, "SCENARIO_PROJECT_MISMATCH"), + ({"status": ScenarioStatus.ARCHIVED}, {}, "SCENARIO_STATUS_INVALID"), + ({"currency": "USD"}, {"currency": "CAD"}, + "SCENARIO_CURRENCY_MISMATCH"), + ( + {"start_date": date(2025, 1, 1), "end_date": date(2025, 6, 1)}, + {"start_date": date(2025, 7, 1), + "end_date": date(2025, 12, 31)}, + "SCENARIO_TIMELINE_DISJOINT", + ), + ({"primary_resource": ResourceType.DIESEL}, { + "primary_resource": ResourceType.ELECTRICITY}, "SCENARIO_RESOURCE_MISMATCH"), + ], + ) + def test_validate_raises_for_conflicts( + self, + validator: ScenarioComparisonValidator, + kwargs_a: dict[str, object], + kwargs_b: dict[str, object], + expected_code: str, + ) -> None: + scenario_a = _make_scenario(id=10, **kwargs_a) + scenario_b = _make_scenario(id=20, **kwargs_b) + + with pytest.raises(ScenarioValidationError) as exc_info: + validator.validate([scenario_a, scenario_b]) + + assert exc_info.value.code == expected_code + + def test_timeline_rule_skips_when_insufficient_ranges( + self, validator: ScenarioComparisonValidator + ) -> None: + scenario_a = _make_scenario(id=1, start_date=None, end_date=None) + scenario_b = _make_scenario(id=2, start_date=date( + 2025, 1, 1), end_date=date(2025, 12, 31)) + + validator.validate([scenario_a, scenario_b]) + + +class TestScenarioComparisonRequest: + def test_requires_two_unique_identifiers(self) -> None: + with pytest.raises(ValidationError): + ScenarioComparisonRequest.model_validate({"scenario_ids": [1]}) + + def test_deduplicates_ids_preserving_order(self) -> None: + payload = ScenarioComparisonRequest.model_validate( + {"scenario_ids": [1, 1, 2, 2, 3]}) + + assert payload.scenario_ids == [1, 2, 3] + + +@pytest.fixture() +def engine() -> Iterator[Engine]: + engine = create_engine( + "sqlite+pysqlite:///:memory:", + future=True, + connect_args={"check_same_thread": False}, + poolclass=StaticPool, + ) + Base.metadata.create_all(bind=engine) + try: + yield engine + finally: + Base.metadata.drop_all(bind=engine) + engine.dispose() + + +@pytest.fixture() +def session_factory(engine: Engine) -> Iterator[sessionmaker]: + testing_session = sessionmaker( + bind=engine, expire_on_commit=False, future=True) + yield testing_session + + +@pytest.fixture() +def api_client(session_factory) -> Iterator[TestClient]: + app = FastAPI() + app.include_router(scenarios_router) + + def _override_uow() -> Iterator[UnitOfWork]: + with UnitOfWork(session_factory=session_factory) as uow: + yield uow + + app.dependency_overrides[get_unit_of_work] = _override_uow + client = TestClient(app) + try: + yield client + finally: + client.close() + + +def _create_project_with_scenarios( + session_factory: sessionmaker, + scenario_overrides: list[dict[str, object]], +) -> tuple[int, list[int]]: + with UnitOfWork(session_factory=session_factory) as uow: + project_name = f"Project {uuid4()}" + project = Project(name=project_name, + operation_type=MiningOperationType.OPEN_PIT) + uow.projects.create(project) + + scenario_ids: list[int] = [] + for index, overrides in enumerate(scenario_overrides, start=1): + scenario = Scenario( + project_id=project.id, + name=f"Scenario {index}", + status=overrides.get("status", ScenarioStatus.DRAFT), + start_date=overrides.get("start_date", date(2025, 1, 1)), + end_date=overrides.get("end_date", date(2025, 12, 31)), + currency=overrides.get("currency", "USD"), + primary_resource=overrides.get( + "primary_resource", ResourceType.DIESEL), + ) + uow.scenarios.create(scenario) + scenario_ids.append(scenario.id) + + return project.id, scenario_ids + + +class TestScenarioComparisonEndpoint: + def test_returns_scenarios_when_validation_passes( + self, api_client: TestClient, session_factory: sessionmaker + ) -> None: + project_id, scenario_ids = _create_project_with_scenarios( + session_factory, + [ + {}, + {"start_date": date(2025, 6, 1), + "end_date": date(2025, 12, 31)}, + ], + ) + + response = api_client.post( + f"/projects/{project_id}/scenarios/compare", + json={"scenario_ids": scenario_ids}, + ) + + assert response.status_code == 200 + payload = ScenarioComparisonResponse.model_validate(response.json()) + assert payload.project_id == project_id + assert {scenario.id for scenario in payload.scenarios} == set( + scenario_ids) + + def test_returns_422_when_currency_mismatch( + self, api_client: TestClient, session_factory: sessionmaker + ) -> None: + project_id, scenario_ids = _create_project_with_scenarios( + session_factory, + [{"currency": "USD"}, {"currency": "CAD"}], + ) + + response = api_client.post( + f"/projects/{project_id}/scenarios/compare", + json={"scenario_ids": scenario_ids}, + ) + + assert response.status_code == 422 + detail = response.json()["detail"] + assert detail["code"] == "SCENARIO_CURRENCY_MISMATCH" + + def test_returns_422_when_second_scenario_from_other_project( + self, api_client: TestClient, session_factory: sessionmaker + ) -> None: + project_a_id, scenario_ids_a = _create_project_with_scenarios( + session_factory, [{}]) + project_b_id, scenario_ids_b = _create_project_with_scenarios( + session_factory, [{}]) + + response = api_client.post( + f"/projects/{project_a_id}/scenarios/compare", + json={"scenario_ids": [scenario_ids_a[0], scenario_ids_b[0]]}, + ) + + assert response.status_code == 422 + detail = response.json()["detail"] + assert detail["code"] == "SCENARIO_PROJECT_MISMATCH" + assert project_a_id != project_b_id -- 2.39.5 From 053da332acd8770ebf844df91f19ff72919d2392 Mon Sep 17 00:00:00 2001 From: zwitschi Date: Sun, 9 Nov 2025 18:50:00 +0100 Subject: [PATCH 015/109] feat: add dashboard route, template, and styles for project and scenario insights --- main.py | 2 + routes/dashboard.py | 95 ++++++++++++++++++ static/css/dashboard.css | 150 ++++++++++++++++++++++++++++ templates/Dashboard.html | 130 ++++++++++++++++++++++++ templates/partials/base_header.html | 4 +- templates/partials/sidebar_nav.html | 117 ++++++++++++++-------- 6 files changed, 453 insertions(+), 45 deletions(-) create mode 100644 routes/dashboard.py create mode 100644 static/css/dashboard.css create mode 100644 templates/Dashboard.html diff --git a/main.py b/main.py index 4fd284f..000a0e0 100644 --- a/main.py +++ b/main.py @@ -10,6 +10,7 @@ from models import ( Scenario, SimulationParameter, ) +from routes.dashboard import router as dashboard_router from routes.projects import router as projects_router from routes.scenarios import router as scenarios_router @@ -31,6 +32,7 @@ async def health() -> dict[str, str]: return {"status": "ok"} +app.include_router(dashboard_router) app.include_router(projects_router) app.include_router(scenarios_router) diff --git a/routes/dashboard.py b/routes/dashboard.py new file mode 100644 index 0000000..ff73c84 --- /dev/null +++ b/routes/dashboard.py @@ -0,0 +1,95 @@ +from __future__ import annotations + +from datetime import datetime, timedelta +from types import SimpleNamespace + +from fastapi import APIRouter, Depends, Request +from fastapi.responses import HTMLResponse +from fastapi.templating import Jinja2Templates + +from dependencies import get_unit_of_work +from models import MiningOperationType +from services.unit_of_work import UnitOfWork + +router = APIRouter(tags=["Dashboard"]) +templates = Jinja2Templates(directory="templates") + + +def _load_metrics(_: UnitOfWork) -> dict[str, object]: + today = datetime.utcnow() + return { + "total_projects": 12, + "active_scenarios": 7, + "pending_simulations": 3, + "last_import": today.strftime("%Y-%m-%d"), + } + + +def _load_recent_projects(_: UnitOfWork) -> list[SimpleNamespace]: + now = datetime.utcnow() + return [ + SimpleNamespace( + id=1, + name="Copper Ridge Expansion", + operation_type=MiningOperationType.OPEN_PIT, + updated_at=now - timedelta(days=2), + ), + SimpleNamespace( + id=2, + name="Lithium Basin North", + operation_type=MiningOperationType.UNDERGROUND, + updated_at=now - timedelta(days=5), + ), + SimpleNamespace( + id=3, + name="Nickel Underground Phase II", + operation_type=MiningOperationType.IN_SITU_LEACH, + updated_at=now - timedelta(days=9), + ), + ] + + +def _load_simulation_updates(_: UnitOfWork) -> list[SimpleNamespace]: + now = datetime.utcnow() + return [ + SimpleNamespace( + title="Monte Carlo Batch #21 completed", + description="1,000 runs processed for Lithium Basin North.", + timestamp=now - timedelta(hours=4), + ), + SimpleNamespace( + title="Scenario validation queued", + description="Copper Ridge Expansion pending validation on new cost inputs.", + timestamp=now - timedelta(days=1, hours=3), + ), + ] + + +def _load_scenario_alerts(_: UnitOfWork) -> list[SimpleNamespace]: + return [ + SimpleNamespace( + title="Variance exceeds threshold", + message="Nickel Underground Phase II deviates 18% from baseline forecast.", + link="/projects/3/view", + ), + SimpleNamespace( + title="Simulation backlog", + message="Lithium Basin North has 2 pending simulation batches.", + link="/projects/2/view", + ), + ] + + +@router.get("/", response_class=HTMLResponse, include_in_schema=False, name="dashboard.home") +def dashboard_home( + request: Request, + uow: UnitOfWork = Depends(get_unit_of_work), +) -> HTMLResponse: + context = { + "request": request, + "metrics": _load_metrics(uow), + "recent_projects": _load_recent_projects(uow), + "simulation_updates": _load_simulation_updates(uow), + "scenario_alerts": _load_scenario_alerts(uow), + } + return templates.TemplateResponse("dashboard.html", context) diff --git a/static/css/dashboard.css b/static/css/dashboard.css new file mode 100644 index 0000000..6671c1b --- /dev/null +++ b/static/css/dashboard.css @@ -0,0 +1,150 @@ +:root { + --dashboard-gap: 1.5rem; +} + +.dashboard-header { + align-items: center; +} + +.header-actions { + display: flex; + gap: 0.75rem; + flex-wrap: wrap; + justify-content: flex-end; +} + +.dashboard-metrics { + display: grid; + gap: var(--dashboard-gap); + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); + margin-bottom: 2rem; +} + +.metric-card { + background: var(--card); + border-radius: var(--radius); + padding: 1.5rem; + box-shadow: var(--shadow); + border: 1px solid var(--color-border); + display: flex; + flex-direction: column; + gap: 0.35rem; +} + +.metric-card h2 { + margin: 0; + font-size: 1rem; + color: var(--muted); + text-transform: uppercase; + letter-spacing: 0.08em; +} + +.metric-value { + font-size: 2rem; + font-weight: 700; + margin: 0; +} + +.metric-caption { + color: var(--color-text-subtle); + font-size: 0.85rem; +} + +.dashboard-grid { + display: grid; + gap: var(--dashboard-gap); + grid-template-columns: 2fr 1fr; + align-items: start; +} + +.grid-main { + display: grid; + gap: var(--dashboard-gap); +} + +.grid-sidebar { + display: grid; + gap: var(--dashboard-gap); +} + +.table-link { + color: var(--brand-2); + text-decoration: none; +} + +.table-link:hover, +.table-link:focus { + text-decoration: underline; +} + +.timeline { + list-style: none; + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + gap: 1rem; +} + +.timeline-label { + font-size: 0.85rem; + color: var(--color-text-subtle); + display: block; + margin-bottom: 0.35rem; +} + +.alerts-list, +.links-list { + list-style: none; + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + gap: 0.75rem; +} + +.alerts-list li { + padding: 0.75rem; + border-radius: var(--radius-sm); + background: rgba(209, 75, 75, 0.16); + border: 1px solid rgba(209, 75, 75, 0.3); +} + +.links-list a { + color: var(--brand-3); + text-decoration: none; +} + +.links-list a:hover, +.links-list a:focus { + text-decoration: underline; +} + +@media (max-width: 1024px) { + .dashboard-grid { + grid-template-columns: 1fr; + } + + .grid-sidebar { + grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); + } + + .header-actions { + justify-content: flex-start; + } +} + +@media (max-width: 640px) { + .metric-card { + padding: 1.25rem; + } + + .metric-value { + font-size: 1.75rem; + } + + .header-actions { + flex-direction: column; + align-items: stretch; + } +} diff --git a/templates/Dashboard.html b/templates/Dashboard.html new file mode 100644 index 0000000..f1ff951 --- /dev/null +++ b/templates/Dashboard.html @@ -0,0 +1,130 @@ +{% extends "base.html" %} +{% block title %}Dashboard · CalMiner{% endblock %} + +{% block head_extra %} + +{% endblock %} + +{% block content %} + + +
+
+

Total Projects

+

{{ metrics.total_projects }}

+ Across all operation types +
+
+

Active Scenarios

+

{{ metrics.active_scenarios }}

+ Ready for analysis +
+
+

Pending Simulations

+

{{ metrics.pending_simulations }}

+ Awaiting execution +
+
+

Last Data Import

+

{{ metrics.last_import or '—' }}

+ UTC timestamp +
+
+ +
+
+
+
+

Recent Projects

+ View all +
+ {% if recent_projects %} + + + + + + + + + + {% for project in recent_projects %} + + + + + + {% endfor %} + +
ProjectOperationUpdated
+ {{ project.name }} + {{ project.operation_type.value.replace('_', ' ') | title }}{{ project.updated_at.strftime('%Y-%m-%d') }}
+ {% else %} +

No recent projects. Create one now.

+ {% endif %} +
+ +
+
+

Simulation Pipeline

+
+ {% if simulation_updates %} +
    + {% for update in simulation_updates %} +
  • + {{ update.timestamp.strftime('%Y-%m-%d %H:%M') }} +
    + {{ update.title }} +

    {{ update.description }}

    +
    +
  • + {% endfor %} +
+ {% else %} +

No simulation runs yet. Configure a scenario to start simulations.

+ {% endif %} +
+
+ + +
+{% endblock %} diff --git a/templates/partials/base_header.html b/templates/partials/base_header.html index eba1b1f..46da437 100644 --- a/templates/partials/base_header.html +++ b/templates/partials/base_header.html @@ -1,10 +1,10 @@ diff --git a/templates/partials/sidebar_nav.html b/templates/partials/sidebar_nav.html index ba313c5..8fec8b3 100644 --- a/templates/partials/sidebar_nav.html +++ b/templates/partials/sidebar_nav.html @@ -1,49 +1,80 @@ -{% set nav_groups = [ { "label": "Dashboard", "links": [ {"href": "/", "label": -"Dashboard"}, ], }, { "label": "Overview", "links": [ {"href": "/ui/parameters", -"label": "Parameters"}, {"href": "/ui/costs", "label": "Costs"}, {"href": -"/ui/consumption", "label": "Consumption"}, {"href": "/ui/production", "label": -"Production"}, { "href": "/ui/equipment", "label": "Equipment", "children": [ -{"href": "/ui/maintenance", "label": "Maintenance"}, ], }, ], }, { "label": -"Simulations", "links": [ {"href": "/ui/simulations", "label": "Simulations"}, -], }, { "label": "Analytics", "links": [ {"href": "/ui/reporting", "label": -"Reporting"}, ], }, { "label": "Settings", "links": [ { "href": "/ui/settings", -"label": "Settings", "children": [ {"href": "/theme-settings", "label": -"Themes"}, {"href": "/ui/currencies", "label": "Currency Management"}, ], }, ], -}, ] %} +{% set dashboard_href = request.url_for('dashboard.home') if request else '/' %} +{% set projects_href = request.url_for('projects.project_list_page') if request else '/projects/ui' %} +{% set project_create_href = request.url_for('projects.create_project_form') if request else '/projects/create' %} + +{% set nav_groups = [ + { + "label": "Workspace", + "links": [ + {"href": dashboard_href, "label": "Dashboard", "match_prefix": "/"}, + {"href": projects_href, "label": "Projects", "match_prefix": "/projects"}, + {"href": project_create_href, "label": "New Project", "match_prefix": "/projects/create"}, + ], + }, + { + "label": "Insights", + "links": [ + {"href": "/ui/simulations", "label": "Simulations"}, + {"href": "/ui/reporting", "label": "Reporting"}, + ], + }, + { + "label": "Configuration", + "links": [ + { + "href": "/ui/settings", + "label": "Settings", + "children": [ + {"href": "/theme-settings", "label": "Themes"}, + {"href": "/ui/currencies", "label": "Currency Management"}, + ], + }, + ], + }, +] %} -- 2.39.5 From 7f5ed6a42d94045b4381a5fd4170cd688cbaa408 Mon Sep 17 00:00:00 2001 From: zwitschi Date: Sun, 9 Nov 2025 19:02:36 +0100 Subject: [PATCH 016/109] feat: enhance dashboard with new metrics, project and scenario utilities, and comprehensive tests --- changelog.md | 2 + routes/dashboard.py | 137 +++++++++++++++++++--------------- services/repositories.py | 58 +++++++++++++- templates/Dashboard.html | 6 +- tests/test_dashboard_route.py | 73 ++++++++++++++++++ tests/test_repositories.py | 90 +++++++++++++++++++++- 6 files changed, 302 insertions(+), 64 deletions(-) create mode 100644 tests/test_dashboard_route.py diff --git a/changelog.md b/changelog.md index 8c5d180..272b138 100644 --- a/changelog.md +++ b/changelog.md @@ -10,3 +10,5 @@ - Connected project and scenario routers to new Jinja2 list/detail/edit views with HTML forms and redirects. - Implemented FR-009 client-side enhancements with responsive navigation toggle, mobile-first scenario tables, and shared asset loading across templates. - Added scenario comparison validator, FastAPI comparison endpoint, and comprehensive unit tests to enforce FR-009 validation rules through API errors. +- Delivered a new dashboard experience with `templates/dashboard.html`, dedicated styling, and a FastAPI route supplying real project/scenario metrics via repository helpers. +- Extended repositories with count/recency utilities and added pytest coverage, including a dashboard rendering smoke test validating empty-state messaging. diff --git a/routes/dashboard.py b/routes/dashboard.py index ff73c84..94e3eea 100644 --- a/routes/dashboard.py +++ b/routes/dashboard.py @@ -1,83 +1,102 @@ from __future__ import annotations -from datetime import datetime, timedelta -from types import SimpleNamespace +from datetime import datetime from fastapi import APIRouter, Depends, Request from fastapi.responses import HTMLResponse from fastapi.templating import Jinja2Templates from dependencies import get_unit_of_work -from models import MiningOperationType +from models import ScenarioStatus from services.unit_of_work import UnitOfWork router = APIRouter(tags=["Dashboard"]) templates = Jinja2Templates(directory="templates") -def _load_metrics(_: UnitOfWork) -> dict[str, object]: - today = datetime.utcnow() +def _format_timestamp(moment: datetime | None) -> str | None: + if moment is None: + return None + return moment.strftime("%Y-%m-%d") + + +def _format_timestamp_with_time(moment: datetime | None) -> str | None: + if moment is None: + return None + return moment.strftime("%Y-%m-%d %H:%M") + + +def _load_metrics(uow: UnitOfWork) -> dict[str, object]: + total_projects = uow.projects.count() + active_scenarios = uow.scenarios.count_by_status(ScenarioStatus.ACTIVE) + pending_simulations = uow.scenarios.count_by_status(ScenarioStatus.DRAFT) + last_import_at = uow.financial_inputs.latest_created_at() return { - "total_projects": 12, - "active_scenarios": 7, - "pending_simulations": 3, - "last_import": today.strftime("%Y-%m-%d"), + "total_projects": total_projects, + "active_scenarios": active_scenarios, + "pending_simulations": pending_simulations, + "last_import": _format_timestamp(last_import_at), } -def _load_recent_projects(_: UnitOfWork) -> list[SimpleNamespace]: - now = datetime.utcnow() - return [ - SimpleNamespace( - id=1, - name="Copper Ridge Expansion", - operation_type=MiningOperationType.OPEN_PIT, - updated_at=now - timedelta(days=2), - ), - SimpleNamespace( - id=2, - name="Lithium Basin North", - operation_type=MiningOperationType.UNDERGROUND, - updated_at=now - timedelta(days=5), - ), - SimpleNamespace( - id=3, - name="Nickel Underground Phase II", - operation_type=MiningOperationType.IN_SITU_LEACH, - updated_at=now - timedelta(days=9), - ), - ] +def _load_recent_projects(uow: UnitOfWork) -> list: + return list(uow.projects.recent(limit=5)) -def _load_simulation_updates(_: UnitOfWork) -> list[SimpleNamespace]: - now = datetime.utcnow() - return [ - SimpleNamespace( - title="Monte Carlo Batch #21 completed", - description="1,000 runs processed for Lithium Basin North.", - timestamp=now - timedelta(hours=4), - ), - SimpleNamespace( - title="Scenario validation queued", - description="Copper Ridge Expansion pending validation on new cost inputs.", - timestamp=now - timedelta(days=1, hours=3), - ), - ] +def _load_simulation_updates(uow: UnitOfWork) -> list[dict[str, object]]: + updates: list[dict[str, object]] = [] + scenarios = uow.scenarios.recent(limit=5, with_project=True) + for scenario in scenarios: + project_name = scenario.project.name if scenario.project else f"Project #{scenario.project_id}" + timestamp_label = _format_timestamp_with_time(scenario.updated_at) + updates.append( + { + "title": f"{scenario.name} · {scenario.status.value.title()}", + "description": f"Latest update recorded for {project_name}.", + "timestamp": scenario.updated_at, + "timestamp_label": timestamp_label, + } + ) + return updates -def _load_scenario_alerts(_: UnitOfWork) -> list[SimpleNamespace]: - return [ - SimpleNamespace( - title="Variance exceeds threshold", - message="Nickel Underground Phase II deviates 18% from baseline forecast.", - link="/projects/3/view", - ), - SimpleNamespace( - title="Simulation backlog", - message="Lithium Basin North has 2 pending simulation batches.", - link="/projects/2/view", - ), - ] +def _load_scenario_alerts( + request: Request, uow: UnitOfWork +) -> list[dict[str, object]]: + alerts: list[dict[str, object]] = [] + + drafts = uow.scenarios.list_by_status( + ScenarioStatus.DRAFT, limit=3, with_project=True + ) + for scenario in drafts: + project_name = scenario.project.name if scenario.project else f"Project #{scenario.project_id}" + alerts.append( + { + "title": f"Draft scenario: {scenario.name}", + "message": f"{project_name} has a scenario awaiting validation.", + "link": request.url_for( + "projects.view_project", project_id=scenario.project_id + ), + } + ) + + if not alerts: + archived = uow.scenarios.list_by_status( + ScenarioStatus.ARCHIVED, limit=3, with_project=True + ) + for scenario in archived: + project_name = scenario.project.name if scenario.project else f"Project #{scenario.project_id}" + alerts.append( + { + "title": f"Archived scenario: {scenario.name}", + "message": f"Review archived scenario insights for {project_name}.", + "link": request.url_for( + "scenarios.view_scenario", scenario_id=scenario.id + ), + } + ) + + return alerts @router.get("/", response_class=HTMLResponse, include_in_schema=False, name="dashboard.home") @@ -90,6 +109,6 @@ def dashboard_home( "metrics": _load_metrics(uow), "recent_projects": _load_recent_projects(uow), "simulation_updates": _load_simulation_updates(uow), - "scenario_alerts": _load_scenario_alerts(uow), + "scenario_alerts": _load_scenario_alerts(request, uow), } return templates.TemplateResponse("dashboard.html", context) diff --git a/services/repositories.py b/services/repositories.py index 8d3dc66..9defd8b 100644 --- a/services/repositories.py +++ b/services/repositories.py @@ -1,13 +1,14 @@ from __future__ import annotations from collections.abc import Iterable +from datetime import datetime from typing import Sequence -from sqlalchemy import select +from sqlalchemy import select, func from sqlalchemy.exc import IntegrityError from sqlalchemy.orm import Session, joinedload, selectinload -from models import FinancialInput, Project, Scenario, SimulationParameter +from models import FinancialInput, Project, Scenario, ScenarioStatus, SimulationParameter from services.exceptions import EntityConflictError, EntityNotFoundError @@ -23,6 +24,18 @@ class ProjectRepository: stmt = stmt.options(selectinload(Project.scenarios)) return self.session.execute(stmt).scalars().all() + def count(self) -> int: + stmt = select(func.count(Project.id)) + return self.session.execute(stmt).scalar_one() + + def recent(self, limit: int = 5) -> Sequence[Project]: + stmt = ( + select(Project) + .order_by(Project.updated_at.desc()) + .limit(limit) + ) + return self.session.execute(stmt).scalars().all() + def get(self, project_id: int, *, with_children: bool = False) -> Project: stmt = select(Project).where(Project.id == project_id) if with_children: @@ -60,6 +73,39 @@ class ScenarioRepository: ) return self.session.execute(stmt).scalars().all() + def count(self) -> int: + stmt = select(func.count(Scenario.id)) + return self.session.execute(stmt).scalar_one() + + def count_by_status(self, status: ScenarioStatus) -> int: + stmt = select(func.count(Scenario.id)).where(Scenario.status == status) + return self.session.execute(stmt).scalar_one() + + def recent(self, limit: int = 5, *, with_project: bool = False) -> Sequence[Scenario]: + stmt = select(Scenario).order_by( + Scenario.updated_at.desc()).limit(limit) + if with_project: + stmt = stmt.options(joinedload(Scenario.project)) + return self.session.execute(stmt).scalars().all() + + def list_by_status( + self, + status: ScenarioStatus, + *, + limit: int | None = None, + with_project: bool = False, + ) -> Sequence[Scenario]: + stmt = ( + select(Scenario) + .where(Scenario.status == status) + .order_by(Scenario.updated_at.desc()) + ) + if with_project: + stmt = stmt.options(joinedload(Scenario.project)) + if limit is not None: + stmt = stmt.limit(limit) + return self.session.execute(stmt).scalars().all() + def get(self, scenario_id: int, *, with_children: bool = False) -> Scenario: stmt = select(Scenario).where(Scenario.id == scenario_id) if with_children: @@ -119,6 +165,14 @@ class FinancialInputRepository: raise EntityNotFoundError(f"Financial input {input_id} not found") self.session.delete(entity) + def latest_created_at(self) -> datetime | None: + stmt = ( + select(FinancialInput.created_at) + .order_by(FinancialInput.created_at.desc()) + .limit(1) + ) + return self.session.execute(stmt).scalar_one_or_none() + class SimulationParameterRepository: """Persistence operations for SimulationParameter entities.""" diff --git a/templates/Dashboard.html b/templates/Dashboard.html index f1ff951..d843aa0 100644 --- a/templates/Dashboard.html +++ b/templates/Dashboard.html @@ -63,7 +63,7 @@ {{ project.name }} {{ project.operation_type.value.replace('_', ' ') | title }} - {{ project.updated_at.strftime('%Y-%m-%d') }} + {{ project.updated_at.strftime('%Y-%m-%d') if project.updated_at else '—' }} {% endfor %} @@ -81,7 +81,7 @@
    {% for update in simulation_updates %}
  • - {{ update.timestamp.strftime('%Y-%m-%d %H:%M') }} + {{ update.timestamp_label or '—' }}
    {{ update.title }}

    {{ update.description }}

    @@ -106,7 +106,9 @@
  • {{ alert.title }}

    {{ alert.message }}

    + {% if alert.link %} Review + {% endif %}
  • {% endfor %}
diff --git a/tests/test_dashboard_route.py b/tests/test_dashboard_route.py new file mode 100644 index 0000000..35a7131 --- /dev/null +++ b/tests/test_dashboard_route.py @@ -0,0 +1,73 @@ +from __future__ import annotations + +from collections.abc import Iterator + +import pytest +from fastapi import FastAPI +from fastapi.testclient import TestClient +from sqlalchemy import create_engine +from sqlalchemy.engine import Engine +from sqlalchemy.orm import sessionmaker +from sqlalchemy.pool import StaticPool + +from config.database import Base +from dependencies import get_unit_of_work +from routes.dashboard import router as dashboard_router +from routes.projects import router as projects_router +from routes.scenarios import router as scenarios_router +from services.unit_of_work import UnitOfWork + + +@pytest.fixture() +def engine() -> Iterator[Engine]: + engine = create_engine( + "sqlite+pysqlite:///:memory:", + future=True, + connect_args={"check_same_thread": False}, + poolclass=StaticPool, + ) + Base.metadata.create_all(bind=engine) + try: + yield engine + finally: + Base.metadata.drop_all(bind=engine) + engine.dispose() + + +@pytest.fixture() +def session_factory(engine: Engine) -> Iterator[sessionmaker]: + testing_session = sessionmaker( + bind=engine, expire_on_commit=False, future=True) + yield testing_session + + +@pytest.fixture() +def client(session_factory: sessionmaker) -> Iterator[TestClient]: + app = FastAPI() + app.include_router(dashboard_router) + app.include_router(projects_router) + app.include_router(scenarios_router) + + def _override_uow() -> Iterator[UnitOfWork]: + with UnitOfWork(session_factory=session_factory) as uow: + yield uow + + app.dependency_overrides[get_unit_of_work] = _override_uow + + test_client = TestClient(app) + try: + yield test_client + finally: + test_client.close() + + +class TestDashboardRoute: + def test_renders_empty_state(self, client: TestClient) -> None: + response = client.get("/") + assert response.status_code == 200 + html = response.text + + assert "No recent projects" in html + assert "No simulation runs yet" in html + assert "All scenarios look good" in html + assert "—" in html # Last data import placeholder diff --git a/tests/test_repositories.py b/tests/test_repositories.py index 23ae50f..7e00910 100644 --- a/tests/test_repositories.py +++ b/tests/test_repositories.py @@ -1,6 +1,7 @@ from __future__ import annotations from collections.abc import Iterator +from datetime import datetime, timezone import pytest from sqlalchemy import create_engine @@ -153,4 +154,91 @@ def test_unit_of_work_commit_and_rollback(engine) -> None: with TestingSession() as session: projects = ProjectRepository(session).list() - assert len(projects) == 1 \ No newline at end of file + assert len(projects) == 1 + + +def test_project_repository_count_and_recent(session: Session) -> None: + repo = ProjectRepository(session) + project_alpha = Project(name="Alpha", operation_type=MiningOperationType.OPEN_PIT) + project_bravo = Project(name="Bravo", operation_type=MiningOperationType.UNDERGROUND) + + repo.create(project_alpha) + repo.create(project_bravo) + + project_alpha.updated_at = datetime(2025, 1, 1, tzinfo=timezone.utc) + project_bravo.updated_at = datetime(2025, 1, 2, tzinfo=timezone.utc) + session.flush() + + assert repo.count() == 2 + recent = repo.recent(limit=1) + assert len(recent) == 1 + assert recent[0].name == "Bravo" + + +def test_scenario_repository_counts_and_filters(session: Session) -> None: + project = Project(name="Project", operation_type=MiningOperationType.PLACER) + session.add(project) + session.flush() + + repo = ScenarioRepository(session) + draft = Scenario(name="Draft", project_id=project.id, + status=ScenarioStatus.DRAFT) + active = Scenario(name="Active", project_id=project.id, + status=ScenarioStatus.ACTIVE) + + repo.create(draft) + repo.create(active) + + draft.updated_at = datetime(2025, 1, 1, tzinfo=timezone.utc) + active.updated_at = datetime(2025, 1, 3, tzinfo=timezone.utc) + session.flush() + + assert repo.count() == 2 + assert repo.count_by_status(ScenarioStatus.ACTIVE) == 1 + + recent = repo.recent(limit=1, with_project=True) + assert len(recent) == 1 + assert recent[0].name == "Active" + assert recent[0].project.name == "Project" + + drafts = repo.list_by_status(ScenarioStatus.DRAFT, limit=2, with_project=True) + assert len(drafts) == 1 + assert drafts[0].name == "Draft" + assert drafts[0].project_id == project.id + + +def test_financial_input_repository_latest_created_at(session: Session) -> None: + project = Project(name="Project FI", operation_type=MiningOperationType.OTHER) + scenario = Scenario(name="Scenario FI", project=project) + session.add(project) + session.flush() + + repo = FinancialInputRepository(session) + old_timestamp = datetime(2024, 12, 31, 15, 0) + new_timestamp = datetime(2025, 1, 2, 8, 30) + + repo.bulk_upsert( + [ + FinancialInput( + scenario_id=scenario.id, + name="Legacy Entry", + category=FinancialCategory.OPERATING_EXPENDITURE, + amount=1000, + currency="usd", + created_at=old_timestamp, + updated_at=old_timestamp, + ), + FinancialInput( + scenario_id=scenario.id, + name="New Entry", + category=FinancialCategory.OPERATING_EXPENDITURE, + amount=2000, + currency="usd", + created_at=new_timestamp, + updated_at=new_timestamp, + ), + ] + ) + + latest = repo.latest_created_at() + assert latest == new_timestamp \ No newline at end of file -- 2.39.5 From 400f85c90713456244daf1d3132cd565e7f11075 Mon Sep 17 00:00:00 2001 From: zwitschi Date: Sun, 9 Nov 2025 19:15:48 +0100 Subject: [PATCH 017/109] feat: enhance project and scenario detail pages with metrics, improved layouts, and updated styles --- changelog.md | 1 + routes/projects.py | 13 ++- routes/scenarios.py | 8 ++ static/css/projects.css | 115 ++++++++++++++++-- static/css/scenarios.css | 88 +++++++++----- templates/projects/detail.html | 147 ++++++++++++++--------- templates/projects/form.html | 48 +++++--- templates/scenarios/detail.html | 200 +++++++++++++++++--------------- templates/scenarios/form.html | 12 +- 9 files changed, 419 insertions(+), 213 deletions(-) diff --git a/changelog.md b/changelog.md index 272b138..642ee46 100644 --- a/changelog.md +++ b/changelog.md @@ -12,3 +12,4 @@ - Added scenario comparison validator, FastAPI comparison endpoint, and comprehensive unit tests to enforce FR-009 validation rules through API errors. - Delivered a new dashboard experience with `templates/dashboard.html`, dedicated styling, and a FastAPI route supplying real project/scenario metrics via repository helpers. - Extended repositories with count/recency utilities and added pytest coverage, including a dashboard rendering smoke test validating empty-state messaging. +- Brought project and scenario detail pages plus their forms in line with the dashboard visuals, adding metric cards, layout grids, and refreshed CTA styles. diff --git a/routes/projects.py b/routes/projects.py index fb937c6..57a1c30 100644 --- a/routes/projects.py +++ b/routes/projects.py @@ -7,7 +7,7 @@ from fastapi.responses import HTMLResponse, RedirectResponse from fastapi.templating import Jinja2Templates from dependencies import get_unit_of_work -from models import MiningOperationType, Project +from models import MiningOperationType, Project, ScenarioStatus from schemas.project import ProjectCreate, ProjectRead, ProjectUpdate from services.exceptions import EntityConflictError, EntityNotFoundError from services.unit_of_work import UnitOfWork @@ -205,12 +205,23 @@ def view_project( ) from exc scenarios = sorted(project.scenarios, key=lambda s: s.created_at) + scenario_stats = { + "total": len(scenarios), + "active": sum(1 for scenario in scenarios if scenario.status == ScenarioStatus.ACTIVE), + "draft": sum(1 for scenario in scenarios if scenario.status == ScenarioStatus.DRAFT), + "archived": sum(1 for scenario in scenarios if scenario.status == ScenarioStatus.ARCHIVED), + "latest_update": max( + (scenario.updated_at for scenario in scenarios if scenario.updated_at), + default=None, + ), + } return templates.TemplateResponse( "projects/detail.html", { "request": request, "project": project, "scenarios": scenarios, + "scenario_stats": scenario_stats, }, ) diff --git a/routes/scenarios.py b/routes/scenarios.py index bce46f1..565cb64 100644 --- a/routes/scenarios.py +++ b/routes/scenarios.py @@ -339,12 +339,20 @@ def view_scenario( scenario.simulation_parameters, key=lambda item: item.created_at ) + scenario_metrics = { + "financial_count": len(financial_inputs), + "parameter_count": len(simulation_parameters), + "currency": scenario.currency, + "primary_resource": scenario.primary_resource.value.replace('_', ' ').title() if scenario.primary_resource else None, + } + return templates.TemplateResponse( "scenarios/detail.html", { "request": request, "project": project, "scenario": scenario, + "scenario_metrics": scenario_metrics, "financial_inputs": financial_inputs, "simulation_parameters": simulation_parameters, }, diff --git a/static/css/projects.css b/static/css/projects.css index bc0daf2..8463267 100644 --- a/static/css/projects.css +++ b/static/css/projects.css @@ -4,23 +4,59 @@ --hover-highlight: rgba(241, 178, 26, 0.12); } -.projects-table { - width: 100%; - border-collapse: collapse; - border-radius: var(--table-radius); - overflow: hidden; - box-shadow: var(--shadow); +.header-actions { + display: flex; + gap: 0.75rem; + flex-wrap: wrap; + justify-content: flex-end; } -.projects-table th, -.projects-table td { - padding: 0.875rem 1rem; - border-bottom: 1px solid var(--card-border); +.project-metrics { + display: grid; + gap: 1.5rem; + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); + margin-bottom: 2rem; +} + +.metric-card { background: var(--card-bg); + border-radius: var(--radius); + padding: 1.5rem; + box-shadow: var(--shadow); + border: 1px solid var(--card-border); + display: flex; + flex-direction: column; + gap: 0.35rem; } -.projects-table tbody tr:hover { - background: var(--hover-highlight); +.metric-card h2 { + margin: 0; + font-size: 1rem; + color: var(--muted); + text-transform: uppercase; + letter-spacing: 0.08em; +} + +.metric-value { + font-size: 2rem; + font-weight: 700; + margin: 0; +} + +.metric-caption { + color: var(--color-text-subtle); + font-size: 0.85rem; +} + +.project-form { + background: var(--card-bg); + border: 1px solid var(--card-border); + border-radius: var(--radius); + box-shadow: var(--shadow); + padding: 1.75rem; + display: flex; + flex-direction: column; + gap: 1.5rem; } .definition-list { @@ -62,6 +98,61 @@ margin: 0; } +.project-layout { + display: grid; + gap: 1.5rem; +} + +.table-responsive { + overflow-x: auto; + border-radius: var(--table-radius); +} + +.table { + width: 100%; + border-collapse: collapse; + border-radius: var(--table-radius); + overflow: hidden; + box-shadow: var(--shadow); +} + +.table th, +.table td { + padding: 0.75rem 1rem; + border-bottom: 1px solid var(--card-border); + background: rgba(21, 27, 35, 0.85); +} + +.table tbody tr:hover { + background: var(--hover-highlight); +} + +.table-link { + color: var(--brand-2); + text-decoration: none; + margin-left: 0.5rem; +} + +.table-link:hover, +.table-link:focus { + text-decoration: underline; +} + +.text-right { + text-align: right; +} + +@media (min-width: 960px) { + .project-layout { + grid-template-columns: 1.1fr 1.9fr; + align-items: start; + } + + .header-actions { + justify-content: flex-start; + } +} + .alert { padding: 0.75rem 1rem; border-radius: var(--radius-sm); diff --git a/static/css/scenarios.css b/static/css/scenarios.css index 981f7a3..fa9d981 100644 --- a/static/css/scenarios.css +++ b/static/css/scenarios.css @@ -37,44 +37,48 @@ text-decoration: none; } -.actions { +.header-actions { display: flex; gap: 0.75rem; + flex-wrap: wrap; + justify-content: flex-end; } -.btn { - display: inline-flex; - align-items: center; - justify-content: center; - padding: 0.6rem 1.1rem; - border-radius: var(--radius-sm); - text-decoration: none; - font-weight: 600; - transition: transform 0.2s ease, box-shadow 0.2s ease; +.scenario-metrics { + display: grid; + gap: 1.5rem; + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); + margin-bottom: 2rem; } -.btn-primary { - background: linear-gradient(90deg, var(--brand) 0%, var(--brand-2) 100%); - color: var(--color-text-dark); - box-shadow: 0 8px 18px rgba(241, 178, 26, 0.25); +.metric-card { + background: rgba(21, 27, 35, 0.85); + border-radius: var(--radius); + padding: 1.5rem; + box-shadow: var(--shadow); + border: 1px solid var(--color-border); + display: flex; + flex-direction: column; + gap: 0.35rem; } -.btn-secondary { - background: rgba(148, 197, 255, 0.2); - color: var(--color-text-invert); - border: 1px solid rgba(148, 197, 255, 0.35); +.metric-card h2 { + margin: 0; + font-size: 1rem; + color: var(--muted); + text-transform: uppercase; + letter-spacing: 0.08em; } -.btn-link { - padding: 0.35rem 0.5rem; - color: var(--brand-3); - text-decoration: none; +.metric-value { + font-size: 2rem; + font-weight: 700; + margin: 0; } -.btn:hover, -.btn:focus { - transform: translateY(-1px); - box-shadow: 0 10px 24px rgba(0, 0, 0, 0.25); +.metric-caption { + color: var(--color-text-subtle); + font-size: 0.85rem; } .scenario-filters { @@ -106,6 +110,17 @@ color: var(--text); } +.scenario-form { + background: rgba(21, 27, 35, 0.85); + border: 1px solid var(--color-border); + border-radius: var(--radius); + box-shadow: var(--shadow); + padding: 1.75rem; + display: flex; + flex-direction: column; + gap: 1.5rem; +} + .table-responsive { width: 100%; overflow-x: auto; @@ -159,3 +174,24 @@ border-radius: var(--radius-sm); } } + +.scenario-layout { + display: grid; + gap: 1.5rem; +} + +.empty-state { + color: var(--muted); + font-style: italic; +} + +@media (min-width: 960px) { + .header-actions { + justify-content: flex-start; + } + + .scenario-layout { + grid-template-columns: 1.1fr 1.9fr; + align-items: start; + } +} diff --git a/templates/projects/detail.html b/templates/projects/detail.html index 001ec4c..10517bb 100644 --- a/templates/projects/detail.html +++ b/templates/projects/detail.html @@ -16,67 +16,98 @@

{{ project.name }}

{{ project.operation_type.value.replace('_', ' ') | title }}

-
- Edit - New Scenario + -
-

Project Overview

-
-
-
Location
-
{{ project.location or '—' }}
-
-
-
Description
-
{{ project.description or 'No description provided.' }}
-
-
-
Created
-
{{ project.created_at.strftime('%Y-%m-%d %H:%M') }}
-
-
-
Updated
-
{{ project.updated_at.strftime('%Y-%m-%d %H:%M') }}
-
-
+
+
+

Total Scenarios

+

{{ scenario_stats.total }}

+ Across this project +
+
+

Active

+

{{ scenario_stats.active }}

+ Currently live analyses +
+
+

Draft

+

{{ scenario_stats.draft }}

+ Awaiting validation +
+
+

Archived

+

{{ scenario_stats.archived }}

+ Historical references +
-
-
-

Scenarios

- Add Scenario -
- {% if scenarios %} - - - - - - - - - - - - {% for scenario in scenarios %} - - - - - - - - {% endfor %} - -
NameStatusCurrencyPrimary Resource
{{ scenario.name }}{{ scenario.status.value.title() }}{{ scenario.currency or '—' }}{{ scenario.primary_resource.value.replace('_', ' ') | title if scenario.primary_resource else '—' }} - View - Edit -
- {% else %} -

No scenarios yet.

- {% endif %} -
+
+
+

Project Overview

+
+
+
Location
+
{{ project.location or '—' }}
+
+
+
Description
+
{{ project.description or 'No description provided.' }}
+
+
+
Created
+
{{ project.created_at.strftime('%Y-%m-%d %H:%M') }}
+
+
+
Updated
+
{{ project.updated_at.strftime('%Y-%m-%d %H:%M') }}
+
+
+
Latest Scenario Update
+
{{ scenario_stats.latest_update.strftime('%Y-%m-%d %H:%M') if scenario_stats.latest_update else '—' }}
+
+
+
+ +
+
+

Scenarios

+ Add Scenario +
+ {% if scenarios %} +
+ + + + + + + + + + + + {% for scenario in scenarios %} + + + + + + + + {% endfor %} + +
NameStatusCurrencyPrimary ResourceActions
{{ scenario.name }}{{ scenario.status.value.title() }}{{ scenario.currency or '—' }}{{ scenario.primary_resource.value.replace('_', ' ') | title if scenario.primary_resource else '—' }} + View + Edit +
+
+ {% else %} +

No scenarios yet. Create the first scenario.

+ {% endif %} +
+
{% endblock %} diff --git a/templates/projects/form.html b/templates/projects/form.html index 88f83f0..505f19a 100644 --- a/templates/projects/form.html +++ b/templates/projects/form.html @@ -21,40 +21,50 @@

{% if project %}Edit Project{% else %}Create Project{% endif %}

Provide core information about the mining project.

+
+ Cancel + +
{% if error %}
{{ error }}
{% endif %} - -
- - -
+ {% if error %} +
{{ error }}
+ {% endif %} -
- - -
+ +
+
+ + +
-
- - +
+ + +
+ +
+ + +
- +
- Cancel - + Cancel +
{% endblock %} diff --git a/templates/scenarios/detail.html b/templates/scenarios/detail.html index eb6980a..9f99ae1 100644 --- a/templates/scenarios/detail.html +++ b/templates/scenarios/detail.html @@ -17,104 +17,118 @@

{{ scenario.name }}

Status: {{ scenario.status.value.title() }}

- Edit + -
-

Scenario Details

-
-
-
Description
-
{{ scenario.description or 'No description provided.' }}
-
-
-
Timeline
-
- {% if scenario.start_date %} - {{ scenario.start_date }} - {% else %} - — - {% endif %} - → - {% if scenario.end_date %} - {{ scenario.end_date }} - {% else %} - — - {% endif %} -
-
-
-
Discount Rate
-
{{ scenario.discount_rate or '—' }}
-
-
-
Currency
-
{{ scenario.currency or '—' }}
-
-
-
Primary Resource
-
{{ scenario.primary_resource.value.replace('_', ' ') | title if scenario.primary_resource else '—' }}
-
-
+
+
+

Financial Inputs

+

{{ scenario_metrics.financial_count }}

+ Line items captured +
+
+

Simulation Parameters

+

{{ scenario_metrics.parameter_count }}

+ Inputs driving forecasts +
+
+

Currency

+

{{ scenario_metrics.currency or '—' }}

+ Financial reporting +
+
+

Primary Resource

+

{{ scenario_metrics.primary_resource or '—' }}

+ Scenario focus +
-
-

Financial Inputs

- {% if financial_inputs %} -
- - - - - - - - - - - {% for item in financial_inputs %} - - - - - - - {% endfor %} - -
NameCategoryAmountCurrency
{{ item.name }}{{ item.category.value.title() }}{{ '{:,.2f}'.format(item.amount) }}{{ item.currency or '—' }}
-
- {% else %} -

No financial inputs recorded.

- {% endif %} -
+
+
+

Scenario Details

+
+
+
Description
+
{{ scenario.description or 'No description provided.' }}
+
+
+
Timeline
+
+ {{ scenario.start_date or '—' }} → {{ scenario.end_date or '—' }} +
+
+
+
Discount Rate
+
{{ scenario.discount_rate or '—' }}
+
+
+
Last Updated
+
{{ scenario.updated_at.strftime('%Y-%m-%d %H:%M') if scenario.updated_at else '—' }}
+
+
+
-
-

Simulation Parameters

- {% if simulation_parameters %} -
- - - - - - - - - - - {% for param in simulation_parameters %} +
+

Financial Inputs

+ {% if financial_inputs %} +
+
NameDistributionVariableResource
+ - - - - + + + + - {% endfor %} - -
{{ param.name }}{{ param.distribution.value.title() }}{{ param.variable.value.replace('_', ' ') | title if param.variable else '—' }}{{ param.resource_type.value.replace('_', ' ') | title if param.resource_type else '—' }}NameCategoryAmountCurrency
-
- {% else %} -

No simulation parameters defined.

- {% endif %} -
+ + + {% for item in financial_inputs %} + + {{ item.name }} + {{ item.category.value.title() }} + {{ '{:,.2f}'.format(item.amount) }} + {{ item.currency or '—' }} + + {% endfor %} + + +
+ {% else %} +

No financial inputs recorded yet.

+ {% endif %} +
+ +
+

Simulation Parameters

+ {% if simulation_parameters %} +
+ + + + + + + + + + + {% for param in simulation_parameters %} + + + + + + + {% endfor %} + +
NameDistributionVariableResource
{{ param.name }}{{ param.distribution.value.title() }}{{ param.variable.value.replace('_', ' ') | title if param.variable else '—' }}{{ param.resource_type.value.replace('_', ' ') | title if param.resource_type else '—' }}
+
+ {% else %} +

No simulation parameters defined.

+ {% endif %} +
+ {% endblock %} diff --git a/templates/scenarios/form.html b/templates/scenarios/form.html index c651037..0ddea13 100644 --- a/templates/scenarios/form.html +++ b/templates/scenarios/form.html @@ -21,13 +21,17 @@

{% if scenario %}Edit Scenario{% else %}Create Scenario{% endif %}

Configure assumptions and metadata for this scenario.

+
+ Cancel + +
{% if error %}
{{ error }}
{% endif %} -
+
@@ -76,12 +80,12 @@
- +
- Cancel - + Cancel +
{% endblock %} -- 2.39.5 From dad862e48e60cd13e090e9f350d254d5e174d5cb Mon Sep 17 00:00:00 2001 From: zwitschi Date: Sun, 9 Nov 2025 19:21:25 +0100 Subject: [PATCH 018/109] feat: reorder project route registration to prioritize static UI paths and add pytest coverage for navigation endpoints --- changelog.md | 1 + routes/projects.py | 81 ++++++++++++++++++----------------- tests/test_dashboard_route.py | 12 ++++++ 3 files changed, 55 insertions(+), 39 deletions(-) diff --git a/changelog.md b/changelog.md index 642ee46..6feea6d 100644 --- a/changelog.md +++ b/changelog.md @@ -13,3 +13,4 @@ - Delivered a new dashboard experience with `templates/dashboard.html`, dedicated styling, and a FastAPI route supplying real project/scenario metrics via repository helpers. - Extended repositories with count/recency utilities and added pytest coverage, including a dashboard rendering smoke test validating empty-state messaging. - Brought project and scenario detail pages plus their forms in line with the dashboard visuals, adding metric cards, layout grids, and refreshed CTA styles. +- Reordered project route registration to prioritize static UI paths, eliminating 422 errors on `/projects/ui` and `/projects/create`, and added pytest smoke coverage for the navigation endpoints. diff --git a/routes/projects.py b/routes/projects.py index 57a1c30..2511620 100644 --- a/routes/projects.py +++ b/routes/projects.py @@ -46,45 +46,6 @@ def create_project( return _to_read_model(created) -@router.get("/{project_id}", response_model=ProjectRead) -def get_project(project_id: int, uow: UnitOfWork = Depends(get_unit_of_work)) -> ProjectRead: - try: - project = uow.projects.get(project_id) - except EntityNotFoundError as exc: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)) from exc - return _to_read_model(project) - - -@router.put("/{project_id}", response_model=ProjectRead) -def update_project( - project_id: int, - payload: ProjectUpdate, - uow: UnitOfWork = Depends(get_unit_of_work), -) -> ProjectRead: - try: - project = uow.projects.get(project_id) - except EntityNotFoundError as exc: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)) from exc - - update_data = payload.model_dump(exclude_unset=True) - for field, value in update_data.items(): - setattr(project, field, value) - - uow.flush() - return _to_read_model(project) - - -@router.delete("/{project_id}", status_code=status.HTTP_204_NO_CONTENT) -def delete_project(project_id: int, uow: UnitOfWork = Depends(get_unit_of_work)) -> None: - try: - uow.projects.delete(project_id) - except EntityNotFoundError as exc: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)) from exc - - @router.get( "/ui", response_class=HTMLResponse, @@ -188,6 +149,48 @@ def create_project_submit( ) +@router.get("/{project_id}", response_model=ProjectRead) +def get_project(project_id: int, uow: UnitOfWork = Depends(get_unit_of_work)) -> ProjectRead: + try: + project = uow.projects.get(project_id) + except EntityNotFoundError as exc: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail=str(exc) + ) from exc + return _to_read_model(project) + + +@router.put("/{project_id}", response_model=ProjectRead) +def update_project( + project_id: int, + payload: ProjectUpdate, + uow: UnitOfWork = Depends(get_unit_of_work), +) -> ProjectRead: + try: + project = uow.projects.get(project_id) + except EntityNotFoundError as exc: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail=str(exc) + ) from exc + + update_data = payload.model_dump(exclude_unset=True) + for field, value in update_data.items(): + setattr(project, field, value) + + uow.flush() + return _to_read_model(project) + + +@router.delete("/{project_id}", status_code=status.HTTP_204_NO_CONTENT) +def delete_project(project_id: int, uow: UnitOfWork = Depends(get_unit_of_work)) -> None: + try: + uow.projects.delete(project_id) + except EntityNotFoundError as exc: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail=str(exc) + ) from exc + + @router.get( "/{project_id}/view", response_class=HTMLResponse, diff --git a/tests/test_dashboard_route.py b/tests/test_dashboard_route.py index 35a7131..02bab98 100644 --- a/tests/test_dashboard_route.py +++ b/tests/test_dashboard_route.py @@ -71,3 +71,15 @@ class TestDashboardRoute: assert "No simulation runs yet" in html assert "All scenarios look good" in html assert "—" in html # Last data import placeholder + + +class TestProjectUIRoutes: + def test_projects_ui_page_resolves(self, client: TestClient) -> None: + response = client.get("/projects/ui") + assert response.status_code == 200 + assert "Projects" in response.text + + def test_projects_create_form_resolves(self, client: TestClient) -> None: + response = client.get("/projects/create") + assert response.status_code == 200 + assert "Create Project" in response.text -- 2.39.5 From 2d848c2e09ea676e4eef3d15058bf30319cde443 Mon Sep 17 00:00:00 2001 From: zwitschi Date: Sun, 9 Nov 2025 19:47:35 +0100 Subject: [PATCH 019/109] feat: add integration tests for project and scenario lifecycles, update templates to new Starlette signature, and optimize project retrieval logic --- changelog.md | 2 + routes/dashboard.py | 3 +- routes/projects.py | 14 +-- routes/scenarios.py | 8 +- services/repositories.py | 5 +- tests/conftest.py | 72 +++++++++++++ tests/integration/README.md | 30 ++++++ tests/integration/test_project_lifecycle.py | 66 ++++++++++++ tests/integration/test_scenario_lifecycle.py | 106 +++++++++++++++++++ tests/test_dashboard_route.py | 58 ---------- 10 files changed, 292 insertions(+), 72 deletions(-) create mode 100644 tests/conftest.py create mode 100644 tests/integration/README.md create mode 100644 tests/integration/test_project_lifecycle.py create mode 100644 tests/integration/test_scenario_lifecycle.py diff --git a/changelog.md b/changelog.md index 6feea6d..49e0146 100644 --- a/changelog.md +++ b/changelog.md @@ -14,3 +14,5 @@ - Extended repositories with count/recency utilities and added pytest coverage, including a dashboard rendering smoke test validating empty-state messaging. - Brought project and scenario detail pages plus their forms in line with the dashboard visuals, adding metric cards, layout grids, and refreshed CTA styles. - Reordered project route registration to prioritize static UI paths, eliminating 422 errors on `/projects/ui` and `/projects/create`, and added pytest smoke coverage for the navigation endpoints. +- Added end-to-end integration tests for project and scenario lifecycles, validating HTML redirects, template rendering, and API interactions, and updated `ProjectRepository.get` to deduplicate joined loads for detail views. +- Updated all Jinja2 template responses to the new Starlette signature to eliminate deprecation warnings while keeping request-aware context available to the templates. diff --git a/routes/dashboard.py b/routes/dashboard.py index 94e3eea..a8f32cf 100644 --- a/routes/dashboard.py +++ b/routes/dashboard.py @@ -105,10 +105,9 @@ def dashboard_home( uow: UnitOfWork = Depends(get_unit_of_work), ) -> HTMLResponse: context = { - "request": request, "metrics": _load_metrics(uow), "recent_projects": _load_recent_projects(uow), "simulation_updates": _load_simulation_updates(uow), "scenario_alerts": _load_scenario_alerts(request, uow), } - return templates.TemplateResponse("dashboard.html", context) + return templates.TemplateResponse(request, "dashboard.html", context) diff --git a/routes/projects.py b/routes/projects.py index 2511620..a449e02 100644 --- a/routes/projects.py +++ b/routes/projects.py @@ -59,9 +59,9 @@ def project_list_page( for project in projects: setattr(project, "scenario_count", len(project.scenarios)) return templates.TemplateResponse( + request, "projects/list.html", { - "request": request, "projects": projects, }, ) @@ -75,9 +75,9 @@ def project_list_page( ) def create_project_form(request: Request) -> HTMLResponse: return templates.TemplateResponse( + request, "projects/form.html", { - "request": request, "project": None, "operation_types": _operation_type_choices(), "form_action": request.url_for("projects.create_project_submit"), @@ -109,9 +109,9 @@ def create_project_submit( op_type = MiningOperationType(operation_type) except ValueError as exc: return templates.TemplateResponse( + request, "projects/form.html", { - "request": request, "project": None, "operation_types": _operation_type_choices(), "form_action": request.url_for("projects.create_project_submit"), @@ -131,9 +131,9 @@ def create_project_submit( uow.projects.create(project) except EntityConflictError as exc: return templates.TemplateResponse( + request, "projects/form.html", { - "request": request, "project": project, "operation_types": _operation_type_choices(), "form_action": request.url_for("projects.create_project_submit"), @@ -219,9 +219,9 @@ def view_project( ), } return templates.TemplateResponse( + request, "projects/detail.html", { - "request": request, "project": project, "scenarios": scenarios, "scenario_stats": scenario_stats, @@ -246,9 +246,9 @@ def edit_project_form( ) from exc return templates.TemplateResponse( + request, "projects/form.html", { - "request": request, "project": project, "operation_types": _operation_type_choices(), "form_action": request.url_for( @@ -295,9 +295,9 @@ def edit_project_submit( project.operation_type = MiningOperationType(operation_type) except ValueError as exc: return templates.TemplateResponse( + request, "projects/form.html", { - "request": request, "project": project, "operation_types": _operation_type_choices(), "form_action": request.url_for( diff --git a/routes/scenarios.py b/routes/scenarios.py index 565cb64..aa4803d 100644 --- a/routes/scenarios.py +++ b/routes/scenarios.py @@ -218,9 +218,9 @@ def create_scenario_form( ) from exc return templates.TemplateResponse( + request, "scenarios/form.html", { - "request": request, "project": project, "scenario": None, "scenario_statuses": _scenario_status_choices(), @@ -291,9 +291,9 @@ def create_scenario_submit( uow.scenarios.create(scenario) except EntityConflictError as exc: return templates.TemplateResponse( + request, "scenarios/form.html", { - "request": request, "project": project, "scenario": scenario, "scenario_statuses": _scenario_status_choices(), @@ -347,9 +347,9 @@ def view_scenario( } return templates.TemplateResponse( + request, "scenarios/detail.html", { - "request": request, "project": project, "scenario": scenario, "scenario_metrics": scenario_metrics, @@ -378,9 +378,9 @@ def edit_scenario_form( project = uow.projects.get(scenario.project_id) return templates.TemplateResponse( + request, "scenarios/form.html", { - "request": request, "project": project, "scenario": scenario, "scenario_statuses": _scenario_status_choices(), diff --git a/services/repositories.py b/services/repositories.py index 9defd8b..5556638 100644 --- a/services/repositories.py +++ b/services/repositories.py @@ -40,7 +40,10 @@ class ProjectRepository: stmt = select(Project).where(Project.id == project_id) if with_children: stmt = stmt.options(joinedload(Project.scenarios)) - project = self.session.execute(stmt).scalar_one_or_none() + result = self.session.execute(stmt) + if with_children: + result = result.unique() + project = result.scalar_one_or_none() if project is None: raise EntityNotFoundError(f"Project {project_id} not found") return project diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..5ad55cc --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,72 @@ +from __future__ import annotations + +from collections.abc import Callable, Iterator + +import pytest +from fastapi import FastAPI +from fastapi.testclient import TestClient +from sqlalchemy import create_engine +from sqlalchemy.engine import Engine +from sqlalchemy.orm import sessionmaker +from sqlalchemy.pool import StaticPool + +from config.database import Base +from dependencies import get_unit_of_work +from routes.dashboard import router as dashboard_router +from routes.projects import router as projects_router +from routes.scenarios import router as scenarios_router +from services.unit_of_work import UnitOfWork + + +@pytest.fixture() +def engine() -> Iterator[Engine]: + engine = create_engine( + "sqlite+pysqlite:///:memory:", + future=True, + connect_args={"check_same_thread": False}, + poolclass=StaticPool, + ) + Base.metadata.create_all(bind=engine) + try: + yield engine + finally: + Base.metadata.drop_all(bind=engine) + engine.dispose() + + +@pytest.fixture() +def session_factory(engine: Engine) -> Iterator[sessionmaker]: + testing_session = sessionmaker(bind=engine, expire_on_commit=False, future=True) + yield testing_session + + +@pytest.fixture() +def app(session_factory: sessionmaker) -> FastAPI: + application = FastAPI() + application.include_router(dashboard_router) + application.include_router(projects_router) + application.include_router(scenarios_router) + + def _override_uow() -> Iterator[UnitOfWork]: + with UnitOfWork(session_factory=session_factory) as uow: + yield uow + + application.dependency_overrides[get_unit_of_work] = _override_uow + return application + + +@pytest.fixture() +def client(app: FastAPI) -> Iterator[TestClient]: + test_client = TestClient(app) + try: + yield test_client + finally: + test_client.close() + + +@pytest.fixture() +def unit_of_work_factory(session_factory: sessionmaker) -> Callable[[], UnitOfWork]: + def _factory() -> UnitOfWork: + return UnitOfWork(session_factory=session_factory) + + return _factory diff --git a/tests/integration/README.md b/tests/integration/README.md new file mode 100644 index 0000000..33cf263 --- /dev/null +++ b/tests/integration/README.md @@ -0,0 +1,30 @@ +# Lifecycle Integration Test Plan + +## Coverage Goals + +- Exercise end-to-end creation, update, and deletion flows for projects through both API endpoints and HTML form submissions to ensure consistency across interfaces. +- Validate scenario lifecycle interactions (create, update, archive) including business rule enforcement and status transitions when performed via API and UI routes. +- Confirm that redirect chains and rendered templates match expected destinations after each lifecycle operation. +- Verify repository-backed statistics (counts, recency metadata) update appropriately after lifecycle actions to maintain dashboard accuracy. +- Guard against regressions in unit-of-work transaction handling by asserting database state after success and failure paths within integration flows. + +## Happy-Path Journeys + +1. **Project Management Flow** + + - Navigate to the project list UI and confirm empty-state messaging. + - Submit the new project form with valid data and verify redirect to the list page with the project present. + - Perform an API-based update to adjust project metadata and check the UI detail view reflects changes. + - Delete the project through the API and ensure the list UI reverts to the empty state. + +2. **Scenario Lifecycle Flow** + + - From an existing project, create a new scenario via the API and verify the project detail page renders the scenario entry. + - Update the scenario through the UI form, ensuring redirect to the scenario detail view with updated fields. + - Trigger a validation rule (e.g., duplicate scenario name within a project) to confirm error messaging without data loss. + - Archive the scenario using the API and confirm status badges and scenario counts update across dashboard and project detail views. + +3. **Dashboard Consistency Flow** + - Seed multiple projects and scenarios through combined API/UI interactions. + - Visit the dashboard and ensure metric cards reflect the latest counts, active/draft status breakdowns, and recent activity timestamps after each mutation. + - Run the lifecycle flows sequentially to confirm cumulative effects propagate to dashboard summaries and navigation badges. diff --git a/tests/integration/test_project_lifecycle.py b/tests/integration/test_project_lifecycle.py new file mode 100644 index 0000000..5092b65 --- /dev/null +++ b/tests/integration/test_project_lifecycle.py @@ -0,0 +1,66 @@ +from __future__ import annotations + +from fastapi.testclient import TestClient + + +class TestProjectLifecycle: + def test_project_create_update_delete_flow(self, client: TestClient) -> None: + # Initial state: no projects listed on the UI page + response = client.get("/projects/ui") + assert response.status_code == 200 + assert "No projects yet" in response.text + + # Create a project via the HTML form submission + create_payload = { + "name": "Lifecycle Mine", + "location": "Nevada", + "operation_type": "open_pit", + "description": "Initial description", + } + response = client.post( + "/projects/create", + data=create_payload, + follow_redirects=False, + ) + assert response.status_code == 303 + assert response.headers["Location"].endswith("/projects/ui") + + # Project should now appear on the list page + response = client.get("/projects/ui") + assert response.status_code == 200 + assert "Lifecycle Mine" in response.text + assert "Nevada" in response.text + + # Fetch the project via API to obtain its identifier + response = client.get("/projects") + assert response.status_code == 200 + projects = response.json() + assert len(projects) == 1 + project_id = projects[0]["id"] + + # Update the project using the API endpoint + update_payload = { + "location": "Arizona", + "description": "Updated description", + } + response = client.put(f"/projects/{project_id}", json=update_payload) + assert response.status_code == 200 + body = response.json() + assert body["location"] == "Arizona" + assert body["description"] == "Updated description" + + # Verify the UI detail page reflects the updates + response = client.get(f"/projects/{project_id}/view") + assert response.status_code == 200 + assert "Arizona" in response.text + assert "Updated description" in response.text + + # Delete the project using the API endpoint + response = client.delete(f"/projects/{project_id}") + assert response.status_code == 204 + + # Ensure the list view returns to the empty state + response = client.get("/projects/ui") + assert response.status_code == 200 + assert "No projects yet" in response.text + assert "Lifecycle Mine" not in response.text diff --git a/tests/integration/test_scenario_lifecycle.py b/tests/integration/test_scenario_lifecycle.py new file mode 100644 index 0000000..3315c8b --- /dev/null +++ b/tests/integration/test_scenario_lifecycle.py @@ -0,0 +1,106 @@ +from __future__ import annotations + +from fastapi.testclient import TestClient + + +class TestScenarioLifecycle: + def test_scenario_lifecycle_flow(self, client: TestClient) -> None: + # Create a project to attach scenarios to + project_response = client.post( + "/projects", + json={ + "name": "Scenario Host Project", + "location": "Ontario", + "operation_type": "open_pit", + "description": "Project for scenario lifecycle testing", + }, + ) + assert project_response.status_code == 201 + project_id = project_response.json()["id"] + + # Create a scenario via the API for the project + scenario_response = client.post( + f"/projects/{project_id}/scenarios", + json={ + "name": "Lifecycle Scenario", + "description": "Initial scenario description", + "status": "draft", + "currency": "usd", + "primary_resource": "diesel", + }, + ) + assert scenario_response.status_code == 201 + scenario_id = scenario_response.json()["id"] + + # Project detail page should list the new scenario in draft state + project_detail = client.get(f"/projects/{project_id}/view") + assert project_detail.status_code == 200 + assert "Lifecycle Scenario" in project_detail.text + assert "Draft" in project_detail.text + + # Update the scenario through the HTML form + form_response = client.post( + f"/scenarios/{scenario_id}/edit", + data={ + "name": "Lifecycle Scenario Revised", + "description": "Revised scenario assumptions", + "status_value": "active", + "start_date": "2025-01-01", + "end_date": "2025-12-31", + "discount_rate": "5.5", + "currency": "cad", + "primary_resource": "electricity", + }, + follow_redirects=False, + ) + assert form_response.status_code == 303 + assert form_response.headers["Location"].endswith( + f"/scenarios/{scenario_id}/view") + + # Scenario detail page should reflect the updated information + scenario_detail = client.get(f"/scenarios/{scenario_id}/view") + assert scenario_detail.status_code == 200 + assert "Lifecycle Scenario Revised" in scenario_detail.text + assert "Status: Active" in scenario_detail.text + assert "CAD" in scenario_detail.text + assert "Electricity" in scenario_detail.text + assert "Revised scenario assumptions" in scenario_detail.text + + # Project detail page should show the scenario as active with updated currency/resource + project_detail = client.get(f"/projects/{project_id}/view") + assert "Active" in project_detail.text + assert "CAD" in project_detail.text + assert "Electricity" in project_detail.text + + # Attempt to update the scenario with invalid currency to trigger validation error + invalid_update = client.put( + f"/scenarios/{scenario_id}", + json={"currency": "ca"}, + ) + assert invalid_update.status_code == 422 + assert ( + invalid_update.json()["detail"][0]["msg"] + == "Value error, Currency code must be a 3-letter ISO value" + ) + + # Scenario detail should still show the previous (valid) currency + scenario_detail = client.get(f"/scenarios/{scenario_id}/view") + assert "CAD" in scenario_detail.text + + # Archive the scenario through the API + archive_response = client.put( + f"/scenarios/{scenario_id}", + json={"status": "archived"}, + ) + assert archive_response.status_code == 200 + assert archive_response.json()["status"] == "archived" + + # Scenario detail reflects archived status + scenario_detail = client.get(f"/scenarios/{scenario_id}/view") + assert "Status: Archived" in scenario_detail.text + + # Project detail metrics and table entries reflect the archived state + project_detail = client.get(f"/projects/{project_id}/view") + assert "

Archived

" in project_detail.text + assert '

1

' in project_detail.text + assert "Archived" in project_detail.text diff --git a/tests/test_dashboard_route.py b/tests/test_dashboard_route.py index 02bab98..bae43c6 100644 --- a/tests/test_dashboard_route.py +++ b/tests/test_dashboard_route.py @@ -1,64 +1,6 @@ from __future__ import annotations -from collections.abc import Iterator - -import pytest -from fastapi import FastAPI from fastapi.testclient import TestClient -from sqlalchemy import create_engine -from sqlalchemy.engine import Engine -from sqlalchemy.orm import sessionmaker -from sqlalchemy.pool import StaticPool - -from config.database import Base -from dependencies import get_unit_of_work -from routes.dashboard import router as dashboard_router -from routes.projects import router as projects_router -from routes.scenarios import router as scenarios_router -from services.unit_of_work import UnitOfWork - - -@pytest.fixture() -def engine() -> Iterator[Engine]: - engine = create_engine( - "sqlite+pysqlite:///:memory:", - future=True, - connect_args={"check_same_thread": False}, - poolclass=StaticPool, - ) - Base.metadata.create_all(bind=engine) - try: - yield engine - finally: - Base.metadata.drop_all(bind=engine) - engine.dispose() - - -@pytest.fixture() -def session_factory(engine: Engine) -> Iterator[sessionmaker]: - testing_session = sessionmaker( - bind=engine, expire_on_commit=False, future=True) - yield testing_session - - -@pytest.fixture() -def client(session_factory: sessionmaker) -> Iterator[TestClient]: - app = FastAPI() - app.include_router(dashboard_router) - app.include_router(projects_router) - app.include_router(scenarios_router) - - def _override_uow() -> Iterator[UnitOfWork]: - with UnitOfWork(session_factory=session_factory) as uow: - yield uow - - app.dependency_overrides[get_unit_of_work] = _override_uow - - test_client = TestClient(app) - try: - yield test_client - finally: - test_client.close() class TestDashboardRoute: -- 2.39.5 From 53879a411f5134b9507c7b8f3a4be33d382914b8 Mon Sep 17 00:00:00 2001 From: zwitschi Date: Sun, 9 Nov 2025 21:45:29 +0100 Subject: [PATCH 020/109] feat: implement user and role models with password hashing, and add tests for user functionality --- models/__init__.py | 5 ++ models/user.py | 168 +++++++++++++++++++++++++++++++++++++++ requirements.txt | 1 + tests/test_user_model.py | 83 +++++++++++++++++++ 4 files changed, 257 insertions(+) create mode 100644 models/user.py create mode 100644 tests/test_user_model.py diff --git a/models/__init__.py b/models/__init__.py index 55e89ac..305ffb7 100644 --- a/models/__init__.py +++ b/models/__init__.py @@ -14,6 +14,7 @@ from .metadata import ( from .project import MiningOperationType, Project from .scenario import Scenario, ScenarioStatus from .simulation_parameter import DistributionType, SimulationParameter +from .user import Role, User, UserRole, password_context __all__ = [ "FinancialCategory", @@ -32,4 +33,8 @@ __all__ = [ "STOCHASTIC_VARIABLE_METADATA", "ResourceDescriptor", "StochasticVariableDescriptor", + "User", + "Role", + "UserRole", + "password_context", ] diff --git a/models/user.py b/models/user.py new file mode 100644 index 0000000..67db19e --- /dev/null +++ b/models/user.py @@ -0,0 +1,168 @@ +from __future__ import annotations + +from datetime import datetime +from typing import List, Optional + +from passlib.context import CryptContext +from sqlalchemy import ( + Boolean, + DateTime, + ForeignKey, + Integer, + String, + Text, + UniqueConstraint, +) +from sqlalchemy.orm import Mapped, mapped_column, relationship +from sqlalchemy.sql import func + +from config.database import Base + +# Configure password hashing strategy. Argon2 provides strong resistance against +# GPU-based cracking attempts, aligning with the security plan. +password_context = CryptContext(schemes=["argon2"], deprecated="auto") + + +class User(Base): + """Authenticated platform user with optional elevated privileges.""" + + __tablename__ = "users" + __table_args__ = ( + UniqueConstraint("email", name="uq_users_email"), + UniqueConstraint("username", name="uq_users_username"), + ) + + id: Mapped[int] = mapped_column(Integer, primary_key=True) + email: Mapped[str] = mapped_column(String(255), nullable=False) + username: Mapped[str] = mapped_column(String(128), nullable=False) + password_hash: Mapped[str] = mapped_column(String(255), nullable=False) + is_active: Mapped[bool] = mapped_column( + Boolean, nullable=False, default=True) + is_superuser: Mapped[bool] = mapped_column( + Boolean, nullable=False, default=False) + last_login_at: Mapped[datetime | None] = mapped_column( + DateTime(timezone=True), nullable=True + ) + created_at: Mapped[datetime] = mapped_column( + DateTime(timezone=True), nullable=False, server_default=func.now() + ) + updated_at: Mapped[datetime] = mapped_column( + DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now() + ) + + role_assignments: Mapped[List["UserRole"]] = relationship( + "UserRole", + back_populates="user", + cascade="all, delete-orphan", + foreign_keys="UserRole.user_id", + ) + roles: Mapped[List["Role"]] = relationship( + "Role", + secondary="user_roles", + primaryjoin="User.id == UserRole.user_id", + secondaryjoin="Role.id == UserRole.role_id", + viewonly=True, + back_populates="users", + ) + + def set_password(self, raw_password: str) -> None: + """Hash and store a password for the user.""" + + self.password_hash = self.hash_password(raw_password) + + @staticmethod + def hash_password(raw_password: str) -> str: + """Return the Argon2 hash for a clear-text password.""" + + return password_context.hash(raw_password) + + def verify_password(self, candidate_password: str) -> bool: + """Validate a password against the stored hash.""" + + if not self.password_hash: + return False + return password_context.verify(candidate_password, self.password_hash) + + def __repr__(self) -> str: # pragma: no cover - helpful for debugging + return f"User(id={self.id!r}, email={self.email!r})" + + +class Role(Base): + """Role encapsulating a set of permissions.""" + + __tablename__ = "roles" + + id: Mapped[int] = mapped_column(Integer, primary_key=True) + name: Mapped[str] = mapped_column(String(64), nullable=False, unique=True) + display_name: Mapped[str] = mapped_column(String(128), nullable=False) + description: Mapped[str | None] = mapped_column(Text, nullable=True) + created_at: Mapped[datetime] = mapped_column( + DateTime(timezone=True), nullable=False, server_default=func.now() + ) + updated_at: Mapped[datetime] = mapped_column( + DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now() + ) + + assignments: Mapped[List["UserRole"]] = relationship( + "UserRole", + back_populates="role", + cascade="all, delete-orphan", + foreign_keys="UserRole.role_id", + ) + users: Mapped[List["User"]] = relationship( + "User", + secondary="user_roles", + primaryjoin="Role.id == UserRole.role_id", + secondaryjoin="User.id == UserRole.user_id", + viewonly=True, + back_populates="roles", + ) + + def __repr__(self) -> str: # pragma: no cover - helpful for debugging + return f"Role(id={self.id!r}, name={self.name!r})" + + +class UserRole(Base): + """Association between users and roles with assignment metadata.""" + + __tablename__ = "user_roles" + __table_args__ = ( + UniqueConstraint("user_id", "role_id", name="uq_user_roles_user_role"), + ) + + user_id: Mapped[int] = mapped_column( + Integer, + ForeignKey("users.id", ondelete="CASCADE"), + primary_key=True, + ) + role_id: Mapped[int] = mapped_column( + Integer, + ForeignKey("roles.id", ondelete="CASCADE"), + primary_key=True, + ) + granted_at: Mapped[datetime] = mapped_column( + DateTime(timezone=True), nullable=False, server_default=func.now() + ) + granted_by: Mapped[Optional[int]] = mapped_column( + Integer, + ForeignKey("users.id", ondelete="SET NULL"), + nullable=True, + ) + + user: Mapped["User"] = relationship( + "User", + foreign_keys=[user_id], + back_populates="role_assignments", + ) + role: Mapped["Role"] = relationship( + "Role", + foreign_keys=[role_id], + back_populates="assignments", + ) + granted_by_user: Mapped[Optional["User"]] = relationship( + "User", + foreign_keys=[granted_by], + ) + + def __repr__(self) -> str: # pragma: no cover - debugging helper + return f"UserRole(user_id={self.user_id!r}, role_id={self.role_id!r})" diff --git a/requirements.txt b/requirements.txt index 1ae5423..7227da4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,5 +9,6 @@ jinja2 pandas numpy passlib +argon2-cffi python-jose python-multipart \ No newline at end of file diff --git a/tests/test_user_model.py b/tests/test_user_model.py new file mode 100644 index 0000000..b03552c --- /dev/null +++ b/tests/test_user_model.py @@ -0,0 +1,83 @@ +from __future__ import annotations + +from collections.abc import Iterator + +import pytest +from sqlalchemy import create_engine +from sqlalchemy.orm import Session, sessionmaker + +from config.database import Base +from models import Role, User, UserRole + + +@pytest.fixture() +def engine() -> Iterator: + engine = create_engine("sqlite:///:memory:", future=True) + Base.metadata.create_all(bind=engine) + try: + yield engine + finally: + Base.metadata.drop_all(bind=engine) + + +@pytest.fixture() +def session(engine) -> Iterator[Session]: + TestingSession = sessionmaker( + bind=engine, expire_on_commit=False, future=True) + session = TestingSession() + try: + yield session + finally: + session.close() + + +def test_user_password_helpers() -> None: + user = User( + email="user@example.com", + username="example", + password_hash=User.hash_password("initial"), + ) + + user.set_password("new-secret") + + assert user.password_hash != "new-secret" + assert user.verify_password("new-secret") + assert not user.verify_password("wrong") + + +def test_user_role_assignment(session: Session) -> None: + grantor = User( + email="admin@example.com", + username="admin", + password_hash=User.hash_password("admin-secret"), + is_superuser=True, + ) + analyst_role = Role( + name="analyst", + display_name="Analyst", + description="Can review project dashboards", + ) + analyst = User( + email="analyst@example.com", + username="analyst", + password_hash=User.hash_password("analyst-secret"), + ) + + assignment = UserRole(user=analyst, role=analyst_role, + granted_by_user=grantor) + + session.add_all([grantor, analyst_role, analyst, assignment]) + session.commit() + + session.refresh(analyst) + session.refresh(analyst_role) + + # Relationship wrapper exposes the role without needing to traverse assignments manually + assert len(analyst.role_assignments) == 1 + assert analyst.role_assignments[0].granted_by_user is grantor + assert len(analyst.roles) == 1 + assert analyst.roles[0].name == "analyst" + + # Ensure reverse relationship exposes the user + assert len(analyst_role.assignments) == 1 + assert analyst_role.users[0].username == "analyst" -- 2.39.5 From 3601c2e422fab74c96d32653b30fb002a97ac333 Mon Sep 17 00:00:00 2001 From: zwitschi Date: Sun, 9 Nov 2025 21:48:35 +0100 Subject: [PATCH 021/109] feat: Implement user and role management with repositories - Added RoleRepository and UserRepository for managing roles and users. - Implemented methods for creating, retrieving, and assigning roles to users. - Introduced functions to ensure default roles and an admin user exist in the system. - Updated UnitOfWork to include user and role repositories. - Created new security module for password hashing and JWT token management. - Added tests for authentication flows, including registration, login, and password reset. - Enhanced HTML templates for user registration, login, and password management with error handling. - Added a logo image to the static assets. --- .../versions/20251109_02_add_auth_tables.py | 210 ++++++++ alembic_test.db | Bin 0 -> 106496 bytes changelog.md | 2 + config/settings.py | 60 +++ dependencies.py | 14 + main.py | 2 + models/user.py | 8 + routes/auth.py | 473 ++++++++++++++++++ schemas/auth.py | 67 +++ services/repositories.py | 228 ++++++++- services/security.py | 213 ++++++++ services/unit_of_work.py | 45 +- static/img/logo_big.png | Bin 0 -> 1894952 bytes templates/forgot_password.html | 36 +- templates/login.html | 50 +- templates/partials/sidebar_nav.html | 126 ++--- templates/register.html | 62 ++- templates/reset_password.html | 36 ++ tests/conftest.py | 5 +- tests/test_auth_repositories.py | 135 +++++ tests/test_auth_routes.py | 239 +++++++++ tests/test_security.py | 76 +++ 22 files changed, 1955 insertions(+), 132 deletions(-) create mode 100644 alembic/versions/20251109_02_add_auth_tables.py create mode 100644 alembic_test.db create mode 100644 config/settings.py create mode 100644 routes/auth.py create mode 100644 schemas/auth.py create mode 100644 services/security.py create mode 100644 static/img/logo_big.png create mode 100644 templates/reset_password.html create mode 100644 tests/test_auth_repositories.py create mode 100644 tests/test_auth_routes.py create mode 100644 tests/test_security.py diff --git a/alembic/versions/20251109_02_add_auth_tables.py b/alembic/versions/20251109_02_add_auth_tables.py new file mode 100644 index 0000000..c669bd2 --- /dev/null +++ b/alembic/versions/20251109_02_add_auth_tables.py @@ -0,0 +1,210 @@ +"""Add authentication and RBAC tables""" + +from __future__ import annotations + +from alembic import op +import sqlalchemy as sa +from passlib.context import CryptContext +from sqlalchemy.sql import column, table + +# revision identifiers, used by Alembic. +revision = "20251109_02" +down_revision = "20251109_01" +branch_labels = None +depends_on = None + +password_context = CryptContext(schemes=["argon2"], deprecated="auto") + + +def upgrade() -> None: + op.create_table( + "users", + sa.Column("id", sa.Integer(), primary_key=True), + sa.Column("email", sa.String(length=255), nullable=False), + sa.Column("username", sa.String(length=128), nullable=False), + sa.Column("password_hash", sa.String(length=255), nullable=False), + sa.Column( + "is_active", + sa.Boolean(), + nullable=False, + server_default=sa.true(), + ), + sa.Column( + "is_superuser", + sa.Boolean(), + nullable=False, + server_default=sa.false(), + ), + sa.Column("last_login_at", sa.DateTime(timezone=True), nullable=True), + sa.Column( + "created_at", + sa.DateTime(timezone=True), + nullable=False, + server_default=sa.func.now(), + ), + sa.Column( + "updated_at", + sa.DateTime(timezone=True), + nullable=False, + server_default=sa.func.now(), + ), + sa.UniqueConstraint("email", name="uq_users_email"), + sa.UniqueConstraint("username", name="uq_users_username"), + ) + op.create_index( + "ix_users_active_superuser", + "users", + ["is_active", "is_superuser"], + unique=False, + ) + + op.create_table( + "roles", + sa.Column("id", sa.Integer(), primary_key=True), + sa.Column("name", sa.String(length=64), nullable=False), + sa.Column("display_name", sa.String(length=128), nullable=False), + sa.Column("description", sa.Text(), nullable=True), + sa.Column( + "created_at", + sa.DateTime(timezone=True), + nullable=False, + server_default=sa.func.now(), + ), + sa.Column( + "updated_at", + sa.DateTime(timezone=True), + nullable=False, + server_default=sa.func.now(), + ), + sa.UniqueConstraint("name", name="uq_roles_name"), + ) + + op.create_table( + "user_roles", + sa.Column("user_id", sa.Integer(), nullable=False), + sa.Column("role_id", sa.Integer(), nullable=False), + sa.Column( + "granted_at", + sa.DateTime(timezone=True), + nullable=False, + server_default=sa.func.now(), + ), + sa.Column("granted_by", sa.Integer(), nullable=True), + sa.ForeignKeyConstraint( + ["user_id"], + ["users.id"], + ondelete="CASCADE", + ), + sa.ForeignKeyConstraint( + ["role_id"], + ["roles.id"], + ondelete="CASCADE", + ), + sa.ForeignKeyConstraint( + ["granted_by"], + ["users.id"], + ondelete="SET NULL", + ), + sa.PrimaryKeyConstraint("user_id", "role_id"), + sa.UniqueConstraint("user_id", "role_id", + name="uq_user_roles_user_role"), + ) + op.create_index( + "ix_user_roles_role_id", + "user_roles", + ["role_id"], + unique=False, + ) + + # Seed default roles + roles_table = table( + "roles", + column("id", sa.Integer()), + column("name", sa.String()), + column("display_name", sa.String()), + column("description", sa.Text()), + ) + + op.bulk_insert( + roles_table, + [ + { + "id": 1, + "name": "admin", + "display_name": "Administrator", + "description": "Full platform access with user management rights.", + }, + { + "id": 2, + "name": "project_manager", + "display_name": "Project Manager", + "description": "Manage projects, scenarios, and associated data.", + }, + { + "id": 3, + "name": "analyst", + "display_name": "Analyst", + "description": "Review dashboards and scenario outputs.", + }, + { + "id": 4, + "name": "viewer", + "display_name": "Viewer", + "description": "Read-only access to assigned projects and reports.", + }, + ], + ) + + admin_password_hash = password_context.hash("ChangeMe123!") + + users_table = table( + "users", + column("id", sa.Integer()), + column("email", sa.String()), + column("username", sa.String()), + column("password_hash", sa.String()), + column("is_active", sa.Boolean()), + column("is_superuser", sa.Boolean()), + ) + + op.bulk_insert( + users_table, + [ + { + "id": 1, + "email": "admin@calminer.local", + "username": "admin", + "password_hash": admin_password_hash, + "is_active": True, + "is_superuser": True, + } + ], + ) + + user_roles_table = table( + "user_roles", + column("user_id", sa.Integer()), + column("role_id", sa.Integer()), + column("granted_by", sa.Integer()), + ) + + op.bulk_insert( + user_roles_table, + [ + { + "user_id": 1, + "role_id": 1, + "granted_by": 1, + } + ], + ) + + +def downgrade() -> None: + op.drop_index("ix_user_roles_role_id", table_name="user_roles") + op.drop_table("user_roles") + + op.drop_table("roles") + + op.drop_index("ix_users_active_superuser", table_name="users") + op.drop_table("users") diff --git a/alembic_test.db b/alembic_test.db new file mode 100644 index 0000000000000000000000000000000000000000..7c40e15365cb32ec3ff6bdce369fb8bd5538b32a GIT binary patch literal 106496 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 "Settings": + """Construct settings from environment variables.""" + + return cls( + jwt_secret_key=os.getenv("CALMINER_JWT_SECRET", "change-me"), + jwt_algorithm=os.getenv("CALMINER_JWT_ALGORITHM", "HS256"), + jwt_access_token_minutes=cls._int_from_env( + "CALMINER_JWT_ACCESS_MINUTES", 15 + ), + jwt_refresh_token_days=cls._int_from_env( + "CALMINER_JWT_REFRESH_DAYS", 7 + ), + ) + + @staticmethod + def _int_from_env(name: str, default: int) -> int: + raw_value = os.getenv(name) + if raw_value is None: + return default + try: + return int(raw_value) + except ValueError: + return default + + def jwt_settings(self) -> JWTSettings: + """Build runtime JWT settings compatible with token helpers.""" + + return JWTSettings( + secret_key=self.jwt_secret_key, + algorithm=self.jwt_algorithm, + access_token_ttl=timedelta(minutes=self.jwt_access_token_minutes), + refresh_token_ttl=timedelta(days=self.jwt_refresh_token_days), + ) + + +@lru_cache(maxsize=1) +def get_settings() -> Settings: + """Return cached application settings.""" + + return Settings.from_environment() diff --git a/dependencies.py b/dependencies.py index e492586..b2b0774 100644 --- a/dependencies.py +++ b/dependencies.py @@ -2,6 +2,8 @@ from __future__ import annotations from collections.abc import Generator +from config.settings import Settings, get_settings +from services.security import JWTSettings from services.unit_of_work import UnitOfWork @@ -10,3 +12,15 @@ def get_unit_of_work() -> Generator[UnitOfWork, None, None]: with UnitOfWork() as uow: yield uow + + +def get_application_settings() -> Settings: + """Provide cached application settings instance.""" + + return get_settings() + + +def get_jwt_settings() -> JWTSettings: + """Provide JWT runtime configuration derived from settings.""" + + return get_settings().jwt_settings() diff --git a/main.py b/main.py index 000a0e0..456aa5a 100644 --- a/main.py +++ b/main.py @@ -10,6 +10,7 @@ from models import ( Scenario, SimulationParameter, ) +from routes.auth import router as auth_router from routes.dashboard import router as dashboard_router from routes.projects import router as projects_router from routes.scenarios import router as scenarios_router @@ -33,6 +34,7 @@ async def health() -> dict[str, str]: app.include_router(dashboard_router) +app.include_router(auth_router) app.include_router(projects_router) app.include_router(scenarios_router) diff --git a/models/user.py b/models/user.py index 67db19e..580c705 100644 --- a/models/user.py +++ b/models/user.py @@ -4,6 +4,14 @@ from datetime import datetime from typing import List, Optional from passlib.context import CryptContext + +try: # pragma: no cover - defensive compatibility shim + import importlib.metadata as importlib_metadata + import argon2 # type: ignore + + setattr(argon2, "__version__", importlib_metadata.version("argon2-cffi")) +except Exception: + pass from sqlalchemy import ( Boolean, DateTime, diff --git a/routes/auth.py b/routes/auth.py new file mode 100644 index 0000000..71a8752 --- /dev/null +++ b/routes/auth.py @@ -0,0 +1,473 @@ +from __future__ import annotations + +from datetime import datetime, timedelta, timezone +from typing import Any, Iterable + +from fastapi import APIRouter, Depends, HTTPException, Request, UploadFile, status +from fastapi.responses import HTMLResponse, RedirectResponse +from fastapi.templating import Jinja2Templates +from pydantic import ValidationError +from starlette.datastructures import FormData + +from dependencies import get_jwt_settings, get_unit_of_work +from models import Role, User +from schemas.auth import ( + LoginForm, + PasswordResetForm, + PasswordResetRequestForm, + RegistrationForm, +) +from services.exceptions import EntityConflictError +from services.security import ( + JWTSettings, + TokenDecodeError, + TokenExpiredError, + TokenTypeMismatchError, + create_access_token, + create_refresh_token, + decode_access_token, + hash_password, + verify_password, +) +from services.repositories import RoleRepository, UserRepository +from services.unit_of_work import UnitOfWork + +router = APIRouter(tags=["Authentication"]) +templates = Jinja2Templates(directory="templates") + +_PASSWORD_RESET_SCOPE = "password-reset" +_AUTH_SCOPE = "auth" + + +def _template( + request: Request, + template_name: str, + context: dict[str, Any], + *, + status_code: int = status.HTTP_200_OK, +) -> HTMLResponse: + return templates.TemplateResponse( + request, + template_name, + context, + status_code=status_code, + ) + + +def _validation_errors(exc: ValidationError) -> list[str]: + return [error.get("msg", "Invalid input.") for error in exc.errors()] + + +def _scopes(include: Iterable[str]) -> list[str]: + return list(include) + + +def _normalise_form_data(form_data: FormData) -> dict[str, str]: + normalised: dict[str, str] = {} + for key, value in form_data.multi_items(): + if isinstance(value, UploadFile): + str_value = value.filename or "" + else: + str_value = str(value) + normalised[key] = str_value + return normalised + + +def _require_users_repo(uow: UnitOfWork) -> UserRepository: + if not uow.users: + raise RuntimeError("User repository is not initialised") + return uow.users + + +def _require_roles_repo(uow: UnitOfWork) -> RoleRepository: + if not uow.roles: + raise RuntimeError("Role repository is not initialised") + return uow.roles + + +@router.get("/login", response_class=HTMLResponse, include_in_schema=False, name="auth.login_form") +def login_form(request: Request) -> HTMLResponse: + return _template( + request, + "login.html", + { + "form_action": request.url_for("auth.login_submit"), + "errors": [], + "username": "", + }, + ) + + +@router.post("/login", include_in_schema=False, name="auth.login_submit") +async def login_submit( + request: Request, + uow: UnitOfWork = Depends(get_unit_of_work), + jwt_settings: JWTSettings = Depends(get_jwt_settings), +): + form_data = _normalise_form_data(await request.form()) + try: + form = LoginForm(**form_data) + except ValidationError as exc: + return _template( + request, + "login.html", + { + "form_action": request.url_for("auth.login_submit"), + "errors": _validation_errors(exc), + }, + status_code=status.HTTP_400_BAD_REQUEST, + ) + + identifier = form.username + users_repo = _require_users_repo(uow) + user = _lookup_user(users_repo, identifier) + errors: list[str] = [] + + if not user or not verify_password(form.password, user.password_hash): + errors.append("Invalid username or password.") + elif not user.is_active: + errors.append("Account is inactive. Contact an administrator.") + + if errors: + return _template( + request, + "login.html", + { + "form_action": request.url_for("auth.login_submit"), + "errors": errors, + "username": identifier, + }, + status_code=status.HTTP_400_BAD_REQUEST, + ) + + assert user is not None # mypy hint - guarded above + user.last_login_at = datetime.now(timezone.utc) + + access_token = create_access_token( + str(user.id), + jwt_settings, + scopes=_scopes((_AUTH_SCOPE,)), + ) + refresh_token = create_refresh_token( + str(user.id), + jwt_settings, + scopes=_scopes((_AUTH_SCOPE,)), + ) + + response = RedirectResponse( + request.url_for("dashboard.home"), + status_code=status.HTTP_303_SEE_OTHER, + ) + _set_auth_cookies(response, access_token, refresh_token, jwt_settings) + return response + + +def _lookup_user(users_repo: UserRepository, identifier: str) -> User | None: + if "@" in identifier: + return users_repo.get_by_email(identifier.lower(), with_roles=True) + return users_repo.get_by_username(identifier, with_roles=True) + + +def _set_auth_cookies( + response: RedirectResponse, + access_token: str, + refresh_token: str, + jwt_settings: JWTSettings, +) -> None: + access_ttl = int(jwt_settings.access_token_ttl.total_seconds()) + refresh_ttl = int(jwt_settings.refresh_token_ttl.total_seconds()) + response.set_cookie( + "calminer_access_token", + access_token, + httponly=True, + secure=False, + samesite="lax", + max_age=max(access_ttl, 0) or None, + ) + response.set_cookie( + "calminer_refresh_token", + refresh_token, + httponly=True, + secure=False, + samesite="lax", + max_age=max(refresh_ttl, 0) or None, + ) + + +@router.get("/register", response_class=HTMLResponse, include_in_schema=False, name="auth.register_form") +def register_form(request: Request) -> HTMLResponse: + return _template( + request, + "register.html", + { + "form_action": request.url_for("auth.register_submit"), + "errors": [], + "form_data": None, + }, + ) + + +@router.post("/register", include_in_schema=False, name="auth.register_submit") +async def register_submit( + request: Request, + uow: UnitOfWork = Depends(get_unit_of_work), +): + form_data = _normalise_form_data(await request.form()) + try: + form = RegistrationForm(**form_data) + except ValidationError as exc: + return _registration_error_response(request, _validation_errors(exc)) + + errors: list[str] = [] + users_repo = _require_users_repo(uow) + roles_repo = _require_roles_repo(uow) + uow.ensure_default_roles() + + if users_repo.get_by_email(form.email): + errors.append("Email is already registered.") + if users_repo.get_by_username(form.username): + errors.append("Username is already taken.") + + if errors: + return _registration_error_response(request, errors, form) + + user = User( + email=form.email, + username=form.username, + password_hash=hash_password(form.password), + is_active=True, + is_superuser=False, + ) + + try: + created = users_repo.create(user) + except EntityConflictError: + return _registration_error_response( + request, + ["An account with this username or email already exists."], + form, + ) + + viewer_role = _ensure_viewer_role(roles_repo) + if viewer_role is not None: + users_repo.assign_role( + user_id=created.id, + role_id=viewer_role.id, + granted_by=created.id, + ) + + redirect_url = request.url_for( + "auth.login_form").include_query_params(registered="1") + return RedirectResponse( + redirect_url, + status_code=status.HTTP_303_SEE_OTHER, + ) + + +def _registration_error_response( + request: Request, + errors: list[str], + form: RegistrationForm | None = None, +) -> HTMLResponse: + context = { + "form_action": request.url_for("auth.register_submit"), + "errors": errors, + "form_data": form.model_dump(exclude={"password", "confirm_password"}) if form else None, + } + return _template( + request, + "register.html", + context, + status_code=status.HTTP_400_BAD_REQUEST, + ) + + +def _ensure_viewer_role(roles_repo: RoleRepository) -> Role | None: + viewer = roles_repo.get_by_name("viewer") + if viewer: + return viewer + return roles_repo.get_by_name("viewer") + + +@router.get( + "/forgot-password", + response_class=HTMLResponse, + include_in_schema=False, + name="auth.password_reset_request_form", +) +def password_reset_request_form(request: Request) -> HTMLResponse: + return _template( + request, + "forgot_password.html", + { + "form_action": request.url_for("auth.password_reset_request_submit"), + "errors": [], + "message": None, + }, + ) + + +@router.post( + "/forgot-password", + include_in_schema=False, + name="auth.password_reset_request_submit", +) +async def password_reset_request_submit( + request: Request, + uow: UnitOfWork = Depends(get_unit_of_work), + jwt_settings: JWTSettings = Depends(get_jwt_settings), +): + form_data = _normalise_form_data(await request.form()) + try: + form = PasswordResetRequestForm(**form_data) + except ValidationError as exc: + return _template( + request, + "forgot_password.html", + { + "form_action": request.url_for("auth.password_reset_request_submit"), + "errors": _validation_errors(exc), + "message": None, + }, + status_code=status.HTTP_400_BAD_REQUEST, + ) + + users_repo = _require_users_repo(uow) + user = users_repo.get_by_email(form.email) + if not user: + return _template( + request, + "forgot_password.html", + { + "form_action": request.url_for("auth.password_reset_request_submit"), + "errors": [], + "message": "If an account exists, a reset link has been sent.", + }, + ) + + token = create_access_token( + str(user.id), + jwt_settings, + scopes=_scopes((_PASSWORD_RESET_SCOPE,)), + expires_delta=timedelta(hours=1), + ) + + reset_url = request.url_for( + "auth.password_reset_form").include_query_params(token=token) + return RedirectResponse(reset_url, status_code=status.HTTP_303_SEE_OTHER) + + +@router.get( + "/reset-password", + response_class=HTMLResponse, + include_in_schema=False, + name="auth.password_reset_form", +) +def password_reset_form( + request: Request, + token: str | None = None, + jwt_settings: JWTSettings = Depends(get_jwt_settings), +) -> HTMLResponse: + errors: list[str] = [] + if not token: + errors.append("Missing password reset token.") + else: + try: + payload = decode_access_token(token, jwt_settings) + if _PASSWORD_RESET_SCOPE not in payload.scopes: + errors.append("Invalid token scope.") + except TokenExpiredError: + errors.append( + "Token has expired. Please request a new password reset.") + except (TokenDecodeError, TokenTypeMismatchError): + errors.append("Invalid password reset token.") + + return _template( + request, + "reset_password.html", + { + "form_action": request.url_for("auth.password_reset_submit"), + "token": token, + "errors": errors, + }, + status_code=status.HTTP_400_BAD_REQUEST if errors else status.HTTP_200_OK, + ) + + +@router.post( + "/reset-password", + include_in_schema=False, + name="auth.password_reset_submit", +) +async def password_reset_submit( + request: Request, + uow: UnitOfWork = Depends(get_unit_of_work), + jwt_settings: JWTSettings = Depends(get_jwt_settings), +): + form_data = _normalise_form_data(await request.form()) + try: + form = PasswordResetForm(**form_data) + except ValidationError as exc: + return _template( + request, + "reset_password.html", + { + "form_action": request.url_for("auth.password_reset_submit"), + "token": form_data.get("token"), + "errors": _validation_errors(exc), + }, + status_code=status.HTTP_400_BAD_REQUEST, + ) + + try: + payload = decode_access_token(form.token, jwt_settings) + except TokenExpiredError: + return _reset_error_response( + request, + form.token, + "Token has expired. Please request a new password reset.", + ) + except (TokenDecodeError, TokenTypeMismatchError): + return _reset_error_response( + request, + form.token, + "Invalid password reset token.", + ) + + if _PASSWORD_RESET_SCOPE not in payload.scopes: + return _reset_error_response( + request, + form.token, + "Invalid password reset token scope.", + ) + + users_repo = _require_users_repo(uow) + user_id = int(payload.sub) + user = users_repo.get(user_id) + if not user: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="User not found") + + user.set_password(form.password) + if not user.is_active: + user.is_active = True + + redirect_url = request.url_for( + "auth.login_form").include_query_params(reset="1") + return RedirectResponse( + redirect_url, + status_code=status.HTTP_303_SEE_OTHER, + ) + + +def _reset_error_response(request: Request, token: str, message: str) -> HTMLResponse: + return _template( + request, + "reset_password.html", + { + "form_action": request.url_for("auth.password_reset_submit"), + "token": token, + "errors": [message], + }, + status_code=status.HTTP_400_BAD_REQUEST, + ) diff --git a/schemas/auth.py b/schemas/auth.py new file mode 100644 index 0000000..3a16191 --- /dev/null +++ b/schemas/auth.py @@ -0,0 +1,67 @@ +from __future__ import annotations + +from pydantic import BaseModel, ConfigDict, Field, ValidationInfo, field_validator + + +class FormModel(BaseModel): + """Base Pydantic model for HTML form submissions.""" + + model_config = ConfigDict(extra="forbid", str_strip_whitespace=True) + + +class RegistrationForm(FormModel): + username: str = Field(min_length=3, max_length=128) + email: str = Field(min_length=5, max_length=255) + password: str = Field(min_length=8, max_length=256) + confirm_password: str + + @field_validator("email") + @classmethod + def validate_email(cls, value: str) -> str: + if "@" not in value or value.startswith("@") or value.endswith("@"): + raise ValueError("Invalid email address.") + local, domain = value.split("@", 1) + if not local or "." not in domain: + raise ValueError("Invalid email address.") + return value.lower() + + @field_validator("confirm_password") + @classmethod + def passwords_match(cls, value: str, info: ValidationInfo) -> str: + password = info.data.get("password") + if password != value: + raise ValueError("Passwords do not match.") + return value + + +class LoginForm(FormModel): + username: str = Field(min_length=1, max_length=255) + password: str = Field(min_length=1, max_length=256) + + +class PasswordResetRequestForm(FormModel): + email: str = Field(min_length=5, max_length=255) + + @field_validator("email") + @classmethod + def validate_email(cls, value: str) -> str: + if "@" not in value or value.startswith("@") or value.endswith("@"): + raise ValueError("Invalid email address.") + local, domain = value.split("@", 1) + if not local or "." not in domain: + raise ValueError("Invalid email address.") + return value.lower() + + +class PasswordResetForm(FormModel): + token: str = Field(min_length=1) + password: str = Field(min_length=8, max_length=256) + confirm_password: str + + @field_validator("confirm_password") + @classmethod + def reset_passwords_match(cls, value: str, info: ValidationInfo) -> str: + password = info.data.get("password") + if password != value: + raise ValueError("Passwords do not match.") + return value diff --git a/services/repositories.py b/services/repositories.py index 5556638..1f82691 100644 --- a/services/repositories.py +++ b/services/repositories.py @@ -8,7 +8,16 @@ from sqlalchemy import select, func from sqlalchemy.exc import IntegrityError from sqlalchemy.orm import Session, joinedload, selectinload -from models import FinancialInput, Project, Scenario, ScenarioStatus, SimulationParameter +from models import ( + FinancialInput, + Project, + Role, + Scenario, + ScenarioStatus, + SimulationParameter, + User, + UserRole, +) from services.exceptions import EntityConflictError, EntityNotFoundError @@ -211,3 +220,220 @@ class SimulationParameterRepository: raise EntityNotFoundError( f"Simulation parameter {parameter_id} not found") self.session.delete(entity) + + +class RoleRepository: + """Persistence operations for Role entities.""" + + def __init__(self, session: Session) -> None: + self.session = session + + def list(self) -> Sequence[Role]: + stmt = select(Role).order_by(Role.name) + return self.session.execute(stmt).scalars().all() + + def get(self, role_id: int) -> Role: + stmt = select(Role).where(Role.id == role_id) + role = self.session.execute(stmt).scalar_one_or_none() + if role is None: + raise EntityNotFoundError(f"Role {role_id} not found") + return role + + def get_by_name(self, name: str) -> Role | None: + stmt = select(Role).where(Role.name == name) + return self.session.execute(stmt).scalar_one_or_none() + + def create(self, role: Role) -> Role: + self.session.add(role) + try: + self.session.flush() + except IntegrityError as exc: # pragma: no cover - DB constraint enforcement + raise EntityConflictError( + "Role violates uniqueness constraints") from exc + return role + + +class UserRepository: + """Persistence operations for User entities and their role assignments.""" + + def __init__(self, session: Session) -> None: + self.session = session + + def list(self, *, with_roles: bool = False) -> Sequence[User]: + stmt = select(User).order_by(User.created_at) + if with_roles: + stmt = stmt.options(selectinload(User.roles)) + return self.session.execute(stmt).scalars().all() + + def _apply_role_option(self, stmt, with_roles: bool): + if with_roles: + stmt = stmt.options( + joinedload(User.role_assignments).joinedload(UserRole.role), + selectinload(User.roles), + ) + return stmt + + def get(self, user_id: int, *, with_roles: bool = False) -> User: + stmt = select(User).where(User.id == user_id).execution_options( + populate_existing=True) + stmt = self._apply_role_option(stmt, with_roles) + result = self.session.execute(stmt) + if with_roles: + result = result.unique() + user = result.scalar_one_or_none() + if user is None: + raise EntityNotFoundError(f"User {user_id} not found") + return user + + def get_by_email(self, email: str, *, with_roles: bool = False) -> User | None: + stmt = select(User).where(User.email == email).execution_options( + populate_existing=True) + stmt = self._apply_role_option(stmt, with_roles) + result = self.session.execute(stmt) + if with_roles: + result = result.unique() + return result.scalar_one_or_none() + + def get_by_username(self, username: str, *, with_roles: bool = False) -> User | None: + stmt = select(User).where(User.username == + username).execution_options(populate_existing=True) + stmt = self._apply_role_option(stmt, with_roles) + result = self.session.execute(stmt) + if with_roles: + result = result.unique() + return result.scalar_one_or_none() + + def create(self, user: User) -> User: + self.session.add(user) + try: + self.session.flush() + except IntegrityError as exc: # pragma: no cover - DB constraint enforcement + raise EntityConflictError( + "User violates uniqueness constraints") from exc + return user + + def assign_role( + self, + *, + user_id: int, + role_id: int, + granted_by: int | None = None, + ) -> UserRole: + stmt = select(UserRole).where( + UserRole.user_id == user_id, + UserRole.role_id == role_id, + ) + assignment = self.session.execute(stmt).scalar_one_or_none() + if assignment: + return assignment + + assignment = UserRole( + user_id=user_id, + role_id=role_id, + granted_by=granted_by, + ) + self.session.add(assignment) + try: + self.session.flush() + except IntegrityError as exc: # pragma: no cover - DB constraint enforcement + raise EntityConflictError( + "Assignment violates constraints") from exc + return assignment + + def revoke_role(self, *, user_id: int, role_id: int) -> None: + stmt = select(UserRole).where( + UserRole.user_id == user_id, + UserRole.role_id == role_id, + ) + assignment = self.session.execute(stmt).scalar_one_or_none() + if assignment is None: + raise EntityNotFoundError( + f"Role {role_id} not assigned to user {user_id}") + self.session.delete(assignment) + self.session.flush() + + +DEFAULT_ROLE_DEFINITIONS: tuple[dict[str, str], ...] = ( + { + "name": "admin", + "display_name": "Administrator", + "description": "Full platform access with user management rights.", + }, + { + "name": "project_manager", + "display_name": "Project Manager", + "description": "Manage projects, scenarios, and associated data.", + }, + { + "name": "analyst", + "display_name": "Analyst", + "description": "Review dashboards and scenario outputs.", + }, + { + "name": "viewer", + "display_name": "Viewer", + "description": "Read-only access to assigned projects and reports.", + }, +) + + +def ensure_default_roles(role_repo: RoleRepository) -> list[Role]: + """Ensure standard roles exist, creating missing ones. + + Returns all current role records in creation order. + """ + + roles: list[Role] = [] + for definition in DEFAULT_ROLE_DEFINITIONS: + existing = role_repo.get_by_name(definition["name"]) + if existing: + roles.append(existing) + continue + role = Role(**definition) + roles.append(role_repo.create(role)) + return roles + + +def ensure_admin_user( + user_repo: UserRepository, + role_repo: RoleRepository, + *, + email: str, + username: str, + password: str, +) -> User: + """Ensure an administrator user exists and holds the admin role.""" + + user = user_repo.get_by_email(email, with_roles=True) + if user is None: + user = User( + email=email, + username=username, + password_hash=User.hash_password(password), + is_active=True, + is_superuser=True, + ) + user_repo.create(user) + else: + if not user.is_active: + user.is_active = True + if not user.is_superuser: + user.is_superuser = True + user_repo.session.flush() + + admin_role = role_repo.get_by_name("admin") + if admin_role is None: # pragma: no cover - safety if ensure_default_roles wasn't called + admin_role = role_repo.create( + Role( + name="admin", + display_name="Administrator", + description="Full platform access with user management rights.", + ) + ) + + user_repo.assign_role( + user_id=user.id, + role_id=admin_role.id, + granted_by=user.id, + ) + return user diff --git a/services/security.py b/services/security.py new file mode 100644 index 0000000..02a078d --- /dev/null +++ b/services/security.py @@ -0,0 +1,213 @@ +from __future__ import annotations + +from dataclasses import dataclass, field +from datetime import datetime, timedelta, timezone +from typing import Any, Dict, Iterable, Literal, Type + +from jose import ExpiredSignatureError, JWTError, jwt +from passlib.context import CryptContext + +try: # pragma: no cover - compatibility shim for passlib/argon2 warning + import importlib.metadata as importlib_metadata + import argon2 # type: ignore + + setattr(argon2, "__version__", importlib_metadata.version("argon2-cffi")) +except Exception: # pragma: no cover - executed only when metadata lookup fails + pass + +from pydantic import BaseModel, Field, ValidationError + +password_context = CryptContext(schemes=["argon2"], deprecated="auto") + + +def hash_password(password: str) -> str: + """Derive a secure hash for a plain-text password.""" + + return password_context.hash(password) + + +def verify_password(candidate: str, hashed: str) -> bool: + """Verify that a candidate password matches a stored hash.""" + + try: + return password_context.verify(candidate, hashed) + except ValueError: + # Raised when the stored hash is malformed or uses an unknown scheme. + return False + + +class TokenError(Exception): + """Base class for token encoding/decoding issues.""" + + +class TokenDecodeError(TokenError): + """Raised when a token cannot be decoded or validated.""" + + +class TokenExpiredError(TokenError): + """Raised when a token has expired.""" + + +class TokenTypeMismatchError(TokenError): + """Raised when a token type does not match the expected flavour.""" + + +TokenKind = Literal["access", "refresh"] + + +class TokenPayload(BaseModel): + """Shared fields for CalMiner JWT payloads.""" + + sub: str + exp: int + type: TokenKind + scopes: list[str] = Field(default_factory=list) + + @property + def expires_at(self) -> datetime: + return datetime.fromtimestamp(self.exp, tz=timezone.utc) + + +@dataclass(slots=True) +class JWTSettings: + """Runtime configuration for JWT encoding and validation.""" + + secret_key: str + algorithm: str = "HS256" + access_token_ttl: timedelta = field( + default_factory=lambda: timedelta(minutes=15)) + refresh_token_ttl: timedelta = field( + default_factory=lambda: timedelta(days=7)) + + +def create_access_token( + subject: str, + settings: JWTSettings, + *, + scopes: Iterable[str] | None = None, + expires_delta: timedelta | None = None, + extra_claims: Dict[str, Any] | None = None, +) -> str: + """Issue a signed access token for the provided subject.""" + + lifetime = expires_delta or settings.access_token_ttl + return _create_token( + subject=subject, + token_type="access", + settings=settings, + lifetime=lifetime, + scopes=scopes, + extra_claims=extra_claims, + ) + + +def create_refresh_token( + subject: str, + settings: JWTSettings, + *, + scopes: Iterable[str] | None = None, + expires_delta: timedelta | None = None, + extra_claims: Dict[str, Any] | None = None, +) -> str: + """Issue a signed refresh token for the provided subject.""" + + lifetime = expires_delta or settings.refresh_token_ttl + return _create_token( + subject=subject, + token_type="refresh", + settings=settings, + lifetime=lifetime, + scopes=scopes, + extra_claims=extra_claims, + ) + + +def decode_access_token(token: str, settings: JWTSettings) -> TokenPayload: + """Validate and decode an access token.""" + + return _decode_token(token, settings, expected_type="access") + + +def decode_refresh_token(token: str, settings: JWTSettings) -> TokenPayload: + """Validate and decode a refresh token.""" + + return _decode_token(token, settings, expected_type="refresh") + + +def _create_token( + *, + subject: str, + token_type: TokenKind, + settings: JWTSettings, + lifetime: timedelta, + scopes: Iterable[str] | None, + extra_claims: Dict[str, Any] | None, +) -> str: + now = datetime.now(timezone.utc) + expire = now + lifetime + payload: Dict[str, Any] = { + "sub": subject, + "type": token_type, + "iat": int(now.timestamp()), + "exp": int(expire.timestamp()), + } + if scopes: + payload["scopes"] = list(scopes) + if extra_claims: + payload.update(extra_claims) + + return jwt.encode(payload, settings.secret_key, algorithm=settings.algorithm) + + +def _decode_token( + token: str, + settings: JWTSettings, + expected_type: TokenKind, +) -> TokenPayload: + try: + decoded = jwt.decode( + token, + settings.secret_key, + algorithms=[settings.algorithm], + options={"verify_aud": False}, + ) + except ExpiredSignatureError as exc: # pragma: no cover - jose marks this path + raise TokenExpiredError("Token has expired") from exc + except JWTError as exc: # pragma: no cover - jose error bubble + raise TokenDecodeError("Unable to decode token") from exc + + try: + payload = _model_validate(TokenPayload, decoded) + except ValidationError as exc: + raise TokenDecodeError("Token payload validation failed") from exc + + if payload.type != expected_type: + raise TokenTypeMismatchError( + f"Expected a {expected_type} token but received '{payload.type}'." + ) + + return payload + + +def _model_validate(model: Type[TokenPayload], data: Dict[str, Any]) -> TokenPayload: + if hasattr(model, "model_validate"): + return model.model_validate(data) # type: ignore[attr-defined] + return model.parse_obj(data) # type: ignore[attr-defined] + + +__all__ = [ + "JWTSettings", + "TokenDecodeError", + "TokenError", + "TokenExpiredError", + "TokenKind", + "TokenPayload", + "TokenTypeMismatchError", + "create_access_token", + "create_refresh_token", + "decode_access_token", + "decode_refresh_token", + "hash_password", + "password_context", + "verify_password", +] diff --git a/services/unit_of_work.py b/services/unit_of_work.py index 2e7b9e8..a51849e 100644 --- a/services/unit_of_work.py +++ b/services/unit_of_work.py @@ -6,12 +6,16 @@ from typing import Callable, Sequence from sqlalchemy.orm import Session from config.database import SessionLocal -from models import Scenario +from models import Role, Scenario from services.repositories import ( FinancialInputRepository, ProjectRepository, + RoleRepository, ScenarioRepository, SimulationParameterRepository, + UserRepository, + ensure_admin_user as ensure_admin_user_record, + ensure_default_roles, ) from services.scenario_validation import ScenarioComparisonValidator @@ -23,6 +27,12 @@ class UnitOfWork(AbstractContextManager["UnitOfWork"]): self._session_factory = session_factory self.session: Session | None = None self._scenario_validator: ScenarioComparisonValidator | None = None + self.projects: ProjectRepository | None = None + self.scenarios: ScenarioRepository | None = None + self.financial_inputs: FinancialInputRepository | None = None + self.simulation_parameters: SimulationParameterRepository | None = None + self.users: UserRepository | None = None + self.roles: RoleRepository | None = None def __enter__(self) -> "UnitOfWork": self.session = self._session_factory() @@ -31,6 +41,8 @@ class UnitOfWork(AbstractContextManager["UnitOfWork"]): self.financial_inputs = FinancialInputRepository(self.session) self.simulation_parameters = SimulationParameterRepository( self.session) + self.users = UserRepository(self.session) + self.roles = RoleRepository(self.session) self._scenario_validator = ScenarioComparisonValidator() return self @@ -42,6 +54,12 @@ class UnitOfWork(AbstractContextManager["UnitOfWork"]): self.session.rollback() self.session.close() self._scenario_validator = None + self.projects = None + self.scenarios = None + self.financial_inputs = None + self.simulation_parameters = None + self.users = None + self.roles = None def flush(self) -> None: if not self.session: @@ -61,7 +79,7 @@ class UnitOfWork(AbstractContextManager["UnitOfWork"]): def validate_scenarios_for_comparison( self, scenario_ids: Sequence[int] ) -> list[Scenario]: - if not self.session or not self._scenario_validator: + if not self.session or not self._scenario_validator or not self.scenarios: raise RuntimeError("UnitOfWork session is not initialised") scenarios = [self.scenarios.get(scenario_id) @@ -75,3 +93,26 @@ class UnitOfWork(AbstractContextManager["UnitOfWork"]): if not self._scenario_validator: raise RuntimeError("UnitOfWork session is not initialised") self._scenario_validator.validate(scenarios) + + def ensure_default_roles(self) -> list[Role]: + if not self.roles: + raise RuntimeError("UnitOfWork session is not initialised") + return ensure_default_roles(self.roles) + + def ensure_admin_user( + self, + *, + email: str, + username: str, + password: str, + ) -> None: + if not self.users or not self.roles: + raise RuntimeError("UnitOfWork session is not initialised") + ensure_default_roles(self.roles) + ensure_admin_user_record( + self.users, + self.roles, + email=email, + username=username, + password=password, + ) diff --git a/static/img/logo_big.png b/static/img/logo_big.png new file mode 100644 index 0000000000000000000000000000000000000000..98f1a78b088d8c969a8f6c7c5d972748bb336ffe GIT binary patch literal 1894952 zcmeFZc{o*H|2MvmgJT{_=Bdnyjwv%o%2b9ZNulEyGtW~vC_+dgGi6Ex5ebpZl+2W< z%v0uhe%3zv-1q%`p6_)%!}a_9*SW6R=j^@qTJQCG&+oOCa6KJ$Y6?~g2!g0JHO}cn z5Ha{l43QCm%VOaB9tdih#$nGI!Pi?J4(8xCC%9VR#GSDaJw%6xpy!ZGiVKo(1A@-% z{As0r*vjF*w(@Xwl!IH!SzxU!tgx06BGMA(79x@o7BV8TR?;{TD;aTdD+#PD4u_Kj zVrl&CSx)-PXy`4ZHx2|Cblcom~H$7jT5uqMqXL3ne(v=!rG< z@Mdt9)!{dLm`qWet0mUm(!#;b$;#ae>uRZDZRzO#!NL*iU}>eUcUeTz>A=Op*4ow` zYj5D>;fk}o?Bi_dt!Cry?ksouG|p4h*4Z5=>f~yD+R@U>4d-NG>2~^0dpAORchLT{ zyDQe#(b7W2-rC94*4@Uz7N>1#VT*n8hZM&{Qk)z;!8|Q30L?G{Oj3}5%6~ga8?2j+ zs0G#?3(wIHo@34}OK+ULhZ~Gk&{In{cdV;B^KmPCOGj&W8y0C1!mJ$Oo~^hYu#UD? zpa~9ZZ*ApfgB6!LV*&SQ#C?&=)%cs%o3>SlqL#dhiHl=bCwM;+^W9kuiZke^dEkq4 z##(5=V;+7G^M6PcAdo7Kv;SZDfwRZjIvjEX@d2hZ_m-_AAkPtJsd>)(l(&PkoUN^e zoTQbdmH8Q|Ga?o^Nvw#Z6xLD%3&;`?my)%RHJ6ZtNstwj1@XQNvLlo!a=ngoE^>DWlh0!&0b+bZ?o{<(6JFs=LwRXh1d$?M9Yr9$6 zTk#MOQ9KPdJN+q&CZ+S)o`tt~~J9j*UELLf+d?+>>R z1ydaN->B=q@BjiL-UhR~>&`XQ$EOH>=HtQFkkaU2qy##ch%FOIgdiefa4Znans0bF zcJjd?&<$z?f{Fw}Yl*R=wES1rv|VRz0drK5`GKiyMOq5jC57%%z++yqI*_GdtD zm=12$i<;cFj@*A*p|7EjlA^$*z9rSdaraJz8f4O*L&Oqdiw zLV`jdh*i+aXvKeiqY8J&gNMJFl)rPVeA0j9T*qSup*56&NcYBeUiY%%F#R;q zh&pR`?23)4(B4ej?FiGkSv9W32^FpKL8R^T!Ukj2Z|6Tjg;A@6w+gi*Mee6S84}$g zvsEI~M!C{VaSKmOt?Ox3vprj`aBGtOEMkpHE@L8M^+)>Nw&1F`6q>o87Gz#Mn14@m zzNT}RzmzTNXp5cB2wMHeYIG0%4wHi5%McBhi`On%EL$|boIz$4UZ}dYnSC3aSbe)hMdWh>xubB-1S31CUe-E=;6BP+p5ha^kEIWdQSu{&gwvh)iaUXE z3tmOlQ@%2r44mhf^lXN=tn-;P*FI>s?ySELdXizGF>FNUd*g+#%1&sg9OB7*uImr( zr$e>RE=i@`fBeGLU3}|kO3HTSwY{FNS1&fdT6p5Zb=RJ2@+-$;)yXkQt9#W+M_>CF z{lq%7RXg^r{G5_tGB|mF^hn4`{yxcgGM0n6>B3ti{pG%uq(+y>xN2wp@+0Gocl~Xc zKN1x?5g`!}A`6GcqG|u7>m+C-P@Dgv?nV_$`ODOk=SeAE3*{Lq@U7`KY_Cwhdz{#uE3B`-Zv%o}Y`n zZP4+2QtX|ZvX=oi6PuB%b9ZKCv03r^{mBPckG(d#9eV~Bmv3V@eZEOoUVtGY^`RZY zBv=@w{;8ALQ29lVDx0Vz<4l)9$5U#G@uM{I(wYiL%y{cSEth7j9EN<8AH{9*OTxAo~2AsF$|1v9@@l z76Wm=8Tq8t+1JwY>TD#|b~?RuuTT6>)Jw+LzR74+UkG`tc%pcSSXuw>Q>PNT z@IrahoGDrNT~bjeo(8_1hY~NC-sIk-qj&pRr4(7pJtr&r%!^p(Z2f!2x9>z$PFzk+ z=e$^RBl$?+m7zONQ!$A)5#zQ8W5tyzapebE7d%YdPR*Zdwfcw^aQUs5&(nSU(wU&B z7ltvp-sEgUSC-ukB({!CqVqJ5UMzpcQ(W|XKd0wsYfpo8nL_wir8fm%G@UNmw`lmy zu3U=#dB4B=Hq>F8D4z33EFtkD|KxFU4%W#sW9Ku!dWDW;WY;=HG&qb_k_#OKM|fW; zz=of!^qbDu(>qANanAhhP_V#s=My;cyLPhVPvH0W@)v=>2>eChF9LrN_=~__1pXrM z7lFSB{6*j|0)G+si@;w5{vz-ffxig+Mc^+2e-ZeLz+VLZBJdZ1zX<$A;4cDy5%`P1 zUj+Ul@E3u<2>eChF9LrN_=~__1pXrM7lHrZ5Lh?DaHLv0h0COVz0mE&)OI2vA)cW) zL2a?~Zh`DI;iQj=qP_0ANiPsr&)-1#J3tnA+v4S_(D!@>-}`Gq1O=L!$ROxJ^Pj^n z|GT4aXDrWPv9gjPVpwT05lLA|84;|7tdxkfG!BiHF~?e&%i_RHjo<(2?*H`Yo586+ zFI)gu3(O}f@yZB|v^>g_eXD`0wnqsEi`;F%@h(TKt-UBX8{-NNnSn!4FsjTraW|Wr z>#IrJ3;u>K;Xy9PtMpwVP|CIKE=($FCcVMh=of8Eh32K6BH7JJ3*jOC<-!)qZAZJ z1z8zc1^=%<2lWsappyha_`q|9KL{cuAPz-FPC*G8l+r;&2qcn-7)e4x3=Tpf{K0dG zn4aXwaWNIrqnEI#6D|zmL9tKBc+M7mJf`2d$SYy)8ca^X$i&RT%EvDtD0EU%>Ws9E zteon(^J?lEnpy^zuNYo6x@L^Cu(YzavAyNy?&0a>?c*DA=Wb}&z3_;KagX8?9w#Ox zKh4U{$<2F~|GfB3N$K0NcjXneb@dI6P0cN>UEMvsef?j*4GfQrj*U-DPEF4&Ew8Mu zt#52@ZNu|IKwy*qpU;0gFTf525jZ(3VAToxt zu}_LVlJiLDFCH^@?WADjl^o_QH3JD?v5jIff;Y|iIEm9G1 zTaP=FC@5?t6XV-~z|@tBk#K{4nDvPCh;}-N^bi+GvX6tGpc^6zQU=HkgUBSwB;E#X zN#K_en;no1ogO+9c_IkP#BOzPXJRpwKZ3|H4GV-8k01(S1}bAn3lgLYaHldzhQC3X z$ST6yKY~Cn;HeY*gu04F4nsfc$!ss z9@u0;*U4Z&K}0|>{s^Ad7*Z;M^x)enm|7SiIu|fW!r1@x5mJhzFb5sq;h2Kp@xh$2 z8bpcY8YJKy)GVep2sH}crPYEIK?4HJ2tg>9JDaEj4O);ud`Lz-)kq=?95nrDo{Y#YR-b(7zQxr9s$ zBLZ;&c$f)X3dc}l?g04B^^W@j-oT@yh8w`>h>JuI?kr$PZI}({s-~%QY!H5KU10hY z#3D!v0Jz!sAM8+CfDS4}K{^6qLK;>cH!o^ zh#wC~&=in_1{M>XShtkTor{3K0{P%0qS?$OnZuBvX@-O#8p7M?OvnI|yaOfyWdrCF z@!K$V8pr`WZEYfyO&F<02v!gR+inrAiKrFugurmX6G5n567mG25G5iqk+U#uctL=+ zpA{eksX>INJJg68V~hYpjUeb0umsi+!jLL}arFuYbR~G*8~~Fb-G{r#BZ+sXe>_0> zLPB^?n5rNYVO-*kTm*(vgDB`O5FR1C9+=lfKq{-(+-p){K_VZxXH);2OD^n$bW`}hf|IxjHQPd08sFu0UR|zeGqUrnSkFEs)y2C zQlo_-O{83FK+^Q!wTOklh({7BHIUOlZeR#m!Q`#}3{RK_j0h|p4^Y5(0Q|vCAU1!- z`p+-4SyNPx8C1rgL}#s>st13&;_2l4|?p6f2e2ZYB1 zwzb(r-N0@KvI35n_TU8}$;08{PJImkd$NhPy9r_n8i3&-SP)^t0Dk(=>_ueNA(aI1 z2s9DEfWZ=iXn{Bq!$L}U0GN*9ggpmL0?WU73G*08?O`K8iq;C2iYLJL7zwPj09xlfhYqnGN*yvG@t_}1SUoBk9Ywwk^F}octCh$z(e^C zn4yH}=@CT`rUJ+#-i4da64*XUk$?>~l@M9ReU0b{hXnTAb0}LZK?I(sBf{JEC{`o*N4q=9r0gQMs z9&8x^W1j(b5YPnff&dupt-~ukbT$GE0C5~Z1wz268T_NftT#otj|BZQZgmd?JKsH9 z$N&M0#2?-$D-ebNL~0;+R|%5yB8k~<1#2m0 zg|6t6j>%mYvi2xngr>=?xXTQzMq>#U4H*F`rvVP95e7b-(M9CeAJgz>o`73$W0)ym zvJ8Ka;ig)_pjKeA52X>Yn-9TCC<25>zJtj90Gd676ubf3VS?~6=mUr-ze^(KGu{-| zNxU#s9ZB;&AvtS3WKDVBr%PyOaxb1|iKm0kTTE)dRr-#|i@|N<<+&p)yk6gkZ<&1W zKs>Z-b!c18dZHe0k}i@vusi@u2z(b{DnagG>4aSd?JF=*5TXI_asjsw zAUQ||_M`Aj^U~2y~ijG1O{gevpFnyZTRLcsXzIs@`9>K_>BoD0?Fi0}MXn+vr7EsHy z_8bVP7QquT;1zB0?uXV9;#`FS7@(?Y7&n3uh0QCl6omMgAbK<gO=L~Lksbxx5e>;SF_+4TWb<+(+l~+2Oji ziL2Xoy5r6)>CZ@el6d_43yrTVo{C?81s=ykg{-gC`901mYSZ^iMMcpHY&JYg^0+Rd z$nH>f?-Ta{WV^|-O>zJ=z38kx+vz&**6aIqrD`}}>U(t9u^tQK?!xinilb+}=s)TQ zEUqd)9-h9j`BGBs@{sHSRHrmi^1Ss?`BUvBrn-b`k;RSm^fb;r9kPx3=5pK19`{D` z$71`_GyF2rJ}pU!4_>J2>pDHZY`3PUpe;C(?`P~+IMWnF#m-r}ZVjTo!+3xM67YpX zNf7Y@+z(|EzQXntzK}O?gWv%cV30RpC|9%5xyNDAgp-I&b1x925sW6p$IS?Ws6(s& zui%RU7(oi)WXrRrA18mwcgUS~R@f*L<%y8u&|PJ25qLeeWFY%YxHyZ`8aJmKYD!+b z891E%Cd8uR(J#fJX0BPT@5;61+%)p(+5IubFXGC3$yVj;~wt3O4i5m?1YS~E-c?IG^ZbrP-pY<62FF5NPj;mb@r(2*iFu)rg04G0BQcZ zd=bNErMAq-!5<3HCYUQQ||9t zd+PedpVOC~9YBU-2hdBd-$e?*U<3}-H1U4+&klHXW#;XQN!8WyDO2p-v4B1{#de92 z>U%55D&Uep2m{-{Okxlb0ayvX_|UTc<37Pd5EUH$Tfo4{P{?62MR)-uXp(ptNkS|E zOa##eFvF-@1fxud83`F6NhS&A0*T&*qcuq0WbK?6oI6}Mb}2R;)2X@wOV<*E`WKGI zD=*KnOn%~;PxTwC-5wok&PWpyo=NHUBk zc5Y9|$Xa`NPv^&aZ%LK9#l2RX-T~y%v8cDb6gw|7*WNbqmE7QQ-;{5qyZ-^yzMy!6 z@wk>!ZQiHd81AM1yLpR!xmT|I)JcqU5}W*q<65BYackoCyXL4Q)9U4`>G;7+cN5p% zfK4O+p7nJi%2!Mmb z|2WzUV3}%ft#o$&p>{0&twPaPq5wTq;pSBrd~K6Lp=>it)7Bh@>IbRMjO=s+BRzfG zL`{=v)igfc#gHUm-IYur}Jer!0K5~T&5q9TY4!*e(iGjhbJtYsA3 zW-;_hYU*u4>9YHm5qt?xq@sGf&di1L$bKB7ALPPayZVG;bx1B!%|z~D9-rWR)B(h5 zv*g{5iM-Uc=t1cr&%<%U!zSi~%S_;p=B<|;*DSrm&X(0~@9XX!K)*Oky-!*48L|PZ z9T~Ab)XqQ+N3n;#k$m-!t)0W5CNZdhKjIHREdjy|!VduEG=`J}68VQ!@K>%Vi0cdB zhwk_x)OgS#h%rE~dLW^)0Y<0+^5P=j=Nj7FF*`HUMw;XhR#}eZQ<-{^PRY(5AdqTa zKHEEHIAcp@X6PT4ckP-@#g(^l*|o2)edms%*Tu-~Y0C1h@g}dB7W|&p2=EzQVK91{ z`{LR4=>+kiA4*?wEHx~=>yQ`!b1_%XvyKC)?O_E49tNUC-sxs3i6T!;4FyBoV)Q?g z>iS1^+%$~SQkpp-?fb^T(OBYv%)HpOuffo-OFM?0TP&3SfcSV+R9{O-vY( zMCRqyzXdA)f;`-vub|zPEBpuJjSGd-*lWD?k6rS(t+}eq^fXyEWT$jwitpEI?ip6k zIxT}2vN%Ew*L&7y_pMWCk_w+Hyv`=g%^03@(tj&0=NXl6@;z8Rh2%|zbvHLo>h47o zRx|c4$ta;u7S@BRBJUTd>-AM7t!ddKt~}bN^EviXfTh5uGFLS+^3918FI5P|9l%@I zp@1MBap-UklRU*|hiMb+WF+z6|35)I5vVu81`y?o0a6f8$dEk$r1(H61T(3fD5s?N z`THG8(oP%|>6r`qc1hh3_Z88;+2?UmQ1~(Pio~2U@uq20%|2^cXGv$Z?%Ly{v7rfP zW!WMxEuOOR76LB$!yxpe48iz z81vlLL_FRpC1!L01L8u>Qyr!QZMK1|+ElH$SR?0}rC{Y(!mw!E-&$z$C13nev2N4G zml`?oy?Jn)*4*!bn&PGmOM@@Nb3cv2@l+p^Xv>`4*|Qp-c2;wbvn)0Gu7ME0cXljpZV-F5d5ymtkH}k4_Ho(|>^DeZ3U#;6ZFgE$ zlo(d7eaEU%YR0HmoKeM?J{UA*xu*O(0HyG3dFfk|{JN2U#Rniu%I~J01lzh@R-|nD zK)1R_5*?rWoo}c|>&SUSat6p<9|K~PVl>KCEFx9u`l-u4GnE1xuhaQ1c?MU?oMxCd zG?iZ*`0?$O%ZwQ9P?17D2NpN|MLkz#l$Z0OqE?ccz_-g@A-k1@N(Yb^?s+y(R8Bir z`-@`iKqxUnU97jV*0QBUmLY-wM3`W*|g=w;?IJT83=N?kAmo>1{h1Y z76=O*!72Z90VPp51NxUw_9Iw+a*%9OfVPAxvKL$+fi0F`Hu2kz3mz`iRVBJo^Cw%l2fqh|c>es!&nN zq3OeMDJnj?qk6QwoVO?2D68;xgt)RwOzr&;bt-8RM_IsWC&1>`iVTugH z4%bq1{HYd*zrQeP_pZAamT)Kac2nV)^6E(s&IB^iyqjH<_d{ZIGWKrOblhv!P>ez| z^L<3IX^weik|xP;p}&BzrbQCJ#1)>-6Jy->G_(sVk`_^{eqz7O4hFH$@uR423bPw-U@|1%YYMadW@a4y4!QQYP`x&yYSmD|0@HP8The49|~9 z#0)20KhxE+^Y6&|U(>Zc5Z6KTa_*LOWVo2|B>k#>rDSva$gV<+7wX=6=lJ9L4=*0? zYK~fW&+!&@Ssg$Zv2QgW96ybzHS-LE#w$lbz;*#^wBrCW3z z(a}`s?@m`K9Tu^lW{q7*x|>~RA5x&m#@V#W|NeJ7$y&vk7}yS;PU^i=??*oLd*k%( zrN!ErGfl?bXG?o*s9H0Y2hd8JO~gT4&(>L%mT$W~QGRjddtTh!l;`N{ADwwsmSk`}!dCa5OZ`ZzWT{BT{eJl5b z=Cbdjhb-(RVTDdwZ6_KN)+B3&75qkmgSAiVJPKpKp%pg2vVl@g+EsL^lJT(8e|*ko zi<^c5k5aV5NxbjoVKlydW=58Rl4{v#?Iq{SZ#I#n0kq6Ydx3H(RCd~pzBEbf-LG$0 zr(e(H-4Y#1lKEhzIYjdpu&55IN0aP)XAmGE@A~K-o+LpUt3n`gzDU{LVdQ_ogidJl1?@8tU4U zn8kc~^D&*Ou9oGNUf(L%f3c*GV^ripSg=JM}V^-7Vwzx1NfK)b|$y{O^5zv}yU=;eeAb}nw8 zoS6L*Fsy#@08$c8KG|5W&F{;p}L2V@~uD5g~zh3COTm&QMxO;Rd&9Y_q!!hw|UwkS7z6w zIxNIemi9}?u&yW_;D@#Mcl8jJ$ii6+>AVesQV+Tg7PpRi_2K#6rmAfUQ0R`?uy~g# zDvN25TIeA)XXCazP;SFjCLVU`uY1mX;;$`W2h~NLFr~ei$$qI7*#zq0yt!$c!VQV9 zwO6lX{hs_V5hJ^#^k6{r<$BHIps*y$^c-5;yc}1qOPgQfDg6e0gV~-H7X}lS<<%*R zJ-gR6Ap9e!8WBi)K)DHC>L8@}Ao~GTtr}SEcK=vyWi3eFgJ8H1Yvq9Fic@f;NQ4J} zPtiRa^<A8w3!$_64aYtL4CFmr^bLCfCe$(c&jMw+;-FV@4>+)79FhMR( zBqfFkLhOdGpEu?+Bd(|;FdPukI~-G`-1Vt@@0VsUZ6@Io9z&jrCwq$Obau~knfvRO zDQx#PDty*4RH&YxxSkR&Dtekx2h`4-m{6h;L$^|UO3Gi1@F@f*> zE8IHzN~V7rp38BdAFm9RDF~sD;+F@JHgWpA6aN=-N4`eS`n%y=Dr?WuGz>P=BBNw= zhgC%xuiF|6Ct8GF*=e4+686isHruL@$wli(^pw@2&vRar!L>}J2gLRa5@~?=XCO*{ z8=|4oMvR(5bowe)h8H_4Mp_b@^$Qot)x???A0NziYhTMx<&k;UVj|Y4BRcEu(O;x8 z@`@5gKiKHT@^P;xaV)SRux8n?+Qn@=F?uO50%+!*qwoH__~Yc=cFFSK>(`SU=INRw zwM!qZD^Gs#-PZEp(Ld?zb#a-G3H3>&^Vp^1hbvh%y0`x1V?bX`dk&2+D73*U3##{o z@;AXG6Y>adxaI@etYwk_vrCdmlQ%pWGQVqC@G4vWX;B!hm)ijED%Tu49ja%Qw0!AU zyx>-{LGHwEOnzoW_qi^I)wGj*!F@x-x59TR`wLxtc+;~}S4{2A$F{OP&pa#^PTHJp zaQRD{CcjWnl+MWsdM#k3<;w>U$lL8WP$Pa;!;~{Ek6t}hC?9;N$y+4L3;rfwW<5??c@)E{#MmK4d(2MH z*7vesZd`)rm#+^zJQeYP1geLKI{JH2WNsRL@dB5&xD37R>O47V7bn-%qO;~`GO8;q z5!t}o9`%=~8AAYjrOFf$@8j20c=lmb(&Y9;+pX`v2YTOMy;R^_$V?Ts_Yi$~kL;z; z~Dn9%Pfh=RSYhpO*>jSgldCn{rO7x1PV+xiNhJ zK}$ul7Fi4WJtJPn%O|Kj3w=p7E7vCQbY&a?CHShW8QnaSa7cuWT?4;2yw6kWHuAc&-eBBsb=j)KPELP9 ztvymQaV?VJL-*o_Sb^HlE&DW=`MR=zthoE$U)e9dj_05lI1S3maR*Sy;HsEpy!Vcl z-@bJQa?pLPFiRm86c2$%0KWY&DkM001VP}mR2dWTgoFeX62ZbJLDmR{s^rky6HJaC z$q>_p_kE=o9OSj{w0Sx2TvDIW{0)I(;<01=`Atdj%;oYtGl5jojOkR1y>A~|7R}4r zT3!m_4O@`ieU12?=_xffYkd3joTkb(8oSWRjKsB;vA*>aW2e1Ogy+V-eI8XGluEm@ z?`!b7z-2rA3Ex_Kk{BC`EG(5)b#~s);-%G4>#6#&Ba}Gi*^8>0#4d3$rh-IvRutu^ zxWW5@8H-PXc~`=@m(t4xIE1L`mXz-?X_OA!HC@)bT{>G5czefsWtT=2J;9KJ`)rl2 znd4*Sx-2{R**ETb5o- z{pibIy9Q3{CdP((o}U#OWY(0WmGyK-gpFpRpX8nqj5z1OOSws}eaX(<@C?1O8>&13Td3VP=DvI6djXk-K8~l;{8*|Cf7tCKj9S?X z2h<(&#CB)Mahyqy$@P`-y-E}K1@eCER$j|CoTDoeO_n1>Z#tviCD~e9JE@f3#ubpB7(bF~rAG58j=}5GT|i1r#_> zz7$T~&v_=*lj3$SXv?Lys?FH&cx6(f&U??@nLA>M^r>re`+@C}HMaJt?!+5&m~e$3 zsJt>ss`kFMdnGS;gwwjp=o@K812Z|=yL$}oJJt`yh*wo4EYDns3_UHJS~R5;(&4!) z>}T|4Lpbh75Yw)5)=%$9qq45mXYaq~N0WMA;rJGFlT;gLT4Ga|toCTSBD$$(pJdT0 z<(CP%!Qz8W&!nEH=r(sEwan5BPva{wV^h-sT_cTYa4in7(}Dynso8T& zDTw_pb-J(n^z?cBxYSRS?e^m&smvYfPWd2=!rSdUr%f6~XS?n?D-W9iaUc^uoA;~J zOmJBW3)eaqR(;-@*4@D^Ykd$`?%3Yp^v4uFsT&nCKlW?ww|D$vsi3ltjrBxD^v!k# z$a*;w33eFpMqhN+B?e`@<4y(`@??n8*#|hdy6xvC-`}R)b}QF9F2L5$d{VbS{s6jR z-PnX>=Ua;~Ed+(S&#TV8J^jT=ZFb4HX|L*QpD+iIq>f*V>O5$=Hrr8aZg_%ercBnZ z(>=#A)Zc>{-WtH(f#@T|&xDmJLh6H8Rsj1Vo`D@ght*PdcsXFZgNTshfVQR+gz63> zo)+6*DiZ3~RL4bre%SBm3p>iPEAcY=vTs#)k$Z1`F7L13uIM$fb#1~Grz$2_4h~lL zn2wCSkHX7bs^q0TfK2Sj1Yexs6RvzTFvV5j8KXnCK7G|(OS<2(Dd9b$W#RIJ2&%!= z-TZqvrC*9d_{N)VD~Jc$SUQDt%OjUC`iDh6*U0?N`{Ed}S+| zWR2e)xZADk?4xipr1tbp#SiVi--KDpTywp1Pc)`ry`Im|8BKR6HVn=c%XC;2?T3^L z;AIsqU(RQBnTT}`Nhwy!+I0BkXGS#b_tRo~sCC3==|RX&_Of9q;4A zSw%r1I75C)6PvoMi!;Uc3C-S5Pm6-6)A1QgnHFyKaUXg0sXHqkVF!cJ>f;!DAHI5B zTZIP~_KO7so63lz<+uBFCU1FNK&FFixw^F6i>aPD5 zhw!)BiHDJTuL_gK&0U*6{m9As?L5ya%PPz8GQzo44Q==1{-cKvzb^4HF?|y0807NuDTf?4pMIyDW4Bs$lOOF3(fOV)EYwi^R@h7w5%vnzD*SKMuyEq>e@`n^vaz2_Ki8 z^3`XqE+36~MZ^>=pV5aW^1ZpbCloYUtx$r#)#CoabzD5~#|`AR!*<{5P-o})XuhGdCfH&NS=Q1=2~q; zsb*y2mmaAkp0}}hqsA`Or-z!%4R#blmGna2Zv65P?{4JBB^{+`>T%zb;fg8s^Ojw{ z5SOK3?=Cw!Czqw@YgNV5%8~x4MhGgOe1&GGhJ-*(mnEAdlC9jnK;N5&!;kZhFW0Rv zuLOG|@4U6FbxbXP>h4?ml65`(u_xsJiuBB?$l#NWN$vwET0KrJd+`c~Tn6QS;l8!M zhWb$&Z>K#Qm){f*UsAl5;l592H=r{!wet38l^DEA50)Va6&XYq!N!4bt~QZIk(!7C z!vVJ092R=uRZv2$8d?YW1gNgkQbw7M&TT)O>pt!n)s$WbmRjAtl=1WGq9@8umvgjD z8+>@bu6XovuTs~DjM{g11?b&SbQnk2P5FQjxy$8Jevf5WSbjL_-mvg^b+KODt)F?e z0nHaH%op8Gmg)e)rPyuG*$jH9~&;X<#g{}a1bQ- z%pFjTNqL?zqf8&;d&*+Ew5XEp<-I~S;l{e-J;BU=9yxX_(JbtvN}HL)#E(A$ksSMZ z^tLWX+lcLeY4^G{|8+d#(Bp6ORweu5ea@ zVw7X+7IlJvKtEUCGtAZ(2@IsDF(tpAN#$U5vx`^gzJNe9F!+U&*Sn(PKc8%!Z}jY# zx%AGE_~_cmTkVE?(NLX}nhn1{<0<5M@GP3BS<^9#?1GAP;-@2nrc;+1#LvTL z$sh-4;_@0X6U$nt-?An@#XOeTb0JZZ^RoLzhzfZti3+R5`K53 z%9i?ZhIl+vGmq~2B6chlbMoYUB9!iJKaY8z>AP~bt5*j545}l|KP06-R9k32mY-OS z<;fef#oymjo{WH^M@9rs_*nVJ78(k~tQR z-Jkl%(L8;l`_5`bbgu8vyJZbY^rsn zWO<@%R#UamjO<H@0OY{3X$nSA)+{-T(aLP0S7S?z@T7;I6R?ew?V0542}ukC&HH zZTqE5wA@K=`mRX(%kO9}`A;=V!S6Vl0i1&eNv)t&==ldiQ_gN7TbK0N7hUEBtFQ6< zB}BcvZs$qqW3vH~YxJ$|@&Et<{*Iq`E ztu(4|D5Z82*~#yuF#WWa;MdXEXWS0?I69D+YA`q>xxjS2nn^#V)oE**^tQ*;pk-mt9ov9ou7`Shl+n1z%A)+$;*Uf@Bc4IOvS>4Jl0T*W? z&;A|1J%X25WBBG=jHnM;W9RDTO7k_6{lQ)pbDBdp;nszGsG#l+yz+gxqp=MV)#jFR zcToR5+WcUDu6kNvK126Ofr25|fZx*K^pmTPhy0FtT(}Z!zC=4D_AAhRJ1}hPOQP== zyM*gcPj-)cU1x}LPXzl#xe42SA2o^)fnD#1qWlFoVz3c<6|c;ClS2^f6FaPZV_<74 zV!NI@|4Y5n-rr;?Eo(bNMN5BNRcq4WSZjmrv7Cx6!_NC8sJNd3JsU3>PHm-66c}H# z*NCHX*`|p%q?bUowxqQ7RFiJ#Q}waMi5DYP*Cgj?ItxT;C^^HA96&zbUxVP6Dp@}{qQ~}(zw+Yf zuxaeCa9nk{Oa5$4V1Y-yGrKE~nb~@b%>KN~jkxDy4$Qn>?Z@2`T5$W`FSSXn-=U@i z5#%3<1GlHC7C$f!vBeynDaOe=vL2tO`_1(n2U_{f-GM?q6y017pR>W*OJlP^K zHPshovvRKLfet7kZNH#ojlz8#s@ZSE`uyA6UiMJDgO zyuXGs|N3=vG+pCWJ^y}{_r}xOcU^wlz8#R+M5EgrhNa@=H(@&2rq6+yps3wjDzhdI ztZATB{(~$~N+=9SdBwbOmTcdwuLAvM{Mlo-6egNm+Uq@;Rl1OyUI$g>nn1`GNhpoo_0le{kdJ2Qv)xIzB}{udligaJgLF)uJzx zJwtk#YgAqE#v{A+Ox&jouKB{v&ekSZq204Nfi9GcYi@XDME?qdYh+?Wx{oNot+ngC zU&fenZ6TIpvhH-KTwzr1^}M#w&{sR3nz&}gdU2=)E`tt#Z&{t^(f8ifPWG6xc1mVY zObh33diGq$&@S*QynnSf>6pEObPQ(ttkkPNP6%EOhvYM(QyOM+Dbo>KZX^ z4YEz)d{#^Ljor#0UsQL>X)pb>^BY~{w(vsQv`#n}g`Mrq?erk2C_{AY7OUwX3-gLr z6|1&^1f6n`+%nyxh9xLNT+!}gufy5}-ip#VcZZKH); zdE?8=PmM&{Rqp$`T{G&i^=M*N+GO}GLEAu{=rG1r`}xDgpaZDBUx({Czq*3IpI^Tw zWV9!fi`0~zZdRBGqOY`96REkBYM}L-t+@RD?=E=>SEI?~sc%M;RgE3c=VKSdYNIvR zpYRVoNYOM&dMqvC?3sCjJCJchd3awvKdkO~z-pPoOC7$R4P4oTcFuHva?YxuoMp*l zY1dR1`=2zt?13)AO)GCQYkxR=d*AR~NwcX(9?p5ah#zJ?XId`{w`x!4{GLVaFSTErv{z zh*>j|02cL;*8_c3djDZ7(eg8QTkGd zB>NS(!9PI2vh#OM$7ozfZDa-qigXT44nOD!8#oTRYAC*Kr9`TDQ z-t;p_k-z4U{XH7ppkdY%bmf?*ki9qO@2J5CY7*&dUFMRnM~hG&`tWob5@|y;>YE)T z@>5UWl|5wXvZZql-$mF+9}CiyAt&O*>K?!g9U*w3xLPA`dD_37+=yg;xX zh@I)8@;1ys}hL_nmsVqsueCH6E5EI+uq-Wr=+!r_BVc%9I|v zuoR{HOlCI7BUEn~@TX474z$*-G_6U!65$oHcimg*xq2nML6q+N1 zz_D9T@N5}A>Nk*!d2)_MSQTx4?1BVVop(Jq&aqkh*^4(PM?A{|H9r3G-AP`v(YqY~ zV>0$$y@yZ68mBLZNZ>Hs4E3Vv4NV56z+PLvq)thK(uCqP@{hogPjN5PW9H=iSdM-m} zbUwegG@ID>ms@WTz07pR;r*rdZS9JOa)~MIffa7M)2=iVIqBtCS?1Jp{(M`35*vZW z>4x&j&GpZVV@smtgB4Y>iM{&Y4tS)Nd95^&yu`Ubl&wMLRqLt*wMsQ+jgLg6@}fK2an|YgxO4m*e7v(5NN_I=8POGjhO2 z!)<9clH_asPhMq z;%sl0ioU6JjA`Zb-RQ`gA9OwMu`fgtQY(+&klfHw(zx2Qo&JtvX^NaaqM?qKt*RyG ze2;ba55^AnLNZ(7Je#5zG}x@&(2e^XU!}RbC`VhMeLGZ`N#R@}zwhd^XFM3Lln%X>E@f)6O^O8%S*e!=aM;z)qs!PcGdum9D zC_jVz><`Mb+vOu8&W?|(IHEP!`(HXoUW8905DpK3!+$p5SRr9k1aR@dvy0i#Rqag? zoI1#Pz`8p+mm4^CxV<90Io=PP)uEXkW=vU6^YvH5P#KD)RaW>!lX2noCKc};i?Ys% z5-P~cy@2*fTKcHCCr{O<7Lt`19W{?C6-J2Pz;UmyK{o@nP7%*4w>47InwCI7c0T-m zmpnFjfg+?OsvI|k3VgDEEh}NzkDckhf^NQd^!WMR&mvC+TJT))fdeu#LvPj^g^e1{ z+nh_WLSXmLUpjGgYuf;k$ANDuGlsA{yAhD%eBw~56`sfO7o9fbHt;*ZWVuyN>-_=r zUPd{0;N8PdoDA>J__hkkM7xZ?^|gMU(H_(I&W+Wy>#_I{$2Wgdt+##>9p^%hmqFUM z=VESWy}XriGbEUGxRDN*SH8Y)z0_Ee@Alz@wbGRdH+ob0yN@i~+dO1E#SG?+j|fw@ zs!~KUxmJztFF3jSQoMVm5!cng)1T+t@%eKP?@wnQE)Tgq-K~9(>6_C73Jdw;qqp2H z#Jcac{e--YM;m@{N9u;}rg*T;3-FwfY~~8*#iKslB&X!KAAKZ4V(2&SY~%}Jo*C^k z{kokc?@HPz7|M$8-z;8xFq$Ax{>x&cDd+axTg$IRZ6*5iG%>GHpHgW}&zs*;U|(n1 z80$E$<`o<;CA$t*16ZNuE#$d2E(4aYF41JOS8-TMsQf7&%Ab-hCb}arMYIF+34Q2ax0jv*Vtz}J<>fvk} z9MdCsU!n)3wuk$+NNr$II_?I}^1#P`APFFZSj=4@dC0OpJQdjX`+!2O8(u;Nl15&W zNo%!uH@x#bj{~KsK>6Nos+WD>?M+G;MTRT)!?#TTVj zhxh(+TuZxxn42#iw9W)r%Az`YHHG*S`A6oXw?&MVi*i1)80wv+nJiGwkXjLc+LP+X zrA^VQXE2Ywp|f#R^355e^;ORJ3ayjPEpLauk+UdxJg0kpjYj39ZVp>$-RH_LW`(au z;w%3Ldv6{O<@f%N-!n$WQf6#XVP-5bQ4C2TV+@8U$_SNx2H6c+CQP!7B}-ArD{GQ9 zvP(%qvXqK4mX{VPS;{g=^1Da9Uhn1e`Tjn?$LII`e82zHqcZn>pZlEadYzH` zSNPo50&nuY2iXzV?(iHl{^Q^uWwxWDgu9mQzU6&Ks@_NF&zQ8UKP}Hr(Fr}V7QYWvzTd)o4v=3&rU`Kv}l(lhXKSkFsYwb>6?jQ-YH^SC1QKjeH+? z(-WKeASvV9;y2-$Q`RQq$4hQJtIUhRx6{EsyJ!?+g+2#EXq{)RJmFFL zvs0#{Q_lG0x78Q$foGIGo-(pOKBe#!g;c6-m9iOc{OB+h?7zInU-e|HFSy1mvgN=N zE|N&Q#fy_sBrxg)Nk91hBb{|V`t7@y6P<^jn$PM!XmWqC^jKNP80xO_F0^Eeb+T`6 z*87g*mI~bk?|q-#n87L(j9uT=cvQ|ZUbWUzFx?1T;S{foJw7@cCY3-W^hbY44EnIR zgKW($KC=ie`r1D$hbm5w?QA(@^bFMyb|X%_q8I*E_?k;sN~$4 zXQEHg#$9jcbqu(qF5*<&##1}N154Kn+5A^fjW)n$#uO{Qx8tF_adYKiUHdPJ)`B{p zD{_|2j?GvoB1S36x072|=Evg}D+?ZbteTun4*qdUx9jugn#P?ybyV(9BSadwUt!8s z3!Xpud7xSA37FUn+6I_e*b-g}%dyAdMerbzqIC5tf87Shdy{M_FI;f^5=fQ&;7&%M(y z*CUv=ul_m>@)^u+Gdz4{23p)*wljUf=DM~@Ff;Yo(wqm$?@ZyzC$H7OV?Me>V)|&W zjyLK~eP7I_?rPS$eQ(S;%Sk;Tqsxf)-39X1^}9!3huMCGWt|;6lp33}4s~i)$UN1< z^LQ@hasqQl%ay+7Gd%C?Q^U`Lhvl-C=AW-9md-9|pClh~w_s87-bcU}N2|t9f7ufI z>B-bkj8EsR!)k4C<2!o8ljPDh-JVa6Zw@a>jH*R;!d&m^PL17BPd4@TyOx)%9bxNW z`C?CWwv6MZGvfG7ezQh5baWzixF62Z`XYa{s{8G`jElzFmj;^!q9s2M=``dzXo9C= zz%+8{8m6CVuY^s1S$S09@(d zFknP%!%ONEgHxYgaI~u7lm@0ro4zDUX<#3Bq5h0lQu8r=ac2PC*w!6<{QY*;OYo>z z*E;apzyml-r-zu0(+1;jeSRr8km|^)v<0-{f%q@>*umq+ z79i)Ac1jQJy)I7cHOREO`RPNW47cG{Xv^(3ohuL6R#peapP`MN$*Tt%B}P~o-rm`3 zle#I(mB|kc1675+0(HLaxK&hUyt~;OhopqRIhWjDdE<1ie5g z>E^A+_lB3Pbz8uZ>el##Nvn0NUG=NjoExSE>p=7_fi#bALG<&<-zLW=3>Zgd*0gqv zNVn}<2l$j)uZNWn_eLchdv`vkY2-)RfG~Em;n0uJ9T5!?Z5cev>6PCX!S4hozJo~? zHC_=@H-2bx#*Z+Q(c^E&W?n*)pFi2;IdJDortI{SEv=z^PQoIsXSWKUxcucRump42 z)?F_C;_C%>t3UQcWuFd0g}qFEF8#Ue;PoTT`@St5?y@KuiRjnVMU1~W^wE44+^hVu zYkEm*Pv`fC+sa$lfy?IMW_h1VgT}K zD8e^$iI}57at@F*hch(+8A-s+%;-W6RSSW$Y?|euQoLBGssWM%Xz=~|YnBFFswz~l={H9^x)ZOO*w?r;?E9g!N5WXj94Rciu0dvSL*XzJ`kjn*J^D>WQ zY>s3IJLK{};=yB?@XcV9QxpIx3BZ*wF#i+evf73KH+2j0U41hXZ31=sHZ$ zVZi;>jX4shC`i?!3IXr5{p6@_}v|>WCa{aRdQjR%wNvh9H>8M%728z5CW2ns2WZEWLe+^Tj2-)agOde zfDgd$pSj!{^Z;AHSsI7(1eyVGxOX^jbMimHnHw(&c*d!6<2*9Rm;$o_Bau!1Q+r*fXd?RWm@2lLRMVi%0)V=;W}?h}SScMVKJ}0ys}x;b;f)(}=W-FgUH^ zqrd;ulauN#aEwFD!&&p$qzn~07ux~_k2@<0XqltH+S*b!-UC;BKvaGveu5d!;Fo2+ zl_#oTOhSRFDd=dBxSO z#?5ows?lh$Yd9BRB;&9iafYiPPH^L_b#B^p4x$di4H6T9+(&8PWAKU7GsIExoIcKx z494H*Cby`9#4lh?c(5UGT}qJ=t`7cJ!vHClI+RhIm%%|0L=Nu`Vj{w+asyu!$VkI! z2fTyIb)3dSil9dkH6X9jhO8W`hTz*lslgrsG3MyOu^LcZFamRQ0_RoX>fn9J_Z#i- z0X*uhKVRh}$8H5lNdWzgmX0C(KvtBWR0$hca5@lC1DpYAjzE(HPZ)mzfpdEDi-ii1 z{)0nN<*+G*(;T?>0M7FwH^`bm7OPN@O9GU3g9r!(x}56Ii2%q(#myzg5S|Bf+FDHr zV&~#E5<#8;AWhB&`XF|CQFrly=E2oM}D4z~km zXOP5M=SG7-I&v5u@F0eM@tgvnX1^A?IqTqHG9LJzBGUvf(xHq7J?f_}V9%w1A076* z0oQaub75>$3%(7?gyQrN=Vk_&@yWXcQdn(l=1@6#xzaz0nZT1{8GIa1{>tfiaMr|e zO|bmZ&s6=56?i;&8qyT(d(aDFPXo4~lZb(y%<09S%BgP{4d=3z6IeJO;EIWyk3VPS z99slM*W!GE1FPi+)r#9Fv=Ll4>fzJ?1;GY=&>7so?gx9xVjV;U3#i2-HllBLf^RKm|dv8!)AxOK=J-44#46V943> zauR&<3jy#4m=^~wFmrI+AQ79{Xq(rRFdz=<2Qs^DxUCbIL&G_Y1-{9NQ6McGhythU zI5Bkh1{$D?fkZ|d!K{@t9QIqrbcgNuq_X*twZ&zz~Ud*m)hb zq0o+aXMx2+ki`o!@&v?U+a{iPFq8rXoZSepKn+O71Hn&%eF!?_7`WXJcN5=|1_nf1r+7LBxchk1+}wTLd>#Ek3e6)_ zHy7sv0qy~w6!#6X%-z1;uI}J1s*k^yV}LVw<#)!>)7@p`#iJBjpl1Lr&)d;C!2Jxx z%+cGON}&Z5fJ=VP&J-Fgy1>QR$AuCs>Fnv~?sd%F)!Q*3(4PWQcGBEkC{~<$+yg?K z1N}XN&1e))stiXLs?Hvc)O!N`y$^6c9B^@@x=JqTLD?lZW5#xD5aPXSPY{_dy^ z2`DZHQ2*5ahJF37>-vTAf1%K+f2#1m-S_`41V`}QpqV=Ui#|I49ew_;>Mx`DyRD4| zNSv1b(LMj?wDIAnhvG>0)26-Ly??qX*prT)3|A^hj|;jR<22}R0gm3z6k}qrQm~ir z0e5$o1Dg9ZogG~`Zlu09XYRXO;do`5zRrjmn)YU-;`d7c+Fu#NU|9Z>W z(bJVmb92P))o|g6ZjGu7ecZp3cid#*vF^l4`PPH(UHl&QdG^tP5IyVs7^JB8zxrsw z|A1@$N1-H3ADlYIuXpfZK*^pP@4EIGsU4~MF?e|8vHzjipaVsx-2WyZ&fkzn{hv_z zPo3iL^8Z9w{0o4V`J$`ee|#B#^wMEwJS<`-!CoTK;d?`)Yk!R2is1ZrFl6`qit;}x zl>gHx{|^ra%L7+AAwJLb*UfYJ)SsU`He~bhrFc_ZPJ)u{h?z`HzYFp&)?|D}t< z|9Zsw+v7N@gmF~4ewq^O>={Vo1gUfR;2`7ZA0UpVdQ!Yy1KcD!mt4F-RlGOG4=_;u z9Ebj6ubN8x+rBcn-Mi>j_2@>^r0Lt^ld|_=a#4%tE*%@%k`n6d>*!*_Y4fi?(AHnV z?%WU-=j{0(hu01B1Oq4d4m6b`>y7`^;V2sWR8>@+Rg|gDR25})O$x}Cz26ZBl4s*6 znojDDAVD=HV52U3SFn5h9Rqy)?KvKOZ$md%eK*GdBXi3jAAi@~&OY7&;Arg_7~r;> z)1&tOG^)yOjeWaS*WLdf=>A{F)c+6E-TyYG`aAnm90MpW_J2n|TGao%?f(zj;?JtF z|CO2l!>@kXc;!{`(C&wtjd_+Qogw~ha* z*5zS_%{pz*8(LmyJP`rQX6UIMTJpka= ze%bF&XTv$u{-gc=Z7`q#<9ay;JP>8Wb3Y#&w0i#05hd{(^L^ETR4||0z8)fffQeNXH|X`Ue29O!cg)t6{)S8aeGx% zRn>4RY8vVqw&114-d625C3WigJOJMu2 z22mV2Q2M6ED0gqvuTm;DD#Dw1K&AS0ppZ@QjSpN99xeg^vyu$%#F`6+&BD4wmP!lH z&=Y2!70BqkC?cr6GF9XysH(sZUEiDjKK0G+`QfaBG6>%+^i`a_zzgN(-C|k$-WQzf zS2@?CA^-{@DGY(E!=NgFiZtg+1}3fo=H1&$dr+UHP2JKvAC@gZKlPAy=+JEyvrX__ zn;_iWJP-&>PeoTn=dWuONQ|mmK!ERoJ$ueL?hg2gAs8aO_E1hcIXmw04RrE!cMhUB z?E#}^Kp<_8k2B3zMUvB=9863_Zk z?Z&e&Iupnb(AP%Wo~16h*(t7l>^&QAH#2Q0JwK^u(m2B9UQ+qcR{xd3W1#ZF;>hVr zvjpYDt3Zz0v-8s}${(zY_HDg1muKhNE~sC4<`EfXylK2@+l8$9t(lS@wc9TUKk_*ITE%E=G3hySb*r{Q*?E$msg0@27nhE*k6Z8OuSz<v2p^PV0{6v)9^5d$pV8Cj@inRN^TX5M{4XD|jq*4v+E0W4=ASQLxOX@Po%mi|;XS2qk2Tbyk}8m3yx1tVUd#3#z*NK2-Coh!k&>mkOZUvWSC{Xc%gb>gO<2Q2PnL%2 zt;WP0faJ{<`H!LUN85@m_WqH0rPMzFw<3J?>bGW_wdXI7n|77Y<@rfRdrD8el$>wD zzE!6(?`8_$3ac7-^y^fKA<$^5Mlo&QuQoQp}9T(Daa3Qn9FX7T% z-=mE}?>eHx+{Eugt9_s#??0r#S;bLB@NZuSsmDRL`4@M$Zu)%l%jPMAO?QkomU*LSp{88^8d}6$ym^ftoR)TakqM<7YSBY7QFJx)9y%{_w4y zty&^FG{!H&U?$=0u_q-{stq*Vpkt1?jyXk#qdzXpcj>Sar&5lK-m*WNs^NU;rkm9V zgU%ybJ4NC_&IHJ*$Xz@}kDtPZ{Mvh_Hp3Yll;wm?=D=~O57sRy#C5DW>Xv8 z?1XK^7C#$%%@^xuCX&YgP*lS^eUvL0mKP;IdUw`bB~RBRuVFMb^w3%UV%e_eyI!Y1 zyA16qfxZx|?P#lJx8VWQ?bnYFJ{^4YsGN}Cc&hBK{uH~^oF^}-IdbNbkKRYm;_O78 z9gEVJ+vO%s-RazXy$m|jc&XeS`}NZ<4NVW-ws2;g*A9gFwV=oHc}6vFx4+UfxqIk* zoerjY6sCJLI^U-T5m%{Ycm4hTfbW}j`^Y?$TfL}Oy7hLE0|H4KZ#kGyi<;TLyEq7T zn9zPltgb;>4`Z2qP0F&^ULY)GE0sn0Naq`AfhtwH47Rl^e#wrj5li)m0@wu0J2|e*SP@y>{Ho zgSRXCjD1W!ACc(O3l>S^i9^rM0#DpCaM#mRFK67BpF;CWZkwvN_0{={X@>0`TDh!M5nsZ$ekaa=5c1THrSfK=CZ$k{zl+80>2UX zjlgdNek1T3f!_%HM&LIBzY+M2z;6V8Bk&u6-w6Ch;5P!l5%`V3Zv=iL@Ed{O2>eFi zHv+#A_>I7C1b!p%8-d>l{6^q60>2UXjlgdNek1T3f!_%HM&N%6fn{sFOzqD7Mmd0$ zw(t$`*BsuROd#%#0qm>2_G;vKo@fa9IwvKq?)Kb&&1kGBR;o{Bc?XhL7jp-7|7$rb z6y@q*=zMP*0e}F2F;UMlI9q#-by2qYq8#&sC?nJ8?ZF#aK53Hzik~h^WV-XMuU~yh zc6DvI3VE_!TFGM!cT`pu)8lCV;`(&tdh+^mgbHuriOX z464ZMFtas^oS7Ld3x_ZyE|-7MDD4^Ch6*+$t0Gh>fEslZ2A$Waib2h`7&IZ|@el_J zR9FOQNT#4=f#AysF$X!Iu*5s4SZJ~brr>DISOlO%7Yo8LU6BSE9?lhT&ul05STeY6 zESLs#Vp&9d*jGQh9Ar*?{>L?y_DlH8s?AEj(tIlU^hpbxg0ghED;WfDq{- zL@WJC;OBCZOX71mEa-$u!3vt{RbUP40x&U&N>L_A#i+$Hs8|`J7?p3rgM#5Zh(%>E z5(y$JPh6xJv=ifuxhG#`x!?=85@g~_rUVsod}2=XZ>6UYV!P_B8HTKLQzM_ zWI41v6jHQPm3)~Qx=rYcmfB8e1D!jSxc@wq+_1|-^x{zi>W>vWOhr7wvt+@pygrfC zwFLMwn9MvZ`kHD-rE`3faGu`~AS!oKN&}|{{Dgbw$vA9xOqD5m{D<$&Dny;9XdUqMG+DMxq1= z0fNU{#AwNI5eP_gQ4GPV+SiLmp6EihPsl*HUsW|n0!7RQeYCk4qOh^f`|b%3`V2S{DSXrKN4q$XEAuqtK zZklWwnawchiA#}k7TC9ogxaJef74&-rinsj8$m_QqM{4_M4ogqGu42INCiDy$wUO% z>gJu%9@Xdp{ z9O%hTQ98<-Ue${K3T~0bUtf3FX^a)TNK^Z8AdVoO1k_d#twp*gIX~@K*OEsqneZs^55WmnztunxisGFJK}1urcs^5N ztAJ5kmu#V_LkFe=PFco6d%6>!k2#-kJes#y7pCT`7orI**630qDJVsnFv`v(GT{lP zMvky3tdc35Btn&4oQFC*U5xIrcJan{;gvpBZBNE9y^PImnC6*nP$rO(nr_$L69vI9 zsoTSD6)oMKaR-dM2HJIq3Q+jdfplJYL%SM^%Q(pPir9pOUcZ`AWLZ}+)Ud<^(V;YG zB@z86%UYqX{taG%fMZ#^O^#e$_0DG?klYOB#8X?r`W~lrzDlS?Z!yVLHW`IRm$WH3 zduB?l+&CpCx+y@-yVr|MICeoH*OWHt=S@?=5OMNBMRWW}xnV`<6F`!Ldsbuz&Qfk3MX=P~x{AgcggHGc9o%V143 z)UN@JL2d<5!i9}!C_>fWiXx`=a2fXX8B4!9*AwVa6- zE>(!WF>}F`HCpF~OnBLZAvB`#-aN%vd|sD%>Vjsy4e{tv{kX)Iy5`tedf#+OSK>=C zkt(c`!jVd> zo_1&P4Yxuyj8N8Gmy-xtu`DD4$F(Z~Aog5w4QcY>0tjwqeSUn$MDHJqO~IaO*h{9! zwSFNL`$=^-4UIMzBTy)jD48!~MQxo=cS+*fAf;#NREi@Ji4+!6{T`y1U`=}A<@z*f zUSNjjc2l##^{v7NF&FzhooWyNH!?JW#cczHMO#pNnM=jDfnUgZ4qL|z*AF5l}U%qso&NqwQ;-#;2cIFYO?qu}PNr9EcPmqvno$Qs1 z@*XD$ZrkB5lV-sze>MK(?)GX)qDZF49_6U~5=+q@fgo4_YxXen+K0!OTzA@u2`vnw z0cWkLHcOX>A_(_Y?;;tbVeu{(dT(PjN^RsMaW3$Z8JEa>IBM^pr6Jz%sH3txyJh)wl!cY((Ak}7UhJ1Zdb{piX}+yAV16!hf|_~_ z8P(9sr^ifS!1E9}?l$Bo<0sDi`(QO*aHh4FI?ba^s*wu!le?pvqaj;Rb}=r;QTS+XUb^&T64}6w zwmL?Pjz-#;d+dQ}EVR}5%6C9dZ$nO=IhA7?ye%4wZ!dX$(Zt0xDH572lG=Urh3U@8 zib!snmt!DJq%Yo38OIW*3Ntf|$(h`#O6Pe~yRJZTi&X~dPMO=>t>2p)k8fMhru(Ef zjt*X9GDS^m>d4$oK(9&qjrH`V$nHhTf?XoMwa#acku1;^sk%kM@S>jpKii9T7)>Qu zQQM0CKvo9Q<88V)8^H$rLlOXf%{U^ez9n7sPgUF-obdu zwlDTSzPWDI7-#J&fHx!=$yT>?o|$S0h%kUcBqYIS-pMRo2`4+Is} zviDOK(mZo^coR*(mEdrBOb}kI;Ig|7s=hB7h9XG3A-3<1kE3~889vG@ zEg?ockO|~r)e;z(*9&*8Z(=OB;fVXOyv9-b@$mO?ND6)=}HI}itm;Dj*yPNk+g zdK86tKt;P`M})gQk4@yz2U;#zr9bYG5h~dMpAY_nKIf~OwB^YC3d;@;I~cM1`QjLD z#>|Ei(=PGB8y>|N7Qu~nkBkT)w(W93IEg|(YlcRhn%+!=K8z|09uIPYUW`pHP<^T? zhz^OF?CHi5ne$JZ_S`^ECuEk47@!y=_F$m zdVN!^9SSgk^AJ+N`>tL(Zit;n~_e1bTgAejQq>G2Qq+!&2^7wW3Y?QN)>K28+p?W ztuz*xcmn(?Yn$rDGLI`f5R9y_-<83D*f@WWuoDgt*}dN980*s!>(ZBBk&!5E+}Z4@ zz|kISX5xrt{Z*Ej_qEIEFj!h57MuR3BjGJ~+rw2iNh!+Ditm}-Rc4Nb zMoC63PUa3@Qo-sL+8#VZR(8hAE=c&r*ckgo)Ii=X41qjyDLNBX&Oi$z{8oP)yl3q) z$4jcClK-fr?G9p%+gIkWd4MbpScXQrD6^?Mo8`20=HOnd zXb(Y6KS1NW?L^~b@g+^mH7%LsG>AF9Sy!4mc#!DQTRXJ7dqLZoPVo+cS(0ha7{`i| zL=$E$ds-W*h#Atk{yF6CkEP0pwQtkXQ6b}{2t(<*@@hUbuIXdg_;9+lqMmJOO|5)t zb$6J3fi(H^%EA^79R4cZRqy6v7TlBjt)(E-T33#L!|lE7&h$Fpq36r3#@&P6)$nJ(iuV ziO<9)ygPcRPqU)7GpUIFL&iom1n9tWaO@qwA4hZfVk*D zQ>Pe!5_%mn&K-Qm&^pxeVbP#JcTjBz{h{uUZ&UrNqbJT|9ge;g?ZBr($jp-;4s@(e z1%ZUWtkjh8sj3GsZextngU-)c?2l6~S!c)FxNagOc;`a&EvJu)4jYD2MF9jF)zIme zSmru_wfao)g5zdB46Qw@IW)BY#zjP=9Dw)e^(rma7^T@QNUgmuw_iAoi8dE)$zg}- z2nL>Td1*hszIdy@f4f0arRw;uQrkBzk`h~VN`#O)y)sn{rp{)5re(5NT`CbFoz~Uf&@90%`KO3tHi2abnOmnx22lD4omR$^zh)OWwsYSO@*fkTU(x>5{NZQ=-j zA~SPKkbOPTh)EFm9JXH~Pd!%|HZfklX?a_L5kT-0Hu0s+=sc@i(Qws+&E!ix-a$l^ zl*DHm7vgGf&gsmZIB|reaMnSBS|0zdS!s3p6NWs$uwq8+hLSS3kfWavWd?fshCWb9 z7c(<0xbb^kTn4L8SR(z#hd!@ubpgluf~79cQ*0b&6JzFt@zrV@;|Kjqj!4Iu0i82e z2AkwDsR6H8B}b!ZS$aq(k5l6O@eOM~)|yL4>crzxQW{E#nwZkpn*H%F={{_EWz}TW zWv^&%xEaefA#*5uSPNa!HBySvyO|erC4LZ#e=p!>@Z<5v|b(=zUvrSY}auW z#c$P#W+NjrH|-T(*IliflBZ14%NNAS6uS@2Db00s`zS%XN2ZB*QZXiCecFCT+xEbojBu~u(6@ah_ly?GJJxFRC(FkJ#~8`VD=Ph~c3QTVLdK{3T*P>h9-+{Vv8zlq+^(d9%>6QaxAtW|hO|9M_Olz} zW(X7)^wams?b~UAVRqk`&Y;&1vWF+fnzG;AsTc=Zf^5JzzmK=iyWLm#V&q9`-&?aY^qo*ZlUDc zJ*$Vqiwk#+hKiwpm>$89R7$ZjHxv2rN|7Qj3#HVk^7*c|#Xdf-9w72|Va_Xol-t%@ z8%Q{HQetIW8M>`fUqH&lDRuoE9i|9{KE6`waekPj*iZWwe{7@@^M7-g<<2|Zc%q? zr(d53=E{kh-CjH3fDL{7zRV#QhCnPPinm0J<@hDm&bXDco$YcSFSGrAd=Ibh6P)9< z8N8F4$373#8AUmx6I>2lYVg7!26&w2Rb=Ig30)d@sK>5!ltEH}B~@(~C)OtovgMIl)S-CW&X3@&Sk{A>5nkz^u-xyY?-;6z-L! z13HiEOI3Ko!tWVJk&GQGM=j&vylQy>JQZZOF2O+&VcX4bx?%XL8YS1d0{Ytv3r*@5 zG^es{RGJ#NaR%}UUzdHg?5D!UO%+D?u+rCftwJ$_tKVt{PYA!RoBCu1E?V%S@;lu6 zf4u6c=y%Q5uRTU4WNPuUT0rDh)ofh{;U{LJTDutHb>}iO^RP+_$VF^Rk#dBZ#>9vNj<8zq&RAi=iQv18@LN<*6>-}8B z+96wy;mM}BVexHbdLPg0A3s9FCJjwXu#DP2u38Sh*U?TnT`oB>ZhsG>Gkss-5pLU~ z)zFBDusm~7%fYa5aK_{%c8!@~PCr8WzBFV{uPpkQdTn>B*a>S~YG%nro55_nTz*sr zskUCd){FUYpacbh`ORv{oY>w@jFK2z^@})@Cb~5kZDF-s0yr?;tN``#3W$bG9*-q)op~Vx0DJh)fqQG`TmIPzaGbbi46tx9zb)Q7fXxeP~Zx zw*bo`WRWYbA~5hnm^$YB_p?Iapc{Er#n-S1AmS&~$U99KVCveGW_}9q@?ACK zV#t1oZM$aEo)h$Xp8Sf$`5Ffcr;*8Y17`F0)vqP+QU`|xZW65c)Kqp$5Wz5V|32!i znh(=%qrJ*p$f$Qq2O`FN6rNNd$k9@hqGm92`01;G?gNVp;fIbsy>N=7kuL);D}`uHH3K*X&NUzroAluJ^OupR{fYLuEuzjv^Yv zH1;6AH3-#OH#&^0s$FW4R74*#7`<>kQ$kpXw_$ua%`;qMRI09CxuLFbFC(2N6i!N{ZE5X%-#N4r5;1w_n|NDRz2*MqSYfv&GB?d|7s1OX>GkDz_6XUCirfT)T;GSqqsLVf6ah*%i2xMc8v zP~Cy7~$|-84sYV%O#ZEw_W8_P zfB^#a>~qA5&-$Bd?DuOE(Ri|z)j1RJ_o_uL5(p$&I}gGO)g-#I;pL9W-3qjE?B-iZ zV}Q#FAI*w-l$DUteW}^X#RaeD@jBHsVDyrNnqy5>*wVq}k=%^O*F72%un;90H9SU0 zkWE5&n_Rz)==^L;Cy?kwZWVghIg-O=QMq{Tw^c|@VMCwU$q%Q7HGE3?^RtOK&prLV7Le952bWM4 zpq=oN#&-UCM*iilYe7fSiJ=Cvg$63A?~ko0jIL@^_8!6hJH;ds zA#qgRSzA=1bB3}g^73BYLg%rqszF}uO}%yH9Z+srCBhursD{#@;OrzMghHd7Tx<;A z#PX-(duRH(pwxHW2kfTqTvk^)Zz?l8NuLQ!8e9Fkwh(?{HZv7(o&T+jk@3ni`g)yw zx1)ER4V^0TT-A`6<(0bWo#@R13Zqd5*ryyMmN~tp(*E9$rrG4HIa4d<2amsWGir*y zaYnD>3$fkHki_UBF$9fNg;V$o_a={q#&)y&jWX0AqE)1fp6|9f5kJ1|nQYchMW>D= z#*sB~gJWh!U%~Z_kvfP4tXT^2A*0SL%Ch-Yq*JlFeS2G3l(=KvzN#HlRf9rg(#bQ& z^NXWS1ShH9*}?TCZ>uw+>O{?u1!N-pTIU(s5rRY8-k|NTufY>jP$UCdrMy|1WxEpd zk<5q&bkVu^W46|EpOX9NGKR4*z4YVG)zhN3;t=cS=9KjPQ+_+VQz<47JYM07!tZ3g z8e?JZuvMo<`wCJkLaH?6Pq&P-Yn5$15Qh1*K2<|QvVj;@7-3~-Wd(ry4lDBQp-HZK zhNNdT&np<*2IhUT&*aagRIf3Q$yw00oKxw?{CI`G)|uM@DO&)q{nU-zNwLKU?ceu>8^y&;D#mGC7Das#`t)fgq|Ej_B9hJF-T;nC^7uJtcI>vBrE)2?pG zZ7o|>g`pVz$TN3m=}hRvZDiJhysS`u+x$FZaKBpUw3Vm_26D`qP^nHs7D1D{y7G6d z_4vMC4qu<`M9!WH(5;4v^~f>^+DQ}XZD-fkjMa@#CxtGu8*&OzM5g{VQw5rhF_Me85Ej~ zoGM6)zgW?gL4^x+F_RCdTsskokhz0{?-$a(!xkeq_Kw`UdxQ`kv75ThStM;nxqZu% zTKgd*qJaWVLInamt`{9FjZhvZ-nzWc(ISaNbGB0I@)rwS7#u!X@5R$r5P0a=)0iDm zIsGT-k9#Uc3qp@m?btOfDL0d)wD?*dr#~FMXE#pwAu>&Gmk-PQVW2{=4w6U1ZyN!* zuap=arzeo;{I;GbT1eVnBsAQ9&Uwdh?A+$Bu9pK?q%>}$ImSF^x?UME>s7a`dElZ; z10CT+IHa~P(;J`Syz7TQOF81Q5F1nq4rH{}VR(kRs^ch>3n;7?#(X?Kp ztvh>Qno~}W@*!U@5Ep$P#yx)^vj3*%;m1xB1xuv)#12@rMePub=#@Qb@zQpBSn1PU zA1~9FaO^p3IVvs-QQ;L0NR;pOHZvc}5{uz}Ht2C@e*MS#k8uf$K%zmD9t26zbAdk^ zJ0YIx*37RabmN3a3nKb!)4|~r1|jXL!x~#1S%=}Uu9kb__mWjH(3HG*Cgc$DU{210 zHqvc&#Z7?yRc|s%LQv*vLregh|5Rj&bgWO;-mQ> z#n{%-jA-#Hl#IdE&Tvw~@HUKwmrdi1mT<|TxmQ~EbbXwXqKZm}C&MzY)?Frxax;2r zmYrT#R#tlP$3suy`BA+}Z&m|cUiVMU&CT7zIhU{T1i@S!gM8kQ1Tpj7MZJRulPY_{ z`fa$K7vsC2Vy|Xl@4T^Dp5e)%a2u26cOs zUSEruNUac3wGxUugB~ceWjEteMN98|-`nveF#y`K1EXjvbeR8}Ox>HA z7~XbR)CIXbEEK`}KJ3&$jbg7y-Jr2$?b;f9-BJ=`X`WF#Lf$6CxKU$8ws}bOP-Yle zC6S{U{oyw+goD4VRE&17Qd*J=mu6|3FyDT3kW%l<4~H7&s|b6u#Q3SAGB>yt$EU+D zRFZaLpz#3-ZKg*Zix!zmu=g$FLpw`dRV6}7mr%zH&f#sYC}XJMe_qOME0c9$b(G9J{W5#KS-j?zgV9>d8^! zsjp7d3ykkJ0mSKC=mhMS+JmQ>-Pn9lXWgi5yHhUNJMpL37f6+FvZoJpG%jCT7~Gv~ z;+a_rN7oju+FAH!U$EBIy>~m_@@{`6-H!WZZKV$H(?O{zzsf(vqH<4>Ly7HmZ2*yt ziW(ZKyr-*+2w602zr3aYW#cmKq}uc=nR|EbJSu#76dVoPi#^{oj7wu?E9=JgAu@O% zI@QnFfr|UQE}(F`u#gX>$*fW5pc%UN5t+9`L%w?iUEBY-*+V&~)A!UsmM_<-GA!}4 zr%mR|9{$rcN!C%p3QdP_=bJ4{)o*2}i}%1&Gs z-184wmB*6B#f-1Ulrx3$n1rGb`xe2dVV3PQ{??@S@a+Tp!ZYUbca09Z3_k6w3rlpK zi%w+gT&*$1n*dIiL)O%FpibpWYoU2hvYOqUUv~xn=_~Jq9v@^`h-WqqiXpoc+Q+tw zP@a!$E&UpQFl%nqaHmTGQw4ukvU9}lZv889=RWZ5^2T^(lx3Gt1ESbd3xCE!?34%E zk1|3#?E`z+u;(6qJ9}-D5XC#+*n!r3Li&`YS96GVd7({WMN56K<;Tmz-?EQWek=`D z=Jdxhx8Ei^IFfbjxSa#oFX@V2B?U7_ElqgkCUTDW<};6`@>lOvr%c%RU%HRCD5g)gsDL@ zg4dtT2xtislqsBU`mqwRTCY>jGK6Ac$`s7WdVE32M5vQLd$GOb^MO(nCb#Xj6sic7 zM(I<&fb4Fuj55erp@lUtvxaS*cOaL$xV>1m{Ja_BLN?q$&CSlfsN>RN6H;-9QRTIf z4Rf2xr#=YANh#*G&4_o}T@zKv5KDaRU}~F5>mOPv>255$Jv;2Gg?Z4EyZ{u!>nS#R zq!vL+Xn!2p)U?~f8&l@%l!V269QG;)5b{o<@`6`s%c^H%;d3u5-;7YbwaO5w;%X(W z{cj#)h{YF6puBaNd|ZD{=@?zoNaD-63$Lno_8Bs)Q`#j{qI%xJTFR9rR(E{*y!CQZ zeI^23T0R;p)u}p1Mr$ze4L zk^WE(HM~nl4^h^z+Lxr6ba(koKe#hA76sT`3%urPFg8ui$9K&Pvz&LNB9Jx!A>Oi% z?YF~&b-Rb4#C}roA&JSjvuu@KY=pD6LMxcPYx1@TZ?StUkdIM&(D~j&SM!b zczt!u!pI}Tw=(cfs2u*qu%SSrK$KZ$-9sY1|BF%^hN{3uPzZfc2Bv5=tMh$kI{bnn z*JM*+LU*weH0z9FkWjd_>1r18MJF@98uePz(3GT;zJD{Z3dlg4=b{dDc+8YmZE~@(jVjha z_xCWLCV$xeAWnUkO-Q1nnvxjAZXsN9kHZ;uJYY(sTJ)&!lN*Olmqdw1TT~Rzc9^E8 zQM?wd6=REdTnvICI9hOqjn?+_F7hrYHKCZ!JA2&CcyGfL&&Bcc-CxFhB$|H^LlMFf z>2?{`iwesS;s1xDbMa@g|Nr=CY7X5tr%18QF{zxX+u5*++aZ%fIUk!jBA?-cwDgC5$Q+=7 zBld~4H{Beett}c>d9^wk^#T1V_0|b%#9AxQ&rs!$ALlo%4Ohk8`zcy*QFV~10)$F7 zz!eW&nb!~>JfZJam`dM}{iv9mNsR2C4Hllymq6Crn}-3Nr}GQQ zoGy-U-DNigKl9HA4aS4fJEK|(3NDIrSzh9jETVm&G|3n5C!IPY9{eYYH2c1lp7rSao@G>lt#x9D}R3%uoR#b+n<7LuC49vCRbO~L{QHgA)|v(W!CE3S3dJ4b z=lvam2R~1_L<){?7vwnfmM-X<&$sQ|m;$0L&69Qd;XVXpOkhQ4z3*+vhnDA5f?sFj zhJi<$e%f6ock_HnvLADG)jt&Cnupzb^>>>SS3-KZxbN}+!(7soUhue_@A6d48)H9pM7rVe+ZXe(&AwmP)%h~+{-|qkZ@!GL=3Ev&b4~Pnr!RpN)Pbw;AXR|9U8-} zOaUPCPaf53uBWJsrGvfgqqr4_{ZdfqK_|OmxlZNnhqej5oC#Uj)p(dtWd5NF9f#2M zl04K(f%hoF=`FlJg2i#X+o}w2ohZSC!%eGiobDCUZmvF5Q*ra)+PrStkz+M_i&BkY z?&L%$Shr*St*OkM0N8j$LBXK`KI6M3R{(;N&}*!ywY)T{BP#E#tx-B3;?oS+yWYqx z5Z`%C^kRm(7sBT=6N0pE=H|4VaY872n5}x^Wx2Bda}lfs73F+VhG?lts;iFn z3z8cC?M@rg|0@d^KGoLh>FL?`ZE?M;-2%^U9o7d$jtQ}Ck+)`=(M5Xl)ut#jpoZC3 zhV(0P4#Q~8>M|uF8JbI+V{c3m0^^29`@>0k8!DM zV-B0Gm6J|Dd{cG=2#87-lEmahPYz-O6~~$l-c&m}AX`G@<^@39ml?UaZtQ?nnE19H zU|hYXEUPVKH(Cnwi*VbJwr4PL?ZO544e;@OfrRVBYt&E7ahVV!~=>Z07 zysa55byJ*!i)}fkh+tsbjcHQ+E2E2C+dkZ9f51Z2EXcG9nV)Cwgrc=zv`;G>TI@b1 zBtKQrJ45)nyws}yI82oP`@MP$7=OR5_N2-q@&e7J1bYkda?xndb;I7XEC9#lSNVR^{w{i&(`Am&NPj}j!_l@C^@KT2`d6#Fo4`foliv-J;+g=*WzF~eMqX}+2mTRX*}TMpOhdrjj}GntrT#&}%xZM?g*4&fo}3@?4If$JLX}PJazs<`BIyQtJL0T+U-2Bx#lcdnQ6|l@ zq4!OiM2(iCr4YKVuENF-T^??w8Hs{u3z}CK2n&OsSv&94b0`EN1cH3X(DLqKoT&7d z5T4=qD2lnFvqyyoF^6#Xe>BujS#fWN@)t&)95CqrO}i{jaFe=);q1Hi#LH5Sie8R6 zhs(V8_#6pR4!;-_**Y=Po^GWmftWm$^jk|pB6LuC(Nt+Vdvw->T@&jJbB#($bScU_ z%oHb;;UDILZ+2;FUuJrX;0Aj{&hvruhnm3KM4^uDiPzfD(-{(cgyyPNT{6H6j!}R= z{n>rh$y}OWrK%}0i$UM_;O%iuVz|Wpz3qd2+2=yYRzaBNEawOzZ8Sg+^5TgHB?byl z;c(B?28NwZ0aS|8B1%@Rk7@9ak}OGSGF{fe zQnQ=d7Wl-|6TA>?>(AcKB^iojlxUB-sPmrM(ugiDBqRg#X4?`--{o>9gC3gj%!_vA zf>eSlq(Ejn>O|73zw1lq$-7Zl3Q@1K$T^p#QB_9y*s=O(O91CZ)CjD9opW`}&`^~h)=@XeQy zUTJK?w|UgS?L-Qz)Xt@3Nt6BW7=2zNhxDC7j#JQx>FA; z=GWQ|@(Si|2 zq3eF}b!wEZt=^<6%G7yVQpl^^RUnSTJzR6oI|T<%f4DvH?~4vV9&s0935LJ;?Ur0a zODpR=_+e%kA2#S0VD1E1Ogs^YdLJh|tlgxM++}iUfDly2C#lT;FgrBzh;x9ZhVV#2 zDOns`UuuWuNAx^N1qqakZAO$6Y?Qnl%>HmVvz4vQNuHZoHn`7EJxklc4(OG;igNVP zR1w+vf5)5e6NQ_;cZPVa$)?83hQ6~QG)CjCO^(OLr?P)oK%PRiUKrY^3eB|ZDFsOF z{<&tVM9Q%^`TVyV64Z-(m(zFqc<^%(b3)CaX)SA{bJ5rPwjD3C2VtJk0tXdWmfil> zPsgWT8P+`NX?*qOWLv)wsNyF545Eq#4T#}CF^~FU?}C@0 zcUz(9{1V=ZC6Q4WqH$pWO3`Ue-H^M=Iz7|B3L4gm0a5RYTtZO24?36PqE$asbGAn( z(qii{3G&vZ=&VHOTpQ1p;&903TWbW-8ovB|pfzKVaB@lqJVu*wmz7%YK|D;ZxDR-9 z#|cM@bY~LEz~guDG6wSJK=^yFlW{h@E(R~1pu-S~5qqa^E*#RcR$aqf z*9N$Ngwh(D`A4IYz-Cd<(e6-`Lmn;t=Hfq)w1oh*v6lBn0xQ}}(gX7{G#k(f8A(_L z9=u^t@ z2Ra%APXnzQ(+)uG+@R3;rs&&UWb+&9oNKoJ zo!ThN^l0Z=pG)}(JtOD*{%}42{*;zzA~4TV&#vw7IxQIdr_COc@zP^Td;iQAZZ$6Z zinWzAgLB^YVzzG0E;t1xv`2`2(!Pvi81zSHTw)3fceI9Iaqzmkp&DfSMh_@;hn=Nn zlfJ+Uig+iR7ns4<=N1KiMx8*1`rN%lT$c%m7J1*-xfUgtP)G84itOcA-k=RB`uXT> zvRV#0Oq`u={@k(!TraXzjc|6ur$Z|Xw4ngf4bxK}|Esat+>gf=bPh!4G|MO0N}L5K zTj2;`pF4<}@FbCNp$5{>i2}X?!6s91`o`WhAR=>KT2hyB7$jwh@c+3%(sofPG(ePL zThR0&#Hd-wOBZ9_aRwj^nYKYBhqDc=UXk4qTIP6)J!*#qh|At&wUeqX`AEpBz_%~6 z@I|VM_r1Ig>z~;8%>ns=dRI-30F0|@J8d0uGja{TLev_Oh-}swVL4_95R)$w4?}n8^F8Vmk7L^CE~3`WgoTE#6k3G zIeb%kV1ILbU^M6F!S0!gA)Og$#Y#T<$t?NSCKAGL3aBNl_E@vW;{Bm1jCs;)Tg|VK zZYaOu!y}T)W4AU{sr<_7(X;j+JSi21BBZ90F@OvNkV(5_C;r-|uehYw$%FG!&^|$U z)_Zh7W3xJimhi8(nLxQ;-mWoyyd7o^xFt4RLyFdEZwS0`<2y5>tiov${^EwtOhRB@ zP$exQajlhaY{z^~B_5ylhtt|JxkwhErFm&uHJk#Ix(L)IyH3wGD?+@+0pTF@(wtqI_teKyL8oM+F0KFFkCR> z-l0eCnGrPZ4U*u3RPB9;%K|YqYUiqV>OIxYE~Ib&k>#wDHGgW2cdorn?Yq`DMsUbW zN_>?jSl;x@JFKWv&AF-O2gS`qEu;(LppZtY0SDtvX`3ZBAiQY#9EU#3)4iNppu%EA z6E>Z(T1zs&)E?WUP-Ui6W*DuH3`$72pxinU;_h=|Ag1G(0YKBoFa1~d34jNAJuf%W z^U5u-A4JIU)BLBOrK9}01v=Sni?@HT*Jf4V@W8(Z+$G(^WAm%tG(i@)i zS|X>iG4epC;DC|y4Z`tOg8_%Jvzd%*;!B_7&i~&rWICxbQrY zm-RE*a&uI;P48{2u?Wo^{|GR#a;0u-^j$K8xzHu>NMQUlEx{qI$E?PGy(T59Nl3pk0fA1f71K?TdJBkSa$&Ya*Og(c;d~OKG zIGauv6{|^asPDX(CKO}9pKXDW5rciP2%@R*DFez8O4@A#0I|1CLBB-m>&D(kW|pf* zp6PSMNUpmEoQ2n+V*{jMU`b+!jFU3-pgcLk=`7Q8fZ_H4KPIf9MwGj9kLL=MtE}G@ z1CPR~b!h-Wh5YmF(e%|H^Yr_r4e9H;)?vd~9hetgu^LpvQ2usj zL45z&q(0P;c&!W`$sYVMrBJE%;?mQ!4c^Pq_IE^OM$-HPujXjYQci+eX)+loPO*Se zh0H&Iu{~^3cH-w~DS_}|oO)ii%OAXBoWK=ycl9BZ!aIJdj$O@(CGW*NP;LV6EZjP> znPxh6obN}gYbrn}t>JdpmR!$^8V#9U@2W6;t1lBWLkdEdayhJulwL^aMr)(vsRd#8qGEl%{)a`WdmX}!0dw2m+K-%)U z9CO=8RU4j70*MFXYwGCR5KLY&?xLj`<0WL{_LH_~m=_9tkJnAUZ?zZAo)UyMH>VTN31Z<+5Qcx0t3Ww6&m(*168S6kvH)e$8k0|xk zUtpis&i;7`)Qq6jI6g|h1c2SIPQZw+Vrg8V(_+Asdw=$7+%fdgBhb%!4o7384kP$G zmO`eq4sGn_SSJYL8z%HPtoVSo{~|BI(n)NU@N|h!r?GY`dr@+mEUC$OX27xoE-g8g zdS8-&#|3n~`mgk#yxM%365Xn>X0g6r$>z)A!IyrtRkJfs%zIU>^_wOuRnTsGvdzJc zs1kIruj)npl{3xMcH}vAt9WDO<}+gnVUW;H&;8^FA^a60?3- z?89Oa+m+b*jP#~rq`U>6_7zF!Kf=9^<>tIH!Z%ntD*1iZ5RCwm?6o2P-0w6gIN17W z4>uoP(mUDnnvzSYpBeaPN5I=8!#c#{=Ju2eutfg-G=2rwE zY7+cxGwGQ z@@fqaTX$^EJ-U0Xv@{0&Bvq`e!X=3Po|YqVw9`-+Cfkk-n_B7-{H&dOG51Wd;M~Pt zzvC20Mv>8dHOy2;oD~f5ZE0zVyZV?!!&3Uuoz{oph9iSH&OIC!QLNE`oOmjx zwfg!1Fjhy%m434-|M&O6?5n?_*EP$BtZg_F zk#7xg!Z*NQV`p>18ihO=JiRWJwj7#Ct3cDu@wKe@Jea2DJxf0*4F1d#h4e)fMy7$q zu9(7hSW8o)S*s4r`O>F)u}+fqPfM^pyeN?R*1>KgA>xFLAi<(&?YXa?3$TT?h*eOK z1Q5GARm8J@y7jcA%~Vf;Hdy*vC2JZ?MUi*3hBs8-tpnAshSVM9>MfwTGO^$0Mq7!h z$ZVJDL#A6%YBB)gvru_Jy6KaBQf_xuK4~|)uP{M40*$GEPV$}$wF|?gIz!qZd^oii zSqB|Qe#Aje3;Xk`!$1XP_%W%9n%E2e65topr3I3nycKh<@W^fqGB^?z<+_RtFeZwv$SBEYfS=MQ=?oyB}Z8oB;27G@n{KiNybZ zQwb50a9KR&n19uy03vY_2SV5?K|srUal@1MNJZNdk2(u&g-g)^CO;;vNac3BENf#D|3)^5ky>@Qz-2E(v&$8Z|eq6 zG!C;_4s`Fzo?w&=oTpuTdYy6PyWn^804q4|v-T?wGX8g^5#24}$l~aKy&)H-If04U z^G;*B`Zp~RFff=uK+z{{Mwd1)42m$ccX;^ABB&z%A|*8Y#uh(G__o0B(mQQ&IxNhm3OeatHkVfcqo^*XTX^X z{d@5pNZ$_PW3{62h!5tRANVjPI1luD;56gS&b9fd;{89^S6#OxJISJH8si=>0BPWS ziFbymsv2pfG3oP7YKPL?(4lSbOM4$8z1Tb(wDXQDF( zv32u7sfxg1y>h}Jb{6-v0@O0Ye_v~0m3xuXJ7g;Hv9rE*Z*MQUJ<2Zl5~xm|!L=ad zP~4o`Cc3ga&oT8zROtD9_RRT|mtWFOW&Lj=Ea|IoDCNudexx)6knItd+rs zV4V8Jxa$uzqi%8h_*HMY&l?)5otAz6HoymPjWc%jm|pLwX}ATNHWyH<4RU7MMccbw^(PJ-`+lHH`gQCa2u?_%nbb5VTHH-(xc&Ql@!! z8fs9pxKHD?PW}ju7p@2w^pl2MRfU3%ZJunm^Z>&9t?_|_ogUhY6nnXrO=-<<9uQg* zcUpgdFU`fxcrHD@N{f3D>xJ)68ifqjTdqFzDtuNHH%T*ut`p0YPgE6larU-86%4ioLyuE(|OI&+toXU)E5?lsy zc8)jvE^!y=)vy8wA!o_#^Utg`=G~O1c%ozB_>Xt^62afU&5UlZa%Nu2?HjyNsr<9$aoLyDHvYj8895;|BXZTMAu^`ESv8GV)7em)Bx|}czJ7h)7sjc zZO$!irp@g{liHPi#V7A6_&!bPe2CFBFVXo*jnW!fmlMQ9r^-V8mBjU{pI8FYQj=4a zY)|_p1;|=k#apg6wFie~o_OS7g`;+_IcSCZL7shgY0KMsx5iKVCHAOy5NM_8uI^L- z_xZO9?H{$!dcAO67h>sB&s8b!p|gj)@+j(HiBqTFN29MhvFWLqHdNEYf(4xI^Pc41 z_~5_d1OA+4kw@uQV~Nc%ZDHfjwN)aX9mY720aA6BONrvGDaHYnf% zF8Gk3pct8uq8)P1d%Iq|zht%K7El#$3H zmFlmDzTSU&%*kKy;@yk$JTosR(K*1Jwt9xwbv>=6<^m+a4y5gK2Ydgew~5dw#m zG<^Lm`*-*5X7bv8=XZx%W@H@jxm}Y@1Mbzqaf)yS|3`W~WYH##jG{G45*AyqyZOxj zk(T)AHT_D7N9o20rkbe~=cqmD{o`j6hm<7_-|!vwQ501HBsfR%A*J)PBnc1w*Um51 zt5f|jm96xBzTQm(Ncc#WO>Uz?cDYPz2q)S!0N-8@Xu`Kb1*VV`zxJqrvnC8r0P+n=bgmDwL^&td`)Gtk7@T-kZ6$|xOk21?4K^`0TB+g7 zi2#`T!>BCob0Y#!pk&l-ASJ=pJRpAORT{us(1oT3#{>Bx0`&FWv`+{_w**h&jYda+ z)I~c?Jl5EJnjYb)56$59Dfp$%GI1dZ;za0*{hz<$+vhIdS09c(NwU+9%%Qn)zcjr) zq6)?V;1;_*kV00ioWSKcn z@|!wj4QGC7v;F2FAoH@Nk;3V}Klipb;}nh&&H1$Nsz~IUbUhE*vo_=m%DoppRg&sv z>8E+q`w`(_bBXQgnY8vf*Fj)R_l(vH`8z_3)8x(1Jq#AWsesJbNHIF*oCboZ1gtMx zdq$#OOTQVLDHa3^=8hdj;x}Mv&)c{j*Oj0RUR}?)%QFbyF*2+P#5T)LC}?p z>B}px<>o=c9HXAQLLl?b)Yrl^=Fa% z+W@&M$csVjlXH;R8rF{7{P0Wz6!us8JZ`63d~P+i;M_wgbX>=dg=MbB%$fU}GBS~$ z<8MpYFEa4(ER8UfR6?J1wT(rWFWN=$luu zLi9~s0C&3P!^R&7li^j4)veeyY$hKlk_GtO3M*qcqtyQasVOe9{N4N7qW_uom~(Ko zMAct_hty-Y4v_%D1ZsuuPUcyRHGsN(P`m`PM)CJ!&wZ+hFB zXS14djanIm(&gB@3Xg_rGX1lG!HaXGBf?6CU0bY|#wLo+om~k+SDYRNn!_B-cA`7Z z3r^})IZkvkB5v>YB$rg_Pop)546G~_U-vvdSn0We%V>ym_P2~L{645g9UAJ)6tpra zx`7Y;R4hCkCF0o+cX--I$j`I6N#Bj%=6Bt+!bfN7lrr-(8Z8!zBa2T8a<27>Sn@2Y z1iDUG?fz<%r-}V^SNzLM+WLpPmu$y8$eVNN&dR3S6JkS;=dN5;>HGWJ^FY@0;&`X6 z{MEsm5@Qp*fUZF9baC!RMfhv(w)f{O}Yy6u0Zh0BY45_1Y zM{FcL62yfS_`MN+07zy8pqgz8R8)kDo&(Ox*^|jvYboymjp)lYt&9&P_@2i#rqn2U zWekc1d1$FFC@4UTkQ;DwN3mNkw67^&VurLFNmD?ATrOSU;Avzsn~t@_6SgK4=6fb0 zPB7~PmAcElG!p>U;`Ahgh~V;^N3P~CA4~eJ#$P|i*UC~mB-or=rY=Ux-uyh? z)ZGe^L8aQG&tNQwwbxqLS~28^t-+AG-3LhTv@PYAZ}=Qt6b>m6@s=}fIvxQioU8hu z@co@0*&CYA!BP}hj~Y%=vNs%QGpIJa`PxkDqBt(_Qi!z~uy*;;oyW7@|bbm6` zqC$GxS4Mg4yH~=b_Ww3cOe}l2r^5IIAf3|*@@d|Sp^z2c_{}ZxP^ztMnUJIKjTJV_ zjT)M>jSQ{XrK?$pls~^of_xBwzzf5#QN`#P!7l;WEpAUvbeUo8vzwxjop27YcT`7c zY5$Zd=5q@>LDCGhtyk9T`v3%X1|B-s{H)_5Ez=34?t~(U5KDCnK#(FG(f<)_tZLwn zj?nJ)_WLzA6#e!p*G`NPKfr~A(n9vyWC;(YK_wf4rfF@z_){?da> ziI(pP>E?xe^rf;HL`XuN?}>E}dvzzl^>w+iTbX8g9G0ol#~qzsM8&4z^fj>fT+btt z@q4}BSLs&^#l-ofK6XC~{D(LnYx(xtro&qW9H|G4=ix>1pxB?vn+bA)zN5#@#bgQKlcpA@`t!sst zxo@sf4LJP0Y_?|tJI)T{me+e?xPLj=Rm4Idm%QpjB+-y!4s++(ylP{@h?-xp9M1=7 zcD(W&pJLAoEE784ZRnIgl3o}gCAc3;yJRIbx{@LMypcHqfG+=1^cu#(!1^= zC2U3H&yemwD#gIZS1F zx{^5oRJ)MlJjrmYJK8rDkRq0cK-de0_joKsv0FJ=gcQezPCyqqDE{Igeh}oa-d|Ip zwb!wp(xtBOE{V?c?WXb*-R1hKdp+qt%~%DtVsLiWHdH@?Aq~?fh3JvfrLqFxUGF5}pB6nMLn>F!FCA9np|jM@3Ai5t zzN&yJx2mIRa#3k#EPNbn^&`WkD&&5u1}I86D@ioyz+Kv2U*|UH@W5abpU$+oAIqz@ z>u%E${<0g-9~%_dm)Rq(nxB`N`fcigSc#<>3OcHdyK@gB1Sz42TJd6P{^wq#^t%k^ zxjqc|RHXQ@Da2i7vcJQmSvPA?^X|ai!NE%+s(rnQI}L#Nln<{nA@rQ$%A7%+O`9-V zd5M?LcB3C1hT!}fUnTATLN=)O)p+r%@#CL#?wq&)7vXE!J_2liAM&qSW;FCs!QYHd zCz2f^U3*jJGRmqPAO+F*6=jRht#+xgT`H8^j>qzSSvt@F>qpE|XJ)ux;?`(El#IbU zhGu1GkG7rFRTNIN^=U>urD}V_z)GK^d+xjKeX$o!Bl852m^Xj7JpXoe2@9SMjOCN` zLj)jXpAbS4*f;CWpr|71xq_CcR$mxIk|5MfYlZzK;9U}~6fjT%7#(EsYW7xr%3I`# z(6llduH#J}0a?QSg1Ou(Xbv}hExoiKrvL%u-_uJ*Y(}+Qt%AWc1LNc4@1~1s!uTe1 zRRoqt3+7(dr}I~^BsWCs7kiBDe7sN0JZcmJ7?u751PLhGN^m^pF8==b`vjP%6yhU( zm^BQaH#jU#jxym{3(pE(q<6-w6tMQ7{c$s@*H+ZdG=-cQWyL4-y5P#H2oftcpZCIt zYoO+$fdJwTcfYH32iy!QK=t|-W@&;TFoqBvk-{U4c)Zv?=wz)=I<*bNiL@W3E=*c; z|6HR4`?O6k^e#b_frGUZg`JjG2%*XbDPz5(;g=_##S~3Fbe*nSZJekbN~mLRN=l+M zOiGjf%$ww|t-X*xXSH)pQgE=MdqNo+EVNz#mR@k}H64^Btzlouj{++jw46pd)#g{Z zj|(IpwuJ_OZ~$g)ySKOK_#?Rmm9n;8X~&Jf*m-P>(rI zI^Ni2RPRH-QOl&=)(u`ioO%7_q_Q)!da9mVnNo18#OMHu78CD}^P}oj4h`Zj`PHg# zdQqQc_=eJ34hx>W0JY$ zPLug9QHYq-;8uG58zcVii84ki1|uxu^tx`rQI-#1B#yxcKUJN3?yvmOZdKwK5m+SUi=ceGKa$* z%4Bg_`i;MD2B(06`7-f3*>9#hg8CK9s8gKK>u*RozV9&d1w)gS)k zYX<37J&P+!X)&T65l4Q^~2)JMJ8QbWd|Y=`=fs+EGSxQNXKVhe9M z2yVvq7ei2%6+Im3fvD>hbjn8|IWbYSpM@_v3I}_Rf-Nl{nv}}&aW%ocZHM?39DUVE z2;3S93^gCVo#{)KC5Dj6ynQr5>#|QFru;6#PIs`KbC%#&K#o*hX$`GW89Xuu%>8$m zC0yV(9(T@X3sd34lvz3Tlpm-S!=nqvtk$WRcbMQI+V@g0nisBWigz4w@|U!SfT3np zI^E=bhRMOtPGdV$f}rN4Plg(ts*SsSVXFXuwEIvp*`BqTnzx#q(;Sjh*-l@&G0-?0 zBfEx8D%3Ft)boD>lFj>+zw(HUkR@KM9`WaOfS*yA85{GyHAHtsK=g@T%x}#?^59m4 z9C~Eq_);<`poqr`R&xbXg#ik)k@9to(VLSf`Ok?RYm`!pNOLC#yyn9om8V;`E*mM% z@PX1oWW9vaQ?(%(T8+HHTEg7RCU=DS=f}RsRU`4AY7VCc13j*wET*<>(8ih6I)cmg{XDI-bz!ggflG;u2J}@nV+_-U7X601!V<9mX^ z-I+?M!5Y?;%^R}0=7>+(5aK&(GEM*}sA{KzN^L*l-KsZHs3!FL;gheilxka`vW>R& z?eq}^s@NqLL9ehonLL(M!VKg;Q@b%0T6`G#{b*NP#Eib!sd?-fr#;cXJ7KT>?%FY< zZ=d8W$yE-FNV2-L9Fz{HFu#w@mX>-Z?fb63Mbh$1_%2!wiU7GiCf|JDhS<{WV_FZub311$q*roK#7Q6Yzu+5m8Nq9&Pu_I5&V_0f zl8*`!1duMpUfZ$8GMdk&frHeCvYOrOA~7L8QkCW%pkkxX9`;<$4_hP>=_VhUsP*g5 z-b?bohW>-Oj|xoK`jBkBMLoz423qwe@J?T5>FWCQF1TK(S0m{Wt4-e_m@Q48QO{L1 zDhofcU$U82BnGt?40eg#+ud%hXU@2z1tdf3S|{#x-t+$`x9{JxQ%HHDNPqJ>w{haE z+F`T4>I)o2sllapP4m(+UkYfyTf+G4^RV{nVsE>$uXAIMZss#2A!LTIka}_IFkCGI4=vhJJ!dB@yYDnd;Y5dB*G@sm{OY+Xdq#l| zfSYa)3am)c5!PSDK=V4aoe#IL+GLgBbKPMwQ!Sn6p<8;>#}u?Pp8E4yxfwJ|!<3yr1q*k#T;##0Pn&q2uyyALjeBIfxCG7hNqdv$&kge~9U$Q3`e~Bh zkg4Me(4XTEd@3+^!DwzoiKwwa+D>$5+(t~8LVlmmoxJ)cKhF3GTW&|OEyCb0$> z!Q=2s`EyF$6HX9V_9gvQY)CUD?CV4-?k&b2czLys+$kg`J^JE_FH7IFPM3#5aA<55 zM#?^Y6EzFru5KT-L7zR0u;K(^Ixq+xsL&1Z(Rvo0RNuvEdmoQR@IuSn^MzKw1MMu8 zXoK+T0k}Y3+VZ-E=)LP6QpSbGr28UZNN*E{qRSZ^iYQ(ePFE8a1jwmKIxB&_`T5F( z;Kq_8H0x-8*U1iY6weGeHEfdfH`Er@TK4snQL{a7&mr7ID}-picxnNI{Ai`~NPFzH z0A*Vqn3*_;$%#<@IHhbJ{1a3SoV*#B2MWx0-G9ZB{5HW9|4JT8rw&-l{H#LKR4S(2 zYWYF@(u29dZ60KDr}Bp@DFEP?iLS409qetu&4SMg&%wOT(VxGcSky=!J1h&OCCqn7 zOx-n#ZutvnSfTN>H52-f7GhJ{DqnPgKRyn99YBSjf~l6bM2ttjPXj|zExdsH*7NhyT@QSalc^N^Uc<3G+@GF*$H`vQ(Iu)s29KF{3YA;IG z8EP+kgGmcuJYJAO9p0+hkghy5v9#%pG9RMB#6Ssa6MJrqxZ9SWbZ76CP-2+qwJbtK zaVjWJ(t4FqoT+XLdk$rD#eiO;^|=demY;YZ|FnkXrOi90@tN0FOY}43d}Cj~H0h(| z64lKG3=l_u?01et=u9Mdg4C(_mv8VTP$M?>(EXrI!mU7Mt9sI!-k$f|YUFlgrtfXm zV*>aq?`i@uudN=2-|e%@CshyhIes4!^u6=5W18S5|{&pup z)APB@IjJ9~IsZrs@X#b5FM zCCEgMHkRL6y zz9jDbGSL`&V)(k{OpLQwMcI$=0RF0|S&FEaS8r!l0ld;s5Qi}QVd#NcNl@|*Gcu~@ zhWctea0g44!(UUB6Jt{puChrE-rg(}d|CtE*S5 z*l`#;lDTrNxia+l*qa?no6h-uP0UrGX8Qy?E@y%*q&a#D=`=5J)Elx4CE{a)RNb{u! zL+W&!Q&a68>NMwny7}y_!C`RKTRq{NzZ+qH7uQSUwpoG#1v8J&j8_wuhYoQ7=19Qw>=WhtMm z*+ZoB=~6fHR_{l^X$|(%DHSIgcCMVkQ5a7xXW{ zV2++&YN^~#xyc!Iw|aT+G4{zUhk}3C#`ij+JNnOPy1z2)(9^&_dwWsX(7wHEt6VNh zE+OosTPk3=2XVp7>@_7o{)iuh*m-gwHc8p5m@=CH1Kc-FP3Qk`X9x;YwxZHLuLJrv zerpjkCJN~ram52ARv>3(rL?jf|4ETtr{9rD`L3>y*ES8vy7!{(B1M%h0USnfNKv{c z?==1SllVuGvO3*`=3p*vUl7$UTD+t>HAW#&GeOA$o8{Nw0H;(TdTSb3_{Gi6=SZpB zNq>3X2SEar;K5_8E(Gzs!7HY(dU|F{MU7YP`u;edoGY&!BBe!cbyA|qOPjjDDksn4 zS`2zTS6@cyY+MU9|MM_=@tXQWSMRf9LVt({T4PgPr#k;aIs+x4W(#pAIBq=?Klf6$ zLARhxi@2R^ z_-PrOJq_qN^uQtH--Bm=_h#FVuH$0lP2&w7I^G8YM*gI^> zq_uHA{C8=!*&_)X7#lDhxvl5&Hbfi%ld1}YsyUzl(9c>Yj|%;XJCl}O`F^2?5pklF zTd4-eusFUaxbgd1$Z!=ZZ7I!0QL1|F^c{SfWgBy}$a1fQHiVczUo=W zLg0;1DJfZ?|NoF&gESsvd6XyKk-0AmWL_Y;23V1ni?KYx8e-HRF~e!5zE7{l)IF_3 zKUnf3lOP@r>emDx=~YQqJR_f{`}+Bh9|BK!6uhO`F7&M17#=@1s@KKl8XSRv^&HL) z1dC<~5plTgiYAQ*_o^4J_{E6&sn=*+T`9?kUs$MJ2Z zhKkK4O3a3|R4%!c+tOSTA)8!szoofLE^}{cE@6cXxhJ{jn#&{!UtyuPa!rb~7!3=l z-{<%5{@FP@JD<<{{dzs0k4J5=G|AtrxyI+gwo-wf^ylQ3N>cd;HhVYpUjWL=3+}Xg z$b-BVLJ93c&9KlpigVALCJvUCc5S|%0N97&{qbOTJxq_OFxS1@aIBRDEsnkA*okR%3^eh6(thhgJk-yenw=CH zlRhhS88?FvP3KIsF}AJ!1Fm4m=QuMaXFytkwe5k5_ishH4hj%1^iD~`Gq~cRcY&*yDn z*D*pjiEVzluVi3}DHe z_ktK=3PK~#KNs}k+-oz|H`xz#3KScRa$FX1*bu~i_|>7tFK8#yP$@NvA@$n;uDjR&sOYD^=qs~Rxk~H{ObG-*{^Use{UVuD!W&H=cMJadZ0C_pT*}(C0|#iJ{fu|yx_^iL}&{=RY0hQdbJ1o9-OBUJR{`9sk*6$ z!b#2tt-~@>u>H+Ql;RaLMS5)PjwEsz7H}fPz<|Zpwd+_R=BXozqBwG~K2s0i`Gu`W zeRIpU2E}YdMBK0*+~^y2u2)Ct8x(Qk{)l%A3~ohPU534}i1{>`pIMxeO$)2t)#%Qt zKYse7LuU?s^qQAv9!+Znw)T{h(8V+Ko@;7ZJlS*|F9=GK`nUyAHr@J&3+n2gN&6>u zbA9(Wo5P+H&_-u)DGikKQI3U~hp^bpRr^S3JwE33BrC##wx7K*>YDK-;_4w0rt<2zjWF^ z|I&nFZ;jRFdvE`dh0Vt@0-syasV8U(Y9yWFUkOAwgxWl&{$(e!ZI+K%J3@xyEKU%2 zD|%-g#TDLn=`ZL)Pn;ML1mW9Lw)U7~(Mc+0jrmPMoES#S(tDhGC<+y4;T7ez_u6W5NbC3^$89JS#s)=Yl4=Z8VEXy?Bcro!vnvy4chrQ}<0{cmr~;sMf`S^c1h zTezq-u7JDPFA0M!XoHc%2`ary8iaBYBhOt=TkyAigrK>HUoRZF8quma_27g+awfP5 zA%xBUNl*5jZA*cuq3?75{P`2HJ(f{ZP^5E$5i6!f;7`iuF-wMSgKRz1^{2O5Sm&H^ zAoBbtyqaz`rmE^iXh6{vX+ky1J5MLMPm|&52^dx&%86kkw{Jz5OOTP$aEskgF=uZ> zC|+uCICNxeGR9KO8yi0~s5-edn7IGT*rZh{;9^Soa(cLta}rjT_*Us;1xVD=iehQ^!DO|I`R?v|y-rtNb+>_YD1EKv zh_NN!C}ko5^$ge#MpbHz@MRoU1=@WlN!k*~aR(vV-P@JZq=))H?D~5kxHZxaj~&5C zHOhT~s`?!B|2Q2O>&f3&JZkp#y4_+7JXCLW&Cp$~iNh`687hX8DwDH&%vZNQg8Ik6 z%iLJrEL=c^!?s-+P>uc91^y8h>%tA)SPI3&CnrY{i*}>DAK=9ZjQs^$GBg@LXjnwT zdb_8KQu0IKpTJ1~>g*qqw(xiQ_i&L2fUiS8fSnYjwd&ZRg5A#gDwz%vEtG18adx49 zH}{-CtS*-YQ^?$>`~XM!NzwWE) zF}4&VCyLT%x)5S<-xm*Ml0LE6XBn`-GV2Rq1RNf%uZ2AHA4Rj@^u!? zq^vWR|43S&t0xtlk*_)6JL5$gjQX$zFJfieWuqNS?&zoo+Et*FPt zQE{h*boy!YrUmeX6Y>_{LY;aM)QbZ{d)IF|548J>+W7folV+EK(-!{x=>qeaMX?5d z1t~UuygpT5Yw?7 zYL47*$$M0h1f}uk@oQt=CW}pf-P$(hu8uAO&puBp`W+g;Vd6<>IF zdOZ;0|94HiX-l;?T<>W1+Ba34P%!5Mi4QALQrYGt9<~9emVXItQc^cQ2Y+gU7Zj3< z%&iruS%zMi)f)n~`k z1FCQ1FCfT;LK}QhO(bqrIXWsi=aL5S0KITJzVC|xfsMHkD%uA^Da|QXnZmFJ25|tp z9-Q)NXt?6YA^y9j_E6f}0Pl0@p(`^-1E-uamI^Xyuy}!!P9l@qy6z*L#77lW-_Ya- zrs`Z~#K@`Aw_0#4)iJHkv|yPZZ{7R5xSudZzZU2}^4~NFUDB~pgY=v_`J8NOp9%uK zU6^coJ7`{d)ju?Vx3{-<%pUe;T&*`65zH@k0e{<+}|_Cw|TQi5hHqVQz;=| ze|G8ONkqEGPr96(2-Lc{R!`jq*IOF4JUPdE`b_`bU&A%ijm%)bB8#u7uUe|lBZEf%+~Ga2w~=r@U#8{G@fpkh5XhF1QQPLg}6}0KLPl36F>s9;8mKdQ9_){MH$?4`q}NP2s?*)f|CI$0R52kti_;NDWE?T zVapS@Tw8Lz`)lN4ZktrH?jaQ|o&Gh`P=o%D#C#4@2bKITY@{qq>`(M6S6W!znsVWi zRnCgBm=sAzwel9SVc9g-Dl<6E;~qN8l&hgvDw%!MJ@U7WLRZI#{>Xt(M#r*F-GuI-{y5%(Z&t6%pcC& z-9M=QLA8tnHvX-=@AP6*&yf{xkFy!k1@`;fr1BHO?b@H4!Jq1Yf#Yn4_XQv)(Q$S! z@+Bi!3?(+{OmBhMxmh^m`6$Rgh!1kSeQj}l$Lt=7E|r_4=VU75am$Z5;Q9dbJMy1| z1i_vJ@C+^OX~`@HWfX>3AH*48@BG=Ar75X3+{jmjgpZg*aHi#3nFRG8tor&n16dH( z)ZMqNo{?le)oP}b?lx8YG31_o;4u-9s;<;?5vvHN!ysz!yF=svYb!V7((5nWPgb|f zmK}Z{Q(wDBJ+0dMv`}bF%vFU!;oG^thg1VC`_%NRKVyH||U?v6JKt+5?fvh#M*u;DRqzLd> zd%7FrVv$uIx$ldO>>Dn-x&5SQGOsmJH{By&bnIg3(SZiK?7J?J0CkWuMWr4mW;@T$ zJ0H>99qy{|tbf)+n%9Lpf)_)`m**e4Ke%BsCMW?m&0dwMXKnZO2?UWSK`6t?4lR*Z z{leq!btg<0!i@XFVt_odVu{$k2)mpc#9oHD!K>E@>g$}=x~igc*^-#%V+5Mrbu5t4 zU@a_L3IWQ=CtO>YlbnZwoYd=}gvU1?=?kHwnhn z>!-K)h-r9xddibQo;-O4t!s8`MKeS_DE{NSl##3!p4p({^QwTyG3Ci%@knHGCAdQV_8;N^)9KEu4P#Mq<^{ z?sW8tYLhf>&{TJA%c{Kz=Pk3~;hLN#pO{gU=!J6eEB1rJ2ai!{mzW*H7GY->E7Pjz zPeX_n>fEs{$ossu)U=qnN5FIOOGK=ti4yXhtV{ z$G;9U803rKKX8D5-Z2jc!J^i048+7V7TAd(b!D5ckohD)m1hm3olhzMY8WObR-_y- zkD3d|7Y@*9!m21e*nboGN=V&rM%Y63k8!oPGeFA>^d)qTOeSC@bpRhU4w}8?eE&%D z@UD7#*d|2QuYb>LE ziMyspsimAxM^B^BGt~B?r$9<6om*oDdBFo1b3y8iPzb0)N##fMRkLX_{dx8Ulxnl z?hZFiNNzF;ZhOhf+7K^E8=Co2*9Xwe zd7L?Z{xq{*4B4;yq+Da~VbrF6quDp>g46PkS=szCXF-)_hOrb&*o{>aSx|;eDypfW z;fGYnSMEKs*VZ3Y>SgOHl_8@nxUM=FN}=f~_q2N!piyeC{0J4ruGfr}ia+M#P&Oj) z;G_{^?8{NFRQpM715UVyF(d_3JI;M~Jp?wAdUk32G9^KiJzkGxNg|+Zbg~$5v0jU`q0Ddeb*f;x~Jp)^Mz> z+N7D8<8LbV#b3xy@)b{hj44KCI)g-Poo`E<*k}mB8X9LEj8!jy{TwF`4G;Y?wek`Q zoSvyEqzWLLAX%^#FQBJrE}0<}ay-lp*EX}G>M!h1Bk+?f5U71T*G%Rh5U@Ak$l^+w zuwu;27v1xy{n`>vJU0V zK@tib-goE^)N{_p-RQ^t)}Gla?5pV&=%es|?pD8<2A9HOjd1v7`{LS8PD{8~`a;eA z(>@A7YJwZq4SB9c@SF`-fSv~fgtmm@;%Vi^@Xa)~x;?g~2`M4!M)oN;!QtW_Z}{XI zN>RbcCpGd{`H5Fzv;}eOgb;{B|1}Qp`eYV2TFC-qOz1_JJ$p3>5E1{{q?4m;Qh9D^VwDf)K61* z6YJLM7nGxae^gCh;Kcpasn$hyGvBwi%x+DZA24rCBdG8twZlM<>J5+d*=~bg-c2zo zZkX!r9lTjP0_wkJ0_hzB##6sMkGyhJZ z)46t2c2a_J+`_wFgVOMUbqs9kkkwM~EZ24%g$Z)I0B!5VSe^lS*9le*sIHm+2Nq~G z3hYI(!o9_T<9DtbKvyDK!Yl)N<#|gtoz{IHwV_|d{u}20gxY1=VNt{+5iz$X zm)YDsMSs|v=~fH5eGrs@x;wKX^t56;%0G`k`-*9(1?setTP)_t>SDn8Lnl<`C{`f2 zg0jHckA=yJi9<0lOvYHh1NXa+#C6^-kUE2Y&drzVPJ5B5MzVW$2EQHTaLkTr?X zqI}>&yqSGMZtZ|mcd zcA0xV`tubV6xKcKo`;g5T_4I^I1(*XC72%j=kK3~_DN9*=r7K;-)eh$q4^=(4jX$bPk6# zKhL{f>uZcHkpaw9wW?~nR}|mt4{M6NPbtQ^l0DsUlcee&6PE9oWC6h<^KcuDaPiZC zGncv^)|Ep$`wC;YuV%Nf>SET`FG_adAT$Z~CapH(-vIpiqvs(}AJCI>s0VbO_Eh&{ z0f6v@37HBQxFIi*U_bbK@MH3Oe+~bnuEi(Aivbe#tWO!LoRgT>;o2Lq5l2!5Me*M9 zO%RAJ&7|ToF+0i5{vD}p1(qp3{G(fPDvz}umK$lL;(Jxq+8g#vGsdN;?$oq$^|<{J z&#Ix6f+DuAu;CH93-8!+DZSC|`*WoFMA%$ya#e>9{#cC2{A{X-o+u`soLT8Q#as~H z$BIr3^3>b$A0E1=D&}pkjH=XOW&Uk9FgNU*6mP<)gY$=`n{;=BYuFQ`7jx?wwWyxq zVbUpKgMs#+ibzHeRNhc=w1cJiB<+DgtwkOYIAr6d`pHCpe<#V-mD!z9vEpdRC&o|v z{~j4>BUMX$u$}z2L`It6V@`DA*_n z*8L-JNI;$V_G6Lned{M9bR#J^Jffbxxk+FA8IwD;8ya)p7`0s=at)0CNavk9#E3^B zHrlJ=^ToDSyWls^1Ncguk z74B@tw}yN0 z==%yc=g)A)`2$6n`G||ppB&fi`P&QeKL{^V)`MfWm&XX4d!OjQ)v?@Bo)PoxcFj8` zXIEn0r+YT58YUzg`*{-JIrl<)nvX4LZ*`Ll0QTM7rA?yrlr7j6A_yr)IM{mJ4)@$T zYeXZ7+KQK7WAB`~74peh09i8xO`Fe3Xj5%2@Bt!&?7A zr)+YIj*21k27;vlP4+MhY#T7OBdMw7Cg%o$i@G%&GNi05R#YwKB>QhD{*=iC_XXs@ z%$m1~J|?E9$vqbVw0_od6>B%|>#7es5?lG;_P%rbvMk%LmDlbX#}AyaCG;r|XTBR< z-z|#c)AtG(tQk9^RLhWFf)~=jAbQ*IS*{|2s@&zLdg+_`(^1xX9&5(M1m`M_SfQH) zO7Le6zQ2X*-~1~#uh_B3tZO|W(jtH+B$(jI*Q5t%5_U4M;VKl-!c+cB%0`3|w|VJ& z)KoymYEb?O?x>Z&xLbDF{h=2IMJQ>LvhpIH2`;%9sh3DN-}0?G8Nu|xE1?8cl*;Ab ze{7PyL+HI;9v3^i^0W8XE^iJKQs`p!&D$Kna`b;T{)vI@TYPy|@Zz>8Znc^H`7e~J5LOcO&-&p)k{8Y-|S|9oyw zaJ-XvTyMQb8U+zT#0$X4=j*+Cbsd^i6gBd3x*P>d5muAnV&U8XjUQ;bPW`K|0u)S+ z1K&&IvAFo}-m9PT>%!E9)br1s@QDcIt6DurQx%dW%(A2HUj&+vp)ysh7`Pi=P+gu{ zx^mxAKraQKH`F9kWzL9}@sfK?+U&}FHi@g(>-cOQVzJ2yh}7k2O7KRvIp(AW^;3?{OM+@F$F#m z%K5!lL57p}{>Hw{9z0g=hI6+C+Z~ZF@>sLc--jxRoSAcZ8Y#DUTa-DB7jJv93cRwl zMB9(PlWo;PYk$dm2s8;q^zI}CzC0?3rCke5F}yv)Ypmyp!rlV8OFlt--nOFW$|QEgcA%6$l~~!F%aO*~2;1!Rx*Ffs>Fh|3l!8lr z=;R2tbY=_jq5ZU+!eZKcIrb#3VYok7<#QMv7{a%Qd&!j9WA6&Vfs5$HzQJ2*9{!B! zsymr#F{{}Gua8diyd-uAo9BjIZ>L79VFE_0u0K^jNo@oBAo$6ej6s9q!|8^Wa)wP&9r9*0__ zelL~j9(n2FLYQBz+WQ-g@Lk!}?#2~|x~%g5m!yEDOkK1F!I6~hj-_hZP)lRER~kOH zo3oS~_DX0z7j|fR=qG*Wfj_xt(^hEWx%CU|j@e1PsdAy`;Zb%}ch{3mdMoM1@wFo+ zsmi#h=+(sQAPNf)2(I*C*`(Ep%Adw z@vfK1*q_UmZf$BPAra@L$ozx865)2@*ua5S-^C?u#K{t+3;A^|=%(u2$b@Sl7h>t% zoJiHs7a1Ne_*Euj_|#GA*+FyOi8nrfE*>&+otj`rv_QNo7)vI0aMw-&A*5&d-}R9A zjXGrxLs;~=snKbmCLA+Uv~6`*)TZT=v&GJIb$D$rMw!yG2yl$15J*8KwR=Mj`=I0( z{^)p3@#R)#t6qAcwR}w<=+=xZ4j=ikSYf4$BaA|ry%|kpv0IpV#Lcp&I*>*LAN;Mx zac{lWG068`fvj(tH&#oll&zq3OpIp~8fiT>oZY(x$y{{`f z#toe##XY|FW`g)2AmMx4v<%@?YGfB;$msc!Y4$q z+hXW56VJ=U@uc6*#It*Qe*xfA$D;c_PsCT8AEtGy={pRtf*B!0>vnIL;3IJc{j;J8 zfap`^+zefj7-%>-S)emhDYx%1v{A*)4u4KR9*7eEoqmTokV#Vj;Gzk64iGIkeg=q-BwjHKQgvA69fT;)IyXIUEQ`u~G#lHx5N{M?N4>ftcv$_^r$rIex2Zn_8|qW(MG-cON&yZaO;FnM0R zHd1tXlE;p&ZKqigW{cSD&6XIUi-}}=_(ok((eC&2BP6zX`Y;bomhs?NL6s`^@;k>6yQNnP@$9l6;BaRr{sumM#57wQWDR2-Jg5`>?)O z;e9{6rBp|`-2ntuM~kBZfXZ&}B7?=6moY+;sZyPY2U%dO6|)g|eo7-Zxr4}i*XiZygil_C!uxgBm}9QivlC;ROI5nPnbvtRd&d>3m4)a(5Llpnt< zQ7$0_U1EmJ{ z1#!Muec1~~T%HE96qhqI`^*V_e(+UiYs<_U2r6{t6De>nmz9L4P)vLMRsp=NT4u=Q zp)~ggEzDred@w#>D=Nl`D>0u|8QmOrf|4!{r$~3p##b~rCdJ!Jy@m6)g)yv)i#uv zJhE|bV5Jr_ykoEBnLZ|)#$DXGjz#w8f^0JK+)rFdE{CjEkp zoGG}oihS>>I0B7LY@ocOABXUXsfAIuw?WB`H6Q62w=ztv1%y_Av~q|y15v%WZ;TaJ zqU@i_$F%7J3W!otk*^Z|!AVAk;FbT}^_uv2O$YO|{;$9g0(c=CA?Edt zmvnVAhR+ID2r?krjBCF~BYwfNK3hIJy@guDc;*miO1pCQ7T7v!`m7JR{lQWFd7T-G zeWcypSXnyyVR7?V>nDd!p7(^_K{)CQn@y@c=a4Z-oJj-n$RS!&TP&b3sgkdwM(OiI z4b{hW0r@EmQhutU?7ur8HTERX$7oDn1BhrkR(_#I3OSV_F62MjCM@S~+Zir#BlMc9 zQsQ2dV$9(mWu2XXfPVXTE2wQmb(FaSR6cb0fL(RqoLd6UyIK&hp_HH_^Ahgiu zrNJZ9izr{e2}&*no=BT16IuLob~z+%)4rbDwOV9~t?BK1Kwo5ShYWvItiqd{fvl{= zT>}8^WY(LyAmG*=7gLvUqk5>~VD)OGT*=led7krQC3tSoY`@{q4=?kZ-7}h{_@tX` zx(lZ*{S`$fKnFlQ(yyHpx#P1jE2~Vf1ATSq+*DnXz6WV|Fd7pFB*Xv4Hc>P`5z)jx z#heXL{A?C3#_<9k(j>19?%1XS!-@KaKFH<$ePd^JM?wm2>If7gla-oGB{B7p|BlQZ zg2RdRp#{Uue$NIwuE{q9ElLFRrYTcjF}O{&;cnx@1w@3L06KHZ))yx2Yj1B<_Mhfq zFRlIZZZ~SrOi?7K5(H4_CM1ntYW~xP=Vl3=2b_fRTaFE^a~W}4+&6e`-imH95)Drf ztk(d4VgFe?t?47?odn?<3$ZEhb12eq=7UEOIc#s7wL&=~20jiD=<9?^DGD>-grsCz zlHZDcIu16K){B9wEs$(uMGG)#tW(I+N~7|=PHrOl)7zwXP4zs zT;dNer-5cV%daYj-#4w#9|XdgPu>4rLbC)sES1Hm9%ZP~B&o{G7XH9qdJ=LO25QHk zQBAlv=n}yaKOG_UYS^Tvyjz7&oI9yyj4%~8gMZgQ-wij*Xnho(jp_b5+89tXLUWKgCq{?Kov#l)7>7vsvf!#=FB|>fHG2_hD?c2OD6OPBI zV{WU|bK@vKozaOIcsmjYeOObFpl5DZ7x{02RnPwG`GLJTIRyuy1E#9(!}-+tqF9tl zjKrl3GCAHqbmNW@UkIb~l6tuOc_1JxYYorp6AX3VS~0DB{2CGu9#_Vz2<_hR&asB( zg5r}+vl9l*{dSqEu*lhKvSsli@sw#^NZkEye=k)}{Suc-cdE~1VJ7K}ukcH4_!~bU z8FW-7S*)pCU4H)GM$z94{dDFgch|{|Y?zp7Z8~*(B7o?%2xu6%Eg9T5hT@d9Y4J=u zW&uu;IvMq3jwdq!3$K*+$bAC4yVQC=&B9TvycYAmzuUsW#T#JeNi5r1C0)1Q^J~2W zM_(nQdwY*8`^4f~c-@cCcQ??(m^&1E?WW!`Sk@a|j1VS+Wtzd>X%05@6m3xsWz$vX zH`^clVsG*%30T2NE9M>#pjHo9JoeIFtlv0sq=g9-sb{!DCT>8 z9psS`vi55|x3q=5?Q;ykW_fGM7>C6{#YYZ6n{Q2Zwv;^+8J|2kZ6}VP|CGK*lFxE% z)OGS7QEQWz7&h&-OT{HUQB(c`eoqN?8-#bhE->9*T4Qkn$Lel*g_vcRO-g z+bZ!!;3tpHS`=YXI2n5aZjiE=V>yq5q7slzH zln)!cG+5Y!WRQPi3_=BnHmd3p*q)ZIwyhW5!!?)@b6AsUcI)nqW4XNUx7E4(v{JDY z!$gb{5|ujhB!9n^T=3ySl8H9eE`m5|dfQvnt27c2HKaU8SM_U;Fz?JpOxAG=LBHdX z`9awG`m2$hpUyekhpj|b-n403n@-y)CkP@y%#HOUcF5tk;o`b-=Rl1OWt+g`4<&E< z(B}m22vB4;?P5kB^O3_l25og{by?a+~O zULG9@MI&K|RXv(ya!lZlQ*9dl)}Kh~Q?}=D{w8iGs@n-2j+tvk)@(W~+ zLX{5K&sR({owLJa?)g9ry+_$sz1+_^pP6!|Nl~p(_(PX630W;g-fq9gGBB=BOdHM> zDW61c>5p?m_htt2rHYs*#84$>hfgq+g_XvcoXuy$759^t=*{7S@+ifP%9;p?e0cZM zWA%@=14`{A15BG=n~<^`zs|$n-L3Nyud7uzkRJc?)%T=Y)X}~dOB;y`(%-)LFiAy-)q9!-qfm7PZ2M&rc2#PWe-A9y;yk` z?_;PiYUQsO29NFE?Z5wIm_M(;qM7nbAobt%(&m}`*5lTT0a65lRBSgxLkmAcIvHwg zCd!C#-tUzEN#fh=P8ef9!-ay&l;D@s#kGUBX8TZS%KjYEO!YDPv%i1-b}`VPX}^3e zLf7S?f2>VouZySpL_%xUb|^{I9fkio|+FI*%&y zf2Z=XfP8!1f(GRTc#@J@Iu{n60TXr$usDTk%39-v`4UsqIGwT{wI7Vbrz}XGnqovTNe z*?9roRDmQIG_KI8^MPQzSxY3j62slIkEaFnjCVrCv_0Q9#cd8^@Za`7_(Yzm+LXwq zUWkADbG_aGduXb)jmM-y;-wUZsP9Io0$_i}Z2E96@jd`B=bX{X7}|g96&BE)YcCb{ zjyv#?Cj;zD-{bXJ8K#Hb#3qYIswnX|P-ILj-x_=Kb{nSexZ0yl`MrJx9pS1R;gSr70qfFoEFw$9rjJF6@XR?rfqs|66ks8%3%$xwZYC&Ya$p7Fowx zHd$2`j*Z#u#YR<2SyM=sUvdH_kH~qJbe_13M?U8yfhKn!8VD&}h^*7EZ37Ow&)%z! z7DZ;vE;59li!}gzV7NP2wIkA#GN@_5@R;M;UqS~FM63z5?X#RVpOsxc@}PNNH(hw- zo0?eF)~s?FeD#3qX`a6E3%;IKg*R=MP|1}w*veJfYBp_26*$#0`-VN!hHqtzMFGUf zKe~nO{!GXEDspLQ^eu&XWy2(F&#uqC?xY40vDQD9|Iyx-Hk4II2q{6~M=c1NPPNa5SVa;=Kg17h9GT{a6K`dCX)eM;7&WHn<(pJ1?-y!~Of(gX(^$@fm& zPoaN+FbvIw>(B2z-*U3I*mc%Q5rNAM zx2FS@)O%0CuEvYs2l#XqACP9891OiBAAr6%RQuW($~UeETqG%uDIg#MrV3ey(`%>(!Y*>gc@g5mKo9|27G>8zzJw7Xk zd;i)$5O=dmDI%uMOzf_HI%$-7#Mb!1wy`oua?k2R%VAeN1JJ%87X7JNCs3QyD+lj4r`Tq#|ww)U}(lyBSqWY`s?fn1Chg)V8^K52cyEm4JlD zoMyZ^cyc|gph#mz3oYXxy()}?%ZXss+~G|dKu`0Qb(6o8Ay6(^wIm%oBKNubaJBBS zH2w1mnd5rbh%r;Mp+y`~wQ5*TGE_jeRZrwo0TyTI+hbl%vF2Tm+uICdh-cTw{^9cG zhR^nIXYJbO8C3>(-u|%m(YI_yH&2Wa=bh#P%#D6XFxI>6v?LVxwUrn3rWS? zy{J;!DnYP_Tpa;P#C6o-lxlb}$ru4q5jVL|yJ?<0#t;nio_THxML-oWxxa;n6w6>k zqKFp)NF!~<0Idf{_QK-ym(I27@I~gF{gpj%&hID+5^vJz8YSi_e%9Bh$LQ@=ldS~| z1nKMV@Hi|ckBCpZo78ISPH^W45+(w`UUE}ocTv`nNu4qWsl5k8m5fyByLtA-lg^=H?1ni%xA@)IyWB&>Z~cSNIAI4IC} zI6a)aL*wd=_Q8a4np3?)2l!haX~`j-c@N5x+9_&o-g-Y};zKgVIt{EC2L{iNUVC#I z*^{O;Z|j!uITVc25-&IW7%F;^7;~>>f8vsJyuCntnk2!_cNud(fvLGNX`kb7>l`XK zzZ+fe&+gv6Q7hYeN>zr>f%qVO{C8o~b2Sf%v^o;WGp}7Or8(`uU)FB`s%NAxs? zZP?^;=%7SG1h5OT}3F9H981 zw^&oeedp_WI?=lhR(6Iy23vX7Mik|}?cvcDk%ZiFkF*OocK5o$zrcSlPsa>shDJ-% zDSbBzs~#+q?o}ei#YB2qwp4ez9>LH8diYvpTSWG56QR6lhSg|ln@l2gRDEeOH!L7_ zl5GCNct^pA&i|3gS^O67Ta%`CWnFlBCm$pwzW_m%dU$FQkyo?8=Wt6gZJ{;oAolx( z(74R6n^As;lPSbNUv~+HR1L8R$k#YjeNlC8TM;zL_#2y1_HXLtp5w&oYFRj=IKI*h zHt07H$@uhJ*g>Vb@e6xzXZtTQ@hQnW(8-bOzjp7onL$wzceAy{!GINW7%wX*p!8K| z{AYWA@ZnC2ya-8zPe{uagaK^`dM;g(jj>iv3;m#MgKI+NcQeg@2UpUjkMLSz=Mve1#B z^5c2!y+aSkk5)={)@Cu%lo=kE=p%0`MmXkQyF&6MX8f5RaSSA$l5od+(_)RjewLv1 z<v(Mc_7azkj)Kc#O@5_B~3l#SgelxoYIA>HMLXGu{X|;lC*9U{eS=w-Zs?Qc>&k z_4$lnot!=>dxLN1;;6*|h(3e0)a_rt@IB4b8nh1LjN9P*Kh-m! zRb|4KmL~GBIj5mHIEqUJUWUl zsP~x3huM3s5I|`^=PAYGueAhBR-{Wh5=kta>h z>h{0eEhI;DIr9o2lJSI5<<>Sqy+KL?R&Fq&?vhj(;HqFUIU4uAUvJtpmba!_TS-xA zs{IMTRqP)yrX6XSS!C6_$UNWbt*NLjb_f&>QZCt-JKnFSM2J&#H;*N zxoJvDQayX=I|t)>L0y=ls5j2jG?KT*rl~aDaC-IjIk}Rz?n%dQb}xO(VEnZXARizM zd_sqiYJgijE6lP6zyTR7hRy7rpG-CfXi);oR8*g-a>1cc%M0sjQ2a5Lf;|65R9FqN1T-hh__r_HAsTsRnzI3+*J;{J`MnY@ZQ-f zXUh~?edus5BBpt~;j1aHI6|ZPk}^Cz$*qg6ZS4&JU1g*#5{Lh@ZPS_ni??zfVE=CK zK3VMB6a*mFjoVi(+4m{_{y3q2@ecnOzG09`!`0H0E<;{Zj9i$u&UX^gl&a9ap&a0|@e89wDrb{hvNP|=M zQnv5S(k^d($NjE@@DKy%urKQAS+Cd@sUwL=piS)kth2mB)*sti8mtmh^jrLR+-_!#K4R3CYDWp zvIEaqiHVd<#SHq3QebyiOV=P|oSH7Y*xH!KVFf$%=V?7x5wte0;KZB%c>H2my3F*6 z+S~x!C)RIjm(_k6t>{Q9*pG!bd~$9b<&sMiov^trBeY9-9SLet=TrMM1CGcn6FyhH zj!x2{g1`?2Jv?71pd>s+HH3vpzu(fp876`aAas%C;NXt>XVBoTrf-dfhY9;2rtO@n zXiotk>;T-2-TAiyq`ld0`!pYOD^S(=2TF`#7Mmar|DK`q>5k+Q-RQ~YD$UB*dq5kY z^G_GhC{ltswY=@9ud_sk#{q~rnZ7!#yL(is&AuY_heuWLoW(uZ8OH!p^Tf2#36#v# zm*CrjhMw*nb=6_Cwu};T^F;JDo04BeyZXDANkW4$jI{GEbP6!1)*o;@M32*tIiUFG z`Za@nin^bchd@wh9#5f^5b0DDx6S&uVKoWJO?G$xL;;oMl(p;Zk}{P`75rZx){za} z_w|^+{`c$Uo{O-~lkgbbJpQ1$YjPsN6L)Fj1J~O|cC}wFc1?X>PWol1=+u7SLpNRA zAFVcIlMiwg-%=!U3}G29*I34;_B0SVEZu{I6vLj#@K2Q@abCWRGdR@Z^Zqeh$CK#zhSxwX?J#5W)%!aH;Ope8p(S~L zniNO>Ygn}_b=k*>n6B@~nLT^5t+O$TAJ;O5QS0T<@0qdD0Dn|hCa&<4tu-=_tDk<@ zQ`I9X06=cLNCh*zS{Vy1(YL=iefnzs;&rngzRCRzyUbun&T-cs?>4`!A5-5#m));6 z%^Deuw-j~*ek=_p+3q9-ODOWKK&IX-=R|Y_^q3NBV+#Wo7cDf3VlkqKwA(Lg_qXRt zQR06~7t^am2K^q_^8gL70w6e-noRIzIO5@{R)8)_d1j<^^ERW_(7FzDTSrjke-xdI zKhysg$2UJ3Lbw*{&*8t(F#YK{e3zJ%nxX!M;c@F z|0Awve>w{z^=Qa9Rqu2gOHnicXDB6rP~MC_xW_{ShppUxb@KkyO1{00wvGp|Y-eXD zkyyp2+Cg%N!f%iWxt+FghIaEgJi($#=z#NxZ<_r67zKWt^H6!Y)#E#bFHf@yOdNXQ zaI#RE5rk!7^icT+-482oZImmXf)=5mQ^;X{r_Iy%dn?P{!cHHF+`L0fktIGn+U-J! zf&BRIZ!p6J?`K?Ff3D*Ef;BLo>??1KmVqJP&W9}>5D_iySh&&w7ececZWP=QfVr!h z9b*Cfv#3#Apq;&p>KGQ1g83Gvzr6F4Q|(1Czdk(3AB9;TS@MG0IIl`f)@#!PTRC)2 z@P0VR0#ONq-uXnlJ8-G7ao?oa%>^ra+q7#1R=+e>Z!c%!YArFxc$D;9R$v>;GD45` zE`gftF+Cq<{$wn3OW(!}1}i}nAn(T~K;4+?0Ca_kMAd#}*}>|5T20~KWKu1>`lEhP zU;D6L@-pLO(x6YPc{b1Ebd{B)FhbD`p>h3oS@mppu+Wd=;g4h>Xk#;o@%8PEP$%}P z9A|o{KS~`z5`Q}QJUC?NQ~`QzW88y$#>n!Jr)mmvYD^#|MeB*pwGfVjs@Z;ZR8Qx6 z6HjLRVFgQoE2Yj8Aoz6A$EP3bQ1(*O2T>aFfQR*~O!aRtPvy3%mmLx!f?zv;v9ThmMeh7h)NZul9f;L&l&qmN(X9vj>2l)+5b36s?6nfsKHQKXH`Bx?S$Q#h1MMVL zRYAR&KWeV^1Ri^BWUhOja-!?fQ8?8U%^6R~uW()Q3v22gBw%mmE+&7ts!}){mW)TG zZycW4kMB}DSS0d>oFhxAVhg-&5xLuc3xDMamHg3Xt?;G<#|3JR&gjUQW2`$+a9ja3 zXJsRO7#P4R?fMk6W!T-^=`#|b(9Wp^WBDn?DPG(qAWhroQ>*~`*mn59w`_dueI#Q_ zO&B>x_v?Y8P(cli)@vKdH1}z8&%?C!e=|vm7Exc5SK5svRG(#p9+p(s(1?f#S6IHN z&;GmABR6gNl#C?yq+J2<$9ZfmHc~(oG`U>@_ljK|jxmQyQ!0avfm%v1FaiL@n2`i$ zyf*u0vS1=EUIPOxQ@^gz2m( z740?~Spu$%$5lh7T?mtldRewg9X*1$NC4sX@n>gW*RsC+})~ffgx6;t^ z#^Yc~F~M{lvAi?h5to3G2NcxuR4O3(Cu8EYE`p8ovw)I{f^G>uTdD5M2}Mx~C~Y=( z%1C}aurPEIvxvH5cru*_{m^9zV*H3g4 zCP1HCPBj`|413yB_98dX>+{x{wrh=2EYzUDrK;v6^gp^mSsmkssfZsou_r-}6A$*O z#4XjWMa5aUsYGgG@P^45zFR&vP0?k&9TAMz`s<^Yq;lXbx(4ri;(UjT6bG}<8_Rr? zGdFb*f-liy<0z$pLJ405pPHo0PFoxQ!Uwt}OuZ{NLKr#)AbGuy(HkvugHA$ZFk=eamu!zJ^U*lokt%Q8$${Gx?`T%yE#l8|lkjUwS^K z%Njf=*2rL&GrQP6msxiH zgfQZaj;hw!^k8zQlBig1QD0_wjsPYKUASQP1)tTpdM$)^GQoGh4qnI2>*Xkw#AOa}>rrMHHYto?5qe?7gdrSy#1+Uhz@_R^?R6iUd z*2-RsELaJ9;!To@tTqdBgV_fCnRmMISaXc|wH^&rH2X5Y$vKw(mGB>l@z@P=T}1lx zB(8q4H9Y*3@~@ff+;~)`Ho&J2Zace`N%|MUC_dsKTHw9*e(YcK&n;btq-8Ori1P;9 z)qkZN1f?aAVZ`5DOvsdx4V4B@3NSMb(!ZC=9^uqSnNRMAT!YzuBgE@=X@cGEk8uIE z9@)uf!x91_41JHHO-1VO}7fav6=j5zVBBWp{d<$L3Uk^5gK8|k z3U>^Qj$+&(cGQIp@MKOg%J}Y~WHG!QMb&8P5Nus_C9~^nlAD5vN*Bo!BGcaQ!nyDm zWPD_s!)d*{uSXNC%(|^8^f3O|dg{OWKzG4Dz~tEG_{Geh8&kWvyXLJ4gS;>)3*5fj zC}Qgi9=<|GmNRG&7t09~QHBX{{jL70%D(N^S^9)eq|pY@ORYT~Uhf*%Tl7#x3-p=T zXhpkU(7nP=i?XnV|EOgjeH5Xvt_bFn?jXhQ-Y)yQdii0c_t>%&1wX=zs>di}6;&7h ztK;UJ^lx}SSs+p+7Kb!WyW6-vm=FCBWtURUy!Q^ z4fzWZ?f>09XktQ3^!0=KMqP?uT(~#)XZxaS8h{J}VT_EXs`y7leW@TBn5s;Oj3-PH zx)L^3Sd(|N{mitSISMKX)w&#fYf)iTi9vK4qGL@hZ=7GOFz{67Pi%fAcmOq&9z0P4 z)Idqpx}JZb168L%)q!}P#e4(HkQpbesOTeO(_=@m82}aK!D}vM>C;WmlO@YzVTKh-XzE?`JIL!g~JbDhY4cQ$_V=r z!Z|NKlNoT-wkLrdc>W8aVlo6%Uw9eoAcCqm0NM`qG7H9Fjvw0-si7}bdo`j3+&ws< zDNa_`5U<{WK3w1%MJ!%W7j4Q}s2@@OK|AbGPB#^y{WPwwH?v4nDPV8Eh~@<7|64U@ z9?nS{O!kMWKJxi6q92wnN0OyuULSi59UySXgv99!!(D>&W`JBLWre{%6;q1Y2NJW4mPz+I$QAkK211%xWiJSVOLNC!ARG7&Nb&XmaWhC^;a#74}5p#c|+^ebe^g#c{ zM5YM|>EftR@M$byEYaA;PqRZq9LRR+sP9D9C)cp;^XW&AN5w`zr<@gv@_y|ce3Zl5 zzTk-rF_kef)(-HpII^H1mnKJ8n5X8!E*;edm4f@O9yUPh!b$yRCB5fv zAQ0-q%kv6MPK9$wZJ5y85>)A6DQ#iP@Wi6+kA+1Zfib;&JV$cTT2Uw z<`s0IaYBQk_Rw&DdBFi%^-oTF#_f5?ta4QHt!%QX;xS5NYmA0YydWR|xa3;3xOwJ^ ziD7v%9V?~sgLrm@BWYz4)AJ|#gXX_24reW74>Bkrz{lD44HCzzWRwg$QAx<#{$LXQbmQmuvRACE$CN&Yg+HxFS_(IFUeSMtzBWBOH02s` zYcZ;5uu07K$O8ixIiU~ZCJ!bWEd(K;9&(KPuyZSCoAf#No)7L`Uy3}OC=Jb87FdzE<~Yav-N6i2+|H5j{n7W!eNIkvpRY$mm#g^uaF7NBRc-M6C3% zp(oW10v1z}y*U)3lwi z1Ky>4YbQhi?ccAHhc8Mo?<$8X2sYPg-{iSiQ?oeuZ>f2XVBxhek@at6Y^sVvv5-s# zpZIhVv%I*-=JAq81hc`g-F+3Ihd$ztDopz^aJNUh*M9Y8L#FAz&ur(0V%5HI-2mX6 znU8^|m3!fk0RH_$3p{>JL$h-T0D^q_ZC!Z(UP)PTb9cpBUv*^~hTd?@V;m2&Z;lAt z&eZKJr{~C%voXQ|M8hH6k;3R0@6md#mXT9dRtPVl5Ho#Yeg4w{%MRw0Q7(HC04;Bu zlkFnGN|e8E;tetFw7l5x@XE<|6T81p@BZE19xKROK9{t$N&en&F`0tq?p#bJCzLaI z>_y*8sAUf4scMvv@|eGu?@|-n#*|)&{^7LOR`X_B?_o3~sYkeJJ#4kEnwBHsd1J1m zgE>|n+Z1ze51E{BAFcAkLao3u6t-psgAuGDBk4UDim!!;lcuoyFl}6bE(S02d9S;F z3l$A}RrM##Ex2X$bQDU6vy7OtW+3kGuZ0u~`YraWWb+9k>b4-?rJ>Y#5?N2?|Nc{Z z-@J%KQ}+yau~4v?1EW8GiVhqo)f{thNLyfi!1~??&hT4l z;~l*_1o`;}ZTVB@-TaeKdE*p&cBI zpRDc`0c9`HoYJ4Wni~One7!>y<1KDXC*+#+@NGzpn+4Yq_gT;MI0Jt?w==*%D3|cu8bQ({VW@t$3`4os=9F!xvRVuO+D9t z@)7&@s!Z8a5)eEMNqCp`{>D{PC_HWk{qIG6T3jusdA{N-S{h1< zCO}_jvapOWi5RvjfCJ0~T~^f|$BW^RW=>7J0(;Vo%W#YPcYffSYP4{g2qI1MK)%?S zIw$YO9-7BPzCfGM)sZqk{aY`Y|7jAU2=jG5a$uJupsl9JD~?AMDSqG56NFWjfYSAq zkea!BVYk2BypCDB#R|(g#&?FERepNEA$G48YN92tVWzX%3Ql$EUUX2$`=hA#z#p)1L z%;@+#zRd&1)BbqzO%sE;UHn1JRDl$R$s*ymf(~Q!4L!Ia`N6sDJwK*lgs^ zF)Ei8`g*F*cY?jOHp}HYzog1*H&;~7K74#YpW#vVHmsMEWspZ#s?_3lLS zWWATEjP93ahxE&CJaf|wDh(CoeYAUHYs+61lIH#XGJs0w>%VuyDo`kbULgQXWry)A zljFVHY)IonVsP>LfS|y^l7~#=1J!bOxxUlpyi2;zJ-u)({Z9UvwXLl!xqZ^0zDH7S z${@b*!}!TGsxP%9l)H6QP212%Sm;dEnLWn&9DJO@2uex&ZHxjtP~`_sz?b#&xWE13 zz?Y<@$P}g|mG;z@^d-;-@2M&7%Pc31?J<5jOnl>8takN3F-RD3$V86PakAH$Aot@^ zzKbiT?Yz_tSxEIVr@iJ+U_0*ubz~xIJ}hiNz%;F*M@EX*equQJ|3!sa{4h$mS}r8- z__CvMMC+oW@h5`_i&ThIuff#Jzvk1%fH}~0n6Jk`EEACCqB4(D`d-CnGyI|~g@NsJ z${odta_rGh(&DDdItbVxv;}RQDD@|*6LGrhKCiwmPJzE z4wji<-NE0~UlGERv?Swz$I~uCD(L1%-d*4&qPF$c8FbM)ZlF2#P$F%=nvBq@h3YTH zt{`P&Yr+?WTJFy#-g!=+u`TY~uaiP1L7?esU-Vd8n?)PJ?K-t*790~ig|vZrwn5zH zDULh{Q`3(Wk&@aYuJaU?cczp2L*6;XHmG{}`j>Nfnb@=G4O}B&gGCi-c&kMXY+G}- zVfn#F)eW`EMdG+Q|GwgqwbFPS?IsFxj}_g8V1m*kDIlwa&oXu4;o*(Cpts|on#Sq0 zh_70pnnc2XfT|vlTlkJ_GHM@H;m?C3jDqU>yx1tth7;d29j0lsd3n6o^>z3Xqw_mN zP3fb+qev~hFnyIA0Lu;dmsptQ0LQo%pfC4#J!1mR z$-X7;s9*TV5X|vDd&-LW)RB6k1N5)n0om0LuS>xQ1HmUIFdvjOG?XYOl$2KHtxbV_ zOr?UmmsNY38qR&$TDg0`+|DaR?mHeUz(jQC6*O1&N~(P6^g*L9>Nh!iQ3(*^ zkOr;Pg-y*o@Y80#;A!^L1zuv%Vrhw!@B=p(pnwQq8L(X#eARxRLvK6|1Chxmi*T%! zP{{AK4Tyt5h^eyjUUCD@rFTDu5EFNAW|?V)fI7f2;iB|%i}G6a9|MvAm@ok6LQ5oz zgT+X1b=V9?GIHp=;?T@{V`xD@g0HSW4i18v%tc1#k;nvV#GNHTj~H7J3^QULcDqIR zeB7}6c)*XAiLxszV(3`0tgV2Ii$!ZH}2yG zFW$*eDe9Vs#HGp&-LwE`(=t*h+;got6?p8|2Y^h0T_qb&un(w1|so3`qD zwkU97)n)1jeGcz%c3HczcG2p+n1@x)X-c0oE;U+I(X^79RRJI^2-VlV;=W0ErIH2N z?$V3c@ABfN`mPz8r}IwrVsh!!`}OVVD#B9yfUns(SC|$}Y)Lz;;uFEzNJ5>In@otG z8?c1@DzsmIp~f_8OF6~6ZF_C8>Oz&~Z72$pJL|?s3}1aUwz;z1*-P_TadR3ns9OH} z{Pj-DugS4%cVn4M1f3ny?k-S8Z zePL!ok2_%|Ki(6#=0~iobRQc_)-}j-;U|0FDn)7^rDoAKOg__jb;(u$Sn*H6ob54B zK$LHlO!5ZIIL;F`N1P!Hm#SRWzQrrgYb&Bf5KM%rDcNC=#3e9TRUp^)WZh7nbd*Jj z(3|dmx9t*_ovx$cosa4(duuy8|AYnt9wrZ)MMi{Ia{4+o{Vqwx{@h+fr?azDM698- zbGi~H#yttb0BrM;`{zHuyq$43rWe0JJ{qh~cIfxl)8E^RIJ%JUCR3v)UWhVp61wA> z6JsZ`Gtf;m?WfVh^woK|y`vyDbdLOYYiEuKmrUydJ2Zk^M{P=I!rzvc>)n9}IY!n? z|Kl8r=)ztrKc+0XRFuOlT%-G&Oz*p}=0|W05o~#EW{x!^40Jq2 zl&IS}`d!&?i?9M@poh`{@hRqK;z#aPc>mSbi04%5b#jHs0#MLtm^y6={GE_=z2SEZxR z$sBd!p8RAB5*Jf!Fv!2}TLJnt8G9zPT3z^G`;LPjGTY{QwAay-N@0G|*Gj6PX43XP z*mw|F1ibFja2}E9fuhu_ChY#>yw+&M@5h^)LBcwU0k#keeKt3-Rxj*D6+2>=w;LMq zf)a_gm1z6=m{ePP235h>l*9Rz%=^|CLpIvfzSTq^ULI1y$i&~C4vU!Ny@u;T6mf1l zE4dL73>Q)Q^HOaJqRBh9)*S1JZW!z&Xa9R-zh zy6NI(+t7H!x)#qy%K8ml?y}Th4&Nww27?D`Noil-i*&->ie@)(lB{ng(j~>j1e{{t zEmGXkG*AW@^PvQH68gEDd8|4tx0|rks$nV(JA}p1Cow@)gsK-r&fr9CW4jMfT1W{f zCG~a-2oqMvZI6Lrll9I--3kpL{I{_oz%vi~>xlBO= z-gWF1k2Tb{wu2Q50CNYO7xis!|5*ctJpMZ z(9jwu4)09nFKoqDoBsNlC`HWoJUYg`Z;OpLg~EMu3bHAT(iT^Gh87K_8|7$ldn8GcXird6o|2b7u))~@7u+eMG087R zFWDP@ljN2AY-oB+f zV^k_hsct+u06I;V^?rckhb0a=j<)*+ow9SIiGjMc#yfo=SD#3ISh*?ERZiQlC~``| zTheT=qTA-bo42}ZwV&2Bm1gN^h`Mz#%Fnsq&CL}TLqQy?KjM$iv{jZS>gZ@kZ(P|x z3+WjAn2MSY^YlU@@>d(G%8r|3v3~Fq-!*gjCp87&jZv5+)uuNlcA{ac^U^T$OtPBT zteb-4?EbFtr$XgFBa=5y5eto0kYH85?W_>f>aqWLl7Muu-71MJf0101MErX zpR!9?NRap_u^h2^de{7TV#@lYLcVpV+5-bnMZM=B{Cx7{MKjgN*1G$qx7dO062iZ~ zrsVs27F9c#zEtX+Y*0*^)UiyqV4%LLkECn3pZ};Fv#x-E@YBGV~=K%h)HI1EXT??4VUC}H1G-IQ5C^FRA zV6W%uBW@rCF3mM?o%vT&7O)2}b_v!!&zgAHFO!xy8$1y{caqe8xz#ZLp5i^u#>$2w zLLf4laPpoGwkcNoCdNFb#aj$vxJK!-L&da^J&A8U{EW7~HUP9b$K+7UBUgE0@e%}c}eV9~*b<6%}~th4T{ zz@z)Bzvf8jh7O2|au&}{p=CrddAE3rujl~@ok~nDpaU**uF=G^%PRF6c*Lo@D0i_D zzDE~}_(7PIc~XP~k(f`@k?E$wXaquf!6Yg=`Myvj)y;p8WJ&LSq_VFyXiwXxe#xVf z#}i8LU6nxg3cHiw=tSnyqxH^ljhjwK3a8j#l00~~a^yGE>eT*hag>;=H?JGO+SU{( zj041*4L*YB%spZUx9TR7&-h!Hy_ujyQx*2_>Mr{J)pcjm$UVI=m%X{*LfyO+%p)vM|II$9{Zq@B`;J)9;;Z9sl)SigdAE?D z_ER2S+wW4SQcV@V?upbBl2%gLi+GaT{Z^fA5UZ}3S_D;ip$xt^#Bc~&7u0l9u*3|P znI<-xI2(!lL?Uw9k(vD!g5TERdbw(r^zk8cv9NHdS>x&@$Xy`#TOBcuv;EmQ$kXs^ zrAc{scKdlLC)K^4*dqtTavYV;XXiSH+(JT+~W5$84K4szcVGh zR5zOqV;+h3MNOiM!gjYi56GSC$RMGak5!WEza8v{qZPVG1nOgw);!0r!KsD2xq?YGyqZ+~t3ByVhj9m;skT{L(0NXcbA z2Cfg&o@O*uO`2gPZvzq2N&ONt*P=_t+ONn%l<@U6{D!!J+Tl;{WukkM-z_R$Mmo6B z4gck+e|BoEl25zbS`uXF*BxZw+EXn^(JBVB5griWI7lzoz3@0%|@3^HP`Q z1|Be=Xnhie#Fjmc4UcfOTH9H?@vB@+XCK_{C9NC|%s)7dzTJ=_7fHQ8stU6=wn3Ea zPd>`2rbiQtCipLi`w>xT%i$5eS-Gt35C#C++h^>E_}0Q5?0%;0PAM0Y@Vgo5W-K?e z7Pnmf=U8-b4v(RBNK?oC*C=N)C7NvalF^*i8o0nR5M*o?t=iWAd}TCh_G+RXab91x z^f>KMkg)TjjGYK|=gl}|*{#OxiMm!GF?7D9zzH&b7>o845F&8>A7KCHUF~wr`sYs; z{ETFxwTv-$pOs|e+c)#H9`agK#ICmJDx$~nBTn3;n;0$j!19@?v{-5yrU%J$^|u%$ zw0Fm6P23AuwvWCb?eQdf>s1)+ECa9z(mDW-He$WXM1F ze&dZFZ)@m>{K!QCw6}cTnmQ}U&2*{LxRnh4%+Z;O!ee35woU<35@QX?D``0ucfE?b zJv50Wpl8FNk~qnu8KD(STT1hD0D5>ZhOCWOksKV|iz!SNGz2xjvp4LA*&A>K?4dY9 zcPxU-hgds%{W?eRXC~{tG;)S2TUFr(gsPW7Y4W<~p$b(u=2oueul#E<9C!4H^`rUI#w=C+M(>4Ob)EZ-|9zc-hln74EbjUR10Rf0r3j)!~73~B~-od-}; z(jqO;?`&(=&tg3ugho)cdY56+UPl4_CjT^*i#~( zzPquNJKEBF8wmKfvC~?b^m~oHg{#k*6DV=5lZP)Z%7r16mF`{_7e6no{Dgj9Tg%NM zVbY*3G-k_t`&Z!ALuvWe;BP)v<6acag3GIr8X1za$0Wh)o!&K*f7@YRKp}nmXnxgm z(Di)6dL_AJJmn?*nkAmgnB(j)ChFo$o!P3^ppb}{`;-*FkLJizYWTi5({uvx4WIA7 z^bhnf@6Y1)x|KWHSCqSTt#|(s55Y20@wT90-(PW;J-j7Hy{nT!7aNl!YQl@63S{zb zVOpB`g^Ym}YEy=wVAXeV{I|`@-DkNkg#}3zMF%FVQwh8)7$n9KX6?jZD4>-m+|0rI z=f#aK91(SN_G~72g@x}TJ+C+OoG~FcOq%ARRTL(Z3u}EgIGe{V$#fh{lo~xxxOAmE+1; zYai~D00m!F3J}?LOl72Bd%C$u?EIiqO|9X|C3x+p25~zva4cA0;yN#ShF_wbN*-_M zl!TfYg*o%~p^sIZlh1J!ja?4Oozc^NLF_p8Xp;THmPEFs%84**xuU3qAl{XP1uS8S z4#AE6WIVqFtp0eD-JS^IC)Iz&FaDk!OUsCP(jRJK(ExzC=WYW8rxGGhu<#3TbWs`A z=dBW8$0G>?JK8BCBImuIwvG!CkX;A8gh=AI?bbXyVY>4|ULAitoCBtN0o^Y=bgM zohEv(6Taf>rCUYEId(Ux#OAf&WFTVf$@L%EdTP*gn=R{ew9eug>)d8J4D@y+Qq%!c3u$Fd!s+lqh{VBU|C~4&-5ti7r zHu$&ZxL;pKr8g32qUi2{A1bCr#r4|T(&EUo15kcTvPaRYGZhB$XHF_k^I0}%`izCu z`O{_wPewL(HnK8$efmqSCwWzWgbRlC>Z)O7nul%+-qYw24u?=N5_6BY(xP+#Opd(5 zF)#+je$?(TSPe|0iQc^KBo&D!fdq4Box(P-Y4!a|H04bR!@#|^ zSnOK4CafF|AM@pTHDu}vKVg=m6qUP3%?&94FVnAW{2Xorr=5??E741e?6O8dUW@l zIhO^DFj2F9_^`*n&{K*&+fWj2(hA9xoy7Q@o=e)#%`cz%!gs=-R28A|19n2g$)a&! zM9QhGl)$6iwQ1=vr`gfbLQ&_wCdIveV^-OsGjcF#2>uAn{Yzokqp>cMa*v!JvWP~w zgfI=lP55{hz$?8#m^96wTx5|IP{j)S7G^=&i}73#BsIk*ekoq^l#ADQoNs=FOFk1r zYtlYho`(d*Y1_wu=nO0hYTHzIFC^~iJv={ns>{lB=A8Beo(PM6gqR}W!Z&#QorEO4 z>8ejRm@Bo=sT)kL?yVR8MYE_6GqO5mp{@=SFce3&~xXH%mJ#X1tY)%UVEQBih)zObg!r7qTlft-8tE{nw~< zkYt6hRF4wA)8Q7#`@ke2k~Jg(2DUZnC)?r?)8ln~)ln`%yoP4xL+mY>%*t9?V(K>L5_*LGxNA zVRXX~w4kNsTUL3mX2K*3YhoIdX?ywoS%2@=Dg(0>uQ%DV&HtrAD2~Jv{kb(b2dcI2 z0ZG)Dc3*i!n``c(WmKh88r76&(bVYm_xDAJ8`t)Gj}>v!Q&2LdK_g=g_5fi|(-az$ z#;_C)C)oa|?Qdm}M(uI6>-~3;p$XG6wl5hhR&tz>hf`QYyXorD&c2E3q!q^l#->Ga z;O~s#JB9FJT~z5qv-dPyf#)rI4%AGnNa6(i_76`-0 zI+y^Vds4l{B3oZ4&1~UM*xR1t7D#i`r^i_AGOgVe&VRE7{Q^bWDem9aXJRm}_Aos7R>vb?oPk0(jp&>l`=)&@8Woo4JrE-`c&htL6d*%xdz+Q14DArS%1vHk%(#hkV1(Ij7CD*s#A3VC7vN-B(` z{%Nl`u(rfxmSbCU1sh6&R>w|3)ZMF?xToUrO{D`{r%GOZwCX8b5L_ftsMitgX(*D| z87Y0Aqk9U8x5s3qB}?J_L?lKSj8Gp14~{o~e5;WHVz(p*pwc#eZj*YO8}50eWsa39 z_`i3Gzk(h)ccvkC9FZz8{^W3r&i%^P`vg)g z=kox1Hpk+tlPmici5h7-x|VZ(4_L!iaoXik5|D_$Hu!|3aL_Ts!bCwgNvEUJK#9fg zYaGHWoRpgx$2$mt9u2B1Q82PH3(-;gnO$glSlJ`ZpjC1~=&qH+0;7Tfw4^tCzf3eN z`oQOvtvLXY^adrYDy*Us`h)xOyy9qNhXP=B)$t?AW?69WGW{nx+s;Se3#cKW zJ_lV>U-s!*8|1>$EpoRGRt?5%Wa1kK1wNfJT{Dv?x*F{D?#fjn z{E5LK)%Hp6kfOYP6-M_Y>~PMfpb9!)fQAke(E!>@1@L{}Ya)-jb+k2B1t#lCK8q%u z&F_8tKu59$AT9Z;*|n;1X?8Ap?Y@tOB?&ed8t8Y$l*zgh2e&&59}X+iWgU((&2}M+ zB24UssdG6NMJ7}L8n-l~5Bc7%TUxJLlSo%VYOW*cSlf_xY-c8KS>AE3d--tlregCR|Ovz!NNO zdJn{z0Mp)*S@&{M;6P{!iEPmo>hZQPz!a|Pf-_2yKjsAKYZGt11Qok7jgLJ5K<3fp z3UAmI0i`LSm8>nfvFvEJcA9K^h6tJV6dd^`-E=shUY%&+NwW+U;=dVdRENYh*FEsr z1BXrEA^CS6pO+Ex#6FOC6H?x8RbJs3;v^aMD*h}i8_$?LJ$4YpqW?dCqSV2Dgmzrn zTU7a7lvXQ{R%pNJPsRrYxlu3dk#;}sd+P`!dFE_m?&8)jZxi%I>3fINOUla;vTli9 zvXg{;J>*}7SPW?aBAGXB3{l16`{Mi=ea-%c#z*CWYIFHBE{lu$ z$9m`7W!}`Lt)|R?hJ4M_)wGkeV6`+bRHq@!QoQd)8BWQaI7$G149+InNZ#@}J*868u~js{cX>P%3YM>VKq zI`4vMP?Zr+R|cKVYIE(j>!GzZxTy$2LcyzxIk8_b{kq>)e7mE1EzrF}=w_}=&2GqU zW<&1xz#<;Q>A02qVv-fiGnL>1Qnm(B!SVO1ZY6DhE+N6wAT{DCR6+cz#zQHux-wXC z$mK}c0RIAS4TK{c89Vf>Vc4K}smCgPn9!Gya9_gYUtQFAyd6?5PXW4DanIWp&K7&D zKg>~5VO!JEl_G$MB3Jw4f5Md-B z;nX%~Gic7%E&rIyL63(pM?)P%no-7 z1_%|sluFs)maFI+@q-fttcc09Hzp$0!&s7e=l3zLr?f^S`bn3C=ArnF%F{LD57Jsw^1on=P_yZtAyQgfFdm~$60CpTN&w5Empw&#c6548+GZ=1e0Ek!rTb4mS(Pic zFvaM7GJxsJ1Bu#7q5~dG!ZE?}=yo&r|Hv}eu}4?~Yx6I2bL8cJ%V2sxjFCq&%?J-S z{L}*OXJC3>z2H43JWwLjXbkJaHw@7Z_g^31E7*zRPOpUJ?8B`zH}WQ(_3DdVfYAwp zT4{LFiA24Gc)_`(sadYg&apOkNZoUy%w7Q9$HMlXITshI>~>z^ot>c zbsq2x-f!ID(M-_bg`H-v`ai-Z|6F&HceglOjgKmb>_tJwsv>UQV_B&SnbVM3eb;&H zIM>2qryEa#9=CF~xGr3Y$b0zM!UvwC09jbx2?YJFN86e7Ch}M(ty6+nS(wb8$#nUb z%vfsabY~fTtvg<$4GM35Iz7SA ztm#TW_?*k+3JLcSHd00AZU;H2Ds89hc6v$N{OI*q1F7SXJwk_4bYX((4~l|}G(d4x z)(IA=^2}RuoE(rK>3Teu)eS>7$3uSyM#x1IANi;%e_HM@pt(21g9?tWBX}%#g4tXIt=+ufrjWoQVW+x%=Q_71tL2H8)zU5+Lp*d;U-qr zHf5U2h$ypXt1wl8+~WpN6fIlys$SI!&=hukFexmXw|d{%5!&_XoPIPI+)+nMQPh|) zK~K60Sev`y_VTtp)}fbR#%3t5my$KQ;Y~ndZ)mHBnGE#ukl$D&BI9KF)6CnRl9t6% z-#eEp_RW3cI&vujjCb~7;uU@x$pmV!KxdiZ5teQcO zt|u?!+xK-n820hcZ!NwnYU61lOWqMX^5k2y0y8n4Ej2wG9Dzl=EN3w zlvXx(ZWf-0xe(5ySGIp-l+mE_pp^dSDawP}8(2G;W0xB*0Jy#lr#z+Qy8}Yojo_mL zIn(HQT+RK(84l;Mip6zB*(E?jv4MuZ{hBX2zx>^|g%|J7`Kva3?GErPGH-LKntB~^ z-d*y9kSk%eQ)fa}>xRrTRXu6__ZJ;hi^o+*4H{!E6D_BH-)s{CX%)~0*n|o+7=pks zj`PFTbc;_CB_YXRPv8Ebdipp-o@`1c?29e=#L0thKQA_V$P!uJ7t_?N9ua zxj(#?BNVQA4{62J<_KF5jbaRB{#$N;jh3jpAn?rstMj&EVe*xpde9t ze_(AbL{SQ|yC}$Pymsk0Y5|`1BW-@ZOI{7`UtR|Be1r5OSKQq^pMY3K5sKKfM-c+D z*Ew&YL!0ca*;eLb2LsIZEAGOA$eS+6HKg^J!J$;O?~9=uNw;(}M<03B)LSaMKlZ;B zRu#||`;{NpdunNGGdMS9{ZUw@c_ja4Uy`AIx^rTo!BFL^AxEOszh=XVD?`O_zEkS_ zEcVo&B8N4I2Rd80oYJgKTIEPZci6)Eg4Xbxe+u%))r zBzZ|$2&NVqFBI@q6)90h3U8~-WnB&`KT~&<;T3&v42x-6zgOtyV7C?^pruN~@E&=* zT$|%F#0nLrZ#SL@ln0emDTM}Ea2E6-^y|_(_ z@~w-N#2JrH7UquW0_Q(2w!{Y2hff@zQ$-R^FER4g+bBm;rOuo)EeYfe{|HpbC78&_ zR>zsQ@QROt;rU>iB-DJ!7l=lvE6bxZ2&V^CVQ!W1El1-N&tMtDoRP^(LZ_y{8D3!t z!Zl*pGeIrd2&Ql4FE$Gso&5qmHHRzC&BZ0c$ZrVhfo*n27g$9 zkT9et$wak<7axc58Y%CHfzjCtm&JNQwSLd6Z}O!j42_o~IM{es6y026VYBHg1Wda#@!u? zC}(U3Z<`1iGb(M|OV0|Q8S_2Ldh(-6kiRq#Y)kbr0kdNUE1?I*hKo*)^+t8s-gZMK zKh?-3PwBmHIUo-Pd~1HEqO6Sr{-Vd(6G?R8uvc-v7&(C}25OSDJ4j$r^f^BX5kZXr z5bJ+rbdkahGtz9JncsO|9-l3Y#U3%8@A7XwC>_ zGFR@SHIx+QSQ^cdqbNh}gfO?rq$Q*w`evCciP2(}VpJ`zEd5Ky`?2FWQrmE zDYH@o{%8zgi*x?G)t*5+iShcH(6{e_BUq5fx(s7-rLykp7FRN|(x*Qv5ik-GFv0{= zqb9s|dW(HN`~6+3vFXWNn2Jy6KQ8OCpV4PE4e8*V*^nOhMBM4#;P+cWg3Ui5-S9z# z&SWhA`k&V8X)XJ*^7>m&z#(1$=@Zo^lLX!3{jStp{+Sj8$X7vDWE=_W7}czzHFZv5 zVw!R=*wSPNjN*-Ng`Fe|fR+x1<49^MrJ<=dLfI8LxI_)V6@(p?$!`VqQ@-2b#s^`V z+PMlMVZWL;T>{!B7LPYD2BwCmmuEsBQVJS|S5rZ}9h@v`vK)67jQFqPvEs7f^z4LD zTc{ceQ%VNjSClM$OcA1zKRacA=}?{l9Gt=ng0t{ym^IlUcRpa;Me;enFp)nD5Gx*z zwgNLJKzH~G3qaVrR<5tLA3dPRQP6BNaGqc7zRs(aDt+s`6yn`bbP2G*2i?=j0!1Rk zEGp_9jJ^yZi-nKn0td5oF893=>gvQd4%cHIYk$`~*OL3bDAG?dHT0a2m{KYJxX-BZ z)u)fC{wA)`i$SvuE9+a)wMK$P7RLfHrjp5x7D6CHOcexLlo5~8nc>}bQHCfFN39#z zaX)4#D%$_}7F$SdA>0!dS)bPa@NuB#a9^zbiN(Gsj~Uw(vV3v^^MO72q}RlcSZUcZ zC)1<@6`B(pW6@wwzHoK=hU>QKylNvy0H#BHWW{2qYSX1-&}X^s{( zbI%>^4?`yZG~SAcPud^sgMS$2@BcbB-jgE+tJ^kC|JY`Yr|w6C%#Rp^B8<{!TUnlI zdnpq&RfZHJRd)CRfZWAi-9Magus~x~fa$+qhS8QCpm?wYbKM zKhV|nk@V^CAxro$y6^f6qCqe3)j#L?kt^T~pCR)6YodgY(lUSMm3el~$4;_LzmB4% zx1ZG|)%2T?YQ|4H4XP(eB&tWWi7+EJx5HHm-XVu~@Y2 zt>Dp>GgoL$^&9S(p3&@n^+%hRls70Nu0~3Bk9hmY;zEirsEEYaw9JQeXKGGvFfJ_3 zH(%>vpX^FjoX6u#%7D%%(P7n?DSjE&ynM~7a=K$vH2~pG6q7i1Pey(C46Z%Zz37++ z*{|*s(T#X`N6l(R=+Bz=s`w#DV~evOreKL72}_h)NfQ?w0-766VS`-53&oB8jHyWx zI0|9wr#ZVZ|DHWAB9i&$aCPo{l`RqTuyvtE;P7PmAU#?kz@RA_AM&dwV^9ySt@E() zccG3p#vuEEB>V?pMl%&H$j$4gAHMdm8h5Hhqh1p(?^_eqpa!?$78T9J9ui#antGC) z!NPkS5DA2sCLUFnl>o~+zqn`4HDZ7zvbb;yGjmxfKN+3NZ^K_%m6rtzm2cCJYAMxT z!RzMqpS2wBPg6@u;T(U=9TuKVOzyFJdt5||k#FOKl~teLXuoZ+-&BCq($*@p`I#hI z%(zQUJR_xO3V(iW^_-j69{@2I8>%WGApM)36+wv`<{q*Pz#4_A@DSk>a%F8@v5CTS zN;|2c#3!7MFt;)#JPmS=^SIONmZs^cSw%89}p>eJ%*SP-TRnKj9(0D%VzQqFNh zLnOYP`;>n0{}ojyX@tbDMI(f^n25@rM$ls63}@4SA!H=ugjLPALP3~g>rV2*o{#H0 ztLt(3IP~f77m~nO0nn_Qo0ynv;daO}+H6AMd2V{lu=$D#&7 zsBe4Tx;Z{+m7{VV3qmo?la>koIp#e}BDsCsz2Rn!wow}TFpZ0ewqdl|YAZCF5;Huk zKg7EJHzux*`@Ky4dE(1WEg^{E`EPw0cJ{*xlw7Sl!2gPLtd#mxgF>Omd(S77-k*B& zUNCBL)5t`kq*luj>|AlrS4QG;0Ol`^XWyZs14&bzmC0d#UFrF;py5D?x43DLDIp5J zXzr-5UDRPlR=MP>6;8;b@*|Y`U@*X(z%}BZ%}}Y{aL-bZ#1W;2Z0w(X`(^y*Z+NUp zD2Z8P$0^V_NR(<2jbDjMi=*Y^?2TG!mZJx#{J#JM-7tMsmm?$iEWLfjXTePQbGQoG zKV56|p$P6~+V1vtn`OaVE>Yx?tTF02Qqhj5E04DNt>r2N4TMchidZY7H{Lm6F{i08@!wJkAA<^wCLz~9!eRE=e=AGenAMyF44ndym%$Zr!{ zT8zWm*7_Pv%Ef<{Mx9`$82D^n+s28W&7-endJJhVi|*b@{R7|*xis3DK);$6Cs&gz zG8?2Cqw{3>2;R%;-s?5~6P&0{;5fO~i#V7g@Pk-Zths&53;n2>Vq5UD-OT=~%(M0rZD?f)sn@f}+hQg*` zxgz$Zu@}6(pK>~{^}R3|KDHkR&FCw$!Mt$>4M#ocYdGF~UIsead2&ln+bY}V$VI3CzLKUu!6 zc+X~gXO(zrkTEvVSr>R!eRR-SO82&-=Y-rQmqp|8lt|K$M?&NfaidX^d9G%IB&3z7 zNH;GJ^tw|iok?9E*`QzP+oTQ8^vN-9(59S9(D@y>h1GP|)&mbCZF|N#5RNZ+Mpyc{ z9ihQ0u|P#txp7J=x3Ji!g14MJlGj_e`h(>fd-KX8EMu}`Wf^dT`-B=L1xh$Lmv7!> zB4%4|MaAs&oqTz`Rbh^$^JniWY3vNMSRY=rp?T3;QhF%%fWV!`pJgzIg`#apjq2Ux z!J~Q&!ALWgfa#|+Xh8H~&ibZlr5RFI{8EU<&B1($WHqBl%lu6?(d|dsD;oD+Zgi-3 z!<2b0u0F-w>T}yNy>AhRO-vkrv8=14AT1zWe)@WRz>`{0`XQ-axkRVg*>o32YZCQ8 z%T-E(at|qMy`-rdS4zetc<(U_|FYB_Q0;Sr+dom&Bg+?-|ejix_WSPubq0C zvb6#ExZV-j(cqW+2A3!F!2hDS5S#vpD;#dBC~doXZ}3+C`zAeeNb(Q zwo9VC*6?;5kWkjr5!v1MjCUWC8FuA7rrL*kOjM#?4H7WV)dhM!e;-#W`u=E1?tj!W zeH(|91;NaFM~Z|FQ+_LX<4c|%@SEHC@ht1->gwv*SLDqmS}(J7;(oGDW5x!s7W`xx zpXn6v3lx!?|4c*+@`HfUU{G3a--J207s6Hj@G50vog^yeg+qQ`tr;oC9a2*E)D52a zk|JvD9nx0O7^32@Ivw6MO@q!%WWM8E-9ax*>#CGoy?0;h+S}=el_=9_6+|1!7L6|7 z?%JJu_4i)wVS_tj_4S`sGLJQBpR=NBX~iZ&VafNNUwttbzZ3sYyk1SU`nRYpsd|nZ z@o&-!*Y7`)?x+ZAF&ZipF1M2!sjSqfK@>Yl_x!{_#QiQn-X&w1$B)%*s zI}KdS;pdr&;wqg4&0e&nikjq;!_WYUGc@ME)49xjsNBV^_BFX2i%D|-o3Tc%Emu>C zM04>JN2)yZXZZ2%gT7D^5euhl6J2DYlT+_v8NbyC*7x}@6g=Skdlk@GZnhz z3m5nd7_V}i(014C9*wzfw;S8Br3fi0&t4v5GgVd2a}}Knhwr!6lY@Cv8J8#$KexBe zY{95y=P}X9J$tau9m}iwkDul9du#768>kfCCUHo@rQ03Ji~1SdaHF`MA;M#NulMY% zd`Rs&DJ^U^3Ag`s52iErk}Lu?`n$IFcGmKSfF2k?W980|7$_U$tQ?}cC)b-rO;QduKGM*$wQ;JB$+66=r zH_+<5Ec03^tJD0&F4OK|W^4Y{3D!Qdo`Q+KC}l?(zuwLYHJ#MsCz+=Y!olC9ctYK2 ze*QIP<%F@PgRz(I1a^4_9yyT@fl>;4CmB*IL$5p=VgtiImA#AFbzBfjbrCjO(KG|X z01+0mc}j#Qptw45#Qp2Jv_2hMX7Ie6lSFoc4ai&6<0H}i0D(H7O2e;t{>Bk9)+w9TcR2@|Cn$ktl5oTrkMtn9%wQJp(b7+eP{ zKza4*RSwSF%5S16K+m`}lhzLXc?RT^Scr*`ktzv`rt*9au_?jd77kc)L;M$g>F4Ibak18RNHWJz`k+ zeH?*DEK);#P<>(W5Q9O`2m=qS$`?ibV3gY8@GU!(_O{W~_Vca3!@91|s6aX_Q{ zo5xl>a^|O}cjeAQzi46Z@~oM*q5>eCU>45QHW3YeOAZ<7NJ><*#7gP5mHg&ML^%4< zO=iTcdFyB8f(JKi^sn><7YbhAiSeX8zM9X+B66(+9DcUbSV288wQiXAcXCw+f%Si?Hq|aW=Phca4Ssxp_gB-S`_)^XTnJUwD8385Mq840c2OaGq@GciD-@ zK}vRn&|$-jU&4olGV$S|%=(4GaHC^Y0}_sE|BVs08kpa@M&r5Wu6f%c6!plY2`3BJrT+1 zNo$#+M1_X3uklzc{GhchF=Y9HAZ#34ubeEWTtbN)x}Vt>)pKFhT*Jz|T(%@3TUufP zjtPkCx+4C$ECKo&<#Q4d`=qZ$6$0|YA5>fspDu2?80C~?8Kjik&`MqAv+`XQI1mJ3 z0NA4lA zwwRdDN*VR-G}ka%fO7T6V5#g&J&%R^>)Wff)gD{!mktIfLs}~^r#O<21ZWt^PGjOt zIKhQl_}^z1!p|=OrK1w995I?7V?1LfDj;~IW<>~#{b2EafX6HP_gm?o0%$s1viPyi zmm*|czfs_0RlALX*i(EfWZTpKTq4DkABxHiah1yS4WT5?;jSlP)l7Xmn z@9FNMJ7zqgN$wSVBXaLqeBnQ5Dp!d7;{B2C!OB49be^d=Z4?QuQY4e z46)0V7t`<^+FW0smj!)U!QupKVPC^$4_NGUZ)dC?fBA1WnC{%cUJ9pEwTjx3_lV zBAbA0)hI%qfK$-!%`dLsTK(zbw?oSkSrI$6(b`gQ!E&fn zajlm%DB?=#GW@32NK2rv9n>OS0|TNLj^zr|=K<;B!q4NQ5&#`z zhIZE}wl>f%E0NZe|Mr%Ghk7bzS)5bt@&WZ$ME4G*Wk>F`&pA4bk_JVr%_=k=Uw@vK7!o79`|pu&<)F&g)xn2SB7mwmAu?d4eu&q z_yZ|pZr3uPHQ9Ak7a6SPkV-4q$NYhV5HpJV{Jiw}qZPbidzJ6B@c6o0hjJol>n|W~ ztd@#WNvUJ(TN;fnrBjnsO+zK0P*@*3xumB58~)@Bz&7$dsUn0-ViGnuZ`(+eMjyA< zX&*b`A9mUob2*-wA9}rtLh*DhE~leoaeqb_Tz=ld#Zc;aEwvO&C_zdN*lQHm#Ne%=U+o-@3$gEEuE zqQ1|qZ;JYlJDYx6*%8cy!D~R7!O)s&;4vBR8k*}OfGbAx3wbON3X5YrRK|RK28}rP zSZgr%7dR4BpXi;_8WKQyie$6d2HJUQXoPXs_pUL+3{z%K&Wd^s{(NNNlmA|>%*!fB zrHU12=M(T{-eApp1khSq-Eo!9l;r27ns3UgyN0fayx2%I2PL``4X?)mjgD%+NblpT zBl(#xULaLW5%z#}np!98=<#)-kgK)Wr^`R=Kvknmv%Gp6=Zd3yYTL_o zQdj291g&~}ZtXEVaP?M(ac>^JIf;Sa^_yM1xA_|iU%vl}=4So#)lSjB%efB?^KPdR zJdYso7X+kbR2#x922w1|qGtGu!aC9VAlGb&d5GA#2Y{GhJnwy)h*+fvjh_CC?LFM{ zyh^(SCxL|@B|f9DC_Iuto3!V@05x_@Qs<(#j?f? z-?VVc(iG)SY6ra3lxGKSwh5D26E8SNMiWIef&G8ncq8ECTmAu~P!aZOM~+LCW`@lW z3yhu@nk7#hoMYaYNYcPi)(vCA2_K(n!#oA-%0*cP!;Qv!TsbN1@D(PFcKg~ZPDIxQ zBrxUKjs7=~Ml|Y8u^E;b%X07!RhbPlh!!qpasO_*FTAX>a5k@8qstx>tv-j!Ix6vP z!>*@6FW6TS872;AD>^ufu!%CwV`Cla-~a!zzW(yMFHDTfM*a4bDi(FfQ(MZ@OEwJe zdfoRs8Jd+NQyXki*Y1Q(oJ$S`bWWbuBC;p`S_+Lf`){3lVyR)JpV6@K(0aUj9~Jfc z&<-cuI#eKN!khv%VlbW9`r>VX3GmrTbBwLug7ue)VXa#|5%EL2K1sao=)=MfUl4Om zMBB&Mm$Dk@RH_8n$|*N;9HXq-v2w)B{?VyP9W>eXV|!a;he89HuslT|O2drjS~gfk$8Le{Eb_(iNKh+y}62-`aLSmQn=r0puhp}i*XY(VZSW{ z_F(0~u#pUKCNTuz6Ci-9K`AHgt=x;fZ%tmp7Ux$7%MasIn*t3z zi&ClYKJTW}6y-r<-9gMg!cy1IW-JSd*r%MG8#R&#_AjM7-&{_V5VtXLUMp90PqzUsG|zCLKpw(Ers`m~q2_iZuSda#S5 z!|PMT=Nj*&C2N?~YG?t15d(e+0}=(%6;sjLsrAa;zu&3(rUAS4OK#l5QljcQBCy2b z4^z-8?Ivw7^(Wj60#T9qw&4&EA{zfixOvQ&?b5P59lKU7AZr@^`vbncS^#O(5KW4d zc6M+`w55)?+>Vs8?yg356Si@ue^|*3s3FYC_a60Dg_GN3*IJbtV6&}WW^Pg>Yb#t< zJqj9JCy1Sk@fVc#TL2Wsb@a_?_5|n3OyV(AkECOn-l|7;4o*JDx|#~_U#LJ~Nf`MS zLN!!!xqa*U+G&!uAuBfHI`Uf~aCq*wp-|S=Sl5TnNToOyqXoonxEZVL?LmGeP65SI z(J~|H&*gF2vk&lA}o9$KD>uRsRU?Ed%lc z2S$hHftdv|u#sk}n<`32T3WCvB!8)@b`MDTkGA9W)C4BVY{jFF2r1P*z8e2`%hEKF zs$FNJp}E>?GhlxmDxvv$Dp4d620S0#9Yo0C)AZYFD1o+}_4W6$9oT?YE#;3>FHb)& zV72voRXo||{#g&oONZl+&xVBfE>4yR%o(yKi6;XB0_-*2&h<4?sTcskKb?vzhGeRj zW~ju@1e|7FS7iQpq&Qq&rImH}8B0ZQbQ)R(x<=dXNM|j{-%m8>pN3!(!hDxuh1A-N zuvGu}@_Tl)qI^u=Z+ajnD+gW4EOg9y<@suX&Dyz(L49A~a9_}hr-va5TNR`XI}e}xw5lohNR?~m#_u9{pGRj#x5O*)a7zGlz;}-jc0GDX(mgmE{mWHf&gsIs20_z@0L*I}L`r z1MX=qdGQ_ADdTh`8iPSN^X>}B1Eu>odd8V&FZpp!erQF)wJYMsHud}R3>vfxp5Bue zJ9h*m`}JJ;`SblqN7;_Lm!cR9wywwx+d8qmp0bwq8BdJl?YZ;xI+oh5`TRFswnUMB z^5x(UIZLpn4`_?sF9>3H9G?iWiBHO#4Jl-3Fp8q~Esqi}dxU$0Z&X0B=2%nPB}GAw zR`u>$u=azz8oc#oOwgn#sv)}^kPlp&(DKA(JU&$@-XSS>l`S2NxHymE=*DO3qUfg& z=%}O3h0w{B_s+q5d|-_5%_hzlOEt_uWt!E%i1@IT`lq8$N#Rp2)}_=V;$7Xcsi+5m zH+*N{J%7?}_dUe# zD)E_0HNSbvwr1e;7H6B-oL(xwB4bN_H~J^s!|vvkUJ9az8)FnY#{Sb~-1E% zGr$GGwH`SXH5Ab`hyHJK_%pC(+}0l+=G^9#JahJsYUjwsPG2_I$`f;RQEt+nY|EGf zRZNx)@H?$sFPr*7idBndW?-5_vkPW_`g0#2SL?44<|9hG4nVbvQ$UWnXNo1Us!lk4 z2@x%)jghYS`0^^sqluQ4k^O!a%;pc%{5(qKDB{heGS?f^Y%wwSpBCSy-^R;3z_CK$ zh8v_{?cOt$>V>$j{WGn%gXZ=II)|r1AxxAX!)o|@?E!EAyew1-+cw2^nQ#sDmNE8%1!_})p`FeD84RZ^x;`` zZ!?B&8&KD=1ulTsS5R2OZ)N z2dhvS&g~hpRMZps6}(LL)#ueMmvi{1Cq1HhsM(PB)&j5%SKF^L4fOj-Oavx5>rSF) zulav+qEd=Be;Ns|;$qtgRzIJuK7Lo(?ROgf&_(`N>v}||7@WazAKq={PjCPH{L4k6 zxS6}&?#4qkZfG~H>Z_brT0Q3~qwIV{#7A<>_hj8wf;19@jg;0bM(vq%-!CK3UHgpJ z?<0xrS`o|I-P+pfqMb4U^kbZR)()E5%3t({2&u^=i84tkhsn#V-G8sNVQtff1)|OT zn?*!e+lyzHBvUj4{QM2S^;#<25uryo7V?{|5~K-Meyt6f00A+V@x;7!yyU(M^Ohf? zI3;G`kVj_!phyW>K`(rQzrMBpyipPRbnTII1}+WdCY6EcX8xe>`EO=s=HK6~dyi@K zq}EDgG32WSfQ^T*tL`kC%oL3sMA|&0hpv9N<86l*Tsi%?UJipDYn|Kah0wf^pSs3s zY$H?btbpP|{;BmRq)o#xMQhqp z$dxq$2+!~&j@;JdVF|Xcml~<82!9f-Olof%Xu5JWu@WLL1FKH>xBTi~{GD9T8=q}e z=Q`WJDj`y`$Z29$<$zMQ&U2tc6f`Gt9BuBmF~WR5R&Ar2lf=BbfVa;%?W)(tu(5ZH z8qFp0P>b~1mh1@N^fh(_Z2q|-D+M|}i9Tha(-vc>jetTkXB{&IzAi0k4b84>Pjk5D z2!J#vFY`_;_n)mW^=%v1;U#PNR{FUd6bv@jc-l3Tu-V!I)mL$>)LTEC44-s~9DcHt z_IG$UjQ=QkgX}Eo&{z2#ylaeoC-ac zzv#ZuK>akvxc4`Ale@#plS+2=MZ4nRq!h>}&bA6$N>VI+#4x;W3Nu(xU;RNH$am}m zW5@?NTweUnaKE(A;lonqOZ@eZRe;c(GH}`w5Sai-TQ^cM0s6Jojxp~jvJ=FgEWHSG ze9dS_P?OXUF2>I}VYh>0Yl&+sA@e_X`%W_d?5rB~zTdb=$okctm5Z^Q;7eaTFD#jK zqpiXRhqZnifG@bG>_{w@2dp-m(*BAx#&oKpVzY3QUu2eHCB$(vCa%l#ij`lNda}HR zJC(P7G;yXQ`gI3y#KKR)8yk$r=V>}a&>?NFc7Ht%{m0}6%~4a!pK)YI)+z`3k;svx zOupy+PjH3cfES53->zNnjPLqqaWPBS!f#1$76Vdh^#ZNduo`XdXLg&6pt|k{fYdzn)c|&{y@TIV{^S_fk9r?HD(Ed zkv+)nmbNMs__Jpv3Mn9goqfk>I3NjL4L_H%7vi;yy`{)Y>tq>rIM1@hK=9Wehvr;X z+yya9ONyNBy)*VN{5&VLTrXMm|9$we665In_21v^E55)R^@$mI!OX%VquJxJw?DPt z!lVRo0d9I|Xf6B_&ipvy9g5H#P;Z0D&Tk#EwPU;L>0*QV!-3TMBhkFvQ!F!-q*3%0 zJT8*AtYs(-^^RS6If^wN^ z@<1}w*3-wHH^O5|{L zu-kiVky)0C(p?Af|DugvbGbm|bpSVcpOlHJv8rq{W?cnvuDg#@PRX;+OzMb&buKy4 zxZgdVD{D8agYFS9r)LBx35-11w`eq}n}T1SPA8F=0$vqiZ;(E?ZPftP>pCHS(JYeg z8xnGoahPJE5I#e@{n{2SBWYp}eR2UZF1Tv3=pRS-k zkOqcbRK;4ntmZ)DTc!-!oz+P+C@dos?5wuyRA&SPIOJe+$pWWF<32)+ z`k-p_`jvMmv(7h+)gWQfq|ND6kb?+GLh+e~mkWG)HM)owtN`9UAa{-!s_}1?7aCwV zJlA@rTrb$f!~}|%tt40_C6 z$d*#S(QAeTRZjN;0=SG>+5Z19k1cDCRaeK3R|pRd_VffuWr6Aqon!#Vl5(Y$&oW`| z5ai~J7;Njz%C<%K>5jqsP}$vRdTichwy63ZeDNViO6V4JL8rsn> z7gAc*^fzM9hmxVVavRLzg@GKu*_>H#iO8-Q+ZWd?kw!hI0wjOW+rPM+xwRO6Qf38^ z!6^9%^=yv!MfnN($Yf6VT>+%Ye%N0k)r!h%9s!3i*ty5|#urvACRWij@=P|IZ}xn4x72$&FKH zLpIBUym@Sy9wx1_WkJhx#^m4kWwgLB$fw5Xk#NAXQbg_OqkO0eFxQ~Hn#PAm9ZIF^A{6E-R;PD9_U6MoT-b)yAyM-=4*@Zy5}o$lJy(9{&;UL~6hh3Cd&=K_wHq8c?Z5?%qG z#MG;QjJx9Aw!Sine`RqrWOgOXHshOsCyn)xeK!5qYCGyuzGj2lgUpvYU{Hys1x{cj zyi%Mgoz-&L+zLOzdUzN46>$xT%yi!G{n07WT<1quph;4xDp;znW$;>>kU9I)@f9v- zXKRaT|8XQ+g<&+bT7VaMZB!cpE?sYj$|n*Z&J-2#x$g%@r*wwrYDAlMK;S{uhXD}v zV{KM^LT$$UCEOGpktwfOD<`Q;9@iI7yK(#Cz`)+c>NM#utq*h6cc`Aq4=K+6{tz$B zAL|tEjykCKpan7=)KDuTE>`mfxsitQ zAAY7qM;`2Ne$1~3_rUzecEPJal(oI#TcU)tj80MX)5Kb`r0l9PgE+|27yDJH2dS1W zxzN^>@wcq=+-x)?vO7o?@wuyGsKe^ z$%V@Au|S$$v5!TZK&e}x1C+ybFpny_^fWI)L@sZ}@^0|@-q|u=Y&1T>p!?7M*{w;C z4k`ab(b0)HbW}9f_&@;ADq`$|u}-UcjWQUZi&VZK#jXXU&83PBwI`c8!BcZfy=ups zr5K$Rir?R3a&Vcj;%jHC$j2WgS>-r2}= zuuY5qyS}+=TB^$ENtReyIboBz7|hs*61(cwHLRjt_Q2dHM6&9pPi)+ktg7na01e55 zQW6MAnMLoO&oYk&A{!HCgIAa&>f|Z9h6iTpOb?(IN*3{Y33lSe&gcc-4V05Mr7Xx| zT{JDWff^b2dKYh4I9YDqlXX4qH}hYM{X6A_>X;t4KasVd6ipSKCW*Y8ZRVJj!dZ>G zOHsAwWqTnR5G%-k70Bk5W1}F=k1A5h*vLTz36VdZw*gzNir9DSFt~B$*WRujPe3kf zR!LBhN{@@V@@jZhkU~5m?UxM=9VLC~G9lSjwT@r!!YuvY5lX+}n3W`3$9P|HLyXdN zA8&u8oppBj&EumChj!Ne9@;M3TOQV>ll3B31~+-xKVX5oo}RWj?i=I;^YYWyaxl_! z^7Hyq)5$wNfSY~+zhRmzsyxM-%7y~rjo0e)Fk9u?7EJr|n!_lD0IT!O@5ong%1}Cu z-MP{fwTA4`Lsd>aD)9GB7I<;^sTdz0Ny;K;s|4bd#Hb?-CJ$7{@@nnI3Tz-Cbi$WX zS8E{tIwS>NHl-V3QpUtM4?tn`0xO=0$6vWD6+CCvMyf=BL(V;` zHG_!D-brI(qBC1>OcjMK`hIrpM{BGW(!!PNpPoy)N*jo(eWn2Za7&TzQ0WYgq!Tv$};n+-q5nA6vgt9i*Kh=k5J)``^vN>_68%$4?u4XPGP z_*A40(DhUb=IF5v{haM*Anc#nwX`@%Cs@)MO)OH(AEX5Y?RfvnMwKe^Ez_q(J{V5k zx3ZE*nAFp&`dJ_OZwrVn*49{ll(_>2GPV!39-lgKSL}|supJ#hd zl_m7|_g!eIDN@mpc!<+}=w$LLK0Bzh3;Mh8QcKj9JAUl~6syV6seAqtf|ge6>(|fl z6bzrXs?jj8%~ek|um9l7p94T|ew!@rajrzdE^AsGX^zP7pv_JTO#1Ri?wpQ3g81(0uD%QgIDBtKdUJ{#& zw#&0JkM2IR#pB+iCW8+>+B~zhLBNOO$se-8NVo~>2xcI`7P=<<3nwVBtgqvq%xec# z2lJsw>92(6{3NRxz+yI6u>D$9+3R9M;N)run?b+-NQ9g#uXb>v-7(~71%#|2dBYy* z2Z`TJ2jZu73|k*>iW+;$IrND+-;+pFrSIm-9xsZGpI^KyYRDZ4a*Bi-h54JNk5q2I1vc-<}T9saUPRF)j*y%h?4|%x zt`PQ;8tgCP#=Y9TxtM=5T>X!Z;kmE$6G(3}^Y%IC!>!J_hiNBET_;@e{7%5DW&W9t zv8EL5$w|pFtB<9nl7>ov!%yclCt{E>fEqyf%UWlwX?%^CT3&Q;@vmK)9&EgkPrT)+@~coiLU)sc87w;ltX^ zy0`l(g&i{~d;kRVJmp@-2x0uvbVNqopb!5Rrv11=pQ8h7{Y>BcNr@`GcjR^!zF>hk zA&JGD5J#)2$;K9=gH`E!lr02%%3d}G*OVey?u=}aek(GAK-=Ct)$oA{2xQC<-lBZs z#E7)~lvqQ|V`y^q@>|=i&1aOQX$!aTjIq*DCgq6wZ~2R|2rt-=2&pR3bKc&t)eVOl z-uCMEz)&NwvB@iQg;X@`k~=@`v(GwZ;K za-ZSryP6ILqon0A?xca{$(SMI{oT?8eBIAOwSrFmN%HvBG~=^EC-TXAp&@SN>#7rn zl0UX>3=I^$ioba%A=92BFCh44kJ3-46;u#nb{x}10}zYj0VOvja&#|;4#wi~1BCIt zt8?qohoJV!hq7qfx0Q}M8J$bPkRg=4Joh8a0B7Bj{bUxd^f_3>NM*7QSA^>>tT!*VB=>VF5yXEQ+rDp1XRHkl0O<9m9wu z=W;qe?xDK!iiYhl_Sq$(=4X^+^EEF!1w@voJ3Gz-GaxGsuLsO9J;yucJ*1kOM?Vet zAXMA=&sz4t4N-9UpUF=8JrDISd;1K4&<@V2x-4fOM8Kz(m9%L3(9BG{-cq?}Nb=g6 zTr!-QrCe4xU+{C*Q!1nB!obO-7a|j$ zkB)V-70k4ncpX0~0(%9N8C~v{ZGau=}fs;PSw1Gm@dfhsOaY3q6zp>Awthe$^ zs_D92bx$qtDwCWDFog$WKw2Uh`Ce6aGJJi6(b&VI(!FS3@8ws`BFr2!eJqG)a&1zE#uzTwR%~@!=XRemFHZ>RzFMg{ZxJ2 z!-2PNUDnPjP{moF&qC?9fxTE=%O|=AzE@lQ4)?H`PcAdr?^{FhgP=_VdfQhHpZzGv+#s&B)}QG;<9OvBIlhCz&T6~NGSGsJ+>^6we1%xs(k3eGMBdmcRfEN|2fuuNru35_|V6+MJw{M=(pWN!3p;Q%?5x zR1$zhGFzT!YF}~!WhTqLTR!oHd60dZsfO&KEYgR$Ldfh!qPm`utAFITkv?~^WbssEVX31S&phH@fzfZYU~d4$oy(r33WYGCKH!U4hVE;v)Lyob^G1_2RW;VROl z=B9@Ljsv5pDAmR3g@obWhrL>e$w~P4$S15}^Ryn8S9wG~qusei`Oa#L2Y=tvz<2qY zWe$g=t+ zD_d^!Y8!?KweMqxS^83_NnB9An1m+yh^o1?0~L4qer4TRzN~CG_g+eK(W`%|vdWSb z7RReF>#Lix+3k;l&e{5jo>%=X3J<7(g1`{%CJ-pq*p-aO69r+ER(hKn{QQaM8jgab z`RSZi1Jgtq2e6KXML?*Xn^;EJc|RX#3qLlVXC(L*k^F#~u{s|j^G7qj(I%S}i{~mT zE-=#^mg>+ZH3zPU!Bi1 zN9N|Egc@*F$bN`K@(bEq%FdzL)6ZUAvDEsL?puIOh7|F{Wsr{kEA%TgDN`foiqy60 zHK3Ov?bE^UMIq|;A|(kRneYR%Z*Js_#N^)ts<~UI@s|7vloZTbUeb+)zixkwJ|*aKrZshS{1kPK$+zn!v+Sh8ETRI#L+EE;p>-20Ro zGp1I0y%m`fL?HEbgLKXNJ_|MM-IfEw^OENt%MHyevX=dJe|AvkuZ_ z4BtVN;XZLE;!(%7-u&(2RqRBHf7;lg@eUK$RCFjf+?c0;VB*=EQwmU#>vfS&4i{@n zsIl108;2}=Iye^{A!~b4FQyJ2LpNWCj`BIrsv`9vgY@{=Sol}YF0_e=n3FrHVQDG- zf^~70?6iXM>(Y6UNH9qL5qo&2xw7s@)~J5l`AZtEc;n68?L6dUZtBmSX#W5*&7a+& z*+G0Es66^Grec61@CE5rd1E+kZ>0dKl3AG&dx)d0z?Rf}Er@Jb`TsgRKE7*P{{={* zZ9|Zk4_<^iyjHAOgn-4WAF+x z3Lr2phx)DKB*7L#btufThAeIV2+eH(nfFX7zQ2Kx=oWpxZ0`)Qs<$CGq0|$P7(Ul- zs7EUVn!0XO6rHh*FPF-H+8`;5mVqDj`E!l%gP0|u&8E95)Mh-%ms-dp+(f2XF(Hq1 zf#E?wx@gKF2dEWk!sxhzrWo1prfw<|0Rh6n^6~(weY9H*7RFeA(z|+ljrzP)rCLB$hR)C-49bXHR3h%NKx_FM+Tvp!V}#qq+zV*KZ3V ziMvo1JSD!!)23CtHe-}d4qJ(NrJMpEL%@C?Ry<*c@f#j*J`jm8lNJl~m(gMrxG7x7 zc$E%#wwe%W32C+Tz;cKT#w_Bn;NQ)^0N`w_F+tkLPXi>Q^_;OcRoG)bnDVWVVaPJJ zoC)AuA8H{LSJecgKe5$VFVn-_uTJoW*gXI!}ls_}O!~YaSClN7ia~O0_ zb+v}2L}dZKHBNaf9L)CSt?nK6AB$EYktDn4)#()T+|m@N!gaWiv@_<*o_67wgqL3O zcI6<%^HFmJ@DIAnH%CW{+<=hmuiZ2v34YSP3o(pkAW2=KpOT<&%r z-lK0((2sZy8)_t`(zda&*>0g=9n89qp{+iV$9|b*N1o))f%>CnnCzA)RfKYaWk7|* z()C_fL496+4mHbR%ymweksx29X~c2Ge%e-pw(S&4N&!GQSwv@DhMM_@rl3f=z0Pny z^qi!ZBnY`i$2G5Ov5p#Ir5zvH$@sr+W>#Nc;{tjlXL3IV9!KV##44d|$ucKfralcFptgc--%| z8(FjdHwG2ibr7fOut?eUMx_UMOWo%Mju{L`rUm0NEu9yOh0iPY6KZ8qv>Rb?QqXkk z-IvX??%kH|H!(9f8!&2#AIlhJKz`{GKRNO#Vorb+Z=mi8we7Qd6%noSN+X z*nNS<&6TjC5@H;!a%EcXNY~o?C?h86@bW(>abCEm8dUmTor$*BcDMrC@8j%pl;Q)z zkzw~i$#;+#p!P9u#sE+g<`E>`+QfbIh889-?v#T?IYxIp1pW2$GJnf^FqAys+HATT zzS!da+C4p>U)&@s0TF|~;>KtGqzl6$5|FR!fQO z*RX?Li=<+Raz!eC`-T^~=4v)Q{r1+s$O*xvFuzf4cM4DCu52OOU+aFlhzLOApnwF2jd0i%@Et5E$n^Ku0(xOUN z&V^+M`CcG@om#Isrh<+*;9we)mV8Rd(FctBFaLiQ7AA%F%=ju8jjixx)J?w+r1tg? zlyUm0hKeTEE2Td`ZF0nD|FxSNV&6t7tHBUQ91;cI754=>ScyYhvL>Yo`-66T>{`Q@&^>A|$ zkgLFJZAIYR?JnF~j7Df;)AZBY12yn!Yz*4>`-xjs2Pr9BUZ%M`B%h_Z+*N`{nNX8w zbGk*}^}_rV>Dz~EonmPC&kf8*KogN#IK-9(->dn8yv8%aq{M^&yG={K6KqnwNp?I0E0YEjV)!zgkM%opNX{I?kln`*fT4LI=PR>Uz zicSh=qh5jDkECEj>{RzN{lnBi;9{sgm`O>*Xc?FeCnq`=b$-+tkfJ(1u0yk}Um_V9 zTC=Z!4D!Gw!Gi`_-p&}Vj;3V*suNDhQ-Zusaoz&)dZLrzCzZAt#VhVT0Oi{)VMBj_ z3D3CoAqTcgSLMU)-4>@-2LB}-D`gze_4jqqyXFww;InW5RM>&;;m>snLph{WuIOL< zT#Ee@Hzga9GjRzT%5Nd|HC8z&7wC{wicSB&)z$5trIlm-4^`i19_l`iaL_xWc=Fh4 zOq?3ywR{-~IqwA$lP7I#+aZyRd z3wvjHBSs&VT`Q96te!o1a7_cx>mO*D&#O-v+^k`B;#%+fUs@kCR<%7c^keFF{qI*t z@$%K$)fKC6-@;7^+{-keQH^aLc;_(Hg8Cg9f_jzBl{g?jHV1Xmh;cMMV>>OLPHMY30@hWd2B1yw>%%6xC@yv*LLn#l-c+?f>AgF zVt{oSd(Lv_-xj@;*?f6Q?#hENAH=-AEO!2jSul*Fp{DeECN`~Q4b!%*RD6l;g3Zvo zOg(FOB3wt@;AxWB{M7~5eC-bs(bvCFf5}3fC7Q?$vWpy3rWmT@=B26d8CKdL zVh(ZM;897iMAAYgs}biNm!BUb>xVk1c^m&-e-uD!=RH9G+LHKVXKjC#6W%@%vH+Pz zqr%UL_GXEMRL`C$!_HJO%{H2%UbV0NE4zJ?WT%N%Xu)7x0EoPMNJum2^8hcT0>d23 zY(7yJ_gforS~q)r{paR{6}B(%OZSaC%46*XeFoXbrERK5=2}Pe_nI>JCp>&|`)x%x z!hf~oeTp}?hg4WZScrD`e-KuCq{aTz@6J^!%9ON04qn%r?_ZOf&YpCXHYskL7zLck za|y){%uNrPk!OeVJ`+3Rpfgo$7Xk=RL(zCoU)J)fVgTA-)#T(TMH%Y`y}Tj~O>lsR z4eDdPBn$yT%UCtw_x@16BD&lN6q8}9NY?PnRU5~II=5DzqK_~t9>T-Q8Lm^0Qf80PY*v-xHoZ9HrS-L2D6z6Y9}h zrtYqc&d5M;YAFi-R8AvUiji|uW^|Mhyn=99Wk*$rse7-Ez~1ZVluQ0Esn=efWasNp zco_UJ?bSg{y{l4Sjwt!e3t44ZPl9>TLzJ8AZpaKjGCF!kv-jio?SCuNGK#dp5=#Ps z+@@T%`hg~V^-@o^9PRgN@w|xP@2g(#dk#fQz?_>9KWlFtw5&D}PlJU&OpzGPF+S{r zQb&37`1%pPC2#*6DM6#zbeSf5Rq;+%vzHMg^!PyUr0{&uLe#4#jJ8X=m-5%WmA$)0 z)kaeX1yNFuc!36^bCc0K|AROdL`biNlBOl+;tVdtS zfyW;|k|CGwxZZwPH>WKTIA96L4(Fius^#tfOUPPUI=9$pEuu3dMbG0&*Frj)J^?}3 zA*vj_;HoCcOm0Ww4%)y{!slspYk`aJ_&+A>0a_>wrtEb+2+gLAlYuT+#OpE zm5_DebtcO@o{m*jlna-;jD1ukLZGqX37t~{=gmI|9vV0 zp}`wt+)aaujv-XsOWpT%pW0eo{2)lM%|+QS!dF5x6hU5a-hBHJCazd^ELi5(W}yEp z^+F+F((Ng`V!{wNKOiOzo*C`#b2;KDn!5;9WkwrCEA=Gdff%{EBm!KXIiOa2GR3wY zJ`-oGkax;o5Os`VwFn6AdzuD%%NN{};#Fj0;Ruvt=C%3;n*PQVF=o~34JSA&sNVJl z=vsNZVQt@|$5>oTttbAO0Q#%wZ}h+Boe#Z>Yhw!q1qHMOTL0xVm9fGYDvHE>5v7GN znwu=o7{M26b(rr5j|&e$O)fi7E6v6`qv}>BcoRu;g*hSSvW}1PzW$zb7!zw5vJPwT z`Ouaw8q2^Ov>ym0GsP0-YBQw^>)N}QghGxhm(QN%3CJ!Cg^e()Se&rLvsIh==|mC=oDG|gx`L>=E;UEPg}`-3Y}?QXcDS}gQG zX-6&h=H=^-6q5b%yYGSUK~ba32**Rp>=wm#{YU@4Sn3+e98Idk(!P%1Z!Pp|Dh@wT zN85LWFb|i3RMDBnQo=aZ^HQJUs9jYC&zGb+9ZK*=DbL2(qCr(ePeUYp_tFE1yev~IwYn>1T#ldkoa)VruSGpD%3Fc6|^ zBED=C9$e*?K&QoH<3KdI^nz=GGxhG($JGC}8zYXg?L@Wu;m#>CWLRk||3diNsDWv@ zcAr9G`t< zp(IMrWZ%4!KO)ki3xT-4RM!olUiv;4706qJf?+8XB(b#IiSYM6CeT|J+8g5#pG;Gw zlE`?)=l3lwEI?w^Q)=lqxF^OVPiZNz!c~#K0-_ou$|}`4<=k}~HJo{(Yb>h)HGH!Q z$YJq}uWuS2Q$s@s=^|4j4Jus?S6nb;hPJLF^`5#i2z*GDbhfOab@qJFdGx^tGpL-TDwfb3~Izok|hqY$N)qLAdfea+f$nm9q>G$?jhH16lLlg!jFGz?NqlU*UOP zMOit~7OLM35Ud)lj#0Z?2k>(VtpIOk5h_j1*uKl^Bz;jhZM5uv_2fyY>`dRvaoOOc z((DZM&-rC{+Z*k#foKH#1SIwDqG3WXxxcGEEv`?@E7Z+JwXaD6<`^uXc5=h3qJnrJ zy^VFNF$U}z)6kHaKy(T>IOA(E*3Rf(q$%`6O}M5LU2Tf-505mNTPX)j$ z6LDSj-DCP0JHZcoX%cnBL&LkV)wGOd9Pm6kBeR z!2qI=Jf)^WW9@zdL;k=0@%zMt6GJM9>xNHF1_w2O4AR#AV-71&p(@7Po%Dedb$9DR zIoZ^TXmr2yxj#^--fZu@*I3&z;gW&bFKcCP+xf3$aiXoMoy#M&mQO0kA;V+nK9Hv;nm9_l z9M3d^fYoBw7cr!8CBxy0p#e8OsG~1kk8Flc_&%|v(6mo^*QH-O?l8>%#F{v27kUY?G?!=Cm{+q=3l-MUuP5jMfpazNjY1q!1VC(d&vC|n z3puZ|_Yy0iNS7w9vjl>)ocv&u+e?@+{-7r{>|?~c#GdfTsxxSjB-ecItigxPyq;4A zl@H;4os4H=ZUWfLHKJ38eLy?2pycFaPFD8ukdtZU@b4W4J#9LR>qqc@uLP@HAeXfL zZ@e+ypS0!9T|X@(o4hDOxZsm(g7{Y+-WXWD%NlT!P97}pUHhX2-iv9A%H3*zZXf#_ z&V0Dg_QDaY?PutL{qq#1fSRWd1B~eC$f2r5vb*EbH~{@XLUu&ulTOR>h0f1G4o-B3Th8MI7z_ ziJONGJi7u*r#81RHSa|o+C^;nsSZGDUIA2ER$sBw9?uNm7|y#d0fmWhU-ird0@2gz zeCvNJ++B4NMbU(vA=TXEMOJh}w?O$zmnDLq!D_+*G>5tAS)laSEVaV_tC+ z1k{p+6UN+6j>Yy+(VDsv!?=a5J*!d4$yvhF-B#)*bEDVW)^#|-&426dCAz-aM|2|= zF&CU9IWXx2EsS&~g!X4Jjo%)@&oHgTeI=DqwO0Vl%mN@n+ia9{=55}$EZ6ZpsynZV#fa^w_4ntq-6;Lqb2fpEPVxbvxYk9s*@Ma-@*4$W+TUc&(n(RR6pj)YbDv(GIB zyWmMRb+C^98X8RMzk%#B-qDg}eX(nddz1q#>&?yCle-$t#+AN?TaiT0U(Re7@TgR5qESgP#62eqsEX9W zEK3(eL`F|bnYRE66V^WCAm@{iUjRun=rA2}{@%2KRNnwd3LKM_t^=WKy%~xxOC!?r z^D87zZTwxoEIBHaM+q9cwX^gq<4!=3@0+%u9&f>LIhZ2q_)SPEqeI^PCof*FGD0fU4n|we;u7Be2*2i1`*xNV|^!whd9gj zgvm2H33}x7!ifXY>OSy|y%^v76hU`iGDB|zga`fI-X4#bKHMOmaJj2tW^wvu)#Pm0 z{8jjGphaR~CXJV921f5gOIosrzJ*39zIw|KJM>uazuzTwpRZR~5q18ojXSdv@p9fd zZL^zORaR-1-`wovA5EX)`VGM-jQinW;ewGio2C zBks?Qw9}Snp;$J|6vICF#XMhyVptl1n)y?r7H7|msx9zT8EiSGOtu>nA8Hm@u2tIU zyctoIF7f^pI%$%;!;Rj|7l9*a_JmFxdfv&NsRTVPOfhkvu!^}cA^!D!jIYw{Z2Mla zgCrKRRyT3_NDf)~!pHtQBv+C2ZG#ABXT_EaZ*KQPsOZuWN58P}U#ggxL>j3|Z7g*c zSTi}6akb6ZyJ^o$3%pD8<#*iB+FpK`h_rNfuC}hUll9uBmwTspHY=yaU^y!}GHnuRLC7GS%#|EA@7)&M1onSlWZd5s!kF)dRRHj~6R9Qg;L-t7{ z)|(EVb?M>X(v3rsjg6Zz%5bz+MNcZ5?dV`DVFhGXXJgf|X+Ix=&}Xx(;u#~kS2kzc zDO}bshv&*~E?(bS-@J1sZCPQtzWeITQg6SUX``V|$^+Ma8fVvWh4f-1UB45Vlt?Ix znb~@QUhocSF0_9+^RoxkIhF@!9+XTsHbi!6Zl3y28C0J8d;Pw_{~+h6>x`bkgff(p zikk8RvXkF8mYQ~eAmax;fRcNxo=gE;FdtkP=$pCKr)GVU8X7m`L``Do!O3(G5NrY- ze~WZD-X@-tZTDEfzlWXGzwTxNcHF#2f_q1%TW%S=-dR7kpz_u^G{}dp_K?mf94o0^ zml}E>7tt3s<+cvOoHU*Qh@OFwD{5ytBa#2zHr|}iuyxKL#KQvQK}B&?m}FM(Tk-_| zRI9d_q|CRkRM6g?Uup z33JZY5&THdOFf4o4IHi-ZVOX=teeI<=1nfwxD0PvCn>0|0Q}pBVI^Y07onpRV}1O| z=!3Jj_c@e_HDhXSescH9JEV$sbjh=y2gSgOvJsRs$h8TLyp;QvzM0zA2L^6Sm!wOp zs;K>G=LVPguNoN{=UZ`0Z58OaUosr92-4IfUuFCRQn9$2(t1GK@HgdhbTVqvewYL`yGYXrFkw_hMeG#%-n}#wl6dy|C zUh2X**@;GT}ygAb@17wozoFpsK z=YOm|=gYUuX!f3NrJx>ZaW#gUvbOWm%ipiw+f58`)i1vP=|C_RMm>##J^l>jVLXqEEh*Du83!NRUtP1Kz@-p6Iobgrwlh4sm zn(r#~{lP{eI8+JQ1*ZRQRn^bB_8#re#Nef5$m!9nrqtk<)#@FQS^ujCIer>Y+|vZ? zjYh>|$M&5iF$pu%>SYI2|3Rf)ZgJk@M?*DLWJ_$4AaJy=MO+PvcqhniToVb?KsST} z9;oH;_V$85n?eDA>^LOzgLO&Zy3|V6xxh8z`l_AemQpwnh&uXLPk2;&yodv% z+QgDd&ynL3P7}+zNYw?^3hFTX(bpubh{8P_2}kxvo5{oiE1e2kM-X@ zNDWI{%Ruxbx9xwcthe8)cmDR&4aAW&WuB6}9WIHW&nK}97ml>D8>uy7E_t_3JH|5K z+0hR;!VN={Pzck@6c)$FoNA6ZK9XF=dU*ul2njYtepK3~97>%i!eD>z{EgmDqm6gN z&UYS$AEta*)}P%;d51Wy7NJnm*_g9>J>5>=3HE+z(6SvE+ujJz*Gbh0-=Q|YLbs33JTfX<0yU3y}H zO~{+Vd#}m8s6*v6;eq$oMRynYBUAB~8prk}&IzsWp3UZZ$TYL}mk4lc9Y zUAStGmq^dM>jr$Z=!iJI zgXk?GQ@?~*_tOHBVq@SK#N2#=KjES_XCj*+6L%t>YNIsmOd1zPvL<1-1pKQ{(~XzW zzTRQ>2e98sQb}6;Inergi=38d*5?6_Y3Ewq82Mt121gq`JdR&!H*%K90zQ_}A1DOV z%v~e*xRsWM`Gj=zx9#WS?ahU72Sgo1s<6&i2F)N74i)JMAo@M)^?@S0->rB$#(cBj zXGeS5Bj~0jOghC0?2Ay*X0oUU5D$BSOG5ow%FYG>IDn{qS zv_?#%J`?w+<5!kdR>Hh+a*|civ@H}&_Jk`kkJZ<~tYFvgB@8mN%X7|H?6EdHrNe$|au12nT=Y=-k(gnLNG#V}3GQ+NbI^nafGD`(x z@eQ!7FDgFcT{@|O8iu(UfVMXwktUu92S~ZNsbM7$NYRxf6`J&P-mdL=+p*Hp@+n3J z{y$!CA4{0K$>+oRA3Q$}1VHwfc)_m?JMmi=KLPM%bQDj_Ly~M<_iG|j{bkZ|S-&Zu z3I_%tDQ+S!u22qe4V98*xHt^`_Ioo=rj^!YEY=gdW>P6aRMpU{&JH z^QeOl+>Q5XHH?g5(MnCBzMxW@YWD1>BetBZfAmSGZ>dqeE|29cDoKkzW#%|PfgxHq zL}0Btq}Mdi(moNCrY>PiJfyov9q&B?^YOl;GUGoCvx2W*z3QO%mi>I`GT~BK#7v>w#Ye>Nt3}a%x}MS9&^NfQ zN$MQUAum^oU0mq=`>T2Q(c^ImarWhIPmA9saiGurat;8Lm&S$?|G*rydD0&_?LzcS zsShV$YPLcU&t0rH&8QWiFo-IrIJys!_!}FnAntW>#;tTt1(@yL* z`SpX1E#h5YSqx~O4plS;0{m%WvlEza%Ppx!-Gyxr#f78g11gT80|_ujXl~D=jC^98 z$orxTS?O{NpOpn7FOqp%0xEF_|C7}wpOCGoCf2*6zd_H+dMQk0kK7WxE24?Slz%%D z15Pw(A`4i6^02MKmk?+C%2jrIeLZlrPzP;LBrF_iH=bl30bwpWMG$=6_g134+^Q1O zGpID*VP^zP`^|Wd{!BZ;?Xn$MzBuN-*vi$$|0jE`$l6JSqz9>pYKCnqTVHI`~| zFwq#0WJ0p&+8Sohx5IZ_oinUeMPgB~f<9yJJ2rFP{eal!)wRDr*xmYIl$Mrg0(CAV z%9US^!&9nRnrNVlh(u|^>S-t&!bo0R>v%#wf1in|H2mY`ibf-gHlt=6Gq9Umj*4Ja9y58<3J5Pnn`97g;jfDJlRy=Wx z%oO@my~hK7sW0D4G2vaVw^L^r$v9f2be=n6X5nhOLVNaYai5}*?g`zzGZKbfZ~eQ> zNeAjJy5+`|eJEkutNbXAA89uTk+(&wjPU;7()GJnZw+8&+h;!EhwIXA54brKx(q?e zomMg`bU82*4A=O^xGqWpXB|M9{-SKEWNk*gHu%$i%sbEyh)n*R4hyr*d!xE_Rla2) zKPx5tgp69M|ADs+*Ad#N%fj)-r#`>fW0@kH!IIbK;>L(3c<0V2W=@{;*;2n0HBR}@ z_x~9`k+L$`jblSp8_30XWYtOO+X%4*WUvU@7X`(@@1xpWz~o+J9+5DNfS?B#KzbqfhASN)0EgHH{Jf^Sw3qY zl1QG+`jooe;fDUXx}CjzjVaLbQ0toySMXt4f7Ic+Zm+JM>(C1VU1heNaZFbS<3(hV zAw<8H3-y202FAq{TfR@1S415{s~x&=1Lo!Q=~fa{tsHxYGz~jxqKYHBlO7yH8AezZ zu>wJhc7judHjW;G>c*oXX;yAI@#bB{m^gFA5Lu}pH$2)#ve%DkrwWUwQ9WZT(`Py$ z^3!2oE{62B=qF$Adg$-!zV`tVu^BbNjii~qE&1=aWdJ3VPqlli4-S>GUXBe1s@VxJb&{^IVU zBm`pbcg;V|(WySZcQIV??c4EPFhm7f?DyO_*0D>Y5M3N0n5vBgMoUr-qgf53BY0s~ zTx2c$?A$P}TcIWLLNSjwHnj8cGd4vm?{e<##ztLK{{W!5_adZ=#I3Zn$Kn~20xl=& zh`2OLYTr2zTxZ;r%zWp!8FgxmOZtrH8!XktR^(&DK$K|TM2LbV*nL@dG?tT70D@mX zpo@HXY3(Q_{zR~(ms(e)<0oLCu%U`8sYje}@{CKg$)$*iE2Fhlx~DgpTEYoe!k3$# zP?Q(8i~=n2>|COWpQ(5nfyBFfHs0}!_lv#t@i9ER$A$JZtG}-Gie+XaOQ#q4-Q@=d z9aD>g`u#f|&f`1dnxdFr+{|#o4#))^@m^mqTN??lP%H4|%$`Dqyo>E)d75uKxMuYn zfkII>s@TwwhE3u zw5N~>?=i^2@=e!DI73VfLjCK@hW|b_g(`el(^LjK3)@pTu@N;+W9+T2t|kw&QcKJ7G51LJf&F8cVVa+tWV1 zgmEPvsw&|vHd{rak$pE)Mo}Wt@(j+NN1?>a7e9L`p6Jkqe0K30%Y$< zcfu231r(bnzu`m@X zc)vp16aG^%z){(konruuz@}RO?N1ez){Ue=?L|hrL!tS+i%nFK^r+r;2A!K=ZN*WsdRbPO^6u9<=o|0h1%C`l+MWAbxEE8{NH*u)xCpIu~d6UOwv#^Q?MN z@|F#%m{%L_#yg!Ve(Uv40h4GdSt4W6|^qAeaDU5jen%W+fTU~Py_TvbS z?cGE`c(^A6BJ$E$6J%B_Do=#uCTV|Dq0xv z_x6GUubDh}!u_C>(d%Oe?Q*vNZ4VS-yw8`%z62#;(Au$C47hV2xovggZHRzjZVR$~ zs;2{rg_Do&D#| z*;=Y~#36KLjj+~4wmOPNQ$qttKHeNZ#P1`YPi~^?v(lx&Qt9A)rgQcPU?66L_9HR7-ek;Qa zjGw!z_)a3jQ_)h1avr(89!Y&>_@bm7<9WZ4)mX*mo$x+%;eN{9u(10i!92VcVecrM zKqu0K|Be&T!OTE^Zz2P>M3Q9f=#QRW8MK2&XlOs21IL}>p3{@UYm>CWc)CDPiyT|UP4fh_)* zEFBhaWiQe-&ns@w^TLCYK9dgi#9Co>xCiMTJ)}_#O^s=y7%euOM^e3!tT)D*8 zWK)JI%+j9#qsju@vXM z$Xy-Y^o+xlc5Yo3M{dBCgJafPCIz)B*A9z;K~ji+;S)yFWZ8fJF7p=nVJaf<2b))? zv+FDHBT?r8C@vu?)h5uQ4|p21pF`IDRwj?1g!#mh-x|jYjCK5YLjFH-QPCsl#g;Ol ztw3U)IQ`MA)E0S;7fp00j6tOg;~1_%%kG^pJg>5-d$2iN3V=w4k`7oNa0nz~ez_N? z@rXWTmOwAznx>hw8n#$Nlqf1AQ_!Tis_};vLjF13I7gPz2 z&M=)1!O=(2#3|O38uk8_uf`jrhjR-Qx2dq4reK8=Cq@0uvxW5>UW^HCd|H3gRZ-q9 zY$2ZgBRqHfHvFEYQ{4I@6QLaU5Djs3Br1bVw7)iv0BP(hI|BB4Q@WL-XiYixHp!Au z)sng_Vn4&X)6lI5BL+si+$+73iPh%tU>eZbX7)g&S!h!HTzWRgn3ix z8MLM~{RolAj~|vi*dr?kPMKyW)(i|~t%pSAr6y96N4DpTPpP~JZTukum2-9xVLNtM z$?%FyvS@SGZ^9p|Pv-|&(r*0vZ?JDTQdIZRWzt%y;vgQ_YjY=cl&}5A0D1w_8z>IFZ{j)w?9TP0?O40s4Gy|grheC(-{wsQj`2kZ9?7qTNg_Cbi5V&I3zNS5^r z({uEUMMY&aae`XSM}8ViBk#`YD)~5*$6Up{uKq%)#vruoE6cG?0qm~qbm8h3%E2Ca zQD9CqRM}Pjg8C`A>6GC2b+T`l*}c!)qs(q5YE1{_J9ERVqb%~^gG*m@%5a#BMK7P^ zmePlKHKAe?xP1Aq3zr{e{yEfxrPtxR-*JjSaU0(I156N&Ghyd}6FPb;DOX~&8Z4!c zv@vaDOUu7bV1BkAbFh;nr_jZ8i)l59F-Y;B@3)a+e_W5khD(o=hsG2N#{SWF>nsC5 zR8setm@UEr)j+nj>Y4}J0@XgGHv)*%d|3K$6Q8M;&pGH9r&`WRBe%V(i({w^4{&@O z-?(=7MGt*y>#`?F|!!y$E$ET~{8kBK( zP*ILD1pTx+gu|da$TR3wL%$sy5hf+i;?0Ai7Q+<|BdQzm?=wkCkNs-Jf3LI3ddG4Y zAUfEvIBj~~(V&??9&c7w6(xE_B;54H5%5|_G-NO;Y9qL;X>;Rvfb90Ic?+c}2WWfd z@mk~VKv5wEX63i{7$Hx%^_X<;&(`Wh$l`od11>kDX{18~5%CD(_-Qknes?9+Hb0*O z@Q~P>?Hj?!PaMH{0ePi%Es*(qbIlG^#FS($W-JPX{RT|~Ra>n@Dbu?v2G)3=WSJ8s z(Rc6e;}4FyT1c3PBoJfME59lB3`TA=oN~Q%Fk}*_AcI1Ui}HS>uMbsis&&os4$wtc zBM$QO_QoeHd|g2!*#3{~L>(NuvSxHs67m(nc$eMC?4I@0*N@_e-K-10eURMO0iK-MOQs!+w|S}Z_L9;bkWRrS6eatfdMgcb_m{GjZLSRb z5Ocs#bdDcYwIJ)1aAn;K0SLJ#0iha59RfAcl|o;55A$^q+R?CL1d(^tne7gRLQm#8 z77K8i84W4BGxAAjwfes&7MTGRF@D_5Eg-dUM=&sMo!~O-Z@RIh7(y0?9enO@0zp`Q zo|_h~(hujyJ7c|!EcQ?vPdJ&!owy|ljk85vOBvZqx#4M#A)g<%5CO%2N)l%f@=z$K zQ-Oi^o$s@ODV{5l*B0>gQ=d+R|N1O(H6yvVivo1#3^wklt+CY}CLmlIrIxO4g^UxH zB3KwLj95iET)yy|_3a3Y{o*S2GEP7V=WT)d7=h-CK~% zWeIk1N?AvV#Oy>?AJOT@qw|!}D&eTT@({eXd2MSM;N|J{o3G^QYD`pfL$8>qsyF!? zN9gzdx4!k%3ZS$_?z?$&kz>$9hc3GcxTm;SPyj}iG;W`jPW`LuN58soE|q_x~U zd%^PNgXAAXhvd(e)#ZA|%zWzc*^Kzv;Nw3iK@bQ&ry`|YxW8J;K+x#%ATT7&6X~6) zMwv)E{^aGQ&o}v_0F5U8KoUD#r8DdxdT*aXBo4t zh??>U{&HT(cvP}aGRcmHfvQ1#2{+FJcNnmiMTod88W=iK4wdcwgncOxcgZ$*!jS`b zI}~!5#T1Niqzm*oCd5NJzSCBMO0afG`07yn{)2(5M^n6d-CEP&X6xZ2+V+sL8&1}j zCR1bfy35+R;&hU75X$vgd#KaP71cBd1d8|5OlB?2MSUrUI4m8co&WOvJgL`EXIX9+ z0tno3%+|dqPGOZTS=I@fCyENZT_dPEyL|SIFH+B!9gk<2tFX7079D(H;$nVkW-c~I%U2Q4%>Uz))U_-k=$H?{c59{W_4KKLWt0ybi)L%By(Luov zy)947h=+o?u3K`9;OLo6{ltJa)BSY#Sql?sjkX+1gZ6dpm4H)b4j252)Qk7Pa;JZ@ zH+9{7>h)9ar32_lrJma6BQMO*CEC*E$PIte?N75hVXVYEkvW2`ohwHi7wH~Ji8`{(^qzU*+)V2h58}6 znV7GO=Zl|O*DCtB;k)?;2E#(4(@amQ0%&6nI)I}TdrPAVII|(i`)=Od*&Z0R7_Pj` zgV!_@7e!WW32B2|S5fmyPF;9Iu}*b3z`_X~_(au_5>G!>*ZOnxY>EGAr$Z>*f??V+ z&9dmQg6zUoF6Uj>v;u#ks{os?*MIhaCDD|SsdL!#CB01NlGDhrbkFarW`%M!AA;{1 z2`82%pEowDMR541#wdhv#p>Ew8&H7eZ3X`}K=bUCN39w8UkvMrbR`S!(rQvd@jC>9 zD=Y6)l`=OZjN?Etbv30a4&uWD2xWt3OLOBYfG~y%;jNARTzYZV-mv24--{&nNSmD{ z)C8)o3iVz7gx7tmxiJ_&m*!`5k)b11i2fG4R#(O_g`!Z+zsXH*e35s(q-vv7*aNK7 zcza7(Ecgrek_lHL7Eb~|WW@t6z5oc~z1MTEEn>$X{-&v(JX-Dqf7(lKb z(eU}b)sq3CHV7r2cO*Q6dd&AtXYat+*wZZ3aK-(rV5d_E%6+X#lK7IV5MVMkX6hE* zd(d{_aG~MzR3fpmFUAD^Bm<6?q2_FDta9DYXvEeeY^=3hx|jg-ezWt(V~0X)`hOXm z>CnHUs_YIr7>~HPYErp5>JiQAL`yZ$H76-5MQiRNhy!0;EItA*1gbi+)yUXtXw$37 zB1o1lON&v>2%Ms2x!4bS2#)VUoxkX$>iw-Hs4z7n&k2>03hjItlU`T^YFtwdkT<1u zb>Zz_jzCSwNO@`<_qyF8DSB%)bn+tY*^=%#rwsPz{wys7fp>K}!~-7M-sL#{1<<^)t1pzFl5OaqR0mJs@Z&lW-MhhxU5>m)*Cf-5v9P@)`GJowGu3|HugioMr-h1->F++^8 zS!oQm4ssJ=`4Z!gv_d_5UwrkZ>$w2#Q!-A|z_U2(hWdSac}f357WeoREVu$m}xqxhaisCqE1M4P{d^F$0!sR5fFLXpr5*O;d}$(WR>|C zl_3}R_k$uSM~#wvPzU+a+Ucr!{3hfED3QURtax*Iqdm1LN?(=V6SJ9^_n%Gk@|31Z z^?`3+k1@kKl*o>*w<#h7yYFOx1q9GAw>5Bgcc9Y+mHKY-QqJqu;E@u20c~yRyvZ0r zlVU~nbRzbhO310;YtwN@X1Cjm2Hi9Q0#J>rV5ERF;jnBoEehvu4%JiZI8y5*()A=c zHP}kLrbG6XzubjypG7kYwJ7psUup@=Lt+y~Rl#g!ANMC_Qv_96@V5}Qk38sP>xSlU z8)BbsSB8_mki6LT7>{eve&fu<%7*mJT10ufwSo_&&3jfvGIFePe+@uMoAhi_LRv{| zF)_^~2?_uUUb&PqYs|}cyO;sa55=YsPc5D|(t-LQTs8RI7@TAhJCs1s(v9_gLQHFj zBVfb!r@QFt!b*c+Ldax38r*muZih16`nx^PWr~9DYZ{ojLKR-v)Wfy>ZntXCm1#~0 zUxNtl!2DK-F3}GD_aje0`VW*_`qVZ17N5ILp(nq4jZAqKlS>2uR<9oGi`U=iO}k<& z6rrB%#OZwtT1( z#?WfR=26x##-kz)6)HSb%P_f%`_f5L`lHmOB_Pj?+Ue}>k`5mcAv{i|IR2O~vN={J z2Uxo^`F+@pOp&3tA zWj3{;`<&~xW88dm^z@cmHCB)JK(%)PD8(iL?}P=63R>MC3e;p;?l4*up?8rp>&VWBnbqjAk1YX;vq!)R<610{H0yzEJ|m&t28Gg|^1h zy-^9%DxWrV&*%sdt+~9Y7cI+7WC0DM;N~iT=%Vl{*(NegO@1p#wbzh61I zGRnJ0W+Q%W&|$EvVtwtw#nf+M8*-ZRxF8F{}Qb*ussV@zNEUT=FnYa*ra zmVWQ+3neE}{O3Y;!7@b~4@86%*#rwL}ia7`tZKhhluatvz1@JQpZc4PK~J4 zW_~g2x9A%Tppo^#5{_hOGoZcNx(x&SSsrBa=sy@l*!^sd9H3uu=+EYzgTUS5z2&`Y z(&W@ZRE>%zYo}Ht~=#o)*jYvI3+eS>>jHO==GGh#)k6GQj0iNLEkOBs!Ks*dx&hRDjy;PgKhKD8S9=Nft9* zHmzaEv~`4YPC;R3vGZVsMT=c3YYeDK}Afnx+- zC`eYjW;qf^?@stq2n52FH_W&dba%QnUobg0zFobz0bS$b|12$$Mt9U_>6j2r6+x%=Il zPGtxR`?}E@8pQcT&uU@PW||heSZ5ZDTV#!-$jz>+pO+4AcRQ8I>!$whAcg-_Np zHv=dV%m%ke|%iaipyCVrojj^MNgU;8mewQOs= zQzxbVfOU5MSe34w9cqG+d>WeW7w(O!zM4}cdQ7QEq~0I!mi;8#Szaxuzr`c4FbFE; zu^mUWkdg2!%{lY<=_}Xa!tBX^tonNNX!*L<-sWcEfP~WC;~QyP@20!=K5Ye84C^tn z6CDvBUn~C_jw?iCR%7W0Q0^0R?b!2xOE2RrFT5BMPN@Jln09+{(JM*S493k1I89mL z>aDxoUVJCe8Rw_-2Vp;trXJTNy(1s_ud9A_C++B6J0+s72^ZTr+VV@^>RY8EGG}gA zGQQEUSmkT;{0Ks(^_VWi7J)snm_~AbRqN#NIOERPd3An^NqcGud>7xHU`EkV6Z&B~ z|1K0YztjN80Q3vCP(8v#h(PR!b}%KdvI_n^6Ci2vWd@!^L@lwlpFBVKu{}DecoxS! zT*iN5n7JY08m%-I-$2ot(q=Tu(DhM3wQ~#F=%bMCuuN*PKYgOkY>J-vyF&1uQ=rrz^QvGnDwNvy5P*T&Y=8?p+Y_;7-CGhCR2 zIwA`!nv)4sE-JFeI!Py5r>SbFf1kY6;#)efvZFttWI5y;QK&wt_G(H70Ghg&!`}_% zL~JX_2=9em=k%HpC3$JwqIK{Rw4Yk!&ALR+DP8n8*T(Q%D`Be?+2HZOHqhA;6oslNz zmj9z19bk}3k#KD`9O>U|H&cN>)O>xhF!ajh#Fq@2iygNDU_UnQoWS6Ypig4x-x{Y# zn>Q%trf;8$B7IMf3OFnZ{5o}6t(@8J=7dx>Uf+(5dscu(Bi1(m0f+gp8g$(y>|tC0 zlU>j#?)Xw(xcsxxl_k0_$x`(rZN#oxs4R3lBcaNO*EzSoa_^9kPb~Z|=CT93=xmF0 z?<3}_CNIOG2qG*3Yk1OHNp>VHoAmw+`8hH%rnVxG14#pXaOQ82B|xTv z;}V)4_@iiy=H?oA(q*QKpc|8wHhaexqR6&}K%bc>Fb0 zFoK`tr*X)!Q@n^H?{ANOae^iiF!q8PulBi!Ytb~!&$=k|#rK~P319Qah1RPIXD!SM z51!T}ecRjI+bRZMRVGULKE?N7)L$%ntotMqu0CAqDln3`*IU_{8-Tt60Q7_ucR)ndn_h^Ld`6H@}~UzuUp^~ zV|{n}Wq?X<#2jV13i`4tSff>uBH(Me(qQM{YhhPyW3wu#g&#i$c9qLje@HXq)~pKv zX&VvBydVV)!DAIosux86g(!h-4zG6Q1#^!&q}(vS4EwvwI%7c6ce3z7x9H)kOMqWS zytpp%70**l*(Lidh8d18AQJ~=oWxA;>_Z4^S25l(>z3l5o*evKL^sbUkv~wD0kHS* zGrX8D|BA;|8IM-R)rpDp9m1M|3U)qsx$6v>>d7Ks<^gqR63`dYzd7gCi_kq|V`Jl45eI||Sp!Eey5+n!rD;MU zFWKFfajb{n*AP}b27?uMq5z>yP#Dd+uJ(Sa{`B;`l1P%Uq-Rw9NXA|5_*~Q@0$E8` z_m4?GZjY#(AwuwVPSy2v4eg&SQZV%4uuR5UQ>>L9Y5g9eR?(Fa~|O1^=41k)E8wY$|p-wPifp2RJ&~Fi%Y`N&eQ}Dn(m(rSzcXLvm#Yi zueLAz_D487as8Lw+;j;x*MD?XZipriI}>ypeq&shP`)s5Py5cSkI0`5)rz zdm&&Qc7rMHJH=z&{r5u|eWL2;yrW~NN5PG2aOzp1PIMOItY-Qo`T#~OZq2{`fo;;{ zKm^Xr&8=vjwdfU!{o*_`K>dz=bj!-%s^8|?MCFYz$v23%snRpmli0{ChWpYkpHTJt zIdmkkUkmBc7%id5}!*n zuI+Af!{jw`=b;Bl+5q^8rnH9Z2*HZ$0>@vw#f>3z*6?phH$9g8N5ygm6pKKl&;*kx zT|vz9*6!l`ucH$OL1#daG;uY)VS0OK>7d-rNIRc547uE8joLQ4wCT>?z=v+#OHJzI zIVENSd7Eb=9#yogRoFORB3(TOZ`#{?-fkFnBnArD`Y+Lvc}i668)yZE|Br)?!jL~% z`F~qlEyyd5dFpcK!D^Q?JY&2y>=^EbJepe{5u5vDe`xWxg4(C(DL%~d{IEF}y%svx zgAl9g>kGBg?czmbwDDGR|Ha+y=ko#LpVnWnsoS=yuEd1-D+t?Yd3R*nz+6=_p)%$U zcl|EM!~|i=TY20wV*CkY5&Fbx!=b^4m8)kZJhiWsxoFL@ICrQUxm`HWG3~rLSWw-fKW};mC}>}J-99GQ}NV`1PITt zu&SU9zvvLAa6Urpc} zO#Oz(hm_eE3ttNjzt_EcThn_km07#(O!*c2k5FaJ;b2qo4G!QLWf>)5XTkE zAwCw-3LefEPZ$5~j$WP`6DlK+$lwxb!&&msIx|M``>KxUgtJoZRkwc-tBw2GJ~weN zcz)^-cdVm2UIme*Bnfy2y%~D;9P@R_tga?xx+9C0Ybh*!0EuaT0=*_z>9K^NoX+&; z)C`eb3CW8%O2R1*4{FROI#;_25_)Uwuy#WOertTH{8D5%1FrGwV}xgMx24r9m6MEO zEn7i}2`@5!un*`q5K&9jIZBo`Zk)8wQ^n`-IIckAY&H;5*%oSNRcmxk-Q4Vul#sAS zm)VmlCj~>prcIO#>3Z)Q(g?hwj3LG_ zN+A9(kH^}H6cRF>p7=Es-{W;f4mY~AzJAcK4y&diYd}?M?#%Q@2)}q~dOQ3#krl^@ zRKM!dHGhOo4hj@Zs#=-%`?CFIBJJ};{*b!{<9&-EtC0QO*qgP!cy4mU&|mLrXoVqr zJ~CB1K-EC)3vCeoD?!rMb0$mt=C*=k$(?R<``Z~S&O5_6qp~=u1$_S3 zoM}OI@&zP%r|r`PKNw5^Ug{G^GkAZC!rxaK8+?g&ykA`%UwO&WbIx`BOY;lm+2M_6 z_S_H}pJoVu#={n+FwW-{B}@1@!;_c&J7pv!+ycEWCCfISj!M^9o?rEd7rNp%Dk%7w z&(KAz?QAN|TjUIjH#BGq$=Z{}KAWZ$4OI%Mii7YCCt|!WA!9`I&|z50>c0~ir!|F% zKxu^}@->hlq*wN(kK?Ks0AUipz0K3?&162QXIfh=Ldh4RCuWVIOGRPJ_Zn{QtFS;f z>grXb5^Jf-vdPP00Q+rdHNGcsb@A~vQS^l>?X?EJ-6KR2(3L?hveTAM%O*M}x&_Z} z=i9puG*F3Wb=_GhN909V>y8Q;a5XJeg)I9)N42tywZ4~RkDtj;d$c8JZec^$SvVnS zr}YFU4N%ipwlkHh5a59rIj{aP1f|3jZd_zB7dNPA4YZo09i z&WKqtxbJaf_Md0iG0_C#`_5%iCr~4`<_~AJqd1XF;-eSTy6V%#Uc(7)Yxz&oY|vkQ zbLc#}GhEE?nR@hpFXt*`D&R2Q->vv57DNLTKXepeW8?ZuD`qaA{KW}i;nDH@>2*NQ zR91v1>*(D8tFiL3$A@K}d8p+N)Yo&VCqw^hJ?U}%u)Y^Ji^jz6t8MwyLT7sK{gs-a zHHAW*>ultAHa7*Vv=;24I)+fcBi3OU&J$^Qyk@n-NB?$_9=Oj$!D9iy!@R`gRu;GT zuBj%A-vjVE$#J8}k8K162FCRY^9*LZRt;`eT3afVH#jGX7uB;m>m|dbd@2Obpy@&- zF5$0EU5b&H|J1g^XJDpPk+JdI9T40q83e!R?qjZybFaD`h;zoO- zac{v;XSCW{`GB||0Dz+NtV?$st!2_|UB7h~M-di4sn2w;6qg!I90=e?CDxYT-yZSU zF1B|^w#8k0ASiX-N-AqM%FCQ&-1+N(O%gMTkJ{# z?N@D8t#qYe{N>dwBzwcT6t6ib0HfxfL20;QO=khTi)Hp}MVFjh|8}i#y zN3n=I+VMoyM`Jx`v%Z?dp84MV9;=r-#&7NtUbY>ebBbpbNoqE1_UVJr-Nelt7hkng zArnTA1Oo!(P%0x~)(EZ38PtQKMbql03$KP%<3AZ#7%|*^7uVn^Jy~bL;iXMw8jKd!fA3>-h56n!DiM#w>G5DFUn8inH2=|N3+N-K18A*+%ppZi)z_b_x_w5_w(pnE%#iIgrVyp0ds|Wil=R~_LMZb5 z`{KcnO7-Ix1ZUbc(}U5-w=jj>mQR+a4ChSUMAX2)verM)O{Z?7bYb5ZfTR--1&T-{ zvCGHG{!Mc9%9?h!mRMe^t8L!X^=oXb?6p9?A*{pTooNtye=3jOP$mal`+`WNF@93w z{$BV|qwBlNM|?*$yz}%Rr4fm^(3*fs&iYu3aXJy-6HN$H*e~T=hk?Ke#DBao^X@Tk z+!5BgD1oz2dbe}BK0y`cBojf9{YGV$4wW%Xcn<|vI$dmC7>J&r-XS*O_fCS4<0Ho$ zn>T&Rmv!;3i)c*X3T85rUtIl;#CZ)!h!?YUAeZ?H^`h&=o~L&_xqRc@!KR;Q-4ua- zBC0a7YT~n#nE=MZ`L;&PU56FMRS}y{Eqrs$acHvfduheI_sddKu2pY|O`4*5Tkk%3 zx`NRwms&n?e)lYhCuV~z1Gx6R*f0&`G1Sy>qbIg|?sPwZj};U*fD+bC?pwbkTNC-& zD_>Ru?u6fMC}i{8D7+ADg_KP_1qdulqcYqc=RNj#8T4fXp)0CnJ295)Zch2C+&tY|-j@^wzD!v<989?e{2@)KIz-*s`%Jd#>wFhP z#_X}$;xP{dlT#kkZ+JyvwA%LO1A$=f3yOL95miYExc)Se@**!xA+?tA?)hH@pGEsq z0?d5>BaH78(5J}ax^w0_m2uFkPEKD7dAxe=UueP1$8Rl4(q1DJ#()>PqjQ#d($s zBKB|W(Yy#;H2MhM)r~K32DLH&+lkf45~kAj*4MXK+-YGOaG9b2xSfmZJl8zQdM>{E zfX~}k{O-mtYmD&*hD^3i72@J|lwGfO*Ty}NriPj?X{ZpJ(A{Ouvd-_1hQ($Oq?0ou zmsy)hL?tT()f7PVy%MI4SsRNwI-kl%U6COfgLAnvbSXYo(_?t3T-x$8v;hFHc?tAC z1U8Jgddru)!-?dl;M`f4;k`F;h*;eVb9@(k=zt=h9kRWS%b(BZ8&A9Dso-nHCVD;W zN2zWii8;sLPqXY@?YD8bmKe}mJ3j*{c}fGd((>QduFj<4Ju&qe#R2vsw<4fbfa-6~ zwSq0ftMcD6ewL3c(kjKz%N-Q0OshJDAZBcPS);|=9D4okHvR2|i5x3q;Uu6iDo5uL z5fkUQb)fg=0BA+zEkvMna>G+>Q!5Hol?osM3%EmY*sAbgP;UamZhc2#h(O$>LmV3DN*niin@CTz-)RQX1>g>C`r{Rk^Ca zx@5xP?UTuis@pv0J%^IIr`e>hE~_d~vY436>b%_&H)3|q^rhq=_B`BaCQg~0LlU|2 z@nD6cm1&*ezv~o@siX0~uIH!fdMlp_f9T~86p&w)YZpEs z-pclm&(WbtN=nbZo^{=tZ}zsfz7svLS@J@C)%cv;B=5n4m*3;Nv0*k&!i`S}S^j@% zYm*`6N8L!dlZPcdS;F{If9^5+w(u0Ua|+^qsE`kkGOeB_;yBwgomfOY00uJ+uQ3Pp zCn%?{aBt>D_$-{Mf2?3GXp~REWxO8Yo+V4?S$}}{^;T<@RF!}s z85s=eZ&VlVQNZLqF_o9Ml7%8raCx81-+yigl2`911tqu%*mrDF*xa+ln}w1ztR`4J zc1Ci1ZQcOEs6BG~_wp04sNdTJur?XDi z*!#6h04%})wu*j@JcZOaU1?W+gxFYk=cu{){STry`883=y>tZwiN@#8HO19U zsx}a+KQkOr{cth>cpWRyc{c%Y>&QMqS%0KVg!50T*rrKlTivR0>#+zNM+NgVFVl=! zIH9LbYPc9Q$NRUIpjx3Xq*}haySZ%~FHn(U%{RTbTgUMgJiMzjDfwCB`_04#j zgAU%_*|oWL$Gw~J|M9d`7#16rFHf__Uiex&NNp2qewji;VM!*`v-+)^+t$t|3IX?$ zgntpsk4s0CA`1tW=I2|pAAW7_K>#p*6J^qBI=jRg;6!c3|OKXG0+o|~MEw_D5DaGP*HC^L;tf5%%|+WjXTf_i8eU`cQhdpO{) zahCW(wG<$x8Zm()0bLaE?xEM?ch1KSIoKSDwtUCWZuxgm&ZgHaxvrvobz8!H6>^EE zJSgj04rPA-Hd)iSwfThu0v{DYJa_+2pc9{XY74)oWb{Wj$hR?7|bt2qFylELT9 z=2jl=1#NtQ_LQ96SNNfj)N;I$v3-lj5tTv3T7W^MpHx5z&)Jfgnu^FnRxi)*u9Xfo zVJ2)bHd>LP-|j3ND3@}+U%j+jIPU`9ELtQHQlc+ilKA8mi1je{(!PT;%Tl*u4>kw~ zSA*{U^|HC`ea0o{8*7tVcfJ&nzbuk7&F>@8mx+S4fg4NP6O5_-V!3gl=exVPXH_J$ zWdsEc%-Li!t1!$-tkKzOy-D$sNxCf=_@#F$CVqEdZ*9*VOQB<*#MuK5(ai_a(3e59 zK=1=XCesWp^U!tzojGm_Ru_2B7@dWH)1S*p5L+;(G5#6bUDVTon>)O2S0{wx49CMw zLIOlMr8ZB`i~xLUe-7j^)zQ!Wdcmh)f+F)aY2-P!xtcu2?L@x~-&y&8ZD zXWYt~%KE@NkWTOk(S{SrO7il*U|9Eqc6P#Qo?S|RH(M=BYz}@zWT~NZDiH!mxj>N4DR*W`@KmX1F*Ll--(I3jGAsu+tdjz$wYCKJN zoSYy8Y-;=WKl)zhU8o;K8>;;Tiy}tLXrF|fI%DNGCm7IIyhLG?*^~7mM-DettUv!H z;F^0DQT8WTz0uH*`x(uz!M9h*z~pT9K;e`BNT7<3NZMJDY1BD@$(Vn=Cgk2&@;*z& zfR$mMABG6Pd2&qs6Iq>)0D>Xa~2&>Q<-Pg$z6()t! z#4Riv!c$C9kKndaD9;H}Lu>Bu?=J^&R0BTY1l|`aQJS3tQs>RPdk@qAT%UoVWhxk~ zj4J9xvr&9tV8GuMf52DVGy{=AE6m%@rWXS9-so9A(;hhjXsN+Q2R7g7<)N1i&dE+k zS`ace2A00GKfmCNj=%osF;M#A&C3BYU>IDtUXDcXG(nrFygx-!qoKeKJ%@EC=+MH62EVJ)2Zs%>gjr^b2cu*+LD3- z{Vh*LFt_bG=SR|pFa^yedME7yMIg;*w;FGrVbX&DhL>F2uxCCA`t|S+?xE7^pWHjp zLh$BK)Hq=Y^PBbxVy$qW$Mh2qorzZ$+DOUx)Qi%jR}7_>_)6TCqNHmw?1zqf7z)*e z{MZagORi~1os5*3$>*0u67SUW^h;jikvH8AU%7Z_{$IE+sLgUoPr`e^rZ>u)w&&?H z%33NaCT^Ksmh)oimMlL?B_?lf)@eJTp1HsPszxZe2jQ?h)>TV|#5aZxAAy-myI{Mk{J`dU;1)kqUdSr%~>;dTuOH6 zgL^06?-vv3x#_}W@#>3`V)SCSzbfD3?d&ale8$XtnAAef`YeGwmJ%)hSbp)!g&c)_-rip+5mE9tEDv=sl- zZa(e3_q&V3=q&?g|F8>~S$t!+=QVr5napKH^hc-o1o(MPoP%nEZfX3CvfezKK-ml# zz-M4X!6^9hNaYLLp2!cix{BrAtT(-O4_8>7qpUB_)Mwo%Zt}+UI?JSgFIGNmP*wMV zQ!9sm8u*e_A)_J07dsC3wTBo5v{}$-g+2LVm z9O4|xY+I-HPc7bL+jqvU&}YWMv8;3X)wqV-+IVSolT{cxxkJ4?xqDPims`w>%v@_J z=h20#2*JxRJ3kG-z^*yD%3E3jq88O{3jS)@nZ9U42`Akvy4iK&UxACs8QFmBF|SKV ziC2@Vs?dHuw3SvCQxBHxdgh~ zh7ntMC!}sv=vMz}JBb{r9U#Vd$DmG;28Cxb8QPI6z-t&KH;4HpzPO-sMM})Y&?0mE z`C(Jg6e9&NJA6BXx-xD(yRo`Ss;f6Ji!9Jf_Of713d%0?<0A^}jH zYNm?*^*pp`GLV1inf$A+FuCIdwWe02d}GC7X_sTi&!DoxB@`}3)?XC_$oyFLO1<(O zT$yfrA9}H(c>YO{^E2yk3+WP;%6i}4?%kb}e49prPIC+IG_M(Lg@7a_F{A8d;m!NO z4(s|^p!eYX3_thCOfzM0l%K`i(!VD0NF6D#JFVbGY?DJ^Vn1iasG#*{>4hm z*}mRpiE_~7{s9?{)e<`hr1$Atgs(F(1EP(31uVnxY5MveAT2n5ql{rUft%i6c#JtK z)7jOPu(uP#RW}4CI>_8hV|m2~$K98g5a?AumIzGegP!DJFS&Zx#lrc6$n_yu`jpDr zOkDpTUD=SnBQx4f7?2h?E-o;Y*b%7vaFpPHCyq4?-2}BB-8RY0SX-vN-B_rdOAoy1 zP3-AYVzTukL**0}Ve+3Vs2h1XvN}`XtFC38P5IHtHeS5`3-ad2p&WVg&1c%pUw1YO zr>C(>KF^+<$|P!{nD@DO=g4HXk9cE5H2}*N_g{c{)Oe|2cG4`XZHeO9@;Wy-F;?pl4cT$99PxuD3%{S^Go_g7>e=HA@?zw5`Z~9s)o%3b+FMbC zD{Zf2A+?1{r60eLV@$%CA03!ewoW8i>!VTkO>8DnfjK2kP~}PmvJrf zyk$6CzW0Zo`Z#$GcLuqf^5#B4^RX16H&~fbHUvuUdmI?T_=Y{`q^2*^x)JAX`_8*Q z%6|FKVn&8Ak6*+$%4rCe`}`;eBx%Z|Iw^flS;3I%s&*D4mw~MfSzA zoK_tWxc`k#I=+c%sDOuPDcx6Xw|+6y~}AKOtI0_4 zS|ac4B&vP9%h9+auw;DB4`q>czQ~k% zTT3CKF6P<>93D)E{^3Yei}!p0G|E^V4wbOPUYj_q((j-kZjLpT;9h4PAjtf{9rn3l z-q16Z?dS@4Hzho7(yvH(EZJ!9w1Rd1YwSHky;)T6LTKZ*EDZ2C60*cg@mZKR!KfX# z)Y!rS*U)kmUX-i?`dXQvEAKyw`M{C}#gOIo-dImZw)mLnhy24O_}JS!ohO~oj+uqk z+(x)#(c3&jm1o+Pa;ncMSmX+nfZwCO2UvnvC{Yc`_6PRpND9ETJP##;B{J~2Z_48piGt)kGKZJ z=<%#__Js`&qINs?BiY-#N9gKTxjXzpGyj!EXd>1IQ-Y4ERRp~Z{AQ4;Dh)_ociD;{gA#@YR|>q$%x<|^zWz*93g1`4n5~;m{7s<(iqnDx(B7PL?#jpn{ z$ri`p0boP5B#MZQtD=(bN9@E0fDSZuVl_2Q+=LvMlH&0w7o=q~h}KiUeJ{zu0~{UQ zdXoeLMF^yIPdOccng@4+=C;P%aEE1Ef)!^F?^=8xR@Js8lEe%RpghC>K3hT!J6CXb zi>C!H4)WBML@CUfe zQ_XK7wY3#)MR>~F?fDPeJ=N7Rf$8DUoCDdJ$w*JRnK@C1fmpX=AepcHWb=Ky)uQ)Q zkhO*w1uH8qYf_q|P0VORg)OJfK8S$F2`*FJuOU94mlkx)djWu&njEQ4FRYK`49|&b zI68x*Qeam!Au_N2fb)4b?UN_mRkV)y$0FscfERYoI|wg%tz^2=ixGeM_?gqUTA7CF z0KRwB34-sraIRyya-7E0s&Gp6mPPG_#K?03G?&Yc5d2D zipt+GNC;s_s}`q+ybJ^X^Hk9|G_W|QWX14Jy$T}3ja#@}$AVGC{B^tAwz0fBiE7hq2Fd~xlgX}OLH;FZnZXyKb z1_G{%UqrMRY;fkbgY^=%;Z#fkKC8Z+5RmMmsA_OkW#o=EKwdx6xv>6qGm}oewVisDzSxrEplHcg zLn|P(5XpF=@@OyN6w9oCaIi`m^1h1?t_u`V%}8*576=dIi&O6zNs$#j3gDI`3$+Z?XN=6Q+SH531rIw%A-rHv3_y25;VZ14~mQ?~;!?+vv{8>(N8 znu!0&VrOnAJU=J{q;kZF(D&-%wp7qe`$3Quz`!`*f)zZFYKM0n9-W1ijO}QY805QU zJNz7B8%{#A#^*_Paks$VVpk+|Cv=??f{N=<~0ps-W!@=RmDajA5w z^qUX0(%AM6i^rSK7qftMC*W|LV&rbt>iRbB(!pPTJ%sm_DD-lQ%6bMJAYu8n zsD^jJEun52`B&QFedhxKVDZxGx$aa|Q_SHrzH{LzgYx}gxbAyqS1N&^`1zKs$-X7_ z(l@4?e(u3**N(o|ea$<8C*GtlpC;uwsoogHu3hfK>OsK$lIj7l%K$BPW{S%wn5LlL zH<}clzPg0a@YsIUg#@GTF@`c4gu#8k*PG&8EM(hOL;&o3@ntF)mNNw%1}&N#h&=fZ zYGLeMza8(Av(`0FVS_JJsi7=C$;S8l;jt9B$yoiVOM}d{>4jkt2RfC$5zU=-@P*dg z3e}sV9W8k_ss1z-l-xio5a-@j6a0GWeD`Ra-q>HWtg%9ZBI)lu^N?MkD!n!B&u5}% z@GpH%Dnmj1xn}ASdVLfxVv@DbQd?Xt>wicy#@F1TwYV{6#F6RY=LI#HrFSL{(J6}< zpoUda3J9?KWN-EE$58?}S2KLWIxwaulbM}JW}V;*X^Z11>TNW z7GFfvM4d0hNm|`e&4C|rW3zX5$IXR^1oRPmTK)kPE}5#cPyx6>p2jnaFx>iyS#h-KS3IUw>^ z*m2X&#ImM&ihZe~v6=zHt2=>ycaQk)b?AdM$>;N@#DK)I3`>O*aA0pnpD8X< zjk#W2d&D~^Pv~kOO@l@Y_UO_M&#;0;PC1^^S2IDzu^Lpmy`8bXKrNf z>l7AoO9mppRFJI^C?yi?JEx=gxi}UAvOWj5_{LrpljV)G=n{fW*$Zc1VVb?YyOrm) zpwC651$r{Sd_P?ub9m?X_iSx3zW{|aTbD2AS2oXXB|7x|i}Y@6-jC7EU_6+22Y#KA z@_KQaUZ0+Q@y+Y|Eg6!7Om^qK!WuJjPZR+s~SC!Y}Y$Me${+d9=W8VzN)ITv#rs|jm=b!;21FDYPzbKEBTTmb#$ z%Z%#Ee245drou4*KA>VqE97yah!Uur7w8Nqr!G%a(nno#)g8?y^uiHrg$D-3&+UB! zK@cNfdWUn`CxyK4g}YgOW835YlQI(vbmRkhIfxSp&BT=~9*XwHG~nlvq4Vp$HVw z)!&`4_vo3nWw5fci?XL@FxU~%j{>%im46j<_8B@e4?55 zR}BqLxw|N>9LUyPj_)S@Ca;2KcDk}yZA=yA%Yg^V!W5j`K&k! zM~*~WW-a|1G@r`jNb|MclqiM#$kPg;Y^P##1mnl<&ic}7TlCFi^8MPXf_{zE7-J`z zQQo^Lmixex95ZfOtEVT&jab&1s|Jt>bZ$q>3Q7W+AK-BrNlAjDu!qYr@nC&}f@!kU zA$QWd*F=a~(FGBSKn*ppnY?K2^Ky~o9>DSHnKm5SUl;yeau|+rx*PAUgu3gsk0EJO z3sUGBYPq3J?8p`FYlf;sTLslr2t;Uj!9Qg6icmf| zC_J~*mL@H+6+EHsr!oua9gA8wE9<~35XeD0q(Bel z#imBZ@?{8NbMQ(kgkf@&WT}igWEmK!nq+!YrK&kAZhFVKZfu7b_*jWNEcC+Aq5_(0 zZS)CmmnEp(5LaI=CI5881`jdn{MRtfB-JvzoYcX{5!RB9c1f?Pwj%*Nv`a|JM$DO7elev| z4K*z%UNP9)?#+#{?Ln#0jI=*##_>9^n0l>HK}6$GgOMa)Q3JSqvRLKlynBrEMzoPU z*po(hqpoPsL^v!H>h1GmkqrTB|99spkDmR@WQ%SjFQln%`Q80YYSF;&P0xB(+e(gW zQZ_MrA>=w1_3iE^ckhDJKI4S)LuAT{%f0v5*ecxpPe*E zwy;@#w-gC&yfM559S}wcY9}$-UK)0(RlT4^bg6?#MtSmlCu{Tc&F2HoAfB+UrLBTh zt@d)39J_vdg)N~rIl6i<8`(4IA)RRBTKpA(GLTgZ_!QSwhui*>al=;2DHZ&4A|^jO z)|ItkY*8*Kldi3sX=d|mh&5@qrll$H)g(SPVdV;IBWOXVmW?^-Hv|tveL8(|MnPKe zyFAT#$+TQCv0&xL1g**haIJ>`Mzsub{*R(_@n`z`%4NCs`~3cdJsvxs$2sr!>-Btg zvaTDEg1b4n@uyM)G>#N@lUIt;0H7<88Msz`5sR_$br!OhF6fRo=^I=3T{@L@e72&V zv!}VZNO(vSR0cmicDA$EL&D9rln!wT(#5Sp8pb$|axOvIe;f{i8v^StbE6~4G@SBd z5DQRU2d2(bi6Wfb+Ani{;&7aet8#^{(_!3h)zpNuD!y03twNi`BU&29h}{(BSNuw8?@oQxGp+jSH8cX*0K7a*Vu-A81&caDv9bwY+oHKy*de5}_c;1%Rh3I#US7x=!_slp zOQHGjL!+}pe9tG<6%q%5(65EFboNEWVW=5LufepX^~w|_17Eo+V{d8!dmrcju~0TO z#xmO&`Y{sDIqkeW)t*j%@zfsY^+u%x1$(eG+QrJI$k)8ik~1-i`Ji{k(|?uaU{;}P zuObmEUbJ{Y-PF}q@fkGQDDR?-Q>>lc=&tohm-$GYxtTWtF6rG!24xU&vD)C(i8SI2 z3<#s~>gqA;+o!k;Dviosw_Zvx$#4@ED@+98mjiQp{WT(qsA2ReP22qQ4c^%A3@b`@uG6(s zp5lS{t<|W8QIOWLn2%$UBP>U+enXe4gZaOR-a@+GPsrUqLY}{s-nI^`w)&SnxA6Vx zhCofWX=tnenJ`3;gj7Wwx>k`i^;5*BinPLV^M z0&pXvYzGlPsR~`h?cRsuoQ&irR#o%eB<4E#CHI2(6Q=4PFduG$CXVP@kAzBFw?7cH@3wNAHo6UsIAO+(PLKI}^a(Lu3Jd3h! zGk}8YIB=z!W&q$-gl3uML2ngub+%4#&ONESupfuPl}+Yjr%wz5TFdDT=628q<@;ED z<55pOSFT3|=SRuy=;Rp^CMG^UgG?Fb1uDq4K25tmJG_|i^{(wB0i{CnVnw#2T^)-X zhVxWRq17tQG;c`B{Z8wt;P6}j=RP0?IIdoN13}y{ zqbyT*gOe+54n*AZ+j2!!x;jNr5`eYZia6d;y_@p{8+-evg|hGRV;YLHOLJkbXcTK` z%%y^OSyu6a-GA0hvlHzlzYV60pBQyVmHiHwxGG`(He4zIr#DGRoTq~K*#9<+wR7yidjdXfj8_K0ZRHIv zTdi3|`Bp$se|5RW555BsF{`>)-8HW&uDc&}CQ3tbb z2whSsrz)iwK1%3!if;(em$~1c*w|=g=E4&wKE9Q%??=SdX&LOS20g2~Mnz~NFpkBJ zp#F_~4C8nA-G<;#U`53Fz>%M)VX!Ax(&NkYWFO~u{9Hr$oke+8Dqo0;lHU7c*G zXJt&$9ZegE8R{I}mr&U1%0QW`|=CFr61tuCyuTBhquaM{J}D zFZ!rt*QR+4(5y{KP|Gw3f156Q^UU=9e223OIUuRuu3@gtu)$CC1;qC-mqI5@J43VI z!j7f|78YqzHpnnsW5g)wz!E?bP^E-)Y`cEK=lws#)ME0J#~5WH(ynkmAA7tqBI}mx z`rv3i{j|Fp3iTIVlj{;qi~6)h2xHqEh4eP zefp1FzbD@88!pF76p}Ts6y9RY@~%>hRgcE7jcx#r@{Z3yiVtBhLNEF;YaAeYP?c~$ z5!~Luu|jwDG#v>lL-A4oPc2~RJN*qx(GzJic#U>=TXhLXyE$;oMmYNC^U0}P4rkpI zqAmu8l$5mfXbVfklF0%=`v8-I!81_be-a6@u+pFZ+*11qvo8Di{tnD@I!b}fM_`EA z(PHNIFi!N(n@XTu^>t)>TF6d%V0=g)o-$8uZF`n54E|&qFl~QsDGSbmdt2+%QH&5P}KZOe7s;3|734J!UeGgnh@A;!q z;@QS7f=m3?dFq+%?S}eKHvbV#=T_y&_R3PB`$8Ke=FB9Wv9LcadS`yj@QAl0OnQd6 zw`Bx>Ra(O*tZZVcEGCY70rIPG7YRb6!8-U-!FPRlS$O2Lu9m8$j|k!8C75)I5;Ygb zA|{!bwT$_!)u#9pr4eAXs<`qsGtBc%TLNs*{@yu{bEqjfa%oKs^+P}CD$Y<`lt0dT z?F-`9Lpiqkfta(Qw?+o!4*BmeCJdF%*Nx4nc0`1y0i;34Hw{%;nF72K6> zD@jl7+4N8Q1+N4JFpemBdm_cdOr@d@cd;X-o}4trLLeo5G2J0KKo(>>3_7HHkUs?H ziV!DAN(6*R{Utt)`KEIj+3F@}osO?ZU&Kb-VelWKHcC zY2)}~67H_@gkMdC7IoR%1#j|VmFI6b#>dx}lH^1gVpc(fn`dO1&h9K@SW%DB$agh1A^W@90$Q0BU^s~ggtw4KIOvNXaK?xyVJ%iAH}Jl9Y3UYg(8e-F*_9-7QOSRmi^yERJRw_}T%@9$-;??qr6MGXFM*0Xq)8iKyLvMigl`Nrib`XwJe_wR=HWt8CVN8-|d zdCgma`iz%8?rg2E15QJ}o0bvH)%mWWUVfg-+VlW{wTtlENvyQ`gK)a)f!L#>sH<6$ zl6+b=o`xPr1R^kzpew%=O#klreY-U|V|&)Q^`Q8h_{CL?m~zQNhG%u%(vSVJQO9cYtw@EBG<5=*mT{%`{{{{V+V-64 znR76%n!hpCmi)uMaOCEhhG1W3ds?yyYGDs^b0PSG2l}{sLkelzVfJmSspmkiVYs4I z*o=g5{Dr%re{*@K4&A59hP-ZyEh#fl++_34D%bfMr$I!6x+hy-q!hd7y)b{fFk-WP zs%?fIpS@<*ZDraAEJZeQU1n#0$O?|NJiPnk&a5JUg~2ZsGiAi%8yA}}@AA|x&jsf4 za;V=a9qZp7(?YpGeTm9*ULLf<{wP?T%n466Z$dk1-C82()u#>oOqV(Eb3Wjl9#NKAWb%QJ8YT@l9Ho0TY;?&`sdrnP zTMgCQ-E$spW^v9;slywZYi=s8zDf9PkX5nLU4C+^r_D*nO-*?)a#nCW>$oEni4c_a zpLFmHrc524u2*j_(kRsRF)Yh2E?){$bhGs~qFbY>A^IbuTf4N+-;8&*%!~isP&C`m zV|8t$kkQ|!NUPyV>XT5Urh`no-iJF(MdG^@hbn0N*LDg^# z6U28z%j`^?iMCDV*-yO9`k;xE-F}QCNTj%{Fd`-lQgG`?Cm=c4xac*BFVl1VJq|f# z(_axoy;*55SaAV7Io>5TOIVLEQ(S#p^4^ z8Jxr(3+tn#9t$;T)Ctu<{hv3^Dulj%8KA=_ztlM9Qx~+h7sR-5+Th6(D7OC9FT;xzuEux8)s^@2<<#ONY>dzD3R9dIdcrU}1clpYrqkwc;C+hq;?;0}NVN^oyE_ItlbzZ;cjV#z zr`c6iV?1_LUe!}a5Q4vn8;Y4lqy3hXZA`F&@71hhVa+kbYa>A3ggF+A8_S{`wJV%y zgNzpkDVKBwU!Y4XK}2Dug6CijJ%2YhEz7G`TiB+gc_2YlfkG!^IJrz~9BI^np^u&+ z7JTBI8e`uf_4HgMWYN%dPo+Sj`LE)%OWO9rMgR(2X~$%56(@#JYLNU@1E{rD9wV`-#LI0z?I zU=DR3SJ+(vaG<>HVLRy2+QhgR(D*d& zg?8UB>jK4H+;^O0YfR(%dr)530jU$vuLMzuO;&UBJ_$AT`VfZk?jqjOErl@5i~hTw zRNquZ0MoEmCLVWjVgFj`*`=&gjOkaCqKf)dwRMX00}%BE9RI`89lk`AVjO%2LR^xBocNXFF+>-(rk5 z*el;D?~DlUF4!?v5%HGjaUD+)g}H* zkg@Eu(wL5U1MaqaaaYJ|<+Q;{0F)v=Gy@|$s@J}}dMb77hQ4%Z^~7ZXj5{)j*FET9 z$|nwS#|hbl;rq4sFHtt5-lh#2@W+DEj{;Q6?D`XPsuo=WM%N#lsTCdBm z9XmArHnobgg_M@cSCbC&#|SVnehk=``LB^Bx*6+F%=`#dGLtbkt5VVn{S+AY_(b+~ zph~qJ&p!G(#%V@zedB`xXmL6ue{|9$gVMIU{*&h*`=KMqxmy4{Bq$1{2uJ;ksN?WB zW0C!RSd`m?lJ2Xx?vl8W4NjJ9)JD5H`%P+;&B&^bP+R?i)_z3Yx>9K=MEy_eLZCb$ znaTE9`+Z~1O!4i%_3iDd028~svijHV4I3xFsVV9FF>p?EtUC~oEdMsjdi&4pxrh8h zss%9XT!no#>WrMlzSBVbY%Ks(T`jMbV2R_cHXR`sJiPadnvltXz{Y8fQCv;_x!oGg z7+_Q!o*@uH*Wm@`;h1Y5j$|c%Kbz|&tc5;z4FD7i^U)>>l(oT^(*Gm~UaM$Ir=$z3 zue!03!q>q37NSbz+h)E_hC>TmdwaqC%)k~XO1pi>8K11GmixU)3Esop>**QY;__yT z4x}N~Kq7rrfjb;twruuYTT&eP?g7$bm%8GBlweV%E|@GkHoYZwEnZth#DI6RYyO!l z4EZkZ~Gp@xekd*e1&2JR<_Dt4Y!LVMy~ z&@*%G-GZL6lQ7dEWAy1-pjH`3@Gy-*v%e}`C0+etvJ&+cmXw%V7X!ry;4NVx%pBE^eAC-MW9lB|t3&|HTTHD&;=RK?zJxmtnzd7^)`akABa25A zk2+%I7-uK!U;mK8=QExYd#mDF4M$S>Oufs>_{2rHO;|Ezx`GEE>R-q?dq=U$-kx$H zIaxA>FSKF;XU+`@K>xXebvG$<1+O4%6GWAGz_ECi9}~ui`IHk}RCgLTGDHL|_*_XBu`Hxf(dU4LCo*aH7&S z2mN%^ZB~lAs?krKp)` zc9Yvj1-B~0)$z%;*w%%OPshKHWGxd=kG|0CaO~f2Mpa1ZpzbS|&8P_c)K*kv1^(k8 zua$6CM@LvETn3nNFG0)QXMGpnG{e(% zm|ZT?Wrhoh^oqT5u5y940*Srrs(;89AQ3MbFRrgVBJy8i&Gw`VJJdy=pH~!rTGX*a zC+s(a()7yAVJSkInx1btTm;ejaxT|p>}3l5n54k-{O7v4V-Ia~N&n?EXW~;^`XRu-diR7V;T%4u@ z@on^Tb&8S6@ovAal^-9%Hs-f(P{1a*QHLYGf~nT&G9cOFNSluKMV$1*2+U=viTdbD zbtTve{GX)9SaIQ0xo`Fu5Zj@=5T0XhKLowF*Kn-VqW=ysh2=00$mG7)go{@)5}IC3 zYK#AxE}B+`eBWx>UPQVR(~KhQFKA&+8U?ivNBT{aEd{W!>!7 z?31vT%A=^(hTA84yv~NmGs3(SBbmYSFCR8_1-1Zm`WfoEAHssLFRW|fFXQTS1UK&B zj`i$g`Sey_P<~a7l>Fv(>BYq4r1^6RJ|y4!{q5xgkuUjGa9T$b`g9$y&6*)TkZAbj z)nEji&nwha)e{H%l!rcV5?dDHO63WGr09KvTMiA?-c3!0?T|}89VgAj34AfDz`N?< z^@Xq~t$FsU^kEXtYSO`bss2sfkI^iV!ioJRoyMy>`+mP3Q}A)@HIqwlTedX+<>j3m z_n+`OKpl-RJp}f8P%^%u`Iu!V1%$nlQ*cr)<(^Ob*jr_+{$-wsp}7N*jw z&f8To1-g6EN1udMN!cV?Xjq6r@KY1(xwh%DFHoSrH_-3+q|`-Wq9SE=X?=aS&CU$0 zMmnWgg={KR)Rrhs6D1xIu1VRh-%XC;yYJ}=i^}9T58!Si{4LV_k|GG6(`P$xlDpfM zAH-OOKbg?;6eRJXWs{it)#*3_77dbA$c~>l(fe1++5j;Y~!aIW@g}S1l7z zh5z%EgCy+B3!5}K{zYLRzTsAjP(OP!0O*yabDb&F<-aHYi94-ZNb+^`1 zm#9DfZ|m(pqqm*T3k#Nx#+@4(U~~;{WC}lpyI}hHNtp=WeKb^}M4=j_j6d|15^+SIMt<+&BhE^AmeQ~ai25pO*1^SPb@iMze7Y6!abQ^)}zZo z|D+*irkB9~J}hT1CVFUVF=(Ktr-SvO0bD>|_P3Cib}p_EMI=O2>hpttudZ&eo&+De zwBKbTT^0tDXe>LidZz@TBp#k9Ew~o`EN{E8u<|0hHyNv79j<8pgBrUx_iw9Y+Ba1|DEr!k-lS+p2&Q;T1 zsa-qH)LmLXgnk0SFS8Dm=jb^qLaIl_GyA&c&F}+(@^hK?<#i2tRoDI11-zYkeBHmp zP3_F+dXQ?R8A7)HNT26m-wnL}ixsFfX01=`2twLaL#ma_9(95pMUAyc**E{)pWj#d zXKQQE?D_1!9dk+ft}^Hy?&uj z=b7|sgrfVJPFN0bkhf|q8X{AbYgwy6bC8qOb(;K_XB;is5SW~(21>GwvD^n55Ajs9 zn0w9c__zIUI~vl^@dwE4*}xtXJ!7>W-^`L~bGU|k-F-_`-waV)U))?<3Z230S|i_q zB;8?CjxT6ekVo_HawZ{1OGYQpBV$p|OrfvZVmsPAPDsz0d3`LUH#nBuhK;9cO$N`P z#Gx|d=0^%Ox<#;d{&7f4?Z+Uy4+cjIG(qX<(9NLe)WNj*mMSKLTl*GjjpOPHgA#^Q zrmVf(CWy7u>DZ1{=cV-ed*@FNR5A2j2TsE;;VuNa4D>`UI)t@$w*L2H&R#{w)ZzW` zP(F`4J4Ce>KSzdXA+-6R?M}d3zo|joRHink3t!nRQX3<{p6fL?#;8>#m zI6GpV2%?f2hp>m-&_k1DDT5e<$I?1r*l-D|2y0E%O~EMX04iaKQfK{t1w%nCospKd zVSRprV$`@r1=Ej(IFN0#n23!{ykQc}kK2b-oegPqCepO4A_3y70?xupaBf7X%4>cR z`o%lfTG;8MHbqUDsg)TZfQ<2SoPl9+W&ZOX%IUk!Ff$Nv1)FcVKv>cd<M2B%8 zcXZ`2jl|*}fM5F_`i$1J5pMFqOT`Jr4}%Fnv@d0$2B-1K^yQD`7?W}!f~&2R%mQiF zmh#0=7YBzD#0&Z&gO?Usk3qfwg1$=^?1C#^wZ+_Je#E8~H2JRH)o>l@3q6U#TG;ib z2;z!Yl|;b3k<$4lUf@EaNUay@wI(9n$Ip?Gf(qRt zQwqARa|O}vuQR&AnjGVThoA0Jj|tR?x!~nbSnntO0XV?rnigGOJY2Ix=hx4#=C_pi72Dc$NgpqMo> zy}TW?C~JjF5JY<7O~3Ov+jL>!4z-~+2~DEhl#iVu&l8i{T{@&n_9Tja^l`3QU3X1A zR`{cRWrJ^#4Hj%|L3N#84l`Ch(lwLPUE# zo%=y~UU=T2zshwLQD5Y)$vStEpMHxIB(3k_P(!a$hHGZnZh7qfVWZV{2L!k zs;bGUs^Dbrvvs{W*U}!tW;EcW4xSJOHq(2-id0=HQhe6-Sr_N`f>Bb3cACkj!gnO7 zKPH{lb0}lTv!8u!V7ZJeInj<8mxuHgQJb_ys`Z-|cB>!RlS-7da!vjzY9RRvn-2=y zzbz4TK!4;x%v6X?aG^kQY3VOU5xde=gE0SFW_d|EYK=bQC;=IN-5((~b2dXz)83&` zf21ezB*-Xr)mh&YRJ~~{g?hq@bUbI26e+~#)rR?|DM47yCTAeE`p;8)AMfLSclxZ+ zc{6W&DWk`{QCHPWo19#di1sh^mb)OXt>JK;@6Sr&=fN>Um)W-ngNXIOh-ExhU*`wl zM3IB7vnXmhd3T$jrbbROl2R^Z>qb@TrKv2R#uc3>2GEt(>povu1`$iR*rMR8J=J2L zRmUQu8P0 z>=O~?-Gc4w#n;8HpJqzEo&l$_5OLHzs~#QvkvYTba`zLKndU^FM+DKbtyAxYd&;m+ zAg-*CmQFfjY`I9nEu0Ub^^4I~sJKsDxccS6w)f!^W}ajK_?W2Cy>_t!ffUfwFVO(K zI}|S9NnO_(b+-8IkjJS+nZ2IwS(1&L99aDYJ6FnM_m>~|ev@Fl1^o_$$;o7hf`=30 ziT;wp#GAP5u9ycG57c@j6P@C|rrS-E@@TdJkK((+elfHqD9U0mcoI(0QBzCj&vdr1 zuogW21$s=8N!nsXlU&kd)YjiGZgasc zsZAk`M^^!yZXjC>q$Z#yFtzb(m!eoNntjZAqT$s~hoBq=NUP^{MD{IZTZ{7rVF7Mt zZH!DkYr?WG9rs!Ni69IChQ2>0`h~f{K$Xc)rY~(!yP>}g(!#0;Wq5TE6U$U3_2Fc# zhq6J-j7UfDS|Etu+y7q1aw`;TYN!u`eP!%!8`RvI=xpmjV*G1f!gbtjY`D8Y{Z&=> zt)p0qzCBhHU!pG;Bha1UyfWQh>Kk;$HdT}N?d6TrqI_E7sdZ@Mqp1|Yg%9KY><$jV;o{=?eRHAei$Ut6Ajrp(yz)c0v-popCc!p782 z*-3_r_q{&bw4rc*_1uvK9PhL3*&+s)rQ;%* z;Z`S;rDArFfnS4@33~m`RXTn5LQREXlKCsAJAoVNu;V^e$FiU#i!16SS_8zHSvCLw zjS6XS;E3CLUIe)mJE@CSPgii1NXGQ^+wR9t)FvQ`bkPx353WQ^v>n282;BedKz1>( ziELY=)BC@uCa2>B>NdoEGv>S}r}z&U4;0lvVsH)d+YjPaLnDp2?;_T9dW?*Dij)7v zoYZRAXJkgsP@P7ULHf#rO=I@)e{KY9bVe~p{j#MzBA3^#CHR$}YEP%t7N=hTOt2jI1 z{a!uYeZ+@T#j3Q^TP9duMCuLy0w=I*W%H#g9c;%>Ho1X+fX0f!=SzqWar?ft^(_ye z*T@r~zVoKje&)%enFRE&NdzCbNLyzB2(#-;LXu3a%wW5xNBLRwaJeh54(53*w@hhG zBiahmvrvI^Mcp%kFUl1j#Ke4Ra-m~r36*&tZ!Deaoj-6+_YsEOnsxHPm3#iB&y9K8 zH;W$C!AxGXr>+Of2l<~kD4qt&7chV4As=MvCd*(ls=M3rFWPKOT9E8Ie&rI@Rs_^$ zlWd-x`SKW3LIB|W&DYOSmFGF9=QG&n9_KT5WkDkL%NoUFO-Ff`dVau;qz2Hv) z2&yFCMGX?^ofM4?#|JShXW*3Ev!b9*E1swOy(?HVIu`9YcVxx9LpmD^vjW-sRy?C- z$oR*_U4%fLvF|X_4@}scmME66ish57jc2&}^Fki6rhBjDmtR!8Ttb^X@u=gm`O9Bd z4j%9Xh>%Q1i?0h&LRtFOQ?LD28G4h}n;?IycocqVWCz~=A#;1qbNW6eq}b@l++XhjMpeJs5UH#8c*c1MzmigLW=UmSN?6z{yPu~ z{lj~~n9x&A5W~SSLEJOtZ4Iwilv_WR7x7m9)D%Zfs6OYcZmy4AR{?g@r;9{j;8G<3 zY81s7K(w%(VCHhR7rH?c-|V*e5Pd^>U()0a4EU;TB;I zzQ{<%@^&hjwVfjhp0q+uFF0TBK#3@5hvN{RoO@>q|D3wgjm%^m^i14noA$mql(nqk zcHD&~;}xx!g~1&y1+v@u7Se!jv~dHFE1v;@NyO5N((sO!mmB+OrS35LJ-j6bB;)?L z&P%S8Bm<>}PMM*Z$ox_=sQ6Zw{TzK`bIV~{D2A2RGfkhdm~ve(3_zkfSf^UFbr9e( zUMo*1f3!}!$KzOl%l#yJlb-umMhF;k31_cCct)vN33~h1`NYbNsmV_a{-fl&Hd6;P z>^15Crij_MX7yg^f|I+ORJAsY{xap{wZ&0&Y1?mbO~!vbPK*k9jTu@uw$++6!QZn& zFpb&`6Lghn8yT`60b}qsRcxiY8mZxmM3!_R(xjK^ z4vHX?Zr+>lv($IW>MdzSpjqy0mlpknW~O<8W;;U~7InC)PYro5O@t!kZR)jbHNLGm zD{!Ul>a-rLZoZyZc^6sA=Pf)HgDu_Ox)~s5(@g_drEZiPlHbM3Q8X#vj4ujrTi@>O zvGc|_UJCZ902P9S=x=uzUinqgRE0?G(ts>3x7`rHhzU}T_Vr#rt*NXeab7Mm(qH;F z`~9mH-cQB>!c_Syz_6LgXZ@JZBnBvJ$PAAG!%jFt%35kU-_oWL8bJ_$#_Nw0v;$jB zFmn}{t7J6*19&WCSm+%fmb!fc)4j%CDzzkVj0W7@-}&zk~Sk$3hOi*qwqz9slBJ)^LJ$gG(~ zkg6+%jTK1mE1!h=RwN~uuJ7)-jTS5Gh}^f^(2mm9?~vXP?rt1CH0j8F8@(sHBq#~7 zu?GL}uQ-?N3GNrlJj2aEm z5@0`-N|wdhcb7P^|BM$q6V18R`s#k0}EQ5+8^CgnF%zcTiE-v=D zAc04)T-A%j=G)muvY|k3&WFMLEnYC#Y51}_A}T7CJKXSC6Qp$23^?jks*;wHU|6=- zch0GzpR1e6EIY5N?cklsR%l?h?^y%yWuMYVyNkprhuVCHYf{gGyhf{BW{r&lyei{J zd>T62M$YfZ<*mFRZ!fpCTTanNV=^sy~A{aO37f}GRpTyH`FYahJ|P~(NL z&`_MXo45EKF#L(0NM&FYT2Ue>zdS1@s5+;na9- zV)wj#EB@{5&2>VQhET(8u!2kK#p_K?*5JE8a#P88f6UFa4{=zEDH0B3IuV1NvZqtq zM=`cKc+CC@hhD6=J;%&#)){9>ZEjS7QC66<(f`)R-lE#IWvrh!n`1yyLSI&6E#8jq z?yfV~oiFd^cE`#3FL#|9lF+P$!us_kFAyV&R{YM3YDPS|ZC1KC86vMdyuj!@WgxCi zvB#+o6C|#AYxg>jKH*o=z}|MW@{)T-UT{e6C?$8tC@3hHrVHL=xtwrxY_@r~@uF12 zi5m5GmNaf((So8ja}ywi|A|V7g8O;x8sgHDBKIy1Rpy0V$KYq~JRIS-5d?wWwUv`E zhJDk6Y5ZCc9tlsvbz6PcAh7VsLQu}fS-xkMI=4Ugr^Jpgcq4DsaVr>%PEH;#0Pc=F z{|=aa?_Ej46yJSZd}9%Yi-&CZG(QzAPoy(0%%0}0894}Pdw3gd6;;+WP#_*OQ&<8N zzT|~^B-mkRh5D82?`Rhznuyr(J@iFUPqbcZp^;jIc~ z>O*>E7e^KpCQpS3kkV~CF3T|_2D9WK)NgLF74Imf+`zB22hHS*w_|{ww>gT3ebL22d}`*N?vgMo8#C ztE*-L)`1PWOPs2z?MQR8YfI)X#Deo~g>`-JjBge$?%D+%3H7KzCHeJ$?Cc7cwl*on zj03ZBdB^v;7n>J~vD$?XH(;)d*WSO4# zpYb%?!x!>@{?Oda*^PjGjdF0M?8;MRybCof7N!honlL}^=!1zNRy@^yAyV1ZvoujN zKvXpu;$BVC^eGN3A#`h_y}$N++nCNMLZzRvIXNno>Glp$cwqP_UBa4Vyi`^a?i>O7 z5R==)^Cwj2)2@H6nZVSogPQNOnGdVh*V_6P*gXRUm%e;!z$WKtm9N42fc!t4aZ8N$0wRMAiZG>8=GJmY2d>1xI{NuxqF6V&!VQXqY$DMn= zZ~A#Gb1t@pjqXge=;=(cM(j*zj9tT*Am$z>yg~-CG;Ux7$}eVAN5pS()(P|(Cz7cy z0wun3<*gAylqMqbZaS^31fVY9H_o@WZg zUoQ#Ayu<##Jeq2F5ccDy)Mbh*671kK_`Q}H7}(}BEO-Z_leaO9};Aro(_6= zT}9cKvz;q!PbsX>eDW=FwoOdCMys@J-)&lzcR8VkG?~48L)b0@WR-4lRk)9o%ws4%+6qi< z(!-fPs1~1-SS4S4-{nFfH@0Wjk*z34WN5I=vj)+Oy_#`vVr5ve&a@p;LP??oq9SUC zDr%{@bnWn*x0{$4Y3^zC%n@2O3Ril-}_ z+vg_T#x`VD=^zAOK}Q|XRN0|363w&_(?`*Jdps`pL{<4=;ZJ}-6!iBe+vhSSUDe8F zj+gWN&Pk^!T(-F7Jr%N#w~fq?HgPG1g7T$BVATg(n%s;2x5xdrHRjT>!;IdUF;jkC zyw&7yO|YbX=SJ^&i!Y&9ra2-|bv_Zki_f*%?jPO`kBo1gvX$~~Wt6n-yz<&-q6L2i zG6_3F;uF8OIzvo@95~|rAWIsZUrNE*_uattj(RxcuC^wgI~v=Dc~hcRKYt$uRaNufH4lOJT^1X)iqK z@CJNNNa=H+e9#h`T3RvH+FMY+EJa>i0LA1uF*#93K#xyS$O4!W5#HV*=<9yj42bCC zgarrkj-)%7)5;y=eSDF-AEeNUw8Dyc$6|Y^RtP@YyPML>#eGtZ!&!RX(^H@yr!LP* zXtu+I-+4UT-P#-TiNeUeXg;FQ;7QL zQ+a%8dwV^~1&h>x#0cQ~SuWhXSvE&h$v7_wY}c%*Dv9_Ky{q`|rB7RtpuJoSVCkUr z(BZee{*Px?x|9XMcj%GozO;2HxxO2!BrbFp?y z3^QZX->a?pUpU(zm;YTjAOV9j4P|LG#LhWepO_i}Oz7=^*U}Q^r!L8zXFGgPz*w`^ zZ|Jq}?nHfv8vnN)c8{0^KvWQoN3<0P<UEHx_}!R$?!XorV`$G0(5IK#7;SNhhMwOBEPSrHf@PmwRMvEHuiw@!%bEoA~&)>Ll86=EQxUMCxY`! z`8;hVYV;1_yujL76@<<8a4!;|!`z{^%l17^fPr6SZr)c(S+b&9VNaRO2ALKoMlI~i zPo2Lc<7eJ68Zz$C-7!h8C_Z$SY-P8Q+cgLdtq>Krk$VLkGP{JM+I&|pEOUnRN)>Ee z+zXmuL0LQ_a`#Y)Yxc9M`PNY_A(Q2j`s2x8^#+O@seq^CJ3X>C{bF{I(HF-RtU$iF zJ4!^&J*I8Y0mkI?l#7|eq}xg~NGRwc<3+pS7*JuTEk)ZTCW`SrZUQ5f$r}uPKFp}h z{O;a4gbJU)0zIPiq)9p9UBpx*J@q-Rpd+__Le-@o9ZTEMFM>9Fj`# z_A?N_4~;Fc4kFjvstEHdOD9da`%B4z5+TA``3D%IaV5r*W#m<0lI)UKrr6$>f6@GY zhsD9nrO|B=a#sIR_9}lqO5VTgcg+oAlU_wKxg7NFmXrENKZP#qSnxUQl*Yy(576BD zCh{|9=gvLHjoq<(UX=`zoOQop^IarZ03igJ9Judml+*M)*EoAt6cPAGXC2rRugb7B zevub*ErhDgGgnR=F&Xuj#t4%+EQWrNoQEcbX!9X8{BA7mhpc&kY(mfKn=!*K&WNeq zBzN{-BcVdmVQX%(Zmh}M*%aMQS$~+q-2ABx?OLF?c5>Z|7MsleT|XKg z9W}X_66}1fSxL*fNkzO=M5$Ao8Lsi)+o;|3)m6Fpth(e-j07f5DnZ80;@d(+3L)CB z$^;Z+VUs)gK}Q_H3mVtG+NOq7D4O9Ja(AnucXd6Us=%+Cdch@(ZmvSEetV7?$Cs%| zf;>#(h%lIIBbPBSU*Fet^T2|E*wia}PEt;eNP!8C{U(9@qN%yWZ}y96vcK+r6-;et zGVr*uEnK=_5d#rKu5Ghto(R86?1W+)m%l?(OlA0Q*EtK~ukb<{sf-(@+VFy&>$rlG z9on@lA--R?kCHFW2_<80b6<34SS_|Ow1q=oXg%s`Yb!=sE=*g!>%l4XZvXxh`lVGR z#zX$yBK~^p2k&oaPqRU9JWLoa5By!(z&(tIZD0`b@qJfY{p628C!8}9Fm?h|h0Sv7 z(UyVLvopbK7I*?_`;Ct$;<0AU&2Z05wTBb!4F$ztC>a(Jvq3rgl>KI9C%i(Nsy(G% zXU#Cyw}Togd!4YLN8iJI)zRnR?ueRwCw+6}D5)M1l~aTyZRvUzbP@JjoHB8|gw^RR z3Q0H>$zMQ()|_;EXUu_Fi#rn)=D)Y%4#8%4FOc1k4zezL86nu%o*FnlDGZom`&q`^ zy)(-6!q9N3#2yysS5-`C{sm^#scFT&MQ6v!A`>BR$zlQvlUrK0-wFQ=m?6(RmI0gC zk;W19V_%J{z0i9R%cBr`cuFG-9ZIi^x#_(OCZUmS_+zSnN?6~3Zg@~mx2t-cx|uW| z)TmXbB#Wp!f)Xe}9eJYFnRO3-{=seK0$_T=8J32l&neD4H5QdeC$5Z*;g?Lir=Gze zFiLRU?W*#JO5gk~1tiu-jJcu-GVcw;@jIZ=jf;JQ2lOw2Mi#swvH#Yicdkg=HwY)4 zq%WN<*YYYp`JtqEX;Mc*`fvbmz1bGDXTmC9|dBGe{F?z=hCB8)6e zjg})vM!74^eQZLLpM*3Pm6bDz(PC;?2*3AVn0fa;&-49$KIjNfp-MV5JL+iVXexf< z0THC49JxK!Ee$SkzODYQl3J=~tJWKzxrUOtbe8w#xL{?Rn=2uu{BOX3EvE z2?uM*-4#QDkKt?=3{2?*qW)^l5Fuf>z!f*XKF;VjUcR^sy>VvK zkhhe)y}8EQI3lF;X==i_xzwM$cFQuz;>Hpy3G;Edmp*@zLw~f_H|VyZBQM2&o`{;K zwzfQjVGkYcJ=rg*_`a?}qP~1@W6slBFaB8rkFy1D!wtG)F%z)FR~HP_qyZSrkvl<8 zFXt)NvkG&RKi(Eov7DI_hl^WK{qk%R!UYB3e(^PnnEEMPLV}U&8EJtW2(%U_SQ)Bi z4sGL#D)iS**q9aHdD5Y!qIb&1_fHVB^k~^oblhF8b`S1WrJctfZJ8GXjDZ?DJN_}k zIO6grqs@TU+SOrC*>J4(hpF}vCfmPMtJ1bxscwt`Q=?uw?PlZ}B$BQhDSedthZCEYC^Ko>Y8$Tmr684jwi-X4AuFr3qt(EVX;Yd{~hcqW8bb4il=nLLN%aNW`4F z4){Yt6PGWV;|QWP6s=_Yi^;o$;zY?-(EQG54rlDR<(@F zV8sEz*OR__7_i$K?RND@-gUw^fN_UH`Mz{d8JGVe0f*4L-^cHHo45~VbhiR8~vbGeC%Wfo2 z**%6vv`l#}Q64$|dW`Q{!Qo%>ZJv+(oZS8C2_+`1JK^e4q6L@Mc5zy^@~EHYfO?G^08 z4F)_OxMGK$vUCGM(OGt+Ea4sp=nF>*2&ONdruNS`lhdhRon}+Bue^6|&TlOB_MoHN zXmLFc(*aGh)ipMi(f0&>K_zv>PGXL74C;o?x!e{!bvOW3_Eb;zh-=qceCueo3)0k# z8@(_1`tBXE1Eh-m2FY%8pXt7nuUV7s;@46|hredt6GS=(Z!EZaum<$`Q=XbHJq7Y` z+&v8-Js4!+#k0_yFSIvIUN!talDyujI&*{|nKP~#*>~EM)e-xk-UST~rG0ZUR)#|% zS*MNlNog)}0uzY}0msi~1bl5;`V-nGpjxf}^o0M^^30Uo%im+;^Zu&YuEJ-8nnvkQ z&l4n;I~2sAaQ&86dhDZlGPyBHxlc4ZUsFB#iXkI!0cwfwxATo;M<<$IHs4v>>}37y zE2U7iF737Bn-d1k_^_EVCtxyk7M)Q@T&LyJGa8?%&d?@t2WL(wUVp&i$4oO|Bwq2X zTxM9{*4_T!XWsmqdd3J*=58IbZ4N7rE@nTLnvvheX$I$pUdkiYG-+3v^`g19nP!k! z6~bIyWw{|64noUbl(5z0t04W?sRd;6N6g&LA&JAhjT}3FNk7kOWp=xklxhwxEI^iq zOH1!4>Zmfc3zhZa1w)5>8N3}8Nr4F7RArXQNkOxIg|2Y@^oQBuXMN*?qrAwI`*)*u zmy!cikLY7njJ%&;_uQ)MH^+ncbE17^N>6JTH<%l*G*M%r^akVEPJqHZYr0)-IqI4X z%}0_X{>b0g9(31G3WmA9<(0Z}fCs*jcM1Pqk)*j`!4{trGtFW}4@F3*kLrWpy(u0u z&(@b!_2R$n?(y)G(QsrHr>HY(X5Fh*b18y)ft%|)pZQ~uAe!gl9ychyKo}$}MEkxL z6FDAvOM#t~3~tCO5T@XpngFkTwQCT1*}=jR>5{bj?{Qxm@x4RQ1m~xrzNF`vB0E}8 zP(NwvG6#-?9g+)MsrrUx$300d6lI{l*xc_owC$O@U(i7lbE7e_;o@+X-ZD<@;-<(m z!@XsI<~Ir^I_^l7lq#m;NwX`Ym}BaHu0dZgYO2f?GshLhiE@ci2sjuyBu4a?XKfqU zWTrgV4x6u^C7tsV{I;~!X}b9%PqcXiF*H1Sy;2Nc=IDH@PFYiCZdJv4T+~faI$Pj# zi(Z*E#Lu-|zFG34cXg(0zEVePwnQ=n-tZDalrJT!;M-9z2cI~PopA+){bO^kPbzD! zGlmbyc;nRkIqj{rRPWiARcg)6S|Z)C=rMb7**n#o=!@Yj&|JAo?K+ZmzzZMln4}=s z#=Dr|{w{Ag6U+4qlL-QT90N$yQz3zykNEzU@Y=$*HHzcTx;y-pl(v7~+YsEB%DhsBIM{3cO;5MppU zY&BT+>`6`9u@?}O9t{4tk6ZWulBPs+0j zVjCnuvrOKLFf~cS!P0xsk>Mg-!aOg4vOqK8UmYv+FA6(MXkah{zIgYn#z|CPL@GVn zsBpRb3^#5hf~=(@38JH8^aq4%60(BkenXaVBl^O%)Ds8&(B*xzcj^ZY^|@hXjqe98 zz#M+IvGo09BUwqn*hx+WSVE@l>ESoS28cvmmI$8W?>~+qDY+Dv^(@7gXRj{slC-*_ zjZK2Bf?gk#Eb-Bb>*RV`cc0R~r={|^{_@*^G;REM8<`W_wSF2h&yDYaO)4~efALw( znQ=Wn?Ru}7`lX16XwWCzDbK~|RUS5JbHP0`>xWax__}Q1j{!tBZT`Ea9b_IpP@8mX z3Y3QL=}uYvUGOmAtS@YYHd!ne!EOk3R1;5U(7-sPwan87U0%CZW-q8Qr$~3!+ZBzt zB1{Y6+#yaJ6D%>g(^pYYjV0yf=L1w+{!C}3yik6 zWbU1Ea>nOE*IGW8T=zu>>Y59P1i$glw3EBG8tKPq;a?pAf5CRViC2P}vSSo%ZP2$< zOW4rjndGeX&SNmtX9L1h{ql3}l#%>{f_kbKU7`LKdeKo4wLxT|x7{gC0ViBlzT#w2 zEZQX4&Q9{@@bu3GzXK~-B{PKwb3mmX7>tC=o58IGAt3@plz>?pJuHp19F<&Vt;ugY zcv^qyvvZopyz<6g5NHyUwZEU@ZxiguSPiF}%_iFs2ZqPjK@#C9c!r=1r@8YMeP_F5 z7gEV$9W-+AK?s86Q@POBevdsINatk$BDLQQNTEUVraMVWZv#s+t1$ zP%Ec1f$O-&H{+g6sJv;Ccs$Pa&`g5DOt1Fsceji6hCYVNsDw=7)<#R7>WKjdOK z*#fBZ-q?u4ww~j8sRI0BLy@5LSbowZ>nqbA5*Qe_yVGFDeJmWg7(AN@b(Q^lrRvkT z8!OfPQxLbdrfY6KZ=>ow^r&drH@P>Xa7(y;W;BTcKJUMp&QLM;e@DD7gz*1EvVFfW zQ*$NTxyI65Za#n{wzo-C86*G#*&{7buSOpf^u<{do}S9P!E=DC`x)Fh=R2h6lmXpU z-sS8@uLcYr*5C6&&-UrkF*vxYww5}05=Ly%91LiR1u1w^3L!6_ zM=k9UWffYUgR$7QMez5J`Et|pMJOf4()y$wa;-h8%wNgnHb{MQV_PEq)zBUL`RzLY zZ+-el`7z=g4~~(DxTOmcQW}zOhq@T3|J4IlK9UZhjJ8usLAk8f$_+L&}{l;n~RqQ_71vqD{M482Z`z(Z2~N6`DjAlU0Byu1Yf`BhiSN@C2|BF zg+O@8OqXyLwIIW%Hy?|GApg;x^pQcpu9-c(vvwJcHfsE}l$A}|3`~6_Pzp?@QBL5z z4ChaW0MdjNa}bPdSB|@;EPH@l{(T6OG;BgIjBBgH_O( zMxvZWI=ofpO-{5HphM*tr^yL8+gV49(>`xDvL18J3>}Os%R06T6pn{BLbaFa9yu#^ zdp`L8*e5l=8<_O&Ut$LK*HX^}ZflF1JQkWo<(o1OqTGY6B`tDd>tz<5@NlUKJ||Ha zX>Ieg&A^#sg$4>d)nYzDrM&=1m7ZXKZ4ddFm|Be$CbTQE+g?>OcXf`ytC`8n_sU zz@S|ruoAQ=Aus+`(#CoOZ=oK1=`%HH>4?Lu(HpB&cA?+KCg(9f!WB+H{EG{9VYo)o zbxR)j6YA;rGUhmIXO_GVs30O~>&x``IlTjdU@YpzwbmMkKwwmhz0YFlJ+}hHPPYdX z!7BQV>r-AGwwj;#@r=`{GS;kip-|ky!dxT&7d^lNRVpR}atXHzUI8;g)D-g=n{m+s zq7=xH?x)oL2HaH+=lzNS7g|gaF!{!C~1v>8w7ad0E*9E?z0E;lbT~iR$4+nVM)9x4zr;` zGMfkig&&5Iq9N~q$*3iSqw(L40(-;DU&ds?8=rnoyvm%J^ZTt^6I=6akfv&K?yFd9 zZ>`)U!XWdOgQuH$Kj=ToBJ-Ilv;GmenKqr58%H>)-C{9V8MC&}yJ zvukhKG>y}Y!qlnHUaV{1LGZTM){eJ6=*ia2WqD*`q;-IHYr@7gnta4 z>joafy&S|@_8RrAL!@D(b2o8G+vlcP)Ui;=TqDkmF?tfGc>H`ww}XOXavg?rJ3Gy{ z^K8l6osFdXlByBdr9yZ1u;?Ot+jFZFfx1QF1$$PhULq_S!Mh{wUz7Pox8Zada?dAl zCep5rpUkwm8ioju6#B6FTWCSFEQ>MfQB+{VDc|O8D9{DE+w)iR(c5iVFFSBhc(g!Z z9Op@a>1#8)Tm_Uvs(`4o0}O#ZE+kjcwzu(@Zcq%)G_j2x91OLTZcUoxoj7!I^Izi7 z4*H7xZRhbzeKVRAkz@e(kCmT6ndt_;Ud;I^2AWpRm0N`@Y2sqvMbXpmM;*_vPyu zGsost289(boswR;6*Kww^}p8Lr02>3(zy*2CDje>w9}W*9vIKLEy-Kkq%B3dO7FWU z1acQ?0~4-c7ufIok5z`Bjk~+AoV(@b9h8*BQr~Ur>Y>Ft`8w zyF!mqk(+(VErO6Y3UgF^`uKEBRnYfjk0t@=GH!he0=CXOGAdR|kSsg*Veh?PrY%c! zalg9uP==&&7ab%n*&!3L|!e z$FMPhdCYEgJ~}G^Ym!7-5M*F7N&$#wtu7!hI5OC3ViXeBTZveVoB`uO^7iK*#B9i3 z1g(A%3h(TTMhTd=#RslNqcowDu}6=93?s?1WQU0aPgb6#LpcmH<>Y$3E{XK}@5rI^ z!77bgz&)|xJ<82P+zAR&?@yR2EC1D+eeT)KIXmgk@%1Sx-%?ay!A*TA+O%kFHM>w)^4dbrw=F**X7Elk^8U2Huql zeDy1)Q-NZ<{BnK~^za55PAOVCwpL zTGDP`HqKOE8=UM&NN87kH~?;7``!Q+Jn(t~DmD0qm(IKymY7xP5sM?FH^X=tEF z&_6t;0-+s%z^TPA%F>*bz|@bHyxK7*|tpFin4fYp5Gb?tVvG()4%A24R|N8jz;1 z!5chl44mu^s-@yQEJ&k!;E9*@^~`S*CI5SMP*N9*q>chfPskH?_A*|M3=onw_O43^ zYfQL?Gd6)IP1~)I{PZueOruP->MGx{M8HS;cdVYvdTfAbGmw!LY)&*S88FUJ$|xSi zF>An5iZfejYqkk-DlP)9x$Nbd?AxpVRBUY|H6XSds@rT{*w}r4pCQYU&+7x)Pz!%F zng3L~s0nr!BM_J%vff-v&1z^)$W5Q%-y?XkPSm!}L-8-!#zWZJubW@6dx8b?$TU|u z5?Gzo11~8#CFb`nCo#0vs+(3Mm*=V8k^%Z98+n&h@#1t{(t|0s3A)JeD_;mEGazWG zL+Jl5D0>}y@oh@bDcF6(x^yw~z}&5!BR#Wg*63_1BUiP&&`|#Sa_6>0nVD6A{YHCK z&qYP%6oX}{5_zaopv}0+5+%CPZVJSzKxA^~W6`!&%8i@-2?}RP#{#5|7+e@1OCVUu z!q9VLe2$TcShZc?@@LO`HT@77e$_siKgi&Ot`3INfjtuyuQRSszjpyc!cQAWne+wh zJ08do2c?-XowPPxFgk6FLx7RVh+J4Uw>1HxB zt+zJ;ed^YB%l!P~zocr2y>6@2`%Bl|fWHx5NFTZ|d>k~=eAQ||^GRwQ=hnZZ?)~y< zZ~5x}^{DR@`0-^F9utWd?AP3GHyt~>^Loc@Hzc3iMm=@&>}&6A2#u`%C%QD0z#o0} zGxME66UYWLJ~-HGlm=Ws`EiPQ8}ZCrBu(lb(R6Y%90Bwm%>|(e&mfwXcMX0^y%_8_ z!yi!0T7&>CeokjO-Vgu7pbLsg6$bgG0P!C1450A?CN-T1}?Z_itTu1D(%e@jZ9@&MY@#JAAiP2BMU zm>KwLxoJQj}8Y zH)FxbD;xW`M;_b{OW zz>i8UCt1myICfrsu+^*NrWtd1{G`-F%8kt4#M-0`mt6jdYeEMY(fmu1E|$Ae*7`gmPHMAHP=1$)?qIMCPXAnZEo?FYg_9$SS>-QfJ$~? zgv~oG6MQO&f5n!olp!p-(ZEP^v)j{slcEo^>HV62=`oE>*=#6h>y@qe;xVId_%i60zc80<9ajD zXuJT|WEl6!-3>F?tT^s~V>amS?H)cX<@t2!0f?LmAg=f53P8r_X}+ZFJZvOVp_WUZ zymY8zuK-QQHO-x|sM$G_Z zVWYWPK|U=jSZUY#B{GB1XH;xoS`?H*B!2Kd86p00Az+GUlmM+Y@tJii7V?cG+L0j6 z&ZM|OT+5z3m236(mh{YsvxA#@^RM3gTjLxo+xhn|sYR!jyw_5UGa=O=_q8J75!kQ< zAWoM*08co0VsC;!jdtG*%+}Y^d>V1^VZt{As3b{TR64iMNJ3j%NGR^&pLr2c($8<; zE+X&&NAhF@0He+{)?ZeSI_rkrYWI14E9euO=emS6CorjlgK5yTM!XPvOb|8z%E}{W z2+}8HpA|`a=BjxiW;fg>ZRNw5z>Qt9?O7NfHKMK-|a0kV~9!yi=~E6 zb5KFt!)kGb($*T%H6}&GCRs_*=kfZz-lAPSI7jqSh13F%H!+(Gbf z$a*{p>XPbr6q#mMx5Z1KLxBEmJ%(!yCa<39YuYY>Uh~GD_)G+)R48+qZwY69@USLr zUwLb2=<;p(PbXlLiuaX8w2`Z`1(+x7;Y!EXZ%Uz2_|-*sc%i&yclZ339D6hIqFgo7 zNIa1n4NNBl!ak=_O$YMftVtzUgs!-8jWYBP}M$2}>R&FW5ML-ec7QKJ9RJj2UmAr1sPP?cAb zXS*`M&{Lg9*E_($dj`~`((t}%!80xJ8 zdDCN!mgY5o9E<^g14Gh<@MWBQT7^Rv00sRu>P;FnZO?KT&x}6?6Q}Sym+klGZkYga zu5y65eTQ6;?moX3nIvrv*eABRyHd9eCJo37x0i9&0h74Z@OH7m!D4>YDPUtfID?b+nDfR~(vC?FN3ijE#M~EAS0|SrIzU5U?+hTf`>y z#sfrWPuvUQ*3|pnP<|nM!%MzVxq;)`J#EIF{l#d19yZIU8u$|ZxE|?r98^{Qyb1z6 zbAa$_{`0tiJEC73JE=4b942NrU+wa?KgYVEpLVol5IDt756a-Edthc#)cwoKvYM5m zI+b{dTcvKgvrZ@P3TGF{r+poSg0j^VN_9#i2?5$1# zJd&`|Zu+`&2aad5v9AtT)~)uZI|O;t?$XQ?KXkjNftEchkLKWn2%s`w8H<@DChxQU zdzvVZe)G|RTrda;lN_JC^H=`U#_&sX6OtILywsS(G2+I{sHzd(zL~O62WDXp$6{oa_>x`2ri<^idzj-T~2p79F_nScQ{ zl~@=5z?32;R?vp~f;BGx+3$C+2nX?i&OEOgkpK zoRnC1IQ2q4Kc@8=qh-+DNUb0~%1UKPVHw?HNBPH3{#RkUdu9lJCHj)@k(-OWPNH_A zmjB0-z0s2g4cg?(R9r2az&7OPTQGn41xAYb_u)h8HM*(-NRacx#pa1)P5j__c=uRC z_fjUs2?ffsS5bJ`1g2y}|5~BnygV6+5E2OPb@bqnu6do3eb8)zV<$`%Nm^8V56*zotiPOGo+0KOGS>U|4JD`yV`tvVPhG@wJeVZLPyNO4y>R z;0}zyY=-`L-|qy{C;0;e2ZcQ6ph)L_2jo7oaF%@S&6~~rea~tr&Z~@2-I?2*G~de0 zP$?BreZO+9My-iVFk19fey|_-OXW1iQp40R^zOZPv6GJ^%@A97lp-iq2GbiI3;MIq zPvr-b{V~CS8L+xyhp-SVW$2y?-u(DsoBe!$#Mnng;vkJ;i4JoA%Sp7J-y>I*uJfd* z=Cr72LE#Dzq30CZ5YGNuP!lg)X%R*?O37tJ`*s8SyVu^+M``K0D^15#?5D=tM!TOI zy9+!2&En$Ibl-raT$&zIpL+^G1YzsZ431!_1E0tMdLX#9O`O<3t@KJnM0z>qrhKcuZ0yPcj6O8fA}r%xV)nJ#cf!(i(DkR9h_ zFQih!qjYJ|%%0Tqnj{h;@xja-gxU(txz}pi{Y0{D_L)LAjE7cNzmPR3rl-L6Ih=O6zktFwSa8pqkRCpkc5(eFKx`SrV>39+?Tkl%L%3~ zy}0@-&nQbq48+l*fvF2_+79ZxLPjY9zq*k)wg6snwl9b-G9&nBnrrg|dr62I_(E-v zTSA&4b3eXz(O|d)8}#&?mWWL{gL>0C>)NFwyq$zvYBi;`lkb5^-cAVm;nCZTblTVA z=GQXIP2M%hmiiBlEDKJ}-goDTlLac4jO=g$qLXFG>ml}8l0bGI(K$~@5j zEbGoGX0wiT1ONBIl9B&@vZ-|;teZ(wh;!=nw0l<2;S#a*C|*l_eZA#5X3D@SqNmT{ zHNwE{YsJdz?WkQJ&tcD;Azr(h1y~9W2Ppr=bKo0;TWO`gzSwdJH@t45siPi`l6SV* zEIw<9;NZK=z}D+*KBr^~ffy;^@%`l)*u=VO$=5?O=*Yvj!O$$hJvE^F}>2;oI>EA-PWA{{P-OPUP$sJoudOcZ-Rc06Pg?e~$kB-%teYX-6q0nVj zGXMgRM3(U*JM|S}MNGYRJwxRO!J(?mBNa34E}UvXQE+DkzD6B_as$<-X`IDS}3)oIj)Yl-u)(blEgx(HOs- zjej3+Acf7L-%fB}ckWsfFq3`pn;Cl-=jO7$Wa`x)@v17=_)R~K^#6fROl+qe+h)Eh z0YBx(c&U#Ojy=SS`7@JlDY`+>GmS|G%?-gXV^#Av+?6C!v3vAHBza?xq-8)az*yIy zcAB@ao0|;gc*I!C>uIw^Z|iziW4M&BS%M%z=2F7Nmul*|_acV%avv?hedj|y4sYTS zcM^P!^340Q+=bk~B3WYr3ghz&wuG{io<}FCF|xf3PUllm@#&I|Q(hSUs8mU)j2@$|EPeC^%fsCf9k$cw9;w`e}{RrRl!tOfPRxd&3`p1IH9YCEXjYH8d zpN8mOjea}D_`BCqJ3Jp8zxoDy&^R{~S=sOVy+#_L5^51*^Kis6VmNvWh~Li!s2~dR6%*Z_il3GPPqNUz^`>e@zPK< zHq)40K1IPtUj6hz7`3~5N(Ksa;$M;}#FbAHhvG6wr8inFtO2I_d4bzqO>$8D{U#{R z+#(b+f5BeXCIuvoqjz?`!HzrjSMa?AiotTr`oQ42bH-buzn|x{UsI(qC0**9gnvrE zzMj~MUU@)VmUH1Tp6MA`n`QRJfls+FD8^QHcAp{&_e$zPVS7+f+uJ;wBVpV2FKpvn z6=#VJM3ww>V$Jgal6$Qp<`}L^#buv=L&v@clYSkSs+46DNlivQ=i%4V1MnTmn-+V`(+n^ zDk-RMi>XLwXE_M`ST|H|#M#iG`2FF7I=a{G@VuCRI~(zpCna*9VKRvW>Q9+mCV(2& zXKsXTH?)~AxEB~Vs}m?SDInjj?(*mUfARCZa_DceHP@4F<(w2U?jx=ck(DWlq37LG z37y-CijfCUVW!81rtoot5|GwW)|CN=?%Qb>oQ{Z#mzVNwi0GFQM?S{d!Vu1~u71xN z{OiJgcMUBf}3i6$Ep8R5QbEbpoe^Z+v;gKoJDF%69Jut6!f!)eL9=`KWx$ zYfjK7yf-~COFMIE`JTAfT+;;LZ4uMNuX*_2%cH2Rq{&(XymP$nGA<^fHnZjB`p-LR zct|GiCHH+S*i69l9r48F!~5Z>K_R}MLv1Vja?UGQ+R;vxp^&hIvD0UAUQH`QRgsx0 z$j`-of6aE;yANk<&kFv0*JkQ2Zt1HDGS8ywL{27^EWP4#*0w!9LUXK(t*f8kzcF1z z5O`4?+*>F`_Pj*YZ;e)XoqVfc&Nzb^1GQrL%|zlzdaPS?ZycB847f9n!P%^~M_NL{ z=c0hR)M`6d=Lu@+HE{Qt^%gJ7V2O+BVaW!5G6FJc;phF&wOXQvNfUa9l?h-$`HjH* zvWBWsP@3`-P=UH86!_l?Th`et&w5g^x-LG`R7v&s?=xJ^6MUZ$d%LcCHgTgc?WwxA zhF!L6)i)7lvGBszpIMM#>oZ2aW?NFN6WRIqy-pOHirDgs*zr4dD|XGbu*nF2QBnPOw84Fh0mRJm_zS42tgJ{W@m@e#ld-qM&44g? z_)Lh|GQIeSfq>fWb6P}2z+5b&)O`hoyBm8e`1&%g8kv?4kz-yrRimus&yY2|4lM%Dx2=-h ze{;*hw^Qw?$J+IcZ&j_VNUOTID{U1zudK0HWA|?tryOcFzJJuxT(j!oRD_d|jf!gx zAxP?>$E7L+(z80thK2YYf3Q6ET|(!s9bui;wjHB@o5bhJmmkWOAKuLFY+gyDOZo}h zoY`|OsyN$kLPWzRTwLvkW_!4DK)3-^)QR_2+dONLII>B+!Ps{*-_=>k^>4Ui7NOg~ zz|HNc7b{PGEFnq^O(ugow-+yhdHni%K3h?V^f*k|y1!OJB9k(75qMbMzl#f`IIBJj zIsBw-nGPq*qjklIUnYfxbpg%sjOtzF1b>rL&mH);#cs{Ai`svhQW(ozQo>6|&J1T> zP$xIGu8n$J!t%gk1+>&M{vJ^VLcEyhi?pdcIW}IjN_4Cw2;*giQOK90rY%x;f`)J> z=Co9bixJ_$u!)wOKS`7L4}mYEQU(U#nso%2IS&%%_$}=e!oczdbM!5OaBYeF)|QJI z>^pZOF$pbaUOQJ~Da6d4<=uMi9l(9iUz((e1G zk9MaIP)qPV1YEt)71r7s=j_qO4{tz}Fdxi|bAftTOWmsgB*bIg>X_u7N7EI`%;3Mw z$G?rj+rFa0`v1BumE`oAJQ5QZHEkbA|f-ZjU0=3jO}FH?Jmq3*b5dfLLwQKi|SWsoSIDg56XkHg)sMt?k$Nwa1) zpEVHpFF%QXp=(T5^hi(lCZC(E+V%U4-mpRUtJZvwU7pfgu=hw&gcT8f1yd4$4B{Me zaohqnzcLnqGKTzTNp(+Q!eq42r_4Rw6^R$32J%Y1*jHf3;MyPcyRkPWl^psjo&I|S z@=#{Qq~cUp^%6HWI1^YCMHkr<h_Nd1Ug zz``YbyqIjQVV%1Bes|p`_ebY(e_2^yS&`}Afn^|rV-^h{i3lt=9TKT@R^P{`9&T8Y zzS%Za{si4H5dLHqjYqs%zY61bs-;6z{S<8d+D@ywNy~%-Hr8j~$J68?+1fD1N!XF2 zxm?}6e3zsfnJ;hCoWc89QN}G6l12$nP>pO2v>t|?bXQHidccDvRqIqaWt`Zr)kMzrrA*nPjdmiJ+pCB}>k4}lYKm`8l zZduck62>F({h)8b7T4DIh~Ij>J35s)s#J3v_Ppq%#*eBG!}Zh3vI5#RI_54rSE9_9 z&k+L0i8sb&gXJFpdNrXgifM1mB%+4VCFg=dqdyrAa;Y{^papr-7Z2u2Nr?nBad#8G z*Id`BODIr~xX*P+5f6rB_7oAb+}%ej5Eco$I~#qkw?EQ?ddI3UR{k)2{}W*{g9OIs zhT)NR;Fn1j0+8aRmVFE%|GCY0pZwoR#prAG)~ONNP_ug;I@&@|>PpDx`|f?5SL~k^ z%P$fEMBv3{6K~g(8309vo_|EqKXuD_BFQiKiodWGEcGd7a1@6^{TSn%xS(=< zeAc+THbwG!ZM?!geIKlqFh<-53Vf@V1X$bgijxfEi)1l9;4adsa>m@IT|V$Z{l`I= zR~4>qc!<9iIZ_;TcQ&T|Dcc4i^rFAYx6*QTpXFcE*Hf(L@FE(p?=Cof6#WB=O5F>+ zzfgG4`0q>2Ec`x@U8+D9E$gLz^|e(C5{8L{fk0J6t*g1{o*!q!BNW43==)WCk?0Z2ZgVHV^ip7G@Vv=L(^O6DqM z;GTh)+u~odyP5!tAcuG?M>G3BGR`D7;R6D)2z~bE8s`x=iTk3wFFMXO zp`nZRt(%xTgvzAt1&59gIV>)9a(Qv~w*ypQf=hJID8CE>&pR#dq_ujo3dAm{Wy>Q^ zq^I_TP0e$t1jxL&c--?N{wh#_jToE&Ill~vyPd6VP6&~;!xY#P0dQxOc_MWk>q7tt zTL%7P<7kN8C@?g9oNyO9I(f+skvRU3?X&yE06|+X>&8sd_Gf36;Tx`!{N}R+dT>t2 zWk-9trukwoTkFKp$#&Oo9EIgu%nq?WJ$R{pjU|LaEG-9-JBL_n@i{;DOx(XAc&MU7 z9u<#vbrq7P{G8fivFe$L;)^FQ1@L^Yo`E^e7@6u$Nqu?*3ArdR zfo&YfnU^W;4$P(QhZN{ez#A1SGH)g8h&wgw9dK#9CRlgQ&!e-fA?)??`NQ|u2kw;y zbkd3Kp|ZLGfv)p8XrOBvKf$~ABB&?EXVyq4JCA>W|Jl>2hd$5YY~4!E;3a;ulzD-` zPJ2n1n-k~{3YfFhUsHg&L4R@*y|&%SyzC?nQ=Pf-+)U#5SB`50S`<$r{?&3n!yZ`W zaqLxT!#})PIi+Nv==3jnyMFgkD*v~`6@04dZMo4{Aw2Qqg|5KhoBW8L#6gbo>zyV3 zmd}9InfB8;(&qUV4fmiZJxgEY)!7!~&J6lNkZJQhhkM=8>vSum8mY7{-i)hdIf2W> z=3cIB7E}MV5r1*=eGRoZi+TqQs{nA-^yw&=Y#A}o1^@Vcp{|Nae&2wdt^cdhzSiXA zBmv8Uc#DK*t=<$j1k1|0~=fU{!vBaYF z;X-fzTZTD4ZQu~vCBGQ3$qDA(v-%yHv_)V)F0dy+7dkU`QqKKsF}*DFczQaL#;?;> zk=usM#T=K|J(GF*pd_q3)j$tqie=c&=4Iy#5G^MP3ve3dL70kCkd%HC0pfwdPL(Gz zUyOj`*r+ghRp1^6H_G;y=7@B|A^!dp2W@0cRp5^$9fpJ_UqJhav*=i?j8~%vC+Gq~ zz)_sWOZ9jJEf2AaD+o-kd{nJ7c`=HtAbr(KNzd{(n zl1{P_fqwfWYG4}2iF0&}`R%+saZ`y}btH7{T96HanWT3fc{1YTBM>0$9$25Rg9GrY zn^9IJRXTt~%hK5&Wiq}3PVNqOAEwsFh)bzxu9PGixE#K7TdKm?b)t);i-#v+ul9qnu9ilhIPg+=EgrHP= z7^_=69`p6ivFjmvl~SfsJ{CPIUZUFz+1CGr9A_8y>Mfmk^wlWSr;mg6>I_d*=6F+T zcWjs9>fhRRJ)WyooLiQcf5&is+>)vxmGyyvzERr&y~GJq`bNkSVC)03bQqngKoyjK z52w%$Q1Wy;f+*$bSL^p@(Us@V&GI~_pMC0^f@ z@{VHe&>$ccn5`M;=k~@V?vi})sTY-6YHfQ(Lu3u~k4S;c1J!ejFdX7qX;fi9pusI{ zs46mhBr7PGk{l8$5)4i)8+oLqf{F`@+7Hry?2gWIHsvdLDYuKLK$`9u z{GmN?W2B?)6vFZ*kcbBm!sj=wIP0Cw%@ch8x5PgDUR=Bjg{C?ANGqgW#@9k|meJ4I zROk!C2&AmLqVv{PUx*!@C=(-RMtie9d%PF((b;aJ2BRAnzJ9(bBBW=L)dxqa4j_<* zHzN+ox)`;~Rx(cyJ+k(_oqAL+4fT|a*2vbC^7l65{f)74jj+{34-&O7A8fRG=(X_5 z{o_SxnZT&KsaDxzcvJ`x2)%i{OKfi?kdK+%^5#d5>0d&sC0AF z#|}eB^@F25$Am55R!HMNU+3CW#SEIs+3-M^foRa*54V_$GpJ7U68wQv22Ufs-+cln|eZR$g5Gj?MotBeIj6()jm1$lt!Jkz} zA3M=wxwVc*r2x=3bN2{5Zw@VJ4s(IJI;5J5%0rQb5Zn;&@0p?Byq)a{142-|t|Uqw zbx9+C0N$S-ICG*_#nx>8iaNqlbmpl3HfQU&K|uG?JoMhG7E_3b_;xn+wCev9op~VB z{~yOUQFB!0igIp9Gvx>!j$s(_b+pMXH)$j!mLpB(2vcN~J8AA~&MirZA*!v+k;K$O zax6r@{r=s5`{VQZyx*_a^Z9s?0b*j+FZh0Uo>6q1YxPJ4h~_hQ5I!M}=4C#VN6rPv zp3K3G@&3*;%e84Jgw==!^Z?Ybvq*WN=xFQZ?DlcmgZ$D4&7t(l_BgS>F7_Dx@YDc0a>U_^q%8<&qr?J_xEyV-rE534-59j$FQJj3t-9^D|+ zjBpnIL5qKc!RZ&{w$J}}`@SqDAmPcm48ogb-QJ%>rNTp!=RVNb9L^hf==*ZK$`E#C z-GF)42?FsE8eY@wy*XNKHOnJ;8RTJ+cb-^hVYL+1OVZ4l1?-ptfvbjw&8zjyDb8Lz zccvM}d3(e`piBte-hncMnKW+vhL=`4x`2L>xJ!oA1_(HoBX8v%aU9xg?^M?zZQtpK z1g5pLE}Gq!UB;mKH@=-qesfEztaWs%0Vn0L*VX`eaqrBx)F@rzUg>m zb|8EdSN(g^f-o8T%_o#ZT0!yK+w_%%E|SRPzLkAa@pxwIPgz*`_N{e$EJ3&4Eo=Yp zW`lD`eB9F>WmJ)LqmUrn9OG;d?JxCC+IQxm+H~m)sb?k5ewY9iDV}9>gz7d=K&m15 z5OC0728wI+pSO#*QM7R^P?6z!aXPZQGp)AkC2i*4{^`?zKl?jPRJglC>x1)WhDBAK zUiC7f&X^^t?sOz*r~8B`c=5h&}%UTxJ4uHV7Bk+zqrkjm=sYf63z!Z#_=kmGz9C5X{iN7Xy_-4u@k z?MLEfu}mkKi?Xn`Hz6B-L(3Dc=8&ATyxhq1vxn=Gg0vw|MY-;^T>oa`!DD$j6+Uh> zQK3X~&+9S0$}sPyz&p@E)cVW>(pm65J-=@%6-Y$03ZnUp9;<(WjqxA*X#I)fBnJWz z=o!eBI@{j&46!-ve`o^SE=5{YNE(c!t11a!mOxv8AG=_H4hcVl?R1dGL96FibqAzP zH@LGec|?wW2!e3dbgf~?2Qrq38h65P7zl)_4_OaQ+3t$Z`zfn#o@G^sm)Fs_lUth? zMj&6nx{ehG?4KDM*!FC|XDeZdj5)hkr{5#L#f_Bf*C!G>nCFi`j_8Z!s%D>Cy;$!0 z-b2qdTG&L0poYZJkIxn#HS)4wIcn-+m40>1LmJRNf>9G*uzDah z#mR(&Wd~%2(_W3_e!R|+o_i}nu(q)a&vcP5qW_*AC>JXh;Ya2Q0ZUb9XTUSN zq056cz391tGyD5@MkB8F`g49#$?`<^iw9Mwi}kCOdzPr$MN|e4!xJBeWx0GpH}p)i zKEJ;u^;6RPSwg6T1X_}(B^xt)iIhpKF-;}Pz;hH2STO0or?T^NbCqh!VyBNeJ)M0X z_NyV%7b^;$o9Jqo;j&9DTV@I=9Ra*5tX5+KKD$uL{^#a~N_EoRmZR8~l|APu4JlPA zzI12wu_Ng{eihVXm!L_lTUrMpSLbe?ga>#AP@^qs=(t)&WSFJ3>9xIfHk>!K`s}1C zFSmE}!SYf$pr|kjHr7F`IQ3a>`HL=hd($-}K;@y4P$v1BWpzzT{lW^a5`@f4F?{}G zX7aU?xrOS%$_4=efrLK|x!#nld7FB;_%hes!yL`@6fl1y08FHxW&Snt0{o>J<4byF z762Z~LcO*~f||~Df&_;6w(=!yjJs=zEbdcOgne!K%zGZ5>4FcpC0)bf!-jhx!t-#s zX+uFyj7CFK7BE%t6=9LW3>|o7PcpYZfWj3q#7GjAH*+t zp14c+Q&8OB3oq+rPl0VkQFk@z$o8*5H~`>ea=$eD_uVDhG$rJiU(BCivz@6+c)_=j zq9_1*g=fdjO1cf>r>q(8v;QTjy}_`GbM3V{nVjnSj>02+Q?%>`czbHfi;#vX9B(xGLxvM`pisZEjnf zgKI?dKTFeIb3gV_G{Pb^hm@{)z-`iyc|J3S*V}XFeo&S9^~1MT7po;C>?H9*Gd<-w zmf<<)plQL?ghxhu=T^#}5HB9&sY81sbbFG3$O32jDTRBI+=P^R;SHMb&r8J_IG%=i+?Ng=()v$vOYrWP_6BwSn zli}UT?B6*vuPJ6mVwWNUV=G-0lL$`)WZs%C5a&J|H|$-6`8SC;j3&}e;gGcyVG9Ca zrLpZn>71gF4a{~mrfr7Y!!^yBU%IuM=~hJ?)Vr6)^DRddm7BfV+@?l%(`ZG@u6+Fk z=5S?%^MZiV6kRU8B6TE+#w(Fa<;?JgaXv`p=4X@U3$Jp;_{7Z_TgE=B6_Yr*`Qd4D zP1^yXThak8?XZ-)dZ*M_r$rF%fb|)#`G%nxhQM)%9Bc@+N z=JP*IhS^^OQbx{`s~)kmeZTS;1Yvw%Q~pc;7Ak+5-shNz_NPG8UWjg|Muc)JcTSFG z>P`YmM8Sk;9x7vM*QA#xeV-U`oCr&q~Cpt1pidB!_k9 zvhOPKd58J3T-P1tB*VLDQ*tFYFREjOuP#jh6iX&*auANK&oKU~$xDhonszpW&tG7Y zUsZ&xo<6LG4GiH!tz4{Dgq5~y9W&3>I8jdgb$RXqIfXwF1++5n`7*o{qc*%p`+eSS z<%eZ60dFs#`<({7RPjR3yL0x6lBg-5YLMMYsw&C61?Z2`kfTPsaJGU`Q~mafW4r>^ z6lDuK^#usT#jkAcMUBP?o;}~W?1nddm^kGV+4`$RmVG0kW#boO)-5zY z_h#_2^v<@sjY0pP-94S1wO9!(@()ozyPA47#e7D#W@quE&&E zJoM+DeS-8>*1XR&J3Le$VeeGcS)ZCQ1Toz+CXCyB4hkJ3gj@a3$WlVz6saZ64M-DORtgJ$`j_j69=}MbWlSD8)fjwqEj4*dJJ=2*kFBL2 zr?Ol>>}X{cb6=7bp_az6?}ID(7gJ3E`8Z5YRms=5xXgHRnhOL7o@GWoIi%!@^!GD$ zN^QV4H>mDNR4KwldFs?mUf@vE|=T4Sx9iA!rZf`?> z`hC44v)-3<*pOL3z}R&at3k^g(eHJ51rF<>N^d@W&-bybfZ(}|oGdHDh-F=ibvgVq zY`%=nT*A)mzPe&EE+mbB(PZ}}F?5@^3Xmd%NnHadY&f?iswhH9i`ZVM_dwI%WNxa# z*9WL&(zAVGmg(l4ed1~E)IhmJs7UGe<<~3{UuRhP4%&G z_jOehcoORB2L@nm-+e#}%_0y@+vi^p4T!iAjMwku2$|`@LC_^yGZ|E8nU>q~D)Out!SHuXoPF&1A7`fpO+UxnZ$ofJ=mt+57QG!AlhmMd?Cj~ca7km_u{rUCO zrQEFfD37p>obvPr0B(muj&@K!a&F);J_@z)P#u%2Qp?)B?ouqX;jd1Q<~pH%z7;YR%|3Zf z$-JRWR}!FDN%(yOU?FnbQ7_eMqhf`m)+-p^1$}pZXE#DJ;NdNgYW<0L#mnvfi&~ku zY5QcWM1ZuN;i-vizb>6$lj4F|;VZg#V}jf$qT>|cSDjOqb-%yWIWG+-nxM&7Ki{!8 zF6KB%Mm$CaAJ2A-tiRME3ArM9b6X1I_k^d)0CS!wC{`#|OH%{e4}r}5HhmM>zBws$ z#xA=Eq>Nm2%S|1mK01{Sd2qKV|Eb>0dxcvw?ENa(u_Q57KS>GFh~E9c$d74>Wx>s- zbl%Qvb&sHwA>b0&YlX+j;^F8ZPVT{9=un@G&_#zBd9RRoF(pd1kP;LudgV6KTtMf+ z%;FTa4ZqWE?5idLRg7-xFt1Q#yVuG!E^l5iD!x<*3Q-!_elh6##Qck=jZ7-&KVBSK z11*pWvo4Hv6(IcRT%Aer2PA5C96H9YBwTsSR0&~-AtjYTu8#2|2z)!x=P6{mFrvf{ovkmdccP(3oc7MD+wB(4@<-A#MSGLo;ldgRmQho-!Ll>MX1CcR|;TE;#%^Y(Mt;%?))-> zAj^wJTuZcIV&YyZ?T^#=@p>St3CQLS;2Md1Ii&gDae&Xb?)>2h11j<|=tctP=SSdgCJ>ehoUxfT2oe^rX zE^vZz*^qYwKp<32m1c>JG6EO8OIN2`N`JYgi-{dbG|z<*Ho)_gN@u}A!ddRrB0605 zWHVd`b7Y+{d3GSc#-G1xjzd3 zxc|~zhJQ-?uDto@JEB-7)g`v8HAII=gM@2MFkP`{oSeZXD`h;us)B%_Js=r# zjK-J+)!aoexy;)5-;UzCRxC2W9|O4>GW~kw!FLCTAQOLnEevBub%@HPYS5h>zA5%B zboF9S2w^_lmdoGdIcg!*1WjQi#@uK%9Rwnp<3f0KGLmlwr+_`)l$PT;%TD;mO?b-Y z1TRZC1yme5rH|!qp5M`GBlb*faX7*NY#mGjMv76D+Vk!sK(lk$Z z#+iblQj$s=^MC(#d$AiUtsc_9miZ*|H$TYwwEaRi;QwVB17VpL_JvpHcK3K=s=*7! z3RkZ(gsLUJ+o?#nMD89gg4;}}Kd^H6G$Od(IIA^W{u^3FupX@Adz(ruDtd(wcxPEX z6jNWn8sUPLR1uxieXQA5?$F=#R3If&taC;I`Db&F%FeVw#=6?p>Lc#tE6r7siIVaj z=Ya~rrxA4_!6($(w+x~r(0mfSX<*D9C1%tt5>6HkUyaM~20!p$b#qCtBHzjv6&I6G z-WmTdy#kif(%Kmre1Yp>K6I9Oe4qh4mUp@9(+mlP>PdWq{1RI8qzF*;ci_P^x}Ni6 zK0^~CQk{Z*$#s;0e8WJR*we0FtP)3p*xlCcqJi+dfyq(Rxj7l?(dLms?Y_LH^Pgy3 z_ho6GB{(M5rZSH>!P~Y$@O=S#I|KqD@XXp-{ErBae_LVyx>s!l{yfQbtG^jwJ9s|u zaUP*B5ihN5W6ZR>t=(9qZQR_Voz5ss^9zua`q&x3kt>xiI?gwCp7mL9ZEyX2=E=ua zxijX`QqZ))`9=Da%+DC=u?m4+OjFZq35eCa{=_UP;rxQL z7WA}^W8(f&so!QMcA&D#Vk{jY8FI0uN`Q(4;u-mLA^jQ)-@ae*G!21|8aRzg6p8@T zc-&_A19PmcsV40MZ(Xmv-vX34e1b3xiHK*qp~~%d{PP+_Ax`E2cQsACAq@7O z%(6%oy@#*DIC8$OX#^Jre#?`k`EFqU!-LQuc@<}JXPg()pr7!|*yT+6{sp!>X8Vsy z^mWy(#LCI8{k>f;_F6I~#CGhegoF1%Epcxg z-i(5P-+7;f$bntzTj0BWx2MEtSIJIXdW9T zm?%J(0CQX2*aUYC0apfDL3RsS*8 zvS+y~QU2bq?&yb%*z^`^P3sX5GWi$UUrn>Ssj>YHm&+`OHg9b%B*oY(@ipA=<5MPn z6VI-_fD=a4_yu#>rbwyyN+#}1@U+4cW1ggsVF>J*LLT?l;!v4OKZ-EG)XAv&XOl?( zy7T>lg2QJ3y`bg!S5hWY+F>l)H&En+v)dlaX_3Asf2*_N<2W0OSFb#oE+A5*F3ZNI*JWd33&J~q7bd`?Cs zGp-qR`E$V2sO4wWlt7bHm-$5_o*hD37?mh{tG;P+olC0nMJ&YG9fX`-e^hCo|D9xo zBHf5T%3r{Q!@;^NzCMrX3=r1x{T6|4!Ng{Q=t7COWv`c>5!1bgK}>hZ^MK1G`}fHX%YputhqBP%+3ANV17pYrzChfDQkd< zU%-0bzis1x38iY)`>&1nclP!>n0r~P2~vIRf(*Qk&C!SBZYLuC^OGNgn7(0%ebhbE zGjKyls$-Vbzyn>))NW`6i`|c1g@$ zNiLzQxn|`CXav-h@dHExJb2;CO$-5?D&oCn;Me+KY`0C5r<9{VNpm0yt?CM=3ASvu zsPTb2pL>5a`&Y{!;EFS;B=F4@;beVBx>EVH#4Vgc411He_h>uPp{h?Cug#o*k-+9% zKRlJJ7a5(4_ge!`P!)Xxld|H`A8%bQ^MQP`jQQ9Svl25hQ+P7HhQpWlQ|q%Btj;0FdDZYCpr04d+_{E2nb>VRvt(_PxX4+Q@DRk_z$ zxBfSl9-Z~IQ)t?T1k_T}zPUkx=Oep*a%Be^dl^p#wr_gb;{`t8MYW-D?e*J-XK&TV zY}JRv<+tljzZbiledearQ)6jBBhYPibKYaKQbT(_LrpfA;=5&6fsDp zyJ5cG%6tgc3DM+O&RMp`B-8-jt|QPuPWF8kGayG2$x`(=> z>r10m#(p1REZkMv=&rVns>Th-1M3{)P(bv zUlkA%bBXqf*7nYVh|O$5ub$2^rP6?c!p(7@-^eKudW+8cW`)DT+WWlTKg}IaI?n^2 zU7zI<>kwM0GA@w?{(rQ-MQFfF826XhotfoV?rKg)$PH+&w+1qcFrca_A-GVn;*h)k zLp5*40h=TnbS@Q2X9Z=gy<$Qzm{T@jm9H@g1i#TY8IX}XR8KFL+bp9#NPh)&6`-8;5ktXn_d)ghaBx3bSvt9-oZDS9z- zDqNn)E5;*$;;u$#|5n9bwY34+FRkoJ7juuM-}*ir`f+j2;k>It`R;$hV>kRyRn=%8 z#a_?L$OiNJ)`WI0L)AXlVQ>m9c(qo5j_1#k)~0M^CcDTu%=6ZAhqP0QJZTJxuPFiE z6w5HFS9HoP&cIggO2(b(^*av;qF*iNJlapaxhZLn1PL0%U`sd7jrxv!9#~T>Dj_R^ z1=B0-{)0TbSyH)hBxw`CFVwX~{}kZz8E2&lQ5Z(o`&|;ZobN3 zX=voPOS5;U#~vmE6k(8?E%76lT!ayXvK(iuCsqXBqA8h5=&((NF5jUR%|CsfWpq8X zN=+^OxLGx*=3;NDJ&cEX>8%sSQ{@SA5u~t-1D(PvhNA!+bE7?;nF!yFl<600if9LJcWNq;{59&%$!}x35t+a~X>S?Z4^XH`14zwaQo1a5 ztZ=Q-UJSiGqwQLdPIf^jH#Pm5JV9&;mjp;$1@msgx7575;`c4Qze67@m8M;vwKSIR z-8(C;0#fwp5sEf7A8L(m59W}M%Q&}>bW$oZ2s1}s{QWtSaQoZM+T`5H$XR`2!wn}T z2tdlu74$H3MZLo6+1^x$TH7syZFOZoKf3`K0wFfL(pr2xG}(%?O+aZu9b1!Q{up`A z2IwnqGSY>>ZRP=Z*17v``JZ_w_uZMQv*^|HM1U}0f&+YHXqTysM&@%hCXh0M3Zo_g zhL?o@Cj3+Qcjdi2alI((?wNA<9yq4ryrN>neKVD2h$-GOi9+EXw|(t<`pN?6SnzTX zt*s;4AwYw^+bP(=b#i9vn%{mhP^2#gxmExLRzt2L3ubLQ6<0Ntf7H7C>E4pgfHMt` zjTuxS@(H=@U?j%9TzYTO&SF0hharcm2OKRt)Xp%j5^=t zXXVSSu_3mTlfnQAf( zC4u=d;{FS1U;CGCEWbt0pVOYWBl#h2nK>?u$X)p^xrR{pV!K$a@2AqOrR$GDc?Ucv z6(q3t!0yY{XO_qMs1F4|q7M@hh3*mZ<(BA5-2JB8_}a}2FXch>C@&t+sxfX^g`O8q zu~6p5z0l*!ux?Jg$y-y^w8pOXcJm;f^{wC=Jl%amHjjb89ftv7cpO?344LA7=qLMn zJl2bge+$#_vCQi#W^bck>9j2=^82_(=q^}qh)6wYi^3>>FOP|JsnPVGR4+)|qOF_| zEgJu?eT?ck@Bba#<*Cr@tbeS3U8|*W{;Mgl z)>NtsrguUeBn|3Za#z>xH8XE*!>YHnsivZovnC&+8m_LYOc09Q-TgY7EckAovhvH| zr72?4bJFe@t)gO|23lY$KW7dxnO4ZqA{jO(GCgJ%8`<{MK0mSN%^+U#)A4DJ{NC+E zrT_Q-uJnt~y*c^Hst@pmrC=Ir!lwn4;*-4n6A+LT$&U(tlsA~f@2z4@BFWgj)qPL; ztO2PJ?X>3jPP4$hkDIPHuWufoTpb#>s4$AR5Xg~bHpOM7kzq@U?sb}R1)pw%hi|zh)yUZ_3Q$Nrm!z2 z^?m&MDm`eDF*6@^_2!>RO4ko9nnv8rs@yX=`M7r?QgE=vSNO&%d(sWhD4O3sf*iz> zuY()ox_QA^@ix6hTEvO!jQP#&y|$K87$w?CyOB_4-)_8G*UN`!n{Em5Rd$Imh1|rb zYC*5!ErGym?{^CZ6B7|tWRXXtfadRmd}l%?x$fo|CpEh1BX|-su2rifiqr0F4$gUfkDP$EiZ`!5vCdHYDmff4b?MhsG+ z`#=Wz`|Y*S?UY98@9cBS!1*ft6`y@#wx2wkX=uD&L{%tP;8}lnm!M!g5)D(-SsB~g z%Q_RmT_|G2CRn#DkP-#AD>B3oZt0qX$M^ows~=93h~PzZ>IacadxA~9?=o!`Pmp+L z%?UASaFsS%#(B#hAvsR0X zXUpJZaCin!&M=6msBxd4$z6g0Vhv>y;pJ(RPO%{Zu-{1!*2Q>H#5D3^OxH$AhQw%oNEb@{%7-Ar&-6gHra?kOsr2A|Si9{&XJlcJ6K&ZOgq4&Zy< zm*K(x-v8T4fCPJQPY%QwDLUR({4UB~BWEm2t-uazi#5vk>W@<%0Bi!xsk1XA=BcK^aCy?v2>vhJ;HQD080&NKZ^CQq27!2Jto47)gx zKaBiLSDd^3;QUFbv25GUfy*|^5)1$j*OY5C>3-SD{96%M>tGWeSE0jsvd-by^c?ze z-Xw{f19xw4M|YJq3~p%66P=SeG919MZSTNx?DRI4EPa>&4{PsD;dw~c< zZe*(*xDs3CsEj{Du!g`NS)OuqNznEnpd;$m+EL(4-gDnAOxAZ{l{%u;*FD#FIVS+X zxt%Gt7U5kj;l{2-1>sLDb*rE!$Zj8YbM;M47C;+Vw_6?;Ievr1YB>d#aLk9kEtdGX zY4L6SW3hr)cD6J*|Fhz9rwhvM5CTA0uKBYKhXj$qR;CmtXH$KMyC2c6HAWx2bGBG9 zL6O@OcF5G!K3I^1ktDg4xhDsxL(NnFoJzW3=o^;%3qlyX_uq+o>1ATTmR3{6Z!uml zpCXV6FGRz*4#Tb7x^;JJm%Yl+74u|!7tOUv@eG`ei(bh=fdG zbwbcuVr_bCc2Ic(K5C|oGSfAxs~%E6?K%buwMiz*d}n=SawKJP6X`Bi$dGCFIiXWi z7OUKDlCHKnUIu#+s(KZ#K0MVd=8VJ`Xuk%NWMCm~vSuT_(8e$4x>5pxKqr3=cBUFe zXwl!Jb7U=5kUL|WyT*U#6I^jRHy&Xm+B{w868X8A$z=~j6XO3lqHgN=zzD zG_UK5wvG-E)dA*RK5#kqC!PvoQ@RAM-XD&TJP5~EUJDg9?`1~$eq4*vw7Q?J2cM$V zagGlF1y)?LVbq%(Y0wSoYB=SWUtg;&29%1~-~9J?_nd`a5GUal$OeZ!EjR7#cZ3B#3p-iKI3YgP3e`(h%kc;ErT**?y(W96?P)fJJ{2FA z(3N7zy9aW+`?r1Y)UC~!X3F?(lLukQmZU+SF8OkVCJo(1g_22)6VRV$ARdEerqy zNcF-naS1c!Sw@|oXYyqFQPh`^WruU%i2GLJg{pARqyN@pLRAsEA6xZ|jNyui#%mY} z^z*I#zx<`54&h(}d+k9XeN!jN+dg%0l0PSpAr250uCDgvs*tmc*=tHaTfuz9RxW2< z6+eGYdV#%e(aY3=izPC5?(j&ZvHx0T#)o?v6SJw^+!arkQ-e|rl=wqJcMc?7h$y2; zp2{$H`9rI()6ky2p7FkS$P6bw*ReITwHeo+Y0Uc2iod-wZH{nGR_}?kG!j1D$A|r^ z`HsP(oSo%zcAnJh7#NnN)+`T%PkK(^wJ9gXOhxr;2$dt+uceyH9G(4CpnA)F-OS&E z&$aF+dz$S^B>h`>!k zmwPbZZYZY3q?dNf2~6y2tRi=^pkC+t zWdc-8?QMS2pe~Z_L0#POa#=pYRgJ(o)f_x8&vGG?Gh=EnO2e}MQwy@J7Xp})$;#iT z8G>acZCYGqL}F~l1%5y8zQ2bmiQiYs(ompa@E^?(;K+3XA6RRowWon~&VybK$!g1D{ATzq?_Hp6&GZd|E+*w~d79z;vVTpviQx;M zVJfk-alXp3P+%28Lig#kE?aXV)~HDbwsV=FDX@^GQNn!k+u(e3!R*;z>afZi9Tu ztN@OX)5RL@U3zGtL!!1JXkiYk@6R&ngZULL-ja3au)_C&F1hd*X ztsb$2s2qy|{tdooq6)FJ;B|j4ipMI1(&BWn07YBuX~($%k@~*GL9qZCD+2L9xWii{ zk`F-7p&6F7?sIqBn$BYIX_0xE&J@Gad}QH{yVcSnzSnW(v@-nfPYe9VHd`I+lcF)p z6AHOn#N`KYTfeYbiUp_ddI(Wd$!c{yPNnCZD?A#dy0MJ1!6$wJV>J64jTM5$P+RbhPHCDP70Mt8Mns^BnG@VnqTf0CG+s{q2pQP2@7iU3@j-_X{P!b7B# zOKMs&W?W;EZqyvMIhu6i%rOh^Ng_`aC1nEsVfH7qEMhc15_W4)$Smiip%)rAxik%x z$B;xlxg+?o)W-yPQH_o+HpH&^x-~CxQnYK}N$BSO9#kdUY{}}2UTkZPHpM^QI+*P( zwe8oGnop0OU!3dxN(0I?B6Xo%Rc15X>kv=UqC-r^J;K(J0A-)qORR{I*YvAII06v; z+B7|pJU)rNgnl8anEKYh?fvh+Q~K)SIX8X#c=h0#Nmg+=-H;vL9UHP*+@`7O5NalI zh{v!m>LPTN;9P#zuMZc;d0n__UUD`d02E~X()h|{Z^bMWo_ZzZX+wcXumRgO#Kik@ zGXEFT-qs8NE6DEbwvvPwH%|5`k?mTm8xUW%R0B#5^|T+u<(6g(0IUR?GshEV9$TxU z+8*E`%drXQkJ{>cOl~yrV{`9?`_uid0skq!!8r6@%_Afddh#7S`V0(3#GR`)^|;rj z(qNQu;|6Vrc6xGY8cMqc(EDSzD(I0cP=ODw#@XshR@)lBWyvv$UFqdKa`bG|3w%v$9`2U^U`h zcuR%|q5ntLg5G6h{a9cIRH+mIt@u7!NbDGPW#HDS)l1u*ZV^u{k3ob98)=bV)GX8S z`yrv;Lx-Ih$d0w5P#lf+lIaO?&x2#%n5$tvw-<(u{eUQ!rE#f(+oVUvKeBGY0{p+7 zy~uLNcIkoRJV76`B5LJGRDILT8$K^GVmt=8>Jaj^)sJq+7<`{_+LtcJB^cpvWHhTW zsm)DR2l9pv3sP$H3x^^O#q@c8cOfbEA*EZVvjrcbWXLPKuTg9c9j<&MI=~}D8w}9C z%5lp?YK_ul$I`4XV_&rvboHkHm$CWl%E10$&s)n6Q7+E~ z%s@iRx!@I6fMli~>McVIE8}8cyBT8}@S&2sXQG^Y8pfiC#?2@=0juWkXpOTY9?aip zJ9Ph#{a^vbIiv$BVRSo6%uSe7@S~|Pt@y&+`28z=GZ0tOT`Q|x|CQv;%~+D9lfkoJ zl?C97eA>X67Cr8Cl;(W*Nm!Ecw)gMBNFMgd4|C(gD2}DG&s+Lm-@B8h!b>u&CrJA9 z(^iiHit4Xau2R>Dhk$37rP2xivTHGoY01n<{+_p{1$wqIXZ)Qlf?fy#O&y-r8EPmi(nx&VNYT;5voEok6*}^`|BQy{~~KFT_rXfre-F8?+6W8z;!lZcchD=5wLW zcvRl43}O*J5gxHxu8U~6A;QmRN@L@BpJr|WrWCH6L>Q}&ZJ94nka#oH!DGw${p`rz zpK4VU2RLi~m9e;qG5>SUHI{VVCrK38e-rJvvRFvEYj6I_u$mlHSG}p%@{*k_IB`LT zMO%w05d$@baa(*vAR<}D6_*f4Lng$3&}IkD(akWziK*x}6wC9(n)3?UWA{9UKoX4| z1QG!N)4}Ne{T}+}GJAG-AV`aOGw>so%cBu163;$ZDAxV=54ks-r(xCPpYrxHW`t5K zPHHnha--y$D8G<$+TVZ0KK}h{)Dbks^LoBlnVekK{9*tZ5;C&Q;i`|+hv;Fas>HN} zljZN1H#7{oUn;D_QAc$Yr@DEwYGNz)RERijac`*(TC;iA-1{IhPc>QXn>@sQ`<;PY zYWBDWX)b7BN?0)2wL;)0_=LqJkbbJs$FRiH@X&E|`OT4C4wth#rqa8@ zU?CvAvAyOir(h&OnF&`W&39K@iNlX%oVQcvKdmUZCM*ZF) z=D8F`tNBjc$kuIc73`Yq0GrlO@*;kvI`syJ#`sr!l2HhmWAD8nuWwGWO46k7sBjem zidW^31Jc>FuuDn(wb5_!`0jeQ+=PR;8shfeo+at!{!V)KV&~j>F@G$pHma`xcMm7l ztauL~Azb*p_Gmb#{P{(6$4SGAGl}knmao;)@q7~TX~Od0)PW#wCy0pgd8{|8)(b<_ zUx$dkppy1L&!-kO4 zqs$}#&qnknumFZ!^^r=$?+39WQs3K0D;P% zBO4k*ZDP|wcfOT?CQm%fmjMU59<1EmJ`VN|r5p6$era6hC+{pR{UDOH9~&1}Hzo}= z045>vm=g)KGd-pVz>O>(OcLG;wFrr@RgLW=d$08zgu3I`K7S|y1mMM+a6R*bv>ChT!`S8_d82c{mRZD_t1;t{So5X z{jjlK%Y4yOrA04W<7QD41?|tfjNvb^Mx)SaXP&uxJ=C^*g2Bs=N{BteNg5^$4k}L- zo%g_0ruf5v3FH2CNmg{F%aWjTi-!vHq!PDYYG*US2ETX}jlO z*Om+Oc|(iMr>LWja;0J*Xt{90%QGKSaQ&ge+5O==Gj@OP0-bdsd1G}0PtubOWex6r zm!7AEV!ZvmitGopwCC4Jll-EvQpmIKPdH1{Q>1M4a7I4epxl#3D~(PP&pun(D>n+C zJ8{=u8D8*Z>rp=pJKNRsctD&lPR@(DX@QXHwa&UQI?pRWw-I8UIS+{VK5(y1S020k z`Ga2yM4e_mkBAtg!UM>YKE$otbIGdSvc>{NMNg`uIdEHwZ%hFZil^ApXeU z?#+YK6a0JnRr+;#QCNOF`1o+G$l2&4l9|_hvP7UO9xU z7`M1Kao+5adfXWW%Y<_hGPxww+511`z+0MKz+pr4cRDFs{;bN)aeUA~=<*+}$LVLk z(*J7l>wK;ow5>8abcnAxAKeeV*g-h!Qp5=2cVAD1Z6J>*rFh>+DAN%Lf)A!_I-kA zZ8OXrz~0I{00R0KkDM3&mz>oYwX zR(qxkvk}#8RLnisnXKz($iPwQ~7-Aw+*7}w_%uSaQWw(2++FT4z%v%xX{bR zXA|m4R!ujphulr4F=-{r$JB6YK50YFONI%G*R}OO5ruf5`<%$hbaNmPGV?t8Dq6#V~7_ zlpne?-4^6)p>sejDb||G@<`Wn25O_)B~|!tVFmvDVUyILLU-+f#qY-D2T1mf*zW$K z{z(9Y@A@bsvhHKnk;H`;wf;_buQ;?Dql}dAC@M)gtUVf=8^>YWEJH;XCnUgl6`fwF zdOFgaET#m#D_FdD{8W91B44t)pr(~FbX03a7sKj2?I=f`WhUcmu0`leBKS1Vp)vUK$JH^OyFG4(yXwV@yqeK{GmQW3D;* zlPEIW8n~($zv5@aw(;G#V!SDh)>DMC?%#E{4h}=E&Iv_SPc1!6b8`lg2=FYNMwcl6Pa*~o?PgA zxZ7hn62*#e_bx~r9#;jJ@MLsBCW^%peZdN}h0zCRSneUqykGH=(^uY)7k=7G zyec+mYOQ#myq7If<{Y(ZRc2q}!3`|`5)+w~B-EU?G2>Uahh!OUi7OQ2l1bSOdg+*C z8v;UEgv2rv<=m|SR@CqB6fmDn$_sMK-^J`eS+c^>Bco|38Jgkf{BRuC=7pk0{u$zhXGCBLEP3lhFxeAdqUNg z)#`Mevxls)xuVo7g^>Y@uDt3Icyn)@nEj-ADpgbr{#9w)ew@Q)a=FDBKL^yYP{r!b zU8eNxYS{7GaJyNOXR=yFS%9aD3nGcXg|U9x7DXrD<`P|)ku(&OOXa$TC5ke+=PofNxz4?* zx#UujA@`)Y=6;(bA;yZ@5acPN~gcV=NW1*IciUrr?mf(<=7%yOKwsK0>?$h08AzS~{4-_WFh&S!H)kQ{n3 zptaEp2|I=rKZ)v3sayrc*3>+sJ?eQcbn{0h)Y5y)i9!IV|cK zkNNzvlg}-GXS;R%3$--UtMzt+klBTj;O(82Q>rH`iynO6i^GE|*HnyJSHKDi>iOwg(nyPliD_mP2&mm5UOb7eTaG;Xy6Kiq&7ZkQ z!ZC@1Hl{a6={}~3cT7wKQM9JYa@r~gC-pM^4iiF*$HTKSIKS&^`~Amx+#p$wlSoEg z9tEz9k$xqofQhQ9MVngRS0>){izvOJfbA;&$~i~|QPopztR%fl>b5USK;^odB6iXsUk82+n!Vos)6{D;|VcPZCG)C%9tU`K85 ztPK)@Q1x*AOI%j2*IuxJ+@mdz%-$-#Qbr5^Cb_Ev2;>8)XQToD6Ki)PZ3ZAv(diHO zg1Bu@B%#(a!UOW7)l80VYII)Ew9J15*5q%;225^vcRLAo ze|;rl)B^vQ6Y7as(bQ-rqHhHGiL1aA#ja)<(Uu`S|Mx)rIom7jQOFG5Pqgo{7(e}X z&%%2&(#8|;ZZzs?PL=w4Ys)NY!ZW=VKRYTEryR#Wi|L%9jZhWDVbBW+p<({#VT)gv z@HhM-J_Y8d8C;FKr>Tcd%C?sVqcn%MbenD&W4q6=17!oMi=kmvD)Q}jaRS(&8cIns zeNvNfreb(yt<@j}s``UvqaSm{h@KW)Q)!baV4+iv_;DHgUd~%N!36EsbJdPKO?r1V z1A($@LNBkKjhdJvTEuGUz`J9LjmXby*D=T&#ny4q&OPUcTNyG6-FCE%X3H? zeq4u4K(SCcKB@XL=dS#KIj+`gPMV)%Ww#sdZ2XOQScv*W8hIrBLN=b=M&iEW{0Re6 zq?N4^avA3!`PO`m2ts(6$+57*&tm zn_w3*j>J+GetmqD*9Xps+38@${QILNN4g7vzIq{klbrG4dxy zG78dj{F&@h)fzBqqG5?`oOhEpg0)NBpM6DWp5Z}+-U-`gWG{Qes|ox&;`P=7HA4`! zA3fFl6Dh$wDob;go_EMp-d|zWGPR;$fx2QP@WejT*nIx>y2s+P+JwKpgjdU)p#6*V zmrrXNNro^aTA}WxDd5K|uG(*LCcy?S?;=m3%W*V#zDM7#AlTWaORh8JT(0@-0h zCm76YF4CYAqat`Ji=I|;T!Y}uwe^nM>p(EU14cuz=dDwfxNm1_%tHU0&HR94XzyI# z;qIP<0`iK+rZ0OHt)nB-X-5mC&kVdhN{w2&zWaATbtVoc)l0T2B;v-}UbOD681Bf( zy2W)f{fNA^k5F)7vf!D4!~8|Zmg53gjjukJz)#AS;wbn+x^qmKa-+wXL#F(QkXx=? z$&|NED|?>ovWFN*>0_o zKNKJ&0gNyJ#OM^~V?|v&?9HF>XVmb9leiQDaPeKrVy#E%5YimDn`>*=jK*Ei`j~vF zLvXR9_P%bUGw1D>ruStqw1j+DU-1uU7P|lFek3U~EgM`-p^6(v+`RfE=PCT+)$ygI zHh&cB{{Tr&9etR6N-~v#?ibF?vP*9PUh)3Ko(EzA6j-LSXE@6C>CW~}Q0FCrbC5`V zf|t=Z+W_mLD)>l_NMZ4x1%dMXH?^bpbz=%Lz#1NG|ojaA>R;`s_(#X{3IPCu0#MGnuA;x>-Qzi%dzC-~UByI0u z4A!MUgbKELqIdE6QOjylT@tCqC##^(Ex^30%lhwj4X&^G?-oDprvOJ<&hEw6yz|dy45m64#f_ z+H_)XM7VvvBW{hV?B3coa|qG_)WuY@=cJa~7guL$URN{e{so-hT`wscE3MJ=;l_hk zbGO{R<(;>reEXz>hEl@zhBSqoJ22xNfPR=T_*VUpmX2S^c|w@)Q*My_EW3uyi2?_< z!c{aU@(ra^kMECLm>rL}ujxk?e{6<{HA&HS80D^jquryev$pj>H>Y$E6;1uP{^j(t zY}og{@M)k+O8HRME;K?Xof06CIg%hk0t*uIbQ)-ZPDYi0-qZa`kH`? z6?>8}W)hl{+i1}CXVR7$JLYNgPTY<-uKA2S8V93h*hSsaIfYgl@OgbPQIW(`(nm|n z&?ZG;HqRld57Wy7RbyncD<$TMOYvdZ6aB%w;a3_80)8f^35Cw-l`cm@udGEI7jQOMGC->xD zRz2`BjIbfw%iKP=7Xhz0K19GG1P$dOk`&+q`HH_snA4HIQ3RL5Mj#aG^-P^9Xw>aA z0LG&5C$X%ty8BP`b&f{e%|-hJkGoO_nA~lp@BMw=@d@dYv}YNZ{fVqenee-7@ev>N zSAG%!YWRiopy=I=(LN*8%*g@E)B1brf8g;e!Ko|Toc6S6=Yz>N4}$Z((KtLoLZ)g^ zJnH=&$IS1=LzC-ya|>9Y0kR&Lf>ve(PmPlzT2Co{>pMs|kV?-Vog6B8R@~7c}>({bo zCPqZlDYV3Ok6*30G`dgmc?87!-P;31qT;6&#a~)rX(YNX-9`pnF`!DdKScP1>AQQy z`g2me`M*;D$4=+iG~YA&4)iKXE~@1Vy^5bTp%<0++W%D9i`$UPux@>v?=Zn0{Z+DN zj$l901a)S6VqK+_QY=7By@-kd;$EZKt;ne0qr011F{{%^wxzkF_Jn zO`%r%n31iUXxd(=R)w;8lC_G9{TvWWE&={_tcwO0>Ng z7Bi!x?{9{fX+LUKs+mkYuPBLfPkxrw1F_4NUH}Pf;2RLs3K|U~E}U*<@4zMv(@+u; zsjFMet$*8sGAq+D+iTaJGrcNTUGKcjuk(QG@GXsE{WYkW!uH=gUtlER;;m-J2H!AJ z?-~1CpW5etHW@nj$mVQ`lmu2%0&%LjB-BWhNmC77j2dbWJ_++47BDqGZPCfMS(KS| zlRPmHOG%7vS>KF!mewawk-xM%%`)>F*PCqeeahA7k%_mLNTo#QnEM7GdjF6ND3L26! z$#~?VT0Brd(w1VX_9yvQ)ikE|4nnBlLR2;d_qMJJ4PNl2J-#(DU(($Fpu@>fIRFb^ zCo&h}L5bnj?riGW}L^L9Vak;E8rY;oy&`7Q<`esogXi?@;!%ya>3mTmQ+A zHg?UNMSNd}8QMz5Md(70a~gM3bMrJFz*kqXAw*a@sKNJ+kM2&m98~xX}_`Xt=G}Q09NL4b~n>owb7*Gb?u3ypR z1&hy(#>bY|u-P*+F>)S??g{dGBU1Ni{IziUFl+qv&UVf80pHDyt(oz(zO~Nz=sM!~ zL_RlT%}j1FC5>k{VdLX0%Dc+QB<#)!X}T z$~dKZYlmaFOU)n=J#S+7t}V|wUY*G`$xl9A@proE88yim$8I`*x5_c%UJq?qRVj(0 zWSMJ|d9AdMyC!MBm6LCf8p3>NWO+|fulE5FXMEl)P(p4r*wn4wGM`~g zMlhEdzZSE#6)+6x3na7nRfuYN1SQ;n$K@=n%W_h{P=vzv^Y9+21Ao@bZP9YVGM3V(jSV;fPk#uGM9PsUDeLqMu z6E4?JiB;?hG%4B|(B4p@^Fhl%4|nsdOM;#KlELttZ-zm2N_bSdTgoWFMjJJ0;}K^E zgyYF0PYl_d)*#qXvBRvlaCmYSZ$&SX3f17IhSND*_8?y@v>G{7ebQt~KW{$Y7@FWiu65#|{twWQ_9t@5>fe*6w0%Q())I4k>&s@8m3i)h(M%h?}4B@puJwc=U_X6D25?j-}C ziX-uq_MZn7lvGm~TYxynvj`Y$B2#{hoKeLeBObg0Q0T~OS6Sd}KaJ4^eNChahTL}E zHbAV)93iQsbIUT}9cF%e`PVtu@Us#KjI|d*(EM8DBf;@Ihm%dSA5>^7Y2_Lnkp=Bd z94z7Y3Xl#98D`ix%F1v8Q-NZ`3SXhEwOUN$qVc)3Mk$24ukjZQA|H5JBX$G?ix0`- z|ARALX!jKC$qcoEjtJ_R{v**9`s*V(YsQq^Ik@*E=)26Gacnyw<6IT&`b@ zudHfh{@ajWXOeG}SEuwQrO%rxUwFyrR87pOy)8xRr8A?fUwM; zl&PYFyi?!GUN{QI_cXWKpJNUzubL&?Lqa8IDS~6t;xzncmZB6Yx;J((zLGl?pc|%g)4yF$bVAI?7n9Z;F+0YIiRZ^38?NizWIbTUI4F3$&XGs=w|;n z%2H`WUgmwV+Y;6JY}e(1m?|xoxlKmz9bN(;0#XFWrgWn#rZ#^Bjf%T^?$@#IOIv82JcO!8vI*E5t`9WTGV3GK8 z1B+E{(}-VmM{e3WUI12FXp?*EqN=0||7#hwY*Jw)vclXHmLZ8JjJl+)VkvO<&qvue z_7tl=OUFF62X!eR?`29WqP7A({WZ_K$H1C1O$6V^g1Z>x?EL4riAnd+GwJG6Nv|Ms z4nelYrrbV#v7NBVv7 z%t(qYEiQghXNm7|Hj5o1>F>FtsMK}a?D=V?fTEQoc0JM-c|$ub1OP_X@vHGR){2++ zl>VvwqkX_T$Iry(s-%~4YD2$kH=X@>#kiQBb>p7s%0g869nYmkf^J1|5~#QiK~@}% z`)xd)jCUXZ!!of#)U6rRd@eb<2Jy`X2B*T~=vwPEJO$0JOG`w8q^X29ZQfHfZ;RQ9P$}3T) z#2@v1B`c2canI$4N(z$-t6PUxCIeKzra=T{z*Em)Kruk($A!N*|F6SP!9pxMSU%(% zwzA~)Qt)|2Va-Qo*vto7)*!p42Tez(BhD);H$VrHAeRqU)#Jk?J_Y8HY8pi~sNWLj z_RIrPQO!WO^2MZG3g>U!R;w0+LVN>It)Ce{}{lswAe+5ozXVNtFm48=bYbZZBJki}48 z=?Cj*#<<@Z_Ak@ zB}2eEXr_mOqGaZmG%wy}8dhn2-$5P#uV|%C=uJkP>waibj(iFI7`j)`ffbY{IZP=d zezDD<^GlMAy9XkgHUR>w@$a`$!#>WFl5lyy>0AlTi};*@$(4=s$xyFjG{0&#_abG6 zXQ+zpMs>V^ySox_rV{@x45(TUloACd+ZKI+xvaT0!Z9JT-5#m6y!6AiQla`E;g?$p!Bq3LM z!n-|E_X-VGdA!%~^rBK<0SQU(S55upc=#IK>h6R=pOC1RefGRDH2p(6gP2A*7?*8F zCf0bRLfzh{n0ziZ`#UM0LffBM=uXwLhEGpW-m|3$pvshfoSR$KYAe4caMBI(3(n|Q zga^nL0|}uSwA%Iq^mMB1fap_i@FDVJJ?FaoL>m<(fNh*R#v_)BI>pIdk+{% zXhH5xH%ezKPU?+Jn*4pm0=ySTkd1m((+?z#65vt>Z1GmgGYA|t4sQpq{(NqTR) zft}@Z#^&bc_sdu7|9cfy*RV>vV=eC*bZs6d(2wpHHFeMUzngiYsqFv?qN*%0n<8Ko zRAVkOI^Fo!rC?=k(BG`(&fq|+63W0mxjO30Pa=8ZlHUlwZ9*3h-SbwpDE3w2SV&s` zU6fRowjzpn4zA;Cl)mJB4wNel%HAiQuC5ps#_l8z1|N=Ss<`-=#n6%T{9Hn} zpo9bc+Q+(|J_@pgQ945j%dNPuA!dH?LC9xT%bK{)3b)`Abx5qr#f(khHXiI={DFzNZ~ z%%=CsQnQ(_(=_X=w}MumzVr5W53uAoS!z|RyNKd&fJfX)rYk$2^56VF54xqV8e`JA z5k4Da$7XdOg$Fo36ioNub^TS{+8e%pvQw{{!~=r}7XFeJc$1)M#;3m4rSKUx@WdLY z1hT0^ItIrcm;V5Oi?PCVtUXRZNOOb%M15(lNA~PdRdH(<@#`dn42-aKoPWe5u`Y4{n>|*gEPKmxCI{RbEz+Q z+tdyKOk%DF9oT+zs%7FTd{jxs1V5YLL?Qp}I2m-y&$3%Q4u3|4SjA>vd-wPknW2~J zu@w?}OdhUd7*XJt_@{9_@CdgPLA$6RKXDG6*@_wJcxIL@U(B1zL0N&7vRsKPWk>V# z!pLe*PiBDfD=Sfnwbd2D4|gNu(x%}HZ8g)mW(d?PiSB&O$f{j4sp{Mn8K^Y+n>iYwk?CFv&V! zA60H!Rlw))=KiWdc&}&PDO*@|q0?^!KojEAdvMr1=u-1xNju&?tbX^kpsm0?n(i4* zO~j*-qaKaoC1gbNzK@^QL-ZjHVPIV>bH*hGY1-^Rxp&g=pS zpR^k1=al&l5_e+BbSSbB;J7auLupkQ%s_W8UYFpp4jrX(&Y)4kBMO?sD=M1B z5a9_c?8YQJNXvh+q>1;7WA9dcb3$f-mZL0EqqX(Xkk_u7dYzO1I9teXbcqib^O^FIeWKb=G(i6gtHo+S=2p7;ErLo!Xckt z3-t+;m!Dv>XFRm5MZ~=#vb7-K~nSbtLb z)=r3-BnyIuzP?dImV7yoj4ehJu|OSd67>i)Kh99B?0Eumn|`h@1XUjE?9-iZ%X}#; zb0NoNb*clPDEurQ>RToooK zHTF&c!Ahu)`<>)cP?09<5_Q^Q>~~oDobYd$W(n3zS)`BBkPLtCcqdj)7- z$r)SH0oHc-bSdi(Hqu{!|Oci*}D+sZ!*SY7DPRYy#N0 zd!;i?8~PEIHeDTF4~0KJC!c#5Sx)hKHm(=r&X>8=?PuTSd)Vqi|NpT|fmA+=oe!AF zD0c-#yT%R}DgW&_fpk3*HBntTy#-*I^Y2DH)XrKozhQ|Bu5M);^xBcM7MqAm8-U8j z*H^n)R-x(YoIZyeNhN9~E5oA;GpQGFX$5~)wtrY!m7=*q53odj$!opl4|^V+4>gNt zJxR~UM;SM<`tS7?dS`SldVHw+hZxH1;ZnAE@Bg4}nc3}-=_AUPMZk0xR;lFF| zZfn4@5%XNkT+XzkN-C4D)ocBG^{tJa@Ev9==6=6Zdn3z1bApj)pP{i?O9#Jf z%zy_xCYxxfLQq*HAAp6LDu$@F)T7I52SxDM!8!!b@>tf|j)nU|l&2^5IS&e|G&`I} zCEsk?;?|PD-TFL_hFbqBf}{okg*45fl^K^l1_hG*{oKwhR##R(%cgNRlKMn|>z-!<-cS-$qM2+B@X zW9|N)N(|4@PV*=fW|#Qll$VK8=wMj>?Y9V-Hco;fRIIB{q>JHCbUPbEXRX(#F{AeA zimQYoomOP~pbyBmXJCbv^nXGcvsW$8uM!DoddHfkDw}6B&Ex`Pf8Zc6i#s(!v79rL zDd4Au=ztCmK(1*s7tt&QxcM3&>+GkboV98D7LWn2BDv|~&=BX*wEQ!>3ypf~c|sZ1 zec`5UCYWMJD-z;cJ~(K`BCH{3(bA$j(pl$(mha$tky?dnakC2*cVm{Bb&ra)B7K2u?4&2t~ zdxG|rmy!$n31G~)OZ|I_IXv|!#Rx6y0%zdysy9ghj}%&5Yijf5z#I9+AOPt+$9;PJ zn$eI}#&>RH)XqP!qB18${S#*=We)I}52G??ZRdVL*Em7+;g7*iTkAwKLv}~HUAw__ zUOxmcFzxru>@{10W!9cA`GcC?di79Id9KLsx-*+bM#+U{?`6-pZ0}eg6X}oKDBhWi zRedkflAYCB!vPtc2P;q#wb&;ce-^8LNGmYB$ry1sI6x=1yL*Lw%aJ`@{g86E<{c`# zag@2U=~OT$ZMZKU014d}y@jdfUZPxo{d&@UF{(*6*50&BcIVgo6MiH>uswgtf~@;= zO&(yYak)<3G~w9n1>e}ozto%)?gZnAbahY;(Fi(-)lKu5W^b$=BNv>}d_)llZx>g< zUhtFgB4IQTyXxQD$4~d)C^5Y23}Osu99UT#?OQltY~>xN$}EDQr3JFn;nwI;e<8TM z>E}HQidn#w(8-&%2`i#EC5ZS*dio>hX_87K!*eawm;KZf_z7LJH z%OsO8Q}&5l`#Wl+lr`Bx04~1|%KQH=H`9+$LwKGznD6E^gm!R`(F z@RD-9<`qw?8IFM^Y`uNAa+h%6GCvj~X-*!Cvkn;PQYIrz=akJdq1u2v6)*c}pgW^ZrtZ{b?19iLq1oH5i% zG8n(|ao!sz?S}tbE{iEaXg;H$>JaJH-(>uk$JfVv5)|RAsD}oHNxAp;p&%cenlXmG zX0Z+GZ!S5>9e))f$vs9C)s+=rH2fVPt3nV;%UP4u zVO2E3o@&g<)Nt6HtQc@OoB^gPJIG*$tdr-W0;ZO6B*)|QeFlB-NDjzaQJoGZEp|D(VxkPy+Tm61EO}Rse-M~a`aT# zz}jU`l7c)7cIE$L@`KQm`5pbq<*e?W3ukLB{;mr~0DR8?c& zFYZcx>KyXxv#nWe+l+G0zkx?c)OQHCH?~lcCGlB)ohoZZBy?r%iB(DL1q$>fsQwP` z7=zAl)RDmTaE~xS5RiU+>{9US&Q6=KsjcuAa7|2g>+|=WJXVCw6q~~h9#8#>cqFP^ zNm2Xcz`o6?<~oeBYJNlJnVhH_hdCP!DZLARLj~ITwKW7Bv3S}UG26a0iv*mN5 zw>Ks21qqpy{uGi~LI#Bd6Ia*^YdNT-P(r!IHYFX>5Jx8e8tV-4d>9D&zez{7*9oFv zj5CrKU!7Jf#{Bnk&rlU@vS-M?ze(hRoX65j<#XEN2cIN-ZwfgwU@@lZZK?=AXqwQj zvzYnb!Y^05O4%FcD-=%Wbt8&dX$;C;f&GHUjv=ZA;I-tj^so9*5ew< zw_D7Vz!BepGce46eEZlc3|3be5rNyBE?Up0?FTXTgJd)&lfNwtsp9dH#B=ghXBzoo zAM()NsVQWL)#N9ZFh6EzrGUp3hnpF4{z?1^KhhLRklG`u|4%A;AguPb6u=m!MJ|DMX~vG&j}a4S!oG=Y-#&b*$=T# z>J4j*XJW);qt>d%hF6x&$^nW!O`%th6%#{b`?QnM@raLSEDve?H{H&E8>#uqCt=ax zN#c4ny1?&sr%T$!r$W$o-6S7JH`22japRCf=NE~D6C=I4jH;K@TT4+kESI2mzIH8o zqZ{3A?={%qcVAq54{sbs`4hVr@R3OSm?EviRzJdi z=RAhC)9gJXcxL2-M&xVX6|%ra7Y`!bI(cXKk@QOz%AlaDpZE2yPBx@^F!ZTCZ*?#9 zS!Z{4$ZfA|n^u3kPKDHfQ^!+5DTX1FKmtwLVBDTfJ?p*m`UyYOtHmX2`0d~Wy@>NH?$ zDr-ClcOVz4!;z3r!Pl8a33jE{xQ^5spPTOUCdl}J;En^1y$?qtr$fapvEHaB^epXl z=-$~fs_Z!jHh_fpzGRV-LfOEV6bZZ5Ohp{;7KOc`HF@OYFo}3=wIDT3@CK5SI_~^LO z)vq=p)7Yi2>G5pNZ>!|vhsb44tl~FoCs(huxbns(j#6)m#B3$#(Z}Ak=*N$eP7~R| zj=z+u(mRfbJ1Qu*HD~6o+fr;ydT5(uZbuy|G0`67KD@=|hMM}%^h`R6geF_TO>?|0 zy#Rw{gx*#G1*d51t*9fO!N_ZBUzn~)i|Yq`fFA? zI1wrS7I&}fAlK{nusY^7E{P>u9D9HF$I!gu2(97IeW!$|Rj=tx&S_8BH^36EB_y9N z3BNtzjdMLvKP`rKdPO`Wg0k&-T%>0HqvSgKxKQRXy}Aw^HD%TDCEVZg2^k?+z8>h2 z1ZYpvGQA1<{NVQKfd0`#ex>`2o=W*r7i0)8@xMF9VA73=l%M*?#S`}F*cN}cpHC1J ztZMWWeTg2DI4E`6?F2m zK-p7i<*Q+L)`O&qa-*WQ@92~xmq@ohHqF1#Nkqvj?L{^<+UVA+B1Y^R3cEd44B3 z^^|Hu-(LHx@L9tw+}u*k^uqcb)2~W=P6Yix5I}nF+b|b7C0Fe7ePZ=S^r;`)JCmF< z!XGq+teybZN8NpUCs%@YHVK5SoU;v>WLm7*hDWWWOOFXK#yzrJM$fSp=ju0y2{fHpg=dCuA=fcpNM>zVoa5qNm{^nl=CJdT2+~`ZjLSU%O^YJE2 z>?iH+qcn%Z37OWHzhAX$GTA6Q`F(9wUInc}7EJIzTR~Gt3~LosnRpt--)N@S6@y~7 zqX}PUVs;(djWpzM&W)K?5?nh8hcSr4FR4&~-3~Qee$1Qhu-5k;*5k_-GI_w%>y2K$rz*R-UJ15v_r&8;XfCT6|D- zaZ}!c9jpPO9f`tyb`b|>#KmT~=IVdcRfWx#-?XGoOsh=U-({Yw+x$`li@9G9vPukF zO||-KxNEp0CN(6y63bZ8Rb!|1jVx0AzxE${wRrBH?X zRV|G8ZDfbC$H+Ln4<|!o8WxnDr~yKDC84N)(J|RpeansYNuZ?I3)ve&7f%QaLsy*Y z8@=@Ns2fBS(xi%2YHAN$hkD`YcDa+G`Kpn4lCEPvK${`=Bu^r9g z@^I=Ro!+=EepjXh#B%D<16ni&(A%jV8}|jU7NwQ%inCqaQXn=>&p@U7dvpAnSo&B8 z5wQp}vX~cd2JD_q+lS>Q|7)ld zs1?@hC`8o4#aDvbj?%w`{E$8%>N;DsRw=g|NUl2CcLVtQ6>8+1y?z|#?urGClmOvq z+!#soRjPN;@ytiHU8f&_yiY1q`s0DT*%knD*x24W1=k+L-}528`5Sq+u`!&9+8d@E zCo#K{V|6?FnY|IY`>RZo=+C~#!XP6iMgmWvH$Q2K(|-HIMU3lGIl_e5j5?*4|-)jf3(hl zo4%1fzeMuRgiS6zG;zF#uj!&OV##yCZJh1aQeQbWkx1&+QccaqhAcTAwuDT7TIGtQ!;Ew8AA{R$HYqEA9!{|ub7-%= z93ka#^}Rq?KN4ESZ+x2WS=Ka9QUJ82XW`Uy;(UX3ZAk~ewU5FrnDud?FB3*&eH~#1 z951S7NYy1^)GjRL?UX{EeW5TkcVJ7?Z1fCUdQYJqHi5malmjJaTX_wmK$3z>AIZXS z2>C?0e}Jv*;y;GiSEhDJF+MTrsSG6j%KZn|0IbtzQ~qM&=k!or#ng{a)hZR0R*+q0 zmg4!c+P$y*^{-w*k(GPevgpG#@hxmcS8vROT{>Y-cO+HE7 zU@~SK?sV+Be%(0he;5uR(e>kBu*|L{m3oDmWTf9VQx!9d?X%^&bp~~8I&A$~%wI2U zE9x4ZSZMmA@dXULX!;c7{OOhxzQgg|ZR^glRYwEb(WJxdf2WUi4lR_P1M%e>BMWi0Uwqw*4kF5s+7vjtPW z&%UBc2#F&)@-b>Cp)R1Trfuq17a;CdW8*HCwj7w}tt1K(1tKcw04e(vz0|p_t#)+U zTy5h8uc3BY>dK_wu}=UtYLNT>#-7`c$MQ4NvtbU>Y7h@EM8w7cFs zAgm4qx%nOPW489{vT~HeRu$?dMby_`eL&UKnT)$wI&#hQA79r2iHiBUcP5;*rV?%C zuavL8@DTcZH{EIpx_7UD5nnf=9e@X{Pc!zbG}o|5V5kP7jpPkWuAAPHdg5M7q#x~R zsS$EYPrsa3CkzN|bH6el9He37Wi;Y4VTqJ?ceci@-dUA?&F8RB>7+3}ep|_>CG9rv ze&01A+gk~u?l{kMKTMFOWUB%PsEY|Kj<*g2-x7_lqO>woA}QUaYMS8UyhrCLa<9?I zY*H9a*alaI9~AYzD>h+F25cwWoxm~RIM73NJQV+nIC)a+WZ}!mYOPK6Tzo&)ZOwmS ziN_2)kS03#;_kcdAT@zQ&V~iKnT}VDTB~`zXc|l=y9r>!YG{tvLCsL!2sadDBs?bl z#*JomeC2>|ee>*+d0MoP$or^*ImsVZiP+97xj0}L@vm}rz5{@H{C8nQq+(-l`D%Kv zWa;^Vl{XcOPR^!wVH;dML;gb{WG(B^T-d|8>${uRu9>_Oo}B3H>};|(z4@4~rV08f zy-yFtz0*I!>YQN*jj5eNFf0TrRsFvW-j|ISXbW|yy;5pOctA;khVCA!K{@$J#pYHt zL^p(AhxT+Y&B}P(#T6EN?UhVHp$TW6CZ<#D#?tet=k5$$%t;9eXjf4wj=0tBWW#l! z_I8nIgM25y$5!EcCE(|6ZIml0pifbky^1(tN4_tOkFV`CR9irLtYltg zXTs_+!qUp!akai8%mMUWsJ|Jr(qe{WlCG{7*>>gy#USW=fluMDS5C)0B~8idmmTxE zEOQ#XY%@9GKxE7DZcEjl<#^WV?l_~w67$%a;G=!i`JTUT8@wcnRS~_F{qfAT@JrUv zSW0GsXqvgH9m*-YLO2~Ij-=*dT=9MVH`g*T0l_3F20aX(oPQ4C( z^wgP^o%!LUzG)qdmx;h>-@XOe&h=q5S;9K`KU(w=lD+ZTG~1!+njzCPa`9Yc#A9qj zBSo-?c7@DIJ=skg3~&psUsmj$3v;uMf3T&^E@ONRGB7C{7|XQ#kbk+5@OefA zL4N81Y?i>}xg+-F12)T@)3`~}18j4ZGbdvDTI>9=5oZ0VdZJ?@30QW4ZRPe!Iq@T- zqBh38+gqUDHcCweo-+$l)BOIncpa!oFEJZuWPMYDFxQ6Do7~_ z##Yv8G3WkGUd;yAgQjlY{k`lXGuV?cTxnq=RQ2QZRoC=9KMnXR_3rhbC4^6T{EcAj zCv$djmnX7$e?K$8W) zlM;bw9{zJLI3w0oO7P1BQmG#Ja9mS9_=!@hpZNTtTc<*NEEt7i&W+7mY6%010`SeL z$0h}9yt(T!hpI1p#xI?@kSz;2cAJ2D3=5A<(DD_^m{+~YmF?pOT_>xjD%3ss)_H3F z#Y?;NFFDwZDwBr_|D-v=mcYi+w3YKO;#mPV!fA1>B-?Rcey#4J?dYBokz0qXo%1gM^DT}AoR>GJZ& zzUc!3e^v^Fp+!InD?B-*eD&^k+3!o2E*O2t(Ukyux?69Ia{}+%Bac+Q=H+VaFCR?9 z9~J@OO^)1&HZbFD0lMJTqSY|PZ1p%B24j7xL{r}@UkD_bZikv|@!5!)fq#CIczT4P zBlXJs*3esps}zON0_l|lULl?dsal0jSUM8o3mvzJe;ETZ?r zFz2&D%loty4t0tcC0@+F5Xpqt%rCxAq1xl2380d+Ml}xwxP;#MvFRk_dLlUG_s;(Ye-W#Y!B9x6yD3Ox&BHI+|iMoa~LV4fmFU|^J@{M z$e0jCC*HjU5Fn$jGX?klkD_z&XX^js_-HhjvbjZxnM)eVrKECQbB$0Yx8yo$MkR&0 zj3%SGtRh41Nps8nIwT2yJ8u~h&-I{Vw2+t#`MnNF_B2q*v9cj7-Khks+*Hp4Gge0ZYW z^pWIUs{6hJIoeK-rpGQP@BaIj?2igixUUc3mBqj7&n$60J3u1N-|}AP#^hrw^IQ?T z9SFKhRFI#qW$%bo<`+JE!r56)5$rP-)w&N6Wu&!=^d@U80WXeNTf$_0-`@hL7FR;~ zA;GF%x~L=E9T+0)1oaewZI$On^EXvL-70z@P-Vz6FM2u*;o~ED`5drJ^fb!)ckBOi zea7poCJIlk|H5+z(y#CH**wWl)`!R>gr%iFnHK2ZI5YkL@ZF&wxdR1lXaOP|-P1oZ zrv#jy$~~2Xu?1v}f#Bu=>P+d;r4b(dIv+Z#J8ZJvK9$eCAF3@GJY7mJ?Nm>x41dIL z(;U(g`<$vV&`J z>yTH26f>ggCU2eFwL7oWv-`)B)ua;;4^p`jRp)4_rYh~2c~#E|+1MA-I`Q4=P?q(6 zvjIu6V9KfVWhVSz?}O>8pio-wS%qiX7%e#q9ms=VTOu667&|XVXb3%+|D|x58b;N71KYX zX;;2#{BLI~y?~p3O4;|m%74F>18XWzuO}cR6Ko4jqeINblGN!500K8AdHJ?Dl8I5) z)tll zA$rF51&S4SUfWJ=Q#GInL$9T`1n&gwL7DUaF}No|$cHpWN1&;2QE)EgO)qgkD{Pu? zLIH)H!1qM;JwhU7g#nlo(ow17Dqyfq4bJz?&+sfO*j=Y|swl^-Jn3R>^P`Qh>v@;XWA?ibnHE9lBy{&{Z6EB%y=h~imc)l-@K`oNbM{kn`OS|eV%X7mWP(nmW`-o*fS**&F*>Yd=E}-=?BsT%T0}?-K@^Zb?;c9{ z-8wVd|3P2>&3L9raf+@U1u4YP^ErV~pVIUPN%zhSB;b+DiUa9e1A}_-7q2w*f5y~| zp4P*jH{ZL?Sk~<`Ub->;>lI!M3W7`e2OuS79~t#r5xiADZ?wpCdp|ISjEvBhabu*A zID8@2Op|j|^NC0JX-2osOq&r3H?lsP` zadGL-2OR#L_ham2C*_@XB1lN3r-@t#4)8aF+bWZ0hDf01B%PN@MCTonAuk9AbBYl9 zeOr9?;lHi8zyAOx)o_xvDp*#d&J{4p(wP-OK}i0J5_S8C#8K;YAB`3jb*v?>PojY{I|4+OXCgp(U(sV}B>{By zR;-}#mHH&@%y*TX{OhyVy>b)uVv zxi)r8J;SAZ5dRv~$Z5 zVsz-PnW5z)z9EI4fZtojgv?I#Yruu0d-gG=(rqEy|ALKF;-5mwM`r})7Ei!YpK`s} zE|MtPT1CN0x+8>>9TsKr%te06{oVW`KoSnHzI6sYHd8`X_QG7vZdduqpK6%yYaAeQ zwqVHm143AmAfApbL+pOi2dB;S+)hFXLCgh}p_iRS#r#$0ygZ&BKB}lm?XmXK##_!XiPV^i(V+!2ozQ}^_50TyKv}twV-8z7q$w4 zyKPVEY$|+vZdj-Y!;2fj?NOuEoUm4_^HJ`b6ZOCHpWUn|zt@m5wFpy?#SUzB;zE&kSgFaY zhQZa=82jW;D!Bt&dTzmh!H@m?X7JjKNf}nAQ>g@g zsl)@b&Ru#ni8OOLjfJdrCNG35Xz`B>vo$r(zQ|6Hh?_t;8s!fk#GNORqpN9iYj)zW zRXprss1JDouq<08Vtjf6*Q^VManh3wr1VHAf$yJ(G1EX|udFZ%w5&Mq$SPt!n}+mt zCu&eLwvhX;whaB*W1pX%F49oeF9NA)zH$V`FS~Qk9`yNwd$Q41!`Yo_;^xK8T>%mZ@H1H_=vu*;Cj-1aleGAaJlEw;i&I7vd2lhjr}+jEC(twNHiK0*vI5_pIIURicX$+B@?v4;Omi94p=W4t_;wpED(EVn zZar$i(C&2$SgQ&94bS=o(m=o@u4ay=)9yzA7V6_4-Bt5(@^NQnzZ+tK1vlXWzoz7g zpn+wd>>fg;pqpE3S-tF|>|^?fTGn@a<0d{t_b!KSlJ@5h_z6+cMcL#?#Z1H2cI5S* z^jxIT7(*|8)XJspytj)0htq7knCoQmZ72HfDYP(t$~<*Qi|%d;P;M%it%;23NFi(b z=Jk*8UW)!*xc;whA*_1lloWlRF2zt8m6PO$@Q<1t#Q~vyNA@~vHl_dTR#inSce8g^ z`iq#`GZ{t9r4F3Z2v9_)VDe>a|De`IxTu-3ppdQIB<>0S0GfAHjNi7wg|fY+vYLd0p@a?J5ue z`{{Wx(c<$W9HFN5*!H4>OH9g2*h#ebNjzfi7eK=|v2r4OcqQc8x#KEta?->>GUsiG z7GP)*7^)9`4U&soKVZFdqic43R6=!tjvKD|d1p53jde+CX0A=}D#wWL>qwS{9&YBw zDD6QjE2Af5(gBxod>AfOqjT9W?`IbYz|-L5CZHiri0wpl1cN1+bQ zsAzl5ar@|8VuZL-%J66dq%oNdnEAOk3GZo?=eF&pX)a$;TP73 zgK|}opDUC^GM!wGC^TR~=efKj!q~i=3he~X8Z0u6>l&_y2mjMOr>rU^rhi*(7PC5W zx;{8mGBSI|I#ZFD=a><+7IVtXi+h?0RflQc0&E7%8wC%wa!1?ufyeZvUKQI~!9r{S zB>y?WH?qUbA&`zzgIe89G|Vya(6{PR!OSewiY2oJ3f0VewaQPgp18i?mbvkR@JR2z zKlQGxK{^eMsRBGrbZz28?&po*V%gjnT`}F-*Ob(Iuq>;V^RU+`_WObAZA)8Qe2LQJ zxNEJVy-Yn=FS#`U)xwQ!%cT;Mh%R3Y*7r!i!W|%%rFQ2>t|KF+ud-uCP$=SO_oRe@ zy2kM3nf6+M&hcst847boMQrO%7{Goon}!5|gcLJr2o;G(0BWY1t}kgC5xi%_K-m!r ze(f)~bQUY2?<4v1oo6IE%~)|JeD(8M_54&~piYOpSaaQ2Mb zOoIT#-z`}ARg^+`vyn@@UFCOL;6pdSo3m-Wm3`*agX*noN|(_S8&IS^rZ``h>DpO; z*^&5u$RZgp_UjFG?N$m>S@}dcF$&yWb;g%!TW_6Sv);Ac$>p!n2bM;cyn5}EO}PGQ zUEH6i^~m6{$_#pQO2~cH<#XVlK{0ADXGyTAmE3|<^6{XXYmSs;`tlrWmr+wQ!tYwSSk)$}s#iJDVdW{VT<&H}0`LpVt>gKlW=osnDXv|8(I zH%mrrjfaKRI_`+rD(+&a1l{8Y1y66dlUtgipHL^OzHDW&6(@7-RM zRjGr7L=k3x%yocc{%UyHc@h5DWoAo*N;@NI)ZyF=_4W1SUK1MlHb?&y5y70fm*#}$ zcJ%9x6{t(lTftWd49_L@2V;2MtCn1kj#%3EcIz>fTD{+4GbO0wFbrmpWl(#0?usEK zpnU2BN^RF?b{gf-pn}T5*?I6OZh@aqJuBsV6fJYl<%O_?!%9vS*xH-!$*nmXL=vJo0 z!F_ASM>u+ifY7Wm@EcgzVe`kgBzMDO_tyXQ|9f>U*ILy(GwCIceU7n4(`Tn05O98V zUFB+gVdy>r$ysLM-mEEXI(T2bj!UQrdhYsx2O^SL8D?pfilSE@>NnauM|e5;K6+S<1G7XC%8KOyOOkgoIR8BG}KoS8dPO6J8M z%7DXO1^@!4+*tdPEv2CkvTYPXe96ZttrAm?{cBzN-tUx=X#qk#g(P;~C(TfVlBb)g z)Fv+P#0=oQd{yIJ7SuvZpq438Y$Rr4Doj=Zakx$w{$h~ zoG_nXq!RG?o=SJZ20$3l2Zt6;0L%5>G^-$FB>fWgn+JkviTg!eiX|z$B@S1&vuVgc zT2SnG^r8Pot)zeF0Cs<@EbcfvD+QuH?W>_8$_c&YKz?H2p|$J`_we=p2OAtLxvW&= z6KdZbo~)$WbGAz5xw|t`#M;MXceazHK z1>gU$*uY&CsTHtgxA9~C^&}*%Tiz)Hdl#eugmtWcadiXJP)K*r8qz2Y<%bWonQMFN&Dydl`176TXk zr4v$6ARW3s&DvQGqRXzE&hd73*&0>h>~Zv?MnoaQP!7M>LqSUVII3OK>>g66H|o1B zN^aGywNgg7Koh`8`}JP#7k&7rZYw-C(EK6F=ZN+_OL;+RN?_?#=Qg)>`xi2*Vd+x@ z>4lXXQ4|y)as$kbnxp{jDf~XqS0!wi$FL=pc$R!aD{N zkxHhgW{~wH!9^)@bNLrhaC~Xn{6J}fbvlg%6tQX0?=jH7UP}EL!>(cNYxKIE6;!VU!;bEsad_ks8s9`yDabWqrG#8-5B&FV|aRWA@;L$XVj2 zD6#v^pC+YPEK?HVTSck-^T$DDkkZqEz9p1z-9+&oR$ zWb^1iR+>0LI)vUvPn&ms^Y3?hSN~aej6w6M17aMtF=|3Xk(DYS!c0W;OfuGN64?|P zP);iB8@4 z{N<)2l3+0@2$>@ClbMSoY!!{Uep}l68yizZTwiD!;bdlr??%QAGg{W)CBRjtm^b+I z3r<_ci&twjh-85pyb$`>))iO^HuX6s6QC+_%qf=>9i#H;S0w4JiYOj3(IozFXJ;6O z@Z@S~l@0=t&NAt*gO=FP;?u`6Kq|@%g!Fcs_-SZ&~a%MYz}j5O~%9?7VvhNr8?6luUl4>2K>=NpmILTBdfD@pto{!u;>4 z^%5nrNH?+-Dwzk^GM-09K3GzFM}|VOlf4fh`5bljDh01#)k8B8#TKy2?DFf^8Oc=&e8M1(7Yz zc5)bhM$qpu9;tb2d{|c}q&`K}XJNawh9l=_fgfM%M`wzN9H*y;Yw8NU+g9v<4-8w? zDhH(-rEROiwPgigZAxwih=ku5*@O$;5`$I>8t&-Eh(KYX*X?gB9s%1T>^Z=Yb~ zxgkm*e}%a3>+OSQn(3Buj<{U7?hS~)@V-NWQYYJUH%Y+7$XRM7l~2Thyq>W5{XZ^&dgx&T| zT&>P=MUO5(jI}5FK=J~_;-Mzd5mQL_6g-!M)`Tsrp)lEeiv4Hf z9FSh`!D#4*H9N=miB16fg-&&do1t@T%)bR6TqH)2@}Z+J%>V!<=-ioo(kS5KeRs_&oC^LvD-49W- zgP2+|i7X=(F}BU_EIR=S@sX8H=pm+px~uep&+F^`syhzbGu%GCudt`W z%-v7J%y;qVrDPGxoS#D2ut}6<$m})dowr&(UuyY_Q9Z@4r>9J>oL7N?_U4qt%$=O3 z*CjBRDgi)Ci5_~0Hp215T}{x*IWHz)=Hd(0wXk?X&cxS-V3-;2$IsVUNME^1Gbu9 zeY%yHO|Bl9ewe?f!{r}&%k0lT+y81Ua%($>J3ZpJrsj&UKlg7pwt||gq5?}fWxgBP zmCnKUj4Ul(|N9|MmqR-0mVdippQNOoKK172TKns{w$T3q=eB>_U&x#`mIRm!lzD_! zHU72ZMcb?XzvkAt2va-ZiJmut+#Ei=W7ZeGK9l=rn`UJyr3ov_KTfWsWfLqG+p_%? zmX`J`i{}*>JQX-?E1*K5P?CC&IqKm5a054NkYk%0mLlqJp)T8?9A=f%<#m0of;*?k zHExwEsSejY4Li)gtlM%)UT9istn*+MRT2TjMK$a>7_M_D7(SS6o+K;a0k{2Uq$E*{4 zy&q{)L;C1*)yzj3bz1YC>v4N*B}PYL3rOzPs&xnn9hzV1 z>quiT$F(goP%-<(;I}XT5%&jZh&fvLGP!Fs-nK9G_qfEct)vBEbNAS`aL$I_hs}N> zA+Fc_bg7KG>ZM#rJdyONua+CdRObVB!)C8vpIx+ZE0u0iF-v5Pa`Pxeq+9Dy)cEUZ zmJX{j1tgN;#_JSWi8vYpQPi`1_Ye+K_5t2GgTG1rK%ywl-7q^ow%d_>`1m^|C2*~6 zcBn+L4<4bc5KqkfU!TT!dD z=pz*^f3Bo;F^r>;{aIh_rbZiSP}>Hq@)$=*1{XS z%o5MKpYaH@e_W=;nwX~q!RFo#4m2<-fldiX?&qD?3+0#P6OeXJa%)1#LPQyIrUmud zDgGu|e~6T9^n<@h9SQ6lu8rgU7EA79`rGJ-XQ7g|&ZJR(Q{dOao)P5x9>*k2ScqxqPq7D194qYl zv2~cS#0g9xFFy&Q+;($n`ttE#r*VV1`$j~zH|dH>>v)irny%HafdxLveS7osd0 z1s!{s_zvU{qT?F5YBS(!Q;8BWLuNYyEzHb5@_z6AY5yPwt)u=*hsESVlx5T(-tkkm zo?LWN|3n-Ul(ZEH&Lvs)I!nu7F5w3?e6{c#wp|h_VsUfo6L$Uq#YK%k1ez&F`D(H3 z9l@Go$>B4M8%O`K;{G)wgzEuj_?c2s(PV*piK0Uo3naS)MpV_$us&RaD+0xvOD2CR zxs8xS2)iyigB=I5MTlE=xp=rX@`W5YBVJEZ9`w`eip@TA3RmgLFd9NG8`J8E5l}fs z)k)d?%bR{mxmB%gkIG%OhdamlQt9?6ZX4Or)wmuiPqr5PdSkWCugq&{JAFxx9bf>i0BsvuNYjZzq^IKj) zbHnO%&SO1wgxRRla_4J38Gvj8-gR=uU^tBQ^Up5PAogK`U05hY`7!psMY5xeyK870 zS|M=XMz|F^W^bqNmey4I*~wHqaVx$?8D<8vUjGh(IIR1n+#4uea;*i;+VxaAWO^kw zDrE>D4ep;mXJ+DH>Y)!vlmlh`so`gQ>hZ4M{(Q&M3&O<&RhveY1bk#bZ}e2mywmUu zF6hI6G=5|+sCeFJDF%mx-IeuqYmzsM8nK>hd#3x7lW}DfGc7ZU8zETP*G*h%zL&Q} z`XD*@98{8T7|X8{mX>8ElJUzjcr!=B4=1gs@|`3UMWe9L_ryHhd+*$EM8l{6Q2<4; z7Oz?fpY9n{O*C15K_&Ui&&xFp8RhELS2n89;9Y5l^e7Zi4ejl&;dMlG%?IPCP?^_n ztr#RKT(xB>DwR<^-HLrQdbd3}gK|OeIBy2Kd8;n~m^6sH0Abz5P5i={~9d>%+Lv=}k*0=O_ zo%x-PuCCbCR;@h==RMtQnL1mPq^LhVh;N4_74;iD?-1eK29>BYs&; z=bem?n~5v%g97xgaYC-eSTV|~Xf-2BU5ZAHFHKL$a1FiYIh`nv%R0pMREa}d`8G%r zIgx@V*agLdkl8?Bo;>hIX2`v?_+G__j+F>&Oo*9H^I&jE@PeuH9$J1y%Yqa#Pv zd#d<2>bFGd&=k-nx0ErJwJ>{~U(eY)qe2D)B#Vg=NM#-J>OjMUrE^;%I3f$K_aXmb zb`9sMd6lugXkq@L9~X4O(g&hx!!K3!*ZENj0=rYh35h-Vxu|58^PI)H4I1}k%d2;G zUdL58dL|L9Wc=0l-DEp?iP+t(Q6s>$$Kx|l!OZ$$-?E8po2jtsblZP?&d}dK*N}7U z$g@ycRlWK^Kmv@eb!oB*DJ@r|Qz3_$z7i$nGpn2CSM|Qv?K9ziZ^sEVDs$p)Ykwa7 zSFajCQSYXOKT70+jLGiLUs`ZZ)X@?Quk{3rV>tYB@rnFT?~h2>)U=KJZ+Lf^ivBu# zJV)AxQox^>$K1yzHgMI|fxnkJ@bX{i&aAvMzA^xH$dlHXu1>9MEiophB(3K|Ki&W? z`{p8=RNyCna`KpgM--_@B%09>f` zQ+Y~I)`cU-1G1pW1IFu(_m9LFJ$gA<_B?T&{a#Fnujay(gvb2bHE5n^9}0*(SKE?n zd%?^huynx^A9w2osC`VOJSCebS1eh392=%(t5qF0c-(TSNB)VJRDS8nq`=U_BIqlv zYN}H0`yrMQm;?B!y{)aS$bX9yOC3k%^slW|?Yl)#Ho@LKT+*rr8;UV60yUac^KKSv z8P%Kz0GEHJiWP49uu4TBJNJA~eVEwstmkH}xta|MiD&cNM^?1>8_y zEPUk!)h)hvPIvuczqc-^*#-WxjV!c1**!Qm%(|Qn{Taq*r!=XXu5;PD!k{p}H6q2V z3C&pR)HGLIb6tEOAPlsOhD|Ce1Ki5txKK0i|N0lcr29FrpNriZ`jURhIxF^j_9DB@ zUmSGIvb+DcO@^FIFHl7BADbf9!vX0Bk3n&! zlrO=7FThezQRSe%U)EaEpLsjdvcAWgTU{_!Tb*kj9%2_|?}Qa5wGVN_F;b5eeX`&6 z3c1RZ@@Ze3%ccACez~@tXEQa6o_e5RPxQKG3o3V`Vx~F#=)L%U+t>S$EO#Y+C7Md@ zY9d}Mc&&6Q()l6En=b+zc_?*!Fk2O>soawuD{bz2rzGZNR#t7c2SI7RyZE>1y| z0;u_!gG0U3O)KEWfi40ei1Rp0R2FYoOWihF`tX;=RT>OWfmDDgB7%-0qF_Aut!F#h z+g!SvscAGapE2XNu9Z|wL|NdWuuK$6)l=5#eWj{rrk&x^bpHUz`doV0pR|oWJucxt znRc5{`yxFUz%kb!DT8*EWHFhgf-m&OG>q1|XGh&!XNi74szo z{MALB9-sOf*)_rl8pmr)8}?1913j(5))9&BYe7q|Y{+ClX4I9HPMJ$T;G zh`K+-pa~BQjW%lv0D}7E#I z4-K1^|FVO(t@l1LG2=dqNs2U0W4Bo{2BQd_G8Ng(mOGzb|KHRjkpfS6#s#V4PAwtt2RfhJ?f z{Pxt8mh%w+U!`d}bW9nkSXmXSuWWL?e<=pyhneR@subV2@8S0GMafvq$zM;eI-HR? zE#Z?U4k)+w^b4EIew(~7$VKy@b>cvV z?7Nj_hS4?TEfM0=}Pj3%RQBF_k zm!y(28N3~~Qr8%E2aOgdB6{*ZIJY#S=R66&^1LNw9A8lBm-E&BxOyql(D(x`kJF@U zV_H%1DX=RT?dH&15BvR{62EpP=0ZKt>o*rE@jyzpBHls^H;WkK>2i;*&CdSYSkRCs zuMPK3@e_05iVRQu=XA)p7VVWRR5q%GYVzsqh#8@8=#8$xKc#>3aWN0Ov^s_wZX}6;dSRQUoTQPMOW7kMEhF`OCC4=W7-qdAmJ(m$WI(ppp5L- zo3hwEE{9UqL-5~!iuFu=lP&+717f}auAbMNh3VjJ$$SDC=_)i9{@K5pm* zrDFy_6NOrAH5}%SQtShD|F`DIx5#}%OJqFHMH{90SRfGt)W!}?+;1+M?R6h`V?1&; z6=lUskNanu&Z&g?`1fIz>5DT_6~&v^EQD^S)zS+G#~_LcoySzw0#WQbY=AdHVZQR^ zvF9tDN?D(j#8iGh0RHhT75-oV=qTW>+JGo0&7?EG9g&FKSjE50k6rLVC<_Dy zHbbGlu)d8=Qo_w1H=`Wk-#P8IQG`?W%|VwOYd8UrYrLs5D($>wzjr6%z)!75;?&5v zP}_Cx=Z3xL*v{cYux$C-hO#*q)PN*QGps5+|D^v;;L%%FC>IS)m%e%R>cdqcD~%@+ zmsilufz$^2_p$0NSzkdo!L<6LIx*Xc>3^*v{904ExYp}d>l+S-UL+yU?r6ZxXF36| z$d|5k zy3CXkz+sTPoIfjLV&=}+>7V%fTFe^`cH`SjTo4-%l4tJMAHfNFBS{l=34lk1jdM5; zIe@IoGeroi$>>>=K{hN!uiofUMnO&d>9<#sT%aP^FZ5#8zO;9A#`0-E6o+HZNE4`* zsxJhls>;m-Sh1UqW+rZM*ZHAJT&Q>O*iwGg@h88V2%7*x8Y+OzD;2 zc<V7akHS6Ri12F7D5P(Zi3DtXzxyAL>~}VcrhCcweF`r0QZ@K^hQW z#SXvHqBg|nCu2?fD2`z>R_j`Nj07YY-{W@|)>b3GfUxV$PX<9|Hi?aoGmg`*Sd--o z!K2Y)9A-Nl9=W-#BNHzw_KWN)AUkKw|NUmd%HdFvC6HXoM4Q#XQXK%y(hNy|q> zMBnY4RH!~e`g6+UBK@%s9zWoh7ZxX%O{vfDs9KRp>y@}@FPD7Sr@9qfn+jR+{82m2 zKJE;F8u>zf6PV*Wqbe13*x1x>#p7^5vQ8*`T| zEQD{IkRByfTID8Bra8G`LVbh+2`$0U91nesM>wWYI6mcbI&Z>M3lb zuU3F3+{uw0XT#c3@q;G5)kMs%VMtN}B5% z8_&#o^xwW-FnE*3$B?Ge)?PXvhPVmfk;Gn*5YZ>MApoeX>?ft&%iHnyK?RNLHG>FI zCgDP;m22mn&$?I!yuh2iJ+T82XkJaA77vqbo6aZ)Ae6K#2+5S$_2zPz?c}yK+TW#| zTK7`g+8Q%mb+iY-|Bo-RYie#yVkDoW`1Up)5Qz$ft?Fh=78%GCbGOd+F}%(kAD4== zt%3U}7s8?Mu&il$#Zm#~%P#@|n&uGbj+exKefWq0;ALD7TVVkuY7y#n@hlO9M{wi~W(_pO?ZOi6%<}A^lU#&svms6GO!o?^T2P zs`zO#0Z}t(;Y}8%QJoC_)1gBj6`@jHOvd>nK|;p7j-sz%U&xFA%W zyWdW6P}EN1sarph$nMmTBD+}-D28uM3yx&u7?kR9)otmDe>Fba$Ws?{2$jY1ud3V# zs~))i1U`xD;Z@_!=Uua=_D3N53`>k`WzFKFeh84no1#0~){m0OpL5&J>YiB~>3G`e zNLY5~$W`eL9Z*_7r7za^3s7lyy`3g3P{XsdEL|#6&2F9U@=7v)SBJIyjpW~_A+>@; zwo{_&e^U1>2)7l2bQv*ufdZ56^gNUdP>x<&%*cbmY}xtLq@`FRZNA*mX_UFqSdb#d zbzv1#;beJ%{8HZ^sSaNRo!a__mlP#3`Q@XyxY$R_%2l9(%o3s`%$CL7FW`0rg6b>D zEwuuxN#|2LxUu>BQI0~YQ*LDxJ(Vl(ygu-5kTVsTxQ7-l0Z^mPh#O-x^p^{1fqAduQy(8}3Z{eai>5eVP))ytYzulpU>e9jD#M%DncFpZTlFqQ-3uQm&B8sZ(Ec* zkUaI=?auz=>&9+ve?njqE0S8ZHb|4F9O^Pn#xWRkP2~weMG}@zS63BGpwa_Sw-t`L zOym57OyW&*hZyxK>xmdZMgT;yJ*GdDFjVw-wXsg7J zGD7T)Y%|u*%DaxB6$t>5v1|x~rgFhq+pj@ZG5uHYHKoxBCLiF*G3u-Uh=^cL*1y(G z(*h%rL@lQ+EGUyu+{O^rQcoy7+MaBZ_mMWixGne<7yEyiZ~y2s*4IU{Ks<^LgW*_y z=cEXjYe>7_{$A8q$i4@<0a9Q;`8HLc?ZZ)<^N z{6wgz5P&MxD`eehqVproZg=jdA1{rh4Pgx-HEaew|nCTJ5Fg)A@f z?~!EY?fkh;E?SMdiS;9CrE9&A{VMN7p5ID2nk?@#$V}&(uaTDg>@s(>hTATvlz-Nx zd}Aqxxux*3H8eNlQyZcLUz>0?$}*}Iw4(X>xj%`(Kd$B?6I<3zTmOz9!5k(8ZMi>7z)ErjJSx-1OJU;O&VX3!( z`)Q_qka@A;&pmZ-WidxAr_I@-fg*2o@AfQlGceC?0H1#XK4(+fbP_3>IS}0uve=YIu;qn3%j)^2Cfry*@^fyh^;#(LkGqPP70%0<#Gz=u!WFG(nZ-CXkszu`v&mJV!)!+%(bIr#p<%D+eOkvWwI>KF6q zAPtnF7Yx>eyfyUMZP$ljgx+5ID}?WLur>oHHkPm9{_boYfGC0kI)JO{fsWp!1wTyW)H*IEoj-bN#q7^4Ng4utagLJ=9^Gt`OG_t>hG;B zG1$=bcy`cC{d)ivabrqrUj?(M_5l)>09dZIc$^&>cVPBN2K)+APYP~BG%L%Mlz}-r ze=>YXdGs+qf5Z>6@zYWAKZBEeuRT1=5(LPk^p54`ylX;{{WhNqLf19K7u=HfxVjy&JNE;t52YHiW8$er2Z$uiX5k zPT5+LbE@4?PcpCsvIKyfPIoEw1L=5a_1MSDipkw}8DIb@?(TftE*~>W2KCf&6{>1h z@hHa&xz;Q+w+hvh>`4PY7mn!|D4*4hlnH7W|)$(`eDlng& zguMr%Zdr8$%Rz?eUI0RHQ*iFR!~}My#qVX~efNHd%9W_hF+(~D{E8-~is!{0jgmG4 zz|k$2pPb;%jNAu6BHhR1L0$hiv;B;dNL7IU@Ll04+|ho<3}*Px)*hDCH(K;v;Dq03 z+Gkgzxi(Y!r_z09GT;yqy86S^|^apY?>+hXIZY$1rqH#xkTu?oEP6K!gavEQZ z%eN&Alk?~PY}i?_{$Hnh%hmci(Uw?21l1TTzlN7`)rZEn@AM!5X6Ru-H6-HHY~ts; zyU{a&M_A1_k~T^3!4t;CkGmJ`i>cqxLU$Y17PHpSU9o$9JZb`;tLFVV<$#S$KoCOO z3pSZPzvEeX*=-D{H!pDszeYn6k%!;sNkKB?>Li~4^ozo8JK!%Di)VKxyCoMtwwZ1c zHCA!)soPM;R;k&a{}4DlVAv+g1wGQ@XqS#{6;uS24~m1G`tr^rmFq#d8UYp&sMZ^A zE031JtmR~XKHZT_yPzWQSW*gnXFrSxcaBu|k$D`7xmH+MYzvk|TD{m)t`F4ZHde>Y zoaLj^s(KXvzP=?&#y7dKSKIiEtE-kR{|6ABOM5_`W#qaM24EE(L7clym>9u!lZ z>S%uiw)(mZ>va_&das#pHq!XGt?ZaZ6Z?4NE*takaKH8;u~cQ zA*Z1^Cc+$+CUYoO^l>QXwB~%+=Hz-M*wYMAVJ$#gV99gWzIXk!_4@Iu$ zts4#=RT?TnQlSWS?{Joh*oifPj(cs(@Ge(z1 z<3C#bg%BSi8yJGRSQCPp>qQHvpG$+6LjHWqK|x>CW?o>)t4mC}--cL*5Vz{1k9g-- z+^>IP*U|6gB!kcqCSGz4&(*5XLuw5!8+cgy2B&F%+CF=(!`PXcW_%t~p9;}M>B!P# z)zk9KALaT!eXy8X{XmXk8HMP~b9Fgy6;Q8PP#q2K(sP76>T<&sEH8OUW9jVms};;0 z!~5JtRcmwm11ZNB=Vhk(r!Y|{1z?CD804{Dm zvxxg`Bel*e)lXZ7fQ`E#)cNg`1SOg8$-{60_n-0Lpr5uA=CQ{FE7H-J^m5AhzpWNO z`NOb)q^lZL#*|~6TOxM2N6>}dkuaYw9&s=(cKbVHekXQ!lQ~kLH#o&yJbV8yOdWMVh}} zDpXYUXD5HXG9iMIxblIKdfebJ2;lBhr)KAvBZs@ACk}yAbqL2~8vaJY)y%>53r173 zd2$D^48dK9+@Kk@cRtVKWxSi!8S$TeR;pe*U5s(qpGN@ z>vA-NKFG=C3TbKF7B3)QKUR9ACcGxw+4%$M_z&hu^8qWzuSBU*5E!6n#wpiBq~1lQ zu`T6|hG+Sk{8Lhs1>tn06eyabdPjC-6sf5s`vEx+AM^Lln|Ep%f1hnS2}TcGA@T~# zAJ_{aFjhyP-^h06H;-6SzLsWQ3UpJy`qcMr+(M!Qfr5ssoX$wMRVgi8X}1dH29H9& z2KmiwY>Eq>WhRLPo8DjH^Ud-bt^DsLeY$~`xyU8h9!B?{$<4(U{@UkY3OMQD<>l-f zq%1ms9Bs&uqSO78Z++-ZR$Hg&Wi`+EvChkd`kubrm;CfRio+ovoU=}agr8EeNuvZZ zxWA15CzC7KM<&|BFMsO1{K(JgLMJOS6b*i(c%hkbH#y%dF;Zqc(#%wfezh-<*KH7; z9^sMObxh~56gz=eGMWbu!BE)rmWWG3z&$(}7K{DcW;$djb}-Ww z>gl)hl6|UJ(w)%q{3?=7{@=)u~u>nZs z=EjD+U4}Pw%H1``xeWPxcW3usLIp~AlogrIJ|v~R5~SyVtQTK@TxOVe*YaFINFA~^ z2PzgMhh!B~Lp_Xiv5ksB+`Ph#?U7q}cs+Mj5_8zwxUz)3IDG%l`(hQFo)Lcd<7dfY z);Xq4=OrWam476utCcr1h7G}BhcX}-c?pRfZ99%s2nq~+83IB`i>QE9{+B^a zxF2b%4Im1ve`jz~6rBBM#o!SZKU9B$-9%0Afz;dpU1txT88jWP=ZDn~ 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�#=k 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

    A`g=<%A(@%#L& zerAZV4w4u!c$q7jK|he`Q(LV4i|VpJ)&f!&HscqOG|h5CvY)Mipr zH@0)QTiS*yLuYj7_1n}iFfI`Aj)T+2bl}Xe>$m?4dMgX7L#JUQYEBw90+5oN4?<~P z_>uf`EVH*EKg7O*KsW{^FKPm#7-PVFD@rlpI)%cURiaR_qiANHRE~O^*WZvsbP)b~ zXYF9ZSQTk8h_m`~kBVP*dfXu#}i~Em>qx^l6sa%f+o!-@RmtI!p ze{?~>Axg+h&;WPkR2{iiONHAQp_WkW$bKuMsZO7bZT{29j>aoc{O=fdJ6jj7Ofv1S zyhQe7IFL9Rt%cL-yoCMQvt>;b_*`?auX-KAFJR z@F%lRLtI}oek(Znleo3TncfX?@MP@%Z>P0f^*;Fehp36LPMyd3jrox@V3E83>j3W} z;zw?#g_HZlgwi<|HS+hGQAa(rhMb+_3{9;Emm9En#D$!ln;2qjyvw|{Ip9PfbpEM0 zE7RRuIh(i+(AGw0u3VctTj1>2MtT1@_&wyHztMF!Xg$4Ot-&)V!T&_IU(?ss~L`U4`FVv$% z?4l4|pBSg+t&qi6BH>$My8EY`ku%p%f6-h*w}0Ukmk@HW4^}rvpC$h#0Lo6mEy^Mh z9tB+TIV+A31#{ult6NcjzSC%d3JB6gmi@ngrwL;*wsJ5!^KZCwg%ha89i0sW;J0Iy z&Yk)r0sVPwqL!~(wD0BI5S~m`j*1KP!~j7{9WT<2_?H(C^EL`@4{Pb@ zkV8QxS_*23EdrZPotut$AhIqg$!+z`%R`7IzlC?hW-*s<-p~Iem;{>}W71%Aqi!4? zQdC9K`x8km08eD^Y2S-%K2b#JRtfyqFmsT}0KQ)L3uF7JE)#=90J#zqRt4 zIAzZ<+2IiQk$8nZU_(&1~;p zfuLIo?kyRsNUK)r41 zHAIi9V?#Re_bF|-zWvC?yk|SWTuk6{L0@)8E3Koh0NV#K1#)>$2cV;EbkTOgNF0H9 zQ~y~O8D=eHsfy%?HF5+#R))-+J3{g3SDyX)XiwI5ygno0ah~Q^(Qxfs87isK2b+iM z|9qT>r6=%xc~+$zsFknvZPSTw0bV84xhvJ%N`klk} z|6DJ$7`}eS4j}mtI(5%hPDkg$kEpu!?q%~|m6+(PeQ)T;2|uW&rOs!8NyU4H(Rv9! zo_lTB)Obsb-UCY?MU_U1<5ix5xH#4*&vqAu=9~a}{bF0-ho0g{hwd z-GMyeUM(xrzNpYWJ0+a(4V%CpR%H&ZjxI*2V?2p_Hjc0I7qaA6h4S0gJZ>&z}xgiF9HRSM-2$*6Us$!B|46~_E);Frfb8U6;yTnT-l8-P^44df5{`S;aU zvyIR!-pvU87NVT>!F+|}!ps!xBB~igeevxJ3)k$+9)d{*M7DM&^o!{KC_3{%rvE>V z&k-Xr5|YX^Med^zIakS9gjjP^tj#%c(^pL7%6%jia*UEWHdn}fACoahZp*|%?%(J4 z|F*yO`F!4=*X#LwJcK~A1t2bW&9=wxD2k6rCenhbj*RWB6S!j;8BWeE*yX&N{6PKl z%}@3eJ^riJg>9d>^;_Okr3=20`!0OJ{ipCzSOElaN2P0LW&$OUQiZ$sem8KQ@>t^n zeQFT%v0rt^VO(KThS+e(OLfRw()O@Sbg3nN30}|e zd@LEo%u4FF%sug9#iK2e+&}Rgr(((cXT8Pbb|VB@51hU$rRHTsFDWe4Jo6t> zO}q3}BJq}@ykdzLr(k$)@lEu7Huk4F^4emI*rg|^k?C!Bt-9?W_i?{{JFF?OuIx zDJmkSOiJnUPV|W1?$UVZzTI*9NpILz-XTTqq}#40Z9O(YYe{4#c)#6L^Pjy)J0RoG zI@q6^V*qn(Uxu${LpiyeMWc|@UO zWkK={0zc_}yiWq)rtNR}|LpasU#$=M`%DVK<)D^3UPuc*F0Kee+Y0dQedh_=BJ55N z4Qn1B?u|^+NQ~B+(6!0rR@2bGAEzmAuMOBX1x7^G0P9OjYB05bUNwBAZ_ZVQ)&W%5 z|Mpeb(_$?qEw8eD*sCu@^0n@Ut~EqQj|kcYLtjSbj}cS$m9)0sjMNAJ`_A4(OAp%{ zSnQ%;*3DHNIAWb-J5e<*SH z^YKx=os&jbR7gf_*t^cBJgyy`wo$)iY9257rT_!Id-djSBSCp#*g!_S@R4C%U7hu~ z%%?#^7Y84GH4lcPJ)YntWPCumb@qc(w;Po#;jP&hedHixIQt$1-6Ezb{0JyMXno** zj>A2E+FZzeGLYqd#2~@n;;j&Gaq6(m#b{{c1C&3Yef{LCNu7BzlV+xujJ0;`EsBnI z6B({Wq`6oxxz#u=kfrt)Q)boLDc!BDn@gMK8~7bRov#Ie=?W;&m28{f{440NFQ0x- z7bRZ!=g>6MPotb>%IWV(@wvpt!ak11yWccA`|*m-1SME=eJ3d9Ygs`RemB$y%ffxP z*%jx!GHG)Tn^$Cmc(+A6U|($v3nuiJmT=F1?lZ8Pyg4ovp?P z2#ptn|5N=9xqaV{hO$1d*7ia(mq}!>!f%}a-<)r^0yE#|MJT%9mJrQPrFCw{AQ-xy zTK@Fa_(0xC)ZXTM^y z^{y>RB!N-11_DG(p~59el)}$zW<*~zkS?Q$B*JzqtH{dE>zs6IO2!9PwqoLW-Q+17 zt8x5us}YQC|2&3W+eNL&VX&O79hM#kdMGhzpfA$a{8RsHv=HQ3=D2F)O%G+b%*F4y zUbs5!DO%@O6rm8>-Xk@Qq(a+qxY<#}nfu)~u&cWmjD4Bq$4~Tjda|x$U*D|Qu?y5! ziNzC5FDn6I=H+?kECtN_omh*eV@XP07FT@N-#I<7N^Z7$?XFGoaJlGBFQEo5u!5Ja z8ZCW6Be?RjITd)BW?xY<&DNG$Ubsc@T~W>dNCe)A4HYFa5KkT0yuJ|6-ODS?Eh!-t z^KH76J$H>@5zX8s-(oRM-L{X&}4x%4QSQn&C zvVWdP4Z*uK<=L#dr|HUv(;HF+m?P{kS4RnN=L?+0_;4AC5bAx@s@g*A<3M3{&;u(a zl5{B3)DzyXJ49JpsK0i;Eh$rs_@(xIcY4W8BC}@5Znlw89A+@k^r68z8B2LnAQ%CP zbU~`>qtIIPa8K)WNHX>YLE}f|&DYWZ&?WYj1g^Z{?8D+4?I4i-7xd(h+Bv$QTowSt zZg|}LeUz%MbQTJ+{S3E|%KxJ0c7ZqIilPI1!cT)Uz0x0CB@1k>XN=K7Sw4!`dvXMm zRxpVBa{<}bDNcWwaK|zJ5s&~A`*&yc4j6iM17F=PNi4mI!hcH4w(i!Ec&y?o?*sGK z!!`+(%XZ&3KW#l&Kz_}oJLUfL>WzjMPhtwP`E)1NRJrt>KTpe@@yAI%r$yn?|7K@0 z4<<505LW9li@(D`gCtvthZ0k1XTavK5!QMl`v9$3_uFd)vAIQ|(LUTMq9G@^Ga?;* z9(a;~&VUGYU?!o&zh-KjwF>PYq#`l^0_?2N4=^PBP8ETyT(FSNpZ6b0u`)|dB7YQr z>wN4dNJfDLs=6T22HnXOuGVc?gPt&phv_`-tturIvwC6uo2EFcEaI&Rd=E@otMUDp z?#u46TV2y#N5Uc2x}Wi%)mhqOj@sHYyG#YW+P~ zjz7t%I9>hO_&B`soog(x`)a*3VQZ0w)Js>Otej?R;)Ca=zV{B$&Y#&C=@%KjpbB9J zP1>AM__!9lzp^|{3OG^GB!&uv%^wY<%Qbq0U>Yv%)bwoHLOpdyGz&{-0O<9gdH>nh zXCw*3ON`;(=#-uD5lbsLhQQO>)}r4ilJ20AyD{K1(Jq3Y=-Z|{NCL)4V$e3x3QY!f zo2tJxkAA;JtMll^yZvrZA4<{HSDYEpVw?_JKcUb3Q#l?u>2EqX2;I0G=(XEYWfJ{g ziK%3P*Z$wP33<0e_b-I)$%QVHW^NBGqh;6J-GPC+Rn5W7o_gr2uja`@v=#vJ{Ef0hf{`F$>kb$|yt-gK?Wb1$hv@A`LtJP{IX!pO}8B^$E|2xQZfeb@Q zg?5!0V1})=TX~(L8XqEsSdA`iHG7@9xZHTqx1lxO#Mt=fq;;gE#aMY$(Mt>AazhCC ze#UUEtVtHk2fu577-SkyA67eFNNP=SvPoBaGt!f8_q#pBC_ly0pbL5?+Cj5|CLWRVCygTKZ@X6Q|z7tdY)78>QBR`PQnusEqjKZ#&1o zSHDG%C_vbgtavK?16uU)#k-pH^6I?>5b4OlHr|yBU+DIv|MY1(w-wKB2fq`Sw?E+V z{8&u<7%BhhF>12=<|j9q(_$b#bxV|!0c_Y{)t#GqE{gieIDu!EbQF;>P&86CSs=6& zFehg%A==AU{)3DrX^O=6@ba1oL%Q%4lLkUNq|LFTqeMkfI3EP|VSZJ$(4lk^bE}Qt zE_l;1gAJ}R@bqpH&S~1ZW(i5)^eVmehD4f2>PtogTY*YXh zyW-M)R*g7D4SKGqf2xRmHBf`G)kbcRC>OUuE*L94@M zaap`u#e?m+;K2R%3-*>Zd`>A6E>zgC#(?yY@%Qti@`iCSz02Eikqv6Krt`I_e9UJJ zCtkas;@7d1fiOdjM{H}h=jI%UL|aOx!|tzEo|E#X&Frwh%e13wVQ$|xe_LUCWXl?= z4sI&GQJFtNcq~>NRnHuJ3;R2uQ8x0iW?JY4t)Dw)2=`qlC|zjcaClZ;Tl7=;ZGF#i z$Z$1Y@(ukWQ_|PxKd8~r-9r4BSJs+k9})WD%IPnk%3UeVw+lwAU(S6T>67PFz@Yf|{W^%g0KG6&jwfq2dQ;hx)QYqg^0vcmH4m(NTd zGT)=&vm-u&v$Jn3HtUy_<5MIYMa3Rh8)t~O7a|c8)r!VJ>)ZU`J0pZon3BO7(oRp zHfP8)O?~*ivFdv1S!)`!!}bD*lb2nA7CxH&HPPlNKlaprLTyaO7F1ox#{2Sg-rU{0 zIymRd0l_{AcBU_6E{>OAVGIGR#H=5OyudtWd0tM|c&*SL=_rPE8mHW8o67y_Ap^D( z6*KA7CJ59iV?kAtr(9DoeaIk{_SiJCTv?JCKSURDwVY6CUm?KE5gjlo;tTEym;DOa zojzyISz@fqHjcHnZV6Snm~fBd;kse_=iPqkH4^{`3$yh24TrrJz_E0-?!j@b^3|1U zP>_65-4!p?ZE(bY?mUvC6!5d0_Tksq8Zuz#^}oUM5YjbTrozv}5=?8z^NMUPCQyiq z>TdxtrJbzptTsqrFh`BijKwGY)X@_m`r&h5DL~XN_@Z1faGVjjq2^ZH$GTKA5ai-k zB2&YS6TZzJxIVOfG5F@jd~GQn^7?^x{oOfv z$n%{X)p)J|pek2TUykDZZk)u-?&~AqMnK|*xY?q>&2gPCBQfQLNp;(I#qb!ILi_LO z<`+^@U&BlFy**qr9G@FAr6yIAnGH8?7jV(>H?@^m*&d;#zenI6WPBgWXW6kobAJ)%^fx?_+4e~yDAO#xm=(*Z9T9VP8sYA2WIBqbF1iq;gr5h6?Vx zrUwbL2o>h^GsNJ~ND2_RKOF&epuNWFsGgbE$jgxQ^!|cI%5{RVJ(5=>4C!dRs~RgD zW5e-6%B7bjQOOK_=AwHA`NkV312=@FbNyqfs%)I-FH86C`#1zj;q`gh9ybwx!e|C; z@#~nk?b4B|1+Cojb-Y!+9Kx<9FYus?`c&&NI^g8!a{0EZ)(HoXiu(Dn8q7rn)$q2t z`O6`H7V!R>2ZtEpQ$x0wO-=JOM(7AU8mSAp5wdysO+HWf6|-hI#vh z)J6{WGoo;YnG?!w8gI@5_cZg~k-cMRe5)!PmbLuW?cDjsospTt-IG?WUAg0m;{v<= zRJ&E=Ws?pU>zM#XZo%_O#k2Q#ulJ-y2mega`Zoszd2crmDINr|f7Dj5Ow-82zpLNy z7)xOuhi(>Lmp3p?boafK5zjFuU;|cn|HjjT?KK!rgX?DV-4X`4X0{hx_}+ zBYj3raTRt`z});KUT{Dw=;WJehy0n=KU+U&tz(g3FJW@Di>!-LGUE$?|F&(ojx_(3 z65aWI(8eQrHPP%=?j@r2>&z|y(()0aq}UX?$v0T&wo4~%GzRW%=|X+vN9b+4{&F*W z-RdI_yYrWi{%ry~>GDe2NR%jlp7%u-gm3^ zM@Ij$57*|}+Mo?dE7uGs4VNw~r1p+c@jG53WJ^b*HkdRPc`ih_eeZ=VLC7IlBEneR zr;K7FGcV6)D2eFb^VL-B9_zzOp*3@Nt%7_|1%+M`Jy}sY2o_ddS^Q_4?$twimOzsv z=F?sE{c20@xsX?%Q`Wzzr=U+O$W1=SsJRMEr)@7SZMvytQHau_4#Hc+=Y_(KuahP# z9g;g+_(*>8a@Y$+yfFzpJkUrXC&)uaz{DVJd)rthP-6yI1Oxn93p9S*(uUO7n7QmE zdG0|Jzf-acwalM;b<3m*1J{S+X{v>lRckUhNHR*PBWGmQcd3~h1~Z z?a$HK8}lOfsbeFtE^zNx0J!o3&@OM1#v|ig<17I%N6I|)h^lO4^lKKW{?eGV8GH-b zACKP-u&(gnUa>VmEE$Uxs>;7&Y>DUd&@au5p*=aE>b1w6`^%phbO==O2X7?5|CqdlPMzccz1}I%d^) z#6a!8()zo59NQSQi|_cTR{Iu(ZaCA*-|B07xnA-t6Oye#yP0OGE6VRUOBNQZw$TNb z+-<>uK!xDxsqeg%3x#7-dR&noC?eX%=g&90<6D?kKh#*Fb9L^a!(tbo00jx^a_)n@?i7SShY)xp5mD3a2DTL_R1yrU{zhV=XHYow=baZ z3!u+8-vFvrqzV+jk_=KRM>Ebg9=+Mvu|yOH{LCCTyhy&NJ3E~! z32{IhEP$KaL_VcO%>vc^sb1~Of-)5NE9ulPd>U1`noK-@X;8F z>3x3Y4q4g&Z2g4Ey2=Xl9Bl|?N^S#jmrxMqf2pcj|6%cXh*^JKS|F6kf44dn^6kIs zX^8$~m}lvmSL5gljxEjjou0)To)2!+YJ~9yDuH6>DnCAl#hHMFb;}4}zZ9oc-Uml6 zaJ#kxu$M^ga8S$8)OY^#^{uX{=j5Y$vWzVBW>3Fb{Z%JX@QL5azy%@nk=4x|c9$D8 zn)Mp|wZf1if}=~eDJ4J(wa_Vs-*r>jDeUo!;*PrgWAoSh>Fyl8>jlR1gT^s(7%eg4 zlyjiL1U0>ql{?_)8swh-;$QCQ)9P0rE62TR%ZI=Vf+kfT7-Vau+L0;0FGg zh%Wf+uC4n8#vnqE?Jkop>>2(PhkFR3Cc_&%Z16^sKz{r(3*kX;Zo!2km(8Li**Tl5 zFi*WXpuG$c@j}TO3PF%V2ly6&ka5NQ8R1Fe-je1*aTTGaA4VvbOu&6Eq0^Tx&VJ!p zEVqe_(r_6vD|Ptsf_N~iuA)Xe$~$^Lx*kNSj@~`_(8FZS9liRbCPX8palQOf*!~b> z;9b-4PEF&{)}G+YKi@kp-wxQX9`mjd0qEa~pMvXwe$?K-728|AkTAe7&{tck%(?zEP~!wBMo%&F4i`4| z-k!7p>sk->+<~Q?>7Wgblizi$^uFHk=F7KLxY zsX)niaB5@Ya#W6YKR!)pxF|1V2iWgUHHAJVucI~^Q42kLtPNqb$(c?LO!;oYrH1X+ z%f~B0K;61&spjNYAz&&4M&O4#J4>5yJ0>lgcJodIXO77=s0!ehe#p1i%mfD64$y4U zT#nkun?i|U0LrO*nXx+Od&z&KZ|vOJu)jkc2W}k-wfjBea^%HBrI}+Bxf?}?dxCQB z8a92c`^i03GYz33oFeUR;Fh92ShD8P-?y#GVn4i$fCJc4-U(W3_vGkJ!f{*JMpzXo zD|FJoQT_5V9^L2J9U#&kknPjmqv3DiNFmF#d-?<+43(^0rzMjF$LR{MMhL(^@hZZC4VOGc9pqBXd!hle?fFJzT2 zI!GfF);ocVFfsZ*%KT+@nwNevdg_ zzpt1>zRPr>*Ruv>G7Mx-FGvaJ_o3S}>2izCYAQESJ+f zIqdqTvcw2znq)Gsc$2Y=hT1bFgZPHsWU=b6U}Dy#mALkNG)*S&cr@?uKc1O`nrpRT zA#A1|!|>&Su!ZS&jeC<3|Nc&d?TjDwSDaivnHoEpwfpzYv}yAozlrnMjmEZV{3`H$ zr8RTpv<+9+(4?iV306j-h`cS-Hv}RH{;>L-(`Q63R#$G5{m0qAO&d4}nDeN;%=&NKk^`41dv=EsgoAq7 ztZ@c@m*D%r{4j3FW&;Bjy3^ky9TDv?j&h@bC=nhX2ry7BeN4h&?Kc(0@sbdbDf7gun5|C?>ucDAK$XlV5!Y1W*n3PFu>1n zWUg#^i0c|!cWCqh85&l96JBbS9|ZnB*7m;RTEjhFEAz1opA9@gz?yhNv>&RrMwC|I zhCeIB4ZjypW@zxt&#o<{nOUw=&LrtO+nSKPAomMHq_U;V!XW?@*49i!c9GK`7l9zb z@;Rsx_hF-0mKq34tZ1x&RLCiqUm`zZvYB^EPaiMfY-#S`x-@)Ul{L-A3Jd0oQrKOJ zER2E=23}bJ&LXmfw-!zd-y*Yl+HaSBHcLm!u)6A^G4H{nL8*y- zqes6=lU>xldUCG5`~`j4GxiCaDhdUw@)6Z-g;slrSr@Kc<7oa=zA_k8WVlPF5)Ga2 zcv^t?;Otji*=fcxuRw+s^YW=SWj8*_$Ma?!R5Qi6t0?gqbD>P;~Hly30d3<<+NlUo0@KLI)qdlC1m5VQoaLqlE5 zdpRSsD|VVd-VO_oTQ&TNh85c^Gl+S9Y*$5y+m+JkX*`_QUK^_`WtIHj7S1b@^qs5T zzYE~T7vONENkPKjN}!I#=(w-hW42D+picdz>hjY{XCoq;^%?bIj{09~-T}2f6`6Tu zjz5($A}?>g5gWTzy9{~oN5dnsHm}gdT0Df~u%y72nT!7$Bol4K0rl8sEeB1gYPXMH z4%ukvIPmy=c(OywJ9-7&Jof5lc2yb8ew*Ur%PQEMq_T+V!IgC{ye zD2WyTsHzzum!eY2yQ_U=OdN1^+uJ{S2EsO%!&b*i4^B2u4)YoU_vwg)nc7SDegyja z*Eg!=Bz*hiSxaPM>?x<;G6fV1(suPPw#g$!{vFY{!)cYq%^#&!_;M_YdYeNlh|vWs zjlcH@R^-)h{_FZ!ghE!`%>TD4m>0ZVx}jNr>$t_^gdAiSz=)nXnFfMS8z(Cr9vVS> zTb`k+ipyq86(I~78sD>bxJKV5Z2)9*DpK2;RyW4&uDAd=tHRbBDsI;kWA%pVcnqnu z^hot4(7HRO1lgVVgdLC1gfjX*OcMDY9drkIXlnGjO!9RQ1iss5(fy+_*xVZY*pd9S zMyPM7>NGX)(1T|je%kIOj9WOSzH;L8k_Sod@0KZL=Bk|T9chFe%B4pqjsh9$x@nUap3F-2zYD+}&>shkhmXrEJgUuf7b>e6tw(fs6%&$`oE>L}a@+L2 zG7z$zsrri*_7nW61vqXdUW3m@?*$t{6|c>PCeh_NPGe}&3WkZ;%*=ut#(Xa$jcaW8 zus;q%)Uj*1i80&&O2)^73?Ovs&)%{$!9CO?10?Y;G|AP|7WQ!c6XaSkK2P{{r zDZp?-*bG=UsJOEJ^2(|@cS@{=5pby^DaNEH*->Mp_988`}Hv!Q=#L1@J_tKgs@6MmEOWI#8!r@^y=E$esL1H)6I8tUT}?iZ#jI%LF)|1jf;kSZ zFoRxEgp~|&u2hSMpHhVNW(`9Fr4-oivHPceDM!5)T$75-IMZvBppX&J?IoFQ^|sTw ze&?0i*~jZQ3V^XG-L_##A_Dv>h(J00@l%e0OyRA#k!tCOa2=9Oe1H^IL`f7g+1}cIPl-FenSoK|yHI}>44cQ-&pBb#2g2nyEo?}6H!qphA!606c{{Q7mi5xw1y~mQblxL6?%Tz@Ht) z)M5DUO$NkX^D;23`anF({P_Yi^z28sdyX3d-YbNe20*9r?Imtqn5+ssS)%@Ta`mMR-a$7e_mQ3LHl^km>} z#;0`77N_nd6Mc8fZUH@MmsALoM!PfECJ5|ZEQ(U$y&BHeZx06OFq5ZlOSSY6Bem~W zmS8s|7tz*zQ$uef6P;}>fvOdD97ya?!5N)3L^V%=@7gZK$*gCHe*Wxm*BCyL`WkXo zp}Zfu6<3nO3WnaVjgV?7bJva3)px{a`Td$* zp&WWU-K&Chn5l@eF<{=ea&cb59fsURs zwNgltDU36YMM2)RKY1+{D02>!`E@kS{8w{NC13uRCoS==WYR0x`zU@~u+*JM6dsO~ zi1^tIY92aOu*(gWHi{A*1yc4P!6a9AB-3EMov1H<*DHfaz)C6Tm!dnJ@C4LlZ@+Je zzla#Y2ZM?1>wR32i@xu8Mm~kK&wbq`PyV-yHcU{CTZ|oO^hVo-xt>gi?RWj36&igK zueCW7w*5Wtj&q^1vxJYoj%&Io!#yv|?|K2Zo-42GUC%MgM-{s&WTppz1@fmcVFzP_ z(&7C*co1bI>B6>ZGalF<5a@gG`-ms3vb^V8hiQyGZGC6o2HwAHq3azU9UXAHA(+

    t%M1)uFLzk?BLRbhQNv2Ly%3TQiqbFMI=OPy=Je9aOtJI4-JdF8Kc=o~gVCXnFZbTJ}!jFk`5HJmv>+aaU14UcqPkX-H zIrM?uXe!fGHI~%OG8)H=!1KM#KI@1BmKR=~dcsRC4jfoyR+fIJ+S><+*vzl2@xLim z2bHqD-XU0vHP!n>OBm29}ejlx*XSb-3FTPH3eyg1s4zpqXxZg% zIRCfmMgrddY*Nl}Ww`<;?`f|ga`o2A(x%x>LW#TDBBM2cNVz>VgIxE>IP`rt)+9o= zM8lKKxOICK^Hf+*wcT@i>~(nP5U~C0TG(%^rY-;DwRxb_uNAto+3<8A-sSE`ue^k1 z#*v%A_lHORCrMiSK_~RE^?OCZ2mNvn!xDG+Za4plJe-c`Wg0UFbC1@)RRfQaqjK<) zd{AA zN=7JGLfEcV{`Zw5*O*XFJi5IeH0`QVn&}g-@b2nma&hnsf?^Z2p_Zrc?&M@wNiYfb z7-ihz@SuIF)cUVk6c*2vl)*d1Bps|jVPSDDl667@rT~JgGM8F*z2Zfntdpay;gaSk zDK=)P7<2P#m0Uo0v!~Oqr4Wg?ZZ%+k!yI=+w8Lj?p9`!Fo9iR%bl#-nZOS9WnP#}y zc|vBuLXLaI(3MnOe#u4K>{Fvy6LHfKS8L0wx(fyU**P*|8V?{jOwcLN$8xkf+LQ(` z=h>JQzR9chG9t`dKs+lAn084K84U&KTVh@I1?KBf@(go@O}S zW!;K2!fx^Nepqn;veB6War2NA^t7sh-bY>6gThd6?%)c>(~8WN3slCw#IC2Qj$xTJ zP%tq=I)#KtgX^zZdYAXSep#*)Gvp-^U~R<;=eBwrD3go(%i< zDR(TW$jAEjnW+9BEnrUyddJ^ia`8#GqMYu1MXZuP&uKW1Hm?GY{FQw2ImmaUcO@MZ z4pv*Hhw#&d{~aFXsoG7dv$Dp!JQ!bj-GaRE&>>u)JKXh5PLlj$B=1w!V$$6Q=c83S zrl>6rctVPqx7m%`sCT^s%0ldX?Sv9Uky1xgWb9w^9T@Q=M7y^q8IJox&f3xjNq<1z zo*J!%=~VqO>iUkN**9|xTfa#=dy5Rr7pgqrSF7b%^wMB=?8o1!Sx3HdO{4HPgG5t} z3^?C?%ec;Z>l7O#cH8w*OIaaZVf`kMwKmRBm?M_8i15OIx@HL`Cb>>Qq4pH?%9RMe zjr`0Brt>k1avk%oS?(ycb67#wNSvoASCOZ`zlrjIp;)1`XL-AKj$2%>QSzTB6@HHS zWGh;Gujk#hWYLISd+4WUhCboR-=f6tyHbc(;R)boFKJqR{0N8%h32IT%qu?@V%nZo zNGd%YlU+mE!Z`D=a{j>xV0t-3E((i&n3sP;tN)jYnH#DqdeUqh^fpCGg_*ppEn(7X7vmYKFcmGv!EI*VQ(vQ_)!sSG_kw>SF18z^ zOGTMKY%hVp@R*_eS9i_%cHZZFDyLycZWwrN|BYo|O>T4{8YNZ4>MAMjuRJ(?D_rk^ zL79M>INTizO-p{OSLTC_hjUQ5iq%=?h#J3ZkB1Lh^G;BgPfnlwp4kO@zInjrxk+Pz zw`28qY+V)dHieq@QhT`KM6gNejbQJO4|cY8m*@#O2B_#q?YN)D>Aal1bHsAm$Wzk| z+Zh>b?#&B-{51N-&w;MD%2(Wz&nvaXsAwy5FaDs7r(0PO%Ouk$CshjgKTXkeBAbp* zH~kwp?9w8%{CndOO#8SICO5T2XgH#)gCt_?ni@lV1L_*4rW=XbH`4)CNExyMQ%l`1Gm2WLen?kl0sg zhCyajPSP`LU<$7%83B`C3JGg6omWn6d6`d#7@6S#Ip(jFZxIg7iH7D3ii8((&G#$9 zq{Bf3fgF2I!Mt?_BbIJ7pFhPM{&lRoWzvl_dQKGfl-5Azb{O&dmSg$lC;h5@31NJZ z6`tK!)i1^8ZDMl)q8O8<2&n{r->d>aOjox@#K>svSQE@Rbr zV_;W*eLEN-q5ZC~mF|>(6}jAa{KiA{N4?h0m(ti+l?sfwg1Anb{2zIL52S~zznFK> zE`-ukn*_sVo@;s3BOhO&(HH=P57T{+(}Bl3M^rTJw38Q?M~}za;!f`7Ppb!hw&ClN9AMbAFF+V>0%<7>wNxEk$+?`Z zMBcaCoVexCcz3w~Q8uumg&3Ev`74k~S0CidLX~vv75*-em(8-fvX^VM88TEcfig!^`K;~k)ijne9VFKK3F1fEVGDw!pAZMs0JMBg) z>^sUA8V^>wc5gwcM1J(l0J|vrJ;63@ZWRj`U6$S$(goJFN~1~YL=$jI7E}9A*4zZ| z{$9!8#bKAe(|OEJFKw0M{06Q(Wtk#hVz72VmafU7M)ySIV+8(bbdK+r{UV zmXl4RGw(!B;9Dok<6mex=`iBIo^L0oo|Ppieu+*x3yxCLZnj25NZ=sv7x?+;ZJ!&M zL^;lGW%6{wjb)lMYW>eG$(yRVg}Ysj8oCWYMVjXy^*@*9N|SbF%Gd3BqUPmsyC{ye zXkL+vMIlr27U(6ryb~%bYRdhbXotS!IQhE>2S}lSYbB(3-c^x7wvlWNzV&A0Zjh3{ z7%PlbBk$W8y~p9;W(fF-m}nyC1L2+FP&FT4Oa_}grvpmHi!VO_$t(0d=^;+MfZ&cy zXq8Vhlm9%%$;|pNn=hBAEc~g#`L8JdSbI-Kjye8}n1O-GcM0mg^*X(ixonyP^3dBA zNYT8>eq-Z*LrE}MdF>-Mby2a%GZM(2JOR^IR#v5YF-=5(OuhM)C8 zk6np!SfR9*rErL35!zm^^qyB;a=8yLaVV55%JHsYi@-$am!t_XeZLZ32ibkF-@x7Y zMbrUtfiu@Bs-znp0edk$oMa72$}FeZbSL^`jROi;&ytW+>~Hj?TNBeQixkTkFF_JH z(Ul^mb5{eN1Dgn4=hII);R^R3@l2cY_$DnV9#G$F?N=O*j-RZDZPc8^pKOhvY=o`W zT-H26mETiT$cd>aRFL7>rPimc_Dx66d7u1g&p#WLWoP?DOXSkj3sMhum7*+IOh^Y1 zdj}+|-T-xs45e>-d$YFw@YmwVwS+VR_ayp$a2Cx~BI1!jW7vyYEs^Pf{ZDb(lcb>a zl7`!%RY$Ye!WIXPrNVaPj>C@Euk>uX`fG$x4O6-7Uh%e(Gm1p|s@5q4O z5o+e&K6RaXJm;CU?639W#N(Lkp%J#_OL=a>2v)rmrTqM+IbxirX>-nQkLTDOx>s!X zuXyGk!7ga)FxPL*v|JX1CSBC}M=?FQeUcmY7gI5Fyqc$WapRSzwd*}*5E{SWee#7p z?w(ZwsXT&@JR&c0X=mP5^>Po6>YMOa|6Zc|D3zV;(Y6kJeqm|I<)sIJFsdvtA)l1K-=9n;4 z@Q< zZzRHhkq#Q<*+f}VlY8q)1@ML)v+h;jKwpNlbpn3kftjec2^p)FBk$F(HY(yN!2wDu z_#n#Sx85?*=i_+hLOU;iJhq&st1Zqmxn5P)LqXK0D$Dj1r#Ob&CsitsNb)p{yCCJN zC&T5>Xg@Y#RAm$cFcvOkHw?JKd zMO^2JjvOU%j})RVI)5goF9JR13HMZu!be!!7r`e76(-}0JEq5*yHRq-4TAo;0mRzc zQB{nbEW)(a*QfU6`JeHozct6`u%(&3xu(BihxF|ryL-=CV(M!LaBbqRZizD20uFQ- zESYrETy7pb*U{>b(@weW^BO01ULM!g%iHzn{)(@L@d*FirEoALa^;c^2=iTSFEfEf z$aVR&2k)AD@24fTg62WGD^eog-s6VPnkz88wSIw?f-=F=o{)ALm!1TA#5tDFKM3>y zu>wpIxax8X&Fsz}u9gkTt5@I{bEK;nz2W}#h<9ppYE8P8<)`_gQC%PJ!P>ESGe!6_ zbBjJFH@x@-uU#OYl0N$g>K04oW4(I0USV zElZ;KCdl`FPMs`Dl*6g?Vj{frwnh#v<85*3!?nurk510_rQMKEJ(l9c#bJIt=9dj@ z5aORS7A&*xq@?r)qL5~M3$Dg667~~JckVr*P?`!ASKL*4@VS~o2@LQ$@6|rtT9=Rx z??+>i4fnFECy4-IO6?r`Q&&%`8_p(TOb`JoK^~z^Lkr*Aoo(wlauB&Euu7;ybX&tp z-8OPF43Z1UL2^w`pSI4LnXyVYsaz_f@nJwPqY>AUG>g8Z^DP-a1{zmZMB)ZC|J}O6 z?#(+4_S~Sa=-huIW0*DRNQw3`$N{9wP%*aIa#vgLRea%-DSY+o&N}Dy+sn+CXhhCy zzeEdjw&r~Ke?5tswkbtrvb9&0ozA~v=*R)1txLZ@XW||q{MVfmAy%#Qq5r8c5x)yLTJ#&rxo)fqck+xNv zvt-7n{doM*U4bm9w*}r_BsRaMDy^QE^AyN+DC#q)EpJ;C`q0I4bRW;y@|MY9N^> zD^t*$@Uos{Lp2d@6%`fzp9kr(G;AiI5lBgMxp?y{6K=R#a;8$cb&cu&O?Q4 z3YeQTH_30PQJMC#W)NGA5T7{MBDvTKlJa9Dr!MwHo80DVDB1`Bdc?3sG$_a_N+f%3OH5eg6Hch6K?DK zn7utZT_1Y1vNumUIG`>$3Ikj$x+(7KC=kUR`gkqdi}By(u&|(z?-6mHAt6dBn+#wx z{qO9|zSc?hNsZRY#_?j_(NWm@zUX_TRZMwJ1tw1`NbwAd>)z(QJS18UNR$6N91(O; zDl013)r=1yHLNsfh5mJJ((1<^18&dhAliD-zzlIxYYKQ3Km2!Vd2VON0_Eq0wFtVc znIl`_|28hKAz<(A%;BF>AQ`vvHevk7)X{~ejSD^AhYRxq#OkM}@5U%(tgG<5+hOE` zsjC2qM3`LleNtYX?y+2V~)Wl_h zvrPaHsafZimz9$(BiWRp?r=FPN8cK-vKnUucaKQ)R@jCn^{8^6*D&d5-TruQ$*;Qa zGS6v_c<~wv2lNaJFFAmzP_(xf`6g-fk$z(0x5qR9CYc=#bePJp5>AqJ)me`NJ*e33 zJ1kL9pfs--XM?c+?0&uwi!``2$`9)0mzVWVj~?lTA-nushpZAdw56y~rwg+h!9lfxXs#GJ}0gwON+ z{ptJs6Sl|pc;2u3b=|HO*I{(v=9I`A;LCCCzQi%uCcU%z=jR3ZgB!X0sJ;_nQ0gP1 zz|5QdoIusfP7<)?dNL>2D?BGyRnUB(FT%u{qoQ{92(2$2#?25A7UpPXD0*>+AU*0) zW4{u?fKp4R`vr2Bpb^-D>>rw_h(EO0P|Xm5$$ld=1^`MHW^uW(hQM%p%U+^JN!|zx zmt{LrHAiQEF@0vu&lBD5FgGpjnusR<{-QER z11>V};O_CuzI!{udQGYGp%9>G9$T44Yzk-5%9CY599^f2DZt%slo?rsMEq zETOZmM*_aPIyimTfGuR`@*bgfBlBPTS^wPe+!^=T8tdNSM2x#N>t$~(9j$5`9H%xl z+ek&p@K)l8GfQs}oMv}tpsdkd-Lq@vDP7{HnUSJ!#A962c~1E>486+zKe9Sw*B@XU&G4oXGjSSwoDkMM`Y5 z?Lf{&|EqU>my&cTl@DoKn$82B53x=rb(uBpMHHnt=arM5s6w8?E+@*08{lfZb-RHh zaYZXn1&4q3LO&EGyfWn-bh(txg~kk()W_Wc$q_>s#!$k`^idDBbR?p(MYR1+yCG&DRJ9gpWXX>L(E% zEfM1y{oB3r*0JN>I3LC7Ak$|Md=#${QefCkH!eG^I#L;8$U&oY-*V&Wo4pbVYru#N z+>Js$*L&vHJWDbZd~kV%X_M{8v7!EoP-Z;nLv@DKZAX?8y}wZgTo1!68=wZQcxZbRlIV@RX-Evj@dJt0mMV`uo z1T`aHJf9Brorff&Q{ENWeU7aH#CVg*C|8!ZW1IDrNrFnUEZI9kpUJJfi%IY=Ic3hw zXXu?hJ2=kFu)0uRc`#D-c@LMeF*6ir>tS|KtC3ym%a+kA#lQKKAB2Bs;$qL3l?S5N zwyA5q1cI8IzqHG7FH*d$x9f|;tGV47D+~NNd-*!IN&M5-YhrLbJ^*WDPf=q-yz1@3 zs<$0vXicd)$;un43LZ$7q0gUh_LXd?PCEKD{^_)Fh3%3ZwII;t&9+s}sBaWIG{oKv z9jBGkTP1zblTr74&_yl;hYx=L;h+9=8UgBQz#ost=?V_5@*izoxocHt70jhP>~rUwcaP6|#>fuuV@P808p0evk6dhSS=^ z?!xchfSYl+sTwhgIQ4tg8cB-=Fe1AWCCE`ZW6>n9UzGgU)%CJ;P;^P`BuM+dp8l5Ch`j>DNefTurssV6`eqh-^rIN;=j(&<=fpgeDiwq zOPnBI2CmN7?#nOs+Gky)%zpa5*DarWJUh1%C>27{f2c(c?}RE27xuX4jNqsUdryw) z>b~jYu-UWsm(Mo0V$LRaR*IufudurJS1mog#MUtr(?%TlCq_fDMH&%-YWrO*g1Ow&i){|}4*#Az9`Dt) zoe96571-=N*(g5#-RJl(^K|U&55N+!JUC7<`#V4)iTgQhmXpMVq9Z>{SK=e^eor>8 z0>iU!J1`!dmjUWf`1gr}K9c!x*WQiO)9?Z#Lm&u#49vWOno$Tncb?dPi#yZ$PBF2A@b~Xxrl6zdu)Fd)pq8^@g_lV{$U0^_L8$$B_Sn-i zKO|~pquCMA4Bl(nuPf^Hrb}iDMen-C{hd#!Ya|T!x&L@I8e<}9Z|~3%zIV%(;GK(Y z;>LLKH!!iLM3n@`o*lVFh1g9DQa(+2hWe+s;^zLXS+0!Ifo1$0+fUYcTW&7-#n8l0 zS2g!!{w;U@QHlY2z~W`Q@y)7rhL=L@D>jl11X+M%nzViLyRndA!W5rIsq}QIt4_(B z{=F-kRyrCIes``i6>I1Y89clBtt<(Ki|$5cG5CoDbrDsx<2ai zMp=u$e|XFE%sc*RwN}PQ>X>j(lTQ5u@mN43d_6^$I^;Kh?DI+ibhIk#*Y}2xPhIpI znKH)G0Nfu`mbw*j?CZ;L* z&4PZKCgW>~8!ZYU<0RQ4GA|VDg6gUp)9z&E9R5IRN(GsC<%w-k z)bqmgGbMAgcPJFfr)77f#od?sUq0E`dSCwh%^r_|-h?P#{!Hp|zNeGmcIO_#Yv{+# z`)1uI1ELE@N23QFzh7yd+{tx3!wH4gB?j-T^F}58N=b?FhHh*gMDI-QO~gRH@)iT8gA{EHcUcixR1yyDEIHiJrn=4t`chH@ z_(H06q7g5~1=VF@YKOJZMy%)K5W=B6Dw?k%#4v_0O}!8-6$z3^RaMkH1fR zC7hptP5ajyXPV0}4_fl=J~xoc(*^6F!B1qH!@Ow5-O%c42n56zF{JN875&gkCd-6! z586<6`A0|0Y>?jSx%^bmXDXakp$qS3WxM$4pA5Oa*uyuMqgBk&@HH^hxYB#x-vpM~ z^EGM<|H_Qth5s{Nde%aif%iX~EIW(4H&*oe`$Z3&MW9jESbPVsw?v^_H{I&E(I`e7?#(A+;Fb+kwm2PcX%UFD2i|b-yNbgOWo=TOJ zdRpnGp*iU7I4B!W&snr^i#+`Ls@L88$u3ALif|NqleF{BL&Y=D)ZhQTtXSy#-U$0E z*O^7@d~S6GNptDHQ$p|rI^bVr+tL`Q3_QcZTvnp2cV17enI^mEgC$plb!?Hw(71#p z!Hf+3ufhL$grxiH&?S;&t0E@&K;Vpiy+=4t6NY$5buu*H%|b-m`TlqbljjObnA~*1 z%-~bce`*g@+;0Og+kymn5M3B84#G34yw}81#-G^$oX|1)EI7=_%qN|HY3fFWalZG) zuP&XE*T2fLPkMw`9(*prU1V5^|LBE7!a@p_7KbCnO(OIcIyz%{J~O{K56^Ed2U_}a zcvE>L+gz^@hC%DqJs$n;RypRS)$<`N{@d8wKbVFC1<2&zn7#&wK&qYczuT(R<3-w&&b80GKkN zxlBwMk@>xttNC}hrTyzeR)J7=30+kcI|HSbe}l2hoj>e1iZrIA2Y=(5XVtIKFqUBx zN=k!mMM5+hfS4G*>9l#NFC}_oFSt5=cWLz{8Oxhdk>d5EHl(o06?ZbQ_ja0iOjdvI z=p!S@!rGc`gGBQ?LI06$Gl328_}S9EGeDKzD>hlz-KT8+Bw_Y{9lF=ymS$|Gh3<_A zsvlCeVk(d)`%0ba#-lY8bvdo<$AqG$#ZRVRXeSuIn3K!18|Q`2-j{w;+YdX)ov>8O z6!O{uaALDafzN8jqxDkOUq~T@j!1$eaCJoPQTKW%6c2e}>LBg+Xr^WqZStnndrkga zn+mU;KcvFAl21{7ooVL|!dTt;qDBFPcb`z(*=cCAdXY))G^L)53Vou?_C7(Mx)C}E z_1kpxQM8PWbEq90fUX5dC$Q~=2DOHc?{$nz2Yz1oHvXx+c~R(4Bz8pe_r0^A zv*EL2&7=I{lfwv&8@Sd>kK@~^n6%|Fwr)y6ltB-$6LIKKg@b$BhFSbuDT4cbgK0G$ z*XTYvd+mfyEyHO9_#0k^AIj;2tEP(P#S_Op3*Q#+)Pg~jjVJe#d8#`!)qo{-YAIp3 zM>g_xQnEE%5wa1@e>>R92iG2Cs+Vm>b(eI1G^n=TMDrr)YKL6mSVNoJa&dUvz9`#3 z@@S}ih1%TOG%|sHn0#zPd%7@6<(RSYqY)T-U)(vrWuY`pn#s{*&R236+25Nwzh+1b`?oY<4ye~uAJ>vk#E*GA!ySqRBiq;1F#V{(W+w}})&`(qvmZkGjA z@ln;X`2|_-BRM@-x9+A2&zKALPalNAJ*?x2s3B=!f{-bllu^Q zSMd5&>%57rUtF_hvVPU+y;`^ z_!|{dQ0O4sRR*dQs=i5mXFj9XG^+5=Sfr?nq?blfvh!O!6|Kh4Hlu}KF@wInb|r~e zZJHC<@!@&x7nJsrFLEOnGZ6QcL~ou&CNzbHN)#f+X;pVeO=-{4yBR@I_O}wv1INn^ z6TLN>!CIjS2-FJ){x( ziOIEcsaa;mgc8zSLyvTK9~DM6m?;PbLGKd846@O-K9poR5G;;fYs=m&^?hxTXw^aQ zYH&ec00^=T0_jw@6DC8a9);IDKX)t8k3vb6T#*9k1rr40>F4?yvs>0JkCTH6+V@nK z;t!cRg}Y%3{!HeQRbi~74dEY|1JSkW16r3@v~cNOc{1S&s6K+lCV5feh&VEyg1_>Xw6iz-SBe2ky(_pV6g82JkFbqLF~dg z_ytxI4z_9^snLLZrNv7N?Gsod8X&G2;6=3KUStPeK_Z{ddvFO$RV_JK`UlH~ z@l*_SPZP?`;A}$$q0V{;_=8NTs%Gzn(f8KggF0IC)F~1rXqZ}HE*I7yjiv|p;F37S z#1PuS%`fx@Z5Tmdfw+9S1hBx@p-6K*BQMyS^1G-(*|8AtMdMYb`8%kcDl{)Ci%o4t z!Ae3jAffigG81*ktF{lJJN_ArPGo!-lKese)WTLri;UsPZ<77u+EhE#_^zf3*mAaz zTNMXaJWU@JB4M&=&??2xPalKjmY#HWRzP?9%M$PK4n6_QMo<^rRdcEy^!>YwbkC*d|VIr6W^j{vm&5SYbU3`!nkSD}4U6ZC1cF;F{Lfq<1fE9QC`**mr zQl2a^USOpzusIj8mEb!4ui>oyRZ$s98VvoA^&F&s+bFZ!`KMHQTxA+NT;X!S9j9Ta>`Cmw_M%X%LJq$(!0i6GAE~;*z0qzF8*+TK=5hc;$DjNe z5U*H_JlU<*Q@yrRKPcky}UmPOa{op+{2$P z&8YFOLa$E{^gQ*hs4i0MTLRydddsJom zv79o4*q!;!_N#1u6(#w$NJwdkULaTPa&}P8V$AG%lW|)S3-`nX?pog>P!~0@fw@Uv ztay3j^OJJVaQlftK)I$iy^9x)U;_C?=iO!~h&oSqJ_k_^PEY?e%jtIU=GjLV7us`F z((5EZJINzJ$Z;dJRbvGbbw8DWN>+Tneb5S9bhn< zOt;+`1|OtwiUR|_yj0(RP}0mtNPa%}e$?DXE!}Ros@Etf^ul(BgqYPtXi&87P|5kc~COU3HnUQ(r3=^-QcpPOmHCi1=|4>DpSQD(n=HFtj96mU-j!p(%?2 zd=;>nxeb|6qFTAlvFy^xf}6dcp#8qDHt=0Kx!kTq#-8psEUIa$tuqG5-$l)XF(m53 z<_-N>6Qa91xGNWdOqwEZgqqX%U|dTMN+RXKtx~(cF{g9Ky4;!}zmcwI@b8@0?m5m; zSTx>VTV5-SU+(MNp~e2}+%r0xjy=&lbv#MpiQN~9ssAUAq;c>(a-zPFZDXPbfHSvX zAA_X;*B}jL=ek~tV)WGi^Mub-%REZhHE>)_Girv1$n-HWMjj5L(03^056=? zn5sOd%g-k^6D9q*Hi%0WIG=%5*nDmS*PEo9Z}J&A|4yc+E-GL%DzX`~kTr>j)E>&y zVEg0%x%9+fEX@>+sdw`rH>pB|g$=es_!TJ+S-avw?E9Qy?B+nNgN`uL5DG>x4m~c+ zb)HH*lw|@pNfj=u!IfKdp^UfmkRdz_xC@EhF1`RH(~@pTCF&R9iofiA$TQP&v*~9# z`8zff4wfdOo(%K&YtZbVDx60N@E)_i3CigG6>kizoTY13v)Q49D|t5=RFxA;-5mc| zaBtw5l1!XNMDb!nl0%E<_?R)ozM6M@D)ok+h{M2gHYYdBd( zK0aapKa`v(tL3=dl0WN36Gpbk36OHEbo;oMJVDzJxAc7s0xFJOzRcr9O`M@5nwou| zO*#jnsRl`dhI^$rKz%1Y9u040`?RRbjnwQ^3E3GiEDzB|xMBqZOqLr{cbU7g092wvH5wgCV&# z#5#~S2QTK#vDvPx9Hhgfcg2j{(KPivk4M?a2HAni*n)%*>3i0ru7BF>}Gm;NC5+mhNQw*o{)G+=N2#1pZMCl;PYJ&Kn1j56L}MI&R@q+rS&608r+gEc0sMSftaJ_upT;v z(QfqpMjA)~%w_%B9oTtSzU0~CR{kw;;)@{PDWfmQ8b}vb?cH8AA2&t-Ha45&?~r3A z2BG)ZtsMMv3zZ0E{}ry5Q&O6}X48!MfRQt@UsLAV`Gqqy1bWipZUZXs7(Ek|V~2H^ zd?iQinZn-%AN}4s{<3(stJHDkdCGnELv#1u$)HT<+16(0M9J*_) zlt-S-5A2QakrrD8lbwGmIQWDaIm&;?4g@BoA3}`p?UorIdnKik&{X%Xxng=6iM)&3-4gwq0$3{bE z1Xo^?6_*<;eH4NA$PO(ri#)}}or-%JXY03kzbl)}oy>-H0BZP*Z|L0dW>``C-oIew zj&*#CzEw$rD-?#y+z z%;0_F6h_21_7V;>!w-*0p2ab6iO${Q*wvj8P4frBt%M%}CN)ssW~I49OlL?$v@Xv* zA)}H_UOu<_0ty88kL{j$8%ZA6G&=70kD>(bx*do>%rN+!#|p21zB=e>#` zi~b99?QOaA<3)COM&IJ6Pd8e4^J+rzUO%$Y52hnk?bQjYXBdz>sCb;w?$7H%J_v+A zk;h~c%~NRV;=m=QA|ME?1+=r?lm&DEOkdUG6J3^d;wNT+ydS73M0$4RyhLx!ksh$RAq`ZkhVV@X2nIzU4J< zaqA12#}Re7jnLfc6NO^*Qub)u(B^P2TNA}$LNN^JZ2VdFPzNyMM@mF!;ItgR10_v6 zUh-Iz2HUVSxl*~uii>{_S7egChD7mraycZ+kmb3zy7&e99n&q897}7n7&=kjjIE(n z{>Dua^Ut3JTO$UuxV^?4;(S$Ka=H`x&m4q%dl4;q$*z8W_z)*H*0}FrW=d0!+o4=`iAWjpbJ4DW9#{$xAfWA znQn7hENA zm+2CtoV2h4AM*6P&2Q8ByV&HYW&mU@0>?#R?Qq^ZmhuPXKJvbRGoC34H4)-$Fz+iU zX^=+>A*rO6n%>dYF)?UV!h=v|=&G8?=G%xwy-QRLrsZI}ZdV@2eT29LhBd{mUKaB< zp(y&TwOf9sSs+AM8P0*`RaT~xT_n^Rm<^xfX$%cAPHhYcG0I2Dv3pxcmemGvczLTd zROMnbfv#7RP2AFlDmiq*0a;mm>zNj6{?Sv8I1p@zWm8ZO)K&cjg4=iI!$15oNfCiJ zylcPydWoZ)&qNCxZw&p4ap6gQkMQ}zi@V8rV*}$gYCGB>o;@IvY{t@OE=kXrRmwa!ScsHPUiiZ3!@)4ZW-4r7z3Q>Y{vPAHiSf#y%t9 zn+WIO8Sb*?hNa{5m-Inxr7q+Vott!Y@`BIsCJ1&GJxR~No1`0UD{@xN&hopnQ1dus z9SYUk9;Rj3?a?v$n9_(0qUT~!(lZN#>p*klQvmCiM%{K|z9_u}5KhR~&-+RbqT{C< zp{k3zh?&XLcR`gnC8a7oJOtuzwJ5zp=tEi1mH3<#4hpd;0E<|O(5S^P%j<(B4ODZ# zvWs4&hruPGEh= z57L$NW}s%9v5MB{JzoB^yS7vO1i5p$bFW?VG%R*wR!7kQyy{Yk z1xYfVJ`VgeIVH*8+vnUA#>OrL8Z2bf)r^+|TnA$;S{)%59 zmohg!Z04(c;+o2Sj^S{3->6RdyguE+QZ3@O`h#i0ZW<+rgMMCZLi%HmH(}(WifuF;G2I7e_olck=#5@A-qE@dG{x3 zAq#_4MuMunu@Yn51Ko<#ABJ&ENqK^Tf)W5CxE6;SY-!~|OW|=Ltv@!`2nsvQiS&4%e{Z)%tw5;^?zMpQt=yM^Zphaec z;V*beZ>5pEpS}25QWBE3nx-R)%=r@U-(`=MBBu0E0Dr-|$rmX-&VJ6oo0AtMr%BAs ze0ro$>fbNwz{x_+{h4v*3Q}WjpKnKNOXUvS*h6DU%_q#y*AY-}`)wSf3O@%pR70z* zO{xka$Fmdj!iYX~7JvNJHt&YyB@>T)C0a(8-kJlrd9;>gLK!rWNNUy zPjr8Rn>F&>`PW?_b`~hpb);6MH~hQzwNVB&yC})!2i#B;s9Yb7M~01bqXDp+9?$D4 zYBmYr!M)r2lAWB*UsYQXz@PP9O(@CSMML0T#1&`2n(afRR9Zt2CWCS-cI0J+$6?X- zN%<+?bGg2ynk5z!a~G1C+A(ae%wCi=bFm}fTHe=Q@Jh(C(Fs^?F|$-yLusN`|&;G z!lLX@g-B(^hu3dg+22A_dfMKVxrx<7AuuYo6o(_~%!d+HH29NAmG&wPwRhDGB|Wb- zV#@7c!^T!DAPY}1);b@7ifimTan-F=#FYob+UujvKr->C?8UHws_`4I_%qrkjbecDo>_+Kh&}^L7V+!OiIJ`64t>`+;(bK{uPk47wC% zmmz-9NK~9_TpFDl;emFR+F8|0M5w@rWkQs`&axXMs&_YHfVO0vU>oi%$VxrpY5w@^ zZAomPQzO~R6a{1}%rnuV!~4&%vhTCo$iC&H57wp(5>qWkUYUxy?Q}GSewdV|T|xhW zh2R7Mt@JZ`rtLtH>20es=sGTh-5A|E{#gnQ#C(>A}Wnc-Lz1l(8+Vk|o4g3P}SCKP}JDHq*b>^)z1?i+(pK z$CQAoMB;9gF;U~xG7iJ-^ztC@&dU!E8h6EO>kpY3uHuT%J%y+8(7d~fUdp~GpyS|_ zOAEcJDWWyti2A$R{8KWU z`i@Lr8m#tdkUOPsq*%b@c*xz_gnQI?d~e(T$w51-cVYbc-pWDu`_`6SSJAo{Fe|* zbvy|=n{))y%D@Wx*1>Y{58oz8UD@LwjFX* z=c#R{HL>I5-*1AnW7OHnh+|C5hiM02X)74BZCrRzl#%}NVnVf{?1y#fW~?4&^Yia< z3I-sY&WFX)!fI=4HxE)Un9CEupK)}=;cw4kM2=Q4ZZkYOVp0IiJR~tdFTSW+JEH}!%&baEG9yojQh*oG*ptCb30|0$c%+}vz^+pPt7 zI_GNk8Vt^ZU%207M&kM+{uAR`!?S-=$iRnuNnfE9{jsy<$X|(&pj&X4p#Ll<}43@TrI(IDFz*GHyu_D9yY zgY0ue%d+#v=(LItz1Pn{ENDZIdjj~3cyD{C33@U}EdKJq?*Rf8^99>2Un;oO zO*=+;edO}LS^;dSKKxs4UQ4r>v3EJnr`guY#^hpv&6E_&R_tGmNGY68^1$o-2%Ha} zQf*|&&aY1^j*T5utf2vJibQf(vfQY1Z0Ab}2nzvUsM~V%s}K;gzIR7{E<0XN8OVD7 z4(I>u%%5_y5SKVHp_I70Hg1%uZ)W(W$4gq$XxR9H?d8wuT7#><^cQ9IdV!LaL@i!cqhZ}aCzlu%!Z#RgdqWtx~OVTpr)$~f> zhd{p1W>qdV?tkttW2zXv9*_fP7i6cX3cpf=bwzCW@m1d_qXTh$j`Q|iP!Uy*jeeg3 zkQ(-GTr$2RY*Qr{c?iUoouD7(N^^KHbNB7OxB1#P0+YrYE0$1S~prU0x z0fK#xDS^XC+d)~5#Pb)7@)S4NBqXa8p)lc%ru<|)9#Iwc+!THv?=RH%#v|?*7quww zmKl!L$V-Z<*(~o-u4-y{&3LY-Q0M6z>8q)*&nhp1HtAbhmzXx=vy@xCc!$Srb+r8o z(0L7qhWhM}JOF#a)I%C=wO>UTevaek6wNd_Ml9ics84$3rt1K;Wy~CnMzALpTzped z9rbgaJ=*?@a*^Xbz~;7+tV+jW7MEYJ^sZgpq&H4tjf|8@fE5Mtmf$1)HcmqH?8zIO zF7{l8bnO4LZu_%l_OHH^Vn~D0A=UK_4e_<0EVB!clCj3?L6tAx?@{BVX2`eG?F$)$ zq|gPk3Z@!%43SYRO#m@Il5UsIcGcil91igmpwT0n1}IN%Sv;I?0{GqS=DL0hI5W z{f)B3g=(@ohqIwIN$M&4@OndUw^aK$$ycq2%bA_SWf$u7ZU-*q`N|c!p{5TMzbQ_j z^x;UKbiH)+MY_tWloW=<3N-jds{L_&QTaw0F@MThos2PaX@;fG;usqG9Z-d^u z>ywKFMPoe#Ygn^V?gU`X){x$b66%!tG-;9?T|@)vMJXhanlD> z>)gE0u=rakNgv{26J@r@t=--${yP;GdvLSPAv#9&+DMT)EqXvrBkEw|Sg9_m5U4Yv zZP~1xc*Pd1aLUgLQOQHPoqHs|2WiL0$5N8IGr;tw9x2sT{N!kHV|+QREnc>3 zR2NG_pz!+B^7y%=2ed^~6V^L-03&)p0$^sfQ1iC3Rq$&n7q})Z-L$hR%=kjro;%pY zT(vLTkvRFgSnY#bM6L z3OjES0LE^{bSlUmhxFufF&siaOSxwBU#Sz*0lB88xS=2Zea*&pI{l^UBimGW9<=uu zfdf)zjEYA=MVa}iMy65R zgYJYPPnV7YSfg8_7JH}dHU$M0iD?heRd>QoBCJ;kQkjC9Wb)Abzt<}KnYlzG3tRFz z_P!E{#KMmmc}#DbIxg>=hBF0aF+qJL(zUWU4rWD65xQ2AhYS9)cv7Y8laq#HcjA)+ zq2oG=+O;~zwkJCuEjtGiV*exvB~R$MxBY;YVtrH=yuoDlP zd-SEoZwBi!sL1T+lUsALMb_VOipI5)ZZUW`9AA-;xI_VY@eJBnf*x$7(?L-%_=1O^ z{f*Q|TW~3}8L4fxd?)l1K@_h_m#tsfC4W;|B9mvBQmNKfyL

    JX} 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 z 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

    3z%L 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 z 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 T#hV)lL-+??xrX%6w;N5Kp*ox1>KbC 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 p62@}Kz_@%?S(wPDthX1uC3!?k0GGc6xq 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� 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

    }oMm~KY+yJ2V6i-2Y#$XYxik?m~I3=9D%f*@wy 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

    `oWG*Xqi%7G-5) z;NJJRUIfZaBA6%njcv5THnL3^Mxz4t3_2;zUP(I_;D#{ zH=R5>y1F5%e4rxYWVG*H9k209E^0EFuuo-GHX#Zpzr zl}^N2;KSA`d_RAGUBA~3cLfoPm{?>mwU|Hm8m7^8{zDPn5z|G)x)G6Mm@sFSI>)Xt zXIAgM|9?H5v34X&ZbTyhWcAF*lp|07|9yM3!|ntkWT4;Dp+`0kE32}Q5%&W6^ZU>H z=PzT$cliAMsq+L=zIK>A8Dx0P@*bkz{Wn;z=XCe&-rJqE#uYOSEAvD~-}hW^z&(l_ z{PZVw852uS0^)w&SvB3v2r(-D{`{>hBA;J1qwWdXj&H@nSjzDG@4wIMEO=)IS(I>| z!&-?!Q{paWX1`*&$k?YF9<|b^fZducnX>ct5C*T15{{3rOt@*s)9@np@ zD&1mO12fKvb)>w`>(e(5!<&=Q;-0cH;J^1%1eho4&YjkuKfmDU5jbv(duqW`Y2p2O zXYf2ve_EE|VD3L=pjW*<_tP_(z}R-4=l17h!5oNkRso*Y^XpkjZfoiscR%Na$lD(Z zKljbz!xGV-u9Lx-o+y$BBf-RIoF|P4jy_(zE#(Ol*xT_Gkl6I#CCt^h%-`V zswd#D$}bA?%wz@0dnYp(VQrNgKzP3aL`GqWhZ_NQg2txU=bx&LJc!7RM`nOrvczD* z(RTcEZpvigjQXG1drr55nfd;CXVDRJ-%maIuA0s`;v^3#f^EZi&YA9rScF~H;p!s- z7&Np7WmehO0>}cHdEaluf{8*M$_id=!Ua^(Z6%Q(}&AFz&r}WG3Q^r_P5m z(Xwr_Dxr|)JoULBh?$(hIXP`IP^Qp5VY5OT+Xo>AJu5DijX?x}Anq_Qj5KWd)L9JI zz|2hG&gCFZy2V=df6g?Rps*60EXal+Ah+%l16Ra%d=7xpYyp&jM&KgV0GW{MJYuJF z>5bqL>h0N8=sQykR-(PPQ3oPcq$$AwB0@`JVCMJf1lb4^iEUy*9FUHxk&MwbA~Nd~ zZueYeL_YcXdEAC$p zk2H(~V)}z5Wg^d$w+F<%pI5yMjQhkhY9_;RyP@4?VDt3quGi^1#NHKEUfn{)Iqv(JIZsJSV1z|>U)^GQRCPuZ z5hD{OF|#iW+Sd#TWlf^;S+^PjBYhYCG?*ts-(}4E^WGUJVS}3$sib)*kH37_(UnX(mxs1h{mf*@1yrzpDJrTW4 zdNQ&q0I~7CFpTeYpOGfL5x60!jjM9)2PCly5)zK!`~8lr*ntZ$W=d><0_#*{HV3*?aX{5tL#!5Bso z2}skDGlP+^Sb0CQ$<_P$Y@atG2rcNWp$G)hMg?Ab!vsb6^j}}kQU_z_bV)Huq9am>`7gB z$4Tu^-~n{ItIF#NAtDokLB#Cyu%+`PjG2kO5-aUBp$kN1_FWQ)Si8HUG=YGugl8hk zwkd97O{()Oxvqi{aa$QNnw~y&maIhvNPwdu6%4MZH0$>Ll?TLb3?zX9G6F%i_7{uEa6h>7SRY1p-&ph`#jNSp^72hHo>#VvJL5f{ zi)D9ZBpILn1hyP+iAnAh$zR+#GGlYtuivlF-%kYUc|PyY266~EXbq&yM{a{4zw_B( zkt5I&+;^Vm)Op?hV2q@{K$P0uT0{WyAd;T3iA-A&v}g}1ch;B;llEXA&$|TVNVcVM zFf?s~5y1*#Wux7fd;VJwDXWvuK+C1ro||@@NJ0%z$^QqqFL)?S5L>YT0000 -

    Forgot Password

    -
    -
    - - -
    - -
    -

    Remember your password? Login here

    +

    Forgot Password

    + {% if errors %} +
    +
      + {% for error in errors %} +
    • {{ error }}
    • + {% endfor %} +
    +
    + {% endif %} {% if message %} +
    {{ message }}
    + {% endif %} +
    +
    + + +
    + +
    +

    Remember your password? Login here

    d5F?3Q*%>n!I{tE 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 z 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�B8(~j=n~d)EAV-}hW;;Xc2F~fB6cdkj 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 z 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≫V93RbD@Ps3ur39n<<)OX?GJvZQ!@vrX7i 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

QgY`{D(15)LMU$1OaJbj+QA;^OGXR= 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΄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πWM;d>Bge$CcwW9`p*Yh!-YQh` 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

{% endblock %} diff --git a/templates/login.html b/templates/login.html index 6c2eb00..7279b8f 100644 --- a/templates/login.html +++ b/templates/login.html @@ -1,22 +1,34 @@ -{% extends "base.html" %} - -{% block title %}Login{% endblock %} - -{% block content %} +{% extends "base.html" %} {% block title %}Login{% endblock %} {% block content +%}
-

Login

-
-
- - -
-
- - -
- -
-

Don't have an account? Register here

-

Forgot password?

+

Login

+ {% if errors %} +
+
    + {% for error in errors %} +
  • {{ error }}
  • + {% endfor %} +
+
+ {% endif %} +
+
+ + +
+
+ + +
+ +
+

Don't have an account? Register here

+

Forgot password?

{% endblock %} diff --git a/templates/partials/sidebar_nav.html b/templates/partials/sidebar_nav.html index 8fec8b3..1980125 100644 --- a/templates/partials/sidebar_nav.html +++ b/templates/partials/sidebar_nav.html @@ -1,80 +1,60 @@ {% set dashboard_href = request.url_for('dashboard.home') if request else '/' %} -{% set projects_href = request.url_for('projects.project_list_page') if request else '/projects/ui' %} -{% set project_create_href = request.url_for('projects.create_project_form') if request else '/projects/create' %} - -{% set nav_groups = [ - { - "label": "Workspace", - "links": [ - {"href": dashboard_href, "label": "Dashboard", "match_prefix": "/"}, - {"href": projects_href, "label": "Projects", "match_prefix": "/projects"}, - {"href": project_create_href, "label": "New Project", "match_prefix": "/projects/create"}, - ], - }, - { - "label": "Insights", - "links": [ - {"href": "/ui/simulations", "label": "Simulations"}, - {"href": "/ui/reporting", "label": "Reporting"}, - ], - }, - { - "label": "Configuration", - "links": [ - { - "href": "/ui/settings", - "label": "Settings", - "children": [ - {"href": "/theme-settings", "label": "Themes"}, - {"href": "/ui/currencies", "label": "Currency Management"}, - ], - }, - ], - }, -] %} +{% set projects_href = request.url_for('projects.project_list_page') if request +else '/projects/ui' %} {% set project_create_href = +request.url_for('projects.create_project_form') if request else +'/projects/create' %} {% set login_href = request.url_for('auth.login_form') if +request else '/login' %} {% set register_href = +request.url_for('auth.register_form') if request else '/register' %} {% set +forgot_href = request.url_for('auth.password_reset_request_form') if request +else '/forgot-password' %} {% set nav_groups = [ { "label": "Workspace", +"links": [ {"href": dashboard_href, "label": "Dashboard", "match_prefix": "/"}, +{"href": projects_href, "label": "Projects", "match_prefix": "/projects"}, +{"href": project_create_href, "label": "New Project", "match_prefix": +"/projects/create"}, ], }, { "label": "Insights", "links": [ {"href": +"/ui/simulations", "label": "Simulations"}, {"href": "/ui/reporting", "label": +"Reporting"}, ], }, { "label": "Configuration", "links": [ { "href": +"/ui/settings", "label": "Settings", "children": [ {"href": "/theme-settings", +"label": "Themes"}, {"href": "/ui/currencies", "label": "Currency Management"}, +], }, ], }, { "label": "Account", "links": [ {"href": login_href, "label": +"Login", "match_prefix": "/login"}, {"href": register_href, "label": "Register", +"match_prefix": "/register"}, {"href": forgot_href, "label": "Forgot Password", +"match_prefix": "/forgot-password"}, ], }, ] %} diff --git a/templates/register.html b/templates/register.html index 04a7b4e..d422405 100644 --- a/templates/register.html +++ b/templates/register.html @@ -1,25 +1,43 @@ -{% extends "base.html" %} - -{% block title %}Register{% endblock %} - -{% block content %} +{% extends "base.html" %} {% block title %}Register{% endblock %} {% block +content %}
-

Register

-
-
- - -
-
- - -
-
- - -
- -
-

Already have an account? Login here

+

Register

+ {% if errors %} +
+
    + {% for error in errors %} +
  • {{ error }}
  • + {% endfor %} +
+
+ {% endif %} +
+
+ + +
+
+ + +
+
+ + +
+ +
+

Already have an account? Login here

{% endblock %} diff --git a/templates/reset_password.html b/templates/reset_password.html new file mode 100644 index 0000000..66c3007 --- /dev/null +++ b/templates/reset_password.html @@ -0,0 +1,36 @@ +{% extends "base.html" %} {% block title %}Reset Password{% endblock %} {% block +content %} +
+

Reset Password

+ {% if errors %} +
+
    + {% for error in errors %} +
  • {{ error }}
  • + {% endfor %} +
+
+ {% endif %} +
+ +
+ + +
+
+ + +
+ +
+

+ Remembered your password? + Return to login +

+
+{% endblock %} diff --git a/tests/conftest.py b/tests/conftest.py index 5ad55cc..6ec51e5 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -12,6 +12,7 @@ from sqlalchemy.pool import StaticPool from config.database import Base from dependencies import get_unit_of_work +from routes.auth import router as auth_router from routes.dashboard import router as dashboard_router from routes.projects import router as projects_router from routes.scenarios import router as scenarios_router @@ -36,13 +37,15 @@ def engine() -> Iterator[Engine]: @pytest.fixture() def session_factory(engine: Engine) -> Iterator[sessionmaker]: - testing_session = sessionmaker(bind=engine, expire_on_commit=False, future=True) + testing_session = sessionmaker( + bind=engine, expire_on_commit=False, future=True) yield testing_session @pytest.fixture() def app(session_factory: sessionmaker) -> FastAPI: application = FastAPI() + application.include_router(auth_router) application.include_router(dashboard_router) application.include_router(projects_router) application.include_router(scenarios_router) diff --git a/tests/test_auth_repositories.py b/tests/test_auth_repositories.py new file mode 100644 index 0000000..9c656db --- /dev/null +++ b/tests/test_auth_repositories.py @@ -0,0 +1,135 @@ +from __future__ import annotations + +from collections.abc import Iterator + +import pytest +from sqlalchemy import create_engine +from sqlalchemy.orm import Session, sessionmaker + +from config.database import Base +from models import Role, User +from services.repositories import ( + RoleRepository, + UserRepository, + ensure_admin_user, + ensure_default_roles, +) +from services.unit_of_work import UnitOfWork + + +@pytest.fixture() +def engine() -> Iterator: + engine = create_engine("sqlite:///:memory:", future=True) + Base.metadata.create_all(bind=engine) + try: + yield engine + finally: + Base.metadata.drop_all(bind=engine) + + +@pytest.fixture() +def session(engine) -> Iterator[Session]: + TestingSession = sessionmaker( + bind=engine, expire_on_commit=False, future=True) + db = TestingSession() + try: + yield db + finally: + db.close() + + +def test_role_repository_create_and_lookup(session: Session) -> None: + repo = RoleRepository(session) + role = Role(name="custom", display_name="Custom", + description="Custom role") + repo.create(role) + + retrieved = repo.get(role.id) + assert retrieved.name == "custom" + assert repo.get_by_name("custom") is retrieved + assert repo.list()[0].name == "custom" + + +def test_user_repository_assign_and_revoke_role(session: Session) -> None: + role_repo = RoleRepository(session) + user_repo = UserRepository(session) + + analyst = role_repo.create( + Role(name="analyst", display_name="Analyst", description="Analyzes data") + ) + user = User( + email="user@example.com", + username="user", + password_hash=User.hash_password("secret"), + ) + user_repo.create(user) + + assignment = user_repo.assign_role( + user_id=user.id, role_id=analyst.id, granted_by=None) + assert assignment.role_id == analyst.id + + refreshed = user_repo.get(user.id, with_roles=True) + assert refreshed.roles[0].name == "analyst" + + user_repo.revoke_role(user_id=user.id, role_id=analyst.id) + refreshed = user_repo.get(user.id, with_roles=True) + assert refreshed.roles == [] + + +def test_default_role_and_admin_helpers(session: Session) -> None: + role_repo = RoleRepository(session) + user_repo = UserRepository(session) + + roles = ensure_default_roles(role_repo) + assert {role.name for role in roles} == { + "admin", "project_manager", "analyst", "viewer"} + + ensure_admin_user( + user_repo, + role_repo, + email="admin@example.com", + username="admin", + password="SecurePass1!", + ) + + admin = user_repo.get_by_email("admin@example.com", with_roles=True) + assert admin is not None + assert admin.is_superuser + assert {role.name for role in admin.roles} >= {"admin"} + + # Idempotent behaviour on subsequent calls + ensure_admin_user( + user_repo, + role_repo, + email="admin@example.com", + username="admin", + password="SecurePass1!", + ) + admin_again = user_repo.get_by_email("admin@example.com", with_roles=True) + assert admin_again is not None + assert len(admin_again.roles) == len( + {role.name for role in admin_again.roles}) + + +def test_unit_of_work_exposes_auth_repositories(engine) -> None: + TestingSession = sessionmaker( + bind=engine, expire_on_commit=False, future=True) + + with UnitOfWork(session_factory=TestingSession) as uow: + assert uow.users is not None + assert uow.roles is not None + + roles = uow.ensure_default_roles() + assert any(role.name == "admin" for role in roles) + + uow.ensure_admin_user( + email="uow-admin@example.com", + username="uow-admin", + password="AnotherSecret1!", + ) + + admin = uow.users.get_by_email( + "uow-admin@example.com", with_roles=True) + assert admin is not None + assert admin.is_superuser + assert any(role.name == "admin" for role in admin.roles) diff --git a/tests/test_auth_routes.py b/tests/test_auth_routes.py new file mode 100644 index 0000000..08b4956 --- /dev/null +++ b/tests/test_auth_routes.py @@ -0,0 +1,239 @@ +from __future__ import annotations + +from collections.abc import Iterator +from urllib.parse import parse_qs, urlparse + +import pytest +from fastapi.testclient import TestClient +from sqlalchemy import select +from sqlalchemy.orm import Session, sessionmaker + +from models import Role, User, UserRole +from services.security import hash_password + + +@pytest.fixture() +def db_session(session_factory: sessionmaker) -> Iterator[Session]: + session = session_factory() + try: + yield session + finally: + session.close() + + +def _get_user(session: Session, *, email: str | None = None, username: str | None = None) -> User | None: + stmt = select(User) + if email is not None: + stmt = stmt.where(User.email == email) + if username is not None: + stmt = stmt.where(User.username == username) + return session.execute(stmt).scalar_one_or_none() + + +class TestRegistrationFlow: + def test_register_creates_user_and_assigns_role( + self, + client: TestClient, + db_session: Session, + ) -> None: + response = client.post( + "/register", + data={ + "username": "newuser", + "email": "newuser@example.com", + "password": "ComplexP@ss1", + "confirm_password": "ComplexP@ss1", + }, + follow_redirects=False, + ) + + assert response.status_code == 303 + location = response.headers.get("location") + assert location + parsed = urlparse(location) + assert parsed.path == "/login" + assert parse_qs(parsed.query).get("registered") == ["1"] + + created = _get_user(db_session, email="newuser@example.com") + assert created is not None + assert created.is_active + + role_stmt = select(Role).where(Role.name == "viewer") + viewer_role = db_session.execute(role_stmt).scalar_one_or_none() + assert viewer_role is not None + + assignments = db_session.execute( + select(UserRole).where( + UserRole.user_id == created.id, + UserRole.role_id == viewer_role.id, + ) + ).scalars().all() + assert len(assignments) == 1 + + def test_register_duplicate_email_shows_error( + self, + client: TestClient, + ) -> None: + first = client.post( + "/register", + data={ + "username": "existing", + "email": "existing@example.com", + "password": "ComplexP@ss1", + "confirm_password": "ComplexP@ss1", + }, + follow_redirects=False, + ) + assert first.status_code == 303 + + second = client.post( + "/register", + data={ + "username": "existing", + "email": "existing@example.com", + "password": "ComplexP@ss1", + "confirm_password": "ComplexP@ss1", + }, + follow_redirects=False, + ) + + assert second.status_code == 400 + assert "Email is already registered" in second.text + + +class TestLoginFlow: + def test_login_sets_tokens_and_updates_last_login( + self, + client: TestClient, + db_session: Session, + ) -> None: + password = "MySecur3Pass!" + user = User( + email="login@example.com", + username="loginuser", + password_hash=hash_password(password), + is_active=True, + ) + db_session.add(user) + db_session.commit() + + response = client.post( + "/login", + data={"username": "loginuser", "password": password}, + follow_redirects=False, + ) + + assert response.status_code == 303 + assert response.headers.get("location") == "http://testserver/" + set_cookie_header = response.headers.get("set-cookie", "") + assert "calminer_access_token=" in set_cookie_header + assert "calminer_refresh_token=" in set_cookie_header + + updated = _get_user(db_session, username="loginuser") + assert updated is not None and updated.last_login_at is not None + + def test_login_invalid_credentials_returns_error(self, client: TestClient) -> None: + response = client.post( + "/login", + data={"username": "unknown", "password": "bad"}, + follow_redirects=False, + ) + + assert response.status_code == 400 + assert "Invalid username or password" in response.text + + +class TestPasswordResetFlow: + def test_password_reset_round_trip( + self, + client: TestClient, + db_session: Session, + ) -> None: + user = User( + email="reset@example.com", + username="resetuser", + password_hash=hash_password("OldP@ssword1"), + is_active=True, + ) + db_session.add(user) + db_session.commit() + + request_response = client.post( + "/forgot-password", + data={"email": "reset@example.com"}, + follow_redirects=False, + ) + + assert request_response.status_code == 303 + reset_location = request_response.headers.get("location") + assert reset_location is not None + parsed = urlparse(reset_location) + assert parsed.path == "/reset-password" + token = parse_qs(parsed.query).get("token", [None])[0] + assert token + + form_response = client.get(reset_location) + assert form_response.status_code == 200 + + submit_response = client.post( + "/reset-password", + data={ + "token": token, + "password": "N3wP@ssword!", + "confirm_password": "N3wP@ssword!", + }, + follow_redirects=False, + ) + + assert submit_response.status_code == 303 + assert "reset=1" in (submit_response.headers.get("location") or "") + + db_session.refresh(user) + assert user.verify_password("N3wP@ssword!") + + def test_password_reset_with_unknown_email_shows_generic_message( + self, + client: TestClient, + ) -> None: + response = client.post( + "/forgot-password", + data={"email": "doesnotexist@example.com"}, + follow_redirects=False, + ) + + assert response.status_code == 200 + assert "If an account exists" in response.text + + def test_password_reset_mismatched_passwords_return_error( + self, + client: TestClient, + db_session: Session, + ) -> None: + user = User( + email="mismatch@example.com", + username="mismatch", + password_hash=hash_password("OldP@ssword1"), + is_active=True, + ) + db_session.add(user) + db_session.commit() + + request_response = client.post( + "/forgot-password", + data={"email": "mismatch@example.com"}, + follow_redirects=False, + ) + token = parse_qs(urlparse(request_response.headers["location"]).query)["token"][0] + + submit_response = client.post( + "/reset-password", + data={ + "token": token, + "password": "NewPass123!", + "confirm_password": "Different123!", + }, + follow_redirects=False, + ) + + assert submit_response.status_code == 400 + assert "Passwords do not match" in submit_response.text \ No newline at end of file diff --git a/tests/test_security.py b/tests/test_security.py new file mode 100644 index 0000000..607716a --- /dev/null +++ b/tests/test_security.py @@ -0,0 +1,76 @@ +from __future__ import annotations + +from datetime import timedelta + +import pytest + +from services.security import ( + JWTSettings, + TokenDecodeError, + TokenExpiredError, + TokenTypeMismatchError, + create_access_token, + create_refresh_token, + decode_access_token, + decode_refresh_token, + hash_password, + verify_password, +) + + +def test_hash_password_round_trip() -> None: + hashed = hash_password("super-secret") + + assert hashed != "super-secret" + assert verify_password("super-secret", hashed) + assert not verify_password("incorrect", hashed) + + +def test_verify_password_handles_malformed_hash() -> None: + assert not verify_password("secret", "not-a-valid-hash") + + +def test_access_token_roundtrip() -> None: + settings = JWTSettings(secret_key="unit-test-secret") + + token = create_access_token( + "user-id-123", + settings, + scopes=("read", "write"), + extra_claims={"custom": "value"}, + ) + + payload = decode_access_token(token, settings) + + assert payload.sub == "user-id-123" + assert payload.type == "access" + assert payload.scopes == ["read", "write"] + + +def test_refresh_token_type_mismatch() -> None: + settings = JWTSettings(secret_key="unit-test-secret") + token = create_refresh_token("user-id-456", settings) + + with pytest.raises(TokenTypeMismatchError): + decode_access_token(token, settings) + + +def test_decode_expired_token() -> None: + settings = JWTSettings(secret_key="unit-test-secret") + expired_token = create_access_token( + "user-id-789", + settings, + expires_delta=timedelta(seconds=-5), + ) + + with pytest.raises(TokenExpiredError): + decode_access_token(expired_token, settings) + + +def test_decode_tampered_token() -> None: + settings = JWTSettings(secret_key="unit-test-secret") + token = create_access_token("user-id-321", settings) + tampered = token[:-1] + ("a" if token[-1] != "a" else "b") + + with pytest.raises(TokenDecodeError): + decode_access_token(tampered, settings) -- 2.39.5 From 27262bdfa34dc18678e90f99a4219f55f136c2f3 Mon Sep 17 00:00:00 2001 From: zwitschi Date: Sun, 9 Nov 2025 23:14:41 +0100 Subject: [PATCH 022/109] feat: Implement session management with middleware and update authentication flow --- config/settings.py | 70 ++++++++++ dependencies.py | 72 ++++++++++ main.py | 6 +- middleware/auth_session.py | 177 ++++++++++++++++++++++++ routes/auth.py | 67 +++++---- services/session.py | 192 ++++++++++++++++++++++++++ templates/partials/sidebar_nav.html | 141 ++++++++++++------- tests/test_auth_routes.py | 51 ++++++- tests/test_auth_session_middleware.py | 111 +++++++++++++++ 9 files changed, 804 insertions(+), 83 deletions(-) create mode 100644 middleware/auth_session.py create mode 100644 services/session.py create mode 100644 tests/test_auth_session_middleware.py diff --git a/config/settings.py b/config/settings.py index 0b928d4..217a487 100644 --- a/config/settings.py +++ b/config/settings.py @@ -5,9 +5,25 @@ from dataclasses import dataclass from datetime import timedelta from functools import lru_cache +from typing import Optional + from services.security import JWTSettings +@dataclass(frozen=True, slots=True) +class SessionSettings: + """Cookie and header configuration for session token transport.""" + + access_cookie_name: str + refresh_cookie_name: str + cookie_secure: bool + cookie_domain: Optional[str] + cookie_path: str + header_name: str + header_prefix: str + allow_header_fallback: bool + + @dataclass(frozen=True, slots=True) class Settings: """Application configuration sourced from environment variables.""" @@ -16,6 +32,14 @@ class Settings: jwt_algorithm: str = "HS256" jwt_access_token_minutes: int = 15 jwt_refresh_token_days: int = 7 + session_access_cookie_name: str = "calminer_access_token" + session_refresh_cookie_name: str = "calminer_refresh_token" + session_cookie_secure: bool = False + session_cookie_domain: Optional[str] = None + session_cookie_path: str = "/" + session_header_name: str = "Authorization" + session_header_prefix: str = "Bearer" + session_allow_header_fallback: bool = True @classmethod def from_environment(cls) -> "Settings": @@ -30,6 +54,26 @@ class Settings: jwt_refresh_token_days=cls._int_from_env( "CALMINER_JWT_REFRESH_DAYS", 7 ), + session_access_cookie_name=os.getenv( + "CALMINER_SESSION_ACCESS_COOKIE", "calminer_access_token" + ), + session_refresh_cookie_name=os.getenv( + "CALMINER_SESSION_REFRESH_COOKIE", "calminer_refresh_token" + ), + session_cookie_secure=cls._bool_from_env( + "CALMINER_SESSION_COOKIE_SECURE", False + ), + session_cookie_domain=os.getenv("CALMINER_SESSION_COOKIE_DOMAIN"), + session_cookie_path=os.getenv("CALMINER_SESSION_COOKIE_PATH", "/"), + session_header_name=os.getenv( + "CALMINER_SESSION_HEADER_NAME", "Authorization" + ), + session_header_prefix=os.getenv( + "CALMINER_SESSION_HEADER_PREFIX", "Bearer" + ), + session_allow_header_fallback=cls._bool_from_env( + "CALMINER_SESSION_ALLOW_HEADER_FALLBACK", True + ), ) @staticmethod @@ -42,6 +86,18 @@ class Settings: except ValueError: return default + @staticmethod + def _bool_from_env(name: str, default: bool) -> bool: + raw_value = os.getenv(name) + if raw_value is None: + return default + lowered = raw_value.strip().lower() + if lowered in {"1", "true", "yes", "on"}: + return True + if lowered in {"0", "false", "no", "off"}: + return False + return default + def jwt_settings(self) -> JWTSettings: """Build runtime JWT settings compatible with token helpers.""" @@ -52,6 +108,20 @@ class Settings: refresh_token_ttl=timedelta(days=self.jwt_refresh_token_days), ) + def session_settings(self) -> SessionSettings: + """Provide transport configuration for session tokens.""" + + return SessionSettings( + access_cookie_name=self.session_access_cookie_name, + refresh_cookie_name=self.session_refresh_cookie_name, + cookie_secure=self.session_cookie_secure, + cookie_domain=self.session_cookie_domain, + cookie_path=self.session_cookie_path, + header_name=self.session_header_name, + header_prefix=self.session_header_prefix, + allow_header_fallback=self.session_allow_header_fallback, + ) + @lru_cache(maxsize=1) def get_settings() -> Settings: diff --git a/dependencies.py b/dependencies.py index b2b0774..a043f55 100644 --- a/dependencies.py +++ b/dependencies.py @@ -2,8 +2,18 @@ from __future__ import annotations from collections.abc import Generator +from fastapi import Depends, HTTPException, Request, status + from config.settings import Settings, get_settings +from models import User from services.security import JWTSettings +from services.session import ( + AuthSession, + SessionStrategy, + SessionTokens, + build_session_strategy, + extract_session_tokens, +) from services.unit_of_work import UnitOfWork @@ -24,3 +34,65 @@ def get_jwt_settings() -> JWTSettings: """Provide JWT runtime configuration derived from settings.""" return get_settings().jwt_settings() + + +def get_session_strategy( + settings: Settings = Depends(get_application_settings), +) -> SessionStrategy: + """Yield configured session transport strategy.""" + + return build_session_strategy(settings.session_settings()) + + +def get_session_tokens( + request: Request, + strategy: SessionStrategy = Depends(get_session_strategy), +) -> SessionTokens: + """Extract raw session tokens from the incoming request.""" + + existing = getattr(request.state, "auth_session", None) + if isinstance(existing, AuthSession): + return existing.tokens + + tokens = extract_session_tokens(request, strategy) + request.state.auth_session = AuthSession(tokens=tokens) + return tokens + + +def get_auth_session( + request: Request, + tokens: SessionTokens = Depends(get_session_tokens), +) -> AuthSession: + """Provide authentication session context for the current request.""" + + existing = getattr(request.state, "auth_session", None) + if isinstance(existing, AuthSession): + return existing + + if tokens.is_empty: + session = AuthSession.anonymous() + else: + session = AuthSession(tokens=tokens) + request.state.auth_session = session + return session + + +def get_current_user( + session: AuthSession = Depends(get_auth_session), +) -> User | None: + """Return the current authenticated user if present.""" + + return session.user + + +def require_current_user( + session: AuthSession = Depends(get_auth_session), +) -> User: + """Ensure that a request is authenticated and return the user context.""" + + if session.user is None: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Authentication required.", + ) + return session.user diff --git a/main.py b/main.py index 456aa5a..6fd8fb8 100644 --- a/main.py +++ b/main.py @@ -2,8 +2,10 @@ from typing import Awaitable, Callable from fastapi import FastAPI, Request, Response from fastapi.staticfiles import StaticFiles -from middleware.validation import validate_json + from config.database import Base, engine +from middleware.auth_session import AuthSessionMiddleware +from middleware.validation import validate_json from models import ( FinancialInput, Project, @@ -20,6 +22,8 @@ Base.metadata.create_all(bind=engine) app = FastAPI() +app.add_middleware(AuthSessionMiddleware) + @app.middleware("http") async def json_validation( diff --git a/middleware/auth_session.py b/middleware/auth_session.py new file mode 100644 index 0000000..fb2cb50 --- /dev/null +++ b/middleware/auth_session.py @@ -0,0 +1,177 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import Callable, Iterable, Optional + +from fastapi import Request, Response +from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint +from starlette.types import ASGIApp + +from config.settings import Settings, get_settings +from models import User +from services.exceptions import EntityNotFoundError +from services.security import ( + JWTSettings, + TokenDecodeError, + TokenError, + TokenExpiredError, + TokenTypeMismatchError, + create_access_token, + create_refresh_token, + decode_access_token, + decode_refresh_token, +) +from services.session import ( + AuthSession, + SessionStrategy, + SessionTokens, + build_session_strategy, + clear_session_cookies, + extract_session_tokens, + set_session_cookies, +) +from services.unit_of_work import UnitOfWork + +_AUTH_SCOPE = "auth" + + +@dataclass(slots=True) +class _ResolutionResult: + session: AuthSession + strategy: SessionStrategy + jwt_settings: JWTSettings + + +class AuthSessionMiddleware(BaseHTTPMiddleware): + """Resolve authenticated users from session cookies and refresh tokens.""" + + def __init__( + self, + app: ASGIApp, + *, + settings_provider: Callable[[], Settings] = get_settings, + unit_of_work_factory: Callable[[], UnitOfWork] = UnitOfWork, + refresh_scopes: Iterable[str] | None = None, + ) -> None: + super().__init__(app) + self._settings_provider = settings_provider + self._unit_of_work_factory = unit_of_work_factory + self._refresh_scopes = tuple( + refresh_scopes) if refresh_scopes else (_AUTH_SCOPE,) + + async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -> Response: + resolved = self._resolve_session(request) + response = await call_next(request) + self._apply_session(response, resolved) + return response + + def _resolve_session(self, request: Request) -> _ResolutionResult: + settings = self._settings_provider() + jwt_settings = settings.jwt_settings() + strategy = build_session_strategy(settings.session_settings()) + + tokens = extract_session_tokens(request, strategy) + session = AuthSession(tokens=tokens) + request.state.auth_session = session + + if tokens.access_token: + if self._try_access_token(session, tokens, jwt_settings): + return _ResolutionResult(session=session, strategy=strategy, jwt_settings=jwt_settings) + + if tokens.refresh_token: + self._try_refresh_token( + session, tokens.refresh_token, jwt_settings) + + return _ResolutionResult(session=session, strategy=strategy, jwt_settings=jwt_settings) + + def _try_access_token( + self, + session: AuthSession, + tokens: SessionTokens, + jwt_settings: JWTSettings, + ) -> bool: + try: + payload = decode_access_token( + tokens.access_token or "", jwt_settings) + except TokenExpiredError: + return False + except (TokenDecodeError, TokenTypeMismatchError, TokenError): + session.mark_cleared() + return False + + user = self._load_user(payload.sub) + if not user or not user.is_active or _AUTH_SCOPE not in payload.scopes: + session.mark_cleared() + return False + + session.user = user + session.scopes = tuple(payload.scopes) + return True + + def _try_refresh_token( + self, + session: AuthSession, + refresh_token: str, + jwt_settings: JWTSettings, + ) -> None: + try: + payload = decode_refresh_token(refresh_token, jwt_settings) + except (TokenExpiredError, TokenDecodeError, TokenTypeMismatchError, TokenError): + session.mark_cleared() + return + + user = self._load_user(payload.sub) + if not user or not user.is_active or not self._is_refresh_scope_allowed(payload.scopes): + session.mark_cleared() + return + + session.user = user + session.scopes = tuple(payload.scopes) + + access_token = create_access_token( + str(user.id), + jwt_settings, + scopes=payload.scopes, + ) + new_refresh = create_refresh_token( + str(user.id), + jwt_settings, + scopes=payload.scopes, + ) + session.issue_tokens(access_token=access_token, + refresh_token=new_refresh) + + def _is_refresh_scope_allowed(self, scopes: Iterable[str]) -> bool: + candidate_scopes = set(scopes) + return any(scope in candidate_scopes for scope in self._refresh_scopes) + + def _load_user(self, subject: str) -> Optional[User]: + try: + user_id = int(subject) + except ValueError: + return None + + with self._unit_of_work_factory() as uow: + if not uow.users: + return None + try: + user = uow.users.get(user_id, with_roles=True) + except EntityNotFoundError: + return None + return user + + def _apply_session(self, response: Response, resolved: _ResolutionResult) -> None: + session = resolved.session + if session.clear_cookies: + clear_session_cookies(response, resolved.strategy) + return + + if session.issued_access_token: + refresh_token = session.issued_refresh_token or session.tokens.refresh_token + set_session_cookies( + response, + access_token=session.issued_access_token, + refresh_token=refresh_token, + strategy=resolved.strategy, + jwt_settings=resolved.jwt_settings, + ) diff --git a/routes/auth.py b/routes/auth.py index 71a8752..19beaa0 100644 --- a/routes/auth.py +++ b/routes/auth.py @@ -9,7 +9,13 @@ from fastapi.templating import Jinja2Templates from pydantic import ValidationError from starlette.datastructures import FormData -from dependencies import get_jwt_settings, get_unit_of_work +from dependencies import ( + get_auth_session, + get_jwt_settings, + get_session_strategy, + get_unit_of_work, + require_current_user, +) from models import Role, User from schemas.auth import ( LoginForm, @@ -29,6 +35,12 @@ from services.security import ( hash_password, verify_password, ) +from services.session import ( + AuthSession, + SessionStrategy, + clear_session_cookies, + set_session_cookies, +) from services.repositories import RoleRepository, UserRepository from services.unit_of_work import UnitOfWork @@ -103,6 +115,7 @@ async def login_submit( request: Request, uow: UnitOfWork = Depends(get_unit_of_work), jwt_settings: JWTSettings = Depends(get_jwt_settings), + session_strategy: SessionStrategy = Depends(get_session_strategy), ): form_data = _normalise_form_data(await request.form()) try: @@ -158,7 +171,31 @@ async def login_submit( request.url_for("dashboard.home"), status_code=status.HTTP_303_SEE_OTHER, ) - _set_auth_cookies(response, access_token, refresh_token, jwt_settings) + set_session_cookies( + response, + access_token=access_token, + refresh_token=refresh_token, + strategy=session_strategy, + jwt_settings=jwt_settings, + ) + return response + + +@router.get("/logout", include_in_schema=False, name="auth.logout") +async def logout( + request: Request, + _: User = Depends(require_current_user), + session: AuthSession = Depends(get_auth_session), + session_strategy: SessionStrategy = Depends(get_session_strategy), +) -> RedirectResponse: + session.mark_cleared() + redirect_url = request.url_for( + "auth.login_form").include_query_params(logout="1") + response = RedirectResponse( + redirect_url, + status_code=status.HTTP_303_SEE_OTHER, + ) + clear_session_cookies(response, session_strategy) return response @@ -168,32 +205,6 @@ def _lookup_user(users_repo: UserRepository, identifier: str) -> User | None: return users_repo.get_by_username(identifier, with_roles=True) -def _set_auth_cookies( - response: RedirectResponse, - access_token: str, - refresh_token: str, - jwt_settings: JWTSettings, -) -> None: - access_ttl = int(jwt_settings.access_token_ttl.total_seconds()) - refresh_ttl = int(jwt_settings.refresh_token_ttl.total_seconds()) - response.set_cookie( - "calminer_access_token", - access_token, - httponly=True, - secure=False, - samesite="lax", - max_age=max(access_ttl, 0) or None, - ) - response.set_cookie( - "calminer_refresh_token", - refresh_token, - httponly=True, - secure=False, - samesite="lax", - max_age=max(refresh_ttl, 0) or None, - ) - - @router.get("/register", response_class=HTMLResponse, include_in_schema=False, name="auth.register_form") def register_form(request: Request) -> HTMLResponse: return _template( diff --git a/services/session.py b/services/session.py new file mode 100644 index 0000000..b989c7a --- /dev/null +++ b/services/session.py @@ -0,0 +1,192 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import Literal, Optional, TYPE_CHECKING + +from fastapi import Request, Response + +from config.settings import SessionSettings +from services.security import JWTSettings + +if TYPE_CHECKING: # pragma: no cover - used only for static typing + from models import User + + +@dataclass(slots=True) +class SessionStrategy: + """Describe how authentication tokens are transported with requests.""" + + access_cookie_name: str + refresh_cookie_name: str + cookie_secure: bool + cookie_domain: Optional[str] + cookie_path: str + header_name: str + header_prefix: str + allow_header_fallback: bool = True + + @classmethod + def from_settings(cls, settings: SessionSettings) -> "SessionStrategy": + return cls( + access_cookie_name=settings.access_cookie_name, + refresh_cookie_name=settings.refresh_cookie_name, + cookie_secure=settings.cookie_secure, + cookie_domain=settings.cookie_domain, + cookie_path=settings.cookie_path, + header_name=settings.header_name, + header_prefix=settings.header_prefix, + allow_header_fallback=settings.allow_header_fallback, + ) + + +@dataclass(slots=True) +class SessionTokens: + """Raw access and refresh tokens extracted from the transport layer.""" + + access_token: Optional[str] + refresh_token: Optional[str] + access_token_source: Literal["cookie", "header", "none"] = "none" + + @property + def has_access(self) -> bool: + return bool(self.access_token) + + @property + def has_refresh(self) -> bool: + return bool(self.refresh_token) + + @property + def is_empty(self) -> bool: + return not self.has_access and not self.has_refresh + + +@dataclass(slots=True) +class AuthSession: + """Holds authenticated user context resolved from session tokens.""" + + tokens: SessionTokens + user: Optional["User"] = None + scopes: tuple[str, ...] = () + issued_access_token: Optional[str] = None + issued_refresh_token: Optional[str] = None + clear_cookies: bool = False + + @property + def is_authenticated(self) -> bool: + return self.user is not None + + @classmethod + def anonymous(cls) -> "AuthSession": + return cls(tokens=SessionTokens(access_token=None, refresh_token=None)) + + def issue_tokens( + self, + *, + access_token: str, + refresh_token: Optional[str] = None, + access_source: Literal["cookie", "header", "none"] = "cookie", + ) -> None: + self.issued_access_token = access_token + if refresh_token is not None: + self.issued_refresh_token = refresh_token + self.tokens = SessionTokens( + access_token=access_token, + refresh_token=refresh_token if refresh_token is not None else self.tokens.refresh_token, + access_token_source=access_source, + ) + + def mark_cleared(self) -> None: + self.clear_cookies = True + self.tokens = SessionTokens(access_token=None, refresh_token=None) + self.user = None + self.scopes = () + + +def extract_session_tokens(request: Request, strategy: SessionStrategy) -> SessionTokens: + """Pull tokens from cookies or headers according to configured strategy.""" + + access_token: Optional[str] = None + refresh_token: Optional[str] = None + access_source: Literal["cookie", "header", "none"] = "none" + + if strategy.access_cookie_name in request.cookies: + access_token = request.cookies.get(strategy.access_cookie_name) or None + if access_token: + access_source = "cookie" + + if strategy.refresh_cookie_name in request.cookies: + refresh_token = request.cookies.get( + strategy.refresh_cookie_name) or None + + if not access_token and strategy.allow_header_fallback: + header_value = request.headers.get(strategy.header_name) + if header_value: + candidate = header_value.strip() + prefix = f"{strategy.header_prefix} " if strategy.header_prefix else "" + if prefix and candidate.lower().startswith(prefix.lower()): + candidate = candidate[len(prefix):].strip() + if candidate: + access_token = candidate + access_source = "header" + + return SessionTokens( + access_token=access_token, + refresh_token=refresh_token, + access_token_source=access_source, + ) + + +def build_session_strategy(settings: SessionSettings) -> SessionStrategy: + """Create a session strategy object from settings configuration.""" + + return SessionStrategy.from_settings(settings) + + +def set_session_cookies( + response: Response, + *, + access_token: str, + refresh_token: Optional[str], + strategy: SessionStrategy, + jwt_settings: JWTSettings, +) -> None: + """Persist session cookies on an outgoing response.""" + + access_ttl = int(jwt_settings.access_token_ttl.total_seconds()) + refresh_ttl = int(jwt_settings.refresh_token_ttl.total_seconds()) + response.set_cookie( + strategy.access_cookie_name, + access_token, + httponly=True, + secure=strategy.cookie_secure, + samesite="lax", + max_age=max(access_ttl, 0) or None, + domain=strategy.cookie_domain, + path=strategy.cookie_path, + ) + if refresh_token is not None: + response.set_cookie( + strategy.refresh_cookie_name, + refresh_token, + httponly=True, + secure=strategy.cookie_secure, + samesite="lax", + max_age=max(refresh_ttl, 0) or None, + domain=strategy.cookie_domain, + path=strategy.cookie_path, + ) + + +def clear_session_cookies(response: Response, strategy: SessionStrategy) -> None: + """Remove session cookies from the client.""" + + response.delete_cookie( + strategy.access_cookie_name, + domain=strategy.cookie_domain, + path=strategy.cookie_path, + ) + response.delete_cookie( + strategy.refresh_cookie_name, + domain=strategy.cookie_domain, + path=strategy.cookie_path, + ) diff --git a/templates/partials/sidebar_nav.html b/templates/partials/sidebar_nav.html index 1980125..90e3c1a 100644 --- a/templates/partials/sidebar_nav.html +++ b/templates/partials/sidebar_nav.html @@ -1,60 +1,97 @@ {% set dashboard_href = request.url_for('dashboard.home') if request else '/' %} -{% set projects_href = request.url_for('projects.project_list_page') if request -else '/projects/ui' %} {% set project_create_href = -request.url_for('projects.create_project_form') if request else -'/projects/create' %} {% set login_href = request.url_for('auth.login_form') if -request else '/login' %} {% set register_href = -request.url_for('auth.register_form') if request else '/register' %} {% set -forgot_href = request.url_for('auth.password_reset_request_form') if request -else '/forgot-password' %} {% set nav_groups = [ { "label": "Workspace", -"links": [ {"href": dashboard_href, "label": "Dashboard", "match_prefix": "/"}, -{"href": projects_href, "label": "Projects", "match_prefix": "/projects"}, -{"href": project_create_href, "label": "New Project", "match_prefix": -"/projects/create"}, ], }, { "label": "Insights", "links": [ {"href": -"/ui/simulations", "label": "Simulations"}, {"href": "/ui/reporting", "label": -"Reporting"}, ], }, { "label": "Configuration", "links": [ { "href": -"/ui/settings", "label": "Settings", "children": [ {"href": "/theme-settings", -"label": "Themes"}, {"href": "/ui/currencies", "label": "Currency Management"}, -], }, ], }, { "label": "Account", "links": [ {"href": login_href, "label": -"Login", "match_prefix": "/login"}, {"href": register_href, "label": "Register", -"match_prefix": "/register"}, {"href": forgot_href, "label": "Forgot Password", -"match_prefix": "/forgot-password"}, ], }, ] %} +{% set projects_href = request.url_for('projects.project_list_page') if request else '/projects/ui' %} +{% set project_create_href = request.url_for('projects.create_project_form') if request else '/projects/create' %} +{% set auth_session = request.state.auth_session if request else None %} +{% set is_authenticated = auth_session and auth_session.is_authenticated %} + +{% if is_authenticated %} + {% set logout_href = request.url_for('auth.logout') if request else '/logout' %} + {% set account_links = [ + {"href": logout_href, "label": "Logout", "match_prefix": "/logout"} + ] %} +{% else %} + {% set login_href = request.url_for('auth.login_form') if request else '/login' %} + {% set register_href = request.url_for('auth.register_form') if request else '/register' %} + {% set forgot_href = request.url_for('auth.password_reset_request_form') if request else '/forgot-password' %} + {% set account_links = [ + {"href": login_href, "label": "Login", "match_prefix": "/login"}, + {"href": register_href, "label": "Register", "match_prefix": "/register"}, + {"href": forgot_href, "label": "Forgot Password", "match_prefix": "/forgot-password"} + ] %} +{% endif %} +{% set nav_groups = [ + { + "label": "Workspace", + "links": [ + {"href": dashboard_href, "label": "Dashboard", "match_prefix": "/"}, + {"href": projects_href, "label": "Projects", "match_prefix": "/projects"}, + {"href": project_create_href, "label": "New Project", "match_prefix": "/projects/create"} + ] + }, + { + "label": "Insights", + "links": [ + {"href": "/ui/simulations", "label": "Simulations"}, + {"href": "/ui/reporting", "label": "Reporting"} + ] + }, + { + "label": "Configuration", + "links": [ + { + "href": "/ui/settings", + "label": "Settings", + "children": [ + {"href": "/theme-settings", "label": "Themes"}, + {"href": "/ui/currencies", "label": "Currency Management"} + ] + } + ] + }, + { + "label": "Account", + "links": account_links + } +] %} diff --git a/tests/test_auth_routes.py b/tests/test_auth_routes.py index 08b4956..9196503 100644 --- a/tests/test_auth_routes.py +++ b/tests/test_auth_routes.py @@ -1,15 +1,19 @@ from __future__ import annotations from collections.abc import Iterator +from typing import cast from urllib.parse import parse_qs, urlparse import pytest +from fastapi import FastAPI from fastapi.testclient import TestClient from sqlalchemy import select from sqlalchemy.orm import Session, sessionmaker from models import Role, User, UserRole +from dependencies import get_auth_session, require_current_user from services.security import hash_password +from services.session import AuthSession, SessionTokens @pytest.fixture() @@ -223,7 +227,8 @@ class TestPasswordResetFlow: data={"email": "mismatch@example.com"}, follow_redirects=False, ) - token = parse_qs(urlparse(request_response.headers["location"]).query)["token"][0] + token = parse_qs(urlparse(request_response.headers["location"]).query)[ + "token"][0] submit_response = client.post( "/reset-password", @@ -236,4 +241,46 @@ class TestPasswordResetFlow: ) assert submit_response.status_code == 400 - assert "Passwords do not match" in submit_response.text \ No newline at end of file + assert "Passwords do not match" in submit_response.text + + +class TestLogoutFlow: + def test_logout_clears_cookies_and_redirects( + self, + client: TestClient, + db_session: Session, + ) -> None: + user = User( + email="logout@example.com", + username="logoutuser", + password_hash=hash_password("SecureP@ss1"), + is_active=True, + ) + db_session.add(user) + db_session.commit() + + session = AuthSession( + tokens=SessionTokens( + access_token="access-token", + refresh_token="refresh-token", + access_token_source="cookie", + ), + user=user, + ) + + app = cast(FastAPI, client.app) + app.dependency_overrides[require_current_user] = lambda: user + app.dependency_overrides[get_auth_session] = lambda: session + + try: + response = client.get("/logout", follow_redirects=False) + finally: + app.dependency_overrides.pop(require_current_user, None) + app.dependency_overrides.pop(get_auth_session, None) + + assert response.status_code == 303 + location = response.headers.get("location") + assert location and location.startswith("http://testserver/login") + set_cookie_header = response.headers.get("set-cookie") or "" + assert "calminer_access_token=" in set_cookie_header + assert "Max-Age=0" in set_cookie_header or "expires=" in set_cookie_header.lower() diff --git a/tests/test_auth_session_middleware.py b/tests/test_auth_session_middleware.py new file mode 100644 index 0000000..d31ab71 --- /dev/null +++ b/tests/test_auth_session_middleware.py @@ -0,0 +1,111 @@ +from __future__ import annotations + +from datetime import timedelta +from typing import Iterator + +import pytest +from fastapi import Depends, FastAPI +from fastapi.responses import JSONResponse +from fastapi.testclient import TestClient +from sqlalchemy.orm import sessionmaker + +from config.settings import get_settings +from dependencies import get_unit_of_work, require_current_user +from middleware.auth_session import AuthSessionMiddleware +from models import User +from services.security import create_access_token, create_refresh_token, hash_password +from services.unit_of_work import UnitOfWork + + +@pytest.fixture() +def auth_app(session_factory: sessionmaker) -> Iterator[TestClient]: + app = FastAPI() + + def _override_uow() -> Iterator[UnitOfWork]: + with UnitOfWork(session_factory=session_factory) as uow: + yield uow + + app.dependency_overrides[get_unit_of_work] = _override_uow + + @app.get("/me") + def read_me(user: User = Depends(require_current_user)) -> JSONResponse: + return JSONResponse({"id": user.id, "username": user.username}) + + app.add_middleware( + AuthSessionMiddleware, + unit_of_work_factory=lambda: UnitOfWork( + session_factory=session_factory), + ) + + client = TestClient(app) + try: + yield client + finally: + client.close() + + +def _create_user(session_factory: sessionmaker) -> User: + with UnitOfWork(session_factory=session_factory) as uow: + assert uow.users is not None + user = User( + email="jane@example.com", + username="jane", + password_hash=hash_password("secret"), + is_active=True, + is_superuser=False, + ) + uow.users.create(user) + return user + + +def _issue_tokens(user: User) -> tuple[str, str]: + settings = get_settings().jwt_settings() + access = create_access_token(str(user.id), settings, scopes=["auth"]) + refresh = create_refresh_token(str(user.id), settings, scopes=["auth"]) + return access, refresh + + +def test_middleware_populates_current_user(auth_app: TestClient, session_factory: sessionmaker) -> None: + user = _create_user(session_factory) + access, refresh = _issue_tokens(user) + + auth_app.cookies.set("calminer_access_token", access) + auth_app.cookies.set("calminer_refresh_token", refresh) + + response = auth_app.get("/me") + assert response.status_code == 200 + payload = response.json() + assert payload["id"] == user.id + assert payload["username"] == user.username + + +def test_middleware_refreshes_expired_access_token(auth_app: TestClient, session_factory: sessionmaker) -> None: + user = _create_user(session_factory) + settings = get_settings().jwt_settings() + expired = create_access_token( + str(user.id), + settings, + scopes=["auth"], + expires_delta=timedelta(seconds=-1), + ) + refresh = create_refresh_token(str(user.id), settings, scopes=["auth"]) + + auth_app.cookies.set("calminer_access_token", expired) + auth_app.cookies.set("calminer_refresh_token", refresh) + + response = auth_app.get("/me") + assert response.status_code == 200 + new_access = response.cookies.get("calminer_access_token") + new_refresh = response.cookies.get("calminer_refresh_token") + assert new_access is not None and new_access != expired + assert new_refresh is not None + + +def test_middleware_blocks_invalid_tokens(auth_app: TestClient) -> None: + auth_app.cookies.set("calminer_access_token", "invalid-token") + auth_app.cookies.set("calminer_refresh_token", "invalid-token") + + response = auth_app.get("/me") + assert response.status_code == 401 + set_cookies = response.headers.get_list("set-cookie") + assert any("calminer_access_token=" in value for value in set_cookies) -- 2.39.5 From 0f79864188f23795a1766553b5f3161fcd484f8f Mon Sep 17 00:00:00 2001 From: zwitschi Date: Sun, 9 Nov 2025 23:14:54 +0100 Subject: [PATCH 023/109] feat: enhance project and scenario management with role-based access control - Implemented role-based access control for project and scenario routes. - Added authorization checks to ensure users have appropriate roles for viewing and managing projects and scenarios. - Introduced utility functions for ensuring project and scenario access based on user roles. - Refactored project and scenario routes to utilize new authorization helpers. - Created initial data seeding script to set up default roles and an admin user. - Added tests for authorization helpers and initial data seeding functionality. - Updated exception handling to include authorization errors. --- .env.example | 3 - changelog.md | 7 + dependencies.py | 153 +++++++++++++++++++- routes/dashboard.py | 13 +- routes/projects.py | 119 ++++++++------- routes/scenarios.py | 135 +++++++++-------- scripts/00_initial_data.py | 20 +++ scripts/__init__.py | 1 + scripts/initial_data.py | 183 ++++++++++++++++++++++++ services/authorization.py | 104 ++++++++++++++ services/exceptions.py | 4 + services/repositories.py | 8 ++ tests/conftest.py | 28 +++- tests/scripts/test_initial_data_seed.py | 156 ++++++++++++++++++++ tests/test_authorization_helpers.py | 165 +++++++++++++++++++++ tests/test_scenario_validation.py | 30 +++- 16 files changed, 997 insertions(+), 132 deletions(-) create mode 100644 scripts/00_initial_data.py create mode 100644 scripts/__init__.py create mode 100644 scripts/initial_data.py create mode 100644 services/authorization.py create mode 100644 tests/scripts/test_initial_data_seed.py create mode 100644 tests/test_authorization_helpers.py diff --git a/.env.example b/.env.example index 7393cde..7329fa2 100644 --- a/.env.example +++ b/.env.example @@ -9,6 +9,3 @@ DATABASE_PASSWORD= DATABASE_NAME=calminer # Optional: set a schema (comma-separated for multiple entries) # DATABASE_SCHEMA=public - -# Legacy fallback (still supported, but granular settings are preferred) -# DATABASE_URL=postgresql://:@localhost:5432/calminer \ No newline at end of file diff --git a/changelog.md b/changelog.md index 7606c0d..ed4c7fc 100644 --- a/changelog.md +++ b/changelog.md @@ -18,3 +18,10 @@ - Updated all Jinja2 template responses to the new Starlette signature to eliminate deprecation warnings while keeping request-aware context available to the templates. - Introduced `services/security.py` to centralize Argon2 password hashing utilities and JWT creation/verification with typed payloads, and added pytest coverage for hashing, expiry, tampering, and token type mismatch scenarios. - Added `routes/auth.py` with registration, login, and password reset flows, refreshed auth templates with error messaging, wired navigation links, and introduced end-to-end pytest coverage for the new forms and token flows. +- Implemented cookie-based authentication session middleware with automatic access token refresh, logout handling, navigation adjustments, and documentation/test updates capturing the new behaviour. +- Delivered idempotent seeding utilities with `scripts/initial_data.py`, entry-point runner `scripts/00_initial_data.py`, documentation updates, and pytest coverage to verify role/admin provisioning. +- Secured project and scenario routers with RBAC guard dependencies, enforced repository access checks via helper utilities, and aligned template routes with FastAPI dependency injection patterns. + +## 2025-11-10 + +- Extended authorization helper layer with project/scenario ownership lookups, integrated them into FastAPI dependencies, refreshed pytest fixtures to keep the suite authenticated, and documented the new patterns across RBAC plan and security guides. diff --git a/dependencies.py b/dependencies.py index a043f55..b91c05d 100644 --- a/dependencies.py +++ b/dependencies.py @@ -1,11 +1,17 @@ from __future__ import annotations -from collections.abc import Generator +from collections.abc import Callable, Iterable, Generator from fastapi import Depends, HTTPException, Request, status from config.settings import Settings, get_settings -from models import User +from models import Project, Role, Scenario, User +from services.authorization import ( + ensure_project_access as ensure_project_access_helper, + ensure_scenario_access as ensure_scenario_access_helper, + ensure_scenario_in_project as ensure_scenario_in_project_helper, +) +from services.exceptions import AuthorizationError, EntityNotFoundError from services.security import JWTSettings from services.session import ( AuthSession, @@ -90,9 +96,150 @@ def require_current_user( ) -> User: """Ensure that a request is authenticated and return the user context.""" - if session.user is None: + if session.user is None or session.tokens.is_empty: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Authentication required.", ) return session.user + + +def require_authenticated_user( + user: User = Depends(require_current_user), +) -> User: + """Ensure the current user account is active.""" + + if not user.is_active: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="User account is disabled.", + ) + return user + + +def _user_role_names(user: User) -> set[str]: + roles: Iterable[Role] = getattr(user, "roles", []) or [] + return {role.name for role in roles} + + +def require_roles(*roles: str) -> Callable[[User], User]: + """Dependency factory enforcing membership in one of the given roles.""" + + required = tuple(role.strip() for role in roles if role.strip()) + if not required: + raise ValueError("require_roles requires at least one role name") + + def _dependency(user: User = Depends(require_authenticated_user)) -> User: + if user.is_superuser: + return user + + role_names = _user_role_names(user) + if not any(role in role_names for role in required): + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="Insufficient permissions for this action.", + ) + return user + + return _dependency + + +def require_any_role(*roles: str) -> Callable[[User], User]: + """Alias of require_roles for readability in some contexts.""" + + return require_roles(*roles) + + +def require_project_resource(*, require_manage: bool = False) -> Callable[[int], Project]: + """Dependency factory that resolves a project with authorization checks.""" + + def _dependency( + project_id: int, + user: User = Depends(require_authenticated_user), + uow: UnitOfWork = Depends(get_unit_of_work), + ) -> Project: + try: + return ensure_project_access_helper( + uow, + project_id=project_id, + user=user, + require_manage=require_manage, + ) + except EntityNotFoundError as exc: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=str(exc), + ) from exc + except AuthorizationError as exc: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail=str(exc), + ) from exc + + return _dependency + + +def require_scenario_resource( + *, require_manage: bool = False, with_children: bool = False +) -> Callable[[int], Scenario]: + """Dependency factory that resolves a scenario with authorization checks.""" + + def _dependency( + scenario_id: int, + user: User = Depends(require_authenticated_user), + uow: UnitOfWork = Depends(get_unit_of_work), + ) -> Scenario: + try: + return ensure_scenario_access_helper( + uow, + scenario_id=scenario_id, + user=user, + require_manage=require_manage, + with_children=with_children, + ) + except EntityNotFoundError as exc: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=str(exc), + ) from exc + except AuthorizationError as exc: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail=str(exc), + ) from exc + + return _dependency + + +def require_project_scenario_resource( + *, require_manage: bool = False, with_children: bool = False +) -> Callable[[int, int], Scenario]: + """Dependency factory ensuring a scenario belongs to the given project and is accessible.""" + + def _dependency( + project_id: int, + scenario_id: int, + user: User = Depends(require_authenticated_user), + uow: UnitOfWork = Depends(get_unit_of_work), + ) -> Scenario: + try: + return ensure_scenario_in_project_helper( + uow, + project_id=project_id, + scenario_id=scenario_id, + user=user, + require_manage=require_manage, + with_children=with_children, + ) + except EntityNotFoundError as exc: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=str(exc), + ) from exc + except AuthorizationError as exc: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail=str(exc), + ) from exc + + return _dependency diff --git a/routes/dashboard.py b/routes/dashboard.py index a8f32cf..dad24b6 100644 --- a/routes/dashboard.py +++ b/routes/dashboard.py @@ -6,7 +6,8 @@ from fastapi import APIRouter, Depends, Request from fastapi.responses import HTMLResponse from fastapi.templating import Jinja2Templates -from dependencies import get_unit_of_work +from dependencies import get_unit_of_work, require_authenticated_user +from models import User from models import ScenarioStatus from services.unit_of_work import UnitOfWork @@ -27,6 +28,8 @@ def _format_timestamp_with_time(moment: datetime | None) -> str | None: def _load_metrics(uow: UnitOfWork) -> dict[str, object]: + if not uow.projects or not uow.scenarios or not uow.financial_inputs: + raise RuntimeError("UnitOfWork repositories not initialised") total_projects = uow.projects.count() active_scenarios = uow.scenarios.count_by_status(ScenarioStatus.ACTIVE) pending_simulations = uow.scenarios.count_by_status(ScenarioStatus.DRAFT) @@ -40,11 +43,15 @@ def _load_metrics(uow: UnitOfWork) -> dict[str, object]: def _load_recent_projects(uow: UnitOfWork) -> list: + if not uow.projects: + raise RuntimeError("Project repository not initialised") return list(uow.projects.recent(limit=5)) def _load_simulation_updates(uow: UnitOfWork) -> list[dict[str, object]]: updates: list[dict[str, object]] = [] + if not uow.scenarios: + raise RuntimeError("Scenario repository not initialised") scenarios = uow.scenarios.recent(limit=5, with_project=True) for scenario in scenarios: project_name = scenario.project.name if scenario.project else f"Project #{scenario.project_id}" @@ -65,6 +72,9 @@ def _load_scenario_alerts( ) -> list[dict[str, object]]: alerts: list[dict[str, object]] = [] + if not uow.scenarios: + raise RuntimeError("Scenario repository not initialised") + drafts = uow.scenarios.list_by_status( ScenarioStatus.DRAFT, limit=3, with_project=True ) @@ -102,6 +112,7 @@ def _load_scenario_alerts( @router.get("/", response_class=HTMLResponse, include_in_schema=False, name="dashboard.home") def dashboard_home( request: Request, + _: User = Depends(require_authenticated_user), uow: UnitOfWork = Depends(get_unit_of_work), ) -> HTMLResponse: context = { diff --git a/routes/projects.py b/routes/projects.py index a449e02..f105881 100644 --- a/routes/projects.py +++ b/routes/projects.py @@ -6,8 +6,13 @@ from fastapi import APIRouter, Depends, Form, HTTPException, Request, status from fastapi.responses import HTMLResponse, RedirectResponse from fastapi.templating import Jinja2Templates -from dependencies import get_unit_of_work -from models import MiningOperationType, Project, ScenarioStatus +from dependencies import ( + get_unit_of_work, + require_any_role, + require_project_resource, + require_roles, +) +from models import MiningOperationType, Project, ScenarioStatus, User from schemas.project import ProjectCreate, ProjectRead, ProjectUpdate from services.exceptions import EntityConflictError, EntityNotFoundError from services.unit_of_work import UnitOfWork @@ -15,11 +20,20 @@ from services.unit_of_work import UnitOfWork router = APIRouter(prefix="/projects", tags=["Projects"]) templates = Jinja2Templates(directory="templates") +READ_ROLES = ("viewer", "analyst", "project_manager", "admin") +MANAGE_ROLES = ("project_manager", "admin") + def _to_read_model(project: Project) -> ProjectRead: return ProjectRead.model_validate(project) +def _require_project_repo(uow: UnitOfWork): + if not uow.projects: + raise RuntimeError("Project repository not initialised") + return uow.projects + + def _operation_type_choices() -> list[tuple[str, str]]: return [ (op.value, op.name.replace("_", " ").title()) for op in MiningOperationType @@ -27,18 +41,23 @@ def _operation_type_choices() -> list[tuple[str, str]]: @router.get("", response_model=List[ProjectRead]) -def list_projects(uow: UnitOfWork = Depends(get_unit_of_work)) -> List[ProjectRead]: - projects = uow.projects.list() +def list_projects( + _: User = Depends(require_any_role(*READ_ROLES)), + uow: UnitOfWork = Depends(get_unit_of_work), +) -> List[ProjectRead]: + projects = _require_project_repo(uow).list() return [_to_read_model(project) for project in projects] @router.post("", response_model=ProjectRead, status_code=status.HTTP_201_CREATED) def create_project( - payload: ProjectCreate, uow: UnitOfWork = Depends(get_unit_of_work) + payload: ProjectCreate, + _: User = Depends(require_roles(*MANAGE_ROLES)), + uow: UnitOfWork = Depends(get_unit_of_work), ) -> ProjectRead: project = Project(**payload.model_dump()) try: - created = uow.projects.create(project) + created = _require_project_repo(uow).create(project) except EntityConflictError as exc: raise HTTPException( status_code=status.HTTP_409_CONFLICT, detail=str(exc) @@ -53,9 +72,11 @@ def create_project( name="projects.project_list_page", ) def project_list_page( - request: Request, uow: UnitOfWork = Depends(get_unit_of_work) + request: Request, + _: User = Depends(require_any_role(*READ_ROLES)), + uow: UnitOfWork = Depends(get_unit_of_work), ) -> HTMLResponse: - projects = uow.projects.list(with_children=True) + projects = _require_project_repo(uow).list(with_children=True) for project in projects: setattr(project, "scenario_count", len(project.scenarios)) return templates.TemplateResponse( @@ -73,7 +94,9 @@ def project_list_page( include_in_schema=False, name="projects.create_project_form", ) -def create_project_form(request: Request) -> HTMLResponse: +def create_project_form( + request: Request, _: User = Depends(require_roles(*MANAGE_ROLES)) +) -> HTMLResponse: return templates.TemplateResponse( request, "projects/form.html", @@ -93,6 +116,7 @@ def create_project_form(request: Request) -> HTMLResponse: ) def create_project_submit( request: Request, + _: User = Depends(require_roles(*MANAGE_ROLES)), name: str = Form(...), location: str | None = Form(None), operation_type: str = Form(...), @@ -128,7 +152,7 @@ def create_project_submit( description=_normalise(description), ) try: - uow.projects.create(project) + _require_project_repo(uow).create(project) except EntityConflictError as exc: return templates.TemplateResponse( request, @@ -150,29 +174,18 @@ def create_project_submit( @router.get("/{project_id}", response_model=ProjectRead) -def get_project(project_id: int, uow: UnitOfWork = Depends(get_unit_of_work)) -> ProjectRead: - try: - project = uow.projects.get(project_id) - except EntityNotFoundError as exc: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail=str(exc) - ) from exc +def get_project(project: Project = Depends(require_project_resource())) -> ProjectRead: return _to_read_model(project) @router.put("/{project_id}", response_model=ProjectRead) def update_project( - project_id: int, payload: ProjectUpdate, + project: Project = Depends( + require_project_resource(require_manage=True) + ), uow: UnitOfWork = Depends(get_unit_of_work), ) -> ProjectRead: - try: - project = uow.projects.get(project_id) - except EntityNotFoundError as exc: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail=str(exc) - ) from exc - update_data = payload.model_dump(exclude_unset=True) for field, value in update_data.items(): setattr(project, field, value) @@ -182,13 +195,11 @@ def update_project( @router.delete("/{project_id}", status_code=status.HTTP_204_NO_CONTENT) -def delete_project(project_id: int, uow: UnitOfWork = Depends(get_unit_of_work)) -> None: - try: - uow.projects.delete(project_id) - except EntityNotFoundError as exc: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail=str(exc) - ) from exc +def delete_project( + project: Project = Depends(require_project_resource(require_manage=True)), + uow: UnitOfWork = Depends(get_unit_of_work), +) -> None: + _require_project_repo(uow).delete(project.id) @router.get( @@ -198,14 +209,11 @@ def delete_project(project_id: int, uow: UnitOfWork = Depends(get_unit_of_work)) name="projects.view_project", ) def view_project( - project_id: int, request: Request, uow: UnitOfWork = Depends(get_unit_of_work) + request: Request, + project: Project = Depends(require_project_resource()), + uow: UnitOfWork = Depends(get_unit_of_work), ) -> HTMLResponse: - try: - project = uow.projects.get(project_id, with_children=True) - except EntityNotFoundError as exc: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail=str(exc) - ) from exc + project = _require_project_repo(uow).get(project.id, with_children=True) scenarios = sorted(project.scenarios, key=lambda s: s.created_at) scenario_stats = { @@ -236,15 +244,11 @@ def view_project( name="projects.edit_project_form", ) def edit_project_form( - project_id: int, request: Request, uow: UnitOfWork = Depends(get_unit_of_work) + request: Request, + project: Project = Depends( + require_project_resource(require_manage=True) + ), ) -> HTMLResponse: - try: - project = uow.projects.get(project_id) - except EntityNotFoundError as exc: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail=str(exc) - ) from exc - return templates.TemplateResponse( request, "projects/form.html", @@ -252,10 +256,10 @@ def edit_project_form( "project": project, "operation_types": _operation_type_choices(), "form_action": request.url_for( - "projects.edit_project_submit", project_id=project_id + "projects.edit_project_submit", project_id=project.id ), "cancel_url": request.url_for( - "projects.view_project", project_id=project_id + "projects.view_project", project_id=project.id ), }, ) @@ -267,21 +271,16 @@ def edit_project_form( name="projects.edit_project_submit", ) def edit_project_submit( - project_id: int, request: Request, + project: Project = Depends( + require_project_resource(require_manage=True) + ), name: str = Form(...), location: str | None = Form(None), operation_type: str | None = Form(None), description: str | None = Form(None), uow: UnitOfWork = Depends(get_unit_of_work), ): - try: - project = uow.projects.get(project_id) - except EntityNotFoundError as exc: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail=str(exc) - ) from exc - def _normalise(value: str | None) -> str | None: if value is None: return None @@ -301,10 +300,10 @@ def edit_project_submit( "project": project, "operation_types": _operation_type_choices(), "form_action": request.url_for( - "projects.edit_project_submit", project_id=project_id + "projects.edit_project_submit", project_id=project.id ), "cancel_url": request.url_for( - "projects.view_project", project_id=project_id + "projects.view_project", project_id=project.id ), "error": "Invalid operation type.", }, @@ -315,6 +314,6 @@ def edit_project_submit( uow.flush() return RedirectResponse( - request.url_for("projects.view_project", project_id=project_id), + request.url_for("projects.view_project", project_id=project.id), status_code=status.HTTP_303_SEE_OTHER, ) diff --git a/routes/scenarios.py b/routes/scenarios.py index aa4803d..89d5060 100644 --- a/routes/scenarios.py +++ b/routes/scenarios.py @@ -7,8 +7,13 @@ from fastapi import APIRouter, Depends, Form, HTTPException, Request, status from fastapi.responses import HTMLResponse, RedirectResponse from fastapi.templating import Jinja2Templates -from dependencies import get_unit_of_work -from models import ResourceType, Scenario, ScenarioStatus +from dependencies import ( + get_unit_of_work, + require_any_role, + require_roles, + require_scenario_resource, +) +from models import ResourceType, Scenario, ScenarioStatus, User from schemas.scenario import ( ScenarioComparisonRequest, ScenarioComparisonResponse, @@ -26,6 +31,9 @@ from services.unit_of_work import UnitOfWork router = APIRouter(tags=["Scenarios"]) templates = Jinja2Templates(directory="templates") +READ_ROLES = ("viewer", "analyst", "project_manager", "admin") +MANAGE_ROLES = ("project_manager", "admin") + def _to_read_model(scenario: Scenario) -> ScenarioRead: return ScenarioRead.model_validate(scenario) @@ -44,20 +52,36 @@ def _scenario_status_choices() -> list[tuple[str, str]]: ] +def _require_project_repo(uow: UnitOfWork): + if not uow.projects: + raise RuntimeError("Project repository not initialised") + return uow.projects + + +def _require_scenario_repo(uow: UnitOfWork): + if not uow.scenarios: + raise RuntimeError("Scenario repository not initialised") + return uow.scenarios + + @router.get( "/projects/{project_id}/scenarios", response_model=List[ScenarioRead], ) def list_scenarios_for_project( - project_id: int, uow: UnitOfWork = Depends(get_unit_of_work) + project_id: int, + _: User = Depends(require_any_role(*READ_ROLES)), + uow: UnitOfWork = Depends(get_unit_of_work), ) -> List[ScenarioRead]: + project_repo = _require_project_repo(uow) + scenario_repo = _require_scenario_repo(uow) try: - uow.projects.get(project_id) + project_repo.get(project_id) except EntityNotFoundError as exc: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)) from exc - scenarios = uow.scenarios.list_for_project(project_id) + scenarios = scenario_repo.list_for_project(project_id) return [_to_read_model(scenario) for scenario in scenarios] @@ -69,10 +93,11 @@ def list_scenarios_for_project( def compare_scenarios( project_id: int, payload: ScenarioComparisonRequest, + _: User = Depends(require_any_role(*READ_ROLES)), uow: UnitOfWork = Depends(get_unit_of_work), ) -> ScenarioComparisonResponse: try: - uow.projects.get(project_id) + _require_project_repo(uow).get(project_id) except EntityNotFoundError as exc: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail=str(exc) @@ -116,10 +141,13 @@ def compare_scenarios( def create_scenario_for_project( project_id: int, payload: ScenarioCreate, + _: User = Depends(require_roles(*MANAGE_ROLES)), uow: UnitOfWork = Depends(get_unit_of_work), ) -> ScenarioRead: + project_repo = _require_project_repo(uow) + scenario_repo = _require_scenario_repo(uow) try: - uow.projects.get(project_id) + project_repo.get(project_id) except EntityNotFoundError as exc: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)) from exc @@ -127,7 +155,7 @@ def create_scenario_for_project( scenario = Scenario(project_id=project_id, **payload.model_dump()) try: - created = uow.scenarios.create(scenario) + created = scenario_repo.create(scenario) except EntityConflictError as exc: raise HTTPException( status_code=status.HTTP_409_CONFLICT, detail=str(exc)) from exc @@ -136,28 +164,19 @@ def create_scenario_for_project( @router.get("/scenarios/{scenario_id}", response_model=ScenarioRead) def get_scenario( - scenario_id: int, uow: UnitOfWork = Depends(get_unit_of_work) + scenario: Scenario = Depends(require_scenario_resource()), ) -> ScenarioRead: - try: - scenario = uow.scenarios.get(scenario_id) - except EntityNotFoundError as exc: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)) from exc return _to_read_model(scenario) @router.put("/scenarios/{scenario_id}", response_model=ScenarioRead) def update_scenario( - scenario_id: int, payload: ScenarioUpdate, + scenario: Scenario = Depends( + require_scenario_resource(require_manage=True) + ), uow: UnitOfWork = Depends(get_unit_of_work), ) -> ScenarioRead: - try: - scenario = uow.scenarios.get(scenario_id) - except EntityNotFoundError as exc: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)) from exc - update_data = payload.model_dump(exclude_unset=True) for field, value in update_data.items(): setattr(scenario, field, value) @@ -168,13 +187,12 @@ def update_scenario( @router.delete("/scenarios/{scenario_id}", status_code=status.HTTP_204_NO_CONTENT) def delete_scenario( - scenario_id: int, uow: UnitOfWork = Depends(get_unit_of_work) + scenario: Scenario = Depends( + require_scenario_resource(require_manage=True) + ), + uow: UnitOfWork = Depends(get_unit_of_work), ) -> None: - try: - uow.scenarios.delete(scenario_id) - except EntityNotFoundError as exc: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)) from exc + _require_scenario_repo(uow).delete(scenario.id) def _normalise(value: str | None) -> str | None: @@ -208,10 +226,13 @@ def _parse_discount_rate(value: str | None) -> float | None: name="scenarios.create_scenario_form", ) def create_scenario_form( - project_id: int, request: Request, uow: UnitOfWork = Depends(get_unit_of_work) + project_id: int, + request: Request, + _: User = Depends(require_roles(*MANAGE_ROLES)), + uow: UnitOfWork = Depends(get_unit_of_work), ) -> HTMLResponse: try: - project = uow.projects.get(project_id) + project = _require_project_repo(uow).get(project_id) except EntityNotFoundError as exc: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail=str(exc) @@ -243,6 +264,7 @@ def create_scenario_form( def create_scenario_submit( project_id: int, request: Request, + _: User = Depends(require_roles(*MANAGE_ROLES)), name: str = Form(...), description: str | None = Form(None), status_value: str = Form(ScenarioStatus.DRAFT.value), @@ -253,8 +275,10 @@ def create_scenario_submit( primary_resource: str | None = Form(None), uow: UnitOfWork = Depends(get_unit_of_work), ): + project_repo = _require_project_repo(uow) + scenario_repo = _require_scenario_repo(uow) try: - project = uow.projects.get(project_id) + project = project_repo.get(project_id) except EntityNotFoundError as exc: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail=str(exc) @@ -288,7 +312,7 @@ def create_scenario_submit( ) try: - uow.scenarios.create(scenario) + scenario_repo.create(scenario) except EntityConflictError as exc: return templates.TemplateResponse( request, @@ -322,16 +346,13 @@ def create_scenario_submit( name="scenarios.view_scenario", ) def view_scenario( - scenario_id: int, request: Request, uow: UnitOfWork = Depends(get_unit_of_work) + request: Request, + scenario: Scenario = Depends( + require_scenario_resource(with_children=True) + ), + uow: UnitOfWork = Depends(get_unit_of_work), ) -> HTMLResponse: - try: - scenario = uow.scenarios.get(scenario_id, with_children=True) - except EntityNotFoundError as exc: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail=str(exc) - ) from exc - - project = uow.projects.get(scenario.project_id) + project = _require_project_repo(uow).get(scenario.project_id) financial_inputs = sorted( scenario.financial_inputs, key=lambda item: item.created_at ) @@ -366,16 +387,13 @@ def view_scenario( name="scenarios.edit_scenario_form", ) def edit_scenario_form( - scenario_id: int, request: Request, uow: UnitOfWork = Depends(get_unit_of_work) + request: Request, + scenario: Scenario = Depends( + require_scenario_resource(require_manage=True) + ), + uow: UnitOfWork = Depends(get_unit_of_work), ) -> HTMLResponse: - try: - scenario = uow.scenarios.get(scenario_id) - except EntityNotFoundError as exc: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail=str(exc) - ) from exc - - project = uow.projects.get(scenario.project_id) + project = _require_project_repo(uow).get(scenario.project_id) return templates.TemplateResponse( request, @@ -386,10 +404,10 @@ def edit_scenario_form( "scenario_statuses": _scenario_status_choices(), "resource_types": _resource_type_choices(), "form_action": request.url_for( - "scenarios.edit_scenario_submit", scenario_id=scenario_id + "scenarios.edit_scenario_submit", scenario_id=scenario.id ), "cancel_url": request.url_for( - "scenarios.view_scenario", scenario_id=scenario_id + "scenarios.view_scenario", scenario_id=scenario.id ), }, ) @@ -401,8 +419,10 @@ def edit_scenario_form( name="scenarios.edit_scenario_submit", ) def edit_scenario_submit( - scenario_id: int, request: Request, + scenario: Scenario = Depends( + require_scenario_resource(require_manage=True) + ), name: str = Form(...), description: str | None = Form(None), status_value: str = Form(ScenarioStatus.DRAFT.value), @@ -413,14 +433,7 @@ def edit_scenario_submit( primary_resource: str | None = Form(None), uow: UnitOfWork = Depends(get_unit_of_work), ): - try: - scenario = uow.scenarios.get(scenario_id) - except EntityNotFoundError as exc: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail=str(exc) - ) from exc - - project = uow.projects.get(scenario.project_id) + project = _require_project_repo(uow).get(scenario.project_id) scenario.name = name.strip() scenario.description = _normalise(description) @@ -447,6 +460,6 @@ def edit_scenario_submit( uow.flush() return RedirectResponse( - request.url_for("scenarios.view_scenario", scenario_id=scenario_id), + request.url_for("scenarios.view_scenario", scenario_id=scenario.id), status_code=status.HTTP_303_SEE_OTHER, ) diff --git a/scripts/00_initial_data.py b/scripts/00_initial_data.py new file mode 100644 index 0000000..e189001 --- /dev/null +++ b/scripts/00_initial_data.py @@ -0,0 +1,20 @@ +from __future__ import annotations + +import logging + +from scripts.initial_data import load_config, seed_initial_data + + +def main() -> int: + logging.basicConfig(level=logging.INFO, format="[%(levelname)s] %(message)s") + try: + config = load_config() + seed_initial_data(config) + except Exception as exc: # pragma: no cover - operational guard + logging.exception("Seeding failed: %s", exc) + return 1 + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/__init__.py b/scripts/__init__.py new file mode 100644 index 0000000..395066d --- /dev/null +++ b/scripts/__init__.py @@ -0,0 +1 @@ +"""Utility scripts for CalMiner maintenance tasks.""" diff --git a/scripts/initial_data.py b/scripts/initial_data.py new file mode 100644 index 0000000..bd877b7 --- /dev/null +++ b/scripts/initial_data.py @@ -0,0 +1,183 @@ +from __future__ import annotations + +import logging +import os +from dataclasses import dataclass +from typing import Callable, Iterable + +from dotenv import load_dotenv + +from models import Role, User +from services.repositories import DEFAULT_ROLE_DEFINITIONS, RoleRepository, UserRepository +from services.unit_of_work import UnitOfWork + + +@dataclass +class SeedConfig: + admin_email: str + admin_username: str + admin_password: str + admin_roles: tuple[str, ...] + force_reset: bool + + +@dataclass +class RoleSeedResult: + created: int + updated: int + total: int + + +@dataclass +class AdminSeedResult: + created_user: bool + updated_user: bool + password_rotated: bool + roles_granted: int + + +def parse_bool(value: str | None) -> bool: + if value is None: + return False + return value.strip().lower() in {"1", "true", "yes", "on"} + + +def normalise_role_list(raw_value: str | None) -> tuple[str, ...]: + if not raw_value: + return ("admin",) + parts = [segment.strip() for segment in raw_value.split(",") if segment.strip()] + if "admin" not in parts: + parts.insert(0, "admin") + seen: set[str] = set() + ordered: list[str] = [] + for role_name in parts: + if role_name not in seen: + ordered.append(role_name) + seen.add(role_name) + return tuple(ordered) + + +def load_config() -> SeedConfig: + load_dotenv() + admin_email = os.getenv("CALMINER_SEED_ADMIN_EMAIL", "admin@calminer.local") + admin_username = os.getenv("CALMINER_SEED_ADMIN_USERNAME", "admin") + admin_password = os.getenv("CALMINER_SEED_ADMIN_PASSWORD", "ChangeMe123!") + admin_roles = normalise_role_list(os.getenv("CALMINER_SEED_ADMIN_ROLES")) + force_reset = parse_bool(os.getenv("CALMINER_SEED_FORCE")) + return SeedConfig( + admin_email=admin_email, + admin_username=admin_username, + admin_password=admin_password, + admin_roles=admin_roles, + force_reset=force_reset, + ) + + +def ensure_default_roles( + role_repo: RoleRepository, + definitions: Iterable[dict[str, str]] = DEFAULT_ROLE_DEFINITIONS, +) -> RoleSeedResult: + created = 0 + updated = 0 + total = 0 + for definition in definitions: + total += 1 + existing = role_repo.get_by_name(definition["name"]) + if existing is None: + role_repo.create(Role(**definition)) + created += 1 + continue + changed = False + if existing.display_name != definition["display_name"]: + existing.display_name = definition["display_name"] + changed = True + if existing.description != definition["description"]: + existing.description = definition["description"] + changed = True + if changed: + updated += 1 + role_repo.session.flush() + return RoleSeedResult(created=created, updated=updated, total=total) + + +def ensure_admin_user( + user_repo: UserRepository, + role_repo: RoleRepository, + config: SeedConfig, +) -> AdminSeedResult: + created_user = False + updated_user = False + password_rotated = False + roles_granted = 0 + + user = user_repo.get_by_email(config.admin_email, with_roles=True) + if user is None: + user = User( + email=config.admin_email, + username=config.admin_username, + password_hash=User.hash_password(config.admin_password), + is_active=True, + is_superuser=True, + ) + user_repo.create(user) + created_user = True + else: + if user.username != config.admin_username: + user.username = config.admin_username + updated_user = True + if not user.is_active: + user.is_active = True + updated_user = True + if not user.is_superuser: + user.is_superuser = True + updated_user = True + if config.force_reset: + user.password_hash = User.hash_password(config.admin_password) + password_rotated = True + updated_user = True + user_repo.session.flush() + + for role_name in config.admin_roles: + role = role_repo.get_by_name(role_name) + if role is None: + logging.warning("Role '%s' is not defined and will be skipped", role_name) + continue + already_assigned = any(assignment.role_id == role.id for assignment in user.role_assignments) + if already_assigned: + continue + user_repo.assign_role(user_id=user.id, role_id=role.id, granted_by=user.id) + roles_granted += 1 + + return AdminSeedResult( + created_user=created_user, + updated_user=updated_user, + password_rotated=password_rotated, + roles_granted=roles_granted, + ) + + +def seed_initial_data( + config: SeedConfig, + *, + unit_of_work_factory: Callable[[], UnitOfWork] | None = None, +) -> None: + logging.info("Starting initial data seeding") + factory = unit_of_work_factory or UnitOfWork + with factory() as uow: + assert uow.roles is not None and uow.users is not None + role_result = ensure_default_roles(uow.roles) + admin_result = ensure_admin_user(uow.users, uow.roles, config) + logging.info( + "Roles processed: %s total, %s created, %s updated", + role_result.total, + role_result.created, + role_result.updated, + ) + logging.info( + "Admin user: created=%s updated=%s password_rotated=%s roles_granted=%s", + admin_result.created_user, + admin_result.updated_user, + admin_result.password_rotated, + admin_result.roles_granted, + ) + logging.info("Initial data seeding completed successfully") \ No newline at end of file diff --git a/services/authorization.py b/services/authorization.py new file mode 100644 index 0000000..3a19a39 --- /dev/null +++ b/services/authorization.py @@ -0,0 +1,104 @@ +from __future__ import annotations + +from typing import Iterable + +from models import Project, Role, Scenario, User +from services.exceptions import AuthorizationError, EntityNotFoundError +from services.repositories import ProjectRepository, ScenarioRepository +from services.unit_of_work import UnitOfWork + +READ_ROLES: frozenset[str] = frozenset( + {"viewer", "analyst", "project_manager", "admin"} +) +MANAGE_ROLES: frozenset[str] = frozenset({"project_manager", "admin"}) + + +def _user_role_names(user: User) -> set[str]: + roles: Iterable[Role] = getattr(user, "roles", []) or [] + return {role.name for role in roles} + + +def _require_project_repo(uow: UnitOfWork) -> ProjectRepository: + if not uow.projects: + raise RuntimeError("Project repository not initialised") + return uow.projects + + +def _require_scenario_repo(uow: UnitOfWork) -> ScenarioRepository: + if not uow.scenarios: + raise RuntimeError("Scenario repository not initialised") + return uow.scenarios + + +def _assert_user_can_access(user: User, *, require_manage: bool) -> None: + if not user.is_active: + raise AuthorizationError("User account is disabled.") + if user.is_superuser: + return + + allowed = MANAGE_ROLES if require_manage else READ_ROLES + if not _user_role_names(user) & allowed: + raise AuthorizationError( + "Insufficient role permissions for this action.") + + +def ensure_project_access( + uow: UnitOfWork, + *, + project_id: int, + user: User, + require_manage: bool = False, +) -> Project: + """Resolve a project and ensure the user holds the required permissions.""" + + repo = _require_project_repo(uow) + project = repo.get(project_id) + _assert_user_can_access(user, require_manage=require_manage) + return project + + +def ensure_scenario_access( + uow: UnitOfWork, + *, + scenario_id: int, + user: User, + require_manage: bool = False, + with_children: bool = False, +) -> Scenario: + """Resolve a scenario and ensure the user holds the required permissions.""" + + repo = _require_scenario_repo(uow) + scenario = repo.get(scenario_id, with_children=with_children) + _assert_user_can_access(user, require_manage=require_manage) + return scenario + + +def ensure_scenario_in_project( + uow: UnitOfWork, + *, + project_id: int, + scenario_id: int, + user: User, + require_manage: bool = False, + with_children: bool = False, +) -> Scenario: + """Resolve a scenario ensuring it belongs to the project and the user may access it.""" + + project = ensure_project_access( + uow, + project_id=project_id, + user=user, + require_manage=require_manage, + ) + scenario = ensure_scenario_access( + uow, + scenario_id=scenario_id, + user=user, + require_manage=require_manage, + with_children=with_children, + ) + if scenario.project_id != project.id: + raise EntityNotFoundError( + f"Scenario {scenario_id} does not belong to project {project_id}." + ) + return scenario diff --git a/services/exceptions.py b/services/exceptions.py index bfd1ce4..786fd43 100644 --- a/services/exceptions.py +++ b/services/exceptions.py @@ -12,6 +12,10 @@ class EntityConflictError(Exception): """Raised when attempting to create or update an entity that violates uniqueness.""" +class AuthorizationError(Exception): + """Raised when a user lacks permission to perform an action.""" + + @dataclass(eq=False) class ScenarioValidationError(Exception): """Raised when scenarios fail comparison validation rules.""" diff --git a/services/repositories.py b/services/repositories.py index 1f82691..170e33e 100644 --- a/services/repositories.py +++ b/services/repositories.py @@ -57,6 +57,10 @@ class ProjectRepository: raise EntityNotFoundError(f"Project {project_id} not found") return project + def exists(self, project_id: int) -> bool: + stmt = select(Project.id).where(Project.id == project_id).limit(1) + return self.session.execute(stmt).scalar_one_or_none() is not None + def create(self, project: Project) -> Project: self.session.add(project) try: @@ -133,6 +137,10 @@ class ScenarioRepository: raise EntityNotFoundError(f"Scenario {scenario_id} not found") return scenario + def exists(self, scenario_id: int) -> bool: + stmt = select(Scenario.id).where(Scenario.id == scenario_id).limit(1) + return self.session.execute(stmt).scalar_one_or_none() is not None + def create(self, scenario: Scenario) -> Scenario: self.session.add(scenario) try: diff --git a/tests/conftest.py b/tests/conftest.py index 6ec51e5..10f8135 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -3,7 +3,7 @@ from __future__ import annotations from collections.abc import Callable, Iterator import pytest -from fastapi import FastAPI +from fastapi import FastAPI, Request from fastapi.testclient import TestClient from sqlalchemy import create_engine from sqlalchemy.engine import Engine @@ -11,12 +11,14 @@ from sqlalchemy.orm import sessionmaker from sqlalchemy.pool import StaticPool from config.database import Base -from dependencies import get_unit_of_work +from dependencies import get_auth_session, get_unit_of_work +from models import User from routes.auth import router as auth_router from routes.dashboard import router as dashboard_router from routes.projects import router as projects_router from routes.scenarios import router as scenarios_router from services.unit_of_work import UnitOfWork +from services.session import AuthSession, SessionTokens @pytest.fixture() @@ -55,6 +57,28 @@ def app(session_factory: sessionmaker) -> FastAPI: yield uow application.dependency_overrides[get_unit_of_work] = _override_uow + + with UnitOfWork(session_factory=session_factory) as uow: + assert uow.users is not None + uow.ensure_default_roles() + user = User( + email="test-superuser@example.com", + username="test-superuser", + password_hash=User.hash_password("test-password"), + is_active=True, + is_superuser=True, + ) + uow.users.create(user) + user = uow.users.get(user.id, with_roles=True) + + def _override_auth_session(request: Request) -> AuthSession: + session = AuthSession(tokens=SessionTokens( + access_token="test", refresh_token="test")) + session.user = user + request.state.auth_session = session + return session + + application.dependency_overrides[get_auth_session] = _override_auth_session return application diff --git a/tests/scripts/test_initial_data_seed.py b/tests/scripts/test_initial_data_seed.py new file mode 100644 index 0000000..ec66ea6 --- /dev/null +++ b/tests/scripts/test_initial_data_seed.py @@ -0,0 +1,156 @@ +from __future__ import annotations + +import logging +from collections.abc import Callable + +import pytest +from sqlalchemy import create_engine +from sqlalchemy.orm import Session, sessionmaker + +from config.database import Base +from scripts import initial_data +from scripts.initial_data import AdminSeedResult, RoleSeedResult, SeedConfig +from services.repositories import DEFAULT_ROLE_DEFINITIONS +from services.unit_of_work import UnitOfWork + + +@pytest.fixture() +def in_memory_session_factory() -> Callable[[], Session]: + engine = create_engine("sqlite+pysqlite:///:memory:", future=True) + Base.metadata.create_all(engine) + factory = sessionmaker(bind=engine, autoflush=False, autocommit=False, future=True) + + def _session_factory() -> Session: + return factory() + + return _session_factory + + +@pytest.fixture() +def uow(in_memory_session_factory: Callable[[], Session]) -> UnitOfWork: + return UnitOfWork(session_factory=in_memory_session_factory) + + +def test_ensure_default_roles_idempotent(uow: UnitOfWork) -> None: + with uow as working: + assert working.roles is not None + result_first = initial_data.ensure_default_roles(working.roles, DEFAULT_ROLE_DEFINITIONS) + assert result_first == RoleSeedResult(created=4, updated=0, total=4) + + with uow as working: + assert working.roles is not None + result_second = initial_data.ensure_default_roles(working.roles, DEFAULT_ROLE_DEFINITIONS) + assert result_second == RoleSeedResult(created=0, updated=0, total=4) + + +def test_ensure_admin_user_creates_and_assigns_roles(uow: UnitOfWork) -> None: + config = SeedConfig( + admin_email="admin@example.com", + admin_username="admin", + admin_password="secret", + admin_roles=("admin", "viewer"), + force_reset=False, + ) + + with uow as working: + assert working.roles is not None + assert working.users is not None + initial_data.ensure_default_roles(working.roles, DEFAULT_ROLE_DEFINITIONS) + result = initial_data.ensure_admin_user(working.users, working.roles, config) + assert result == AdminSeedResult( + created_user=True, + updated_user=False, + password_rotated=False, + roles_granted=2, + ) + + with uow as working: + assert working.roles is not None + assert working.users is not None + result_again = initial_data.ensure_admin_user(working.users, working.roles, config) + assert result_again == AdminSeedResult( + created_user=False, + updated_user=False, + password_rotated=False, + roles_granted=0, + ) + + with uow as working: + assert working.users is not None + user = working.users.get_by_email("admin@example.com", with_roles=True) + assert user is not None + assert user.is_active is True + assert user.is_superuser is True + role_names = {role.name for role in user.roles} + assert role_names == {"admin", "viewer"} + + +def test_ensure_admin_user_force_reset_rotates_password(uow: UnitOfWork) -> None: + base_config = SeedConfig( + admin_email="admin@example.com", + admin_username="admin", + admin_password="first", + admin_roles=("admin",), + force_reset=False, + ) + + with uow as working: + assert working.roles is not None + assert working.users is not None + initial_data.ensure_default_roles(working.roles, DEFAULT_ROLE_DEFINITIONS) + initial_data.ensure_admin_user(working.users, working.roles, base_config) + + rotate_config = SeedConfig( + admin_email="admin@example.com", + admin_username="admin", + admin_password="second", + admin_roles=("admin",), + force_reset=True, + ) + + with uow as working: + assert working.users is not None + user_before = working.users.get_by_email("admin@example.com") + assert user_before is not None + old_hash = user_before.password_hash + + with uow as working: + assert working.roles is not None + assert working.users is not None + result = initial_data.ensure_admin_user(working.users, working.roles, rotate_config) + assert result.password_rotated is True + + with uow as working: + assert working.users is not None + user_after = working.users.get_by_email("admin@example.com") + assert user_after is not None + assert user_after.password_hash != old_hash + + +def test_seed_initial_data_logs_results( + caplog, + in_memory_session_factory: Callable[[], Session], +) -> None: + caplog.set_level(logging.INFO) + config = SeedConfig( + admin_email="seed@example.com", + admin_username="seed", + admin_password="seed-pass", + admin_roles=("admin",), + force_reset=False, + ) + + initial_data.seed_initial_data( + config, + unit_of_work_factory=lambda: UnitOfWork(session_factory=in_memory_session_factory), + ) + + assert "Starting initial data seeding" in caplog.text + assert "Initial data seeding completed successfully" in caplog.text + + with UnitOfWork(session_factory=in_memory_session_factory) as check_uow: + assert check_uow.users is not None + assert check_uow.roles is not None + user = check_uow.users.get_by_email("seed@example.com") + assert user is not None + assert check_uow.roles.get_by_name("admin") is not None diff --git a/tests/test_authorization_helpers.py b/tests/test_authorization_helpers.py new file mode 100644 index 0000000..a408578 --- /dev/null +++ b/tests/test_authorization_helpers.py @@ -0,0 +1,165 @@ +from __future__ import annotations + +import pytest + +from models import MiningOperationType, Project, Scenario, ScenarioStatus, User +from services.authorization import ( + ensure_project_access, + ensure_scenario_access, + ensure_scenario_in_project, +) +from services.exceptions import AuthorizationError, EntityNotFoundError +from services.unit_of_work import UnitOfWork + + +def _create_user_with_roles( + uow: UnitOfWork, + *, + email: str, + username: str, + roles: set[str], +) -> User: + assert uow.users is not None + assert uow.roles is not None + + user = User( + email=email, + username=username, + password_hash=User.hash_password("secret"), + is_active=True, + ) + uow.users.create(user) + uow.ensure_default_roles() + + for role_name in roles: + role = uow.roles.get_by_name(role_name) + assert role is not None, f"Role {role_name} should exist" + uow.users.assign_role(user_id=user.id, role_id=role.id) + + return uow.users.get(user.id, with_roles=True) + + +def _create_project(uow: UnitOfWork, name: str) -> Project: + assert uow.projects is not None + project = Project( + name=name, + location=None, + operation_type=MiningOperationType.OTHER, + description=None, + ) + uow.projects.create(project) + return project + + +def _create_scenario(uow: UnitOfWork, project: Project, name: str) -> Scenario: + assert uow.scenarios is not None + scenario = Scenario( + project_id=project.id, + name=name, + description=None, + status=ScenarioStatus.DRAFT, + ) + uow.scenarios.create(scenario) + return scenario + + +def test_ensure_project_access_allows_view_roles(unit_of_work_factory) -> None: + with unit_of_work_factory() as uow: + project = _create_project(uow, "Project A") + user = _create_user_with_roles( + uow, + email="viewer@example.com", + username="viewer", + roles={"viewer"}, + ) + + resolved = ensure_project_access( + uow, + project_id=project.id, + user=user, + ) + assert resolved.id == project.id + + with pytest.raises(AuthorizationError): + ensure_project_access( + uow, + project_id=project.id, + user=user, + require_manage=True, + ) + + +def test_ensure_project_access_allows_manage_roles(unit_of_work_factory) -> None: + with unit_of_work_factory() as uow: + project = _create_project(uow, "Project B") + user = _create_user_with_roles( + uow, + email="manager@example.com", + username="manager", + roles={"project_manager"}, + ) + + resolved = ensure_project_access( + uow, + project_id=project.id, + user=user, + require_manage=True, + ) + assert resolved.id == project.id + + +def test_ensure_scenario_access(unit_of_work_factory) -> None: + with unit_of_work_factory() as uow: + project = _create_project(uow, "Project C") + scenario = _create_scenario(uow, project, "Scenario C1") + user = _create_user_with_roles( + uow, + email="analyst@example.com", + username="analyst", + roles={"analyst"}, + ) + + resolved = ensure_scenario_access( + uow, + scenario_id=scenario.id, + user=user, + ) + assert resolved.id == scenario.id + + with pytest.raises(AuthorizationError): + ensure_scenario_access( + uow, + scenario_id=scenario.id, + user=user, + require_manage=True, + ) + + +def test_ensure_scenario_in_project_validates_membership(unit_of_work_factory) -> None: + with unit_of_work_factory() as uow: + project_one = _create_project(uow, "Project D") + project_two = _create_project(uow, "Project E") + scenario = _create_scenario(uow, project_one, "Scenario D1") + user = _create_user_with_roles( + uow, + email="manager2@example.com", + username="manager2", + roles={"project_manager"}, + ) + + resolved = ensure_scenario_in_project( + uow, + project_id=project_one.id, + scenario_id=scenario.id, + user=user, + require_manage=True, + ) + assert resolved.id == scenario.id + + with pytest.raises(EntityNotFoundError): + ensure_scenario_in_project( + uow, + project_id=project_two.id, + scenario_id=scenario.id, + user=user, + ) diff --git a/tests/test_scenario_validation.py b/tests/test_scenario_validation.py index bb26203..a9aaf07 100644 --- a/tests/test_scenario_validation.py +++ b/tests/test_scenario_validation.py @@ -6,7 +6,7 @@ from typing import cast from uuid import uuid4 import pytest -from fastapi import FastAPI +from fastapi import FastAPI, Request from fastapi.testclient import TestClient from pydantic import ValidationError from sqlalchemy import create_engine @@ -15,13 +15,14 @@ from sqlalchemy.engine import Engine from sqlalchemy.pool import StaticPool from config.database import Base -from dependencies import get_unit_of_work +from dependencies import get_auth_session, get_unit_of_work from models import ( MiningOperationType, Project, ResourceType, Scenario, ScenarioStatus, + User, ) from schemas.scenario import ( ScenarioComparisonRequest, @@ -30,6 +31,7 @@ from schemas.scenario import ( from services.exceptions import ScenarioValidationError from services.scenario_validation import ScenarioComparisonValidator from services.unit_of_work import UnitOfWork +from services.session import AuthSession, SessionTokens from routes.scenarios import router as scenarios_router @@ -159,6 +161,28 @@ def api_client(session_factory) -> Iterator[TestClient]: yield uow app.dependency_overrides[get_unit_of_work] = _override_uow + + with UnitOfWork(session_factory=session_factory) as uow: + assert uow.users is not None + uow.ensure_default_roles() + user = User( + email="test-scenarios@example.com", + username="scenario-tester", + password_hash=User.hash_password("password"), + is_active=True, + is_superuser=True, + ) + uow.users.create(user) + user = uow.users.get(user.id, with_roles=True) + + def _override_auth_session(request: Request) -> AuthSession: + session = AuthSession(tokens=SessionTokens( + access_token="test", refresh_token="test")) + session.user = user + request.state.auth_session = session + return session + + app.dependency_overrides[get_auth_session] = _override_auth_session client = TestClient(app) try: yield client @@ -171,6 +195,8 @@ def _create_project_with_scenarios( scenario_overrides: list[dict[str, object]], ) -> tuple[int, list[int]]: with UnitOfWork(session_factory=session_factory) as uow: + assert uow.projects is not None + assert uow.scenarios is not None project_name = f"Project {uuid4()}" project = Project(name=project_name, operation_type=MiningOperationType.OPEN_PIT) -- 2.39.5 From 118657491ca7737cb9ff0427ceaf08f5299060fc Mon Sep 17 00:00:00 2001 From: zwitschi Date: Sun, 9 Nov 2025 23:27:10 +0100 Subject: [PATCH 024/109] feat: add tests for authorization guards and role-based access control --- changelog.md | 2 + tests/test_authorization_integration.py | 208 ++++++++++++++++++ tests/test_dependencies_guards.py | 267 ++++++++++++++++++++++++ 3 files changed, 477 insertions(+) create mode 100644 tests/test_authorization_integration.py create mode 100644 tests/test_dependencies_guards.py diff --git a/changelog.md b/changelog.md index ed4c7fc..bdb8c93 100644 --- a/changelog.md +++ b/changelog.md @@ -25,3 +25,5 @@ ## 2025-11-10 - Extended authorization helper layer with project/scenario ownership lookups, integrated them into FastAPI dependencies, refreshed pytest fixtures to keep the suite authenticated, and documented the new patterns across RBAC plan and security guides. +- Added dedicated pytest coverage for guard dependencies, exercising success plus failure paths (missing session, inactive user, missing roles, project/scenario access errors) via `tests/test_dependencies_guards.py`. +- Added integration tests in `tests/test_authorization_integration.py` verifying anonymous 401 responses, role-based 403s, and authorized project manager flows across API and UI endpoints. diff --git a/tests/test_authorization_integration.py b/tests/test_authorization_integration.py new file mode 100644 index 0000000..825b414 --- /dev/null +++ b/tests/test_authorization_integration.py @@ -0,0 +1,208 @@ +from __future__ import annotations + +from contextlib import contextmanager +from uuid import uuid4 + +import pytest +from fastapi import Request, status + +from dependencies import get_auth_session +from models import MiningOperationType, Project, User +from services.session import AuthSession, SessionTokens + + +@pytest.fixture() +def auth_session_context(client): + """Allow tests to swap the current auth session for the test app.""" + + original_override = client.app.dependency_overrides[get_auth_session] + + @contextmanager + def _use(user: User | None): + def _override(request: Request) -> AuthSession: + if user is None: + session = AuthSession.anonymous() + else: + session = AuthSession(tokens=SessionTokens( + access_token="token", refresh_token="refresh")) + session.user = user + request.state.auth_session = session + return session + + client.app.dependency_overrides[get_auth_session] = _override + try: + yield + finally: + client.app.dependency_overrides[get_auth_session] = original_override + + return _use + + +def _unique(name: str) -> str: + return f"{name}-{uuid4().hex}" + + +def _create_user(uow, *, roles: tuple[str, ...] = (), is_superuser: bool = False) -> User: + assert uow.users and uow.roles + uow.ensure_default_roles() + user = User( + email=f"{_unique('user')}@example.com", + username=_unique('user'), + password_hash=User.hash_password("password"), + is_active=True, + is_superuser=is_superuser, + ) + uow.users.create(user) + uow.flush() + + for role_name in roles: + role = uow.roles.get_by_name(role_name) + if role is None: # pragma: no cover - defensive guard + raise AssertionError(f"Role {role_name} not found") + uow.users.assign_role(user_id=user.id, role_id=role.id) + return uow.users.get(user.id, with_roles=True) + + +def _create_project(uow) -> Project: + assert uow.projects + project = Project( + name=_unique('project'), + location="Integration Site", + operation_type=MiningOperationType.OPEN_PIT, + ) + uow.projects.create(project) + uow.flush() + return project + + +class TestAuthenticationRequirements: + def test_api_projects_list_requires_login(self, client, auth_session_context): + with auth_session_context(None): + response = client.get("/projects") + + assert response.status_code == status.HTTP_401_UNAUTHORIZED + assert response.json()["detail"] == "Authentication required." + + def test_ui_project_list_requires_login(self, client, auth_session_context): + with auth_session_context(None): + response = client.get("/projects/ui") + + assert response.status_code == status.HTTP_401_UNAUTHORIZED + + +class TestRoleRestrictions: + def test_api_projects_create_forbidden_for_viewer( + self, + client, + auth_session_context, + unit_of_work_factory, + ) -> None: + with unit_of_work_factory() as uow: + viewer = _create_user(uow, roles=("viewer",)) + + payload = { + "name": _unique("project"), + "location": "Restricted", + "operation_type": MiningOperationType.OPEN_PIT.value, + "description": "Test restriction", + } + with auth_session_context(viewer): + response = client.post("/projects", json=payload) + + assert response.status_code == status.HTTP_403_FORBIDDEN + assert response.json()[ + "detail"] == "Insufficient permissions for this action." + + def test_api_projects_create_allows_project_manager( + self, + client, + auth_session_context, + unit_of_work_factory, + ) -> None: + with unit_of_work_factory() as uow: + manager = _create_user(uow, roles=("project_manager",)) + + payload = { + "name": _unique("managed-project"), + "location": "Permitted", + "operation_type": MiningOperationType.OPEN_PIT.value, + "description": "Authorized creation", + } + with auth_session_context(manager): + response = client.post("/projects", json=payload) + + assert response.status_code == status.HTTP_201_CREATED + assert response.json()["name"] == payload["name"] + + def test_api_projects_update_forbidden_for_viewer( + self, + client, + auth_session_context, + unit_of_work_factory, + ) -> None: + with unit_of_work_factory() as uow: + project = _create_project(uow) + viewer = _create_user(uow, roles=("viewer",)) + + with auth_session_context(viewer): + response = client.put( + f"/projects/{project.id}", + json={"description": "Updated"}, + ) + + assert response.status_code == status.HTTP_403_FORBIDDEN + assert response.json()[ + "detail"] == "Insufficient role permissions for this action." + + def test_api_projects_update_allows_manager( + self, + client, + auth_session_context, + unit_of_work_factory, + ) -> None: + with unit_of_work_factory() as uow: + project = _create_project(uow) + manager = _create_user(uow, roles=("project_manager",)) + + updated_description = "Manager updated description" + with auth_session_context(manager): + response = client.put( + f"/projects/{project.id}", + json={"description": updated_description}, + ) + + assert response.status_code == status.HTTP_200_OK + assert response.json()["description"] == updated_description + + def test_ui_project_edit_forbidden_for_viewer( + self, + client, + auth_session_context, + unit_of_work_factory, + ) -> None: + with unit_of_work_factory() as uow: + project = _create_project(uow) + viewer = _create_user(uow, roles=("viewer",)) + + with auth_session_context(viewer): + response = client.get(f"/projects/{project.id}/edit") + + assert response.status_code == status.HTTP_403_FORBIDDEN + assert response.json()[ + "detail"] == "Insufficient role permissions for this action." + + def test_ui_project_edit_accessible_to_manager( + self, + client, + auth_session_context, + unit_of_work_factory, + ) -> None: + with unit_of_work_factory() as uow: + project = _create_project(uow) + manager = _create_user(uow, roles=("project_manager",)) + + with auth_session_context(manager): + response = client.get(f"/projects/{project.id}/edit") + + assert response.status_code == status.HTTP_200_OK + assert response.template.name == "projects/form.html" diff --git a/tests/test_dependencies_guards.py b/tests/test_dependencies_guards.py new file mode 100644 index 0000000..40439f8 --- /dev/null +++ b/tests/test_dependencies_guards.py @@ -0,0 +1,267 @@ +from __future__ import annotations + +from uuid import uuid4 + +import pytest +from fastapi import HTTPException, status + +from dependencies import ( + require_any_role, + require_authenticated_user, + require_current_user, + require_project_resource, + require_project_scenario_resource, + require_roles, + require_scenario_resource, +) +from models import Project, Scenario, User +from services.session import AuthSession, SessionTokens + + +@pytest.fixture() +def uow(unit_of_work_factory): + with unit_of_work_factory() as uow: + assert uow.users and uow.roles and uow.projects and uow.scenarios + uow.ensure_default_roles() + yield uow + + +def _unique(prefix: str) -> str: + return f"{prefix}-{uuid4().hex}" + + +def _create_user( + uow, + *, + roles: tuple[str, ...] = (), + is_active: bool = True, + is_superuser: bool = False, +) -> User: + user = User( + email=f"{_unique('user')}@example.com", + username=_unique('user'), + password_hash=User.hash_password("password"), + is_active=is_active, + is_superuser=is_superuser, + ) + assert uow.users and uow.roles + uow.users.create(user) + uow.flush() + + for role_name in roles: + role = uow.roles.get_by_name(role_name) + if role is None: # pragma: no cover - defensive for missing roles + raise AssertionError(f"Role {role_name} expected in test database") + uow.users.assign_role(user_id=user.id, role_id=role.id) + return uow.users.get(user.id, with_roles=True) + + +def _create_project(uow) -> Project: + assert uow.projects + project = Project(name=_unique('project'), location="Test Site") + uow.projects.create(project) + uow.flush() + return project + + +def _create_scenario(uow, project: Project) -> Scenario: + assert uow.scenarios + scenario = Scenario(project_id=project.id, name=_unique('scenario')) + uow.scenarios.create(scenario) + uow.flush() + return scenario + + +def test_require_current_user_returns_authenticated_user(): + user = User( + email="user@example.com", + username="user", + password_hash=User.hash_password("password"), + is_active=True, + ) + session = AuthSession(tokens=SessionTokens( + access_token="token", refresh_token=None)) + session.user = user + + result = require_current_user(session=session) + + assert result is user + + +def test_require_current_user_raises_when_session_missing(): + anonymous_session = AuthSession.anonymous() + + with pytest.raises(HTTPException) as exc: + require_current_user(session=anonymous_session) + + assert exc.value.status_code == status.HTTP_401_UNAUTHORIZED + assert exc.value.detail == "Authentication required." + + +def test_require_authenticated_user_blocks_inactive_users(): + user = User( + email="user@example.com", + username="user", + password_hash=User.hash_password("password"), + is_active=False, + ) + + with pytest.raises(HTTPException) as exc: + require_authenticated_user(user=user) + + assert exc.value.status_code == status.HTTP_403_FORBIDDEN + assert exc.value.detail == "User account is disabled." + + +def test_require_roles_accepts_user_with_role(uow): + user = _create_user(uow, roles=("viewer",)) + + dependency = require_roles("viewer") + result = dependency(user=user) + + assert result is user + + +def test_require_roles_allows_superuser_without_matching_role(uow): + user = _create_user(uow, roles=(), is_superuser=True) + + dependency = require_roles("project_manager") + result = dependency(user=user) + + assert result is user + + +def test_require_roles_rejects_user_missing_required_role(uow): + user = _create_user(uow, roles=("viewer",)) + + dependency = require_any_role("project_manager", "admin") + with pytest.raises(HTTPException) as exc: + dependency(user=user) + + assert exc.value.status_code == status.HTTP_403_FORBIDDEN + assert exc.value.detail == "Insufficient permissions for this action." + + +def test_require_roles_raises_value_error_when_no_roles_provided(): + with pytest.raises(ValueError) as exc: + require_roles() + + assert str(exc.value) == "require_roles requires at least one role name" + + +def test_require_project_resource_returns_project_for_authorized_user(uow): + user = _create_user(uow, roles=("viewer",)) + project = _create_project(uow) + + dependency = require_project_resource() + result = dependency(project.id, user=user, uow=uow) + + assert result.id == project.id + + +def test_require_project_resource_enforces_manage_requirement(uow): + user = _create_user(uow, roles=("viewer",)) + project = _create_project(uow) + + dependency = require_project_resource(require_manage=True) + with pytest.raises(HTTPException) as exc: + dependency(project.id, user=user, uow=uow) + + assert exc.value.status_code == status.HTTP_403_FORBIDDEN + assert exc.value.detail == "Insufficient role permissions for this action." + + +def test_require_project_resource_raises_not_found_for_missing_project(uow): + user = _create_user(uow, roles=("viewer",)) + + dependency = require_project_resource() + with pytest.raises(HTTPException) as exc: + dependency(9999, user=user, uow=uow) + + assert exc.value.status_code == status.HTTP_404_NOT_FOUND + assert exc.value.detail == "Project 9999 not found" + + +def test_require_project_resource_rejects_inactive_user(uow): + user = _create_user(uow, roles=("viewer",), is_active=False) + project = _create_project(uow) + + dependency = require_project_resource() + with pytest.raises(HTTPException) as exc: + dependency(project.id, user=user, uow=uow) + + assert exc.value.status_code == status.HTTP_403_FORBIDDEN + assert exc.value.detail == "User account is disabled." + + +def test_require_scenario_resource_returns_scenario_for_authorized_user(uow): + user = _create_user(uow, roles=("viewer",)) + project = _create_project(uow) + scenario = _create_scenario(uow, project) + + dependency = require_scenario_resource() + result = dependency(scenario.id, user=user, uow=uow) + + assert result.id == scenario.id + + +def test_require_scenario_resource_requires_manage_role(uow): + user = _create_user(uow, roles=("viewer",)) + project = _create_project(uow) + scenario = _create_scenario(uow, project) + + dependency = require_scenario_resource(require_manage=True) + with pytest.raises(HTTPException) as exc: + dependency(scenario.id, user=user, uow=uow) + + assert exc.value.status_code == status.HTTP_403_FORBIDDEN + assert exc.value.detail == "Insufficient role permissions for this action." + + +def test_require_scenario_resource_raises_not_found(uow): + user = _create_user(uow, roles=("viewer",)) + + dependency = require_scenario_resource() + with pytest.raises(HTTPException) as exc: + dependency(12345, user=user, uow=uow) + + assert exc.value.status_code == status.HTTP_404_NOT_FOUND + assert exc.value.detail == "Scenario 12345 not found" + + +def test_require_project_scenario_resource_returns_scenario_when_linked(uow): + user = _create_user(uow, roles=("viewer",)) + project = _create_project(uow) + scenario = _create_scenario(uow, project) + + dependency = require_project_scenario_resource() + result = dependency(project.id, scenario.id, user=user, uow=uow) + + assert result.id == scenario.id + + +def test_require_project_scenario_resource_raises_when_scenario_not_in_project(uow): + user = _create_user(uow, roles=("viewer",)) + project = _create_project(uow) + other_project = _create_project(uow) + scenario = _create_scenario(uow, other_project) + + dependency = require_project_scenario_resource() + with pytest.raises(HTTPException) as exc: + dependency(project.id, scenario.id, user=user, uow=uow) + + assert exc.value.status_code == status.HTTP_404_NOT_FOUND + assert "does not belong" in exc.value.detail + + +def test_require_project_scenario_resource_requires_manage_role(uow): + user = _create_user(uow, roles=("viewer",)) + project = _create_project(uow) + scenario = _create_scenario(uow, project) + + dependency = require_project_scenario_resource(require_manage=True) + with pytest.raises(HTTPException) as exc: + dependency(project.id, scenario.id, user=user, uow=uow) + + assert exc.value.status_code == status.HTTP_403_FORBIDDEN + assert exc.value.detail == "Insufficient role permissions for this action." -- 2.39.5 From 24cb3c2f57010f777b05209bf2b4318cf6a327d1 Mon Sep 17 00:00:00 2001 From: zwitschi Date: Sun, 9 Nov 2025 23:43:13 +0100 Subject: [PATCH 025/109] feat: implement admin bootstrap settings and ensure default roles and admin account --- changelog.md | 1 + config/settings.py | 58 ++++++++++++++++++ main.py | 22 +++++++ services/bootstrap.py | 129 ++++++++++++++++++++++++++++++++++++++++ tests/test_bootstrap.py | 116 ++++++++++++++++++++++++++++++++++++ 5 files changed, 326 insertions(+) create mode 100644 services/bootstrap.py create mode 100644 tests/test_bootstrap.py diff --git a/changelog.md b/changelog.md index bdb8c93..463dcd0 100644 --- a/changelog.md +++ b/changelog.md @@ -27,3 +27,4 @@ - Extended authorization helper layer with project/scenario ownership lookups, integrated them into FastAPI dependencies, refreshed pytest fixtures to keep the suite authenticated, and documented the new patterns across RBAC plan and security guides. - Added dedicated pytest coverage for guard dependencies, exercising success plus failure paths (missing session, inactive user, missing roles, project/scenario access errors) via `tests/test_dependencies_guards.py`. - Added integration tests in `tests/test_authorization_integration.py` verifying anonymous 401 responses, role-based 403s, and authorized project manager flows across API and UI endpoints. +- Implemented environment-driven admin bootstrap settings, wired the `bootstrap_admin` helper into FastAPI startup, added pytest coverage for creation/idempotency/reset logic, and documented operational guidance in the RBAC plan and security concept. diff --git a/config/settings.py b/config/settings.py index 217a487..339a879 100644 --- a/config/settings.py +++ b/config/settings.py @@ -10,6 +10,17 @@ from typing import Optional from services.security import JWTSettings +@dataclass(frozen=True, slots=True) +class AdminBootstrapSettings: + """Default administrator bootstrap configuration.""" + + email: str + username: str + password: str + roles: tuple[str, ...] + force_reset: bool + + @dataclass(frozen=True, slots=True) class SessionSettings: """Cookie and header configuration for session token transport.""" @@ -40,6 +51,11 @@ class Settings: session_header_name: str = "Authorization" session_header_prefix: str = "Bearer" session_allow_header_fallback: bool = True + admin_email: str = "admin@calminer.local" + admin_username: str = "admin" + admin_password: str = "ChangeMe123!" + admin_roles: tuple[str, ...] = ("admin",) + admin_force_reset: bool = False @classmethod def from_environment(cls) -> "Settings": @@ -74,6 +90,21 @@ class Settings: session_allow_header_fallback=cls._bool_from_env( "CALMINER_SESSION_ALLOW_HEADER_FALLBACK", True ), + admin_email=os.getenv( + "CALMINER_SEED_ADMIN_EMAIL", "admin@calminer.local" + ), + admin_username=os.getenv( + "CALMINER_SEED_ADMIN_USERNAME", "admin" + ), + admin_password=os.getenv( + "CALMINER_SEED_ADMIN_PASSWORD", "ChangeMe123!" + ), + admin_roles=cls._parse_admin_roles( + os.getenv("CALMINER_SEED_ADMIN_ROLES") + ), + admin_force_reset=cls._bool_from_env( + "CALMINER_SEED_FORCE", False + ), ) @staticmethod @@ -98,6 +129,22 @@ class Settings: return False return default + @staticmethod + def _parse_admin_roles(raw_value: str | None) -> tuple[str, ...]: + if not raw_value: + return ("admin",) + parts = [segment.strip() + for segment in raw_value.split(",") if segment.strip()] + if "admin" not in parts: + parts.insert(0, "admin") + seen: set[str] = set() + ordered: list[str] = [] + for role_name in parts: + if role_name not in seen: + ordered.append(role_name) + seen.add(role_name) + return tuple(ordered) + def jwt_settings(self) -> JWTSettings: """Build runtime JWT settings compatible with token helpers.""" @@ -122,6 +169,17 @@ class Settings: allow_header_fallback=self.session_allow_header_fallback, ) + def admin_bootstrap_settings(self) -> AdminBootstrapSettings: + """Return configured admin bootstrap settings.""" + + return AdminBootstrapSettings( + email=self.admin_email, + username=self.admin_username, + password=self.admin_password, + roles=self.admin_roles, + force_reset=self.admin_force_reset, + ) + @lru_cache(maxsize=1) def get_settings() -> Settings: diff --git a/main.py b/main.py index 6fd8fb8..7c28ebf 100644 --- a/main.py +++ b/main.py @@ -1,9 +1,11 @@ +import logging from typing import Awaitable, Callable from fastapi import FastAPI, Request, Response from fastapi.staticfiles import StaticFiles from config.database import Base, engine +from config.settings import get_settings from middleware.auth_session import AuthSessionMiddleware from middleware.validation import validate_json from models import ( @@ -16,6 +18,7 @@ from routes.auth import router as auth_router from routes.dashboard import router as dashboard_router from routes.projects import router as projects_router from routes.scenarios import router as scenarios_router +from services.bootstrap import bootstrap_admin # Initialize database schema (imports above ensure models are registered) Base.metadata.create_all(bind=engine) @@ -24,6 +27,8 @@ app = FastAPI() app.add_middleware(AuthSessionMiddleware) +logger = logging.getLogger(__name__) + @app.middleware("http") async def json_validation( @@ -37,6 +42,23 @@ async def health() -> dict[str, str]: return {"status": "ok"} +@app.on_event("startup") +async def ensure_admin_bootstrap() -> None: + settings = get_settings().admin_bootstrap_settings() + try: + role_result, admin_result = bootstrap_admin(settings=settings) + logger.info( + "Admin bootstrap completed: roles=%s created=%s updated=%s rotated=%s assigned=%s", + role_result.ensured, + admin_result.created_user, + admin_result.updated_user, + admin_result.password_rotated, + admin_result.roles_granted, + ) + except Exception: # pragma: no cover - defensive logging + logger.exception("Failed to bootstrap administrator account") + + app.include_router(dashboard_router) app.include_router(auth_router) app.include_router(projects_router) diff --git a/services/bootstrap.py b/services/bootstrap.py new file mode 100644 index 0000000..4ccb4d4 --- /dev/null +++ b/services/bootstrap.py @@ -0,0 +1,129 @@ +from __future__ import annotations + +import logging +from dataclasses import dataclass +from typing import Callable + +from config.settings import AdminBootstrapSettings +from models import User +from services.repositories import ensure_default_roles +from services.unit_of_work import UnitOfWork + + +logger = logging.getLogger(__name__) + + +@dataclass(slots=True) +class RoleBootstrapResult: + created: int + ensured: int + + +@dataclass(slots=True) +class AdminBootstrapResult: + created_user: bool + updated_user: bool + password_rotated: bool + roles_granted: int + + +def bootstrap_admin( + *, + settings: AdminBootstrapSettings, + unit_of_work_factory: Callable[[], UnitOfWork] = UnitOfWork, +) -> tuple[RoleBootstrapResult, AdminBootstrapResult]: + """Ensure default roles and administrator account exist.""" + + with unit_of_work_factory() as uow: + assert uow.roles is not None and uow.users is not None + + role_result = _bootstrap_roles(uow) + admin_result = _bootstrap_admin_user(uow, settings) + + logger.info( + "Admin bootstrap result: created_user=%s updated_user=%s password_rotated=%s roles_granted=%s", + admin_result.created_user, + admin_result.updated_user, + admin_result.password_rotated, + admin_result.roles_granted, + ) + return role_result, admin_result + + +def _bootstrap_roles(uow: UnitOfWork) -> RoleBootstrapResult: + assert uow.roles is not None + before = {role.name for role in uow.roles.list()} + ensure_default_roles(uow.roles) + after = {role.name for role in uow.roles.list()} + created = len(after - before) + return RoleBootstrapResult(created=created, ensured=len(after)) + + +def _bootstrap_admin_user( + uow: UnitOfWork, + settings: AdminBootstrapSettings, +) -> AdminBootstrapResult: + assert uow.users is not None and uow.roles is not None + + created_user = False + updated_user = False + password_rotated = False + roles_granted = 0 + + user = uow.users.get_by_email(settings.email, with_roles=True) + if user is None: + user = User( + email=settings.email, + username=settings.username, + password_hash=User.hash_password(settings.password), + is_active=True, + is_superuser=True, + ) + uow.users.create(user) + created_user = True + else: + if user.username != settings.username: + user.username = settings.username + updated_user = True + if not user.is_active: + user.is_active = True + updated_user = True + if not user.is_superuser: + user.is_superuser = True + updated_user = True + if settings.force_reset: + user.password_hash = User.hash_password(settings.password) + password_rotated = True + updated_user = True + uow.users.session.flush() + + user = uow.users.get(user.id, with_roles=True) + assert user is not None + + existing_roles = {role.name for role in user.roles} + for role_name in settings.roles: + role = uow.roles.get_by_name(role_name) + if role is None: + logger.warning( + "Bootstrap admin role '%s' is not defined; skipping assignment", + role_name, + ) + continue + if role.name in existing_roles: + continue + uow.users.assign_role( + user_id=user.id, + role_id=role.id, + granted_by=user.id, + ) + roles_granted += 1 + existing_roles.add(role.name) + + uow.users.session.flush() + + return AdminBootstrapResult( + created_user=created_user, + updated_user=updated_user, + password_rotated=password_rotated, + roles_granted=roles_granted, + ) diff --git a/tests/test_bootstrap.py b/tests/test_bootstrap.py new file mode 100644 index 0000000..676ee42 --- /dev/null +++ b/tests/test_bootstrap.py @@ -0,0 +1,116 @@ +from __future__ import annotations + +from collections.abc import Callable + +from typing import Any + +import pytest +from sqlalchemy import create_engine +from sqlalchemy.orm import Session, sessionmaker + +from config.database import Base +from config.settings import AdminBootstrapSettings +from services.bootstrap import AdminBootstrapResult, RoleBootstrapResult, bootstrap_admin +from services.unit_of_work import UnitOfWork + + +@pytest.fixture() +def session_factory() -> Callable[[], Session]: + engine = create_engine("sqlite+pysqlite:///:memory:", future=True) + Base.metadata.create_all(engine) + factory = sessionmaker(bind=engine, expire_on_commit=False, future=True) + + def _factory() -> Session: + return factory() + + return _factory + + +@pytest.fixture() +def unit_of_work_factory(session_factory: Callable[[], Session]) -> Callable[[], UnitOfWork]: + def _factory() -> UnitOfWork: + return UnitOfWork(session_factory=session_factory) + + return _factory + + +def _settings(**overrides: Any) -> AdminBootstrapSettings: + defaults: dict[str, Any] = { + "email": "admin@example.com", + "username": "admin", + "password": "changeme", + "roles": ("admin", "viewer"), + "force_reset": False, + } + defaults.update(overrides) + return AdminBootstrapSettings( + email=str(defaults["email"]), + username=str(defaults["username"]), + password=str(defaults["password"]), + roles=tuple(defaults["roles"]), + force_reset=bool(defaults["force_reset"]), + ) + + +def test_bootstrap_creates_admin_and_roles(unit_of_work_factory: Callable[[], UnitOfWork]) -> None: + settings = _settings() + + role_result, admin_result = bootstrap_admin( + settings=settings, + unit_of_work_factory=unit_of_work_factory, + ) + + assert role_result == RoleBootstrapResult(created=4, ensured=4) + assert admin_result == AdminBootstrapResult( + created_user=True, + updated_user=False, + password_rotated=False, + roles_granted=2, + ) + + with unit_of_work_factory() as uow: + users_repo = uow.users + assert users_repo is not None + user = users_repo.get_by_email(settings.email, with_roles=True) + assert user is not None + assert user.is_superuser is True + assert {role.name for role in user.roles} == {"admin", "viewer"} + + +def test_bootstrap_is_idempotent(unit_of_work_factory: Callable[[], UnitOfWork]) -> None: + settings = _settings() + + bootstrap_admin(settings=settings, + unit_of_work_factory=unit_of_work_factory) + role_result, admin_result = bootstrap_admin( + settings=settings, + unit_of_work_factory=unit_of_work_factory, + ) + + assert role_result.created == 0 + assert role_result.ensured == 4 + assert admin_result.created_user is False + assert admin_result.updated_user is False + assert admin_result.roles_granted == 0 + + +def test_bootstrap_respects_force_reset(unit_of_work_factory: Callable[[], UnitOfWork]) -> None: + base_settings = _settings(password="initial") + bootstrap_admin(settings=base_settings, + unit_of_work_factory=unit_of_work_factory) + + rotated_settings = _settings(password="rotated", force_reset=True) + _, admin_result = bootstrap_admin( + settings=rotated_settings, + unit_of_work_factory=unit_of_work_factory, + ) + + assert admin_result.password_rotated is True + assert admin_result.updated_user is True + + with unit_of_work_factory() as uow: + users_repo = uow.users + assert users_repo is not None + user = users_repo.get_by_email(rotated_settings.email) + assert user is not None + assert user.verify_password("rotated") -- 2.39.5 From ab328b1a0b8917817ff49a41fc13e9e3071e6680 Mon Sep 17 00:00:00 2001 From: zwitschi Date: Sun, 9 Nov 2025 23:46:51 +0100 Subject: [PATCH 026/109] feat: implement environment-driven admin bootstrap settings and retire legacy RBAC documentation --- changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.md b/changelog.md index 463dcd0..b696dda 100644 --- a/changelog.md +++ b/changelog.md @@ -28,3 +28,4 @@ - Added dedicated pytest coverage for guard dependencies, exercising success plus failure paths (missing session, inactive user, missing roles, project/scenario access errors) via `tests/test_dependencies_guards.py`. - Added integration tests in `tests/test_authorization_integration.py` verifying anonymous 401 responses, role-based 403s, and authorized project manager flows across API and UI endpoints. - Implemented environment-driven admin bootstrap settings, wired the `bootstrap_admin` helper into FastAPI startup, added pytest coverage for creation/idempotency/reset logic, and documented operational guidance in the RBAC plan and security concept. +- Retired the legacy authentication RBAC implementation plan document after migrating its guidance into live documentation and synchronized the contributor instructions to reflect the removal. -- 2.39.5 From e0fa3861a6f312bf308a6f51e4271c75f096db1e Mon Sep 17 00:00:00 2001 From: zwitschi Date: Mon, 10 Nov 2025 07:59:42 +0100 Subject: [PATCH 027/109] feat: complete Authentication & RBAC checklist by finalizing models, migrations, repositories, guard dependencies, and integration tests --- changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.md b/changelog.md index b696dda..34770e7 100644 --- a/changelog.md +++ b/changelog.md @@ -29,3 +29,4 @@ - Added integration tests in `tests/test_authorization_integration.py` verifying anonymous 401 responses, role-based 403s, and authorized project manager flows across API and UI endpoints. - Implemented environment-driven admin bootstrap settings, wired the `bootstrap_admin` helper into FastAPI startup, added pytest coverage for creation/idempotency/reset logic, and documented operational guidance in the RBAC plan and security concept. - Retired the legacy authentication RBAC implementation plan document after migrating its guidance into live documentation and synchronized the contributor instructions to reflect the removal. +- Completed the Authentication & RBAC checklist by shipping the new models, migrations, repositories, guard dependencies, and integration tests. -- 2.39.5 From 7058eb41727713180e01174a63dab75144989949 Mon Sep 17 00:00:00 2001 From: zwitschi Date: Mon, 10 Nov 2025 09:10:08 +0100 Subject: [PATCH 028/109] feat: add default administrative credentials and reset options to environment configuration --- .env.example | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.env.example b/.env.example index 7329fa2..de54798 100644 --- a/.env.example +++ b/.env.example @@ -9,3 +9,14 @@ DATABASE_PASSWORD= DATABASE_NAME=calminer # Optional: set a schema (comma-separated for multiple entries) # DATABASE_SCHEMA=public + +# Default administrative credentials are provided at deployment time through environment variables +# (`CALMINER_SEED_ADMIN_EMAIL`, `CALMINER_SEED_ADMIN_USERNAME`, `CALMINER_SEED_ADMIN_PASSWORD`, `CALMINER_SEED_ADMIN_ROLES`). +# These values are consumed by a shared bootstrap helper on application startup, ensuring mandatory roles and the administrator account exist before any user interaction. +CALMINER_SEED_ADMIN_EMAIL= +CALMINER_SEED_ADMIN_USERNAME= +CALMINER_SEED_ADMIN_PASSWORD= +CALMINER_SEED_ADMIN_ROLES= +# Operators can request a managed credential reset by setting `CALMINER_SEED_FORCE=true`. +# On the next startup the helper rotates the admin password and reapplies role assignments, so downstream environments must update stored secrets immediately after the reset. +# CALMINER_SEED_FORCE=false \ No newline at end of file -- 2.39.5 From 3bc124c11f9641802173e04129f663db3845f805 Mon Sep 17 00:00:00 2001 From: zwitschi Date: Mon, 10 Nov 2025 09:10:47 +0100 Subject: [PATCH 029/109] feat: implement import functionality for projects and scenarios with CSV/XLSX support, including validation and error handling --- changelog.md | 2 + requirements.txt | 3 +- schemas/imports.py | 172 ++++++++++ services/importers.py | 564 +++++++++++++++++++++++++++++++++ services/repositories.py | 30 +- tests/test_import_ingestion.py | 237 ++++++++++++++ tests/test_import_parsing.py | 78 +++++ 7 files changed, 1084 insertions(+), 2 deletions(-) create mode 100644 schemas/imports.py create mode 100644 services/importers.py create mode 100644 tests/test_import_ingestion.py create mode 100644 tests/test_import_parsing.py diff --git a/changelog.md b/changelog.md index 34770e7..e449756 100644 --- a/changelog.md +++ b/changelog.md @@ -30,3 +30,5 @@ - Implemented environment-driven admin bootstrap settings, wired the `bootstrap_admin` helper into FastAPI startup, added pytest coverage for creation/idempotency/reset logic, and documented operational guidance in the RBAC plan and security concept. - Retired the legacy authentication RBAC implementation plan document after migrating its guidance into live documentation and synchronized the contributor instructions to reflect the removal. - Completed the Authentication & RBAC checklist by shipping the new models, migrations, repositories, guard dependencies, and integration tests. +- Documented the project/scenario import/export field mapping and file format guidelines in `calminer-docs/requirements/FR-008.md`, and introduced `schemas/imports.py` with Pydantic models that normalise incoming CSV/Excel rows for projects and scenarios. +- Added `services/importers.py` to load CSV/XLSX files into the new import schemas, pulled in `openpyxl` for Excel support, and covered the parsing behaviour with `tests/test_import_parsing.py`. diff --git a/requirements.txt b/requirements.txt index 7227da4..7658aa7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,4 +11,5 @@ numpy passlib argon2-cffi python-jose -python-multipart \ No newline at end of file +python-multipart +openpyxl \ No newline at end of file diff --git a/schemas/imports.py b/schemas/imports.py new file mode 100644 index 0000000..a697c94 --- /dev/null +++ b/schemas/imports.py @@ -0,0 +1,172 @@ +from __future__ import annotations + +from datetime import date, datetime +from typing import Any, Mapping + +from pydantic import BaseModel, ConfigDict, field_validator, model_validator + +from models import MiningOperationType, ResourceType, ScenarioStatus + + +def _normalise_string(value: Any) -> str: + if value is None: + return "" + if isinstance(value, str): + return value.strip() + return str(value).strip() + + +def _strip_or_none(value: Any | None) -> str | None: + if value is None: + return None + text = _normalise_string(value) + return text or None + + +def _coerce_enum(value: Any, enum_cls: Any, aliases: Mapping[str, Any]) -> Any: + if value is None: + return value + if isinstance(value, enum_cls): + return value + text = _normalise_string(value).lower() + if not text: + return None + if text in aliases: + return aliases[text] + try: + return enum_cls(text) + except ValueError as exc: # pragma: no cover - surfaced by Pydantic + raise ValueError( + f"Invalid value '{value}' for {enum_cls.__name__}") from exc + + +OPERATION_TYPE_ALIASES: dict[str, MiningOperationType] = { + "open pit": MiningOperationType.OPEN_PIT, + "openpit": MiningOperationType.OPEN_PIT, + "underground": MiningOperationType.UNDERGROUND, + "in-situ leach": MiningOperationType.IN_SITU_LEACH, + "in situ": MiningOperationType.IN_SITU_LEACH, + "placer": MiningOperationType.PLACER, + "quarry": MiningOperationType.QUARRY, + "mountaintop removal": MiningOperationType.MOUNTAINTOP_REMOVAL, + "other": MiningOperationType.OTHER, +} + + +SCENARIO_STATUS_ALIASES: dict[str, ScenarioStatus] = { + "draft": ScenarioStatus.DRAFT, + "active": ScenarioStatus.ACTIVE, + "archived": ScenarioStatus.ARCHIVED, +} + + +RESOURCE_TYPE_ALIASES: dict[str, ResourceType] = { + key.replace("_", " ").lower(): value for key, value in ResourceType.__members__.items() +} +RESOURCE_TYPE_ALIASES.update( + {value.value.replace("_", " ").lower(): value for value in ResourceType} +) + + +class ProjectImportRow(BaseModel): + name: str + location: str | None = None + operation_type: MiningOperationType + description: str | None = None + created_at: datetime | None = None + updated_at: datetime | None = None + + model_config = ConfigDict(extra="forbid") + + @field_validator("name", mode="before") + @classmethod + def validate_name(cls, value: Any) -> str: + text = _normalise_string(value) + if not text: + raise ValueError("Project name is required") + return text + + @field_validator("location", "description", mode="before") + @classmethod + def optional_text(cls, value: Any | None) -> str | None: + return _strip_or_none(value) + + @field_validator("operation_type", mode="before") + @classmethod + def map_operation_type(cls, value: Any) -> MiningOperationType | None: + return _coerce_enum(value, MiningOperationType, OPERATION_TYPE_ALIASES) + + +class ScenarioImportRow(BaseModel): + project_name: str + name: str + status: ScenarioStatus = ScenarioStatus.DRAFT + start_date: date | None = None + end_date: date | None = None + discount_rate: float | None = None + currency: str | None = None + primary_resource: ResourceType | None = None + description: str | None = None + created_at: datetime | None = None + updated_at: datetime | None = None + + model_config = ConfigDict(extra="forbid") + + @field_validator("project_name", "name", mode="before") + @classmethod + def validate_required_text(cls, value: Any, info) -> str: + text = _normalise_string(value) + if not text: + raise ValueError( + f"{info.field_name.replace('_', ' ').title()} is required") + return text + + @field_validator("status", mode="before") + @classmethod + def map_status(cls, value: Any) -> ScenarioStatus | None: + return _coerce_enum(value, ScenarioStatus, SCENARIO_STATUS_ALIASES) + + @field_validator("primary_resource", mode="before") + @classmethod + def map_resource(cls, value: Any) -> ResourceType | None: + return _coerce_enum(value, ResourceType, RESOURCE_TYPE_ALIASES) + + @field_validator("description", mode="before") + @classmethod + def optional_description(cls, value: Any | None) -> str | None: + return _strip_or_none(value) + + @field_validator("currency", mode="before") + @classmethod + def normalise_currency(cls, value: Any | None) -> str | None: + if value is None: + return None + text = _normalise_string(value).upper() + if not text: + return None + if len(text) != 3: + raise ValueError("Currency code must be a 3-letter ISO value") + return text + + @field_validator("discount_rate", mode="before") + @classmethod + def coerce_discount_rate(cls, value: Any | None) -> float | None: + if value is None: + return None + if isinstance(value, (int, float)): + return float(value) + text = _normalise_string(value) + if not text: + return None + if text.endswith("%"): + text = text[:-1] + try: + return float(text) + except ValueError as exc: + raise ValueError("Discount rate must be numeric") from exc + + @model_validator(mode="after") + def validate_dates(self) -> "ScenarioImportRow": + if self.start_date and self.end_date and self.start_date > self.end_date: + raise ValueError("End date must be on or after start date") + return self diff --git a/services/importers.py b/services/importers.py new file mode 100644 index 0000000..921f2fd --- /dev/null +++ b/services/importers.py @@ -0,0 +1,564 @@ +from __future__ import annotations + +from dataclasses import dataclass +from enum import Enum +from pathlib import Path +from typing import Any, BinaryIO, Callable, Generic, Iterable, Mapping, TypeVar, cast +from uuid import uuid4 +from types import MappingProxyType + +import pandas as pd +from pandas import DataFrame +from pydantic import BaseModel, ValidationError + +from models import Project, Scenario +from schemas.imports import ProjectImportRow, ScenarioImportRow +from services.unit_of_work import UnitOfWork + +TImportRow = TypeVar("TImportRow", bound=BaseModel) + +PROJECT_COLUMNS: tuple[str, ...] = ( + "name", + "location", + "operation_type", + "description", + "created_at", + "updated_at", +) + +SCENARIO_COLUMNS: tuple[str, ...] = ( + "project_name", + "name", + "status", + "start_date", + "end_date", + "discount_rate", + "currency", + "primary_resource", + "description", + "created_at", + "updated_at", +) + + +@dataclass(slots=True) +class ImportRowError: + row_number: int + field: str | None + message: str + + +@dataclass(slots=True) +class ParsedImportRow(Generic[TImportRow]): + row_number: int + data: TImportRow + + +@dataclass(slots=True) +class ImportResult(Generic[TImportRow]): + rows: list[ParsedImportRow[TImportRow]] + errors: list[ImportRowError] + + +class UnsupportedImportFormat(ValueError): + pass + + +class ImportPreviewState(str, Enum): + NEW = "new" + UPDATE = "update" + SKIP = "skip" + ERROR = "error" + + +@dataclass(slots=True) +class ImportPreviewRow(Generic[TImportRow]): + row_number: int + data: TImportRow + state: ImportPreviewState + issues: list[str] + context: dict[str, Any] | None = None + + +@dataclass(slots=True) +class ImportPreviewSummary: + total_rows: int + accepted: int + skipped: int + errored: int + + +@dataclass(slots=True) +class ImportPreview(Generic[TImportRow]): + rows: list[ImportPreviewRow[TImportRow]] + summary: ImportPreviewSummary + row_issues: list["ImportPreviewRowIssues"] + parser_errors: list[ImportRowError] + stage_token: str | None + + +@dataclass(slots=True) +class StagedRow(Generic[TImportRow]): + parsed: ParsedImportRow[TImportRow] + context: dict[str, Any] + + +@dataclass(slots=True) +class ImportPreviewRowIssue: + message: str + field: str | None = None + + +@dataclass(slots=True) +class ImportPreviewRowIssues: + row_number: int + state: ImportPreviewState | None + issues: list[ImportPreviewRowIssue] + + +@dataclass(slots=True) +class StagedImport(Generic[TImportRow]): + token: str + rows: list[StagedRow[TImportRow]] + + +@dataclass(slots=True, frozen=True) +class StagedRowView(Generic[TImportRow]): + row_number: int + data: TImportRow + context: Mapping[str, Any] + + +@dataclass(slots=True, frozen=True) +class StagedImportView(Generic[TImportRow]): + token: str + rows: tuple[StagedRowView[TImportRow], ...] + + +UnitOfWorkFactory = Callable[[], UnitOfWork] + + +class ImportIngestionService: + """Coordinates parsing, validation, and preview staging for imports.""" + + def __init__(self, uow_factory: UnitOfWorkFactory) -> None: + self._uow_factory = uow_factory + self._project_stage: dict[str, StagedImport[ProjectImportRow]] = {} + self._scenario_stage: dict[str, StagedImport[ScenarioImportRow]] = {} + + def preview_projects( + self, + stream: BinaryIO, + filename: str, + ) -> ImportPreview[ProjectImportRow]: + result = load_project_imports(stream, filename) + parser_errors = result.errors + + preview_rows: list[ImportPreviewRow[ProjectImportRow]] = [] + staged_rows: list[StagedRow[ProjectImportRow]] = [] + accepted = skipped = errored = 0 + + seen_names: set[str] = set() + + existing_by_name: dict[str, Project] = {} + if result.rows: + with self._uow_factory() as uow: + if not uow.projects: + raise RuntimeError("Project repository is unavailable") + existing_by_name = dict( + uow.projects.find_by_names( + parsed.data.name for parsed in result.rows + ) + ) + + for parsed in result.rows: + name_key = _normalise_key(parsed.data.name) + issues: list[str] = [] + context: dict[str, Any] | None = None + state = ImportPreviewState.NEW + + if name_key in seen_names: + state = ImportPreviewState.SKIP + issues.append( + "Duplicate project name within upload; row skipped.") + else: + seen_names.add(name_key) + existing = existing_by_name.get(name_key) + if existing: + state = ImportPreviewState.UPDATE + context = { + "mode": "update", + "project_id": existing.id, + } + issues.append("Existing project will be updated.") + else: + context = {"mode": "create"} + + preview_rows.append( + ImportPreviewRow( + row_number=parsed.row_number, + data=parsed.data, + state=state, + issues=issues, + context=context, + ) + ) + + if state in {ImportPreviewState.NEW, ImportPreviewState.UPDATE}: + accepted += 1 + staged_rows.append( + StagedRow(parsed=parsed, context=context or { + "mode": "create"}) + ) + elif state == ImportPreviewState.SKIP: + skipped += 1 + else: + errored += 1 + + parser_error_rows = {error.row_number for error in parser_errors} + errored += len(parser_error_rows) + total_rows = len(preview_rows) + len(parser_error_rows) + + summary = ImportPreviewSummary( + total_rows=total_rows, + accepted=accepted, + skipped=skipped, + errored=errored, + ) + + row_issues = _compile_row_issues(preview_rows, parser_errors) + + stage_token: str | None = None + if staged_rows: + stage_token = self._store_project_stage(staged_rows) + + return ImportPreview( + rows=preview_rows, + summary=summary, + row_issues=row_issues, + parser_errors=parser_errors, + stage_token=stage_token, + ) + + def preview_scenarios( + self, + stream: BinaryIO, + filename: str, + ) -> ImportPreview[ScenarioImportRow]: + result = load_scenario_imports(stream, filename) + parser_errors = result.errors + + preview_rows: list[ImportPreviewRow[ScenarioImportRow]] = [] + staged_rows: list[StagedRow[ScenarioImportRow]] = [] + accepted = skipped = errored = 0 + + seen_pairs: set[tuple[str, str]] = set() + + existing_projects: dict[str, Project] = {} + existing_scenarios: dict[tuple[int, str], Scenario] = {} + + if result.rows: + with self._uow_factory() as uow: + if not uow.projects or not uow.scenarios: + raise RuntimeError("Repositories are unavailable") + + existing_projects = dict( + uow.projects.find_by_names( + parsed.data.project_name for parsed in result.rows + ) + ) + + names_by_project: dict[int, set[str]] = {} + for parsed in result.rows: + project = existing_projects.get( + _normalise_key(parsed.data.project_name) + ) + if not project: + continue + names_by_project.setdefault(project.id, set()).add( + _normalise_key(parsed.data.name) + ) + + for project_id, names in names_by_project.items(): + matches = uow.scenarios.find_by_project_and_names( + project_id, names) + for name_key, scenario in matches.items(): + existing_scenarios[(project_id, name_key)] = scenario + + for parsed in result.rows: + project_key = _normalise_key(parsed.data.project_name) + scenario_key = _normalise_key(parsed.data.name) + issues: list[str] = [] + context: dict[str, Any] | None = None + state = ImportPreviewState.NEW + + if (project_key, scenario_key) in seen_pairs: + state = ImportPreviewState.SKIP + issues.append( + "Duplicate scenario for project within upload; row skipped." + ) + else: + seen_pairs.add((project_key, scenario_key)) + project = existing_projects.get(project_key) + if not project: + state = ImportPreviewState.ERROR + issues.append( + f"Project '{parsed.data.project_name}' does not exist." + ) + else: + context = {"mode": "create", "project_id": project.id} + existing = existing_scenarios.get( + (project.id, scenario_key)) + if existing: + state = ImportPreviewState.UPDATE + context = { + "mode": "update", + "project_id": project.id, + "scenario_id": existing.id, + } + issues.append("Existing scenario will be updated.") + + preview_rows.append( + ImportPreviewRow( + row_number=parsed.row_number, + data=parsed.data, + state=state, + issues=issues, + context=context, + ) + ) + + if state in {ImportPreviewState.NEW, ImportPreviewState.UPDATE}: + accepted += 1 + staged_rows.append( + StagedRow(parsed=parsed, context=context or { + "mode": "create"}) + ) + elif state == ImportPreviewState.SKIP: + skipped += 1 + else: + errored += 1 + + parser_error_rows = {error.row_number for error in parser_errors} + errored += len(parser_error_rows) + total_rows = len(preview_rows) + len(parser_error_rows) + + summary = ImportPreviewSummary( + total_rows=total_rows, + accepted=accepted, + skipped=skipped, + errored=errored, + ) + + row_issues = _compile_row_issues(preview_rows, parser_errors) + + stage_token: str | None = None + if staged_rows: + stage_token = self._store_scenario_stage(staged_rows) + + return ImportPreview( + rows=preview_rows, + summary=summary, + row_issues=row_issues, + parser_errors=parser_errors, + stage_token=stage_token, + ) + + def get_staged_projects( + self, token: str + ) -> StagedImportView[ProjectImportRow] | None: + staged = self._project_stage.get(token) + if not staged: + return None + return _build_staged_view(staged) + + def get_staged_scenarios( + self, token: str + ) -> StagedImportView[ScenarioImportRow] | None: + staged = self._scenario_stage.get(token) + if not staged: + return None + return _build_staged_view(staged) + + def consume_staged_projects( + self, token: str + ) -> StagedImportView[ProjectImportRow] | None: + staged = self._project_stage.pop(token, None) + if not staged: + return None + return _build_staged_view(staged) + + def consume_staged_scenarios( + self, token: str + ) -> StagedImportView[ScenarioImportRow] | None: + staged = self._scenario_stage.pop(token, None) + if not staged: + return None + return _build_staged_view(staged) + + def clear_staged_projects(self, token: str) -> bool: + return self._project_stage.pop(token, None) is not None + + def clear_staged_scenarios(self, token: str) -> bool: + return self._scenario_stage.pop(token, None) is not None + + def _store_project_stage( + self, rows: list[StagedRow[ProjectImportRow]] + ) -> str: + token = str(uuid4()) + self._project_stage[token] = StagedImport(token=token, rows=rows) + return token + + def _store_scenario_stage( + self, rows: list[StagedRow[ScenarioImportRow]] + ) -> str: + token = str(uuid4()) + self._scenario_stage[token] = StagedImport(token=token, rows=rows) + return token + + +def load_project_imports(stream: BinaryIO, filename: str) -> ImportResult[ProjectImportRow]: + df = _load_dataframe(stream, filename) + return _parse_dataframe(df, ProjectImportRow, PROJECT_COLUMNS) + + +def load_scenario_imports(stream: BinaryIO, filename: str) -> ImportResult[ScenarioImportRow]: + df = _load_dataframe(stream, filename) + return _parse_dataframe(df, ScenarioImportRow, SCENARIO_COLUMNS) + + +def _load_dataframe(stream: BinaryIO, filename: str) -> DataFrame: + stream.seek(0) + suffix = Path(filename).suffix.lower() + if suffix == ".csv": + df = pd.read_csv(stream, dtype=str, + keep_default_na=False, encoding="utf-8") + elif suffix in {".xls", ".xlsx"}: + df = pd.read_excel(stream, dtype=str, engine="openpyxl") + else: + raise UnsupportedImportFormat( + f"Unsupported file type: {suffix or 'unknown'}") + df.columns = [str(col).strip().lower() for col in df.columns] + return df + + +def _parse_dataframe( + df: DataFrame, + model: type[TImportRow], + expected_columns: Iterable[str], +) -> ImportResult[TImportRow]: + rows: list[ParsedImportRow[TImportRow]] = [] + errors: list[ImportRowError] = [] + for index, raw in enumerate(df.to_dict(orient="records"), start=2): + payload = _prepare_payload( + cast(dict[str, object], raw), expected_columns) + try: + rows.append( + ParsedImportRow(row_number=index, data=model(**payload)) + ) + except ValidationError as exc: # pragma: no cover - exercised via tests + for detail in exc.errors(): + loc = ".".join(str(part) + for part in detail.get("loc", [])) or None + errors.append( + ImportRowError( + row_number=index, + field=loc, + message=detail.get("msg", "Invalid value"), + ) + ) + return ImportResult(rows=rows, errors=errors) + + +def _prepare_payload( + raw: dict[str, object], expected_columns: Iterable[str] +) -> dict[str, object | None]: + payload: dict[str, object | None] = {} + for column in expected_columns: + if column not in raw: + continue + value = raw.get(column) + if isinstance(value, str): + value = value.strip() + if value == "": + value = None + if value is not None and pd.isna(cast(Any, value)): + value = None + payload[column] = value + return payload + + +def _normalise_key(value: str) -> str: + return value.strip().lower() + + +def _build_staged_view( + staged: StagedImport[TImportRow], +) -> StagedImportView[TImportRow]: + rows = tuple( + StagedRowView( + row_number=row.parsed.row_number, + data=cast(TImportRow, _deep_copy_model(row.parsed.data)), + context=MappingProxyType(dict(row.context)), + ) + for row in staged.rows + ) + return StagedImportView(token=staged.token, rows=rows) + + +def _deep_copy_model(model: BaseModel) -> BaseModel: + copy_method = getattr(model, "model_copy", None) + if callable(copy_method): # pydantic v2 + return cast(BaseModel, copy_method(deep=True)) + return model.copy(deep=True) # type: ignore[attr-defined] + + +def _compile_row_issues( + preview_rows: Iterable[ImportPreviewRow[Any]], + parser_errors: Iterable[ImportRowError], +) -> list[ImportPreviewRowIssues]: + issue_map: dict[int, ImportPreviewRowIssues] = {} + + def ensure_bundle( + row_number: int, + state: ImportPreviewState | None, + ) -> ImportPreviewRowIssues: + bundle = issue_map.get(row_number) + if bundle is None: + bundle = ImportPreviewRowIssues( + row_number=row_number, + state=state, + issues=[], + ) + issue_map[row_number] = bundle + else: + if _state_priority(state) > _state_priority(bundle.state): + bundle.state = state + return bundle + + for row in preview_rows: + if not row.issues: + continue + bundle = ensure_bundle(row.row_number, row.state) + for message in row.issues: + bundle.issues.append(ImportPreviewRowIssue(message=message)) + + for error in parser_errors: + bundle = ensure_bundle(error.row_number, ImportPreviewState.ERROR) + bundle.issues.append( + ImportPreviewRowIssue(message=error.message, field=error.field) + ) + + return sorted(issue_map.values(), key=lambda item: item.row_number) + + +def _state_priority(state: ImportPreviewState | None) -> int: + if state is None: + return -1 + if state == ImportPreviewState.ERROR: + return 3 + if state == ImportPreviewState.SKIP: + return 2 + if state == ImportPreviewState.UPDATE: + return 1 + return 0 diff --git a/services/repositories.py b/services/repositories.py index 170e33e..64e7108 100644 --- a/services/repositories.py +++ b/services/repositories.py @@ -2,7 +2,7 @@ from __future__ import annotations from collections.abc import Iterable from datetime import datetime -from typing import Sequence +from typing import Mapping, Sequence from sqlalchemy import select, func from sqlalchemy.exc import IntegrityError @@ -70,6 +70,15 @@ class ProjectRepository: "Project violates uniqueness constraints") from exc return project + def find_by_names(self, names: Iterable[str]) -> Mapping[str, Project]: + normalised = {name.strip().lower() + for name in names if name and name.strip()} + if not normalised: + return {} + stmt = select(Project).where(func.lower(Project.name).in_(normalised)) + records = self.session.execute(stmt).scalars().all() + return {project.name.lower(): project for project in records} + def delete(self, project_id: int) -> None: project = self.get(project_id) self.session.delete(project) @@ -149,6 +158,25 @@ class ScenarioRepository: raise EntityConflictError("Scenario violates constraints") from exc return scenario + def find_by_project_and_names( + self, + project_id: int, + names: Iterable[str], + ) -> Mapping[str, Scenario]: + normalised = {name.strip().lower() + for name in names if name and name.strip()} + if not normalised: + return {} + stmt = ( + select(Scenario) + .where( + Scenario.project_id == project_id, + func.lower(Scenario.name).in_(normalised), + ) + ) + records = self.session.execute(stmt).scalars().all() + return {scenario.name.lower(): scenario for scenario in records} + def delete(self, scenario_id: int) -> None: scenario = self.get(scenario_id) self.session.delete(scenario) diff --git a/tests/test_import_ingestion.py b/tests/test_import_ingestion.py new file mode 100644 index 0000000..cfba1f2 --- /dev/null +++ b/tests/test_import_ingestion.py @@ -0,0 +1,237 @@ +from __future__ import annotations + +from io import BytesIO +from typing import Callable + +import pandas as pd +import pytest + +from models.project import MiningOperationType, Project +from models.scenario import Scenario, ScenarioStatus +from services.importers import ( + ImportIngestionService, + ImportPreviewState, + StagedImportView, +) +from services.unit_of_work import UnitOfWork + + +@pytest.fixture() +def ingestion_service(unit_of_work_factory: Callable[[], UnitOfWork]) -> ImportIngestionService: + return ImportIngestionService(unit_of_work_factory) + + +def test_preview_projects_flags_updates_and_duplicates( + ingestion_service: ImportIngestionService, + unit_of_work_factory: Callable[[], UnitOfWork], +) -> None: + with unit_of_work_factory() as uow: + assert uow.projects is not None + existing = Project( + name="Project A", + location="Chile", + operation_type=MiningOperationType.OPEN_PIT, + ) + uow.projects.create(existing) + + csv_content = ( + "name,location,operation_type\n" + "Project A,Peru,open pit\n" + "Project B,Canada,underground\n" + "Project B,Canada,underground\n" + ) + stream = BytesIO(csv_content.encode("utf-8")) + + preview = ingestion_service.preview_projects(stream, "projects.csv") + + states = [row.state for row in preview.rows] + assert states == [ + ImportPreviewState.UPDATE, + ImportPreviewState.NEW, + ImportPreviewState.SKIP, + ] + assert preview.summary.total_rows == 3 + assert preview.summary.accepted == 2 + assert preview.summary.skipped == 1 + assert preview.summary.errored == 0 + assert preview.parser_errors == [] + assert preview.stage_token is not None + issue_map = {bundle.row_number: bundle for bundle in preview.row_issues} + assert 2 in issue_map and issue_map[2].state == ImportPreviewState.UPDATE + assert { + detail.message for detail in issue_map[2].issues + } == {"Existing project will be updated."} + assert 4 in issue_map and issue_map[4].state == ImportPreviewState.SKIP + assert any( + "Duplicate project name" in detail.message + for detail in issue_map[4].issues + ) + # type: ignore[attr-defined] + staged = ingestion_service._project_stage[preview.stage_token] + assert len(staged.rows) == 2 + update_context = preview.rows[0].context + assert update_context is not None and update_context.get( + "project_id") is not None + + +def test_preview_scenarios_validates_projects_and_updates( + ingestion_service: ImportIngestionService, + unit_of_work_factory: Callable[[], UnitOfWork], +) -> None: + with unit_of_work_factory() as uow: + assert uow.projects is not None and uow.scenarios is not None + project = Project( + name="Existing Project", + location="Chile", + operation_type=MiningOperationType.OPEN_PIT, + ) + uow.projects.create(project) + scenario = Scenario( + project_id=project.id, + name="Existing Scenario", + status=ScenarioStatus.ACTIVE, + ) + uow.scenarios.create(scenario) + + df = pd.DataFrame( + [ + { + "project_name": "Existing Project", + "name": "Existing Scenario", + "status": "Active", + }, + { + "project_name": "Existing Project", + "name": "New Scenario", + "status": "Draft", + }, + { + "project_name": "Missing Project", + "name": "Ghost Scenario", + "status": "Draft", + }, + { + "project_name": "Existing Project", + "name": "New Scenario", + "status": "Draft", + }, + ] + ) + buffer = BytesIO() + df.to_csv(buffer, index=False) + buffer.seek(0) + + preview = ingestion_service.preview_scenarios(buffer, "scenarios.csv") + + states = [row.state for row in preview.rows] + assert states == [ + ImportPreviewState.UPDATE, + ImportPreviewState.NEW, + ImportPreviewState.ERROR, + ImportPreviewState.SKIP, + ] + assert preview.summary.total_rows == 4 + assert preview.summary.accepted == 2 + assert preview.summary.skipped == 1 + assert preview.summary.errored == 1 + assert preview.stage_token is not None + issue_map = {bundle.row_number: bundle for bundle in preview.row_issues} + assert 2 in issue_map and issue_map[2].state == ImportPreviewState.UPDATE + assert 4 in issue_map and issue_map[4].state == ImportPreviewState.ERROR + assert any( + "does not exist" in detail.message + for detail in issue_map[4].issues + ) + # type: ignore[attr-defined] + staged = ingestion_service._scenario_stage[preview.stage_token] + assert len(staged.rows) == 2 + error_row = preview.rows[2] + assert any("does not exist" in msg for msg in error_row.issues) + + +def test_preview_scenarios_aggregates_parser_errors( + ingestion_service: ImportIngestionService, + unit_of_work_factory: Callable[[], UnitOfWork], +) -> None: + with unit_of_work_factory() as uow: + assert uow.projects is not None + project = Project( + name="Existing Project", + location="Chile", + operation_type=MiningOperationType.OPEN_PIT, + ) + uow.projects.create(project) + + csv_content = ( + "project_name,name,status\n" + "Existing Project,Broken Scenario,UNKNOWN_STATUS\n" + ) + stream = BytesIO(csv_content.encode("utf-8")) + + preview = ingestion_service.preview_scenarios(stream, "invalid.csv") + + assert preview.rows == [] + assert preview.summary.total_rows == 1 + assert preview.summary.errored == 1 + assert preview.stage_token is None + assert len(preview.parser_errors) == 1 + issue_map = {bundle.row_number: bundle for bundle in preview.row_issues} + assert 2 in issue_map + bundle = issue_map[2] + assert bundle.state == ImportPreviewState.ERROR + assert any(detail.field == "status" for detail in bundle.issues) + assert all(detail.message for detail in bundle.issues) + + +def test_consume_staged_projects_removes_token( + ingestion_service: ImportIngestionService, + unit_of_work_factory: Callable[[], UnitOfWork], +) -> None: + with unit_of_work_factory() as uow: + assert uow.projects is not None + + csv_content = ( + "name,location,operation_type\n" + "Project X,Peru,open pit\n" + ) + stream = BytesIO(csv_content.encode("utf-8")) + + preview = ingestion_service.preview_projects(stream, "projects.csv") + assert preview.stage_token is not None + token = preview.stage_token + + initial_view = ingestion_service.get_staged_projects(token) + assert isinstance(initial_view, StagedImportView) + consumed = ingestion_service.consume_staged_projects(token) + assert consumed == initial_view + assert ingestion_service.get_staged_projects(token) is None + assert ingestion_service.consume_staged_projects(token) is None + + +def test_clear_staged_scenarios_drops_entry( + ingestion_service: ImportIngestionService, + unit_of_work_factory: Callable[[], UnitOfWork], +) -> None: + with unit_of_work_factory() as uow: + assert uow.projects is not None + project = Project( + name="Project Y", + location="Chile", + operation_type=MiningOperationType.OPEN_PIT, + ) + uow.projects.create(project) + + csv_content = ( + "project_name,name,status\n" + "Project Y,Scenario 1,Active\n" + ) + stream = BytesIO(csv_content.encode("utf-8")) + + preview = ingestion_service.preview_scenarios(stream, "scenarios.csv") + assert preview.stage_token is not None + token = preview.stage_token + + assert ingestion_service.get_staged_scenarios(token) is not None + assert ingestion_service.clear_staged_scenarios(token) is True + assert ingestion_service.get_staged_scenarios(token) is None + assert ingestion_service.clear_staged_scenarios(token) is False diff --git a/tests/test_import_parsing.py b/tests/test_import_parsing.py new file mode 100644 index 0000000..6c79974 --- /dev/null +++ b/tests/test_import_parsing.py @@ -0,0 +1,78 @@ +from __future__ import annotations + +from io import BytesIO + +import pandas as pd +import pytest + +from services.importers import ImportResult, load_project_imports, load_scenario_imports +from schemas.imports import ProjectImportRow, ScenarioImportRow + + +def test_load_project_imports_from_csv() -> None: + csv_content = ( + "name,location,operation_type,description\n" + "Project A,Chile,open pit,First project\n" + "Project B,,underground,Second project\n" + ) + stream = BytesIO(csv_content.encode("utf-8")) + + result = load_project_imports(stream, "projects.csv") + + assert isinstance(result, ImportResult) + assert len(result.rows) == 2 + assert not result.errors + first = result.rows[0] + assert first.row_number == 2 + assert isinstance(first.data, ProjectImportRow) + assert first.data.name == "Project A" + assert first.data.operation_type.value == "open_pit" + second = result.rows[1] + assert second.row_number == 3 + assert isinstance(second.data, ProjectImportRow) + assert second.data.location is None + + +def test_load_scenario_imports_from_excel() -> None: + df = pd.DataFrame( + [ + { + "project_name": "Project A", + "name": "Scenario 1", + "status": "Active", + "start_date": "2025-01-01", + "end_date": "2025-12-31", + "discount_rate": "7.5%", + "currency": "usd", + "primary_resource": "Electricity", + } + ] + ) + buffer = BytesIO() + df.to_excel(buffer, index=False) + buffer.seek(0) + + result = load_scenario_imports(buffer, "scenarios.xlsx") + + assert len(result.rows) == 1 + assert not result.errors + row = result.rows[0] + assert row.row_number == 2 + assert isinstance(row.data, ScenarioImportRow) + assert row.data.status.value == "active" + assert row.data.currency == "USD" + assert row.data.discount_rate == pytest.approx(7.5) + + +def test_import_errors_include_row_numbers() -> None: + csv_content = "name,operation_type\n,open pit\n" + stream = BytesIO(csv_content.encode("utf-8")) + + result = load_project_imports(stream, "projects.csv") + + assert len(result.rows) == 0 + assert len(result.errors) == 1 + error = result.errors[0] + assert error.row_number == 2 + assert error.field == "name" + assert "required" in error.message -- 2.39.5 From eaef99f0acdc79cdd37aff9f981819ecf99fc383 Mon Sep 17 00:00:00 2001 From: zwitschi Date: Mon, 10 Nov 2025 09:20:41 +0100 Subject: [PATCH 030/109] feat: enhance import functionality with commit results and summary models for projects and scenarios --- schemas/imports.py | 122 ++++++++++++++++++++++++- services/importers.py | 143 +++++++++++++++++++++++++++++ tests/test_import_ingestion.py | 158 +++++++++++++++++++++++++++++++++ 3 files changed, 422 insertions(+), 1 deletion(-) diff --git a/schemas/imports.py b/schemas/imports.py index a697c94..c60b9a7 100644 --- a/schemas/imports.py +++ b/schemas/imports.py @@ -2,11 +2,14 @@ from __future__ import annotations from datetime import date, datetime from typing import Any, Mapping +from typing import Literal -from pydantic import BaseModel, ConfigDict, field_validator, model_validator +from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator from models import MiningOperationType, ResourceType, ScenarioStatus +PreviewStateLiteral = Literal["new", "update", "skip", "error"] + def _normalise_string(value: Any) -> str: if value is None: @@ -170,3 +173,120 @@ class ScenarioImportRow(BaseModel): if self.start_date and self.end_date and self.start_date > self.end_date: raise ValueError("End date must be on or after start date") return self + + +class ImportRowErrorModel(BaseModel): + row_number: int + field: str | None = None + message: str + + model_config = ConfigDict(from_attributes=True, extra="forbid") + + +class ImportPreviewRowIssueModel(BaseModel): + message: str + field: str | None = None + + model_config = ConfigDict(from_attributes=True, extra="forbid") + + +class ImportPreviewRowIssuesModel(BaseModel): + row_number: int + state: PreviewStateLiteral | None = None + issues: list[ImportPreviewRowIssueModel] = Field(default_factory=list) + + model_config = ConfigDict(from_attributes=True, extra="forbid") + + +class ImportPreviewSummaryModel(BaseModel): + total_rows: int + accepted: int + skipped: int + errored: int + + model_config = ConfigDict(from_attributes=True, extra="forbid") + + +class ProjectImportPreviewRow(BaseModel): + row_number: int + data: ProjectImportRow + state: PreviewStateLiteral + issues: list[str] = Field(default_factory=list) + context: dict[str, Any] | None = None + + model_config = ConfigDict(from_attributes=True, extra="forbid") + + +class ScenarioImportPreviewRow(BaseModel): + row_number: int + data: ScenarioImportRow + state: PreviewStateLiteral + issues: list[str] = Field(default_factory=list) + context: dict[str, Any] | None = None + + model_config = ConfigDict(from_attributes=True, extra="forbid") + + +class ProjectImportPreviewResponse(BaseModel): + rows: list[ProjectImportPreviewRow] + summary: ImportPreviewSummaryModel + row_issues: list[ImportPreviewRowIssuesModel] = Field(default_factory=list) + parser_errors: list[ImportRowErrorModel] = Field(default_factory=list) + stage_token: str | None = None + + model_config = ConfigDict(from_attributes=True, extra="forbid") + + +class ScenarioImportPreviewResponse(BaseModel): + rows: list[ScenarioImportPreviewRow] + summary: ImportPreviewSummaryModel + row_issues: list[ImportPreviewRowIssuesModel] = Field(default_factory=list) + parser_errors: list[ImportRowErrorModel] = Field(default_factory=list) + stage_token: str | None = None + + model_config = ConfigDict(from_attributes=True, extra="forbid") + + +class ImportCommitSummaryModel(BaseModel): + created: int + updated: int + + model_config = ConfigDict(from_attributes=True, extra="forbid") + + +class ProjectImportCommitRow(BaseModel): + row_number: int + data: ProjectImportRow + context: dict[str, Any] + + model_config = ConfigDict(from_attributes=True, extra="forbid") + + +class ScenarioImportCommitRow(BaseModel): + row_number: int + data: ScenarioImportRow + context: dict[str, Any] + + model_config = ConfigDict(from_attributes=True, extra="forbid") + + +class ProjectImportCommitResponse(BaseModel): + token: str + rows: list[ProjectImportCommitRow] + summary: ImportCommitSummaryModel + + model_config = ConfigDict(from_attributes=True, extra="forbid") + + +class ScenarioImportCommitResponse(BaseModel): + token: str + rows: list[ScenarioImportCommitRow] + summary: ImportCommitSummaryModel + + model_config = ConfigDict(from_attributes=True, extra="forbid") + + +class ImportCommitRequest(BaseModel): + token: str + + model_config = ConfigDict(extra="forbid") diff --git a/services/importers.py b/services/importers.py index 921f2fd..4594783 100644 --- a/services/importers.py +++ b/services/importers.py @@ -135,6 +135,19 @@ class StagedImportView(Generic[TImportRow]): rows: tuple[StagedRowView[TImportRow], ...] +@dataclass(slots=True, frozen=True) +class ImportCommitSummary: + created: int + updated: int + + +@dataclass(slots=True, frozen=True) +class ImportCommitResult(Generic[TImportRow]): + token: str + rows: tuple[StagedRowView[TImportRow], ...] + summary: ImportCommitSummary + + UnitOfWorkFactory = Callable[[], UnitOfWork] @@ -402,6 +415,136 @@ class ImportIngestionService: def clear_staged_scenarios(self, token: str) -> bool: return self._scenario_stage.pop(token, None) is not None + def commit_project_import(self, token: str) -> ImportCommitResult[ProjectImportRow]: + staged = self._project_stage.get(token) + if not staged: + raise ValueError(f"Unknown project import token: {token}") + + staged_view = _build_staged_view(staged) + created = updated = 0 + + with self._uow_factory() as uow: + if not uow.projects: + raise RuntimeError("Project repository is unavailable") + + for row in staged.rows: + mode = row.context.get("mode") + data = row.parsed.data + + if mode == "create": + project = Project( + name=data.name, + location=data.location, + operation_type=data.operation_type, + description=data.description, + ) + if data.created_at: + project.created_at = data.created_at + if data.updated_at: + project.updated_at = data.updated_at + uow.projects.create(project) + created += 1 + elif mode == "update": + project_id = row.context.get("project_id") + if not project_id: + raise ValueError( + "Staged project update is missing project_id context" + ) + project = uow.projects.get(project_id) + project.name = data.name + project.location = data.location + project.operation_type = data.operation_type + project.description = data.description + if data.created_at: + project.created_at = data.created_at + if data.updated_at: + project.updated_at = data.updated_at + updated += 1 + else: + raise ValueError( + f"Unsupported staged project mode: {mode!r}") + + self._project_stage.pop(token, None) + return ImportCommitResult( + token=token, + rows=staged_view.rows, + summary=ImportCommitSummary(created=created, updated=updated), + ) + + def commit_scenario_import(self, token: str) -> ImportCommitResult[ScenarioImportRow]: + staged = self._scenario_stage.get(token) + if not staged: + raise ValueError(f"Unknown scenario import token: {token}") + + staged_view = _build_staged_view(staged) + created = updated = 0 + + with self._uow_factory() as uow: + if not uow.scenarios or not uow.projects: + raise RuntimeError("Scenario repositories are unavailable") + + for row in staged.rows: + mode = row.context.get("mode") + data = row.parsed.data + + project_id = row.context.get("project_id") + if not project_id: + raise ValueError( + "Staged scenario row is missing project_id context" + ) + + project = uow.projects.get(project_id) + + if mode == "create": + scenario = Scenario( + project_id=project.id, + name=data.name, + status=data.status, + start_date=data.start_date, + end_date=data.end_date, + discount_rate=data.discount_rate, + currency=data.currency, + primary_resource=data.primary_resource, + description=data.description, + ) + if data.created_at: + scenario.created_at = data.created_at + if data.updated_at: + scenario.updated_at = data.updated_at + uow.scenarios.create(scenario) + created += 1 + elif mode == "update": + scenario_id = row.context.get("scenario_id") + if not scenario_id: + raise ValueError( + "Staged scenario update is missing scenario_id context" + ) + scenario = uow.scenarios.get(scenario_id) + scenario.project_id = project.id + scenario.name = data.name + scenario.status = data.status + scenario.start_date = data.start_date + scenario.end_date = data.end_date + scenario.discount_rate = data.discount_rate + scenario.currency = data.currency + scenario.primary_resource = data.primary_resource + scenario.description = data.description + if data.created_at: + scenario.created_at = data.created_at + if data.updated_at: + scenario.updated_at = data.updated_at + updated += 1 + else: + raise ValueError( + f"Unsupported staged scenario mode: {mode!r}") + + self._scenario_stage.pop(token, None) + return ImportCommitResult( + token=token, + rows=staged_view.rows, + summary=ImportCommitSummary(created=created, updated=updated), + ) + def _store_project_stage( self, rows: list[StagedRow[ProjectImportRow]] ) -> str: diff --git a/tests/test_import_ingestion.py b/tests/test_import_ingestion.py index cfba1f2..97f9174 100644 --- a/tests/test_import_ingestion.py +++ b/tests/test_import_ingestion.py @@ -9,11 +9,19 @@ import pytest from models.project import MiningOperationType, Project from models.scenario import Scenario, ScenarioStatus from services.importers import ( + ImportCommitResult, ImportIngestionService, ImportPreviewState, StagedImportView, ) +from services.repositories import ProjectRepository from services.unit_of_work import UnitOfWork +from schemas.imports import ( + ProjectImportCommitResponse, + ProjectImportPreviewResponse, + ScenarioImportCommitResponse, + ScenarioImportPreviewResponse, +) @pytest.fixture() @@ -73,6 +81,9 @@ def test_preview_projects_flags_updates_and_duplicates( assert update_context is not None and update_context.get( "project_id") is not None + response_model = ProjectImportPreviewResponse.model_validate(preview) + assert response_model.summary.accepted == preview.summary.accepted + def test_preview_scenarios_validates_projects_and_updates( ingestion_service: ImportIngestionService, @@ -148,6 +159,9 @@ def test_preview_scenarios_validates_projects_and_updates( error_row = preview.rows[2] assert any("does not exist" in msg for msg in error_row.issues) + response_model = ScenarioImportPreviewResponse.model_validate(preview) + assert response_model.summary.errored == preview.summary.errored + def test_preview_scenarios_aggregates_parser_errors( ingestion_service: ImportIngestionService, @@ -182,6 +196,9 @@ def test_preview_scenarios_aggregates_parser_errors( assert any(detail.field == "status" for detail in bundle.issues) assert all(detail.message for detail in bundle.issues) + response_model = ScenarioImportPreviewResponse.model_validate(preview) + assert response_model.summary.total_rows == 1 + def test_consume_staged_projects_removes_token( ingestion_service: ImportIngestionService, @@ -235,3 +252,144 @@ def test_clear_staged_scenarios_drops_entry( assert ingestion_service.clear_staged_scenarios(token) is True assert ingestion_service.get_staged_scenarios(token) is None assert ingestion_service.clear_staged_scenarios(token) is False + + +def test_commit_project_import_applies_create_and_update( + ingestion_service: ImportIngestionService, + unit_of_work_factory: Callable[[], UnitOfWork], +) -> None: + with unit_of_work_factory() as uow: + assert uow.projects is not None + existing = Project( + name="Project A", + location="Chile", + operation_type=MiningOperationType.OPEN_PIT, + ) + uow.projects.create(existing) + + csv_content = ( + "name,location,operation_type\n" + "Project A,Peru,underground\n" + "Project B,Canada,open pit\n" + ) + stream = BytesIO(csv_content.encode("utf-8")) + preview = ingestion_service.preview_projects(stream, "projects.csv") + assert preview.stage_token is not None + + result = ingestion_service.commit_project_import(preview.stage_token) + assert isinstance(result, ImportCommitResult) + assert result.summary.created == 1 + assert result.summary.updated == 1 + assert ingestion_service.get_staged_projects(preview.stage_token) is None + + commit_response = ProjectImportCommitResponse.model_validate(result) + assert commit_response.summary.updated == 1 + + with unit_of_work_factory() as uow: + assert uow.projects is not None + projects = uow.projects.list() + names = sorted(project.name for project in projects) + assert names == ["Project A", "Project B"] + updated_project = next(p for p in projects if p.name == "Project A") + assert updated_project.location == "Peru" + assert updated_project.operation_type == MiningOperationType.UNDERGROUND + new_project = next(p for p in projects if p.name == "Project B") + assert new_project.location == "Canada" + + +def test_commit_project_import_with_invalid_token_raises( + ingestion_service: ImportIngestionService, +) -> None: + with pytest.raises(ValueError): + ingestion_service.commit_project_import("missing-token") + + +def test_commit_scenario_import_applies_create_and_update( + ingestion_service: ImportIngestionService, + unit_of_work_factory: Callable[[], UnitOfWork], +) -> None: + with unit_of_work_factory() as uow: + assert uow.projects is not None and uow.scenarios is not None + project = Project( + name="Project X", + location="Chile", + operation_type=MiningOperationType.OPEN_PIT, + ) + uow.projects.create(project) + scenario = Scenario( + project_id=project.id, + name="Existing Scenario", + status=ScenarioStatus.ACTIVE, + ) + uow.scenarios.create(scenario) + + csv_content = ( + "project_name,name,status\n" + "Project X,Existing Scenario,Archived\n" + "Project X,New Scenario,Draft\n" + ) + stream = BytesIO(csv_content.encode("utf-8")) + preview = ingestion_service.preview_scenarios(stream, "scenarios.csv") + assert preview.stage_token is not None + + result = ingestion_service.commit_scenario_import(preview.stage_token) + assert result.summary.created == 1 + assert result.summary.updated == 1 + assert ingestion_service.get_staged_scenarios(preview.stage_token) is None + + commit_response = ScenarioImportCommitResponse.model_validate(result) + assert commit_response.summary.created == 1 + + with unit_of_work_factory() as uow: + assert uow.projects is not None and uow.scenarios is not None + scenarios = uow.scenarios.list_for_project(uow.projects.list()[0].id) + names = sorted(scenario.name for scenario in scenarios) + assert names == ["Existing Scenario", "New Scenario"] + updated_scenario = next( + item for item in scenarios if item.name == "Existing Scenario" + ) + assert updated_scenario.status == ScenarioStatus.ARCHIVED + new_scenario = next( + item for item in scenarios if item.name == "New Scenario" + ) + assert new_scenario.status == ScenarioStatus.DRAFT + + +def test_commit_scenario_import_with_invalid_token_raises( + ingestion_service: ImportIngestionService, +) -> None: + with pytest.raises(ValueError): + ingestion_service.commit_scenario_import("missing-token") + + +def test_commit_project_import_rolls_back_on_failure( + ingestion_service: ImportIngestionService, + unit_of_work_factory: Callable[[], UnitOfWork], + monkeypatch: pytest.MonkeyPatch, +) -> None: + with unit_of_work_factory() as uow: + assert uow.projects is not None + + csv_content = ( + "name,location,operation_type\n" + "Project Fail,Peru,open pit\n" + ) + stream = BytesIO(csv_content.encode("utf-8")) + preview = ingestion_service.preview_projects(stream, "projects.csv") + assert preview.stage_token is not None + token = preview.stage_token + + def _boom(self: ProjectRepository, project: Project) -> Project: + raise RuntimeError("boom") + + monkeypatch.setattr(ProjectRepository, "create", _boom) + + with pytest.raises(RuntimeError): + ingestion_service.commit_project_import(token) + + # Token should still be present for retry. + assert ingestion_service.get_staged_projects(token) is not None + + with unit_of_work_factory() as uow: + assert uow.projects is not None + assert uow.projects.count() == 0 -- 2.39.5 From 609b0d779fcb76d090eaa3bbf2cae40faee8f45f Mon Sep 17 00:00:00 2001 From: zwitschi Date: Mon, 10 Nov 2025 09:28:32 +0100 Subject: [PATCH 031/109] feat: add import routes and ingestion service for project and scenario imports --- dependencies.py | 10 ++++ main.py | 2 + routes/imports.py | 143 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 155 insertions(+) create mode 100644 routes/imports.py diff --git a/dependencies.py b/dependencies.py index b91c05d..100504d 100644 --- a/dependencies.py +++ b/dependencies.py @@ -21,6 +21,7 @@ from services.session import ( extract_session_tokens, ) from services.unit_of_work import UnitOfWork +from services.importers import ImportIngestionService def get_unit_of_work() -> Generator[UnitOfWork, None, None]: @@ -30,6 +31,15 @@ def get_unit_of_work() -> Generator[UnitOfWork, None, None]: yield uow +_IMPORT_INGESTION_SERVICE = ImportIngestionService(lambda: UnitOfWork()) + + +def get_import_ingestion_service() -> ImportIngestionService: + """Provide singleton import ingestion service.""" + + return _IMPORT_INGESTION_SERVICE + + def get_application_settings() -> Settings: """Provide cached application settings instance.""" diff --git a/main.py b/main.py index 7c28ebf..999b17c 100644 --- a/main.py +++ b/main.py @@ -16,6 +16,7 @@ from models import ( ) from routes.auth import router as auth_router from routes.dashboard import router as dashboard_router +from routes.imports import router as imports_router from routes.projects import router as projects_router from routes.scenarios import router as scenarios_router from services.bootstrap import bootstrap_admin @@ -61,6 +62,7 @@ async def ensure_admin_bootstrap() -> None: app.include_router(dashboard_router) app.include_router(auth_router) +app.include_router(imports_router) app.include_router(projects_router) app.include_router(scenarios_router) diff --git a/routes/imports.py b/routes/imports.py new file mode 100644 index 0000000..40cffac --- /dev/null +++ b/routes/imports.py @@ -0,0 +1,143 @@ +from __future__ import annotations + +from io import BytesIO + +from fastapi import APIRouter, Depends, File, HTTPException, UploadFile, status + +from dependencies import get_import_ingestion_service, require_roles +from models import User +from schemas.imports import ( + ImportCommitRequest, + ProjectImportCommitResponse, + ProjectImportPreviewResponse, + ScenarioImportCommitResponse, + ScenarioImportPreviewResponse, +) +from services.importers import ImportIngestionService, UnsupportedImportFormat + +router = APIRouter(prefix="/imports", tags=["Imports"]) + +MANAGE_ROLES = ("project_manager", "admin") + + +async def _read_upload_file(upload: UploadFile) -> BytesIO: + content = await upload.read() + if not content: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="Uploaded file is empty.", + ) + return BytesIO(content) + + +@router.post( + "/projects/preview", + response_model=ProjectImportPreviewResponse, + status_code=status.HTTP_200_OK, +) +async def preview_project_import( + file: UploadFile = File(..., + description="Project import file (CSV or Excel)"), + _: User = Depends(require_roles(*MANAGE_ROLES)), + ingestion_service: ImportIngestionService = Depends( + get_import_ingestion_service), +) -> ProjectImportPreviewResponse: + if not file.filename: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="Filename is required for import.", + ) + + stream = await _read_upload_file(file) + + try: + preview = ingestion_service.preview_projects(stream, file.filename) + except UnsupportedImportFormat as exc: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail=str(exc), + ) from exc + + return ProjectImportPreviewResponse.model_validate(preview) + + +@router.post( + "/scenarios/preview", + response_model=ScenarioImportPreviewResponse, + status_code=status.HTTP_200_OK, +) +async def preview_scenario_import( + file: UploadFile = File(..., + description="Scenario import file (CSV or Excel)"), + _: User = Depends(require_roles(*MANAGE_ROLES)), + ingestion_service: ImportIngestionService = Depends( + get_import_ingestion_service), +) -> ScenarioImportPreviewResponse: + if not file.filename: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="Filename is required for import.", + ) + + stream = await _read_upload_file(file) + + try: + preview = ingestion_service.preview_scenarios(stream, file.filename) + except UnsupportedImportFormat as exc: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail=str(exc), + ) from exc + + return ScenarioImportPreviewResponse.model_validate(preview) + + +def _value_error_status(exc: ValueError) -> int: + detail = str(exc) + if detail.lower().startswith("unknown"): + return status.HTTP_404_NOT_FOUND + return status.HTTP_400_BAD_REQUEST + + +@router.post( + "/projects/commit", + response_model=ProjectImportCommitResponse, + status_code=status.HTTP_200_OK, +) +async def commit_project_import_endpoint( + payload: ImportCommitRequest, + _: User = Depends(require_roles(*MANAGE_ROLES)), + ingestion_service: ImportIngestionService = Depends( + get_import_ingestion_service), +) -> ProjectImportCommitResponse: + try: + result = ingestion_service.commit_project_import(payload.token) + except ValueError as exc: + raise HTTPException( + status_code=_value_error_status(exc), + detail=str(exc), + ) from exc + + return ProjectImportCommitResponse.model_validate(result) + + +@router.post( + "/scenarios/commit", + response_model=ScenarioImportCommitResponse, + status_code=status.HTTP_200_OK, +) +async def commit_scenario_import_endpoint( + payload: ImportCommitRequest, + _: User = Depends(require_roles(*MANAGE_ROLES)), + ingestion_service: ImportIngestionService = Depends( + get_import_ingestion_service), +) -> ScenarioImportCommitResponse: + try: + result = ingestion_service.commit_scenario_import(payload.token) + except ValueError as exc: + raise HTTPException( + status_code=_value_error_status(exc), + detail=str(exc), + ) from exc + + return ScenarioImportCommitResponse.model_validate(result) -- 2.39.5 From b1a0153a8d518806f01458da58488ea79081027f Mon Sep 17 00:00:00 2001 From: zwitschi Date: Mon, 10 Nov 2025 10:14:42 +0100 Subject: [PATCH 032/109] feat: expand import ingestion workflow with staging previews, transactional commits, and new API tests --- changelog.md | 1 + tests/conftest.py | 17 +++++++++- tests/test_import_api.py | 70 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 tests/test_import_api.py diff --git a/changelog.md b/changelog.md index e449756..1cbf0cb 100644 --- a/changelog.md +++ b/changelog.md @@ -32,3 +32,4 @@ - Completed the Authentication & RBAC checklist by shipping the new models, migrations, repositories, guard dependencies, and integration tests. - Documented the project/scenario import/export field mapping and file format guidelines in `calminer-docs/requirements/FR-008.md`, and introduced `schemas/imports.py` with Pydantic models that normalise incoming CSV/Excel rows for projects and scenarios. - Added `services/importers.py` to load CSV/XLSX files into the new import schemas, pulled in `openpyxl` for Excel support, and covered the parsing behaviour with `tests/test_import_parsing.py`. +- Expanded the import ingestion workflow with staging previews, transactional persistence commits, FastAPI preview/commit endpoints under `/imports`, and new API tests (`tests/test_import_ingestion.py`, `tests/test_import_api.py`) ensuring end-to-end coverage. diff --git a/tests/conftest.py b/tests/conftest.py index 10f8135..1b96454 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -11,12 +11,14 @@ from sqlalchemy.orm import sessionmaker from sqlalchemy.pool import StaticPool from config.database import Base -from dependencies import get_auth_session, get_unit_of_work +from dependencies import get_auth_session, get_import_ingestion_service, get_unit_of_work from models import User from routes.auth import router as auth_router from routes.dashboard import router as dashboard_router from routes.projects import router as projects_router from routes.scenarios import router as scenarios_router +from routes.imports import router as imports_router +from services.importers import ImportIngestionService from services.unit_of_work import UnitOfWork from services.session import AuthSession, SessionTokens @@ -51,6 +53,7 @@ def app(session_factory: sessionmaker) -> FastAPI: application.include_router(dashboard_router) application.include_router(projects_router) application.include_router(scenarios_router) + application.include_router(imports_router) def _override_uow() -> Iterator[UnitOfWork]: with UnitOfWork(session_factory=session_factory) as uow: @@ -58,6 +61,18 @@ def app(session_factory: sessionmaker) -> FastAPI: application.dependency_overrides[get_unit_of_work] = _override_uow + def _ingestion_uow_factory() -> UnitOfWork: + return UnitOfWork(session_factory=session_factory) + + ingestion_service = ImportIngestionService(_ingestion_uow_factory) + + def _override_ingestion_service() -> ImportIngestionService: + return ingestion_service + + application.dependency_overrides[ + get_import_ingestion_service + ] = _override_ingestion_service + with UnitOfWork(session_factory=session_factory) as uow: assert uow.users is not None uow.ensure_default_roles() diff --git a/tests/test_import_api.py b/tests/test_import_api.py new file mode 100644 index 0000000..47ae9d9 --- /dev/null +++ b/tests/test_import_api.py @@ -0,0 +1,70 @@ +from __future__ import annotations + +from fastapi.testclient import TestClient + +from models.project import MiningOperationType, Project +from models.scenario import Scenario, ScenarioStatus + + +def test_project_import_preview_and_commit_flow( + client: TestClient, + unit_of_work_factory, +) -> None: + with unit_of_work_factory() as uow: + assert uow.projects is not None + existing = Project( + name="Existing Project", + location="Chile", + operation_type=MiningOperationType.OPEN_PIT, + ) + uow.projects.create(existing) + + csv_content = ( + "name,location,operation_type\n" + "Existing Project,Peru,underground\n" + "New Project,Canada,open pit\n" + ) + + preview_response = client.post( + "/imports/projects/preview", + files={"file": ("projects.csv", csv_content, "text/csv")}, + ) + assert preview_response.status_code == 200 + preview_data = preview_response.json() + assert preview_data["summary"]["accepted"] == 2 + token = preview_data["stage_token"] + assert token + + commit_response = client.post( + "/imports/projects/commit", + json={"token": token}, + ) + assert commit_response.status_code == 200 + commit_data = commit_response.json() + assert commit_data["summary"] == {"created": 1, "updated": 1} + + with unit_of_work_factory() as uow: + assert uow.projects is not None + projects = {project.name: project for project in uow.projects.list()} + assert "Existing Project" in projects and "New Project" in projects + assert ( + projects["Existing Project"].operation_type + == MiningOperationType.UNDERGROUND + ) + + repeat_commit = client.post( + "/imports/projects/commit", + json={"token": token}, + ) + assert repeat_commit.status_code == 404 + + +def test_scenario_import_commit_invalid_token_returns_404( + client: TestClient, +) -> None: + response = client.post( + "/imports/scenarios/commit", + json={"token": "missing-token"}, + ) + assert response.status_code == 404 + assert "Unknown scenario import token" in response.json()["detail"] -- 2.39.5 From 1a7581cda0033e239883e401b163562e8f62a107 Mon Sep 17 00:00:00 2001 From: zwitschi Date: Mon, 10 Nov 2025 15:36:06 +0100 Subject: [PATCH 033/109] feat: add export filters for projects and scenarios with filtering capabilities --- services/export_query.py | 113 +++++++++++++++++++++ services/export_serializers.py | 178 +++++++++++++++++++++++++++++++++ services/repositories.py | 118 ++++++++++++++++++++++ tests/test_repositories.py | 126 ++++++++++++++++++++++- 4 files changed, 533 insertions(+), 2 deletions(-) create mode 100644 services/export_query.py create mode 100644 services/export_serializers.py diff --git a/services/export_query.py b/services/export_query.py new file mode 100644 index 0000000..acbcb6e --- /dev/null +++ b/services/export_query.py @@ -0,0 +1,113 @@ +from __future__ import annotations + +from dataclasses import dataclass +from datetime import date, datetime +from typing import Iterable + +from models import MiningOperationType, ResourceType, ScenarioStatus + + +def _normalise_lower_strings(values: Iterable[str]) -> tuple[str, ...]: + unique: set[str] = set() + for value in values: + if not value: + continue + trimmed = value.strip().lower() + if not trimmed: + continue + unique.add(trimmed) + return tuple(sorted(unique)) + + +def _normalise_upper_strings(values: Iterable[str]) -> tuple[str, ...]: + unique: set[str] = set() + for value in values: + if not value: + continue + trimmed = value.strip().upper() + if not trimmed: + continue + unique.add(trimmed) + return tuple(sorted(unique)) + + +@dataclass(slots=True, frozen=True) +class ProjectExportFilters: + """Filter parameters for project export queries.""" + + ids: tuple[int, ...] = () + names: tuple[str, ...] = () + name_contains: str | None = None + locations: tuple[str, ...] = () + operation_types: tuple[MiningOperationType, ...] = () + created_from: datetime | None = None + created_to: datetime | None = None + updated_from: datetime | None = None + updated_to: datetime | None = None + + def normalised_ids(self) -> tuple[int, ...]: + unique = {identifier for identifier in self.ids if identifier > 0} + return tuple(sorted(unique)) + + def normalised_names(self) -> tuple[str, ...]: + return _normalise_lower_strings(self.names) + + def normalised_locations(self) -> tuple[str, ...]: + return _normalise_lower_strings(self.locations) + + def name_search_pattern(self) -> str | None: + if not self.name_contains: + return None + pattern = self.name_contains.strip() + if not pattern: + return None + return f"%{pattern}%" + + +@dataclass(slots=True, frozen=True) +class ScenarioExportFilters: + """Filter parameters for scenario export queries.""" + + ids: tuple[int, ...] = () + project_ids: tuple[int, ...] = () + project_names: tuple[str, ...] = () + name_contains: str | None = None + statuses: tuple[ScenarioStatus, ...] = () + start_date_from: date | None = None + start_date_to: date | None = None + end_date_from: date | None = None + end_date_to: date | None = None + created_from: datetime | None = None + created_to: datetime | None = None + updated_from: datetime | None = None + updated_to: datetime | None = None + currencies: tuple[str, ...] = () + primary_resources: tuple[ResourceType, ...] = () + + def normalised_ids(self) -> tuple[int, ...]: + unique = {identifier for identifier in self.ids if identifier > 0} + return tuple(sorted(unique)) + + def normalised_project_ids(self) -> tuple[int, ...]: + unique = {identifier for identifier in self.project_ids if identifier > 0} + return tuple(sorted(unique)) + + def normalised_project_names(self) -> tuple[str, ...]: + return _normalise_lower_strings(self.project_names) + + def name_search_pattern(self) -> str | None: + if not self.name_contains: + return None + pattern = self.name_contains.strip() + if not pattern: + return None + return f"%{pattern}%" + + def normalised_currencies(self) -> tuple[str, ...]: + return _normalise_upper_strings(self.currencies) + + +__all__ = ( + "ProjectExportFilters", + "ScenarioExportFilters", +) diff --git a/services/export_serializers.py b/services/export_serializers.py new file mode 100644 index 0000000..6036788 --- /dev/null +++ b/services/export_serializers.py @@ -0,0 +1,178 @@ +from __future__ import annotations + +from dataclasses import dataclass +from datetime import timezone +from functools import partial +from typing import Callable, Iterable, Iterator, Sequence + +from .export_query import ProjectExportFilters, ScenarioExportFilters + +__all__ = [ + "CSVExportColumn", + "CSVExportRowFactory", + "build_project_row_factory", + "build_scenario_row_factory", +] + +CSVValueFormatter = Callable[[object | None], str] +RowExtractor = Callable[[object], Sequence[object | None]] + + +@dataclass(slots=True, frozen=True) +class CSVExportColumn: + """Describe how a field is rendered into CSV output.""" + + header: str + accessor: str + formatter: CSVValueFormatter | None = None + + +@dataclass(slots=True, frozen=True) +class CSVExportRowFactory: + """Builds row values for CSV serialization.""" + + columns: tuple[CSVExportColumn, ...] + + def headers(self) -> tuple[str, ...]: + return tuple(column.header for column in self.columns) + + def to_row(self, entity: object) -> tuple[str, ...]: + values: list[str] = [] + for column in self.columns: + value = getattr(entity, column.accessor, None) + formatter = column.formatter or _default_formatter + values.append(formatter(value)) + return tuple(values) + + +def build_project_row_factory( + *, + include_timestamps: bool = True, + include_description: bool = True, +) -> CSVExportRowFactory: + columns: list[CSVExportColumn] = [ + CSVExportColumn(header="name", accessor="name"), + CSVExportColumn(header="location", accessor="location"), + CSVExportColumn(header="operation_type", accessor="operation_type"), + ] + if include_description: + columns.append( + CSVExportColumn(header="description", accessor="description") + ) + if include_timestamps: + columns.extend( + ( + CSVExportColumn( + header="created_at", + accessor="created_at", + formatter=_format_datetime, + ), + CSVExportColumn( + header="updated_at", + accessor="updated_at", + formatter=_format_datetime, + ), + ) + ) + return CSVExportRowFactory(columns=tuple(columns)) + + +def build_scenario_row_factory( + *, + include_description: bool = True, + include_timestamps: bool = True, +) -> CSVExportRowFactory: + columns: list[CSVExportColumn] = [ + CSVExportColumn(header="project_name", accessor="project_name"), + CSVExportColumn(header="name", accessor="name"), + CSVExportColumn(header="status", accessor="status"), + CSVExportColumn( + header="start_date", + accessor="start_date", + formatter=_format_date, + ), + CSVExportColumn( + header="end_date", + accessor="end_date", + formatter=_format_date, + ), + CSVExportColumn(header="discount_rate", + accessor="discount_rate", formatter=_format_number), + CSVExportColumn(header="currency", accessor="currency"), + CSVExportColumn(header="primary_resource", + accessor="primary_resource"), + ] + if include_description: + columns.append( + CSVExportColumn(header="description", accessor="description") + ) + if include_timestamps: + columns.extend( + ( + CSVExportColumn( + header="created_at", + accessor="created_at", + formatter=_format_datetime, + ), + CSVExportColumn( + header="updated_at", + accessor="updated_at", + formatter=_format_datetime, + ), + ) + ) + return CSVExportRowFactory(columns=tuple(columns)) + + +def stream_projects_to_csv( + projects: Sequence[object], + *, + row_factory: CSVExportRowFactory | None = None, +) -> Iterator[str]: + factory = row_factory or build_project_row_factory() + yield _join_row(factory.headers()) + for project in projects: + yield _join_row(factory.to_row(project)) + + +def stream_scenarios_to_csv( + scenarios: Sequence[object], + *, + row_factory: CSVExportRowFactory | None = None, +) -> Iterator[str]: + factory = row_factory or build_scenario_row_factory() + yield _join_row(factory.headers()) + for scenario in scenarios: + yield _join_row(factory.to_row(scenario)) + + +def _join_row(values: Iterable[str]) -> str: + return ",".join(values) + + +def _default_formatter(value: object | None) -> str: + if value is None: + return "" + return str(value) + + +def _format_datetime(value: object | None) -> str: + from datetime import datetime + + if not isinstance(value, datetime): + return "" + return value.astimezone(timezone.utc).replace(tzinfo=timezone.utc).isoformat().replace("+00:00", "Z") + + +def _format_date(value: object | None) -> str: + from datetime import date + + if not isinstance(value, date): + return "" + return value.isoformat() + + +def _format_number(value: object | None) -> str: + if value is None: + return "" + return f"{value:.2f}" diff --git a/services/repositories.py b/services/repositories.py index 64e7108..189dea8 100644 --- a/services/repositories.py +++ b/services/repositories.py @@ -11,6 +11,7 @@ from sqlalchemy.orm import Session, joinedload, selectinload from models import ( FinancialInput, Project, + ResourceType, Role, Scenario, ScenarioStatus, @@ -19,6 +20,7 @@ from models import ( UserRole, ) from services.exceptions import EntityConflictError, EntityNotFoundError +from services.export_query import ProjectExportFilters, ScenarioExportFilters class ProjectRepository: @@ -79,6 +81,52 @@ class ProjectRepository: records = self.session.execute(stmt).scalars().all() return {project.name.lower(): project for project in records} + def filtered_for_export( + self, + filters: ProjectExportFilters | None = None, + *, + include_scenarios: bool = False, + ) -> Sequence[Project]: + stmt = select(Project) + if include_scenarios: + stmt = stmt.options(selectinload(Project.scenarios)) + + if filters: + ids = filters.normalised_ids() + if ids: + stmt = stmt.where(Project.id.in_(ids)) + + name_matches = filters.normalised_names() + if name_matches: + stmt = stmt.where(func.lower(Project.name).in_(name_matches)) + + name_pattern = filters.name_search_pattern() + if name_pattern: + stmt = stmt.where(Project.name.ilike(name_pattern)) + + locations = filters.normalised_locations() + if locations: + stmt = stmt.where(func.lower(Project.location).in_(locations)) + + if filters.operation_types: + stmt = stmt.where(Project.operation_type.in_( + filters.operation_types)) + + if filters.created_from: + stmt = stmt.where(Project.created_at >= filters.created_from) + + if filters.created_to: + stmt = stmt.where(Project.created_at <= filters.created_to) + + if filters.updated_from: + stmt = stmt.where(Project.updated_at >= filters.updated_from) + + if filters.updated_to: + stmt = stmt.where(Project.updated_at <= filters.updated_to) + + stmt = stmt.order_by(Project.name, Project.id) + return self.session.execute(stmt).scalars().all() + def delete(self, project_id: int) -> None: project = self.get(project_id) self.session.delete(project) @@ -177,6 +225,76 @@ class ScenarioRepository: records = self.session.execute(stmt).scalars().all() return {scenario.name.lower(): scenario for scenario in records} + def filtered_for_export( + self, + filters: ScenarioExportFilters | None = None, + *, + include_project: bool = True, + ) -> Sequence[Scenario]: + stmt = select(Scenario) + if include_project: + stmt = stmt.options(joinedload(Scenario.project)) + + if filters: + scenario_ids = filters.normalised_ids() + if scenario_ids: + stmt = stmt.where(Scenario.id.in_(scenario_ids)) + + project_ids = filters.normalised_project_ids() + if project_ids: + stmt = stmt.where(Scenario.project_id.in_(project_ids)) + + project_names = filters.normalised_project_names() + if project_names: + project_id_select = select(Project.id).where( + func.lower(Project.name).in_(project_names) + ) + stmt = stmt.where(Scenario.project_id.in_(project_id_select)) + + name_pattern = filters.name_search_pattern() + if name_pattern: + stmt = stmt.where(Scenario.name.ilike(name_pattern)) + + if filters.statuses: + stmt = stmt.where(Scenario.status.in_(filters.statuses)) + + if filters.start_date_from: + stmt = stmt.where(Scenario.start_date >= + filters.start_date_from) + + if filters.start_date_to: + stmt = stmt.where(Scenario.start_date <= filters.start_date_to) + + if filters.end_date_from: + stmt = stmt.where(Scenario.end_date >= filters.end_date_from) + + if filters.end_date_to: + stmt = stmt.where(Scenario.end_date <= filters.end_date_to) + + if filters.created_from: + stmt = stmt.where(Scenario.created_at >= filters.created_from) + + if filters.created_to: + stmt = stmt.where(Scenario.created_at <= filters.created_to) + + if filters.updated_from: + stmt = stmt.where(Scenario.updated_at >= filters.updated_from) + + if filters.updated_to: + stmt = stmt.where(Scenario.updated_at <= filters.updated_to) + + currencies = filters.normalised_currencies() + if currencies: + stmt = stmt.where(func.upper( + Scenario.currency).in_(currencies)) + + if filters.primary_resources: + stmt = stmt.where(Scenario.primary_resource.in_( + filters.primary_resources)) + + stmt = stmt.order_by(Scenario.name, Scenario.id) + return self.session.execute(stmt).scalars().all() + def delete(self, scenario_id: int) -> None: scenario = self.get(scenario_id) self.session.delete(scenario) diff --git a/tests/test_repositories.py b/tests/test_repositories.py index 7e00910..1ea6eb9 100644 --- a/tests/test_repositories.py +++ b/tests/test_repositories.py @@ -1,7 +1,7 @@ from __future__ import annotations from collections.abc import Iterator -from datetime import datetime, timezone +from datetime import date, datetime, timezone import pytest from sqlalchemy import create_engine @@ -16,6 +16,7 @@ from models import ( Project, Scenario, ScenarioStatus, + ResourceType, SimulationParameter, StochasticVariable, ) @@ -25,6 +26,7 @@ from services.repositories import ( ScenarioRepository, SimulationParameterRepository, ) +from services.export_query import ProjectExportFilters, ScenarioExportFilters from services.unit_of_work import UnitOfWork @@ -136,6 +138,7 @@ def test_unit_of_work_commit_and_rollback(engine) -> None: # Commit path with UnitOfWork(session_factory=TestingSession) as uow: + assert uow.projects is not None uow.projects.create( Project(name="Project Delta", operation_type=MiningOperationType.PLACER) ) @@ -147,6 +150,7 @@ def test_unit_of_work_commit_and_rollback(engine) -> None: # Rollback path with pytest.raises(RuntimeError): with UnitOfWork(session_factory=TestingSession) as uow: + assert uow.projects is not None uow.projects.create( Project(name="Project Epsilon", operation_type=MiningOperationType.OTHER) ) @@ -241,4 +245,122 @@ def test_financial_input_repository_latest_created_at(session: Session) -> None: ) latest = repo.latest_created_at() - assert latest == new_timestamp \ No newline at end of file + assert latest == new_timestamp + + +def test_project_repository_filtered_for_export(session: Session) -> None: + repo = ProjectRepository(session) + + alpha_created = datetime(2025, 1, 1, 9, 30, tzinfo=timezone.utc) + alpha_updated = datetime(2025, 1, 2, 12, 0, tzinfo=timezone.utc) + bravo_created = datetime(2025, 2, 1, 9, 30, tzinfo=timezone.utc) + bravo_updated = datetime(2025, 2, 2, 12, 0, tzinfo=timezone.utc) + + project_alpha = Project( + name="Alpha", + location="Nevada", + operation_type=MiningOperationType.OPEN_PIT, + description="Primary export candidate", + ) + project_alpha.created_at = alpha_created + project_alpha.updated_at = alpha_updated + + project_bravo = Project( + name="Bravo", + location="Ontario", + operation_type=MiningOperationType.UNDERGROUND, + description="Excluded project", + ) + project_bravo.created_at = bravo_created + project_bravo.updated_at = bravo_updated + + scenario_alpha = Scenario( + name="Alpha Scenario", + project=project_alpha, + status=ScenarioStatus.ACTIVE, + ) + + session.add_all([project_alpha, project_bravo, scenario_alpha]) + session.flush() + + filters = ProjectExportFilters( + ids=(project_alpha.id, project_alpha.id, -5), + names=("Alpha", " alpha ", ""), + name_contains="alp", + locations=(" nevada ", ""), + operation_types=(MiningOperationType.OPEN_PIT,), + created_from=alpha_created, + created_to=alpha_created, + updated_from=alpha_updated, + updated_to=alpha_updated, + ) + + results = repo.filtered_for_export(filters, include_scenarios=True) + + assert [project.name for project in results] == ["Alpha"] + assert len(results[0].scenarios) == 1 + assert results[0].scenarios[0].name == "Alpha Scenario" + + +def test_scenario_repository_filtered_for_export(session: Session) -> None: + repo = ScenarioRepository(session) + + project_export = Project( + name="Export Project", + operation_type=MiningOperationType.PLACER, + ) + project_other = Project( + name="Other Project", + operation_type=MiningOperationType.OTHER, + ) + + scenario_match = Scenario( + name="Case Alpha", + project=project_export, + status=ScenarioStatus.ACTIVE, + start_date=date(2025, 1, 5), + end_date=date(2025, 2, 1), + discount_rate=7.5, + currency="usd", + primary_resource=ResourceType.EXPLOSIVES, + ) + scenario_match.created_at = datetime(2025, 1, 6, tzinfo=timezone.utc) + scenario_match.updated_at = datetime(2025, 1, 16, tzinfo=timezone.utc) + + scenario_other = Scenario( + name="Case Beta", + project=project_other, + status=ScenarioStatus.DRAFT, + start_date=date(2024, 12, 20), + end_date=date(2025, 3, 1), + currency="cad", + primary_resource=ResourceType.WATER, + ) + scenario_other.created_at = datetime(2024, 12, 25, tzinfo=timezone.utc) + scenario_other.updated_at = datetime(2025, 3, 5, tzinfo=timezone.utc) + + session.add_all([project_export, project_other, scenario_match, scenario_other]) + session.flush() + + filters = ScenarioExportFilters( + ids=(scenario_match.id, scenario_match.id, -1), + project_ids=(project_export.id, 0), + project_names=(" Export Project ", "EXPORT PROJECT"), + name_contains="case", + statuses=(ScenarioStatus.ACTIVE,), + start_date_from=date(2025, 1, 1), + start_date_to=date(2025, 1, 31), + end_date_from=date(2025, 1, 31), + end_date_to=date(2025, 2, 28), + created_from=datetime(2025, 1, 1, tzinfo=timezone.utc), + created_to=datetime(2025, 1, 31, tzinfo=timezone.utc), + updated_from=datetime(2025, 1, 10, tzinfo=timezone.utc), + updated_to=datetime(2025, 1, 31, tzinfo=timezone.utc), + currencies=(" usd ", "USD"), + primary_resources=(ResourceType.EXPLOSIVES,), + ) + + results = repo.filtered_for_export(filters, include_project=True) + + assert [scenario.name for scenario in results] == ["Case Alpha"] + assert results[0].project.name == "Export Project" \ No newline at end of file -- 2.39.5 From 5f183faa63bccde1a7d4ddcc8e267fffc432d667 Mon Sep 17 00:00:00 2001 From: zwitschi Date: Mon, 10 Nov 2025 15:36:14 +0100 Subject: [PATCH 034/109] feat: implement CSV export functionality with customizable columns and formatters --- services/export_serializers.py | 300 +++++++++++++++++++------------ tests/test_export_serializers.py | 166 +++++++++++++++++ 2 files changed, 347 insertions(+), 119 deletions(-) create mode 100644 tests/test_export_serializers.py diff --git a/services/export_serializers.py b/services/export_serializers.py index 6036788..8e05da2 100644 --- a/services/export_serializers.py +++ b/services/export_serializers.py @@ -1,178 +1,240 @@ from __future__ import annotations +import csv from dataclasses import dataclass -from datetime import timezone -from functools import partial -from typing import Callable, Iterable, Iterator, Sequence +from datetime import date, datetime, timezone +from decimal import Decimal, InvalidOperation, ROUND_HALF_UP +from enum import Enum +from io import StringIO +from typing import Any, Callable, Iterable, Iterator, Sequence -from .export_query import ProjectExportFilters, ScenarioExportFilters +CSVValueFormatter = Callable[[Any], str] +Accessor = Callable[[Any], Any] __all__ = [ "CSVExportColumn", - "CSVExportRowFactory", - "build_project_row_factory", - "build_scenario_row_factory", + "CSVExporter", + "default_project_columns", + "default_scenario_columns", + "stream_projects_to_csv", + "stream_scenarios_to_csv", + "default_formatter", + "format_datetime_utc", + "format_date_iso", + "format_decimal", ] -CSVValueFormatter = Callable[[object | None], str] -RowExtractor = Callable[[object], Sequence[object | None]] - -@dataclass(slots=True, frozen=True) +@dataclass(slots=True) class CSVExportColumn: - """Describe how a field is rendered into CSV output.""" + """Declarative description of a CSV export column.""" header: str - accessor: str + accessor: Accessor | str formatter: CSVValueFormatter | None = None + required: bool = False + + def resolve_accessor(self) -> Accessor: + if isinstance(self.accessor, str): + return _coerce_accessor(self.accessor) + return self.accessor + + def value_for(self, entity: Any) -> Any: + accessor = self.resolve_accessor() + try: + return accessor(entity) + except Exception: # pragma: no cover - defensive safeguard + return None -@dataclass(slots=True, frozen=True) -class CSVExportRowFactory: - """Builds row values for CSV serialization.""" +class CSVExporter: + """Stream Python objects as UTF-8 encoded CSV rows.""" - columns: tuple[CSVExportColumn, ...] + def __init__( + self, + columns: Sequence[CSVExportColumn], + *, + include_header: bool = True, + line_terminator: str = "\n", + ) -> None: + if not columns: + raise ValueError("At least one column is required for CSV export.") + self._columns: tuple[CSVExportColumn, ...] = tuple(columns) + self._include_header = include_header + self._line_terminator = line_terminator + + @property + def columns(self) -> tuple[CSVExportColumn, ...]: + return self._columns def headers(self) -> tuple[str, ...]: - return tuple(column.header for column in self.columns) + return tuple(column.header for column in self._columns) - def to_row(self, entity: object) -> tuple[str, ...]: - values: list[str] = [] - for column in self.columns: - value = getattr(entity, column.accessor, None) - formatter = column.formatter or _default_formatter - values.append(formatter(value)) - return tuple(values) + def iter_bytes(self, records: Iterable[Any]) -> Iterator[bytes]: + buffer = StringIO() + writer = csv.writer(buffer, lineterminator=self._line_terminator) + + if self._include_header: + writer.writerow(self.headers()) + yield _drain_buffer(buffer) + + for record in records: + writer.writerow(self._format_row(record)) + yield _drain_buffer(buffer) + + def _format_row(self, record: Any) -> list[str]: + formatted: list[str] = [] + for column in self._columns: + accessor = column.resolve_accessor() + raw_value = accessor(record) + formatter = column.formatter or default_formatter + formatted.append(formatter(raw_value)) + return formatted -def build_project_row_factory( - *, - include_timestamps: bool = True, - include_description: bool = True, -) -> CSVExportRowFactory: - columns: list[CSVExportColumn] = [ - CSVExportColumn(header="name", accessor="name"), - CSVExportColumn(header="location", accessor="location"), - CSVExportColumn(header="operation_type", accessor="operation_type"), - ] - if include_description: - columns.append( - CSVExportColumn(header="description", accessor="description") - ) - if include_timestamps: - columns.extend( - ( - CSVExportColumn( - header="created_at", - accessor="created_at", - formatter=_format_datetime, - ), - CSVExportColumn( - header="updated_at", - accessor="updated_at", - formatter=_format_datetime, - ), - ) - ) - return CSVExportRowFactory(columns=tuple(columns)) - - -def build_scenario_row_factory( +def default_project_columns( *, include_description: bool = True, include_timestamps: bool = True, -) -> CSVExportRowFactory: +) -> tuple[CSVExportColumn, ...]: columns: list[CSVExportColumn] = [ - CSVExportColumn(header="project_name", accessor="project_name"), - CSVExportColumn(header="name", accessor="name"), - CSVExportColumn(header="status", accessor="status"), - CSVExportColumn( - header="start_date", - accessor="start_date", - formatter=_format_date, - ), - CSVExportColumn( - header="end_date", - accessor="end_date", - formatter=_format_date, - ), - CSVExportColumn(header="discount_rate", - accessor="discount_rate", formatter=_format_number), - CSVExportColumn(header="currency", accessor="currency"), - CSVExportColumn(header="primary_resource", - accessor="primary_resource"), + CSVExportColumn("name", "name", required=True), + CSVExportColumn("location", "location"), + CSVExportColumn("operation_type", "operation_type"), ] if include_description: - columns.append( - CSVExportColumn(header="description", accessor="description") - ) + columns.append(CSVExportColumn("description", "description")) if include_timestamps: columns.extend( ( - CSVExportColumn( - header="created_at", - accessor="created_at", - formatter=_format_datetime, - ), - CSVExportColumn( - header="updated_at", - accessor="updated_at", - formatter=_format_datetime, - ), + CSVExportColumn("created_at", "created_at", + formatter=format_datetime_utc), + CSVExportColumn("updated_at", "updated_at", + formatter=format_datetime_utc), ) ) - return CSVExportRowFactory(columns=tuple(columns)) + return tuple(columns) + + +def default_scenario_columns( + *, + include_description: bool = True, + include_timestamps: bool = True, +) -> tuple[CSVExportColumn, ...]: + columns: list[CSVExportColumn] = [ + CSVExportColumn( + "project_name", + lambda scenario: getattr( + getattr(scenario, "project", None), "name", None), + required=True, + ), + CSVExportColumn("name", "name", required=True), + CSVExportColumn("status", "status"), + CSVExportColumn("start_date", "start_date", formatter=format_date_iso), + CSVExportColumn("end_date", "end_date", formatter=format_date_iso), + CSVExportColumn("discount_rate", "discount_rate", + formatter=format_decimal), + CSVExportColumn("currency", "currency"), + CSVExportColumn("primary_resource", "primary_resource"), + ] + if include_description: + columns.append(CSVExportColumn("description", "description")) + if include_timestamps: + columns.extend( + ( + CSVExportColumn("created_at", "created_at", + formatter=format_datetime_utc), + CSVExportColumn("updated_at", "updated_at", + formatter=format_datetime_utc), + ) + ) + return tuple(columns) def stream_projects_to_csv( - projects: Sequence[object], + projects: Iterable[Any], *, - row_factory: CSVExportRowFactory | None = None, -) -> Iterator[str]: - factory = row_factory or build_project_row_factory() - yield _join_row(factory.headers()) - for project in projects: - yield _join_row(factory.to_row(project)) + columns: Sequence[CSVExportColumn] | None = None, +) -> Iterator[bytes]: + resolved_columns = tuple(columns or default_project_columns()) + exporter = CSVExporter(resolved_columns) + yield from exporter.iter_bytes(projects) def stream_scenarios_to_csv( - scenarios: Sequence[object], + scenarios: Iterable[Any], *, - row_factory: CSVExportRowFactory | None = None, -) -> Iterator[str]: - factory = row_factory or build_scenario_row_factory() - yield _join_row(factory.headers()) - for scenario in scenarios: - yield _join_row(factory.to_row(scenario)) + columns: Sequence[CSVExportColumn] | None = None, +) -> Iterator[bytes]: + resolved_columns = tuple(columns or default_scenario_columns()) + exporter = CSVExporter(resolved_columns) + yield from exporter.iter_bytes(scenarios) -def _join_row(values: Iterable[str]) -> str: - return ",".join(values) - - -def _default_formatter(value: object | None) -> str: +def default_formatter(value: Any) -> str: if value is None: return "" + if isinstance(value, Enum): + return str(value.value) + if isinstance(value, Decimal): + return format_decimal(value) + if isinstance(value, datetime): + return format_datetime_utc(value) + if isinstance(value, date): + return format_date_iso(value) + if isinstance(value, bool): + return "true" if value else "false" return str(value) -def _format_datetime(value: object | None) -> str: - from datetime import datetime - +def format_datetime_utc(value: Any) -> str: if not isinstance(value, datetime): return "" - return value.astimezone(timezone.utc).replace(tzinfo=timezone.utc).isoformat().replace("+00:00", "Z") + if value.tzinfo is None: + value = value.replace(tzinfo=timezone.utc) + value = value.astimezone(timezone.utc) + return value.isoformat().replace("+00:00", "Z") -def _format_date(value: object | None) -> str: - from datetime import date - +def format_date_iso(value: Any) -> str: if not isinstance(value, date): return "" return value.isoformat() -def _format_number(value: object | None) -> str: +def format_decimal(value: Any) -> str: if value is None: return "" - return f"{value:.2f}" + if isinstance(value, Decimal): + try: + quantised = value.quantize(Decimal("0.01"), rounding=ROUND_HALF_UP) + except InvalidOperation: # pragma: no cover - unexpected precision issues + quantised = value + return format(quantised, "f") + if isinstance(value, (int, float)): + return f"{value:.2f}" + return default_formatter(value) + + +def _coerce_accessor(accessor: Accessor | str) -> Accessor: + if callable(accessor): + return accessor + + path = [segment for segment in accessor.split(".") if segment] + + def _resolve(entity: Any) -> Any: + current: Any = entity + for segment in path: + if current is None: + return None + current = getattr(current, segment, None) + return current + + return _resolve + + +def _drain_buffer(buffer: StringIO) -> bytes: + data = buffer.getvalue() + buffer.seek(0) + buffer.truncate(0) + return data.encode("utf-8") diff --git a/tests/test_export_serializers.py b/tests/test_export_serializers.py new file mode 100644 index 0000000..7529d5d --- /dev/null +++ b/tests/test_export_serializers.py @@ -0,0 +1,166 @@ +from __future__ import annotations + +from dataclasses import dataclass +from datetime import date, datetime, timezone +from decimal import Decimal +from typing import Any, Iterable + +import pytest + +from services.export_serializers import ( + CSVExportColumn, + CSVExporter, + default_formatter, + default_project_columns, + default_scenario_columns, + format_date_iso, + format_datetime_utc, + format_decimal, + stream_projects_to_csv, + stream_scenarios_to_csv, +) + + +@dataclass(slots=True) +class DummyProject: + name: str + location: str | None = None + operation_type: str = "open_pit" + description: str | None = None + created_at: datetime | None = None + updated_at: datetime | None = None + + +@dataclass(slots=True) +class DummyScenario: + project: DummyProject | None + name: str + status: str = "draft" + start_date: date | None = None + end_date: date | None = None + discount_rate: Decimal | None = None + currency: str | None = None + primary_resource: str | None = None + description: str | None = None + created_at: datetime | None = None + updated_at: datetime | None = None + + +def collect_csv_bytes(chunks: Iterable[bytes]) -> list[str]: + return [chunk.decode("utf-8") for chunk in chunks] + + +def test_csv_exporter_writes_header_and_rows() -> None: + exporter = CSVExporter( + [ + CSVExportColumn("Name", "name"), + CSVExportColumn("Location", "location"), + ] + ) + + project = DummyProject(name="Alpha", location="Nevada") + + chunks = collect_csv_bytes(exporter.iter_bytes([project])) + + assert chunks[0] == "Name,Location\n" + assert chunks[1] == "Alpha,Nevada\n" + + +def test_csv_exporter_handles_optional_values_and_default_formatter() -> None: + exporter = CSVExporter( + [ + CSVExportColumn("Name", "name"), + CSVExportColumn("Description", "description"), + ] + ) + + project = DummyProject(name="Bravo") + + chunks = collect_csv_bytes(exporter.iter_bytes([project])) + + assert chunks[-1] == "Bravo,\n" + + +def test_stream_projects_uses_default_columns() -> None: + projects = [ + DummyProject( + name="Alpha", + location="Nevada", + operation_type="open_pit", + description="Primary", + created_at=datetime(2025, 1, 1, tzinfo=timezone.utc), + updated_at=datetime(2025, 1, 2, tzinfo=timezone.utc), + ) + ] + + chunks = collect_csv_bytes(stream_projects_to_csv(projects)) + + assert chunks[0].startswith("name,location,operation_type") + assert any("Alpha" in chunk for chunk in chunks) + + +def test_stream_scenarios_resolves_project_name_accessor() -> None: + project = DummyProject(name="Project X") + scenario = DummyScenario(project=project, name="Scenario A") + + chunks = collect_csv_bytes(stream_scenarios_to_csv([scenario])) + + assert "Project X" in chunks[-1] + assert "Scenario A" in chunks[-1] + + +def test_custom_formatter_applies() -> None: + def uppercase(value: Any) -> str: + return str(value).upper() if value is not None else "" + + exporter = CSVExporter([ + CSVExportColumn("Name", "name", formatter=uppercase), + ]) + + chunks = collect_csv_bytes( + exporter.iter_bytes([DummyProject(name="alpha")])) + + assert chunks[-1] == "ALPHA\n" + + +def test_default_formatter_handles_multiple_types() -> None: + assert default_formatter(None) == "" + assert default_formatter(True) == "true" + assert default_formatter(False) == "false" + assert default_formatter(Decimal("1.234")) == "1.23" + assert default_formatter( + datetime(2025, 1, 1, tzinfo=timezone.utc)).endswith("Z") + assert default_formatter(date(2025, 1, 1)) == "2025-01-01" + + +def test_format_helpers() -> None: + assert format_date_iso(date(2025, 5, 1)) == "2025-05-01" + assert format_date_iso("not-a-date") == "" + + ts = datetime(2025, 5, 1, 12, 0, tzinfo=timezone.utc) + assert format_datetime_utc(ts) == "2025-05-01T12:00:00Z" + + assert format_datetime_utc("nope") == "" + assert format_decimal(None) == "" + assert format_decimal(Decimal("12.345")) == "12.35" + assert format_decimal(10) == "10.00" + + +def test_default_project_columns_includes_required_fields() -> None: + columns = default_project_columns() + headers = [column.header for column in columns] + assert headers[:3] == ["name", "location", "operation_type"] + + +def test_default_scenario_columns_handles_missing_project() -> None: + scenario = DummyScenario(project=None, name="Orphan Scenario") + + exporter = CSVExporter(default_scenario_columns()) + chunks = collect_csv_bytes(exporter.iter_bytes([scenario])) + + assert chunks[-1].startswith(",Orphan Scenario") + + +def test_csv_exporter_requires_columns() -> None: + with pytest.raises(ValueError): + CSVExporter([]) -- 2.39.5 From 4b33a5dba3c86938823877d15b7f202322434422 Mon Sep 17 00:00:00 2001 From: zwitschi Date: Mon, 10 Nov 2025 18:32:09 +0100 Subject: [PATCH 035/109] feat: add Excel export functionality with support for metadata and customizable sheets --- services/export_serializers.py | 131 ++++++++++++++++++++++++++++--- tests/test_export_serializers.py | 68 ++++++++++++++++ 2 files changed, 189 insertions(+), 10 deletions(-) diff --git a/services/export_serializers.py b/services/export_serializers.py index 8e05da2..5242a15 100644 --- a/services/export_serializers.py +++ b/services/export_serializers.py @@ -1,13 +1,14 @@ from __future__ import annotations import csv -from dataclasses import dataclass +from dataclasses import dataclass, field from datetime import date, datetime, timezone from decimal import Decimal, InvalidOperation, ROUND_HALF_UP from enum import Enum -from io import StringIO -from typing import Any, Callable, Iterable, Iterator, Sequence +from io import BytesIO, StringIO +from typing import Any, Callable, Iterable, Iterator, Mapping, Sequence +from openpyxl import Workbook CSVValueFormatter = Callable[[Any], str] Accessor = Callable[[Any], Any] @@ -18,6 +19,9 @@ __all__ = [ "default_scenario_columns", "stream_projects_to_csv", "stream_scenarios_to_csv", + "ExcelExporter", + "export_projects_to_excel", + "export_scenarios_to_excel", "default_formatter", "format_datetime_utc", "format_date_iso", @@ -34,13 +38,13 @@ class CSVExportColumn: formatter: CSVValueFormatter | None = None required: bool = False - def resolve_accessor(self) -> Accessor: - if isinstance(self.accessor, str): - return _coerce_accessor(self.accessor) - return self.accessor + _accessor: Accessor = field(init=False, repr=False) + + def __post_init__(self) -> None: + object.__setattr__(self, "_accessor", _coerce_accessor(self.accessor)) def value_for(self, entity: Any) -> Any: - accessor = self.resolve_accessor() + accessor = object.__getattribute__(self, "_accessor") try: return accessor(entity) except Exception: # pragma: no cover - defensive safeguard @@ -85,8 +89,7 @@ class CSVExporter: def _format_row(self, record: Any) -> list[str]: formatted: list[str] = [] for column in self._columns: - accessor = column.resolve_accessor() - raw_value = accessor(record) + raw_value = column.value_for(record) formatter = column.formatter or default_formatter formatted.append(formatter(raw_value)) return formatted @@ -216,6 +219,114 @@ def format_decimal(value: Any) -> str: return default_formatter(value) +class ExcelExporter: + """Produce Excel workbooks via write-only streaming.""" + + def __init__( + self, + columns: Sequence[CSVExportColumn], + *, + sheet_name: str = "Export", + workbook_title: str | None = None, + include_header: bool = True, + metadata: Mapping[str, Any] | None = None, + metadata_sheet_name: str = "Metadata", + ) -> None: + if not columns: + raise ValueError( + "At least one column is required for Excel export.") + self._columns: tuple[CSVExportColumn, ...] = tuple(columns) + self._sheet_name = sheet_name or "Export" + self._include_header = include_header + self._metadata = dict(metadata) if metadata else None + self._metadata_sheet_name = metadata_sheet_name or "Metadata" + self._workbook = Workbook(write_only=True) + if workbook_title: + self._workbook.properties.title = workbook_title + + def export(self, records: Iterable[Any]) -> bytes: + sheet = self._workbook.create_sheet(title=self._sheet_name) + if self._include_header: + sheet.append([column.header for column in self._columns]) + + for record in records: + sheet.append(self._format_row(record)) + + self._append_metadata_sheet() + return self._finalize() + + def _format_row(self, record: Any) -> list[Any]: + row: list[Any] = [] + for column in self._columns: + raw_value = column.value_for(record) + formatter = column.formatter or default_formatter + row.append(formatter(raw_value)) + return row + + def _append_metadata_sheet(self) -> None: + if not self._metadata: + return + + sheet_name = self._metadata_sheet_name + existing = set(self._workbook.sheetnames) + if sheet_name in existing: + index = 1 + while True: + candidate = f"{sheet_name}_{index}" + if candidate not in existing: + sheet_name = candidate + break + index += 1 + + meta_ws = self._workbook.create_sheet(title=sheet_name) + meta_ws.append(["Key", "Value"]) + for key, value in self._metadata.items(): + meta_ws.append([ + str(key), + "" if value is None else str(value), + ]) + + def _finalize(self) -> bytes: + buffer = BytesIO() + self._workbook.save(buffer) + buffer.seek(0) + return buffer.getvalue() + + +def export_projects_to_excel( + projects: Iterable[Any], + *, + columns: Sequence[CSVExportColumn] | None = None, + sheet_name: str = "Projects", + workbook_title: str | None = None, + metadata: Mapping[str, Any] | None = None, +) -> bytes: + exporter = ExcelExporter( + columns or default_project_columns(), + sheet_name=sheet_name, + workbook_title=workbook_title, + metadata=metadata, + ) + return exporter.export(projects) + + +def export_scenarios_to_excel( + scenarios: Iterable[Any], + *, + columns: Sequence[CSVExportColumn] | None = None, + sheet_name: str = "Scenarios", + workbook_title: str | None = None, + metadata: Mapping[str, Any] | None = None, +) -> bytes: + exporter = ExcelExporter( + columns or default_scenario_columns(), + sheet_name=sheet_name, + workbook_title=workbook_title, + metadata=metadata, + ) + return exporter.export(scenarios) + + def _coerce_accessor(accessor: Accessor | str) -> Accessor: if callable(accessor): return accessor diff --git a/tests/test_export_serializers.py b/tests/test_export_serializers.py index 7529d5d..b08413b 100644 --- a/tests/test_export_serializers.py +++ b/tests/test_export_serializers.py @@ -3,6 +3,7 @@ from __future__ import annotations from dataclasses import dataclass from datetime import date, datetime, timezone from decimal import Decimal +from io import BytesIO from typing import Any, Iterable import pytest @@ -10,15 +11,19 @@ import pytest from services.export_serializers import ( CSVExportColumn, CSVExporter, + ExcelExporter, default_formatter, default_project_columns, default_scenario_columns, + export_projects_to_excel, + export_scenarios_to_excel, format_date_iso, format_datetime_utc, format_decimal, stream_projects_to_csv, stream_scenarios_to_csv, ) +from openpyxl import load_workbook @dataclass(slots=True) @@ -50,6 +55,11 @@ def collect_csv_bytes(chunks: Iterable[bytes]) -> list[str]: return [chunk.decode("utf-8") for chunk in chunks] +def load_workbook_bytes(data: bytes): + buffer = BytesIO(data) + return load_workbook(buffer, read_only=True, data_only=True) + + def test_csv_exporter_writes_header_and_rows() -> None: exporter = CSVExporter( [ @@ -66,6 +76,64 @@ def test_csv_exporter_writes_header_and_rows() -> None: assert chunks[1] == "Alpha,Nevada\n" +def test_excel_exporter_basic_workbook() -> None: + exporter = ExcelExporter(default_project_columns(), sheet_name="Projects") + project = DummyProject(name="Alpha", location="Nevada") + + data = exporter.export([project]) + workbook = load_workbook_bytes(data) + sheet = workbook["Projects"] + rows = list(sheet.rows) + + assert [cell.value for cell in rows[0]] == [ + "name", + "location", + "operation_type", + "description", + "created_at", + "updated_at", + ] + assert rows[1][0].value == "Alpha" + + +def test_excel_export_projects_helper_with_metadata() -> None: + project = DummyProject(name="Alpha", location="Nevada") + data = export_projects_to_excel( + [project], metadata={"rows": 1}, workbook_title="Project Export") + + workbook = load_workbook_bytes(data) + assert workbook.properties.title == "Project Export" + assert "Projects" in workbook.sheetnames + assert any(sheet.title.startswith("Metadata") + for sheet in workbook.worksheets) + + +def test_excel_export_scenarios_helper_projects_resolved() -> None: + project = DummyProject(name="Alpha") + scenario = DummyScenario(project=project, name="Scenario 1") + data = export_scenarios_to_excel([scenario]) + + workbook = load_workbook_bytes(data) + sheet = workbook["Scenarios"] + rows = list(sheet.rows) + + assert rows[1][0].value == "Alpha" + assert rows[1][1].value == "Scenario 1" + exporter = CSVExporter( + [ + CSVExportColumn("Name", "name"), + CSVExportColumn("Location", "location"), + ] + ) + + project = DummyProject(name="Alpha", location="Nevada") + + chunks = collect_csv_bytes(exporter.iter_bytes([project])) + + assert chunks[0] == "Name,Location\n" + assert chunks[1] == "Alpha,Nevada\n" + + def test_csv_exporter_handles_optional_values_and_default_formatter() -> None: exporter = CSVExporter( [ -- 2.39.5 From 43b1e53837a4460b81aa289c389bb9f1003479cc Mon Sep 17 00:00:00 2001 From: zwitschi Date: Mon, 10 Nov 2025 18:32:24 +0100 Subject: [PATCH 036/109] feat: implement export functionality for projects and scenarios with CSV and Excel support --- main.py | 2 + routes/dashboard.py | 7 +- routes/exports.py | 132 ++++++++ schemas/exports.py | 69 +++++ static/css/imports.css | 86 ++++++ static/js/exports.js | 97 ++++++ static/js/imports.js | 100 ++++++ templates/Dashboard.html | 306 +++++++++++-------- templates/base.html | 5 +- templates/exports/modal.html | 51 ++++ templates/partials/alerts.html | 10 + templates/partials/import_preview_table.html | 17 ++ templates/partials/import_upload.html | 25 ++ tests/conftest.py | 2 + tests/test_export_routes.py | 130 ++++++++ 15 files changed, 906 insertions(+), 133 deletions(-) create mode 100644 routes/exports.py create mode 100644 schemas/exports.py create mode 100644 static/css/imports.css create mode 100644 static/js/exports.js create mode 100644 static/js/imports.js create mode 100644 templates/exports/modal.html create mode 100644 templates/partials/alerts.html create mode 100644 templates/partials/import_preview_table.html create mode 100644 templates/partials/import_upload.html create mode 100644 tests/test_export_routes.py diff --git a/main.py b/main.py index 999b17c..6aab622 100644 --- a/main.py +++ b/main.py @@ -17,6 +17,7 @@ from models import ( from routes.auth import router as auth_router from routes.dashboard import router as dashboard_router from routes.imports import router as imports_router +from routes.exports import router as exports_router from routes.projects import router as projects_router from routes.scenarios import router as scenarios_router from services.bootstrap import bootstrap_admin @@ -63,6 +64,7 @@ async def ensure_admin_bootstrap() -> None: app.include_router(dashboard_router) app.include_router(auth_router) app.include_router(imports_router) +app.include_router(exports_router) app.include_router(projects_router) app.include_router(scenarios_router) diff --git a/routes/dashboard.py b/routes/dashboard.py index dad24b6..d6ecdd3 100644 --- a/routes/dashboard.py +++ b/routes/dashboard.py @@ -7,8 +7,7 @@ from fastapi.responses import HTMLResponse from fastapi.templating import Jinja2Templates from dependencies import get_unit_of_work, require_authenticated_user -from models import User -from models import ScenarioStatus +from models import ScenarioStatus, User from services.unit_of_work import UnitOfWork router = APIRouter(tags=["Dashboard"]) @@ -120,5 +119,9 @@ def dashboard_home( "recent_projects": _load_recent_projects(uow), "simulation_updates": _load_simulation_updates(uow), "scenario_alerts": _load_scenario_alerts(request, uow), + "export_modals": { + "projects": request.url_for("exports.modal", dataset="projects"), + "scenarios": request.url_for("exports.modal", dataset="scenarios"), + }, } return templates.TemplateResponse(request, "dashboard.html", context) diff --git a/routes/exports.py b/routes/exports.py new file mode 100644 index 0000000..e896d37 --- /dev/null +++ b/routes/exports.py @@ -0,0 +1,132 @@ +from __future__ import annotations + +from datetime import datetime, timezone +from typing import Annotated + +from fastapi import APIRouter, Depends, HTTPException, Request, Response, status +from fastapi.responses import HTMLResponse, StreamingResponse +from fastapi.templating import Jinja2Templates + +from dependencies import get_unit_of_work, require_any_role +from schemas.exports import ( + ExportFormat, + ProjectExportRequest, + ScenarioExportRequest, +) +from services.export_serializers import ( + export_projects_to_excel, + export_scenarios_to_excel, + stream_projects_to_csv, + stream_scenarios_to_csv, +) +from services.unit_of_work import UnitOfWork + +router = APIRouter(prefix="/exports", tags=["exports"]) + + +@router.get( + "/modal/{dataset}", + response_model=None, + response_class=HTMLResponse, + include_in_schema=False, + name="exports.modal", +) +async def export_modal( + dataset: str, + request: Request, +) -> HTMLResponse: + dataset = dataset.lower() + if dataset not in {"projects", "scenarios"}: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Unknown dataset") + + submit_url = request.url_for( + "export_projects" if dataset == "projects" else "export_scenarios" + ) + templates = Jinja2Templates(directory="templates") + return templates.TemplateResponse( + request, + "exports/modal.html", + { + "dataset": dataset, + "submit_url": submit_url, + }, + ) + + +def _timestamp_suffix() -> str: + return datetime.now(timezone.utc).strftime("%Y%m%d-%H%M%S") + + +def _ensure_repository(repo, name: str): + if repo is None: + raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"{name} repository unavailable") + return repo + + +@router.post( + "/projects", + status_code=status.HTTP_200_OK, + response_class=StreamingResponse, + dependencies=[Depends(require_any_role( + "admin", "project_manager", "analyst"))], +) +async def export_projects( + request: ProjectExportRequest, + uow: Annotated[UnitOfWork, Depends(get_unit_of_work)], +) -> Response: + project_repo = _ensure_repository( + getattr(uow, "projects", None), "Project") + projects = project_repo.filtered_for_export(request.filters) + + filename = f"projects-{_timestamp_suffix()}" + + if request.format == ExportFormat.CSV: + stream = stream_projects_to_csv(projects) + response = StreamingResponse(stream, media_type="text/csv") + response.headers["Content-Disposition"] = f"attachment; filename={filename}.csv" + return response + + data = export_projects_to_excel(projects) + return StreamingResponse( + iter([data]), + media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + headers={ + "Content-Disposition": f"attachment; filename={filename}.xlsx", + }, + ) + + +@router.post( + "/scenarios", + status_code=status.HTTP_200_OK, + response_class=StreamingResponse, + dependencies=[Depends(require_any_role( + "admin", "project_manager", "analyst"))], +) +async def export_scenarios( + request: ScenarioExportRequest, + uow: Annotated[UnitOfWork, Depends(get_unit_of_work)], +) -> Response: + scenario_repo = _ensure_repository( + getattr(uow, "scenarios", None), "Scenario") + scenarios = scenario_repo.filtered_for_export( + request.filters, include_project=True) + + filename = f"scenarios-{_timestamp_suffix()}" + + if request.format == ExportFormat.CSV: + stream = stream_scenarios_to_csv(scenarios) + response = StreamingResponse(stream, media_type="text/csv") + response.headers["Content-Disposition"] = f"attachment; filename={filename}.csv" + return response + + data = export_scenarios_to_excel(scenarios) + return StreamingResponse( + iter([data]), + media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + headers={ + "Content-Disposition": f"attachment; filename={filename}.xlsx", + }, + ) diff --git a/schemas/exports.py b/schemas/exports.py new file mode 100644 index 0000000..f1a6105 --- /dev/null +++ b/schemas/exports.py @@ -0,0 +1,69 @@ +from __future__ import annotations + +from enum import Enum +from typing import Literal + +from pydantic import BaseModel, ConfigDict, field_validator + +from services.export_query import ProjectExportFilters, ScenarioExportFilters + + +class ExportFormat(str, Enum): + CSV = "csv" + XLSX = "xlsx" + + +class BaseExportRequest(BaseModel): + format: ExportFormat = ExportFormat.CSV + include_metadata: bool = False + + model_config = ConfigDict(extra="forbid") + + +class ProjectExportRequest(BaseExportRequest): + filters: ProjectExportFilters | None = None + + @field_validator("filters", mode="before") + @classmethod + def validate_filters(cls, value: ProjectExportFilters | None) -> ProjectExportFilters | None: + if value is None: + return None + if isinstance(value, ProjectExportFilters): + return value + return ProjectExportFilters(**value) + + +class ScenarioExportRequest(BaseExportRequest): + filters: ScenarioExportFilters | None = None + + @field_validator("filters", mode="before") + @classmethod + def validate_filters(cls, value: ScenarioExportFilters | None) -> ScenarioExportFilters | None: + if value is None: + return None + if isinstance(value, ScenarioExportFilters): + return value + return ScenarioExportFilters(**value) + + +class ExportTicket(BaseModel): + token: str + format: ExportFormat + resource: Literal["projects", "scenarios"] + + model_config = ConfigDict(extra="forbid") + + +class ExportResponse(BaseModel): + ticket: ExportTicket + + model_config = ConfigDict(extra="forbid") + + +__all__ = [ + "ExportFormat", + "ProjectExportRequest", + "ScenarioExportRequest", + "ExportTicket", + "ExportResponse", +] diff --git a/static/css/imports.css b/static/css/imports.css new file mode 100644 index 0000000..799ccec --- /dev/null +++ b/static/css/imports.css @@ -0,0 +1,86 @@ +.import-upload { + background-color: var(--surface-color); + border: 1px dashed var(--border-color); + border-radius: var(--radius-md); + padding: 1.5rem; + margin-bottom: 1.5rem; +} + +.import-upload__header { + margin-bottom: 1rem; +} + +.import-upload__dropzone { + border: 2px dashed var(--border-color); + border-radius: var(--radius-sm); + padding: 2rem; + text-align: center; + transition: border-color 0.2s ease, background-color 0.2s ease; +} + +.import-upload__dropzone.dragover { + border-color: var(--primary-color); + background-color: rgba(0, 123, 255, 0.05); +} + +.import-upload__actions { + display: flex; + gap: 0.75rem; + margin-top: 1rem; +} + +.table-cell-actions { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.btn-ghost { + background: transparent; + border: none; + cursor: pointer; + padding: 0.25rem 0.5rem; + color: var(--text-muted); +} + +.btn-ghost:hover { + color: var(--primary-color); +} + +.toast { + position: fixed; + right: 1rem; + bottom: 1rem; + display: flex; + align-items: center; + gap: 0.75rem; + padding: 1rem 1.25rem; + border-radius: var(--radius-md); + color: #fff; + box-shadow: var(--shadow-lg); + z-index: 1000; +} + +.toast.hidden { + display: none; +} + +.toast--success { + background-color: #198754; +} + +.toast--error { + background-color: #dc3545; +} + +.toast--info { + background-color: #0d6efd; +} + +.toast__close { + background: none; + border: none; + color: inherit; + cursor: pointer; + font-size: 1.1rem; +} diff --git a/static/js/exports.js b/static/js/exports.js new file mode 100644 index 0000000..5c2a6de --- /dev/null +++ b/static/js/exports.js @@ -0,0 +1,97 @@ +document.addEventListener("DOMContentLoaded", () => { + const modalContainer = document.createElement("div"); + modalContainer.id = "export-modal-container"; + document.body.appendChild(modalContainer); + + async function loadModal(dataset) { + const response = await fetch(`/exports/modal/${dataset}`); + if (!response.ok) { + throw new Error(`Failed to load export modal (${response.status})`); + } + const html = await response.text(); + modalContainer.innerHTML = html; + const modal = modalContainer.querySelector(".modal"); + if (!modal) return; + modal.classList.add("is-active"); + + const closeButtons = modal.querySelectorAll("[data-dismiss='modal']"); + closeButtons.forEach((btn) => + btn.addEventListener("click", () => closeModal(modal)) + ); + + const form = modal.querySelector("[data-export-form]"); + if (form) { + form.addEventListener("submit", handleSubmit); + } + } + + function closeModal(modal) { + modal.classList.remove("is-active"); + setTimeout(() => { + modalContainer.innerHTML = ""; + }, 200); + } + + async function handleSubmit(event) { + event.preventDefault(); + const form = event.currentTarget; + const submitUrl = form.action; + const formData = new FormData(form); + const format = formData.get("format") || "csv"; + + const response = await fetch(submitUrl, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + format, + include_metadata: formData.get("include_metadata") === "true", + filters: null, + }), + }); + + if (!response.ok) { + alert("Export failed. Please try again."); + return; + } + + const blob = await response.blob(); + const disposition = response.headers.get("Content-Disposition"); + let filename = "export"; + if (disposition) { + const match = disposition.match(/filename=([^;]+)/i); + if (match) { + filename = match[1].replace(/"/g, ""); + } + } + + const url = window.URL.createObjectURL(blob); + const link = document.createElement("a"); + link.href = url; + link.download = filename; + document.body.appendChild(link); + link.click(); + link.remove(); + window.URL.revokeObjectURL(url); + + const modal = modalContainer.querySelector(".modal"); + if (modal) { + closeModal(modal); + } + } + + document.querySelectorAll("[data-export-trigger]").forEach((button) => { + button.addEventListener("click", async (event) => { + event.preventDefault(); + const dataset = button.getAttribute("data-export-target"); + if (!dataset) return; + try { + await loadModal(dataset); + } catch (error) { + console.error(error); + alert("Unable to open export dialog."); + } + }); + }); +}); diff --git a/static/js/imports.js b/static/js/imports.js new file mode 100644 index 0000000..f64480a --- /dev/null +++ b/static/js/imports.js @@ -0,0 +1,100 @@ +document.addEventListener("DOMContentLoaded", () => { + const dropzones = document.querySelectorAll("[data-import-dropzone]"); + const uploadButtons = document.querySelectorAll( + "[data-import-upload-trigger]" + ); + const resetButtons = document.querySelectorAll("[data-import-reset]"); + const feedbackEl = document.querySelector("#import-upload-feedback"); + + function showFeedback(message, type = "info") { + if (!feedbackEl) return; + feedbackEl.textContent = message; + feedbackEl.classList.remove("hidden", "success", "error", "info"); + feedbackEl.classList.add(type); + } + + function hideFeedback() { + if (!feedbackEl) return; + feedbackEl.textContent = ""; + feedbackEl.classList.add("hidden"); + } + + dropzones.forEach((zone) => { + const input = zone.querySelector("input[type='file']"); + const uploadButton = zone + .closest("[data-import-upload]") + .querySelector("[data-import-upload-trigger]"); + const resetButton = zone + .closest("[data-import-upload]") + .querySelector("[data-import-reset]"); + + function enableUpload() { + if (uploadButton) { + uploadButton.disabled = false; + } + if (resetButton) { + resetButton.hidden = false; + } + } + + function disableUpload() { + if (uploadButton) { + uploadButton.disabled = true; + } + if (resetButton) { + resetButton.hidden = true; + } + } + + zone.addEventListener("dragover", (event) => { + event.preventDefault(); + zone.classList.add("dragover"); + }); + + zone.addEventListener("dragleave", () => { + zone.classList.remove("dragover"); + }); + + zone.addEventListener("drop", (event) => { + event.preventDefault(); + zone.classList.remove("dragover"); + if (!event.dataTransfer?.files?.length) { + return; + } + input.files = event.dataTransfer.files; + enableUpload(); + hideFeedback(); + }); + + input.addEventListener("change", () => { + if (input.files?.length) { + enableUpload(); + hideFeedback(); + } else { + disableUpload(); + } + }); + + resetButton?.addEventListener("click", () => { + input.value = ""; + disableUpload(); + hideFeedback(); + }); + + uploadButton?.addEventListener("click", () => { + if (!input.files?.length) { + showFeedback( + "Please select a CSV or XLSX file before uploading.", + "error" + ); + return; + } + + showFeedback("Uploading…", "info"); + uploadButton.disabled = true; + uploadButton.classList.add("loading"); + + // Actual upload logic handled separately (e.g., fetch). + }); + }); +}); diff --git a/templates/Dashboard.html b/templates/Dashboard.html index d843aa0..9eea868 100644 --- a/templates/Dashboard.html +++ b/templates/Dashboard.html @@ -1,132 +1,178 @@ -{% extends "base.html" %} -{% block title %}Dashboard · CalMiner{% endblock %} +{% extends "base.html" %} {% block title %}Dashboard · CalMiner{% endblock %} {% +block head_extra %} + +{% endblock %} {% block content %} + -{% block head_extra %} - -{% endblock %} - -{% block content %} - - -
-
-

Total Projects

-

{{ metrics.total_projects }}

- Across all operation types -
-
-

Active Scenarios

-

{{ metrics.active_scenarios }}

- Ready for analysis -
-
-

Pending Simulations

-

{{ metrics.pending_simulations }}

- Awaiting execution -
-
-

Last Data Import

-

{{ metrics.last_import or '—' }}

- UTC timestamp -
-
- -
-
-
-
-

Recent Projects

- View all -
- {% if recent_projects %} - - - - - - - - - - {% for project in recent_projects %} - - - - - - {% endfor %} - -
ProjectOperationUpdated
- {{ project.name }} - {{ project.operation_type.value.replace('_', ' ') | title }}{{ project.updated_at.strftime('%Y-%m-%d') if project.updated_at else '—' }}
- {% else %} -

No recent projects. Create one now.

- {% endif %} -
- -
-
-

Simulation Pipeline

-
- {% if simulation_updates %} -
    - {% for update in simulation_updates %} -
  • - {{ update.timestamp_label or '—' }} -
    - {{ update.title }} -

    {{ update.description }}

    -
    -
  • - {% endfor %} -
- {% else %} -

No simulation runs yet. Configure a scenario to start simulations.

- {% endif %} -
-
- - -
+
+
+

Total Projects

+

{{ metrics.total_projects }}

+ Across all operation types +
+
+

Active Scenarios

+

{{ metrics.active_scenarios }}

+ Ready for analysis +
+
+

Pending Simulations

+

{{ metrics.pending_simulations }}

+ Awaiting execution +
+
+

Last Data Import

+

{{ metrics.last_import or '—' }}

+ UTC timestamp +
+
+ +
+
+
+
+

Recent Projects

+ View all +
+ {% if recent_projects %} + + + + + + + + + + {% for project in recent_projects %} + + + + + + {% endfor %} + +
ProjectOperationUpdated
+ {{ project.name }} + + + {{ project.operation_type.value.replace('_', ' ') | title }} + + {{ project.updated_at.strftime('%Y-%m-%d') if project.updated_at + else '—' }} +
+ {% else %} +

+ No recent projects. + Create one now. +

+ {% endif %} +
+ +
+
+

Simulation Pipeline

+
+ {% if simulation_updates %} +
    + {% for update in simulation_updates %} +
  • + {{ update.timestamp_label or '—' }} +
    + {{ update.title }} +

    {{ update.description }}

    +
    +
  • + {% endfor %} +
+ {% else %} +

+ No simulation runs yet. Configure a scenario to start simulations. +

+ {% endif %} +
+
+ + +
{% endblock %} diff --git a/templates/base.html b/templates/base.html index b159e20..a7c9700 100644 --- a/templates/base.html +++ b/templates/base.html @@ -5,6 +5,7 @@ {% block title %}CalMiner{% endblock %} + {% block head_extra %}{% endblock %} @@ -20,7 +21,9 @@
{% block scripts %}{% endblock %} - + + + diff --git a/templates/exports/modal.html b/templates/exports/modal.html new file mode 100644 index 0000000..6da58db --- /dev/null +++ b/templates/exports/modal.html @@ -0,0 +1,51 @@ + diff --git a/templates/partials/alerts.html b/templates/partials/alerts.html new file mode 100644 index 0000000..9315d62 --- /dev/null +++ b/templates/partials/alerts.html @@ -0,0 +1,10 @@ +{% macro toast(id, hidden=True, level="info", message="") %} + +{% endmacro %} diff --git a/templates/partials/import_preview_table.html b/templates/partials/import_preview_table.html new file mode 100644 index 0000000..86b582f --- /dev/null +++ b/templates/partials/import_preview_table.html @@ -0,0 +1,17 @@ +{% from "partials/components.html" import table_container %} + +{% call table_container("import-preview-container", hidden=True, aria_label="Import preview table", heading=table_heading or "Preview Rows") %} + + + Row + Status + Issues + {% for column in columns %} + {{ column }} + {% endfor %} + + + + + +{% endcall %} diff --git a/templates/partials/import_upload.html b/templates/partials/import_upload.html new file mode 100644 index 0000000..e0879a1 --- /dev/null +++ b/templates/partials/import_upload.html @@ -0,0 +1,25 @@ +{% from "partials/components.html" import feedback %} + +
+
+

{{ title or "Bulk Import" }}

+ {% if description %}

{{ description }}

{% endif %} +
+ +
+ +

Drag & drop CSV/XLSX files here or

+ +

Maximum size {{ max_size_hint or "10 MB" }}. UTF-8 encoding required.

+
+ +
+ + +
+ + {{ feedback("import-upload-feedback", hidden=True, role="alert") }} +
diff --git a/tests/conftest.py b/tests/conftest.py index 1b96454..631125c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -18,6 +18,7 @@ from routes.dashboard import router as dashboard_router from routes.projects import router as projects_router from routes.scenarios import router as scenarios_router from routes.imports import router as imports_router +from routes.exports import router as exports_router from services.importers import ImportIngestionService from services.unit_of_work import UnitOfWork from services.session import AuthSession, SessionTokens @@ -54,6 +55,7 @@ def app(session_factory: sessionmaker) -> FastAPI: application.include_router(projects_router) application.include_router(scenarios_router) application.include_router(imports_router) + application.include_router(exports_router) def _override_uow() -> Iterator[UnitOfWork]: with UnitOfWork(session_factory=session_factory) as uow: diff --git a/tests/test_export_routes.py b/tests/test_export_routes.py new file mode 100644 index 0000000..e103b13 --- /dev/null +++ b/tests/test_export_routes.py @@ -0,0 +1,130 @@ +from __future__ import annotations + +import csv +from io import BytesIO, StringIO +from zipfile import ZipFile + +from fastapi.testclient import TestClient +from sqlalchemy.orm import Session + +from models import Project, Scenario, ScenarioStatus +from services.unit_of_work import UnitOfWork + + +def _seed_projects(session: Session) -> None: + project = Project(name="Alpha", operation_type="open_pit") + project.updated_at = project.created_at + session.add(project) + session.commit() + + +def _seed_scenarios(session: Session, project: Project) -> Scenario: + scenario = Scenario( + name="Scenario A", + project_id=project.id, + status=ScenarioStatus.ACTIVE, + ) + session.add(scenario) + session.commit() + session.refresh(scenario) + return scenario + + +def test_projects_export_modal(client: TestClient) -> None: + response = client.get("/exports/modal/projects") + assert response.status_code == 200 + assert "Export Projects" in response.text + + +def test_scenarios_export_modal(client: TestClient) -> None: + response = client.get("/exports/modal/scenarios") + assert response.status_code == 200 + assert "Export Scenarios" in response.text + + +def test_project_export_csv(client: TestClient, unit_of_work_factory) -> None: + with unit_of_work_factory() as uow: + project = Project(name="CSV Project", operation_type="open_pit") + uow.projects.create(project) + + response = client.post( + "/exports/projects", + json={"format": "csv"}, + ) + + assert response.status_code == 200 + assert response.headers["Content-Type"].startswith("text/csv") + assert "attachment; filename=" in response.headers["Content-Disposition"] + + content = response.content.decode("utf-8") + reader = csv.reader(StringIO(content)) + rows = list(reader) + assert rows[0][:3] == ["name", "location", "operation_type"] + assert any(row[0] == "CSV Project" for row in rows[1:]) + + +def test_project_export_excel(client: TestClient, unit_of_work_factory) -> None: + with unit_of_work_factory() as uow: + project = Project(name="XLSX Project", operation_type="open_pit") + uow.projects.create(project) + + response = client.post( + "/exports/projects", + json={"format": "xlsx"}, + ) + + assert response.status_code == 200 + assert response.headers["Content-Type"].startswith( + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" + ) + + with ZipFile(BytesIO(response.content)) as archive: + assert "xl/workbook.xml" in archive.namelist() + + +def test_scenario_export_csv(client: TestClient, unit_of_work_factory) -> None: + with unit_of_work_factory() as uow: + project = Project(name="Scenario Project", operation_type="open_pit") + uow.projects.create(project) + scenario = Scenario( + name="Scenario CSV", + project_id=project.id, + status=ScenarioStatus.ACTIVE, + ) + uow.scenarios.create(scenario) + + response = client.post( + "/exports/scenarios", + json={"format": "csv"}, + ) + + assert response.status_code == 200 + reader = csv.reader(StringIO(response.content.decode("utf-8"))) + rows = list(reader) + assert rows[0][0] == "project_name" + assert any(row[0] == "Scenario Project" for row in rows[1:]) + + +def test_scenario_export_excel(client: TestClient, unit_of_work_factory) -> None: + with unit_of_work_factory() as uow: + project = Project(name="Scenario Excel", operation_type="open_pit") + uow.projects.create(project) + scenario = Scenario( + name="Scenario XLSX", + project_id=project.id, + status=ScenarioStatus.ACTIVE, + ) + uow.scenarios.create(scenario) + + response = client.post( + "/exports/scenarios", + json={"format": "xlsx"}, + ) + + assert response.status_code == 200 + assert response.headers["Content-Type"].startswith( + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" + ) + + with ZipFile(BytesIO(response.content)) as archive: + assert "xl/workbook.xml" in archive.namelist() -- 2.39.5 From e2465188c2ea1a677243d90a2bed0c1d0b4ed227 Mon Sep 17 00:00:00 2001 From: zwitschi Date: Mon, 10 Nov 2025 18:44:42 +0100 Subject: [PATCH 037/109] feat: enhance export and import workflows with improved error handling and notifications --- static/js/alerts.js | 11 +++++ static/js/exports.js | 84 ++++++++++++++++++++++++++++++------ static/js/imports.js | 5 +++ static/js/notifications.js | 38 ++++++++++++++++ templates/base.html | 1 + templates/exports/modal.html | 1 + 6 files changed, 127 insertions(+), 13 deletions(-) create mode 100644 static/js/alerts.js create mode 100644 static/js/notifications.js diff --git a/static/js/alerts.js b/static/js/alerts.js new file mode 100644 index 0000000..4336d9d --- /dev/null +++ b/static/js/alerts.js @@ -0,0 +1,11 @@ +document.addEventListener("DOMContentLoaded", () => { + document.querySelectorAll("[data-toast-close]").forEach((button) => { + button.addEventListener("click", () => { + const toast = button.closest(".toast"); + if (toast) { + toast.classList.add("hidden"); + setTimeout(() => toast.remove(), 200); + } + }); + }); +}); diff --git a/static/js/exports.js b/static/js/exports.js index 5c2a6de..f47e298 100644 --- a/static/js/exports.js +++ b/static/js/exports.js @@ -39,20 +39,67 @@ document.addEventListener("DOMContentLoaded", () => { const formData = new FormData(form); const format = formData.get("format") || "csv"; - const response = await fetch(submitUrl, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - format, - include_metadata: formData.get("include_metadata") === "true", - filters: null, - }), - }); + const submitBtn = form.querySelector("button[type='submit']"); + if (submitBtn) { + submitBtn.disabled = true; + submitBtn.classList.add("loading"); + } + + let response; + try { + response = await fetch(submitUrl, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + format, + include_metadata: formData.get("include_metadata") === "true", + filters: null, + }), + }); + } catch (error) { + console.error(error); + NotificationCenter.show({ + message: "Network error during export.", + level: "error", + }); + const errorContainer = form.querySelector("[data-export-error]"); + if (errorContainer) { + errorContainer.textContent = "Network error during export."; + errorContainer.classList.remove("hidden"); + } + submitBtn?.classList.remove("loading"); + submitBtn?.removeAttribute("disabled"); + return; + } if (!response.ok) { - alert("Export failed. Please try again."); + let detail = "Export failed. Please try again."; + try { + const payload = await response.json(); + if (payload?.detail) { + detail = Array.isArray(payload.detail) + ? payload.detail.map((item) => item.msg || item).join("; ") + : payload.detail; + } + } catch (error) { + // ignore JSON parse issues + } + + NotificationCenter.show({ + message: detail, + level: "error", + }); + + const errorContainer = form.querySelector("[data-export-error]"); + if (errorContainer) { + errorContainer.textContent = detail; + errorContainer.classList.remove("hidden"); + } + + submitBtn?.classList.remove("loading"); + submitBtn?.removeAttribute("disabled"); return; } @@ -79,6 +126,14 @@ document.addEventListener("DOMContentLoaded", () => { if (modal) { closeModal(modal); } + + NotificationCenter.show({ + message: `Export ready: ${filename}`, + level: "success", + }); + + submitBtn?.classList.remove("loading"); + submitBtn?.removeAttribute("disabled"); } document.querySelectorAll("[data-export-trigger]").forEach((button) => { @@ -90,7 +145,10 @@ document.addEventListener("DOMContentLoaded", () => { await loadModal(dataset); } catch (error) { console.error(error); - alert("Unable to open export dialog."); + NotificationCenter.show({ + message: "Unable to open export dialog.", + level: "error", + }); } }); }); diff --git a/static/js/imports.js b/static/js/imports.js index f64480a..c6fff40 100644 --- a/static/js/imports.js +++ b/static/js/imports.js @@ -95,6 +95,11 @@ document.addEventListener("DOMContentLoaded", () => { uploadButton.classList.add("loading"); // Actual upload logic handled separately (e.g., fetch). + NotificationCenter?.show({ + message: "Upload started…", + level: "info", + timeout: 2000, + }); }); }); }); diff --git a/static/js/notifications.js b/static/js/notifications.js new file mode 100644 index 0000000..8842ba5 --- /dev/null +++ b/static/js/notifications.js @@ -0,0 +1,38 @@ +(() => { + let container; + + function ensureContainer() { + if (!container) { + container = document.createElement("div"); + container.className = "toast-container"; + document.body.appendChild(container); + } + return container; + } + + function show({ message, level = "info", timeout = 5000 } = {}) { + const root = ensureContainer(); + const toast = document.createElement("div"); + toast.className = `toast toast--${level}`; + toast.setAttribute("role", "alert"); + toast.innerHTML = ` + +

${message}

+ + `; + root.appendChild(toast); + + const close = () => { + toast.classList.add("hidden"); + setTimeout(() => toast.remove(), 200); + }; + + toast.querySelector(".toast__close").addEventListener("click", close); + + if (timeout > 0) { + setTimeout(close, timeout); + } + } + + window.NotificationCenter = { show }; +})(); diff --git a/templates/base.html b/templates/base.html index a7c9700..2169b4d 100644 --- a/templates/base.html +++ b/templates/base.html @@ -24,6 +24,7 @@ + diff --git a/templates/exports/modal.html b/templates/exports/modal.html index 6da58db..7f1a107 100644 --- a/templates/exports/modal.html +++ b/templates/exports/modal.html @@ -45,6 +45,7 @@ + -- 2.39.5 From 3051f91ab0447356815f6248842e1dd5d274d46e Mon Sep 17 00:00:00 2001 From: zwitschi Date: Mon, 10 Nov 2025 18:50:46 +0100 Subject: [PATCH 038/109] feat: add export button for projects in the projects list view --- templates/projects/list.html | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/templates/projects/list.html b/templates/projects/list.html index a6dfb8c..39e3d65 100644 --- a/templates/projects/list.html +++ b/templates/projects/list.html @@ -36,7 +36,18 @@ {% for project in projects %} - {{ project.name }} + + {{ project.name }} + + {{ project.location or '—' }} {{ project.operation_type.value.replace('_', ' ') | title }} {{ project.scenario_count }} -- 2.39.5 From 51c0fcec95710db8aea7392835661bc72dff153c Mon Sep 17 00:00:00 2001 From: zwitschi Date: Mon, 10 Nov 2025 19:06:27 +0100 Subject: [PATCH 039/109] feat: add import dashboard UI and functionality for CSV and Excel uploads --- routes/imports.py | 23 +++ static/js/imports.js | 287 ++++++++++++++++++++-------- templates/imports/ui.html | 34 ++++ templates/partials/sidebar_nav.html | 3 +- 4 files changed, 270 insertions(+), 77 deletions(-) create mode 100644 templates/imports/ui.html diff --git a/routes/imports.py b/routes/imports.py index 40cffac..7dd85c3 100644 --- a/routes/imports.py +++ b/routes/imports.py @@ -3,6 +3,9 @@ from __future__ import annotations from io import BytesIO from fastapi import APIRouter, Depends, File, HTTPException, UploadFile, status +from fastapi import Request +from fastapi.responses import HTMLResponse +from fastapi.templating import Jinja2Templates from dependencies import get_import_ingestion_service, require_roles from models import User @@ -16,10 +19,30 @@ from schemas.imports import ( from services.importers import ImportIngestionService, UnsupportedImportFormat router = APIRouter(prefix="/imports", tags=["Imports"]) +templates = Jinja2Templates(directory="templates") MANAGE_ROLES = ("project_manager", "admin") +@router.get( + "/ui", + response_class=HTMLResponse, + include_in_schema=False, + name="imports.ui", +) +def import_dashboard( + request: Request, + _: User = Depends(require_roles(*MANAGE_ROLES)), +) -> HTMLResponse: + return templates.TemplateResponse( + request, + "imports/ui.html", + { + "title": "Imports", + }, + ) + + async def _read_upload_file(upload: UploadFile) -> BytesIO: content = await upload.read() if not content: diff --git a/static/js/imports.js b/static/js/imports.js index c6fff40..7354693 100644 --- a/static/js/imports.js +++ b/static/js/imports.js @@ -1,10 +1,19 @@ document.addEventListener("DOMContentLoaded", () => { - const dropzones = document.querySelectorAll("[data-import-dropzone]"); - const uploadButtons = document.querySelectorAll( - "[data-import-upload-trigger]" - ); - const resetButtons = document.querySelectorAll("[data-import-reset]"); - const feedbackEl = document.querySelector("#import-upload-feedback"); + const moduleEl = document.querySelector("[data-import-module]"); + if (!moduleEl) return; + + const dropzone = moduleEl.querySelector("[data-import-dropzone]"); + const input = dropzone?.querySelector("input[type='file']"); + const uploadButton = moduleEl.querySelector("[data-import-upload-trigger]"); + const resetButton = moduleEl.querySelector("[data-import-reset]"); + const feedbackEl = moduleEl.querySelector("#import-upload-feedback"); + const previewBody = moduleEl.querySelector("[data-import-preview-body]"); + const previewContainer = moduleEl.querySelector("#import-preview-container"); + const actionsEl = moduleEl.querySelector("[data-import-actions]"); + const commitButton = moduleEl.querySelector("[data-import-commit]"); + const cancelButton = moduleEl.querySelector("[data-import-cancel]"); + + let stageToken = null; function showFeedback(message, type = "info") { if (!feedbackEl) return; @@ -19,87 +28,213 @@ document.addEventListener("DOMContentLoaded", () => { feedbackEl.classList.add("hidden"); } - dropzones.forEach((zone) => { - const input = zone.querySelector("input[type='file']"); - const uploadButton = zone - .closest("[data-import-upload]") - .querySelector("[data-import-upload-trigger]"); - const resetButton = zone - .closest("[data-import-upload]") - .querySelector("[data-import-reset]"); - - function enableUpload() { - if (uploadButton) { - uploadButton.disabled = false; - } - if (resetButton) { - resetButton.hidden = false; - } + function clearPreview() { + if (previewBody) { + previewBody.innerHTML = ""; } + previewContainer?.classList.add("hidden"); + actionsEl?.classList.add("hidden"); + commitButton?.setAttribute("disabled", "disabled"); + stageToken = null; + } - function disableUpload() { - if (uploadButton) { - uploadButton.disabled = true; - } - if (resetButton) { - resetButton.hidden = true; - } + function enableUpload() { + uploadButton?.removeAttribute("disabled"); + resetButton?.classList.remove("hidden"); + } + + function disableUpload() { + uploadButton?.setAttribute("disabled", "disabled"); + uploadButton?.classList.remove("loading"); + resetButton?.classList.add("hidden"); + } + + dropzone?.addEventListener("dragover", (event) => { + event.preventDefault(); + dropzone.classList.add("dragover"); + }); + + dropzone?.addEventListener("dragleave", () => { + dropzone.classList.remove("dragover"); + }); + + dropzone?.addEventListener("drop", (event) => { + event.preventDefault(); + dropzone.classList.remove("dragover"); + if (!event.dataTransfer?.files?.length || !input) { + return; } + input.files = event.dataTransfer.files; + enableUpload(); + hideFeedback(); + }); - zone.addEventListener("dragover", (event) => { - event.preventDefault(); - zone.classList.add("dragover"); - }); - - zone.addEventListener("dragleave", () => { - zone.classList.remove("dragover"); - }); - - zone.addEventListener("drop", (event) => { - event.preventDefault(); - zone.classList.remove("dragover"); - if (!event.dataTransfer?.files?.length) { - return; - } - input.files = event.dataTransfer.files; + input?.addEventListener("change", () => { + if (input.files?.length) { enableUpload(); hideFeedback(); - }); - - input.addEventListener("change", () => { - if (input.files?.length) { - enableUpload(); - hideFeedback(); - } else { - disableUpload(); - } - }); - - resetButton?.addEventListener("click", () => { - input.value = ""; + } else { disableUpload(); - hideFeedback(); - }); + } + }); - uploadButton?.addEventListener("click", () => { - if (!input.files?.length) { - showFeedback( - "Please select a CSV or XLSX file before uploading.", - "error" - ); - return; - } + resetButton?.addEventListener("click", () => { + if (input) { + input.value = ""; + } + disableUpload(); + hideFeedback(); + clearPreview(); + }); - showFeedback("Uploading…", "info"); - uploadButton.disabled = true; - uploadButton.classList.add("loading"); + async function uploadAndPreview() { + if (!input?.files?.length) { + showFeedback( + "Please select a CSV or XLSX file before uploading.", + "error" + ); + return; + } - // Actual upload logic handled separately (e.g., fetch). - NotificationCenter?.show({ - message: "Upload started…", - level: "info", - timeout: 2000, + const file = input.files[0]; + showFeedback("Uploading…", "info"); + uploadButton?.classList.add("loading"); + uploadButton?.setAttribute("disabled", "disabled"); + + const formData = new FormData(); + formData.append("file", file); + + let response; + try { + response = await fetch("/imports/projects/preview", { + method: "POST", + body: formData, }); + } catch (error) { + console.error(error); + NotificationCenter?.show({ + message: "Network error during upload.", + level: "error", + }); + showFeedback("Network error during upload.", "error"); + uploadButton?.classList.remove("loading"); + uploadButton?.removeAttribute("disabled"); + return; + } + + if (!response.ok) { + const detail = await response.json().catch(() => ({})); + const message = detail?.detail || "Upload failed. Please check the file."; + NotificationCenter?.show({ message, level: "error" }); + showFeedback(message, "error"); + uploadButton?.classList.remove("loading"); + uploadButton?.removeAttribute("disabled"); + return; + } + + const payload = await response.json(); + hideFeedback(); + renderPreview(payload); + uploadButton?.classList.remove("loading"); + uploadButton?.removeAttribute("disabled"); + + NotificationCenter?.show({ + message: `Preview ready: ${payload.summary.accepted} row(s) accepted`, + level: "success", }); + } + + function renderPreview(payload) { + const rows = payload.rows || []; + const issues = payload.row_issues || []; + stageToken = payload.stage_token || null; + + if (!previewBody) return; + previewBody.innerHTML = ""; + + const issueMap = new Map(); + issues.forEach((issue) => { + issueMap.set(issue.row_number, issue.issues); + }); + + rows.forEach((row) => { + const tr = document.createElement("tr"); + const rowIssues = issueMap.get(row.row_number) || []; + const issuesText = [ + ...row.issues, + ...rowIssues.map((i) => i.message), + ].join(", "); + + tr.innerHTML = ` + ${row.row_number} + ${row.state} + ${issuesText || "—"} + ${Object.values(row.data) + .map((value) => `${value ?? ""}`) + .join("")} + `; + previewBody.appendChild(tr); + }); + + previewContainer?.classList.remove("hidden"); + if (stageToken && payload.summary.accepted > 0) { + actionsEl?.classList.remove("hidden"); + commitButton?.removeAttribute("disabled"); + } else { + actionsEl?.classList.add("hidden"); + commitButton?.setAttribute("disabled", "disabled"); + } + } + + uploadButton?.addEventListener("click", uploadAndPreview); + + commitButton?.addEventListener("click", async () => { + if (!stageToken) return; + commitButton.classList.add("loading"); + commitButton.setAttribute("disabled", "disabled"); + + let response; + try { + response = await fetch("/imports/projects/commit", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ token: stageToken }), + }); + } catch (error) { + console.error(error); + NotificationCenter?.show({ + message: "Network error during commit.", + level: "error", + }); + commitButton.classList.remove("loading"); + commitButton.removeAttribute("disabled"); + return; + } + + if (!response.ok) { + const detail = await response.json().catch(() => ({})); + const message = + detail?.detail || "Commit failed. Please review the import data."; + NotificationCenter?.show({ message, level: "error" }); + commitButton.classList.remove("loading"); + commitButton.removeAttribute("disabled"); + return; + } + + const result = await response.json(); + NotificationCenter?.show({ + message: `Import committed. Created: ${result.summary.created}, Updated: ${result.summary.updated}`, + level: "success", + }); + clearPreview(); + if (input) { + input.value = ""; + } + disableUpload(); + }); + + cancelButton?.addEventListener("click", () => { + clearPreview(); + NotificationCenter?.show({ message: "Import canceled.", level: "info" }); }); }); diff --git a/templates/imports/ui.html b/templates/imports/ui.html new file mode 100644 index 0000000..c583047 --- /dev/null +++ b/templates/imports/ui.html @@ -0,0 +1,34 @@ +{% extends "base.html" %} +{% from "partials/alerts.html" import toast %} + +{% block title %}Imports · CalMiner{% endblock %} + +{% block head_extra %} + +{% endblock %} + +{% block content %} + + +
+
+

Upload Projects or Scenarios

+
+
+ {% include "partials/import_upload.html" %} + {% include "partials/import_preview_table.html" %} + + +
+
+ + {{ toast("import-toast", hidden=True) }} +{% endblock %} \ No newline at end of file diff --git a/templates/partials/sidebar_nav.html b/templates/partials/sidebar_nav.html index 90e3c1a..64c7115 100644 --- a/templates/partials/sidebar_nav.html +++ b/templates/partials/sidebar_nav.html @@ -25,7 +25,8 @@ "links": [ {"href": dashboard_href, "label": "Dashboard", "match_prefix": "/"}, {"href": projects_href, "label": "Projects", "match_prefix": "/projects"}, - {"href": project_create_href, "label": "New Project", "match_prefix": "/projects/create"} + {"href": project_create_href, "label": "New Project", "match_prefix": "/projects/create"}, + {"href": "/imports/ui", "label": "Imports", "match_prefix": "/imports"} ] }, { -- 2.39.5 From 032e6d2681ac48363b62a31e14d2ce6f93b11aa2 Mon Sep 17 00:00:00 2001 From: zwitschi Date: Mon, 10 Nov 2025 21:37:07 +0100 Subject: [PATCH 040/109] feat: implement persistent audit logging for import/export operations with Prometheus metrics --- changelog.md | 1 + main.py | 2 + models/import_export_log.py | 32 ++ monitoring/__init__.py | 13 + monitoring/metrics.py | 42 +++ requirements.txt | 3 +- routes/exports.py | 193 +++++++++++- services/importers.py | 382 ++++++++++++++++++------ tests/test_import_export_integration.py | 124 ++++++++ tests/test_import_parsing.py | 64 ++++ 10 files changed, 760 insertions(+), 96 deletions(-) create mode 100644 models/import_export_log.py create mode 100644 monitoring/__init__.py create mode 100644 monitoring/metrics.py create mode 100644 tests/test_import_export_integration.py diff --git a/changelog.md b/changelog.md index 1cbf0cb..f30041c 100644 --- a/changelog.md +++ b/changelog.md @@ -33,3 +33,4 @@ - Documented the project/scenario import/export field mapping and file format guidelines in `calminer-docs/requirements/FR-008.md`, and introduced `schemas/imports.py` with Pydantic models that normalise incoming CSV/Excel rows for projects and scenarios. - Added `services/importers.py` to load CSV/XLSX files into the new import schemas, pulled in `openpyxl` for Excel support, and covered the parsing behaviour with `tests/test_import_parsing.py`. - Expanded the import ingestion workflow with staging previews, transactional persistence commits, FastAPI preview/commit endpoints under `/imports`, and new API tests (`tests/test_import_ingestion.py`, `tests/test_import_api.py`) ensuring end-to-end coverage. +- Added persistent audit logging via `ImportExportLog`, structured log emission, Prometheus metrics instrumentation, `/metrics` endpoint exposure, and updated operator/deployment documentation to guide monitoring setup. diff --git a/main.py b/main.py index 6aab622..ef58b7f 100644 --- a/main.py +++ b/main.py @@ -20,6 +20,7 @@ from routes.imports import router as imports_router from routes.exports import router as exports_router from routes.projects import router as projects_router from routes.scenarios import router as scenarios_router +from monitoring import router as monitoring_router from services.bootstrap import bootstrap_admin # Initialize database schema (imports above ensure models are registered) @@ -67,5 +68,6 @@ app.include_router(imports_router) app.include_router(exports_router) app.include_router(projects_router) app.include_router(scenarios_router) +app.include_router(monitoring_router) app.mount("/static", StaticFiles(directory="static"), name="static") diff --git a/models/import_export_log.py b/models/import_export_log.py new file mode 100644 index 0000000..4a5592a --- /dev/null +++ b/models/import_export_log.py @@ -0,0 +1,32 @@ +from __future__ import annotations + +from datetime import datetime + +from sqlalchemy import Column, DateTime, ForeignKey, Integer, String, Text +from sqlalchemy.sql import func + +from config.database import Base + + +class ImportExportLog(Base): + """Audit log for import and export operations.""" + + __tablename__ = "import_export_logs" + + id = Column(Integer, primary_key=True, index=True) + action = Column(String(32), nullable=False) # preview, commit, export + dataset = Column(String(32), nullable=False) # projects, scenarios, etc. + status = Column(String(16), nullable=False) # success, failure + filename = Column(String(255), nullable=True) + row_count = Column(Integer, nullable=True) + detail = Column(Text, nullable=True) + user_id = Column(Integer, ForeignKey("users.id"), nullable=True) + created_at = Column( + DateTime(timezone=True), nullable=False, server_default=func.now() + ) + + def __repr__(self) -> str: # pragma: no cover + return ( + f"ImportExportLog(id={self.id}, action={self.action}, " + f"dataset={self.dataset}, status={self.status})" + ) diff --git a/monitoring/__init__.py b/monitoring/__init__.py new file mode 100644 index 0000000..64edf71 --- /dev/null +++ b/monitoring/__init__.py @@ -0,0 +1,13 @@ +from __future__ import annotations + +from fastapi import APIRouter, Response +from prometheus_client import CONTENT_TYPE_LATEST, generate_latest + + +router = APIRouter(prefix="/metrics", tags=["monitoring"]) + + +@router.get("", summary="Prometheus metrics endpoint", include_in_schema=False) +async def metrics_endpoint() -> Response: + payload = generate_latest() + return Response(content=payload, media_type=CONTENT_TYPE_LATEST) diff --git a/monitoring/metrics.py b/monitoring/metrics.py new file mode 100644 index 0000000..a38787b --- /dev/null +++ b/monitoring/metrics.py @@ -0,0 +1,42 @@ +from __future__ import annotations + +from typing import Iterable + +from prometheus_client import Counter, Histogram + +IMPORT_DURATION = Histogram( + "calminer_import_duration_seconds", + "Duration of import preview and commit operations", + labelnames=("dataset", "action", "status"), +) + +IMPORT_TOTAL = Counter( + "calminer_import_total", + "Count of import operations", + labelnames=("dataset", "action", "status"), +) + +EXPORT_DURATION = Histogram( + "calminer_export_duration_seconds", + "Duration of export operations", + labelnames=("dataset", "status", "format"), +) + +EXPORT_TOTAL = Counter( + "calminer_export_total", + "Count of export operations", + labelnames=("dataset", "status", "format"), +) + + +def observe_import(action: str, dataset: str, status: str, seconds: float) -> None: + IMPORT_TOTAL.labels(dataset=dataset, action=action, status=status).inc() + IMPORT_DURATION.labels(dataset=dataset, action=action, + status=status).observe(seconds) + + +def observe_export(dataset: str, status: str, export_format: str, seconds: float) -> None: + EXPORT_TOTAL.labels(dataset=dataset, status=status, + format=export_format).inc() + EXPORT_DURATION.labels(dataset=dataset, status=status, + format=export_format).observe(seconds) diff --git a/requirements.txt b/requirements.txt index 7658aa7..fe0b091 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,4 +12,5 @@ passlib argon2-cffi python-jose python-multipart -openpyxl \ No newline at end of file +openpyxl +prometheus-client \ No newline at end of file diff --git a/routes/exports.py b/routes/exports.py index e896d37..d55c8a1 100644 --- a/routes/exports.py +++ b/routes/exports.py @@ -1,5 +1,7 @@ from __future__ import annotations +import logging +import time from datetime import datetime, timezone from typing import Annotated @@ -20,6 +22,10 @@ from services.export_serializers import ( stream_scenarios_to_csv, ) from services.unit_of_work import UnitOfWork +from models.import_export_log import ImportExportLog +from monitoring.metrics import observe_export + +logger = logging.getLogger(__name__) router = APIRouter(prefix="/exports", tags=["exports"]) @@ -65,6 +71,43 @@ def _ensure_repository(repo, name: str): return repo +def _record_export_audit( + *, + uow: UnitOfWork, + dataset: str, + status: str, + export_format: ExportFormat, + row_count: int, + filename: str | None, +) -> None: + try: + if uow.session is None: + return + log = ImportExportLog( + action="export", + dataset=dataset, + status=status, + filename=filename, + row_count=row_count, + detail=f"format={export_format.value}", + ) + uow.session.add(log) + uow.commit() + except Exception: + # best-effort auditing, do not break exports + if uow.session is not None: + uow.session.rollback() + logger.exception( + "export.audit.failed", + extra={ + "event": "export.audit", + "dataset": dataset, + "status": status, + "format": export_format.value, + }, + ) + + @router.post( "/projects", status_code=status.HTTP_200_OK, @@ -78,17 +121,89 @@ async def export_projects( ) -> Response: project_repo = _ensure_repository( getattr(uow, "projects", None), "Project") - projects = project_repo.filtered_for_export(request.filters) + try: + start = time.perf_counter() + projects = project_repo.filtered_for_export(request.filters) + except Exception as exc: + _record_export_audit( + uow=uow, + dataset="projects", + status="failure", + export_format=request.format, + row_count=0, + filename=None, + ) + logger.exception( + "export.failed", + extra={ + "event": "export", + "dataset": "projects", + "status": "failure", + "format": request.format.value, + }, + ) + raise exc filename = f"projects-{_timestamp_suffix()}" + start = time.perf_counter() if request.format == ExportFormat.CSV: stream = stream_projects_to_csv(projects) response = StreamingResponse(stream, media_type="text/csv") response.headers["Content-Disposition"] = f"attachment; filename={filename}.csv" + _record_export_audit( + uow=uow, + dataset="projects", + status="success", + export_format=request.format, + row_count=len(projects), + filename=f"{filename}.csv", + ) + logger.info( + "export", + extra={ + "event": "export", + "dataset": "projects", + "status": "success", + "format": request.format.value, + "row_count": len(projects), + "filename": f"{filename}.csv", + }, + ) + observe_export( + dataset="projects", + status="success", + export_format=request.format.value, + seconds=time.perf_counter() - start, + ) return response data = export_projects_to_excel(projects) + _record_export_audit( + uow=uow, + dataset="projects", + status="success", + export_format=request.format, + row_count=len(projects), + filename=f"{filename}.xlsx", + ) + logger.info( + "export", + extra={ + "event": "export", + "dataset": "projects", + "status": "success", + "format": request.format.value, + "row_count": len(projects), + "filename": f"{filename}.xlsx", + }, + ) + observe_export( + dataset="projects", + status="success", + export_format=request.format.value, + seconds=time.perf_counter() - start, + ) return StreamingResponse( iter([data]), media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", @@ -111,18 +226,90 @@ async def export_scenarios( ) -> Response: scenario_repo = _ensure_repository( getattr(uow, "scenarios", None), "Scenario") - scenarios = scenario_repo.filtered_for_export( - request.filters, include_project=True) + try: + start = time.perf_counter() + scenarios = scenario_repo.filtered_for_export( + request.filters, include_project=True) + except Exception as exc: + _record_export_audit( + uow=uow, + dataset="scenarios", + status="failure", + export_format=request.format, + row_count=0, + filename=None, + ) + logger.exception( + "export.failed", + extra={ + "event": "export", + "dataset": "scenarios", + "status": "failure", + "format": request.format.value, + }, + ) + raise exc filename = f"scenarios-{_timestamp_suffix()}" + start = time.perf_counter() if request.format == ExportFormat.CSV: stream = stream_scenarios_to_csv(scenarios) response = StreamingResponse(stream, media_type="text/csv") response.headers["Content-Disposition"] = f"attachment; filename={filename}.csv" + _record_export_audit( + uow=uow, + dataset="scenarios", + status="success", + export_format=request.format, + row_count=len(scenarios), + filename=f"{filename}.csv", + ) + logger.info( + "export", + extra={ + "event": "export", + "dataset": "scenarios", + "status": "success", + "format": request.format.value, + "row_count": len(scenarios), + "filename": f"{filename}.csv", + }, + ) + observe_export( + dataset="scenarios", + status="success", + export_format=request.format.value, + seconds=time.perf_counter() - start, + ) return response data = export_scenarios_to_excel(scenarios) + _record_export_audit( + uow=uow, + dataset="scenarios", + status="success", + export_format=request.format, + row_count=len(scenarios), + filename=f"{filename}.xlsx", + ) + logger.info( + "export", + extra={ + "event": "export", + "dataset": "scenarios", + "status": "success", + "format": request.format.value, + "row_count": len(scenarios), + "filename": f"{filename}.xlsx", + }, + ) + observe_export( + dataset="scenarios", + status="success", + export_format=request.format.value, + seconds=time.perf_counter() - start, + ) return StreamingResponse( iter([data]), media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", diff --git a/services/importers.py b/services/importers.py index 4594783..c1107b5 100644 --- a/services/importers.py +++ b/services/importers.py @@ -1,9 +1,11 @@ from __future__ import annotations +import logging +import time from dataclasses import dataclass from enum import Enum from pathlib import Path -from typing import Any, BinaryIO, Callable, Generic, Iterable, Mapping, TypeVar, cast +from typing import Any, BinaryIO, Callable, Generic, Iterable, Mapping, Optional, TypeVar, cast from uuid import uuid4 from types import MappingProxyType @@ -14,6 +16,10 @@ from pydantic import BaseModel, ValidationError from models import Project, Scenario from schemas.imports import ProjectImportRow, ScenarioImportRow from services.unit_of_work import UnitOfWork +from models.import_export_log import ImportExportLog +from monitoring.metrics import observe_import + +logger = logging.getLogger(__name__) TImportRow = TypeVar("TImportRow", bound=BaseModel) @@ -164,7 +170,34 @@ class ImportIngestionService: stream: BinaryIO, filename: str, ) -> ImportPreview[ProjectImportRow]: + start = time.perf_counter() result = load_project_imports(stream, filename) + status = "success" if not result.errors else "partial" + self._record_audit_log( + action="preview", + dataset="projects", + status=status, + filename=filename, + row_count=len(result.rows), + detail=f"accepted={len(result.rows)} parser_errors={len(result.errors)}", + ) + observe_import( + action="preview", + dataset="projects", + status=status, + seconds=time.perf_counter() - start, + ) + logger.info( + "import.preview", + extra={ + "event": "import.preview", + "dataset": "projects", + "status": status, + "filename": filename, + "row_count": len(result.rows), + "error_count": len(result.errors), + }, + ) parser_errors = result.errors preview_rows: list[ImportPreviewRow[ProjectImportRow]] = [] @@ -258,7 +291,34 @@ class ImportIngestionService: stream: BinaryIO, filename: str, ) -> ImportPreview[ScenarioImportRow]: + start = time.perf_counter() result = load_scenario_imports(stream, filename) + status = "success" if not result.errors else "partial" + self._record_audit_log( + action="preview", + dataset="scenarios", + status=status, + filename=filename, + row_count=len(result.rows), + detail=f"accepted={len(result.rows)} parser_errors={len(result.errors)}", + ) + observe_import( + action="preview", + dataset="scenarios", + status=status, + seconds=time.perf_counter() - start, + ) + logger.info( + "import.preview", + extra={ + "event": "import.preview", + "dataset": "scenarios", + "status": status, + "filename": filename, + "row_count": len(result.rows), + "error_count": len(result.errors), + }, + ) parser_errors = result.errors preview_rows: list[ImportPreviewRow[ScenarioImportRow]] = [] @@ -423,46 +483,101 @@ class ImportIngestionService: staged_view = _build_staged_view(staged) created = updated = 0 - with self._uow_factory() as uow: - if not uow.projects: - raise RuntimeError("Project repository is unavailable") + start = time.perf_counter() + try: + with self._uow_factory() as uow: + if not uow.projects: + raise RuntimeError("Project repository is unavailable") - for row in staged.rows: - mode = row.context.get("mode") - data = row.parsed.data + for row in staged.rows: + mode = row.context.get("mode") + data = row.parsed.data - if mode == "create": - project = Project( - name=data.name, - location=data.location, - operation_type=data.operation_type, - description=data.description, - ) - if data.created_at: - project.created_at = data.created_at - if data.updated_at: - project.updated_at = data.updated_at - uow.projects.create(project) - created += 1 - elif mode == "update": - project_id = row.context.get("project_id") - if not project_id: - raise ValueError( - "Staged project update is missing project_id context" + if mode == "create": + project = Project( + name=data.name, + location=data.location, + operation_type=data.operation_type, + description=data.description, ) - project = uow.projects.get(project_id) - project.name = data.name - project.location = data.location - project.operation_type = data.operation_type - project.description = data.description - if data.created_at: - project.created_at = data.created_at - if data.updated_at: - project.updated_at = data.updated_at - updated += 1 - else: - raise ValueError( - f"Unsupported staged project mode: {mode!r}") + if data.created_at: + project.created_at = data.created_at + if data.updated_at: + project.updated_at = data.updated_at + uow.projects.create(project) + created += 1 + elif mode == "update": + project_id = row.context.get("project_id") + if not project_id: + raise ValueError( + "Staged project update is missing project_id context" + ) + project = uow.projects.get(project_id) + project.name = data.name + project.location = data.location + project.operation_type = data.operation_type + project.description = data.description + if data.created_at: + project.created_at = data.created_at + if data.updated_at: + project.updated_at = data.updated_at + updated += 1 + else: + raise ValueError( + f"Unsupported staged project mode: {mode!r}") + except Exception as exc: + self._record_audit_log( + action="commit", + dataset="projects", + status="failure", + filename=None, + row_count=len(staged.rows), + detail=f"error={type(exc).__name__}: {exc}", + ) + observe_import( + action="commit", + dataset="projects", + status="failure", + seconds=time.perf_counter() - start, + ) + logger.exception( + "import.commit.failed", + extra={ + "event": "import.commit", + "dataset": "projects", + "status": "failure", + "row_count": len(staged.rows), + "token": token, + }, + ) + raise + else: + self._record_audit_log( + action="commit", + dataset="projects", + status="success", + filename=None, + row_count=len(staged.rows), + detail=f"created={created} updated={updated}", + ) + observe_import( + action="commit", + dataset="projects", + status="success", + seconds=time.perf_counter() - start, + ) + logger.info( + "import.commit", + extra={ + "event": "import.commit", + "dataset": "projects", + "status": "success", + "row_count": len(staged.rows), + "created": created, + "updated": updated, + "token": token, + }, + ) self._project_stage.pop(token, None) return ImportCommitResult( @@ -479,64 +594,119 @@ class ImportIngestionService: staged_view = _build_staged_view(staged) created = updated = 0 - with self._uow_factory() as uow: - if not uow.scenarios or not uow.projects: - raise RuntimeError("Scenario repositories are unavailable") + start = time.perf_counter() + try: + with self._uow_factory() as uow: + if not uow.scenarios or not uow.projects: + raise RuntimeError("Scenario repositories are unavailable") - for row in staged.rows: - mode = row.context.get("mode") - data = row.parsed.data + for row in staged.rows: + mode = row.context.get("mode") + data = row.parsed.data - project_id = row.context.get("project_id") - if not project_id: - raise ValueError( - "Staged scenario row is missing project_id context" - ) - - project = uow.projects.get(project_id) - - if mode == "create": - scenario = Scenario( - project_id=project.id, - name=data.name, - status=data.status, - start_date=data.start_date, - end_date=data.end_date, - discount_rate=data.discount_rate, - currency=data.currency, - primary_resource=data.primary_resource, - description=data.description, - ) - if data.created_at: - scenario.created_at = data.created_at - if data.updated_at: - scenario.updated_at = data.updated_at - uow.scenarios.create(scenario) - created += 1 - elif mode == "update": - scenario_id = row.context.get("scenario_id") - if not scenario_id: + project_id = row.context.get("project_id") + if not project_id: raise ValueError( - "Staged scenario update is missing scenario_id context" + "Staged scenario row is missing project_id context" ) - scenario = uow.scenarios.get(scenario_id) - scenario.project_id = project.id - scenario.name = data.name - scenario.status = data.status - scenario.start_date = data.start_date - scenario.end_date = data.end_date - scenario.discount_rate = data.discount_rate - scenario.currency = data.currency - scenario.primary_resource = data.primary_resource - scenario.description = data.description - if data.created_at: - scenario.created_at = data.created_at - if data.updated_at: - scenario.updated_at = data.updated_at - updated += 1 - else: - raise ValueError( - f"Unsupported staged scenario mode: {mode!r}") + + project = uow.projects.get(project_id) + + if mode == "create": + scenario = Scenario( + project_id=project.id, + name=data.name, + status=data.status, + start_date=data.start_date, + end_date=data.end_date, + discount_rate=data.discount_rate, + currency=data.currency, + primary_resource=data.primary_resource, + description=data.description, + ) + if data.created_at: + scenario.created_at = data.created_at + if data.updated_at: + scenario.updated_at = data.updated_at + uow.scenarios.create(scenario) + created += 1 + elif mode == "update": + scenario_id = row.context.get("scenario_id") + if not scenario_id: + raise ValueError( + "Staged scenario update is missing scenario_id context" + ) + scenario = uow.scenarios.get(scenario_id) + scenario.project_id = project.id + scenario.name = data.name + scenario.status = data.status + scenario.start_date = data.start_date + scenario.end_date = data.end_date + scenario.discount_rate = data.discount_rate + scenario.currency = data.currency + scenario.primary_resource = data.primary_resource + scenario.description = data.description + if data.created_at: + scenario.created_at = data.created_at + if data.updated_at: + scenario.updated_at = data.updated_at + updated += 1 + else: + raise ValueError( + f"Unsupported staged scenario mode: {mode!r}") + except Exception as exc: + self._record_audit_log( + action="commit", + dataset="scenarios", + status="failure", + filename=None, + row_count=len(staged.rows), + detail=f"error={type(exc).__name__}: {exc}", + ) + observe_import( + action="commit", + dataset="scenarios", + status="failure", + seconds=time.perf_counter() - start, + ) + logger.exception( + "import.commit.failed", + extra={ + "event": "import.commit", + "dataset": "scenarios", + "status": "failure", + "row_count": len(staged.rows), + "token": token, + }, + ) + raise + else: + self._record_audit_log( + action="commit", + dataset="scenarios", + status="success", + filename=None, + row_count=len(staged.rows), + detail=f"created={created} updated={updated}", + ) + observe_import( + action="commit", + dataset="scenarios", + status="success", + seconds=time.perf_counter() - start, + ) + logger.info( + "import.commit", + extra={ + "event": "import.commit", + "dataset": "scenarios", + "status": "success", + "row_count": len(staged.rows), + "created": created, + "updated": updated, + "token": token, + }, + ) self._scenario_stage.pop(token, None) return ImportCommitResult( @@ -545,6 +715,34 @@ class ImportIngestionService: summary=ImportCommitSummary(created=created, updated=updated), ) + def _record_audit_log( + self, + *, + action: str, + dataset: str, + status: str, + row_count: int, + detail: Optional[str], + filename: Optional[str], + ) -> None: + try: + with self._uow_factory() as uow: + if uow.session is None: + return + log = ImportExportLog( + action=action, + dataset=dataset, + status=status, + filename=filename, + row_count=row_count, + detail=detail, + ) + uow.session.add(log) + uow.commit() + except Exception: + # Audit logging must not break core workflows + pass + def _store_project_stage( self, rows: list[StagedRow[ProjectImportRow]] ) -> str: diff --git a/tests/test_import_export_integration.py b/tests/test_import_export_integration.py new file mode 100644 index 0000000..f74e318 --- /dev/null +++ b/tests/test_import_export_integration.py @@ -0,0 +1,124 @@ +from __future__ import annotations + +from io import BytesIO + +import pandas as pd +import pytest +from fastapi.testclient import TestClient + +from models import ( + MiningOperationType, + Project, + Scenario, + ScenarioStatus, +) +from models.import_export_log import ImportExportLog + + +@pytest.fixture() +def project_seed(unit_of_work_factory): + with unit_of_work_factory() as uow: + assert uow.projects is not None + project = Project(name="Seed Project", operation_type=MiningOperationType.OPEN_PIT) + uow.projects.create(project) + yield project + + +def test_project_import_preview_and_commit(client: TestClient, unit_of_work_factory) -> None: + csv_content = ( + "name,location,operation_type\n" + "Project Import A,Chile,open pit\n" + "Project Import B,Canada,underground\n" + ) + files = {"file": ("projects.csv", csv_content, "text/csv")} + + preview_response = client.post("/imports/projects/preview", files=files) + assert preview_response.status_code == 200 + preview_payload = preview_response.json() + assert preview_payload["summary"]["accepted"] == 2 + assert preview_payload["stage_token"] + + token = preview_payload["stage_token"] + + commit_response = client.post("/imports/projects/commit", json={"token": token}) + assert commit_response.status_code == 200 + commit_payload = commit_response.json() + assert commit_payload["summary"]["created"] == 2 + + with unit_of_work_factory() as uow: + assert uow.projects is not None + names = {project.name for project in uow.projects.list()} + assert {"Project Import A", "Project Import B"}.issubset(names) + # ensure audit logs recorded preview and commit events + assert uow.session is not None + logs = ( + uow.session.query(ImportExportLog) + .filter(ImportExportLog.dataset == "projects") + .order_by(ImportExportLog.created_at) + .all() + ) + actions = [log.action for log in logs] + assert "preview" in actions + assert "commit" in actions + + +def test_scenario_import_preview_and_commit(client: TestClient, unit_of_work_factory, project_seed) -> None: + csv_content = ( + "project_name,name,status\n" + "Seed Project,Scenario Import A,Draft\n" + "Seed Project,Scenario Import B,Active\n" + ) + files = {"file": ("scenarios.csv", csv_content, "text/csv")} + + preview_response = client.post("/imports/scenarios/preview", files=files) + assert preview_response.status_code == 200 + preview_payload = preview_response.json() + assert preview_payload["summary"]["accepted"] == 2 + token = preview_payload["stage_token"] + + commit_response = client.post("/imports/scenarios/commit", json={"token": token}) + assert commit_response.status_code == 200 + commit_payload = commit_response.json() + assert commit_payload["summary"]["created"] == 2 + + with unit_of_work_factory() as uow: + assert uow.projects is not None and uow.scenarios is not None + project = uow.projects.list()[0] + scenarios = uow.scenarios.list_for_project(project.id) + names = {scenario.name for scenario in scenarios} + assert {"Scenario Import A", "Scenario Import B"}.issubset(names) + assert uow.session is not None + logs = ( + uow.session.query(ImportExportLog) + .filter(ImportExportLog.dataset == "scenarios") + .order_by(ImportExportLog.created_at) + .all() + ) + actions = [log.action for log in logs] + assert "preview" in actions + assert "commit" in actions + + +def test_project_export_endpoint(client: TestClient, unit_of_work_factory) -> None: + with unit_of_work_factory() as uow: + assert uow.projects is not None + uow.projects.create(Project(name="Export Project", operation_type=MiningOperationType.OPEN_PIT)) + + response = client.post("/exports/projects", json={"format": "csv"}) + assert response.status_code == 200 + assert response.headers["Content-Type"].startswith("text/csv") + assert "attachment; filename=" in response.headers["Content-Disposition"] + body = response.content.decode("utf-8") + assert "Export Project" in body + + with unit_of_work_factory() as uow: + assert uow.session is not None + logs = ( + uow.session.query(ImportExportLog) + .filter(ImportExportLog.dataset == "projects", ImportExportLog.action == "export") + .order_by(ImportExportLog.created_at.desc()) + .first() + ) + assert logs is not None + assert logs.status == "success" + assert logs.row_count >= 1 \ No newline at end of file diff --git a/tests/test_import_parsing.py b/tests/test_import_parsing.py index 6c79974..2f08be7 100644 --- a/tests/test_import_parsing.py +++ b/tests/test_import_parsing.py @@ -1,12 +1,14 @@ from __future__ import annotations from io import BytesIO +from textwrap import dedent import pandas as pd import pytest from services.importers import ImportResult, load_project_imports, load_scenario_imports from schemas.imports import ProjectImportRow, ScenarioImportRow +from models.project import MiningOperationType def test_load_project_imports_from_csv() -> None: @@ -76,3 +78,65 @@ def test_import_errors_include_row_numbers() -> None: assert error.row_number == 2 assert error.field == "name" assert "required" in error.message + + +def test_project_import_handles_missing_columns() -> None: + csv_content = "name\nProject Only\n" + stream = BytesIO(csv_content.encode("utf-8")) + + result = load_project_imports(stream, "projects.csv") + + assert result.rows == [] + assert len(result.errors) == 1 + error = result.errors[0] + assert error.row_number == 2 + assert error.field == "operation_type" + + +def test_project_import_rejects_invalid_operation_type() -> None: + csv_content = "name,operation_type\nProject X,unknown\n" + stream = BytesIO(csv_content.encode("utf-8")) + + result = load_project_imports(stream, "projects.csv") + + assert len(result.rows) == 0 + assert len(result.errors) == 1 + error = result.errors[0] + assert error.row_number == 2 + assert error.field == "operation_type" + + +def test_scenario_import_flags_invalid_dates() -> None: + csv_content = dedent( + """ + project_name,name,status,start_date,end_date + Project A,Scenario Reverse,Draft,2025-12-31,2025-01-01 + """ + ).strip() + stream = BytesIO(csv_content.encode("utf-8")) + + result = load_scenario_imports(stream, "scenarios.csv") + + assert len(result.rows) == 0 + assert len(result.errors) == 1 + error = result.errors[0] + assert error.row_number == 2 + assert error.field is None + + +def test_scenario_import_handles_large_dataset() -> None: + buffer = BytesIO() + df = pd.DataFrame( + { + "project_name": ["Project"] * 500, + "name": [f"Scenario {i}" for i in range(500)], + "status": ["draft"] * 500, + } + ) + df.to_csv(buffer, index=False) + buffer.seek(0) + + result = load_scenario_imports(buffer, "bulk.csv") + + assert len(result.rows) == 500 + assert len(result.rows) == 500 -- 2.39.5 From 795a9f99f4ec4262a3e606fa7f3867296326ec18 Mon Sep 17 00:00:00 2001 From: zwitschi Date: Tue, 11 Nov 2025 18:29:59 +0100 Subject: [PATCH 041/109] feat: Enhance currency handling and validation across scenarios - Updated form template to prefill currency input with default value and added help text for clarity. - Modified integration tests to assert more descriptive error messages for invalid currency codes. - Introduced new tests for currency normalization and validation in various scenarios, including imports and exports. - Added comprehensive tests for pricing calculations, ensuring defaults are respected and overrides function correctly. - Implemented unit tests for pricing settings repository, ensuring CRUD operations and default settings are handled properly. - Enhanced scenario pricing evaluation tests to validate currency handling and metadata defaults. - Added simulation tests to ensure Monte Carlo runs are accurate and handle various distribution scenarios. --- changelog.md | 14 +- config/settings.py | 45 ++ dependencies.py | 26 + main.py | 22 +- models/__init__.py | 8 + models/financial_input.py | 11 +- models/pricing_settings.py | 176 +++++ models/project.py | 11 +- models/scenario.py | 14 +- routes/exports.py | 52 +- routes/projects.py | 13 +- routes/reports.py | 512 +++++++++++++ routes/scenarios.py | 159 +++- schemas/imports.py | 14 +- schemas/scenario.py | 32 +- scripts/initial_data.py | 64 +- services/__init__.py | 9 + services/bootstrap.py | 46 +- services/currency.py | 43 ++ services/export_query.py | 18 +- services/financial.py | 248 +++++++ services/pricing.py | 176 +++++ services/reporting.py | 676 ++++++++++++++++++ services/repositories.py | 304 +++++++- services/scenario_evaluation.py | 54 ++ services/simulation.py | 352 +++++++++ services/unit_of_work.py | 52 +- static/css/main.css | 201 ++++++ templates/partials/reports/filters_card.html | 33 + .../partials/reports/monte_carlo_table.html | 46 ++ templates/partials/reports/options_card.html | 56 ++ .../partials/reports/scenario_actions.html | 14 + templates/partials/reports_header.html | 24 + templates/reports/project_summary.html | 205 ++++++ templates/reports/scenario_comparison.html | 166 +++++ templates/reports/scenario_distribution.html | 149 ++++ templates/scenarios/form.html | 6 +- tests/integration/test_scenario_lifecycle.py | 6 +- tests/test_bootstrap.py | 93 ++- tests/test_currency.py | 42 ++ tests/test_export_routes.py | 14 + tests/test_financial.py | 162 +++++ tests/test_import_api.py | 32 + tests/test_import_parsing.py | 19 + tests/test_pricing.py | 155 ++++ tests/test_pricing_settings_repository.py | 209 ++++++ tests/test_repositories.py | 63 ++ tests/test_scenario_pricing.py | 120 ++++ tests/test_scenario_validation.py | 67 ++ tests/test_simulation.py | 158 ++++ 50 files changed, 5110 insertions(+), 81 deletions(-) create mode 100644 models/pricing_settings.py create mode 100644 routes/reports.py create mode 100644 services/currency.py create mode 100644 services/financial.py create mode 100644 services/pricing.py create mode 100644 services/reporting.py create mode 100644 services/scenario_evaluation.py create mode 100644 services/simulation.py create mode 100644 templates/partials/reports/filters_card.html create mode 100644 templates/partials/reports/monte_carlo_table.html create mode 100644 templates/partials/reports/options_card.html create mode 100644 templates/partials/reports/scenario_actions.html create mode 100644 templates/partials/reports_header.html create mode 100644 templates/reports/project_summary.html create mode 100644 templates/reports/scenario_comparison.html create mode 100644 templates/reports/scenario_distribution.html create mode 100644 tests/test_currency.py create mode 100644 tests/test_financial.py create mode 100644 tests/test_pricing.py create mode 100644 tests/test_pricing_settings_repository.py create mode 100644 tests/test_scenario_pricing.py create mode 100644 tests/test_simulation.py diff --git a/changelog.md b/changelog.md index f30041c..5ba2548 100644 --- a/changelog.md +++ b/changelog.md @@ -24,7 +24,6 @@ ## 2025-11-10 -- Extended authorization helper layer with project/scenario ownership lookups, integrated them into FastAPI dependencies, refreshed pytest fixtures to keep the suite authenticated, and documented the new patterns across RBAC plan and security guides. - Added dedicated pytest coverage for guard dependencies, exercising success plus failure paths (missing session, inactive user, missing roles, project/scenario access errors) via `tests/test_dependencies_guards.py`. - Added integration tests in `tests/test_authorization_integration.py` verifying anonymous 401 responses, role-based 403s, and authorized project manager flows across API and UI endpoints. - Implemented environment-driven admin bootstrap settings, wired the `bootstrap_admin` helper into FastAPI startup, added pytest coverage for creation/idempotency/reset logic, and documented operational guidance in the RBAC plan and security concept. @@ -34,3 +33,16 @@ - Added `services/importers.py` to load CSV/XLSX files into the new import schemas, pulled in `openpyxl` for Excel support, and covered the parsing behaviour with `tests/test_import_parsing.py`. - Expanded the import ingestion workflow with staging previews, transactional persistence commits, FastAPI preview/commit endpoints under `/imports`, and new API tests (`tests/test_import_ingestion.py`, `tests/test_import_api.py`) ensuring end-to-end coverage. - Added persistent audit logging via `ImportExportLog`, structured log emission, Prometheus metrics instrumentation, `/metrics` endpoint exposure, and updated operator/deployment documentation to guide monitoring setup. + +## 2025-11-11 + +- Centralised ISO-4217 currency validation across scenarios, imports, and export filters (`models/scenario.py`, `routes/scenarios.py`, `schemas/scenario.py`, `schemas/imports.py`, `services/export_query.py`) so malformed codes are rejected consistently at every entry point. +- Updated scenario services and UI flows to surface friendly validation errors and added regression coverage for imports, exports, API creation, and lifecycle flows ensuring currencies are normalised end-to-end. +- Recorded the completed “Ensure currency is used consistently” work in `.github/instructions/DONE.md` and ran the full pytest suite (150 tests) to verify the refactor. +- Linked projects to their pricing settings by updating SQLAlchemy models, repositories, seeding utilities, and migrations, and added regression tests to cover the new association and default backfill. +- Bootstrapped database-stored pricing settings at application startup, aligned initial data seeding with the database-first metadata flow, and added tests covering pricing bootstrap creation, project assignment, and idempotency. +- Extended pricing configuration support to prefer persisted metadata via `dependencies.get_pricing_metadata`, added retrieval tests for project/default fallbacks, and refreshed docs (`calminer-docs/specifications/price_calculation.md`, `pricing_settings_data_model.md`) to describe the database-backed workflow and bootstrap behaviour. +- Added `services/financial.py` NPV, IRR, and payback helpers with robust cash-flow normalisation, convergence safeguards, and fractional period support, plus comprehensive pytest coverage exercising representative project scenarios and failure modes. +- Authored `calminer-docs/specifications/financial_metrics.md` capturing DCF assumptions, solver behaviours, and worked examples, and cross-linked the architecture concepts to the new reference for consistent navigation. +- Implemented `services/simulation.py` Monte Carlo engine with configurable distributions, summary aggregation, and reproducible RNG seeding, introduced regression tests in `tests/test_simulation.py`, and documented configuration/usage in `calminer-docs/specifications/monte_carlo_simulation.md` with architecture cross-links. +- Polished reporting HTML contexts by cleaning stray fragments in `routes/reports.py`, adding download action metadata for project and scenario pages, and generating scenario comparison download URLs with correctly serialised repeated `scenario_ids` parameters. diff --git a/config/settings.py b/config/settings.py index 339a879..cb30fb4 100644 --- a/config/settings.py +++ b/config/settings.py @@ -7,6 +7,8 @@ from functools import lru_cache from typing import Optional +from services.pricing import PricingMetadata + from services.security import JWTSettings @@ -56,6 +58,10 @@ class Settings: admin_password: str = "ChangeMe123!" admin_roles: tuple[str, ...] = ("admin",) admin_force_reset: bool = False + pricing_default_payable_pct: float = 100.0 + pricing_default_currency: str | None = "USD" + pricing_moisture_threshold_pct: float = 8.0 + pricing_moisture_penalty_per_pct: float = 0.0 @classmethod def from_environment(cls) -> "Settings": @@ -105,6 +111,18 @@ class Settings: admin_force_reset=cls._bool_from_env( "CALMINER_SEED_FORCE", False ), + pricing_default_payable_pct=cls._float_from_env( + "CALMINER_PRICING_DEFAULT_PAYABLE_PCT", 100.0 + ), + pricing_default_currency=cls._optional_str( + "CALMINER_PRICING_DEFAULT_CURRENCY", "USD" + ), + pricing_moisture_threshold_pct=cls._float_from_env( + "CALMINER_PRICING_MOISTURE_THRESHOLD_PCT", 8.0 + ), + pricing_moisture_penalty_per_pct=cls._float_from_env( + "CALMINER_PRICING_MOISTURE_PENALTY_PER_PCT", 0.0 + ), ) @staticmethod @@ -145,6 +163,23 @@ class Settings: seen.add(role_name) return tuple(ordered) + @staticmethod + def _float_from_env(name: str, default: float) -> float: + raw_value = os.getenv(name) + if raw_value is None: + return default + try: + return float(raw_value) + except ValueError: + return default + + @staticmethod + def _optional_str(name: str, default: str | None = None) -> str | None: + raw_value = os.getenv(name) + if raw_value is None or raw_value.strip() == "": + return default + return raw_value.strip() + def jwt_settings(self) -> JWTSettings: """Build runtime JWT settings compatible with token helpers.""" @@ -180,6 +215,16 @@ class Settings: force_reset=self.admin_force_reset, ) + def pricing_metadata(self) -> PricingMetadata: + """Build pricing metadata defaults.""" + + return PricingMetadata( + default_payable_pct=self.pricing_default_payable_pct, + default_currency=self.pricing_default_currency, + moisture_threshold_pct=self.pricing_moisture_threshold_pct, + moisture_penalty_per_pct=self.pricing_moisture_penalty_per_pct, + ) + @lru_cache(maxsize=1) def get_settings() -> Settings: diff --git a/dependencies.py b/dependencies.py index 100504d..2d18234 100644 --- a/dependencies.py +++ b/dependencies.py @@ -22,6 +22,9 @@ from services.session import ( ) from services.unit_of_work import UnitOfWork from services.importers import ImportIngestionService +from services.pricing import PricingMetadata +from services.scenario_evaluation import ScenarioPricingConfig, ScenarioPricingEvaluator +from services.repositories import pricing_settings_to_metadata def get_unit_of_work() -> Generator[UnitOfWork, None, None]: @@ -46,6 +49,29 @@ def get_application_settings() -> Settings: return get_settings() +def get_pricing_metadata( + settings: Settings = Depends(get_application_settings), + uow: UnitOfWork = Depends(get_unit_of_work), +) -> PricingMetadata: + """Return pricing metadata defaults sourced from persisted pricing settings.""" + + stored = uow.get_pricing_metadata() + if stored is not None: + return stored + + fallback = settings.pricing_metadata() + seed_result = uow.ensure_default_pricing_settings(metadata=fallback) + return pricing_settings_to_metadata(seed_result.settings) + + +def get_pricing_evaluator( + metadata: PricingMetadata = Depends(get_pricing_metadata), +) -> ScenarioPricingEvaluator: + """Provide a configured scenario pricing evaluator.""" + + return ScenarioPricingEvaluator(ScenarioPricingConfig(metadata=metadata)) + + def get_jwt_settings() -> JWTSettings: """Provide JWT runtime configuration derived from settings.""" diff --git a/main.py b/main.py index ef58b7f..daf9e7a 100644 --- a/main.py +++ b/main.py @@ -19,9 +19,10 @@ from routes.dashboard import router as dashboard_router from routes.imports import router as imports_router from routes.exports import router as exports_router from routes.projects import router as projects_router +from routes.reports import router as reports_router from routes.scenarios import router as scenarios_router from monitoring import router as monitoring_router -from services.bootstrap import bootstrap_admin +from services.bootstrap import bootstrap_admin, bootstrap_pricing_settings # Initialize database schema (imports above ensure models are registered) Base.metadata.create_all(bind=engine) @@ -47,9 +48,12 @@ async def health() -> dict[str, str]: @app.on_event("startup") async def ensure_admin_bootstrap() -> None: - settings = get_settings().admin_bootstrap_settings() + settings = get_settings() + admin_settings = settings.admin_bootstrap_settings() + pricing_metadata = settings.pricing_metadata() try: - role_result, admin_result = bootstrap_admin(settings=settings) + role_result, admin_result = bootstrap_admin(settings=admin_settings) + pricing_result = bootstrap_pricing_settings(metadata=pricing_metadata) logger.info( "Admin bootstrap completed: roles=%s created=%s updated=%s rotated=%s assigned=%s", role_result.ensured, @@ -58,8 +62,17 @@ async def ensure_admin_bootstrap() -> None: admin_result.password_rotated, admin_result.roles_granted, ) + logger.info( + "Pricing settings bootstrap completed: slug=%s created=%s updated_fields=%s impurity_upserts=%s projects_assigned=%s", + pricing_result.seed.settings.slug, + pricing_result.seed.created, + pricing_result.seed.updated_fields, + pricing_result.seed.impurity_upserts, + pricing_result.projects_assigned, + ) except Exception: # pragma: no cover - defensive logging - logger.exception("Failed to bootstrap administrator account") + logger.exception( + "Failed to bootstrap administrator or pricing settings") app.include_router(dashboard_router) @@ -68,6 +81,7 @@ app.include_router(imports_router) app.include_router(exports_router) app.include_router(projects_router) app.include_router(scenarios_router) +app.include_router(reports_router) app.include_router(monitoring_router) app.mount("/static", StaticFiles(directory="static"), name="static") diff --git a/models/__init__.py b/models/__init__.py index 305ffb7..58b518a 100644 --- a/models/__init__.py +++ b/models/__init__.py @@ -11,6 +11,11 @@ from .metadata import ( StochasticVariable, StochasticVariableDescriptor, ) +from .pricing_settings import ( + PricingImpuritySettings, + PricingMetalSettings, + PricingSettings, +) from .project import MiningOperationType, Project from .scenario import Scenario, ScenarioStatus from .simulation_parameter import DistributionType, SimulationParameter @@ -21,6 +26,9 @@ __all__ = [ "FinancialInput", "MiningOperationType", "Project", + "PricingSettings", + "PricingMetalSettings", + "PricingImpuritySettings", "Scenario", "ScenarioStatus", "DistributionType", diff --git a/models/financial_input.py b/models/financial_input.py index eecdea2..fbcf69b 100644 --- a/models/financial_input.py +++ b/models/financial_input.py @@ -31,6 +31,7 @@ from sqlalchemy.sql import func from config.database import Base from .metadata import CostBucket +from services.currency import normalise_currency if TYPE_CHECKING: # pragma: no cover from .scenario import Scenario @@ -73,16 +74,12 @@ class FinancialInput(Base): DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now() ) - scenario: Mapped["Scenario"] = relationship("Scenario", back_populates="financial_inputs") + scenario: Mapped["Scenario"] = relationship( + "Scenario", back_populates="financial_inputs") @validates("currency") def _validate_currency(self, key: str, value: str | None) -> str | None: - if value is None: - return value - value = value.upper() - if len(value) != 3: - raise ValueError("Currency code must be a 3-letter ISO 4217 value") - return value + return normalise_currency(value) def __repr__(self) -> str: # pragma: no cover return f"FinancialInput(id={self.id!r}, scenario_id={self.scenario_id!r}, name={self.name!r})" diff --git a/models/pricing_settings.py b/models/pricing_settings.py new file mode 100644 index 0000000..cb6b893 --- /dev/null +++ b/models/pricing_settings.py @@ -0,0 +1,176 @@ +from __future__ import annotations + +"""Database models for persisted pricing configuration settings.""" + +from datetime import datetime +from typing import TYPE_CHECKING + +from sqlalchemy import ( + JSON, + DateTime, + ForeignKey, + Integer, + Numeric, + String, + Text, + UniqueConstraint, +) +from sqlalchemy.orm import Mapped, mapped_column, relationship, validates +from sqlalchemy.sql import func + +from config.database import Base +from services.currency import normalise_currency + +if TYPE_CHECKING: # pragma: no cover + from .project import Project + + +class PricingSettings(Base): + """Persisted pricing defaults applied to scenario evaluations.""" + + __tablename__ = "pricing_settings" + + id: Mapped[int] = mapped_column(Integer, primary_key=True) + name: Mapped[str] = mapped_column(String(128), nullable=False, unique=True) + slug: Mapped[str] = mapped_column(String(64), nullable=False, unique=True) + description: Mapped[str | None] = mapped_column(Text, nullable=True) + default_currency: Mapped[str | None] = mapped_column( + String(3), nullable=True) + default_payable_pct: Mapped[float] = mapped_column( + Numeric(5, 2), nullable=False, default=100.0 + ) + moisture_threshold_pct: Mapped[float] = mapped_column( + Numeric(5, 2), nullable=False, default=8.0 + ) + moisture_penalty_per_pct: Mapped[float] = mapped_column( + Numeric(14, 4), nullable=False, default=0.0 + ) + metadata_payload: Mapped[dict | None] = mapped_column( + "metadata", JSON, nullable=True + ) + created_at: Mapped[datetime] = mapped_column( + DateTime(timezone=True), nullable=False, server_default=func.now() + ) + updated_at: Mapped[datetime] = mapped_column( + DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now() + ) + + metal_overrides: Mapped[list["PricingMetalSettings"]] = relationship( + "PricingMetalSettings", + back_populates="pricing_settings", + cascade="all, delete-orphan", + passive_deletes=True, + ) + impurity_overrides: Mapped[list["PricingImpuritySettings"]] = relationship( + "PricingImpuritySettings", + back_populates="pricing_settings", + cascade="all, delete-orphan", + passive_deletes=True, + ) + projects: Mapped[list["Project"]] = relationship( + "Project", + back_populates="pricing_settings", + cascade="all", + ) + + @validates("slug") + def _normalise_slug(self, key: str, value: str) -> str: + return value.strip().lower() + + @validates("default_currency") + def _validate_currency(self, key: str, value: str | None) -> str | None: + return normalise_currency(value) + + def __repr__(self) -> str: # pragma: no cover + return f"PricingSettings(id={self.id!r}, slug={self.slug!r})" + + +class PricingMetalSettings(Base): + """Contract-specific overrides for a particular metal.""" + + __tablename__ = "pricing_metal_settings" + __table_args__ = ( + UniqueConstraint( + "pricing_settings_id", "metal_code", name="uq_pricing_metal_settings_code" + ), + ) + + id: Mapped[int] = mapped_column(Integer, primary_key=True) + pricing_settings_id: Mapped[int] = mapped_column( + ForeignKey("pricing_settings.id", ondelete="CASCADE"), nullable=False, index=True + ) + metal_code: Mapped[str] = mapped_column(String(32), nullable=False) + payable_pct: Mapped[float | None] = mapped_column( + Numeric(5, 2), nullable=True) + moisture_threshold_pct: Mapped[float | None] = mapped_column( + Numeric(5, 2), nullable=True) + moisture_penalty_per_pct: Mapped[float | None] = mapped_column( + Numeric(14, 4), nullable=True + ) + data: Mapped[dict | None] = mapped_column(JSON, nullable=True) + created_at: Mapped[datetime] = mapped_column( + DateTime(timezone=True), nullable=False, server_default=func.now() + ) + updated_at: Mapped[datetime] = mapped_column( + DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now() + ) + + pricing_settings: Mapped["PricingSettings"] = relationship( + "PricingSettings", back_populates="metal_overrides" + ) + + @validates("metal_code") + def _normalise_metal_code(self, key: str, value: str) -> str: + return value.strip().lower() + + def __repr__(self) -> str: # pragma: no cover + return ( + "PricingMetalSettings(" # noqa: ISC001 + f"id={self.id!r}, pricing_settings_id={self.pricing_settings_id!r}, " + f"metal_code={self.metal_code!r})" + ) + + +class PricingImpuritySettings(Base): + """Impurity penalty thresholds associated with pricing settings.""" + + __tablename__ = "pricing_impurity_settings" + __table_args__ = ( + UniqueConstraint( + "pricing_settings_id", + "impurity_code", + name="uq_pricing_impurity_settings_code", + ), + ) + + id: Mapped[int] = mapped_column(Integer, primary_key=True) + pricing_settings_id: Mapped[int] = mapped_column( + ForeignKey("pricing_settings.id", ondelete="CASCADE"), nullable=False, index=True + ) + impurity_code: Mapped[str] = mapped_column(String(32), nullable=False) + threshold_ppm: Mapped[float] = mapped_column( + Numeric(14, 4), nullable=False, default=0.0) + penalty_per_ppm: Mapped[float] = mapped_column( + Numeric(14, 4), nullable=False, default=0.0) + notes: Mapped[str | None] = mapped_column(Text, nullable=True) + created_at: Mapped[datetime] = mapped_column( + DateTime(timezone=True), nullable=False, server_default=func.now() + ) + updated_at: Mapped[datetime] = mapped_column( + DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now() + ) + + pricing_settings: Mapped["PricingSettings"] = relationship( + "PricingSettings", back_populates="impurity_overrides" + ) + + @validates("impurity_code") + def _normalise_impurity_code(self, key: str, value: str) -> str: + return value.strip().upper() + + def __repr__(self) -> str: # pragma: no cover + return ( + "PricingImpuritySettings(" # noqa: ISC001 + f"id={self.id!r}, pricing_settings_id={self.pricing_settings_id!r}, " + f"impurity_code={self.impurity_code!r})" + ) diff --git a/models/project.py b/models/project.py index de6e0ff..8493df0 100644 --- a/models/project.py +++ b/models/project.py @@ -4,7 +4,7 @@ from datetime import datetime from enum import Enum from typing import TYPE_CHECKING, List -from sqlalchemy import DateTime, Enum as SQLEnum, Integer, String, Text +from sqlalchemy import DateTime, Enum as SQLEnum, ForeignKey, Integer, String, Text from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.sql import func @@ -12,6 +12,7 @@ from config.database import Base if TYPE_CHECKING: # pragma: no cover from .scenario import Scenario + from .pricing_settings import PricingSettings class MiningOperationType(str, Enum): @@ -38,6 +39,10 @@ class Project(Base): SQLEnum(MiningOperationType), nullable=False, default=MiningOperationType.OTHER ) description: Mapped[str | None] = mapped_column(Text, nullable=True) + pricing_settings_id: Mapped[int | None] = mapped_column( + ForeignKey("pricing_settings.id", ondelete="SET NULL"), + nullable=True, + ) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False, server_default=func.now() ) @@ -51,6 +56,10 @@ class Project(Base): cascade="all, delete-orphan", passive_deletes=True, ) + pricing_settings: Mapped["PricingSettings | None"] = relationship( + "PricingSettings", + back_populates="projects", + ) def __repr__(self) -> str: # pragma: no cover - helpful for debugging return f"Project(id={self.id!r}, name={self.name!r})" diff --git a/models/scenario.py b/models/scenario.py index b7f08b0..0aa2410 100644 --- a/models/scenario.py +++ b/models/scenario.py @@ -14,10 +14,11 @@ from sqlalchemy import ( String, Text, ) -from sqlalchemy.orm import Mapped, mapped_column, relationship +from sqlalchemy.orm import Mapped, mapped_column, relationship, validates from sqlalchemy.sql import func from config.database import Base +from services.currency import normalise_currency from .metadata import ResourceType if TYPE_CHECKING: # pragma: no cover @@ -50,7 +51,8 @@ class Scenario(Base): ) start_date: Mapped[date | None] = mapped_column(Date, nullable=True) end_date: Mapped[date | None] = mapped_column(Date, nullable=True) - discount_rate: Mapped[float | None] = mapped_column(Numeric(5, 2), nullable=True) + discount_rate: Mapped[float | None] = mapped_column( + Numeric(5, 2), nullable=True) currency: Mapped[str | None] = mapped_column(String(3), nullable=True) primary_resource: Mapped[ResourceType | None] = mapped_column( SQLEnum(ResourceType), nullable=True @@ -62,7 +64,8 @@ class Scenario(Base): DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now() ) - project: Mapped["Project"] = relationship("Project", back_populates="scenarios") + project: Mapped["Project"] = relationship( + "Project", back_populates="scenarios") financial_inputs: Mapped[List["FinancialInput"]] = relationship( "FinancialInput", back_populates="scenario", @@ -76,5 +79,10 @@ class Scenario(Base): passive_deletes=True, ) + @validates("currency") + def _normalise_currency(self, key: str, value: str | None) -> str | None: + # Normalise to uppercase ISO-4217; raises when the code is malformed. + return normalise_currency(value) + def __repr__(self) -> str: # pragma: no cover return f"Scenario(id={self.id!r}, name={self.name!r}, project_id={self.project_id!r})" diff --git a/routes/exports.py b/routes/exports.py index d55c8a1..421b2df 100644 --- a/routes/exports.py +++ b/routes/exports.py @@ -121,9 +121,32 @@ async def export_projects( ) -> Response: project_repo = _ensure_repository( getattr(uow, "projects", None), "Project") + start = time.perf_counter() try: - start = time.perf_counter() projects = project_repo.filtered_for_export(request.filters) + except ValueError as exc: + _record_export_audit( + uow=uow, + dataset="projects", + status="failure", + export_format=request.format, + row_count=0, + filename=None, + ) + logger.warning( + "export.validation_failed", + extra={ + "event": "export", + "dataset": "projects", + "status": "validation_failed", + "format": request.format.value, + "error": str(exc), + }, + ) + raise HTTPException( + status_code=status.HTTP_422_UNPROCESSABLE_CONTENT, + detail=str(exc), + ) from exc except Exception as exc: _record_export_audit( uow=uow, @@ -145,7 +168,6 @@ async def export_projects( raise exc filename = f"projects-{_timestamp_suffix()}" - start = time.perf_counter() if request.format == ExportFormat.CSV: stream = stream_projects_to_csv(projects) @@ -226,10 +248,33 @@ async def export_scenarios( ) -> Response: scenario_repo = _ensure_repository( getattr(uow, "scenarios", None), "Scenario") + start = time.perf_counter() try: - start = time.perf_counter() scenarios = scenario_repo.filtered_for_export( request.filters, include_project=True) + except ValueError as exc: + _record_export_audit( + uow=uow, + dataset="scenarios", + status="failure", + export_format=request.format, + row_count=0, + filename=None, + ) + logger.warning( + "export.validation_failed", + extra={ + "event": "export", + "dataset": "scenarios", + "status": "validation_failed", + "format": request.format.value, + "error": str(exc), + }, + ) + raise HTTPException( + status_code=status.HTTP_422_UNPROCESSABLE_CONTENT, + detail=str(exc), + ) from exc except Exception as exc: _record_export_audit( uow=uow, @@ -251,7 +296,6 @@ async def export_scenarios( raise exc filename = f"scenarios-{_timestamp_suffix()}" - start = time.perf_counter() if request.format == ExportFormat.CSV: stream = stream_scenarios_to_csv(scenarios) diff --git a/routes/projects.py b/routes/projects.py index f105881..fbade1b 100644 --- a/routes/projects.py +++ b/routes/projects.py @@ -7,6 +7,7 @@ from fastapi.responses import HTMLResponse, RedirectResponse from fastapi.templating import Jinja2Templates from dependencies import ( + get_pricing_metadata, get_unit_of_work, require_any_role, require_project_resource, @@ -15,6 +16,7 @@ from dependencies import ( from models import MiningOperationType, Project, ScenarioStatus, User from schemas.project import ProjectCreate, ProjectRead, ProjectUpdate from services.exceptions import EntityConflictError, EntityNotFoundError +from services.pricing import PricingMetadata from services.unit_of_work import UnitOfWork router = APIRouter(prefix="/projects", tags=["Projects"]) @@ -54,6 +56,7 @@ def create_project( payload: ProjectCreate, _: User = Depends(require_roles(*MANAGE_ROLES)), uow: UnitOfWork = Depends(get_unit_of_work), + metadata: PricingMetadata = Depends(get_pricing_metadata), ) -> ProjectRead: project = Project(**payload.model_dump()) try: @@ -62,6 +65,9 @@ def create_project( raise HTTPException( status_code=status.HTTP_409_CONFLICT, detail=str(exc) ) from exc + default_settings = uow.ensure_default_pricing_settings( + metadata=metadata).settings + uow.set_project_pricing_settings(created, default_settings) return _to_read_model(created) @@ -122,6 +128,7 @@ def create_project_submit( operation_type: str = Form(...), description: str | None = Form(None), uow: UnitOfWork = Depends(get_unit_of_work), + metadata: PricingMetadata = Depends(get_pricing_metadata), ): def _normalise(value: str | None) -> str | None: if value is None: @@ -152,7 +159,7 @@ def create_project_submit( description=_normalise(description), ) try: - _require_project_repo(uow).create(project) + created = _require_project_repo(uow).create(project) except EntityConflictError as exc: return templates.TemplateResponse( request, @@ -167,6 +174,10 @@ def create_project_submit( status_code=status.HTTP_409_CONFLICT, ) + default_settings = uow.ensure_default_pricing_settings( + metadata=metadata).settings + uow.set_project_pricing_settings(created, default_settings) + return RedirectResponse( request.url_for("projects.project_list_page"), status_code=status.HTTP_303_SEE_OTHER, diff --git a/routes/reports.py b/routes/reports.py new file mode 100644 index 0000000..eea21d1 --- /dev/null +++ b/routes/reports.py @@ -0,0 +1,512 @@ +from __future__ import annotations + +from datetime import date +from urllib.parse import urlencode + +from fastapi import APIRouter, Depends, HTTPException, Query, Request, status +from fastapi.encoders import jsonable_encoder +from fastapi.responses import HTMLResponse +from fastapi.templating import Jinja2Templates + +from dependencies import ( + get_unit_of_work, + require_any_role, + require_project_resource, + require_roles, + require_scenario_resource, +) +from models import Project, Scenario, User +from services.exceptions import EntityNotFoundError, ScenarioValidationError +from services.reporting import ( + DEFAULT_ITERATIONS, + IncludeOptions, + ReportFilters, + ReportingService, + parse_include_tokens, + validate_percentiles, +) +from services.unit_of_work import UnitOfWork + +router = APIRouter(prefix="/reports", tags=["Reports"]) +templates = Jinja2Templates(directory="templates") + +READ_ROLES = ("viewer", "analyst", "project_manager", "admin") +MANAGE_ROLES = ("project_manager", "admin") + + +@router.get("/projects/{project_id}", name="reports.project_summary") +def project_summary_report( + project: Project = Depends(require_project_resource()), + _: User = Depends(require_any_role(*READ_ROLES)), + uow: UnitOfWork = Depends(get_unit_of_work), + include: str | None = Query( + None, + description="Comma-separated include tokens (distribution,samples,all).", + ), + scenario_ids: list[int] | None = Query( + None, + alias="scenario_ids", + description="Repeatable scenario identifier filter.", + ), + start_date: date | None = Query( + None, + description="Filter scenarios starting on or after this date.", + ), + end_date: date | None = Query( + None, + description="Filter scenarios ending on or before this date.", + ), + fmt: str = Query( + "json", + alias="format", + description="Response format (json only for this endpoint).", + ), + iterations: int | None = Query( + None, + gt=0, + description="Override Monte Carlo iteration count when distribution is included.", + ), + percentiles: list[float] | None = Query( + None, + description="Percentiles (0-100) for Monte Carlo summaries when included.", + ), +) -> dict[str, object]: + if fmt.lower() != "json": + raise HTTPException( + status_code=status.HTTP_406_NOT_ACCEPTABLE, + detail="Only JSON responses are supported; use the HTML endpoint for templates.", + ) + + include_options = parse_include_tokens(include) + try: + percentile_values = validate_percentiles(percentiles) + except ValueError as exc: + raise HTTPException( + status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, + detail=str(exc), + ) from exc + + scenario_filter = ReportFilters( + scenario_ids=set(scenario_ids) if scenario_ids else None, + start_date=start_date, + end_date=end_date, + ) + + service = ReportingService(uow) + report = service.project_summary( + project, + filters=scenario_filter, + include=include_options, + iterations=iterations or DEFAULT_ITERATIONS, + percentiles=percentile_values, + ) + return jsonable_encoder(report) + + +@router.get( + "/projects/{project_id}/scenarios/compare", + name="reports.project_scenario_comparison", +) +def project_scenario_comparison_report( + project: Project = Depends(require_project_resource()), + _: User = Depends(require_any_role(*READ_ROLES)), + uow: UnitOfWork = Depends(get_unit_of_work), + scenario_ids: list[int] = Query( + ..., alias="scenario_ids", description="Repeatable scenario identifier."), + include: str | None = Query( + None, + description="Comma-separated include tokens (distribution,samples,all).", + ), + fmt: str = Query( + "json", + alias="format", + description="Response format (json only for this endpoint).", + ), + iterations: int | None = Query( + None, + gt=0, + description="Override Monte Carlo iteration count when distribution is included.", + ), + percentiles: list[float] | None = Query( + None, + description="Percentiles (0-100) for Monte Carlo summaries when included.", + ), +) -> dict[str, object]: + unique_ids = list(dict.fromkeys(scenario_ids)) + if len(unique_ids) < 2: + raise HTTPException( + status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, + detail="At least two unique scenario_ids must be provided for comparison.", + ) + if fmt.lower() != "json": + raise HTTPException( + status_code=status.HTTP_406_NOT_ACCEPTABLE, + detail="Only JSON responses are supported; use the HTML endpoint for templates.", + ) + + include_options = parse_include_tokens(include) + try: + percentile_values = validate_percentiles(percentiles) + except ValueError as exc: + raise HTTPException( + status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, + detail=str(exc), + ) from exc + + try: + scenarios = uow.validate_scenarios_for_comparison(unique_ids) + except ScenarioValidationError as exc: + raise HTTPException( + status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, + detail={ + "code": exc.code, + "message": exc.message, + "scenario_ids": list(exc.scenario_ids or []), + }, + ) from exc + except EntityNotFoundError as exc: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=str(exc), + ) from exc + + if any(scenario.project_id != project.id for scenario in scenarios): + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="One or more scenarios are not associated with this project.", + ) + + service = ReportingService(uow) + report = service.scenario_comparison( + project, + scenarios, + include=include_options, + iterations=iterations or DEFAULT_ITERATIONS, + percentiles=percentile_values, + ) + return jsonable_encoder(report) + + +@router.get( + "/scenarios/{scenario_id}/distribution", + name="reports.scenario_distribution", +) +def scenario_distribution_report( + scenario: Scenario = Depends(require_scenario_resource()), + _: User = Depends(require_any_role(*READ_ROLES)), + uow: UnitOfWork = Depends(get_unit_of_work), + include: str | None = Query( + None, + description="Comma-separated include tokens (samples,all).", + ), + fmt: str = Query( + "json", + alias="format", + description="Response format (json only for this endpoint).", + ), + iterations: int | None = Query( + None, + gt=0, + description="Override Monte Carlo iteration count (default applies otherwise).", + ), + percentiles: list[float] | None = Query( + None, + description="Percentiles (0-100) for Monte Carlo summaries.", + ), +) -> dict[str, object]: + if fmt.lower() != "json": + raise HTTPException( + status_code=status.HTTP_406_NOT_ACCEPTABLE, + detail="Only JSON responses are supported; use the HTML endpoint for templates.", + ) + + requested = parse_include_tokens(include) + include_options = IncludeOptions( + distribution=True, samples=requested.samples) + + try: + percentile_values = validate_percentiles(percentiles) + except ValueError as exc: + raise HTTPException( + status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, + detail=str(exc), + ) from exc + + service = ReportingService(uow) + report = service.scenario_distribution( + scenario, + include=include_options, + iterations=iterations or DEFAULT_ITERATIONS, + percentiles=percentile_values, + ) + return jsonable_encoder(report) + + +@router.get( + "/projects/{project_id}/ui", + response_class=HTMLResponse, + include_in_schema=False, + name="reports.project_summary_page", +) +def project_summary_page( + request: Request, + project: Project = Depends(require_project_resource()), + _: User = Depends(require_any_role(*READ_ROLES)), + uow: UnitOfWork = Depends(get_unit_of_work), + include: str | None = Query( + None, + description="Comma-separated include tokens (distribution,samples,all).", + ), + scenario_ids: list[int] | None = Query( + None, + alias="scenario_ids", + description="Repeatable scenario identifier filter.", + ), + start_date: date | None = Query( + None, + description="Filter scenarios starting on or after this date.", + ), + end_date: date | None = Query( + None, + description="Filter scenarios ending on or before this date.", + ), + iterations: int | None = Query( + None, + gt=0, + description="Override Monte Carlo iteration count when distribution is included.", + ), + percentiles: list[float] | None = Query( + None, + description="Percentiles (0-100) for Monte Carlo summaries when included.", + ), +) -> HTMLResponse: + include_options = parse_include_tokens(include) + try: + percentile_values = validate_percentiles(percentiles) + except ValueError as exc: + raise HTTPException( + status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, + detail=str(exc), + ) from exc + + scenario_filter = ReportFilters( + scenario_ids=set(scenario_ids) if scenario_ids else None, + start_date=start_date, + end_date=end_date, + ) + + service = ReportingService(uow) + report = service.project_summary( + project, + filters=scenario_filter, + include=include_options, + iterations=iterations or DEFAULT_ITERATIONS, + percentiles=percentile_values, + ) + context = { + "request": request, + "project": report["project"], + "scenario_count": report["scenario_count"], + "aggregates": report["aggregates"], + "scenarios": report["scenarios"], + "filters": report["filters"], + "include_options": include_options, + "iterations": iterations or DEFAULT_ITERATIONS, + "percentiles": percentile_values, + "title": f"Project Summary · {project.name}", + "subtitle": "Aggregated financial and simulation insights across scenarios.", + "actions": [ + { + "href": request.url_for( + "reports.project_summary", + project_id=project.id, + ), + "label": "Download JSON", + } + ], + } + return templates.TemplateResponse( + request, + "reports/project_summary.html", + context, + ) + + +@router.get( + "/projects/{project_id}/scenarios/compare/ui", + response_class=HTMLResponse, + include_in_schema=False, + name="reports.project_scenario_comparison_page", +) +def project_scenario_comparison_page( + request: Request, + project: Project = Depends(require_project_resource()), + _: User = Depends(require_any_role(*READ_ROLES)), + uow: UnitOfWork = Depends(get_unit_of_work), + scenario_ids: list[int] = Query( + ..., alias="scenario_ids", description="Repeatable scenario identifier."), + include: str | None = Query( + None, + description="Comma-separated include tokens (distribution,samples,all).", + ), + iterations: int | None = Query( + None, + gt=0, + description="Override Monte Carlo iteration count when distribution is included.", + ), + percentiles: list[float] | None = Query( + None, + description="Percentiles (0-100) for Monte Carlo summaries when included.", + ), +) -> HTMLResponse: + unique_ids = list(dict.fromkeys(scenario_ids)) + if len(unique_ids) < 2: + raise HTTPException( + status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, + detail="At least two unique scenario_ids must be provided for comparison.", + ) + + include_options = parse_include_tokens(include) + try: + percentile_values = validate_percentiles(percentiles) + except ValueError as exc: + raise HTTPException( + status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, + detail=str(exc), + ) from exc + + try: + scenarios = uow.validate_scenarios_for_comparison(unique_ids) + except ScenarioValidationError as exc: + raise HTTPException( + status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, + detail={ + "code": exc.code, + "message": exc.message, + "scenario_ids": list(exc.scenario_ids or []), + }, + ) from exc + except EntityNotFoundError as exc: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=str(exc), + ) from exc + + if any(scenario.project_id != project.id for scenario in scenarios): + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="One or more scenarios are not associated with this project.", + ) + + service = ReportingService(uow) + report = service.scenario_comparison( + project, + scenarios, + include=include_options, + iterations=iterations or DEFAULT_ITERATIONS, + percentiles=percentile_values, + ) + comparison_json_url = request.url_for( + "reports.project_scenario_comparison", + project_id=project.id, + ) + comparison_query = urlencode( + [("scenario_ids", str(identifier)) for identifier in unique_ids] + ) + if comparison_query: + comparison_json_url = f"{comparison_json_url}?{comparison_query}" + + context = { + "request": request, + "project": report["project"], + "scenarios": report["scenarios"], + "comparison": report["comparison"], + "include_options": include_options, + "iterations": iterations or DEFAULT_ITERATIONS, + "percentiles": percentile_values, + "title": f"Scenario Comparison · {project.name}", + "subtitle": "Evaluate deterministic metrics and Monte Carlo trends side by side.", + "actions": [ + { + "href": comparison_json_url, + "label": "Download JSON", + } + ], + } + return templates.TemplateResponse( + request, + "reports/scenario_comparison.html", + context, + ) + + +@router.get( + "/scenarios/{scenario_id}/distribution/ui", + response_class=HTMLResponse, + include_in_schema=False, + name="reports.scenario_distribution_page", +) +def scenario_distribution_page( + request: Request, + scenario: Scenario = Depends(require_scenario_resource()), + _: User = Depends(require_any_role(*READ_ROLES)), + uow: UnitOfWork = Depends(get_unit_of_work), + include: str | None = Query( + None, + description="Comma-separated include tokens (samples,all).", + ), + iterations: int | None = Query( + None, + gt=0, + description="Override Monte Carlo iteration count (default applies otherwise).", + ), + percentiles: list[float] | None = Query( + None, + description="Percentiles (0-100) for Monte Carlo summaries.", + ), +) -> HTMLResponse: + requested = parse_include_tokens(include) + include_options = IncludeOptions( + distribution=True, samples=requested.samples) + + try: + percentile_values = validate_percentiles(percentiles) + except ValueError as exc: + raise HTTPException( + status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, + detail=str(exc), + ) from exc + + service = ReportingService(uow) + report = service.scenario_distribution( + scenario, + include=include_options, + iterations=iterations or DEFAULT_ITERATIONS, + percentiles=percentile_values, + ) + context = { + "request": request, + "scenario": report["scenario"], + "summary": report["summary"], + "metrics": report["metrics"], + "monte_carlo": report["monte_carlo"], + "include_options": include_options, + "iterations": iterations or DEFAULT_ITERATIONS, + "percentiles": percentile_values, + "title": f"Scenario Distribution · {scenario.name}", + "subtitle": "Deterministic and simulated distributions for a single scenario.", + "actions": [ + { + "href": request.url_for( + "reports.scenario_distribution", + scenario_id=scenario.id, + ), + "label": "Download JSON", + } + ], + } + return templates.TemplateResponse( + request, + "reports/scenario_distribution.html", + context, + ) diff --git a/routes/scenarios.py b/routes/scenarios.py index 89d5060..b229298 100644 --- a/routes/scenarios.py +++ b/routes/scenarios.py @@ -1,6 +1,7 @@ from __future__ import annotations from datetime import date +from types import SimpleNamespace from typing import List from fastapi import APIRouter, Depends, Form, HTTPException, Request, status @@ -8,6 +9,7 @@ from fastapi.responses import HTMLResponse, RedirectResponse from fastapi.templating import Jinja2Templates from dependencies import ( + get_pricing_metadata, get_unit_of_work, require_any_role, require_roles, @@ -21,11 +23,13 @@ from schemas.scenario import ( ScenarioRead, ScenarioUpdate, ) +from services.currency import CurrencyValidationError, normalise_currency from services.exceptions import ( EntityConflictError, EntityNotFoundError, ScenarioValidationError, ) +from services.pricing import PricingMetadata from services.unit_of_work import UnitOfWork router = APIRouter(tags=["Scenarios"]) @@ -143,6 +147,7 @@ def create_scenario_for_project( payload: ScenarioCreate, _: User = Depends(require_roles(*MANAGE_ROLES)), uow: UnitOfWork = Depends(get_unit_of_work), + metadata: PricingMetadata = Depends(get_pricing_metadata), ) -> ScenarioRead: project_repo = _require_project_repo(uow) scenario_repo = _require_scenario_repo(uow) @@ -152,7 +157,10 @@ def create_scenario_for_project( raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)) from exc - scenario = Scenario(project_id=project_id, **payload.model_dump()) + scenario_data = payload.model_dump() + if not scenario_data.get("currency") and metadata.default_currency: + scenario_data["currency"] = metadata.default_currency + scenario = Scenario(project_id=project_id, **scenario_data) try: created = scenario_repo.create(scenario) @@ -219,6 +227,33 @@ def _parse_discount_rate(value: str | None) -> float | None: return None +def _scenario_form_state( + *, + project_id: int, + name: str, + description: str | None, + status: ScenarioStatus, + start_date: date | None, + end_date: date | None, + discount_rate: float | None, + currency: str | None, + primary_resource: ResourceType | None, + scenario_id: int | None = None, +) -> SimpleNamespace: + return SimpleNamespace( + id=scenario_id, + project_id=project_id, + name=name, + description=description, + status=status, + start_date=start_date, + end_date=end_date, + discount_rate=discount_rate, + currency=currency, + primary_resource=primary_resource, + ) + + @router.get( "/projects/{project_id}/scenarios/new", response_class=HTMLResponse, @@ -230,6 +265,7 @@ def create_scenario_form( request: Request, _: User = Depends(require_roles(*MANAGE_ROLES)), uow: UnitOfWork = Depends(get_unit_of_work), + metadata: PricingMetadata = Depends(get_pricing_metadata), ) -> HTMLResponse: try: project = _require_project_repo(uow).get(project_id) @@ -252,6 +288,7 @@ def create_scenario_form( "cancel_url": request.url_for( "projects.view_project", project_id=project_id ), + "default_currency": metadata.default_currency, }, ) @@ -274,6 +311,7 @@ def create_scenario_submit( currency: str | None = Form(None), primary_resource: str | None = Form(None), uow: UnitOfWork = Depends(get_unit_of_work), + metadata: PricingMetadata = Depends(get_pricing_metadata), ): project_repo = _require_project_repo(uow) scenario_repo = _require_scenario_repo(uow) @@ -296,17 +334,59 @@ def create_scenario_submit( except ValueError: resource_enum = None - currency_value = _normalise(currency) - currency_value = currency_value.upper() if currency_value else None + name_value = name.strip() + description_value = _normalise(description) + start_date_value = _parse_date(start_date) + end_date_value = _parse_date(end_date) + discount_rate_value = _parse_discount_rate(discount_rate) + currency_input = _normalise(currency) + effective_currency = currency_input or metadata.default_currency + + try: + currency_value = ( + normalise_currency(effective_currency) + if effective_currency else None + ) + except CurrencyValidationError as exc: + form_state = _scenario_form_state( + project_id=project_id, + name=name_value, + description=description_value, + status=status_enum, + start_date=start_date_value, + end_date=end_date_value, + discount_rate=discount_rate_value, + currency=currency_input or metadata.default_currency, + primary_resource=resource_enum, + ) + return templates.TemplateResponse( + request, + "scenarios/form.html", + { + "project": project, + "scenario": form_state, + "scenario_statuses": _scenario_status_choices(), + "resource_types": _resource_type_choices(), + "form_action": request.url_for( + "scenarios.create_scenario_submit", project_id=project_id + ), + "cancel_url": request.url_for( + "projects.view_project", project_id=project_id + ), + "error": str(exc), + "default_currency": metadata.default_currency, + }, + status_code=status.HTTP_400_BAD_REQUEST, + ) scenario = Scenario( project_id=project_id, - name=name.strip(), - description=_normalise(description), + name=name_value, + description=description_value, status=status_enum, - start_date=_parse_date(start_date), - end_date=_parse_date(end_date), - discount_rate=_parse_discount_rate(discount_rate), + start_date=start_date_value, + end_date=end_date_value, + discount_rate=discount_rate_value, currency=currency_value, primary_resource=resource_enum, ) @@ -329,6 +409,7 @@ def create_scenario_submit( "projects.view_project", project_id=project_id ), "error": "Scenario could not be created.", + "default_currency": metadata.default_currency, }, status_code=status.HTTP_409_CONFLICT, ) @@ -392,6 +473,7 @@ def edit_scenario_form( require_scenario_resource(require_manage=True) ), uow: UnitOfWork = Depends(get_unit_of_work), + metadata: PricingMetadata = Depends(get_pricing_metadata), ) -> HTMLResponse: project = _require_project_repo(uow).get(scenario.project_id) @@ -409,6 +491,7 @@ def edit_scenario_form( "cancel_url": request.url_for( "scenarios.view_scenario", scenario_id=scenario.id ), + "default_currency": metadata.default_currency, }, ) @@ -432,22 +515,17 @@ def edit_scenario_submit( currency: str | None = Form(None), primary_resource: str | None = Form(None), uow: UnitOfWork = Depends(get_unit_of_work), + metadata: PricingMetadata = Depends(get_pricing_metadata), ): project = _require_project_repo(uow).get(scenario.project_id) - scenario.name = name.strip() - scenario.description = _normalise(description) + name_value = name.strip() + description_value = _normalise(description) try: scenario.status = ScenarioStatus(status_value) except ValueError: scenario.status = ScenarioStatus.DRAFT - scenario.start_date = _parse_date(start_date) - scenario.end_date = _parse_date(end_date) - - scenario.discount_rate = _parse_discount_rate(discount_rate) - - currency_value = _normalise(currency) - scenario.currency = currency_value.upper() if currency_value else None + status_enum = scenario.status resource_enum = None if primary_resource: @@ -455,6 +533,53 @@ def edit_scenario_submit( resource_enum = ResourceType(primary_resource) except ValueError: resource_enum = None + + start_date_value = _parse_date(start_date) + end_date_value = _parse_date(end_date) + discount_rate_value = _parse_discount_rate(discount_rate) + currency_input = _normalise(currency) + + try: + currency_value = normalise_currency(currency_input) + except CurrencyValidationError as exc: + form_state = _scenario_form_state( + scenario_id=scenario.id, + project_id=scenario.project_id, + name=name_value, + description=description_value, + status=status_enum, + start_date=start_date_value, + end_date=end_date_value, + discount_rate=discount_rate_value, + currency=currency_input, + primary_resource=resource_enum, + ) + return templates.TemplateResponse( + request, + "scenarios/form.html", + { + "project": project, + "scenario": form_state, + "scenario_statuses": _scenario_status_choices(), + "resource_types": _resource_type_choices(), + "form_action": request.url_for( + "scenarios.edit_scenario_submit", scenario_id=scenario.id + ), + "cancel_url": request.url_for( + "scenarios.view_scenario", scenario_id=scenario.id + ), + "error": str(exc), + "default_currency": metadata.default_currency, + }, + status_code=status.HTTP_400_BAD_REQUEST, + ) + + scenario.name = name_value + scenario.description = description_value + scenario.start_date = start_date_value + scenario.end_date = end_date_value + scenario.discount_rate = discount_rate_value + scenario.currency = currency_value scenario.primary_resource = resource_enum uow.flush() diff --git a/schemas/imports.py b/schemas/imports.py index c60b9a7..e9f3895 100644 --- a/schemas/imports.py +++ b/schemas/imports.py @@ -7,6 +7,7 @@ from typing import Literal from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator from models import MiningOperationType, ResourceType, ScenarioStatus +from services.currency import CurrencyValidationError, normalise_currency PreviewStateLiteral = Literal["new", "update", "skip", "error"] @@ -142,14 +143,13 @@ class ScenarioImportRow(BaseModel): @field_validator("currency", mode="before") @classmethod def normalise_currency(cls, value: Any | None) -> str | None: - if value is None: + text = _strip_or_none(value) + if text is None: return None - text = _normalise_string(value).upper() - if not text: - return None - if len(text) != 3: - raise ValueError("Currency code must be a 3-letter ISO value") - return text + try: + return normalise_currency(text) + except CurrencyValidationError as exc: + raise ValueError(str(exc)) from exc @field_validator("discount_rate", mode="before") @classmethod diff --git a/schemas/scenario.py b/schemas/scenario.py index 73573d6..4295868 100644 --- a/schemas/scenario.py +++ b/schemas/scenario.py @@ -5,6 +5,7 @@ from datetime import date, datetime from pydantic import BaseModel, ConfigDict, field_validator, model_validator from models import ResourceType, ScenarioStatus +from services.currency import CurrencyValidationError, normalise_currency class ScenarioBase(BaseModel): @@ -23,11 +24,15 @@ class ScenarioBase(BaseModel): @classmethod def normalise_currency(cls, value: str | None) -> str | None: if value is None: - return value - value = value.upper() - if len(value) != 3: - raise ValueError("Currency code must be a 3-letter ISO value") - return value + return None + candidate = value if isinstance(value, str) else str(value) + candidate = candidate.strip() + if not candidate: + return None + try: + return normalise_currency(candidate) + except CurrencyValidationError as exc: + raise ValueError(str(exc)) from exc class ScenarioCreate(ScenarioBase): @@ -50,11 +55,15 @@ class ScenarioUpdate(BaseModel): @classmethod def normalise_currency(cls, value: str | None) -> str | None: if value is None: - return value - value = value.upper() - if len(value) != 3: - raise ValueError("Currency code must be a 3-letter ISO value") - return value + return None + candidate = value if isinstance(value, str) else str(value) + candidate = candidate.strip() + if not candidate: + return None + try: + return normalise_currency(candidate) + except CurrencyValidationError as exc: + raise ValueError(str(exc)) from exc class ScenarioRead(ScenarioBase): @@ -75,7 +84,8 @@ class ScenarioComparisonRequest(BaseModel): def ensure_minimum_ids(self) -> "ScenarioComparisonRequest": unique_ids: list[int] = list(dict.fromkeys(self.scenario_ids)) if len(unique_ids) < 2: - raise ValueError("At least two unique scenario identifiers are required for comparison.") + raise ValueError( + "At least two unique scenario identifiers are required for comparison.") self.scenario_ids = unique_ids return self diff --git a/scripts/initial_data.py b/scripts/initial_data.py index bd877b7..fa752e8 100644 --- a/scripts/initial_data.py +++ b/scripts/initial_data.py @@ -7,8 +7,15 @@ from typing import Callable, Iterable from dotenv import load_dotenv +from config.settings import Settings from models import Role, User -from services.repositories import DEFAULT_ROLE_DEFINITIONS, RoleRepository, UserRepository +from services.repositories import ( + DEFAULT_ROLE_DEFINITIONS, + PricingSettingsSeedResult, + RoleRepository, + UserRepository, + ensure_default_pricing_settings, +) from services.unit_of_work import UnitOfWork @@ -45,7 +52,8 @@ def parse_bool(value: str | None) -> bool: def normalise_role_list(raw_value: str | None) -> tuple[str, ...]: if not raw_value: return ("admin",) - parts = [segment.strip() for segment in raw_value.split(",") if segment.strip()] + parts = [segment.strip() + for segment in raw_value.split(",") if segment.strip()] if "admin" not in parts: parts.insert(0, "admin") seen: set[str] = set() @@ -59,7 +67,8 @@ def normalise_role_list(raw_value: str | None) -> tuple[str, ...]: def load_config() -> SeedConfig: load_dotenv() - admin_email = os.getenv("CALMINER_SEED_ADMIN_EMAIL", "admin@calminer.local") + admin_email = os.getenv("CALMINER_SEED_ADMIN_EMAIL", + "admin@calminer.local") admin_username = os.getenv("CALMINER_SEED_ADMIN_USERNAME", "admin") admin_password = os.getenv("CALMINER_SEED_ADMIN_PASSWORD", "ChangeMe123!") admin_roles = normalise_role_list(os.getenv("CALMINER_SEED_ADMIN_ROLES")) @@ -140,12 +149,15 @@ def ensure_admin_user( for role_name in config.admin_roles: role = role_repo.get_by_name(role_name) if role is None: - logging.warning("Role '%s' is not defined and will be skipped", role_name) + logging.warning( + "Role '%s' is not defined and will be skipped", role_name) continue - already_assigned = any(assignment.role_id == role.id for assignment in user.role_assignments) + already_assigned = any(assignment.role_id == + role.id for assignment in user.role_assignments) if already_assigned: continue - user_repo.assign_role(user_id=user.id, role_id=role.id, granted_by=user.id) + user_repo.assign_role( + user_id=user.id, role_id=role.id, granted_by=user.id) roles_granted += 1 return AdminSeedResult( @@ -164,9 +176,33 @@ def seed_initial_data( logging.info("Starting initial data seeding") factory = unit_of_work_factory or UnitOfWork with factory() as uow: - assert uow.roles is not None and uow.users is not None + assert ( + uow.roles is not None + and uow.users is not None + and uow.pricing_settings is not None + and uow.projects is not None + ) role_result = ensure_default_roles(uow.roles) admin_result = ensure_admin_user(uow.users, uow.roles, config) + pricing_metadata = uow.get_pricing_metadata() + metadata_source = "database" + if pricing_metadata is None: + pricing_metadata = Settings.from_environment().pricing_metadata() + metadata_source = "environment" + pricing_result: PricingSettingsSeedResult = ensure_default_pricing_settings( + uow.pricing_settings, + metadata=pricing_metadata, + ) + + projects_without_pricing = [ + project + for project in uow.projects.list(with_pricing=True) + if project.pricing_settings is None + ] + assigned_projects = 0 + for project in projects_without_pricing: + uow.set_project_pricing_settings(project, pricing_result.settings) + assigned_projects += 1 logging.info( "Roles processed: %s total, %s created, %s updated", role_result.total, @@ -180,4 +216,16 @@ def seed_initial_data( admin_result.password_rotated, admin_result.roles_granted, ) - logging.info("Initial data seeding completed successfully") \ No newline at end of file + logging.info( + "Pricing settings ensured (source=%s): slug=%s created=%s updated_fields=%s impurity_upserts=%s", + metadata_source, + pricing_result.settings.slug, + pricing_result.created, + pricing_result.updated_fields, + pricing_result.impurity_upserts, + ) + logging.info( + "Projects updated with default pricing settings: %s", + assigned_projects, + ) + logging.info("Initial data seeding completed successfully") diff --git a/services/__init__.py b/services/__init__.py index 1476ae9..c6e3b26 100644 --- a/services/__init__.py +++ b/services/__init__.py @@ -1 +1,10 @@ """Service layer utilities.""" + +from .pricing import calculate_pricing, PricingInput, PricingMetadata, PricingResult + +__all__ = [ + "calculate_pricing", + "PricingInput", + "PricingMetadata", + "PricingResult", +] diff --git a/services/bootstrap.py b/services/bootstrap.py index 4ccb4d4..025996c 100644 --- a/services/bootstrap.py +++ b/services/bootstrap.py @@ -6,7 +6,11 @@ from typing import Callable from config.settings import AdminBootstrapSettings from models import User -from services.repositories import ensure_default_roles +from services.pricing import PricingMetadata +from services.repositories import ( + PricingSettingsSeedResult, + ensure_default_roles, +) from services.unit_of_work import UnitOfWork @@ -27,6 +31,12 @@ class AdminBootstrapResult: roles_granted: int +@dataclass(slots=True) +class PricingBootstrapResult: + seed: PricingSettingsSeedResult + projects_assigned: int + + def bootstrap_admin( *, settings: AdminBootstrapSettings, @@ -127,3 +137,37 @@ def _bootstrap_admin_user( password_rotated=password_rotated, roles_granted=roles_granted, ) + + +def bootstrap_pricing_settings( + *, + metadata: PricingMetadata, + unit_of_work_factory: Callable[[], UnitOfWork] = UnitOfWork, + default_slug: str = "default", +) -> PricingBootstrapResult: + """Ensure baseline pricing settings exist and projects reference them.""" + + with unit_of_work_factory() as uow: + seed_result = uow.ensure_default_pricing_settings( + metadata=metadata, + slug=default_slug, + ) + + assigned = 0 + if uow.projects: + default_settings = seed_result.settings + projects = uow.projects.list(with_pricing=True) + for project in projects: + if project.pricing_settings is None: + uow.set_project_pricing_settings(project, default_settings) + assigned += 1 + + logger.info( + "Pricing bootstrap result: slug=%s created=%s updated_fields=%s impurity_upserts=%s projects_assigned=%s", + seed_result.settings.slug, + seed_result.created, + seed_result.updated_fields, + seed_result.impurity_upserts, + assigned, + ) + return PricingBootstrapResult(seed=seed_result, projects_assigned=assigned) diff --git a/services/currency.py b/services/currency.py new file mode 100644 index 0000000..fa036f6 --- /dev/null +++ b/services/currency.py @@ -0,0 +1,43 @@ +from __future__ import annotations + +"""Utilities for currency normalization within pricing and financial workflows.""" + +import re +from dataclasses import dataclass + +VALID_CURRENCY_PATTERN = re.compile(r"^[A-Z]{3}$") + + +@dataclass(frozen=True) +class CurrencyValidationError(ValueError): + """Raised when a currency code fails validation.""" + + code: str + + def __str__(self) -> str: # pragma: no cover - dataclass repr not required in tests + return f"Invalid currency code: {self.code!r}" + + +def normalise_currency(code: str | None) -> str | None: + """Normalise currency codes to uppercase ISO-4217 values.""" + + if code is None: + return None + candidate = code.strip().upper() + if not VALID_CURRENCY_PATTERN.match(candidate): + raise CurrencyValidationError(candidate) + return candidate + + +def require_currency(code: str | None, default: str | None = None) -> str: + """Return normalised currency code, falling back to default when missing.""" + + normalised = normalise_currency(code) + if normalised is not None: + return normalised + if default is None: + raise CurrencyValidationError("") + fallback = normalise_currency(default) + if fallback is None: + raise CurrencyValidationError("") + return fallback diff --git a/services/export_query.py b/services/export_query.py index acbcb6e..7f6acf9 100644 --- a/services/export_query.py +++ b/services/export_query.py @@ -5,6 +5,7 @@ from datetime import date, datetime from typing import Iterable from models import MiningOperationType, ResourceType, ScenarioStatus +from services.currency import CurrencyValidationError, normalise_currency def _normalise_lower_strings(values: Iterable[str]) -> tuple[str, ...]: @@ -19,15 +20,22 @@ def _normalise_lower_strings(values: Iterable[str]) -> tuple[str, ...]: return tuple(sorted(unique)) -def _normalise_upper_strings(values: Iterable[str]) -> tuple[str, ...]: +def _normalise_upper_strings(values: Iterable[str | None]) -> tuple[str, ...]: unique: set[str] = set() for value in values: - if not value: + if value is None: continue - trimmed = value.strip().upper() - if not trimmed: + candidate = value if isinstance(value, str) else str(value) + candidate = candidate.strip() + if not candidate: continue - unique.add(trimmed) + try: + normalised = normalise_currency(candidate) + except CurrencyValidationError as exc: + raise ValueError(str(exc)) from exc + if normalised is None: + continue + unique.add(normalised) return tuple(sorted(unique)) diff --git a/services/financial.py b/services/financial.py new file mode 100644 index 0000000..93d8c3d --- /dev/null +++ b/services/financial.py @@ -0,0 +1,248 @@ +from __future__ import annotations + +"""Financial calculation helpers for project evaluation metrics.""" + +from dataclasses import dataclass +from datetime import date, datetime +from math import isclose, isfinite +from typing import Iterable, List, Sequence, Tuple + +Number = float + + +@dataclass(frozen=True, slots=True) +class CashFlow: + """Represents a dated cash flow in scenario currency.""" + + amount: Number + period_index: int | None = None + date: date | datetime | None = None + + +class ConvergenceError(RuntimeError): + """Raised when an iterative solver fails to converge.""" + + +class PaybackNotReachedError(RuntimeError): + """Raised when cumulative cash flows never reach a non-negative total.""" + + +def _coerce_date(value: date | datetime) -> date: + if isinstance(value, datetime): + return value.date() + return value + + +def normalize_cash_flows( + cash_flows: Iterable[CashFlow], + *, + compounds_per_year: int = 1, +) -> List[Tuple[Number, float]]: + """Normalise cash flows to ``(amount, periods)`` tuples. + + When explicit ``period_index`` values are provided they take precedence. If + only dates are supplied, the first dated cash flow anchors the timeline and + subsequent cash flows convert their day offsets into fractional periods + based on ``compounds_per_year``. When neither a period index nor a date is + present, cash flows are treated as sequential periods in input order. + """ + + flows: Sequence[CashFlow] = list(cash_flows) + if not flows: + return [] + + if compounds_per_year <= 0: + raise ValueError("compounds_per_year must be a positive integer") + + base_date: date | None = None + for flow in flows: + if flow.date is not None: + base_date = _coerce_date(flow.date) + break + + normalised: List[Tuple[Number, float]] = [] + for idx, flow in enumerate(flows): + amount = float(flow.amount) + if flow.period_index is not None: + periods = float(flow.period_index) + elif flow.date is not None and base_date is not None: + current_date = _coerce_date(flow.date) + delta_days = (current_date - base_date).days + period_length_days = 365.0 / float(compounds_per_year) + periods = delta_days / period_length_days + else: + periods = float(idx) + normalised.append((amount, periods)) + + return normalised + + +def discount_factor(rate: Number, periods: float, *, compounds_per_year: int = 1) -> float: + """Return the factor used to discount a value ``periods`` steps in the future.""" + + if compounds_per_year <= 0: + raise ValueError("compounds_per_year must be a positive integer") + + periodic_rate = rate / float(compounds_per_year) + return (1.0 + periodic_rate) ** (-periods) + + +def net_present_value( + rate: Number, + cash_flows: Iterable[CashFlow], + *, + residual_value: Number | None = None, + residual_periods: float | None = None, + compounds_per_year: int = 1, +) -> float: + """Calculate Net Present Value for ``cash_flows``. + + ``rate`` is a decimal (``0.1`` for 10%). Cash flows are discounted using the + given compounding frequency. When ``residual_value`` is provided it is + discounted at ``residual_periods`` periods; by default the value occurs one + period after the final cash flow. + """ + + normalised = normalize_cash_flows( + cash_flows, + compounds_per_year=compounds_per_year, + ) + + if not normalised and residual_value is None: + return 0.0 + + total = 0.0 + for amount, periods in normalised: + factor = discount_factor( + rate, periods, compounds_per_year=compounds_per_year) + total += amount * factor + + if residual_value is not None: + if residual_periods is None: + last_period = normalised[-1][1] if normalised else 0.0 + residual_periods = last_period + 1.0 + factor = discount_factor( + rate, residual_periods, compounds_per_year=compounds_per_year) + total += float(residual_value) * factor + + return total + + +def internal_rate_of_return( + cash_flows: Iterable[CashFlow], + *, + guess: Number = 0.1, + max_iterations: int = 100, + tolerance: float = 1e-6, + compounds_per_year: int = 1, +) -> float: + """Return the internal rate of return for ``cash_flows``. + + Uses Newton-Raphson iteration with a bracketed fallback when the derivative + becomes unstable. Raises :class:`ConvergenceError` if no root is found. + """ + + flows = normalize_cash_flows( + cash_flows, + compounds_per_year=compounds_per_year, + ) + if not flows: + raise ValueError("cash_flows must contain at least one item") + + amounts = [amount for amount, _ in flows] + if not any(amount < 0 for amount in amounts) or not any(amount > 0 for amount in amounts): + raise ValueError("cash_flows must include both negative and positive values") + + def _npv_with_flows(rate: float) -> float: + periodic_rate = rate / float(compounds_per_year) + if periodic_rate <= -1.0: + return float("inf") + total = 0.0 + for amount, periods in flows: + factor = (1.0 + periodic_rate) ** (-periods) + total += amount * factor + return total + + def _derivative(rate: float) -> float: + periodic_rate = rate / float(compounds_per_year) + if periodic_rate <= -1.0: + return float("inf") + derivative = 0.0 + for amount, periods in flows: + factor = (1.0 + periodic_rate) ** (-periods - 1.0) + derivative += -amount * periods * factor / float(compounds_per_year) + return derivative + + rate = float(guess) + for _ in range(max_iterations): + value = _npv_with_flows(rate) + if isclose(value, 0.0, abs_tol=tolerance): + return rate + derivative = _derivative(rate) + if derivative == 0.0 or not isfinite(derivative): + break + next_rate = rate - value / derivative + if abs(next_rate - rate) < tolerance: + return next_rate + rate = next_rate + + # Fallback to bracketed bisection between sensible bounds. + lower_bound = -0.99 * float(compounds_per_year) + upper_bound = 10.0 + lower_value = _npv_with_flows(lower_bound) + upper_value = _npv_with_flows(upper_bound) + + attempts = 0 + while lower_value * upper_value > 0 and attempts < 12: + upper_bound *= 2.0 + upper_value = _npv_with_flows(upper_bound) + attempts += 1 + + if lower_value * upper_value > 0: + raise ConvergenceError("IRR could not be bracketed within default bounds") + + for _ in range(max_iterations * 2): + midpoint = (lower_bound + upper_bound) / 2.0 + mid_value = _npv_with_flows(midpoint) + if isclose(mid_value, 0.0, abs_tol=tolerance): + return midpoint + if lower_value * mid_value < 0: + upper_bound = midpoint + upper_value = mid_value + else: + lower_bound = midpoint + lower_value = mid_value + raise ConvergenceError("IRR solver failed to converge") + + +def payback_period( + cash_flows: Iterable[CashFlow], + *, + allow_fractional: bool = True, + compounds_per_year: int = 1, +) -> float: + """Return the period index where cumulative cash flow becomes non-negative.""" + + flows = normalize_cash_flows( + cash_flows, + compounds_per_year=compounds_per_year, + ) + if not flows: + raise ValueError("cash_flows must contain at least one item") + + flows = sorted(flows, key=lambda item: item[1]) + cumulative = 0.0 + previous_period = flows[0][1] + + for index, (amount, periods) in enumerate(flows): + next_cumulative = cumulative + amount + if next_cumulative >= 0.0: + if not allow_fractional or isclose(amount, 0.0): + return periods + prev_period = previous_period if index > 0 else periods + fraction = -cumulative / amount + return prev_period + fraction * (periods - prev_period) + cumulative = next_cumulative + previous_period = periods + + raise PaybackNotReachedError("Cumulative cash flow never becomes non-negative") diff --git a/services/pricing.py b/services/pricing.py new file mode 100644 index 0000000..5ddbe50 --- /dev/null +++ b/services/pricing.py @@ -0,0 +1,176 @@ +from __future__ import annotations + +"""Pricing service implementing commodity revenue calculations. + +This module exposes data models and helpers for computing product pricing +according to the formulas outlined in +``calminer-docs/specifications/price_calculation.md``. It focuses on the core +calculation steps (payable metal, penalties, net revenue) and is intended to be +composed within broader scenario evaluation workflows. +""" + +from dataclasses import dataclass, field +from typing import Mapping + +from pydantic import BaseModel, Field, PositiveFloat, field_validator +from services.currency import require_currency + + +class PricingInput(BaseModel): + """Normalized inputs for pricing calculations.""" + + metal: str = Field(..., min_length=1) + ore_tonnage: PositiveFloat = Field( + ..., description="Total ore mass processed (metric tonnes)") + head_grade_pct: PositiveFloat = Field(..., gt=0, + le=100, description="Head grade as percent") + recovery_pct: PositiveFloat = Field(..., gt=0, + le=100, description="Recovery rate percent") + payable_pct: float | None = Field( + None, gt=0, le=100, description="Contractual payable percentage") + reference_price: PositiveFloat = Field( + ..., description="Reference price in base currency per unit") + treatment_charge: float = Field(0, ge=0) + smelting_charge: float = Field(0, ge=0) + moisture_pct: float = Field(0, ge=0, le=100) + moisture_threshold_pct: float | None = Field(None, ge=0, le=100) + moisture_penalty_per_pct: float | None = Field(None) + impurity_ppm: Mapping[str, float] = Field(default_factory=dict) + impurity_thresholds: Mapping[str, float] = Field(default_factory=dict) + impurity_penalty_per_ppm: Mapping[str, float] = Field(default_factory=dict) + premiums: float = Field(0) + fx_rate: PositiveFloat = Field( + 1, description="Multiplier to convert to scenario currency") + currency_code: str | None = Field( + None, description="Optional explicit currency override") + + @field_validator("impurity_ppm", mode="before") + @classmethod + def _validate_impurity_mapping(cls, value): + if isinstance(value, Mapping): + return {k: float(v) for k, v in value.items()} + return value + + +class PricingResult(BaseModel): + """Structured output summarising pricing computation results.""" + + metal: str + ore_tonnage: float + head_grade_pct: float + recovery_pct: float + payable_metal_tonnes: float + reference_price: float + gross_revenue: float + moisture_penalty: float + impurity_penalty: float + treatment_smelt_charges: float + premiums: float + net_revenue: float + currency: str | None + + +@dataclass(frozen=True) +class PricingMetadata: + """Metadata defaults applied when explicit inputs are omitted.""" + + default_payable_pct: float = 100.0 + default_currency: str | None = "USD" + moisture_threshold_pct: float = 8.0 + moisture_penalty_per_pct: float = 0.0 + impurity_thresholds: Mapping[str, float] = field(default_factory=dict) + impurity_penalty_per_ppm: Mapping[str, float] = field(default_factory=dict) + + +def calculate_pricing( + pricing_input: PricingInput, + *, + metadata: PricingMetadata | None = None, + currency: str | None = None, +) -> PricingResult: + """Calculate pricing metrics for the provided commodity input. + + Parameters + ---------- + pricing_input: + Normalised input data including ore tonnage, grades, charges, and + optional penalties. + metadata: + Optional default metadata applied when specific values are omitted from + ``pricing_input``. + currency: + Optional override for the output currency label. Falls back to + ``metadata.default_currency`` when not provided. + """ + + applied_metadata = metadata or PricingMetadata() + + payable_pct = ( + pricing_input.payable_pct + if pricing_input.payable_pct is not None + else applied_metadata.default_payable_pct + ) + moisture_threshold = ( + pricing_input.moisture_threshold_pct + if pricing_input.moisture_threshold_pct is not None + else applied_metadata.moisture_threshold_pct + ) + moisture_penalty_factor = ( + pricing_input.moisture_penalty_per_pct + if pricing_input.moisture_penalty_per_pct is not None + else applied_metadata.moisture_penalty_per_pct + ) + + impurity_thresholds = { + **applied_metadata.impurity_thresholds, + **pricing_input.impurity_thresholds, + } + impurity_penalty_factors = { + **applied_metadata.impurity_penalty_per_ppm, + **pricing_input.impurity_penalty_per_ppm, + } + + q_metal = pricing_input.ore_tonnage * (pricing_input.head_grade_pct / 100.0) * ( + pricing_input.recovery_pct / 100.0 + ) + payable_metal = q_metal * (payable_pct / 100.0) + + gross_revenue_ref = payable_metal * pricing_input.reference_price + charges = pricing_input.treatment_charge + pricing_input.smelting_charge + + moisture_excess = max(0.0, pricing_input.moisture_pct - moisture_threshold) + moisture_penalty = moisture_excess * moisture_penalty_factor + + impurity_penalty_total = 0.0 + for impurity, value in pricing_input.impurity_ppm.items(): + threshold = impurity_thresholds.get(impurity, 0.0) + penalty_factor = impurity_penalty_factors.get(impurity, 0.0) + impurity_penalty_total += max(0.0, value - threshold) * penalty_factor + + net_revenue_ref = ( + gross_revenue_ref - charges - moisture_penalty - impurity_penalty_total + ) + net_revenue_ref += pricing_input.premiums + + net_revenue = net_revenue_ref * pricing_input.fx_rate + + currency_code = require_currency( + currency or pricing_input.currency_code, + default=applied_metadata.default_currency, + ) + + return PricingResult( + metal=pricing_input.metal, + ore_tonnage=pricing_input.ore_tonnage, + head_grade_pct=pricing_input.head_grade_pct, + recovery_pct=pricing_input.recovery_pct, + payable_metal_tonnes=payable_metal, + reference_price=pricing_input.reference_price, + gross_revenue=gross_revenue_ref, + moisture_penalty=moisture_penalty, + impurity_penalty=impurity_penalty_total, + treatment_smelt_charges=charges, + premiums=pricing_input.premiums, + net_revenue=net_revenue, + currency=currency_code, + ) diff --git a/services/reporting.py b/services/reporting.py new file mode 100644 index 0000000..5ff5ef3 --- /dev/null +++ b/services/reporting.py @@ -0,0 +1,676 @@ +from __future__ import annotations + +"""Reporting service layer aggregating deterministic and simulation metrics.""" + +from dataclasses import dataclass, field +from datetime import date +import math +from typing import Iterable, Mapping, Sequence + +from models import FinancialCategory, Project, Scenario +from services.financial import ( + CashFlow, + ConvergenceError, + PaybackNotReachedError, + internal_rate_of_return, + net_present_value, + payback_period, +) +from services.simulation import ( + CashFlowSpec, + SimulationConfig, + SimulationMetric, + SimulationResult, + run_monte_carlo, +) +from services.unit_of_work import UnitOfWork + +DEFAULT_DISCOUNT_RATE = 0.1 +DEFAULT_ITERATIONS = 500 +DEFAULT_PERCENTILES: tuple[float, float, float] = (5.0, 50.0, 95.0) + +_COST_CATEGORY_SIGNS: Mapping[FinancialCategory, float] = { + FinancialCategory.REVENUE: 1.0, + FinancialCategory.CAPITAL_EXPENDITURE: -1.0, + FinancialCategory.OPERATING_EXPENDITURE: -1.0, + FinancialCategory.CONTINGENCY: -1.0, + FinancialCategory.OTHER: -1.0, +} + + +@dataclass(frozen=True) +class IncludeOptions: + """Flags controlling optional sections in report payloads.""" + + distribution: bool = False + samples: bool = False + + +@dataclass(slots=True) +class ReportFilters: + """Filter parameters applied when selecting scenarios for a report.""" + + scenario_ids: set[int] | None = None + start_date: date | None = None + end_date: date | None = None + + def matches(self, scenario: Scenario) -> bool: + if self.scenario_ids is not None and scenario.id not in self.scenario_ids: + return False + if self.start_date and scenario.start_date and scenario.start_date < self.start_date: + return False + if self.end_date and scenario.end_date and scenario.end_date > self.end_date: + return False + return True + + def to_dict(self) -> dict[str, object]: + payload: dict[str, object] = {} + if self.scenario_ids is not None: + payload["scenario_ids"] = sorted(self.scenario_ids) + if self.start_date is not None: + payload["start_date"] = self.start_date + if self.end_date is not None: + payload["end_date"] = self.end_date + return payload + + +@dataclass(slots=True) +class ScenarioFinancialTotals: + currency: str | None + inflows: float + outflows: float + net: float + by_category: dict[str, float] + + def to_dict(self) -> dict[str, object]: + return { + "currency": self.currency, + "inflows": _round_optional(self.inflows), + "outflows": _round_optional(self.outflows), + "net": _round_optional(self.net), + "by_category": { + key: _round_optional(value) for key, value in sorted(self.by_category.items()) + }, + } + + +@dataclass(slots=True) +class ScenarioDeterministicMetrics: + currency: str | None + discount_rate: float + compounds_per_year: int + npv: float | None + irr: float | None + payback_period: float | None + notes: list[str] = field(default_factory=list) + + def to_dict(self) -> dict[str, object]: + return { + "currency": self.currency, + "discount_rate": _round_optional(self.discount_rate, digits=4), + "compounds_per_year": self.compounds_per_year, + "npv": _round_optional(self.npv), + "irr": _round_optional(self.irr, digits=6), + "payback_period": _round_optional(self.payback_period, digits=4), + "notes": self.notes, + } + + +@dataclass(slots=True) +class ScenarioMonteCarloResult: + available: bool + notes: list[str] = field(default_factory=list) + result: SimulationResult | None = None + include_samples: bool = False + + def to_dict(self) -> dict[str, object]: + if not self.available or self.result is None: + return { + "available": False, + "notes": self.notes, + } + + metrics: dict[str, dict[str, object]] = {} + for metric, summary in self.result.summaries.items(): + metrics[metric.value] = { + "mean": _round_optional(summary.mean), + "std_dev": _round_optional(summary.std_dev), + "minimum": _round_optional(summary.minimum), + "maximum": _round_optional(summary.maximum), + "percentiles": { + f"{percentile:g}": _round_optional(value) + for percentile, value in sorted(summary.percentiles.items()) + }, + "sample_size": summary.sample_size, + "failed_runs": summary.failed_runs, + } + + samples_payload: dict[str, list[float | None]] | None = None + if self.include_samples and self.result.samples: + samples_payload = {} + for metric, samples in self.result.samples.items(): + samples_payload[metric.value] = [ + _sanitize_float(sample) for sample in samples.tolist() + ] + + payload: dict[str, object] = { + "available": True, + "iterations": self.result.iterations, + "metrics": metrics, + "notes": self.notes, + } + if samples_payload: + payload["samples"] = samples_payload + return payload + + +@dataclass(slots=True) +class ScenarioReport: + scenario: Scenario + totals: ScenarioFinancialTotals + deterministic: ScenarioDeterministicMetrics + monte_carlo: ScenarioMonteCarloResult | None + + def to_dict(self) -> dict[str, object]: + scenario_info = { + "id": self.scenario.id, + "project_id": self.scenario.project_id, + "name": self.scenario.name, + "description": self.scenario.description, + "status": self.scenario.status.value, + "start_date": self.scenario.start_date, + "end_date": self.scenario.end_date, + "currency": self.scenario.currency, + "primary_resource": self.scenario.primary_resource.value + if self.scenario.primary_resource + else None, + "discount_rate": _round_optional(self.deterministic.discount_rate, digits=4), + "created_at": self.scenario.created_at, + "updated_at": self.scenario.updated_at, + "simulation_parameter_count": len(self.scenario.simulation_parameters or []), + } + payload: dict[str, object] = { + "scenario": scenario_info, + "financials": self.totals.to_dict(), + "metrics": self.deterministic.to_dict(), + } + if self.monte_carlo is not None: + payload["monte_carlo"] = self.monte_carlo.to_dict() + return payload + + +@dataclass(slots=True) +class AggregatedMetric: + average: float | None + minimum: float | None + maximum: float | None + + def to_dict(self) -> dict[str, object]: + return { + "average": _round_optional(self.average), + "minimum": _round_optional(self.minimum), + "maximum": _round_optional(self.maximum), + } + + +@dataclass(slots=True) +class ProjectAggregates: + total_inflows: float + total_outflows: float + total_net: float + deterministic_metrics: dict[str, AggregatedMetric] + + def to_dict(self) -> dict[str, object]: + return { + "financials": { + "total_inflows": _round_optional(self.total_inflows), + "total_outflows": _round_optional(self.total_outflows), + "total_net": _round_optional(self.total_net), + }, + "deterministic_metrics": { + metric: data.to_dict() + for metric, data in sorted(self.deterministic_metrics.items()) + }, + } + + +@dataclass(slots=True) +class MetricComparison: + metric: str + direction: str + best: tuple[int, str, float] | None + worst: tuple[int, str, float] | None + average: float | None + + def to_dict(self) -> dict[str, object]: + return { + "metric": self.metric, + "direction": self.direction, + "best": _comparison_entry(self.best), + "worst": _comparison_entry(self.worst), + "average": _round_optional(self.average), + } + + +def parse_include_tokens(raw: str | None) -> IncludeOptions: + tokens: set[str] = set() + if raw: + for part in raw.split(","): + token = part.strip().lower() + if token: + tokens.add(token) + if "all" in tokens: + return IncludeOptions(distribution=True, samples=True) + return IncludeOptions( + distribution=bool({"distribution", "monte_carlo", "mc"} & tokens), + samples="samples" in tokens, + ) + + +def validate_percentiles(values: Sequence[float] | None) -> tuple[float, ...]: + if not values: + return DEFAULT_PERCENTILES + seen: set[float] = set() + cleaned: list[float] = [] + for value in values: + percentile = float(value) + if percentile < 0.0 or percentile > 100.0: + raise ValueError("Percentiles must be between 0 and 100.") + if percentile not in seen: + seen.add(percentile) + cleaned.append(percentile) + if not cleaned: + return DEFAULT_PERCENTILES + return tuple(cleaned) + + +class ReportingService: + """Coordinates project and scenario reporting aggregation.""" + + def __init__(self, uow: UnitOfWork) -> None: + self._uow = uow + + def project_summary( + self, + project: Project, + *, + filters: ReportFilters, + include: IncludeOptions, + iterations: int, + percentiles: tuple[float, ...], + ) -> dict[str, object]: + scenarios = self._load_scenarios(project.id, filters) + reports = [ + self._build_scenario_report( + scenario, + include_distribution=include.distribution, + include_samples=include.samples, + iterations=iterations, + percentiles=percentiles, + ) + for scenario in scenarios + ] + aggregates = self._aggregate_project(reports) + return { + "project": _project_payload(project), + "scenario_count": len(reports), + "filters": filters.to_dict(), + "aggregates": aggregates.to_dict(), + "scenarios": [report.to_dict() for report in reports], + } + + def scenario_comparison( + self, + project: Project, + scenarios: Sequence[Scenario], + *, + include: IncludeOptions, + iterations: int, + percentiles: tuple[float, ...], + ) -> dict[str, object]: + reports = [ + self._build_scenario_report( + self._reload_scenario(scenario.id), + include_distribution=include.distribution, + include_samples=include.samples, + iterations=iterations, + percentiles=percentiles, + ) + for scenario in scenarios + ] + comparison = { + metric: data.to_dict() + for metric, data in self._build_comparisons(reports).items() + } + return { + "project": _project_payload(project), + "scenarios": [report.to_dict() for report in reports], + "comparison": comparison, + } + + def scenario_distribution( + self, + scenario: Scenario, + *, + include: IncludeOptions, + iterations: int, + percentiles: tuple[float, ...], + ) -> dict[str, object]: + report = self._build_scenario_report( + self._reload_scenario(scenario.id), + include_distribution=True, + include_samples=include.samples, + iterations=iterations, + percentiles=percentiles, + ) + return { + "scenario": report.to_dict()["scenario"], + "summary": report.totals.to_dict(), + "metrics": report.deterministic.to_dict(), + "monte_carlo": ( + report.monte_carlo.to_dict() if report.monte_carlo else { + "available": False} + ), + } + + def _load_scenarios(self, project_id: int, filters: ReportFilters) -> list[Scenario]: + repo = self._require_scenario_repo() + scenarios = repo.list_for_project(project_id, with_children=True) + return [scenario for scenario in scenarios if filters.matches(scenario)] + + def _reload_scenario(self, scenario_id: int) -> Scenario: + repo = self._require_scenario_repo() + return repo.get(scenario_id, with_children=True) + + def _build_scenario_report( + self, + scenario: Scenario, + *, + include_distribution: bool, + include_samples: bool, + iterations: int, + percentiles: tuple[float, ...], + ) -> ScenarioReport: + cash_flows, totals = _build_cash_flows(scenario) + deterministic = _calculate_deterministic_metrics( + scenario, cash_flows, totals) + monte_carlo: ScenarioMonteCarloResult | None = None + if include_distribution: + monte_carlo = _run_monte_carlo( + scenario, + cash_flows, + include_samples=include_samples, + iterations=iterations, + percentiles=percentiles, + ) + return ScenarioReport( + scenario=scenario, + totals=totals, + deterministic=deterministic, + monte_carlo=monte_carlo, + ) + + def _aggregate_project(self, reports: Sequence[ScenarioReport]) -> ProjectAggregates: + total_inflows = sum(report.totals.inflows for report in reports) + total_outflows = sum(report.totals.outflows for report in reports) + total_net = sum(report.totals.net for report in reports) + + metrics: dict[str, AggregatedMetric] = {} + for metric_name in ("npv", "irr", "payback_period"): + values = [ + getattr(report.deterministic, metric_name) + for report in reports + if getattr(report.deterministic, metric_name) is not None + ] + if values: + metrics[metric_name] = AggregatedMetric( + average=sum(values) / len(values), + minimum=min(values), + maximum=max(values), + ) + return ProjectAggregates( + total_inflows=total_inflows, + total_outflows=total_outflows, + total_net=total_net, + deterministic_metrics=metrics, + ) + + def _build_comparisons( + self, reports: Sequence[ScenarioReport] + ) -> Mapping[str, MetricComparison]: + comparisons: dict[str, MetricComparison] = {} + for metric_name, direction in ( + ("npv", "higher_is_better"), + ("irr", "higher_is_better"), + ("payback_period", "lower_is_better"), + ): + entries: list[tuple[int, str, float]] = [] + for report in reports: + value = getattr(report.deterministic, metric_name) + if value is None: + continue + entries.append( + (report.scenario.id, report.scenario.name, value)) + if not entries: + continue + if direction == "higher_is_better": + best = max(entries, key=lambda item: item[2]) + worst = min(entries, key=lambda item: item[2]) + else: + best = min(entries, key=lambda item: item[2]) + worst = max(entries, key=lambda item: item[2]) + average = sum(item[2] for item in entries) / len(entries) + comparisons[metric_name] = MetricComparison( + metric=metric_name, + direction=direction, + best=best, + worst=worst, + average=average, + ) + return comparisons + + def _require_scenario_repo(self): + if not self._uow.scenarios: + raise RuntimeError("Scenario repository not initialised") + return self._uow.scenarios + + +def _build_cash_flows(scenario: Scenario) -> tuple[list[CashFlow], ScenarioFinancialTotals]: + cash_flows: list[CashFlow] = [] + by_category: dict[str, float] = {} + inflows = 0.0 + outflows = 0.0 + net = 0.0 + period_index = 0 + + for financial_input in scenario.financial_inputs or []: + sign = _COST_CATEGORY_SIGNS.get(financial_input.category, -1.0) + amount = float(financial_input.amount) * sign + net += amount + if amount >= 0: + inflows += amount + else: + outflows += -amount + by_category.setdefault(financial_input.category.value, 0.0) + by_category[financial_input.category.value] += amount + + if financial_input.effective_date is not None: + cash_flows.append( + CashFlow(amount=amount, date=financial_input.effective_date) + ) + else: + cash_flows.append( + CashFlow(amount=amount, period_index=period_index)) + period_index += 1 + + currency = scenario.currency + if currency is None and scenario.financial_inputs: + currency = scenario.financial_inputs[0].currency + + totals = ScenarioFinancialTotals( + currency=currency, + inflows=inflows, + outflows=outflows, + net=net, + by_category=by_category, + ) + return cash_flows, totals + + +def _calculate_deterministic_metrics( + scenario: Scenario, + cash_flows: Sequence[CashFlow], + totals: ScenarioFinancialTotals, +) -> ScenarioDeterministicMetrics: + notes: list[str] = [] + discount_rate = _normalise_discount_rate(scenario.discount_rate) + if scenario.discount_rate is None: + notes.append( + f"Discount rate not set; defaulted to {discount_rate:.2%}." + ) + + if not cash_flows: + notes.append( + "No financial inputs available for deterministic metrics.") + return ScenarioDeterministicMetrics( + currency=totals.currency, + discount_rate=discount_rate, + compounds_per_year=1, + npv=None, + irr=None, + payback_period=None, + notes=notes, + ) + + npv_value: float | None + try: + npv_value = net_present_value( + discount_rate, + cash_flows, + compounds_per_year=1, + ) + except ValueError as exc: + npv_value = None + notes.append(f"NPV unavailable: {exc}.") + + irr_value: float | None + try: + irr_value = internal_rate_of_return( + cash_flows, + compounds_per_year=1, + ) + except (ValueError, ConvergenceError) as exc: + irr_value = None + notes.append(f"IRR unavailable: {exc}.") + + payback_value: float | None + try: + payback_value = payback_period( + cash_flows, + compounds_per_year=1, + ) + except (ValueError, PaybackNotReachedError) as exc: + payback_value = None + notes.append(f"Payback period unavailable: {exc}.") + + return ScenarioDeterministicMetrics( + currency=totals.currency, + discount_rate=discount_rate, + compounds_per_year=1, + npv=npv_value, + irr=irr_value, + payback_period=payback_value, + notes=notes, + ) + + +def _run_monte_carlo( + scenario: Scenario, + cash_flows: Sequence[CashFlow], + *, + include_samples: bool, + iterations: int, + percentiles: tuple[float, ...], +) -> ScenarioMonteCarloResult: + if not cash_flows: + return ScenarioMonteCarloResult( + available=False, + notes=["No financial inputs available for Monte Carlo simulation."], + ) + + discount_rate = _normalise_discount_rate(scenario.discount_rate) + specs = [CashFlowSpec(cash_flow=flow) for flow in cash_flows] + notes: list[str] = [] + if not scenario.simulation_parameters: + notes.append( + "Scenario has no stochastic parameters; simulation mirrors deterministic cash flows." + ) + config = SimulationConfig( + iterations=iterations, + discount_rate=discount_rate, + metrics=( + SimulationMetric.NPV, + SimulationMetric.IRR, + SimulationMetric.PAYBACK, + ), + percentiles=percentiles, + return_samples=include_samples, + ) + try: + result = run_monte_carlo(specs, config) + except Exception as exc: # pragma: no cover - safeguard for unexpected failures + notes.append(f"Simulation failed: {exc}.") + return ScenarioMonteCarloResult(available=False, notes=notes) + return ScenarioMonteCarloResult( + available=True, + notes=notes, + result=result, + include_samples=include_samples, + ) + + +def _normalise_discount_rate(value: float | None) -> float: + if value is None: + return DEFAULT_DISCOUNT_RATE + rate = float(value) + if rate > 1.0: + return rate / 100.0 + return rate + + +def _sanitize_float(value: float | None) -> float | None: + if value is None: + return None + if math.isnan(value) or math.isinf(value): + return None + return float(value) + + +def _round_optional(value: float | None, *, digits: int = 2) -> float | None: + clean = _sanitize_float(value) + if clean is None: + return None + return round(clean, digits) + + +def _comparison_entry(entry: tuple[int, str, float] | None) -> dict[str, object] | None: + if entry is None: + return None + scenario_id, name, value = entry + return { + "scenario_id": scenario_id, + "name": name, + "value": _round_optional(value), + } + + +def _project_payload(project: Project) -> dict[str, object]: + return { + "id": project.id, + "name": project.name, + "location": project.location, + "operation_type": project.operation_type.value, + "description": project.description, + "created_at": project.created_at, + "updated_at": project.updated_at, + } diff --git a/services/repositories.py b/services/repositories.py index 189dea8..1323a8f 100644 --- a/services/repositories.py +++ b/services/repositories.py @@ -1,6 +1,7 @@ from __future__ import annotations from collections.abc import Iterable +from dataclasses import dataclass from datetime import datetime from typing import Mapping, Sequence @@ -11,6 +12,9 @@ from sqlalchemy.orm import Session, joinedload, selectinload from models import ( FinancialInput, Project, + PricingImpuritySettings, + PricingMetalSettings, + PricingSettings, ResourceType, Role, Scenario, @@ -21,6 +25,7 @@ from models import ( ) from services.exceptions import EntityConflictError, EntityNotFoundError from services.export_query import ProjectExportFilters, ScenarioExportFilters +from services.pricing import PricingMetadata class ProjectRepository: @@ -29,10 +34,17 @@ class ProjectRepository: def __init__(self, session: Session) -> None: self.session = session - def list(self, *, with_children: bool = False) -> Sequence[Project]: + def list( + self, + *, + with_children: bool = False, + with_pricing: bool = False, + ) -> Sequence[Project]: stmt = select(Project).order_by(Project.created_at) if with_children: stmt = stmt.options(selectinload(Project.scenarios)) + if with_pricing: + stmt = stmt.options(selectinload(Project.pricing_settings)) return self.session.execute(stmt).scalars().all() def count(self) -> int: @@ -47,10 +59,18 @@ class ProjectRepository: ) return self.session.execute(stmt).scalars().all() - def get(self, project_id: int, *, with_children: bool = False) -> Project: + def get( + self, + project_id: int, + *, + with_children: bool = False, + with_pricing: bool = False, + ) -> Project: stmt = select(Project).where(Project.id == project_id) if with_children: stmt = stmt.options(joinedload(Project.scenarios)) + if with_pricing: + stmt = stmt.options(joinedload(Project.pricing_settings)) result = self.session.execute(stmt) if with_children: result = result.unique() @@ -86,10 +106,13 @@ class ProjectRepository: filters: ProjectExportFilters | None = None, *, include_scenarios: bool = False, + include_pricing: bool = False, ) -> Sequence[Project]: stmt = select(Project) if include_scenarios: stmt = stmt.options(selectinload(Project.scenarios)) + if include_pricing: + stmt = stmt.options(selectinload(Project.pricing_settings)) if filters: ids = filters.normalised_ids() @@ -131,6 +154,18 @@ class ProjectRepository: project = self.get(project_id) self.session.delete(project) + def set_pricing_settings( + self, + project: Project, + pricing_settings: PricingSettings | None, + ) -> Project: + project.pricing_settings = pricing_settings + project.pricing_settings_id = ( + pricing_settings.id if pricing_settings is not None else None + ) + self.session.flush() + return project + class ScenarioRepository: """Persistence operations for Scenario entities.""" @@ -138,13 +173,26 @@ class ScenarioRepository: def __init__(self, session: Session) -> None: self.session = session - def list_for_project(self, project_id: int) -> Sequence[Scenario]: + def list_for_project( + self, + project_id: int, + *, + with_children: bool = False, + ) -> Sequence[Scenario]: stmt = ( select(Scenario) .where(Scenario.project_id == project_id) .order_by(Scenario.created_at) ) - return self.session.execute(stmt).scalars().all() + if with_children: + stmt = stmt.options( + selectinload(Scenario.financial_inputs), + selectinload(Scenario.simulation_parameters), + ) + result = self.session.execute(stmt) + if with_children: + result = result.unique() + return result.scalars().all() def count(self) -> int: stmt = select(func.count(Scenario.id)) @@ -376,6 +424,101 @@ class SimulationParameterRepository: self.session.delete(entity) +class PricingSettingsRepository: + """Persistence operations for pricing configuration entities.""" + + def __init__(self, session: Session) -> None: + self.session = session + + def list(self, *, include_children: bool = False) -> Sequence[PricingSettings]: + stmt = select(PricingSettings).order_by(PricingSettings.created_at) + if include_children: + stmt = stmt.options( + selectinload(PricingSettings.metal_overrides), + selectinload(PricingSettings.impurity_overrides), + ) + result = self.session.execute(stmt) + if include_children: + result = result.unique() + return result.scalars().all() + + def get(self, settings_id: int, *, include_children: bool = False) -> PricingSettings: + stmt = select(PricingSettings).where(PricingSettings.id == settings_id) + if include_children: + stmt = stmt.options( + selectinload(PricingSettings.metal_overrides), + selectinload(PricingSettings.impurity_overrides), + ) + result = self.session.execute(stmt) + if include_children: + result = result.unique() + settings = result.scalar_one_or_none() + if settings is None: + raise EntityNotFoundError( + f"Pricing settings {settings_id} not found") + return settings + + def find_by_slug( + self, + slug: str, + *, + include_children: bool = False, + ) -> PricingSettings | None: + normalised = slug.strip().lower() + stmt = select(PricingSettings).where( + PricingSettings.slug == normalised) + if include_children: + stmt = stmt.options( + selectinload(PricingSettings.metal_overrides), + selectinload(PricingSettings.impurity_overrides), + ) + result = self.session.execute(stmt) + if include_children: + result = result.unique() + return result.scalar_one_or_none() + + def get_by_slug(self, slug: str, *, include_children: bool = False) -> PricingSettings: + settings = self.find_by_slug(slug, include_children=include_children) + if settings is None: + raise EntityNotFoundError( + f"Pricing settings slug '{slug}' not found" + ) + return settings + + def create(self, settings: PricingSettings) -> PricingSettings: + self.session.add(settings) + try: + self.session.flush() + except IntegrityError as exc: # pragma: no cover - relies on DB constraints + raise EntityConflictError( + "Pricing settings violates constraints") from exc + return settings + + def delete(self, settings_id: int) -> None: + settings = self.get(settings_id, include_children=True) + self.session.delete(settings) + + def attach_metal_override( + self, + settings: PricingSettings, + override: PricingMetalSettings, + ) -> PricingMetalSettings: + settings.metal_overrides.append(override) + self.session.add(override) + self.session.flush() + return override + + def attach_impurity_override( + self, + settings: PricingSettings, + override: PricingImpuritySettings, + ) -> PricingImpuritySettings: + settings.impurity_overrides.append(override) + self.session.add(override) + self.session.flush() + return override + + class RoleRepository: """Persistence operations for Role entities.""" @@ -507,6 +650,159 @@ class UserRepository: self.session.flush() +DEFAULT_PRICING_SETTINGS_NAME = "Default Pricing Settings" +DEFAULT_PRICING_SETTINGS_DESCRIPTION = ( + "Default pricing configuration generated from environment metadata." +) + + +@dataclass(slots=True) +class PricingSettingsSeedResult: + settings: PricingSettings + created: bool + updated_fields: int + impurity_upserts: int + + +def ensure_default_pricing_settings( + repo: PricingSettingsRepository, + *, + metadata: PricingMetadata, + slug: str = "default", + name: str | None = None, + description: str | None = None, +) -> PricingSettingsSeedResult: + """Ensure a baseline pricing settings record exists and matches metadata defaults.""" + + normalised_slug = (slug or "default").strip().lower() or "default" + target_name = name or DEFAULT_PRICING_SETTINGS_NAME + target_description = description or DEFAULT_PRICING_SETTINGS_DESCRIPTION + + updated_fields = 0 + impurity_upserts = 0 + + try: + settings = repo.get_by_slug(normalised_slug, include_children=True) + created = False + except EntityNotFoundError: + settings = PricingSettings( + name=target_name, + slug=normalised_slug, + description=target_description, + default_currency=metadata.default_currency, + default_payable_pct=metadata.default_payable_pct, + moisture_threshold_pct=metadata.moisture_threshold_pct, + moisture_penalty_per_pct=metadata.moisture_penalty_per_pct, + ) + settings.metadata_payload = None + settings = repo.create(settings) + created = True + else: + if settings.name != target_name: + settings.name = target_name + updated_fields += 1 + if target_description and settings.description != target_description: + settings.description = target_description + updated_fields += 1 + if settings.default_currency != metadata.default_currency: + settings.default_currency = metadata.default_currency + updated_fields += 1 + if float(settings.default_payable_pct) != float(metadata.default_payable_pct): + settings.default_payable_pct = metadata.default_payable_pct + updated_fields += 1 + if float(settings.moisture_threshold_pct) != float(metadata.moisture_threshold_pct): + settings.moisture_threshold_pct = metadata.moisture_threshold_pct + updated_fields += 1 + if float(settings.moisture_penalty_per_pct) != float(metadata.moisture_penalty_per_pct): + settings.moisture_penalty_per_pct = metadata.moisture_penalty_per_pct + updated_fields += 1 + + impurity_thresholds = { + code.strip().upper(): float(value) + for code, value in (metadata.impurity_thresholds or {}).items() + if code.strip() + } + impurity_penalties = { + code.strip().upper(): float(value) + for code, value in (metadata.impurity_penalty_per_ppm or {}).items() + if code.strip() + } + + if impurity_thresholds or impurity_penalties: + existing_map = { + override.impurity_code: override + for override in settings.impurity_overrides + } + target_codes = set(impurity_thresholds) | set(impurity_penalties) + for code in sorted(target_codes): + threshold_value = impurity_thresholds.get(code, 0.0) + penalty_value = impurity_penalties.get(code, 0.0) + existing = existing_map.get(code) + if existing is None: + repo.attach_impurity_override( + settings, + PricingImpuritySettings( + impurity_code=code, + threshold_ppm=threshold_value, + penalty_per_ppm=penalty_value, + ), + ) + impurity_upserts += 1 + continue + changed = False + if float(existing.threshold_ppm) != float(threshold_value): + existing.threshold_ppm = threshold_value + changed = True + if float(existing.penalty_per_ppm) != float(penalty_value): + existing.penalty_per_ppm = penalty_value + changed = True + if changed: + updated_fields += 1 + + if updated_fields > 0 or impurity_upserts > 0: + repo.session.flush() + + return PricingSettingsSeedResult( + settings=settings, + created=created, + updated_fields=updated_fields, + impurity_upserts=impurity_upserts, + ) + + +def pricing_settings_to_metadata(settings: PricingSettings) -> PricingMetadata: + """Convert a persisted pricing settings record into metadata defaults.""" + + payload = settings.metadata_payload or {} + payload_thresholds = payload.get("impurity_thresholds") or {} + payload_penalties = payload.get("impurity_penalty_per_ppm") or {} + + thresholds: dict[str, float] = { + code.strip().upper(): float(value) + for code, value in payload_thresholds.items() + if isinstance(code, str) and code.strip() + } + penalties: dict[str, float] = { + code.strip().upper(): float(value) + for code, value in payload_penalties.items() + if isinstance(code, str) and code.strip() + } + + for override in settings.impurity_overrides: + code = override.impurity_code.strip().upper() + thresholds[code] = float(override.threshold_ppm) + penalties[code] = float(override.penalty_per_ppm) + + return PricingMetadata( + default_payable_pct=float(settings.default_payable_pct), + default_currency=settings.default_currency, + moisture_threshold_pct=float(settings.moisture_threshold_pct), + moisture_penalty_per_pct=float(settings.moisture_penalty_per_pct), + impurity_thresholds=thresholds, + impurity_penalty_per_ppm=penalties, + ) + + DEFAULT_ROLE_DEFINITIONS: tuple[dict[str, str], ...] = ( { "name": "admin", diff --git a/services/scenario_evaluation.py b/services/scenario_evaluation.py new file mode 100644 index 0000000..c3aab15 --- /dev/null +++ b/services/scenario_evaluation.py @@ -0,0 +1,54 @@ +from __future__ import annotations + +"""Scenario evaluation services including pricing integration.""" + +from dataclasses import dataclass +from typing import Iterable, Mapping + +from models.scenario import Scenario +from services.pricing import ( + PricingInput, + PricingMetadata, + PricingResult, + calculate_pricing, +) + + +@dataclass(slots=True) +class ScenarioPricingConfig: + """Configuration for pricing evaluation within a scenario.""" + + metadata: PricingMetadata | None = None + + +@dataclass(slots=True) +class ScenarioPricingSnapshot: + """Captured pricing results for a scenario.""" + + scenario_id: int + results: list[PricingResult] + + +class ScenarioPricingEvaluator: + """Evaluate scenario profitability inputs using pricing services.""" + + def __init__(self, config: ScenarioPricingConfig | None = None) -> None: + self._config = config or ScenarioPricingConfig() + + def evaluate( + self, + scenario: Scenario, + *, + inputs: Iterable[PricingInput], + metadata_override: PricingMetadata | None = None, + ) -> ScenarioPricingSnapshot: + metadata = metadata_override or self._config.metadata + results: list[PricingResult] = [] + for pricing_input in inputs: + result = calculate_pricing( + pricing_input, + metadata=metadata, + currency=scenario.currency, + ) + results.append(result) + return ScenarioPricingSnapshot(scenario_id=scenario.id, results=results) diff --git a/services/simulation.py b/services/simulation.py new file mode 100644 index 0000000..02ae588 --- /dev/null +++ b/services/simulation.py @@ -0,0 +1,352 @@ +from __future__ import annotations + +from dataclasses import dataclass +from enum import Enum +from typing import Any, Dict, Iterable, Mapping, Sequence + +import numpy as np +from numpy.random import Generator, default_rng + +from .financial import ( + CashFlow, + ConvergenceError, + PaybackNotReachedError, + internal_rate_of_return, + net_present_value, + payback_period, +) + + +class DistributionConfigError(ValueError): + """Raised when a distribution specification is invalid.""" + + +class SimulationMetric(Enum): + """Supported Monte Carlo summary metrics.""" + + NPV = "npv" + IRR = "irr" + PAYBACK = "payback" + + +class DistributionType(Enum): + """Supported probability distribution families.""" + + NORMAL = "normal" + LOGNORMAL = "lognormal" + TRIANGULAR = "triangular" + DISCRETE = "discrete" + + +class DistributionSource(Enum): + """Origins for parameter values when sourcing dynamically.""" + + STATIC = "static" + SCENARIO_FIELD = "scenario_field" + METADATA_KEY = "metadata_key" + + +@dataclass(frozen=True, slots=True) +class DistributionSpec: + """Defines the stochastic behaviour for a single cash flow.""" + + type: DistributionType + parameters: Mapping[str, Any] + source: DistributionSource = DistributionSource.STATIC + source_key: str | None = None + + +@dataclass(frozen=True, slots=True) +class CashFlowSpec: + """Pairs a baseline cash flow with an optional distribution.""" + + cash_flow: CashFlow + distribution: DistributionSpec | None = None + + +@dataclass(frozen=True, slots=True) +class SimulationConfig: + """Controls Monte Carlo simulation behaviour.""" + + iterations: int + discount_rate: float + seed: int | None = None + metrics: Sequence[SimulationMetric] = ( + SimulationMetric.NPV, SimulationMetric.IRR, SimulationMetric.PAYBACK) + percentiles: Sequence[float] = (5.0, 50.0, 95.0) + compounds_per_year: int = 1 + return_samples: bool = False + residual_value: float | None = None + residual_periods: float | None = None + + +@dataclass(frozen=True, slots=True) +class MetricSummary: + """Aggregated statistics for a simulated metric.""" + + mean: float + std_dev: float + minimum: float + maximum: float + percentiles: Mapping[float, float] + sample_size: int + failed_runs: int + + +@dataclass(frozen=True, slots=True) +class SimulationResult: + """Monte Carlo output including per-metric summaries.""" + + iterations: int + summaries: Mapping[SimulationMetric, MetricSummary] + samples: Mapping[SimulationMetric, np.ndarray] | None = None + + +def run_monte_carlo( + cash_flows: Sequence[CashFlowSpec], + config: SimulationConfig, + *, + scenario_context: Mapping[str, Any] | None = None, + metadata: Mapping[str, Any] | None = None, + rng: Generator | None = None, +) -> SimulationResult: + """Execute Monte Carlo simulation for the provided cash flows.""" + + if config.iterations <= 0: + raise ValueError("iterations must be greater than zero") + if config.compounds_per_year <= 0: + raise ValueError("compounds_per_year must be greater than zero") + for pct in config.percentiles: + if pct < 0.0 or pct > 100.0: + raise ValueError("percentiles must be within [0, 100]") + + generator = rng or default_rng(config.seed) + + metric_arrays: Dict[SimulationMetric, np.ndarray] = { + metric: np.empty(config.iterations, dtype=float) + for metric in config.metrics + } + + for idx in range(config.iterations): + iteration_flows = [ + _realise_cash_flow( + spec, + generator, + scenario_context=scenario_context, + metadata=metadata, + ) + for spec in cash_flows + ] + + if SimulationMetric.NPV in metric_arrays: + metric_arrays[SimulationMetric.NPV][idx] = net_present_value( + config.discount_rate, + iteration_flows, + residual_value=config.residual_value, + residual_periods=config.residual_periods, + compounds_per_year=config.compounds_per_year, + ) + if SimulationMetric.IRR in metric_arrays: + try: + metric_arrays[SimulationMetric.IRR][idx] = internal_rate_of_return( + iteration_flows, + compounds_per_year=config.compounds_per_year, + ) + except (ValueError, ConvergenceError): + metric_arrays[SimulationMetric.IRR][idx] = np.nan + if SimulationMetric.PAYBACK in metric_arrays: + try: + metric_arrays[SimulationMetric.PAYBACK][idx] = payback_period( + iteration_flows, + compounds_per_year=config.compounds_per_year, + ) + except (ValueError, PaybackNotReachedError): + metric_arrays[SimulationMetric.PAYBACK][idx] = np.nan + + summaries = { + metric: _summarise(metric_arrays[metric], config.percentiles) + for metric in metric_arrays + } + + samples = metric_arrays if config.return_samples else None + return SimulationResult( + iterations=config.iterations, + summaries=summaries, + samples=samples, + ) + + +def _realise_cash_flow( + spec: CashFlowSpec, + generator: Generator, + *, + scenario_context: Mapping[str, Any] | None, + metadata: Mapping[str, Any] | None, +) -> CashFlow: + if spec.distribution is None: + return spec.cash_flow + + distribution = spec.distribution + base_amount = spec.cash_flow.amount + params = _resolve_parameters( + distribution, + base_amount, + scenario_context=scenario_context, + metadata=metadata, + ) + sample = _sample_distribution( + distribution.type, + params, + generator, + ) + return CashFlow( + amount=float(sample), + period_index=spec.cash_flow.period_index, + date=spec.cash_flow.date, + ) + + +def _resolve_parameters( + distribution: DistributionSpec, + base_amount: float, + *, + scenario_context: Mapping[str, Any] | None, + metadata: Mapping[str, Any] | None, +) -> Dict[str, Any]: + params = dict(distribution.parameters) + + if distribution.source == DistributionSource.SCENARIO_FIELD: + if distribution.source_key is None: + raise DistributionConfigError( + "source_key is required for scenario_field sourcing") + if not scenario_context or distribution.source_key not in scenario_context: + raise DistributionConfigError( + f"scenario field '{distribution.source_key}' not found for distribution" + ) + params.setdefault("mean", float( + scenario_context[distribution.source_key])) + elif distribution.source == DistributionSource.METADATA_KEY: + if distribution.source_key is None: + raise DistributionConfigError( + "source_key is required for metadata_key sourcing") + if not metadata or distribution.source_key not in metadata: + raise DistributionConfigError( + f"metadata key '{distribution.source_key}' not found for distribution" + ) + params.setdefault("mean", float(metadata[distribution.source_key])) + else: + params.setdefault("mean", float(base_amount)) + + return params + + +def _sample_distribution( + distribution_type: DistributionType, + params: Mapping[str, Any], + generator: Generator, +) -> float: + if distribution_type is DistributionType.NORMAL: + return _sample_normal(params, generator) + if distribution_type is DistributionType.LOGNORMAL: + return _sample_lognormal(params, generator) + if distribution_type is DistributionType.TRIANGULAR: + return _sample_triangular(params, generator) + if distribution_type is DistributionType.DISCRETE: + return _sample_discrete(params, generator) + raise DistributionConfigError( + f"Unsupported distribution type: {distribution_type}") + + +def _sample_normal(params: Mapping[str, Any], generator: Generator) -> float: + if "std_dev" not in params: + raise DistributionConfigError("normal distribution requires 'std_dev'") + std_dev = float(params["std_dev"]) + if std_dev < 0: + raise DistributionConfigError("std_dev must be non-negative") + mean = float(params.get("mean", 0.0)) + if std_dev == 0: + return mean + return float(generator.normal(loc=mean, scale=std_dev)) + + +def _sample_lognormal(params: Mapping[str, Any], generator: Generator) -> float: + if "sigma" not in params: + raise DistributionConfigError( + "lognormal distribution requires 'sigma'") + sigma = float(params["sigma"]) + if sigma < 0: + raise DistributionConfigError("sigma must be non-negative") + if "mean" not in params: + raise DistributionConfigError( + "lognormal distribution requires 'mean' (mu in log space)") + mean = float(params["mean"]) + return float(generator.lognormal(mean=mean, sigma=sigma)) + + +def _sample_triangular(params: Mapping[str, Any], generator: Generator) -> float: + required = {"min", "mode", "max"} + if not required.issubset(params): + missing = ", ".join(sorted(required - params.keys())) + raise DistributionConfigError( + f"triangular distribution missing parameters: {missing}") + left = float(params["min"]) + mode = float(params["mode"]) + right = float(params["max"]) + if not (left <= mode <= right): + raise DistributionConfigError( + "triangular distribution requires min <= mode <= max") + if left == right: + return mode + return float(generator.triangular(left=left, mode=mode, right=right)) + + +def _sample_discrete(params: Mapping[str, Any], generator: Generator) -> float: + values = params.get("values") + probabilities = params.get("probabilities") + if not isinstance(values, Sequence) or not isinstance(probabilities, Sequence): + raise DistributionConfigError( + "discrete distribution requires 'values' and 'probabilities' sequences") + if len(values) != len(probabilities) or not values: + raise DistributionConfigError( + "values and probabilities must be non-empty and of equal length") + probs = np.array(probabilities, dtype=float) + if np.any(probs < 0): + raise DistributionConfigError("probabilities must be non-negative") + total = probs.sum() + if not np.isclose(total, 1.0): + raise DistributionConfigError("probabilities must sum to 1.0") + probs = probs / total + choices = np.array(values, dtype=float) + return float(generator.choice(choices, p=probs)) + + +def _summarise(values: np.ndarray, percentiles: Sequence[float]) -> MetricSummary: + clean = values[~np.isnan(values)] + sample_size = clean.size + failed_runs = values.size - sample_size + + if sample_size == 0: + percentile_map: Dict[float, float] = { + pct: float("nan") for pct in percentiles} + return MetricSummary( + mean=float("nan"), + std_dev=float("nan"), + minimum=float("nan"), + maximum=float("nan"), + percentiles=percentile_map, + sample_size=0, + failed_runs=failed_runs, + ) + + percentile_map = { + pct: float(np.percentile(clean, pct)) for pct in percentiles + } + return MetricSummary( + mean=float(np.mean(clean)), + std_dev=float(np.std(clean, ddof=1)) if sample_size > 1 else 0.0, + minimum=float(np.min(clean)), + maximum=float(np.max(clean)), + percentiles=percentile_map, + sample_size=sample_size, + failed_runs=failed_runs, + ) diff --git a/services/unit_of_work.py b/services/unit_of_work.py index a51849e..d8579c7 100644 --- a/services/unit_of_work.py +++ b/services/unit_of_work.py @@ -6,16 +6,21 @@ from typing import Callable, Sequence from sqlalchemy.orm import Session from config.database import SessionLocal -from models import Role, Scenario +from models import PricingSettings, Project, Role, Scenario +from services.pricing import PricingMetadata from services.repositories import ( FinancialInputRepository, + PricingSettingsRepository, + PricingSettingsSeedResult, ProjectRepository, RoleRepository, ScenarioRepository, SimulationParameterRepository, UserRepository, ensure_admin_user as ensure_admin_user_record, + ensure_default_pricing_settings, ensure_default_roles, + pricing_settings_to_metadata, ) from services.scenario_validation import ScenarioComparisonValidator @@ -33,6 +38,7 @@ class UnitOfWork(AbstractContextManager["UnitOfWork"]): self.simulation_parameters: SimulationParameterRepository | None = None self.users: UserRepository | None = None self.roles: RoleRepository | None = None + self.pricing_settings: PricingSettingsRepository | None = None def __enter__(self) -> "UnitOfWork": self.session = self._session_factory() @@ -43,6 +49,7 @@ class UnitOfWork(AbstractContextManager["UnitOfWork"]): self.session) self.users = UserRepository(self.session) self.roles = RoleRepository(self.session) + self.pricing_settings = PricingSettingsRepository(self.session) self._scenario_validator = ScenarioComparisonValidator() return self @@ -60,6 +67,7 @@ class UnitOfWork(AbstractContextManager["UnitOfWork"]): self.simulation_parameters = None self.users = None self.roles = None + self.pricing_settings = None def flush(self) -> None: if not self.session: @@ -116,3 +124,45 @@ class UnitOfWork(AbstractContextManager["UnitOfWork"]): username=username, password=password, ) + + def ensure_default_pricing_settings( + self, + *, + metadata: PricingMetadata, + slug: str = "default", + name: str | None = None, + description: str | None = None, + ) -> PricingSettingsSeedResult: + if not self.pricing_settings: + raise RuntimeError("UnitOfWork session is not initialised") + return ensure_default_pricing_settings( + self.pricing_settings, + metadata=metadata, + slug=slug, + name=name, + description=description, + ) + + def get_pricing_metadata( + self, + *, + slug: str = "default", + ) -> PricingMetadata | None: + if not self.pricing_settings: + raise RuntimeError("UnitOfWork session is not initialised") + settings = self.pricing_settings.find_by_slug( + slug, + include_children=True, + ) + if settings is None: + return None + return pricing_settings_to_metadata(settings) + + def set_project_pricing_settings( + self, + project: Project, + pricing_settings: PricingSettings | None, + ) -> Project: + if not self.projects: + raise RuntimeError("UnitOfWork session is not initialised") + return self.projects.set_pricing_settings(project, pricing_settings) diff --git a/static/css/main.css b/static/css/main.css index 7848704..7ac28d2 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -1,3 +1,204 @@ +.report-overview { + margin-bottom: 2.5rem; +} + +.report-grid { + display: grid; + gap: 1.5rem; + grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); +} + +.report-card { + background: var(--card); + border-radius: var(--radius); + padding: 1.5rem; + border: 1px solid var(--color-border); + box-shadow: 0 12px 30px rgba(4, 7, 14, 0.35); +} + +.report-card h2 { + margin-top: 0; + margin-bottom: 1rem; +} + +.report-section + .report-section { + margin-top: 3rem; +} + +.section-header { + margin-bottom: 1.25rem; +} + +.section-header h2 { + margin: 0; +} + +.section-subtitle { + margin: 0.35rem 0 0; + color: var(--muted); +} + +.metric-list { + list-style: none; + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + gap: 0.75rem; +} + +.metric-list.compact { + gap: 0.35rem; +} + +.metric-list li { + display: flex; + justify-content: space-between; + align-items: baseline; + font-size: 0.95rem; + color: var(--muted); +} + +.metric-list strong { + font-size: 1.05rem; + color: var(--text); +} + +.metrics-table { + width: 100%; + border-collapse: collapse; + background: rgba(21, 27, 35, 0.6); + border-radius: var(--radius-sm); + overflow: hidden; +} + +.metrics-table th, +.metrics-table td { + padding: 0.65rem 0.9rem; + text-align: left; + border-bottom: 1px solid rgba(255, 255, 255, 0.08); +} + +.metrics-table th { + font-weight: 600; + color: var(--text); +} + +.metrics-table tr:last-child td, +.metrics-table tr:last-child th { + border-bottom: none; +} + +.definition-list { + margin: 0; + display: grid; + gap: 0.75rem; +} + +.definition-list div { + display: grid; + grid-template-columns: 140px 1fr; + gap: 0.5rem; + align-items: baseline; +} + +.definition-list dt { + color: var(--muted); + font-weight: 600; +} + +.definition-list dd { + margin: 0; +} + +.scenario-card { + background: var(--card); + border-radius: var(--radius); + padding: 1.5rem; + border: 1px solid var(--color-border); + box-shadow: 0 16px 32px rgba(4, 7, 14, 0.42); + display: flex; + flex-direction: column; + gap: 1.25rem; +} + +.scenario-card + .scenario-card { + margin-top: 1.75rem; +} + +.scenario-card-header { + display: flex; + justify-content: space-between; + gap: 1rem; + align-items: flex-start; +} + +.scenario-card h3 { + margin: 0; +} + +.scenario-meta { + text-align: right; +} + +.meta-label { + display: block; + color: var(--muted); + font-size: 0.8rem; + text-transform: uppercase; + letter-spacing: 0.08em; +} + +.meta-value { + font-weight: 600; +} + +.scenario-grid { + display: grid; + gap: 1.25rem; + grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); +} + +.scenario-panel { + background: rgba(15, 20, 27, 0.8); + border-radius: var(--radius-sm); + padding: 1.1rem; + border: 1px solid rgba(255, 255, 255, 0.05); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06); +} + +.scenario-panel h4, +.scenario-panel h5 { + margin-top: 0; + margin-bottom: 0.75rem; +} + +.note-list { + padding-left: 1.1rem; + color: var(--muted); + font-size: 0.9rem; +} + +.muted { + color: var(--muted); +} + +.page-actions .button { + text-decoration: none; + background: transparent; + border: 1px solid var(--color-border); + padding: 0.6rem 1rem; + border-radius: var(--radius-sm); + color: var(--text); + font-weight: 600; + transition: background 0.2s ease, border-color 0.2s ease; +} + +.page-actions .button:hover, +.page-actions .button:focus { + background: rgba(241, 178, 26, 0.14); + border-color: var(--brand); +} :root { --bg: #0b0f14; --bg-2: #0f141b; diff --git a/templates/partials/reports/filters_card.html b/templates/partials/reports/filters_card.html new file mode 100644 index 0000000..238393d --- /dev/null +++ b/templates/partials/reports/filters_card.html @@ -0,0 +1,33 @@ +{% if filters %} +
+
+

Active Filters

+
+ {% if filters.scenario_ids %} +
+
Scenario IDs
+
{{ filters.scenario_ids | join(', ') }}
+
+ {% endif %} + {% if filters.start_date %} +
+
Start Date
+
{{ filters.start_date }}
+
+ {% endif %} + {% if filters.end_date %} +
+
End Date
+
{{ filters.end_date }}
+
+ {% endif %} + {% if not (filters.scenario_ids or filters.start_date or filters.end_date) %} +
+
Status
+
No filters applied
+
+ {% endif %} +
+
+
+{% endif %} diff --git a/templates/partials/reports/monte_carlo_table.html b/templates/partials/reports/monte_carlo_table.html new file mode 100644 index 0000000..7388104 --- /dev/null +++ b/templates/partials/reports/monte_carlo_table.html @@ -0,0 +1,46 @@ +{% set sorted_metrics = metrics | dictsort %} +{% set ns = namespace(percentile_keys=[]) %} +{% if percentiles %} + {% set ns.percentile_keys = percentiles %} +{% elif sorted_metrics %} + {% set reference_percentiles = sorted_metrics[0][1].percentiles.keys() | list %} + {% set ns.percentile_keys = reference_percentiles %} +{% endif %} + +{% if sorted_metrics %} + + + + + + {% for percentile in ns.percentile_keys %} + {% set percentile_label = '%g' % percentile %} + + {% endfor %} + + + + + {% for metric_name, summary in sorted_metrics %} + + + + {% for percentile in ns.percentile_keys %} + {% set percentile_key = '%g' % percentile %} + {% set percentile_value = summary.percentiles.get(percentile_key) %} + + {% endfor %} + + + {% endfor %} + +
MetricMeanP{{ percentile_label }}Std Dev
{{ metric_name | replace('_', ' ') | title }}{{ summary.mean | format_metric(metric_name, currency) }} + {% if percentile_value is not none %} + {{ percentile_value | format_metric(metric_name, currency) }} + {% else %} + — + {% endif %} + {{ summary.std_dev | format_metric(metric_name, currency) }}
+{% else %} +

Monte Carlo metrics are unavailable.

+{% endif %} diff --git a/templates/partials/reports/options_card.html b/templates/partials/reports/options_card.html new file mode 100644 index 0000000..dc192f5 --- /dev/null +++ b/templates/partials/reports/options_card.html @@ -0,0 +1,56 @@ +{% if options %} + {% set distribution_enabled = options.distribution %} + {% set samples_enabled = options.samples and options.distribution %} +{% endif %} + +
+
+

Data Options

+
    +
  • + Monte Carlo Distribution + + {% if options %} + {{ distribution_enabled and "Enabled" or "Disabled" }} + {% else %} + Not requested + {% endif %} + +
  • +
  • + Sample Storage + + {% if options %} + {% if options.samples %} + {% if samples_enabled %} + Enabled + {% else %} + Requires distribution + {% endif %} + {% else %} + Disabled + {% endif %} + {% else %} + Not requested + {% endif %} + +
  • +
  • + Iterations + {{ iterations }} +
  • +
  • + Percentiles + + {% if percentiles %} + {% for percentile in percentiles %} + {{ '%g' % percentile }}{% if not loop.last %}, {% endif %} + {% endfor %} + {% else %} + Defaults + {% endif %} + +
  • +
+
+
diff --git a/templates/partials/reports/scenario_actions.html b/templates/partials/reports/scenario_actions.html new file mode 100644 index 0000000..823ea6d --- /dev/null +++ b/templates/partials/reports/scenario_actions.html @@ -0,0 +1,14 @@ + diff --git a/templates/partials/reports_header.html b/templates/partials/reports_header.html new file mode 100644 index 0000000..c12e731 --- /dev/null +++ b/templates/partials/reports_header.html @@ -0,0 +1,24 @@ + diff --git a/templates/reports/project_summary.html b/templates/reports/project_summary.html new file mode 100644 index 0000000..f036240 --- /dev/null +++ b/templates/reports/project_summary.html @@ -0,0 +1,205 @@ +{% extends "base.html" %} +{% block title %}Project Summary | CalMiner{% endblock %} + +{% block content %} + {% include "partials/reports_header.html" with context %} + + {% include "partials/reports/options_card.html" with options=include_options iterations=iterations percentiles=percentiles %} + {% include "partials/reports/filters_card.html" with filters=filters %} + +
+
+
+

Project Details

+
+
+
Name
+
{{ project.name }}
+
+
+
Location
+
{{ project.location or "—" }}
+
+
+
Operation Type
+
{{ project.operation_type | replace("_", " ") | title }}
+
+
+
Scenarios
+
{{ scenario_count }}
+
+
+
Created
+
{{ project.created_at | format_datetime }}
+
+
+
Updated
+
{{ project.updated_at | format_datetime }}
+
+
+
+ +
+

Financial Summary

+
    +
  • + Total Inflows + {{ aggregates.financials.total_inflows | currency_display(project.currency) }} +
  • +
  • + Total Outflows + {{ aggregates.financials.total_outflows | currency_display(project.currency) }} +
  • +
  • + Net Cash Flow + {{ aggregates.financials.total_net | currency_display(project.currency) }} +
  • +
+
+ +
+

Deterministic Metrics

+ {% if aggregates.deterministic_metrics %} + + + + + + + + + + + {% for key, metric in aggregates.deterministic_metrics.items() %} + + + + + + + {% endfor %} + +
MetricAverageBestWorst
{{ key | replace("_", " ") | title }}{{ metric.average | format_metric(key, project.currency) }}{{ metric.maximum | format_metric(key, project.currency) }}{{ metric.minimum | format_metric(key, project.currency) }}
+ {% else %} +

Deterministic metrics are unavailable for the current filters.

+ {% endif %} +
+
+
+ +
+
+

Scenario Breakdown

+

Deterministic metrics and Monte Carlo summaries for each scenario.

+
+ + {% if scenarios %} + {% for item in scenarios %} +
+
+
+

{{ item.scenario.name }}

+

{{ item.scenario.status | title }} · {{ item.scenario.primary_resource or "No primary resource" }}

+
+
+ Currency + {{ item.scenario.currency or project.currency or "—" }} +
+ {% include "partials/reports/scenario_actions.html" with scenario=item.scenario %} +
+ +
+
+

Financial Totals

+
    +
  • + Inflows + {{ item.financials.inflows | currency_display(item.scenario.currency or project.currency) }} +
  • +
  • + Outflows + {{ item.financials.outflows | currency_display(item.scenario.currency or project.currency) }} +
  • +
  • + Net + {{ item.financials.net | currency_display(item.scenario.currency or project.currency) }} +
  • +
+
By Category
+ {% if item.financials.by_category %} +
    + {% for label, value in item.financials.by_category.items() %} +
  • + {{ label | replace("_", " ") | title }} + {{ value | currency_display(item.scenario.currency or project.currency) }} +
  • + {% endfor %} +
+ {% else %} +

No financial inputs recorded.

+ {% endif %} +
+ +
+

Deterministic Metrics

+ + + + + + + + + + + + + + + + + + + +
Discount Rate{{ item.metrics.discount_rate | percentage_display }}
NPV{{ item.metrics.npv | currency_display(item.scenario.currency or project.currency) }}
IRR{{ item.metrics.irr | percentage_display }}
Payback Period{{ item.metrics.payback_period | period_display }}
+ {% if item.metrics.notes %} +
    + {% for note in item.metrics.notes %} +
  • {{ note }}
  • + {% endfor %} +
+ {% endif %} +
+ +
+

Monte Carlo Summary

+ {% if item.monte_carlo and item.monte_carlo.available %} +

+ Iterations: {{ item.monte_carlo.iterations }} + {% if percentiles %} + · Percentiles: + {% for percentile in percentiles %} + {{ '%g' % percentile }}{% if not loop.last %}, {% endif %} + {% endfor %} + {% endif %} +

+ {% include "partials/reports/monte_carlo_table.html" with metrics=item.monte_carlo.metrics currency=item.scenario.currency or project.currency percentiles=percentiles %} + {% else %} +

Monte Carlo metrics are unavailable for this scenario.

+ {% if item.monte_carlo and item.monte_carlo.notes %} +
    + {% for note in item.monte_carlo.notes %} +
  • {{ note }}
  • + {% endfor %} +
+ {% endif %} + {% endif %} +
+
+
+ {% endfor %} + {% else %} +

No scenarios match the current filters.

+ {% endif %} +
+{% endblock %} diff --git a/templates/reports/scenario_comparison.html b/templates/reports/scenario_comparison.html new file mode 100644 index 0000000..86b0b79 --- /dev/null +++ b/templates/reports/scenario_comparison.html @@ -0,0 +1,166 @@ +{% extends "base.html" %} +{% block title %}Scenario Comparison | CalMiner{% endblock %} + +{% block content %} + {% include "partials/reports_header.html" with context %} + + {% include "partials/reports/options_card.html" with options=include_options iterations=iterations percentiles=percentiles %} +
+
+

Compared Scenarios

+
    + {% for item in scenarios %} +
  • + {{ item.scenario.name }} + #{{ item.scenario.id }} +
  • + {% endfor %} +
+
+
+ +
+
+

Project Details

+
+
+
Name
+
{{ project.name }}
+
+
+
Location
+
{{ project.location or "—" }}
+
+
+
Operation Type
+
{{ project.operation_type | replace("_", " ") | title }}
+
+
+
Scenarios Compared
+
{{ scenarios | length }}
+
+
+
+ +
+

Comparison Summary

+ {% if comparison %} + + + + + + + + + + + + {% for key, metric in comparison.items() %} + + + + + + + + {% endfor %} + +
MetricDirectionBest PerformerWorst PerformerAverage
{{ key | replace("_", " ") | title }}{{ metric.direction | replace("_", " ") | title }} + {% if metric.best %} + {{ metric.best.name }} + ({{ metric.best.value | format_metric(key, project.currency) }}) + {% else %} + — + {% endif %} + + {% if metric.worst %} + {{ metric.worst.name }} + ({{ metric.worst.value | format_metric(key, project.currency) }}) + {% else %} + — + {% endif %} + {{ metric.average | format_metric(key, project.currency) }}
+ {% else %} +

No deterministic metrics available for comparison.

+ {% endif %} +
+
+ +
+
+

Scenario Details

+

Each scenario includes deterministic metrics and Monte Carlo summaries.

+
+ + {% for item in scenarios %} +
+
+
+

{{ item.scenario.name }}

+

{{ item.scenario.status | title }} · Currency: {{ item.scenario.currency or project.currency }}

+
+
+ Primary Resource + {{ item.scenario.primary_resource or "—" }} +
+ {% include "partials/reports/scenario_actions.html" with scenario=item.scenario %} +
+ +
+
+

Deterministic Metrics

+ + + + + + + + + + + + + + + +
NPV{{ item.metrics.npv | currency_display(item.scenario.currency or project.currency) }}
IRR{{ item.metrics.irr | percentage_display }}
Payback Period{{ item.metrics.payback_period | period_display }}
+ {% if item.metrics.notes %} +
    + {% for note in item.metrics.notes %} +
  • {{ note }}
  • + {% endfor %} +
+ {% endif %} +
+ +
+

Monte Carlo Summary

+ {% if item.monte_carlo and item.monte_carlo.available %} +

+ Iterations: {{ item.monte_carlo.iterations }} + {% if percentiles %} + · Percentiles: + {% for percentile in percentiles %} + {{ '%g' % percentile }}{% if not loop.last %}, {% endif %} + {% endfor %} + {% endif %} +

+ {% include "partials/reports/monte_carlo_table.html" with metrics=item.monte_carlo.metrics currency=item.scenario.currency or project.currency percentiles=percentiles %} + {% else %} +

No Monte Carlo data available for this scenario.

+ {% if item.monte_carlo and item.monte_carlo.notes %} +
    + {% for note in item.monte_carlo.notes %} +
  • {{ note }}
  • + {% endfor %} +
+ {% endif %} + {% endif %} +
+
+
+ {% endfor %} +
+{% endblock %} diff --git a/templates/reports/scenario_distribution.html b/templates/reports/scenario_distribution.html new file mode 100644 index 0000000..f47e94a --- /dev/null +++ b/templates/reports/scenario_distribution.html @@ -0,0 +1,149 @@ +{% extends "base.html" %} +{% block title %}Scenario Distribution | CalMiner{% endblock %} + +{% block content %} + {% include "partials/reports_header.html" with context %} + +
+
+
+

Scenario Details

+
+
+
Name
+
{{ scenario.name }}
+
+
+
Project ID
+
{{ scenario.project_id }}
+
+
+
Status
+
{{ scenario.status | title }}
+
+
+
Currency
+
{{ scenario.currency or "—" }}
+
+
+
Discount Rate
+
{{ metrics.discount_rate | percentage_display }}
+
+
+
Updated
+
{{ scenario.updated_at | format_datetime }}
+
+
+
+ +
+

Financial Totals

+
    +
  • + Inflows + {{ summary.inflows | currency_display(scenario.currency) }} +
  • +
  • + Outflows + {{ summary.outflows | currency_display(scenario.currency) }} +
  • +
  • + Net Cash Flow + {{ summary.net | currency_display(scenario.currency) }} +
  • +
+ {% if summary.by_category %} +

By Category

+
    + {% for label, value in summary.by_category.items() %} +
  • + {{ label | replace("_", " ") | title }} + {{ value | currency_display(scenario.currency) }} +
  • + {% endfor %} +
+ {% endif %} +
+
+
+ +
+
+

Deterministic Metrics

+

Key financial indicators calculated from deterministic cash flows.

+
+ + + + + + + + + + + + + + + +
NPV{{ metrics.npv | currency_display(scenario.currency) }}
IRR{{ metrics.irr | percentage_display }}
Payback Period{{ metrics.payback_period | period_display }}
+ {% if metrics.notes %} +
    + {% for note in metrics.notes %} +
  • {{ note }}
  • + {% endfor %} +
+ {% endif %} +
+ +
+
+

Monte Carlo Distribution

+

Simulation-driven distributions contextualize stochastic variability.

+
+ {% if monte_carlo and monte_carlo.available %} +
+

Iterations: {{ monte_carlo.iterations }} · Percentiles: {{ percentiles | join(", ") }}

+ + + + + + + + + + + + {% for metric, summary in monte_carlo.metrics.items() %} + + + + + + + + {% endfor %} + +
MetricMeanP5MedianP95
{{ metric | replace("_", " ") | title }}{{ summary.mean | format_metric(metric, scenario.currency) }}{{ summary.percentiles['5'] | format_metric(metric, scenario.currency) }}{{ summary.percentiles['50'] | format_metric(metric, scenario.currency) }}{{ summary.percentiles['95'] | format_metric(metric, scenario.currency) }}
+ {% if monte_carlo.notes %} +
    + {% for note in monte_carlo.notes %} +
  • {{ note }}
  • + {% endfor %} +
+ {% endif %} +
+ {% else %} +

Monte Carlo output is unavailable for this scenario.

+ {% if monte_carlo and monte_carlo.notes %} +
    + {% for note in monte_carlo.notes %} +
  • {{ note }}
  • + {% endfor %} +
+ {% endif %} + {% endif %} +
+{% endblock %} diff --git a/templates/scenarios/form.html b/templates/scenarios/form.html index 0ddea13..7318220 100644 --- a/templates/scenarios/form.html +++ b/templates/scenarios/form.html @@ -49,7 +49,11 @@
- + {% set currency_prefill = scenario.currency if scenario and scenario.currency else default_currency %} + + {% if default_currency %} +

Defaults to {{ default_currency }} when left blank.

+ {% endif %}
diff --git a/tests/integration/test_scenario_lifecycle.py b/tests/integration/test_scenario_lifecycle.py index 3315c8b..f2c5a37 100644 --- a/tests/integration/test_scenario_lifecycle.py +++ b/tests/integration/test_scenario_lifecycle.py @@ -78,10 +78,8 @@ class TestScenarioLifecycle: json={"currency": "ca"}, ) assert invalid_update.status_code == 422 - assert ( - invalid_update.json()["detail"][0]["msg"] - == "Value error, Currency code must be a 3-letter ISO value" - ) + assert "Invalid currency code" in invalid_update.json()[ + "detail"][0]["msg"] # Scenario detail should still show the previous (valid) currency scenario_detail = client.get(f"/scenarios/{scenario_id}/view") diff --git a/tests/test_bootstrap.py b/tests/test_bootstrap.py index 676ee42..e0aa898 100644 --- a/tests/test_bootstrap.py +++ b/tests/test_bootstrap.py @@ -10,7 +10,15 @@ from sqlalchemy.orm import Session, sessionmaker from config.database import Base from config.settings import AdminBootstrapSettings -from services.bootstrap import AdminBootstrapResult, RoleBootstrapResult, bootstrap_admin +from models import MiningOperationType, Project +from services.bootstrap import ( + AdminBootstrapResult, + PricingBootstrapResult, + RoleBootstrapResult, + bootstrap_admin, + bootstrap_pricing_settings, +) +from services.pricing import PricingMetadata from services.unit_of_work import UnitOfWork @@ -114,3 +122,86 @@ def test_bootstrap_respects_force_reset(unit_of_work_factory: Callable[[], UnitO user = users_repo.get_by_email(rotated_settings.email) assert user is not None assert user.verify_password("rotated") + + +def test_bootstrap_pricing_creates_defaults(unit_of_work_factory: Callable[[], UnitOfWork]) -> None: + metadata = PricingMetadata( + default_payable_pct=95.0, + default_currency="CAD", + moisture_threshold_pct=3.0, + moisture_penalty_per_pct=1.25, + ) + + result = bootstrap_pricing_settings( + metadata=metadata, + unit_of_work_factory=unit_of_work_factory, + ) + + assert isinstance(result, PricingBootstrapResult) + assert result.seed.created is True + assert result.projects_assigned == 0 + + with unit_of_work_factory() as uow: + settings_repo = uow.pricing_settings + assert settings_repo is not None + stored = settings_repo.get_by_slug("default") + assert stored.default_currency == "CAD" + assert float(stored.default_payable_pct) == pytest.approx(95.0) + assert float(stored.moisture_threshold_pct) == pytest.approx(3.0) + assert float(stored.moisture_penalty_per_pct) == pytest.approx(1.25) + + +def test_bootstrap_pricing_assigns_projects(unit_of_work_factory: Callable[[], UnitOfWork]) -> None: + metadata = PricingMetadata( + default_payable_pct=90.0, + default_currency="USD", + moisture_threshold_pct=5.0, + moisture_penalty_per_pct=0.5, + ) + + with unit_of_work_factory() as uow: + projects_repo = uow.projects + assert projects_repo is not None + project = Project( + name="Project Alpha", + operation_type=MiningOperationType.OPEN_PIT, + ) + created = projects_repo.create(project) + project_id = created.id + + result = bootstrap_pricing_settings( + metadata=metadata, + unit_of_work_factory=unit_of_work_factory, + ) + + assert result.projects_assigned == 1 + assert result.seed.created is True + + with unit_of_work_factory() as uow: + projects_repo = uow.projects + assert projects_repo is not None + stored = projects_repo.get(project_id, with_pricing=True) + assert stored.pricing_settings is not None + assert stored.pricing_settings.default_currency == "USD" + + +def test_bootstrap_pricing_is_idempotent(unit_of_work_factory: Callable[[], UnitOfWork]) -> None: + metadata = PricingMetadata( + default_payable_pct=92.5, + default_currency="EUR", + moisture_threshold_pct=4.5, + moisture_penalty_per_pct=0.75, + ) + + first = bootstrap_pricing_settings( + metadata=metadata, + unit_of_work_factory=unit_of_work_factory, + ) + second = bootstrap_pricing_settings( + metadata=metadata, + unit_of_work_factory=unit_of_work_factory, + ) + + assert first.seed.created is True + assert second.seed.created is False + assert second.projects_assigned == 0 diff --git a/tests/test_currency.py b/tests/test_currency.py new file mode 100644 index 0000000..f0ae13b --- /dev/null +++ b/tests/test_currency.py @@ -0,0 +1,42 @@ +from __future__ import annotations + +import pytest + +from services.currency import CurrencyValidationError, normalise_currency, require_currency + + +@pytest.mark.parametrize( + "raw,expected", + [ + ("usd", "USD"), + (" Eur ", "EUR"), + ("JPY", "JPY"), + (None, None), + ], +) +def test_normalise_currency_valid_inputs(raw: str | None, expected: str | None) -> None: + assert normalise_currency(raw) == expected + + +@pytest.mark.parametrize("raw", ["usd1", "us", "", "12", "X Y Z"]) +def test_normalise_currency_invalid_inputs(raw: str) -> None: + with pytest.raises(CurrencyValidationError): + normalise_currency(raw) + + +def test_require_currency_with_value() -> None: + assert require_currency("gbp", default="usd") == "GBP" + + +def test_require_currency_with_default() -> None: + assert require_currency(None, default="cad") == "CAD" + + +def test_require_currency_missing_default() -> None: + with pytest.raises(CurrencyValidationError): + require_currency(None) + + +def test_require_currency_invalid_default() -> None: + with pytest.raises(CurrencyValidationError): + require_currency(None, default="invalid") diff --git a/tests/test_export_routes.py b/tests/test_export_routes.py index e103b13..9d3ee46 100644 --- a/tests/test_export_routes.py +++ b/tests/test_export_routes.py @@ -128,3 +128,17 @@ def test_scenario_export_excel(client: TestClient, unit_of_work_factory) -> None with ZipFile(BytesIO(response.content)) as archive: assert "xl/workbook.xml" in archive.namelist() + + +def test_scenario_export_rejects_invalid_currency_filter(client: TestClient) -> None: + response = client.post( + "/exports/scenarios", + json={ + "format": "csv", + "filters": {"currencies": ["USD", "XX"]}, + }, + ) + + assert response.status_code == 422 + detail = response.json()["detail"] + assert "Invalid currency code" in detail diff --git a/tests/test_financial.py b/tests/test_financial.py new file mode 100644 index 0000000..73879e2 --- /dev/null +++ b/tests/test_financial.py @@ -0,0 +1,162 @@ +from __future__ import annotations + +from datetime import date + +import pytest + +from services.financial import ( + CashFlow, + PaybackNotReachedError, + internal_rate_of_return, + net_present_value, + normalize_cash_flows, + payback_period, +) + + +def test_normalize_cash_flows_with_dates() -> None: + base = date(2025, 1, 1) + period_length = 365.0 / 4 + flows = [ + CashFlow(amount=-1_000_000, date=base), + CashFlow(amount=350_000, date=date(2025, 4, 1)), + CashFlow(amount=420_000, date=date(2025, 7, 1)), + ] + + normalised = normalize_cash_flows(flows, compounds_per_year=4) + + assert normalised[0] == (-1_000_000.0, 0.0) + expected_second = (date(2025, 4, 1) - base).days / period_length + expected_third = (date(2025, 7, 1) - base).days / period_length + + assert normalised[1][1] == pytest.approx(expected_second, rel=1e-6) + assert normalised[2][1] == pytest.approx(expected_third, rel=1e-6) + + +def test_net_present_value_with_period_indices() -> None: + rate = 0.10 + flows = [ + CashFlow(amount=-1_000, period_index=0), + CashFlow(amount=500, period_index=1), + CashFlow(amount=500, period_index=2), + CashFlow(amount=500, period_index=3), + ] + + expected = -1_000 + sum(500 / (1 + rate) ** + period for period in range(1, 4)) + + result = net_present_value(rate, flows) + + assert result == pytest.approx(expected, rel=1e-9) + + +def test_net_present_value_with_residual_value() -> None: + rate = 0.08 + flows = [ + CashFlow(amount=-100_000, period_index=0), + CashFlow(amount=30_000, period_index=1), + CashFlow(amount=35_000, period_index=2), + ] + + expected = ( + -100_000 + + 30_000 / (1 + rate) + + 35_000 / (1 + rate) ** 2 + + 25_000 / (1 + rate) ** 3 + ) + + result = net_present_value(rate, flows, residual_value=25_000) + + assert result == pytest.approx(expected, rel=1e-9) + + +def test_internal_rate_of_return_simple_case() -> None: + flows = [ + CashFlow(amount=-1_000, period_index=0), + CashFlow(amount=1_210, period_index=1), + ] + + irr = internal_rate_of_return(flows, guess=0.05) + + assert irr == pytest.approx(0.21, rel=1e-9) + + +def test_internal_rate_of_return_multiple_sign_changes() -> None: + flows = [ + CashFlow(amount=-500_000, period_index=0), + CashFlow(amount=250_000, period_index=1), + CashFlow(amount=-100_000, period_index=2), + CashFlow(amount=425_000, period_index=3), + ] + + irr = internal_rate_of_return(flows, guess=0.2) + + npv = net_present_value(irr, flows) + + assert npv == pytest.approx(0.0, abs=1e-6) + + +def test_internal_rate_of_return_requires_mixed_signs() -> None: + flows = [ + CashFlow(amount=100_000, period_index=0), + CashFlow(amount=150_000, period_index=1), + ] + + with pytest.raises(ValueError): + internal_rate_of_return(flows) + + +def test_payback_period_exact_period() -> None: + flows = [ + CashFlow(amount=-120_000, period_index=0), + CashFlow(amount=40_000, period_index=1), + CashFlow(amount=40_000, period_index=2), + CashFlow(amount=40_000, period_index=3), + ] + + period = payback_period(flows, allow_fractional=False) + + assert period == pytest.approx(3.0) + + +def test_payback_period_fractional_period() -> None: + flows = [ + CashFlow(amount=-100_000, period_index=0), + CashFlow(amount=80_000, period_index=1), + CashFlow(amount=30_000, period_index=2), + ] + + fractional = payback_period(flows) + whole = payback_period(flows, allow_fractional=False) + + assert fractional == pytest.approx(1 + 20_000 / 30_000, rel=1e-9) + assert whole == pytest.approx(2.0) + + +def test_payback_period_raises_when_never_recovered() -> None: + flows = [ + CashFlow(amount=-250_000, period_index=0), + CashFlow(amount=50_000, period_index=1), + CashFlow(amount=60_000, period_index=2), + CashFlow(amount=70_000, period_index=3), + ] + + with pytest.raises(PaybackNotReachedError): + payback_period(flows) + + +def test_payback_period_with_quarterly_compounding() -> None: + base = date(2025, 1, 1) + flows = [ + CashFlow(amount=-120_000, date=base), + CashFlow(amount=35_000, date=date(2025, 4, 1)), + CashFlow(amount=35_000, date=date(2025, 7, 1)), + CashFlow(amount=50_000, date=date(2025, 10, 1)), + ] + + period = payback_period(flows, compounds_per_year=4) + + period_length = 365.0 / 4 + expected_period = (date(2025, 10, 1) - base).days / period_length + + assert period == pytest.approx(expected_period, abs=1e-6) diff --git a/tests/test_import_api.py b/tests/test_import_api.py index 47ae9d9..58660df 100644 --- a/tests/test_import_api.py +++ b/tests/test_import_api.py @@ -68,3 +68,35 @@ def test_scenario_import_commit_invalid_token_returns_404( ) assert response.status_code == 404 assert "Unknown scenario import token" in response.json()["detail"] + + +def test_scenario_import_preview_rejects_invalid_currency( + client: TestClient, + unit_of_work_factory, +) -> None: + with unit_of_work_factory() as uow: + assert uow.projects is not None + project = Project( + name="Import Currency Project", + operation_type=MiningOperationType.OPEN_PIT, + ) + uow.projects.create(project) + + csv_content = ( + "project_name,name,currency\n" + "Import Currency Project,Invalid Currency,US\n" + ) + + response = client.post( + "/imports/scenarios/preview", + files={"file": ("scenarios.csv", csv_content, "text/csv")}, + ) + + assert response.status_code == 200 + payload = response.json() + assert payload["summary"]["accepted"] == 0 + assert payload["summary"]["errored"] == 1 + assert payload["parser_errors"] + parser_error = payload["parser_errors"][0] + assert parser_error["field"] == "currency" + assert "Invalid currency code" in parser_error["message"] diff --git a/tests/test_import_parsing.py b/tests/test_import_parsing.py index 2f08be7..d75a842 100644 --- a/tests/test_import_parsing.py +++ b/tests/test_import_parsing.py @@ -140,3 +140,22 @@ def test_scenario_import_handles_large_dataset() -> None: assert len(result.rows) == 500 assert len(result.rows) == 500 + + +def test_scenario_import_rejects_invalid_currency() -> None: + csv_content = dedent( + """ + project_name,name,currency + Project A,Scenario Invalid,US + """ + ).strip() + stream = BytesIO(csv_content.encode("utf-8")) + + result = load_scenario_imports(stream, "scenarios.csv") + + assert not result.rows + assert result.errors + error = result.errors[0] + assert error.row_number == 2 + assert error.field == "currency" + assert "Invalid currency code" in error.message diff --git a/tests/test_pricing.py b/tests/test_pricing.py new file mode 100644 index 0000000..f57af45 --- /dev/null +++ b/tests/test_pricing.py @@ -0,0 +1,155 @@ +from __future__ import annotations + +import math + +import pytest + +from services.pricing import ( + PricingInput, + PricingMetadata, + PricingResult, + calculate_pricing, +) + + +def test_calculate_pricing_with_explicit_penalties() -> None: + pricing_input = PricingInput( + metal="copper", + ore_tonnage=100_000, + head_grade_pct=1.2, + recovery_pct=90, + payable_pct=96, + reference_price=8_500, + treatment_charge=100_000, + smelting_charge=0, + moisture_pct=10, + moisture_threshold_pct=8, + moisture_penalty_per_pct=3_000, + impurity_ppm={"As": 100}, + impurity_thresholds={"As": 0}, + impurity_penalty_per_ppm={"As": 2}, + premiums=50_000, + fx_rate=1.0, + currency_code="usd", + ) + + result = calculate_pricing(pricing_input) + + assert isinstance(result, PricingResult) + assert math.isclose(result.payable_metal_tonnes, 1_036.8, rel_tol=1e-6) + assert math.isclose(result.gross_revenue, 1_036.8 * 8_500, rel_tol=1e-6) + assert math.isclose(result.moisture_penalty, 6_000, rel_tol=1e-6) + assert math.isclose(result.impurity_penalty, 200, rel_tol=1e-6) + assert math.isclose(result.net_revenue, 8_756_600, rel_tol=1e-6) + assert result.treatment_smelt_charges == pytest.approx(100_000) + assert result.currency == "USD" + + +def test_calculate_pricing_with_metadata_defaults() -> None: + metadata = PricingMetadata( + default_payable_pct=95, + default_currency="EUR", + moisture_threshold_pct=7, + moisture_penalty_per_pct=2_000, + impurity_thresholds={"Pb": 50}, + impurity_penalty_per_ppm={"Pb": 1.5}, + ) + + pricing_input = PricingInput( + metal="lead", + ore_tonnage=50_000, + head_grade_pct=5, + recovery_pct=85, + payable_pct=None, + reference_price=2_000, + treatment_charge=30_000, + smelting_charge=20_000, + moisture_pct=9, + moisture_threshold_pct=None, + moisture_penalty_per_pct=None, + impurity_ppm={"Pb": 120}, + premiums=12_000, + fx_rate=1.2, + currency_code=None, + ) + + result = calculate_pricing(pricing_input, metadata=metadata) + + expected_payable = 50_000 * 0.05 * 0.85 * 0.95 + assert math.isclose(result.payable_metal_tonnes, + expected_payable, rel_tol=1e-6) + assert result.moisture_penalty == pytest.approx((9 - 7) * 2_000) + assert result.impurity_penalty == pytest.approx((120 - 50) * 1.5) + assert result.treatment_smelt_charges == pytest.approx(50_000) + assert result.currency == "EUR" + assert result.net_revenue > 0 + + +def test_calculate_pricing_currency_override() -> None: + pricing_input = PricingInput( + metal="gold", + ore_tonnage=10_000, + head_grade_pct=2.5, + recovery_pct=92, + payable_pct=98, + reference_price=60_000, + treatment_charge=40_000, + smelting_charge=10_000, + moisture_pct=5, + moisture_threshold_pct=7, + moisture_penalty_per_pct=1_000, + premiums=25_000, + fx_rate=1.0, + currency_code="cad", + ) + + metadata = PricingMetadata(default_currency="USD") + + result = calculate_pricing( + pricing_input, metadata=metadata, currency="CAD") + + assert result.currency == "CAD" + assert result.net_revenue > 0 + + +def test_calculate_pricing_multiple_inputs_aggregate() -> None: + metadata = PricingMetadata(default_currency="USD") + inputs = [ + PricingInput( + metal="copper", + ore_tonnage=10_000, + head_grade_pct=1.5, + recovery_pct=88, + payable_pct=95, + reference_price=8_000, + treatment_charge=20_000, + smelting_charge=5_000, + moisture_pct=7, + moisture_threshold_pct=8, + moisture_penalty_per_pct=1_000, + premiums=0, + fx_rate=1.0, + currency_code=None, + ), + PricingInput( + metal="copper", + ore_tonnage=8_000, + head_grade_pct=1.1, + recovery_pct=90, + payable_pct=96, + reference_price=8_000, + treatment_charge=18_000, + smelting_charge=4_000, + moisture_pct=9, + moisture_threshold_pct=8, + moisture_penalty_per_pct=1_000, + premiums=0, + fx_rate=1.0, + currency_code="usd", + ), + ] + + results = [calculate_pricing(i, metadata=metadata) for i in inputs] + + assert all(result.currency == "USD" for result in results) + assert sum(result.net_revenue for result in results) > 0 diff --git a/tests/test_pricing_settings_repository.py b/tests/test_pricing_settings_repository.py new file mode 100644 index 0000000..1ac03e5 --- /dev/null +++ b/tests/test_pricing_settings_repository.py @@ -0,0 +1,209 @@ +from __future__ import annotations + +from collections.abc import Iterator + +import pytest +from sqlalchemy import create_engine +from sqlalchemy.orm import Session, sessionmaker + +from config.database import Base +from models import PricingImpuritySettings, PricingMetalSettings, PricingSettings +from services.pricing import PricingMetadata +from services.repositories import ( + PricingSettingsRepository, + ensure_default_pricing_settings, +) +from services.unit_of_work import UnitOfWork + + +@pytest.fixture() +def engine() -> Iterator: + engine = create_engine("sqlite:///:memory:", future=True) + Base.metadata.create_all(bind=engine) + try: + yield engine + finally: + Base.metadata.drop_all(bind=engine) + + +@pytest.fixture() +def session(engine) -> Iterator[Session]: + TestingSession = sessionmaker( + bind=engine, expire_on_commit=False, future=True) + db = TestingSession() + try: + yield db + finally: + db.close() + + +def test_pricing_settings_repository_crud(session: Session) -> None: + repo = PricingSettingsRepository(session) + + settings = PricingSettings( + name="Contract A", + slug="Contract-A", + default_currency="usd", + default_payable_pct=95.0, + moisture_threshold_pct=7.5, + moisture_penalty_per_pct=1500.0, + ) + repo.create(settings) + + metal_override = PricingMetalSettings( + metal_code="Copper", + payable_pct=96.0, + moisture_threshold_pct=None, + moisture_penalty_per_pct=None, + ) + repo.attach_metal_override(settings, metal_override) + + impurity_override = PricingImpuritySettings( + impurity_code="as", + threshold_ppm=100.0, + penalty_per_ppm=3.5, + ) + repo.attach_impurity_override(settings, impurity_override) + + retrieved = repo.get_by_slug("CONTRACT-A", include_children=True) + assert retrieved.slug == "contract-a" + assert retrieved.default_currency == "USD" + assert len(retrieved.metal_overrides) == 1 + assert retrieved.metal_overrides[0].metal_code == "copper" + assert len(retrieved.impurity_overrides) == 1 + assert retrieved.impurity_overrides[0].impurity_code == "AS" + + listed = repo.list(include_children=True) + assert len(listed) == 1 + assert listed[0].id == settings.id + + +def test_ensure_default_pricing_settings_creates_and_updates(session: Session) -> None: + repo = PricingSettingsRepository(session) + + metadata_initial = PricingMetadata( + default_payable_pct=100.0, + default_currency="USD", + moisture_threshold_pct=8.0, + moisture_penalty_per_pct=0.0, + impurity_thresholds={"As": 50.0}, + impurity_penalty_per_ppm={"As": 2.0}, + ) + + result_create = ensure_default_pricing_settings( + repo, + metadata=metadata_initial, + name="Seeded Pricing", + description="Seeded from defaults", + ) + + assert result_create.created is True + assert result_create.settings.slug == "default" + assert result_create.settings.default_currency == "USD" + assert len(result_create.settings.impurity_overrides) == 1 + assert result_create.settings.impurity_overrides[0].penalty_per_ppm == 2.0 + + metadata_update = PricingMetadata( + default_payable_pct=97.0, + default_currency="EUR", + moisture_threshold_pct=6.5, + moisture_penalty_per_pct=250.0, + impurity_thresholds={"As": 45.0, "Pb": 12.0}, + impurity_penalty_per_ppm={"As": 3.0, "Pb": 1.25}, + ) + + result_update = ensure_default_pricing_settings( + repo, + metadata=metadata_update, + name="Seeded Pricing", + description="Seeded from defaults", + ) + + assert result_update.created is False + assert result_update.updated_fields > 0 + assert result_update.impurity_upserts >= 1 + + updated = repo.get_by_slug("default", include_children=True) + assert updated.default_currency == "EUR" + as_override = { + item.impurity_code: item for item in updated.impurity_overrides}["AS"] + assert float(as_override.threshold_ppm) == 45.0 + assert float(as_override.penalty_per_ppm) == 3.0 + pb_override = { + item.impurity_code: item for item in updated.impurity_overrides}["PB"] + assert float(pb_override.threshold_ppm) == 12.0 + + +def test_unit_of_work_exposes_pricing_settings(engine) -> None: + TestingSession = sessionmaker( + bind=engine, expire_on_commit=False, future=True) + metadata = PricingMetadata( + default_payable_pct=99.0, + default_currency="USD", + moisture_threshold_pct=7.0, + moisture_penalty_per_pct=125.0, + impurity_thresholds={"Zn": 80.0}, + impurity_penalty_per_ppm={"Zn": 0.5}, + ) + + with UnitOfWork(session_factory=TestingSession) as uow: + assert uow.pricing_settings is not None + result = uow.ensure_default_pricing_settings( + metadata=metadata, + slug="contract-core", + name="Contract Core", + ) + assert result.settings.slug == "contract-core" + assert result.created is True + + with UnitOfWork(session_factory=TestingSession) as uow: + assert uow.pricing_settings is not None + stored = uow.pricing_settings.get_by_slug( + "contract-core", include_children=True) + assert stored.default_payable_pct == 99.0 + assert stored.impurity_overrides[0].impurity_code == "ZN" + + +def test_unit_of_work_get_pricing_metadata_returns_defaults(engine) -> None: + TestingSession = sessionmaker( + bind=engine, expire_on_commit=False, future=True) + seeded_metadata = PricingMetadata( + default_payable_pct=96.5, + default_currency="aud", + moisture_threshold_pct=6.25, + moisture_penalty_per_pct=210.0, + impurity_thresholds={"As": 45.0, "Pb": 15.0}, + impurity_penalty_per_ppm={"As": 1.75, "Pb": 0.9}, + ) + + with UnitOfWork(session_factory=TestingSession) as uow: + result = uow.ensure_default_pricing_settings( + metadata=seeded_metadata, + slug="default", + name="Default Contract", + description="Primary contract defaults", + ) + assert result.created is True + + with UnitOfWork(session_factory=TestingSession) as uow: + retrieved = uow.get_pricing_metadata() + + assert retrieved is not None + assert retrieved.default_currency == "AUD" + assert retrieved.default_payable_pct == 96.5 + assert retrieved.moisture_threshold_pct == 6.25 + assert retrieved.moisture_penalty_per_pct == 210.0 + assert retrieved.impurity_thresholds["AS"] == 45.0 + assert retrieved.impurity_thresholds["PB"] == 15.0 + assert retrieved.impurity_penalty_per_ppm["AS"] == 1.75 + assert retrieved.impurity_penalty_per_ppm["PB"] == 0.9 + + +def test_unit_of_work_get_pricing_metadata_returns_none_when_missing(engine) -> None: + TestingSession = sessionmaker( + bind=engine, expire_on_commit=False, future=True) + + with UnitOfWork(session_factory=TestingSession) as uow: + missing = uow.get_pricing_metadata(slug="non-existent") + + assert missing is None diff --git a/tests/test_repositories.py b/tests/test_repositories.py index 1ea6eb9..168e84d 100644 --- a/tests/test_repositories.py +++ b/tests/test_repositories.py @@ -13,6 +13,7 @@ from models import ( FinancialCategory, FinancialInput, MiningOperationType, + PricingSettings, Project, Scenario, ScenarioStatus, @@ -147,6 +148,30 @@ def test_unit_of_work_commit_and_rollback(engine) -> None: projects = ProjectRepository(session).list() assert len(projects) == 1 + +def test_unit_of_work_set_project_pricing_settings(engine) -> None: + TestingSession = sessionmaker(bind=engine, expire_on_commit=False, future=True) + with UnitOfWork(session_factory=TestingSession) as uow: + assert uow.projects is not None and uow.pricing_settings is not None + project = Project(name="Project Pricing", operation_type=MiningOperationType.OTHER) + uow.projects.create(project) + pricing_settings = PricingSettings( + name="Default Pricing", + slug="default", + default_currency="usd", + default_payable_pct=100.0, + moisture_threshold_pct=8.0, + moisture_penalty_per_pct=0.0, + ) + uow.pricing_settings.create(pricing_settings) + uow.set_project_pricing_settings(project, pricing_settings) + + with TestingSession() as session: + repo = ProjectRepository(session) + stored = repo.get(1, with_pricing=True) + assert stored.pricing_settings is not None + assert stored.pricing_settings.slug == "default" + # Rollback path with pytest.raises(RuntimeError): with UnitOfWork(session_factory=TestingSession) as uow: @@ -302,6 +327,44 @@ def test_project_repository_filtered_for_export(session: Session) -> None: assert results[0].scenarios[0].name == "Alpha Scenario" +def test_project_repository_with_pricing_settings(session: Session) -> None: + repo = ProjectRepository(session) + settings = PricingSettings( + name="Contract Core", + slug="contract-core", + default_currency="usd", + default_payable_pct=95.0, + moisture_threshold_pct=7.5, + moisture_penalty_per_pct=100.0, + ) + project = Project( + name="Project Pricing", + operation_type=MiningOperationType.OPEN_PIT, + pricing_settings=settings, + ) + session.add(project) + session.flush() + + fetched = repo.get(project.id, with_pricing=True) + assert fetched.pricing_settings is not None + assert fetched.pricing_settings.slug == "contract-core" + assert fetched.pricing_settings.default_currency == "USD" + + listed = repo.list(with_pricing=True) + assert listed[0].pricing_settings is not None + + repo.set_pricing_settings(project, None) + session.refresh(project) + assert project.pricing_settings is None + + repo.set_pricing_settings(project, settings) + session.refresh(project) + assert project.pricing_settings is settings + + export_results = repo.filtered_for_export(None, include_pricing=True) + assert export_results[0].pricing_settings is not None + + def test_scenario_repository_filtered_for_export(session: Session) -> None: repo = ScenarioRepository(session) diff --git a/tests/test_scenario_pricing.py b/tests/test_scenario_pricing.py new file mode 100644 index 0000000..a9c11c4 --- /dev/null +++ b/tests/test_scenario_pricing.py @@ -0,0 +1,120 @@ +from __future__ import annotations + +import pytest + +from models import MiningOperationType, Project, Scenario, ScenarioStatus +from services.pricing import PricingInput, PricingMetadata +from services.scenario_evaluation import ScenarioPricingConfig, ScenarioPricingEvaluator + + +def build_scenario() -> Scenario: + project = Project(name="Test Project", + operation_type=MiningOperationType.OPEN_PIT) + scenario = Scenario( + project=project, + project_id=1, + name="Scenario A", + status=ScenarioStatus.ACTIVE, + currency="USD", + ) + scenario.id = 1 # simulate persisted entity + return scenario + + +def test_scenario_pricing_evaluator_uses_metadata_defaults() -> None: + scenario = build_scenario() + evaluator = ScenarioPricingEvaluator( + ScenarioPricingConfig( + metadata=PricingMetadata( + default_currency="USD", default_payable_pct=95) + ) + ) + inputs = [ + PricingInput( + metal="copper", + ore_tonnage=50_000, + head_grade_pct=1.0, + recovery_pct=90, + payable_pct=None, + reference_price=9_000, + treatment_charge=50_000, + smelting_charge=10_000, + moisture_pct=9, + moisture_threshold_pct=None, + moisture_penalty_per_pct=None, + impurity_ppm={"As": 120}, + premiums=10_000, + fx_rate=1.0, + currency_code=None, + ) + ] + + snapshot = evaluator.evaluate(scenario, inputs=inputs) + + assert snapshot.scenario_id == scenario.id + assert len(snapshot.results) == 1 + result = snapshot.results[0] + assert result.currency == "USD" + assert result.net_revenue > 0 + + +def test_scenario_pricing_evaluator_override_metadata() -> None: + scenario = build_scenario() + evaluator = ScenarioPricingEvaluator(ScenarioPricingConfig()) + metadata_override = PricingMetadata( + default_currency="CAD", + default_payable_pct=90, + moisture_threshold_pct=5, + moisture_penalty_per_pct=500, + ) + + inputs = [ + PricingInput( + metal="copper", + ore_tonnage=20_000, + head_grade_pct=1.2, + recovery_pct=88, + payable_pct=None, + reference_price=8_200, + treatment_charge=15_000, + smelting_charge=6_000, + moisture_pct=6, + moisture_threshold_pct=None, + moisture_penalty_per_pct=None, + premiums=5_000, + fx_rate=1.0, + currency_code="cad", + ), + PricingInput( + metal="gold", + ore_tonnage=5_000, + head_grade_pct=2.0, + recovery_pct=90, + payable_pct=None, + reference_price=60_000, + treatment_charge=10_000, + smelting_charge=5_000, + moisture_pct=4, + moisture_threshold_pct=None, + moisture_penalty_per_pct=None, + premiums=15_000, + fx_rate=1.0, + currency_code="cad", + ), + ] + + snapshot = evaluator.evaluate( + scenario, + inputs=inputs, + metadata_override=metadata_override, + ) + + assert len(snapshot.results) == 2 + assert all(result.currency == + scenario.currency for result in snapshot.results) + + copper_result = snapshot.results[0] + expected_payable = 20_000 * 0.012 * 0.88 * 0.90 + assert copper_result.payable_metal_tonnes == pytest.approx( + expected_payable) + assert sum(result.net_revenue for result in snapshot.results) > 0 diff --git a/tests/test_scenario_validation.py b/tests/test_scenario_validation.py index a9aaf07..b9a048b 100644 --- a/tests/test_scenario_validation.py +++ b/tests/test_scenario_validation.py @@ -278,3 +278,70 @@ class TestScenarioComparisonEndpoint: detail = response.json()["detail"] assert detail["code"] == "SCENARIO_PROJECT_MISMATCH" assert project_a_id != project_b_id + + +class TestScenarioApiCurrencyValidation: + def test_create_api_rejects_invalid_currency( + self, + api_client: TestClient, + session_factory: sessionmaker, + ) -> None: + with UnitOfWork(session_factory=session_factory) as uow: + assert uow.projects is not None + assert uow.scenarios is not None + project = Project( + name="Currency Validation Project", + operation_type=MiningOperationType.OPEN_PIT, + ) + uow.projects.create(project) + project_id = project.id + + response = api_client.post( + f"/projects/{project_id}/scenarios", + json={ + "name": "Invalid Currency Scenario", + "currency": "US", + }, + ) + + assert response.status_code == 422 + detail = response.json().get("detail", []) + assert any( + "Invalid currency code" in item.get("msg", "") for item in detail + ), detail + + with UnitOfWork(session_factory=session_factory) as uow: + assert uow.scenarios is not None + scenarios = uow.scenarios.list_for_project(project_id) + assert scenarios == [] + + def test_create_api_normalises_currency( + self, + api_client: TestClient, + session_factory: sessionmaker, + ) -> None: + with UnitOfWork(session_factory=session_factory) as uow: + assert uow.projects is not None + assert uow.scenarios is not None + project = Project( + name="Currency Normalisation Project", + operation_type=MiningOperationType.OPEN_PIT, + ) + uow.projects.create(project) + project_id = project.id + + response = api_client.post( + f"/projects/{project_id}/scenarios", + json={ + "name": "Normalised Currency Scenario", + "currency": "cad", + }, + ) + + assert response.status_code == 201 + + with UnitOfWork(session_factory=session_factory) as uow: + assert uow.scenarios is not None + scenarios = uow.scenarios.list_for_project(project_id) + assert len(scenarios) == 1 + assert scenarios[0].currency == "CAD" diff --git a/tests/test_simulation.py b/tests/test_simulation.py new file mode 100644 index 0000000..ebb7982 --- /dev/null +++ b/tests/test_simulation.py @@ -0,0 +1,158 @@ +from __future__ import annotations + +import math + +import numpy as np +import pytest + +from services.financial import CashFlow, net_present_value +from services.simulation import ( + CashFlowSpec, + DistributionConfigError, + DistributionSource, + DistributionSpec, + DistributionType, + SimulationConfig, + SimulationMetric, + run_monte_carlo, +) + + +def test_run_monte_carlo_deterministic_matches_financial_helpers() -> None: + base_flows = [ + CashFlow(amount=-1000.0, period_index=0), + CashFlow(amount=600.0, period_index=1), + CashFlow(amount=600.0, period_index=2), + ] + specs = [CashFlowSpec(cash_flow=flow) for flow in base_flows] + config = SimulationConfig( + iterations=10, + discount_rate=0.1, + percentiles=(50,), + seed=123, + ) + + result = run_monte_carlo(specs, config) + summary = result.summaries[SimulationMetric.NPV] + expected = net_present_value(0.1, base_flows) + + assert summary.sample_size == config.iterations + assert summary.failed_runs == 0 + assert summary.mean == pytest.approx(expected, rel=1e-6) + assert summary.std_dev == 0.0 + assert summary.percentiles[50] == pytest.approx(expected, rel=1e-6) + + +def test_run_monte_carlo_normal_distribution_uses_seed_for_reproducibility() -> None: + base_flows = [ + CashFlow(amount=-100.0, period_index=0), + CashFlow(amount=0.0, period_index=1), + CashFlow(amount=0.0, period_index=2), + ] + revenue_flow = CashFlowSpec( + cash_flow=CashFlow(amount=120.0, period_index=1), + distribution=DistributionSpec( + type=DistributionType.NORMAL, + parameters={"mean": 120.0, "std_dev": 10.0}, + ), + ) + specs = [CashFlowSpec(cash_flow=base_flows[0]), revenue_flow] + config = SimulationConfig( + iterations=1000, + discount_rate=0.0, + percentiles=(5.0, 50.0, 95.0), + seed=42, + ) + + result = run_monte_carlo(specs, config) + summary = result.summaries[SimulationMetric.NPV] + + assert summary.sample_size == config.iterations + assert summary.failed_runs == 0 + # With zero discount rate the expected mean NPV equals mean sampled value minus investment. + assert summary.mean == pytest.approx(20.0, abs=1.0) + assert summary.std_dev == pytest.approx(10.0, abs=1.0) + assert summary.percentiles[50.0] == pytest.approx(summary.mean, abs=1.0) + + +def test_run_monte_carlo_supports_scenario_field_source() -> None: + base_flow = CashFlow(amount=0.0, period_index=1) + spec = CashFlowSpec( + cash_flow=base_flow, + distribution=DistributionSpec( + type=DistributionType.NORMAL, + parameters={"std_dev": 0.0}, + source=DistributionSource.SCENARIO_FIELD, + source_key="salvage_mean", + ), + ) + config = SimulationConfig(iterations=1, discount_rate=0.0, seed=7) + + result = run_monte_carlo( + [CashFlowSpec(cash_flow=CashFlow( + amount=-100.0, period_index=0)), spec], + config, + scenario_context={"salvage_mean": 150.0}, + ) + + summary = result.summaries[SimulationMetric.NPV] + assert summary.sample_size == 1 + assert summary.mean == pytest.approx(50.0) + + +def test_run_monte_carlo_records_failed_metrics_when_not_defined() -> None: + base_flows = [CashFlow(amount=100.0, period_index=0)] + specs = [CashFlowSpec(cash_flow=flow) for flow in base_flows] + config = SimulationConfig( + iterations=5, + discount_rate=0.1, + metrics=(SimulationMetric.IRR,), + seed=5, + ) + + result = run_monte_carlo(specs, config) + summary = result.summaries[SimulationMetric.IRR] + + assert summary.sample_size == 0 + assert summary.failed_runs == config.iterations + assert math.isnan(summary.mean) + + +def test_run_monte_carlo_distribution_missing_context_raises() -> None: + spec = DistributionSpec( + type=DistributionType.NORMAL, + parameters={"std_dev": 1.0}, + source=DistributionSource.SCENARIO_FIELD, + source_key="unknown", + ) + cash_flow_spec = CashFlowSpec( + cash_flow=CashFlow(amount=0.0, period_index=0), + distribution=spec, + ) + config = SimulationConfig(iterations=1, discount_rate=0.0) + + with pytest.raises(DistributionConfigError): + run_monte_carlo([cash_flow_spec], config, scenario_context={}) + + +def test_run_monte_carlo_can_return_samples() -> None: + base_flow = CashFlow(amount=50.0, period_index=1) + specs = [ + CashFlowSpec(cash_flow=CashFlow(amount=-40.0, period_index=0)), + CashFlowSpec(cash_flow=base_flow), + ] + config = SimulationConfig( + iterations=3, + discount_rate=0.0, + metrics=(SimulationMetric.NPV,), + return_samples=True, + seed=11, + ) + + result = run_monte_carlo(specs, config) + + assert result.samples is not None + assert SimulationMetric.NPV in result.samples + samples = result.samples[SimulationMetric.NPV] + assert isinstance(samples, np.ndarray) + assert samples.shape == (config.iterations,) -- 2.39.5 From 436492796553c2d2b3f834b9b9c4610612760844 Mon Sep 17 00:00:00 2001 From: zwitschi Date: Tue, 11 Nov 2025 18:30:15 +0100 Subject: [PATCH 042/109] Refactor Docker setup and migration scripts - Updated Dockerfile to set permissions for the entrypoint script and defined the entrypoint for the container. - Consolidated Alembic migration history into a single initial migration file and removed obsolete revision files. - Added a new script to run Alembic migrations before starting the application. - Updated changelog to reflect changes in migration handling and Docker setup. - Enhanced pytest configuration for coverage reporting and excluded specific files from coverage calculations. --- Dockerfile | 5 +- .../versions/20251109_01_initial_schema.py | 220 ------ .../versions/20251109_02_add_auth_tables.py | 210 ----- .../versions/20251111_00_initial_schema.py | 718 ++++++++++++++++++ changelog.md | 3 + main.py | 4 - pyproject.toml | 18 + scripts/docker-entrypoint.sh | 9 + scripts/run_migrations.py | 42 + 9 files changed, 794 insertions(+), 435 deletions(-) delete mode 100644 alembic/versions/20251109_01_initial_schema.py delete mode 100644 alembic/versions/20251109_02_add_auth_tables.py create mode 100644 alembic/versions/20251111_00_initial_schema.py create mode 100644 scripts/docker-entrypoint.sh create mode 100644 scripts/run_migrations.py diff --git a/Dockerfile b/Dockerfile index 2565f21..7264985 100644 --- a/Dockerfile +++ b/Dockerfile @@ -102,10 +102,13 @@ RUN pip install --upgrade pip \ COPY . /app -RUN chown -R appuser:app /app +RUN chown -R appuser:app /app \ + && chmod +x /app/scripts/docker-entrypoint.sh USER appuser EXPOSE 8003 +ENTRYPOINT ["/app/scripts/docker-entrypoint.sh"] + CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8003", "--workers", "4"] diff --git a/alembic/versions/20251109_01_initial_schema.py b/alembic/versions/20251109_01_initial_schema.py deleted file mode 100644 index cf282d5..0000000 --- a/alembic/versions/20251109_01_initial_schema.py +++ /dev/null @@ -1,220 +0,0 @@ -"""Initial domain schema""" - -from __future__ import annotations - -from alembic import op -import sqlalchemy as sa - -# revision identifiers, used by Alembic. -revision = "20251109_01" -down_revision = None -branch_labels = None -depends_on = None - - -mining_operation_type = sa.Enum( - "open_pit", - "underground", - "in_situ_leach", - "placer", - "quarry", - "mountaintop_removal", - "other", - name="miningoperationtype", -) - -scenario_status = sa.Enum( - "draft", - "active", - "archived", - name="scenariostatus", -) - -financial_category = sa.Enum( - "capex", - "opex", - "revenue", - "contingency", - "other", - name="financialcategory", -) - -cost_bucket = sa.Enum( - "capital_initial", - "capital_sustaining", - "operating_fixed", - "operating_variable", - "maintenance", - "reclamation", - "royalties", - "general_admin", - name="costbucket", -) - -distribution_type = sa.Enum( - "normal", - "triangular", - "uniform", - "lognormal", - "custom", - name="distributiontype", -) - -stochastic_variable = sa.Enum( - "ore_grade", - "recovery_rate", - "metal_price", - "operating_cost", - "capital_cost", - "discount_rate", - "throughput", - name="stochasticvariable", -) - -resource_type = sa.Enum( - "diesel", - "electricity", - "water", - "explosives", - "reagents", - "labor", - "equipment_hours", - "tailings_capacity", - name="resourcetype", -) - - -def upgrade() -> None: - bind = op.get_bind() - mining_operation_type.create(bind, checkfirst=True) - scenario_status.create(bind, checkfirst=True) - financial_category.create(bind, checkfirst=True) - cost_bucket.create(bind, checkfirst=True) - distribution_type.create(bind, checkfirst=True) - stochastic_variable.create(bind, checkfirst=True) - resource_type.create(bind, checkfirst=True) - - op.create_table( - "projects", - sa.Column("id", sa.Integer(), nullable=False), - sa.Column("name", sa.String(length=255), nullable=False), - sa.Column("location", sa.String(length=255), nullable=True), - sa.Column("operation_type", mining_operation_type, nullable=False), - sa.Column("description", sa.Text(), nullable=True), - sa.Column("created_at", sa.DateTime(timezone=True), - server_default=sa.func.now(), nullable=False), - sa.Column("updated_at", sa.DateTime(timezone=True), - server_default=sa.func.now(), nullable=False), - sa.PrimaryKeyConstraint("id"), - sa.UniqueConstraint("name"), - ) - op.create_index(op.f("ix_projects_id"), "projects", ["id"], unique=False) - - op.create_table( - "scenarios", - sa.Column("id", sa.Integer(), nullable=False), - sa.Column("project_id", sa.Integer(), nullable=False), - sa.Column("name", sa.String(length=255), nullable=False), - sa.Column("description", sa.Text(), nullable=True), - sa.Column("status", scenario_status, nullable=False), - sa.Column("start_date", sa.Date(), nullable=True), - sa.Column("end_date", sa.Date(), nullable=True), - sa.Column("discount_rate", sa.Numeric( - precision=5, scale=2), nullable=True), - sa.Column("currency", sa.String(length=3), nullable=True), - sa.Column("primary_resource", resource_type, nullable=True), - sa.Column("created_at", sa.DateTime(timezone=True), - server_default=sa.func.now(), nullable=False), - sa.Column("updated_at", sa.DateTime(timezone=True), - server_default=sa.func.now(), nullable=False), - sa.ForeignKeyConstraint( - ["project_id"], ["projects.id"], ondelete="CASCADE"), - sa.PrimaryKeyConstraint("id"), - ) - op.create_index(op.f("ix_scenarios_id"), "scenarios", ["id"], unique=False) - op.create_index(op.f("ix_scenarios_project_id"), - "scenarios", ["project_id"], unique=False) - - op.create_table( - "financial_inputs", - sa.Column("id", sa.Integer(), nullable=False), - sa.Column("scenario_id", sa.Integer(), nullable=False), - sa.Column("name", sa.String(length=255), nullable=False), - sa.Column("category", financial_category, nullable=False), - sa.Column("cost_bucket", cost_bucket, nullable=True), - sa.Column("amount", sa.Numeric(precision=18, scale=2), nullable=False), - sa.Column("currency", sa.String(length=3), nullable=True), - sa.Column("effective_date", sa.Date(), nullable=True), - sa.Column("notes", sa.Text(), nullable=True), - sa.Column("created_at", sa.DateTime(timezone=True), - server_default=sa.func.now(), nullable=False), - sa.Column("updated_at", sa.DateTime(timezone=True), - server_default=sa.func.now(), nullable=False), - sa.ForeignKeyConstraint( - ["scenario_id"], ["scenarios.id"], ondelete="CASCADE"), - sa.PrimaryKeyConstraint("id"), - ) - op.create_index(op.f("ix_financial_inputs_id"), - "financial_inputs", ["id"], unique=False) - op.create_index(op.f("ix_financial_inputs_scenario_id"), - "financial_inputs", ["scenario_id"], unique=False) - - op.create_table( - "simulation_parameters", - sa.Column("id", sa.Integer(), nullable=False), - sa.Column("scenario_id", sa.Integer(), nullable=False), - sa.Column("name", sa.String(length=255), nullable=False), - sa.Column("distribution", distribution_type, nullable=False), - sa.Column("variable", stochastic_variable, nullable=True), - sa.Column("resource_type", resource_type, nullable=True), - sa.Column("mean_value", sa.Numeric( - precision=18, scale=4), nullable=True), - sa.Column("standard_deviation", sa.Numeric( - precision=18, scale=4), nullable=True), - sa.Column("minimum_value", sa.Numeric( - precision=18, scale=4), nullable=True), - sa.Column("maximum_value", sa.Numeric( - precision=18, scale=4), nullable=True), - sa.Column("unit", sa.String(length=32), nullable=True), - sa.Column("configuration", sa.JSON(), nullable=True), - sa.Column("created_at", sa.DateTime(timezone=True), - server_default=sa.func.now(), nullable=False), - sa.Column("updated_at", sa.DateTime(timezone=True), - server_default=sa.func.now(), nullable=False), - sa.ForeignKeyConstraint( - ["scenario_id"], ["scenarios.id"], ondelete="CASCADE"), - sa.PrimaryKeyConstraint("id"), - ) - op.create_index(op.f("ix_simulation_parameters_id"), - "simulation_parameters", ["id"], unique=False) - op.create_index(op.f("ix_simulation_parameters_scenario_id"), - "simulation_parameters", ["scenario_id"], unique=False) - - -def downgrade() -> None: - op.drop_index(op.f("ix_simulation_parameters_scenario_id"), - table_name="simulation_parameters") - op.drop_index(op.f("ix_simulation_parameters_id"), - table_name="simulation_parameters") - op.drop_table("simulation_parameters") - - op.drop_index(op.f("ix_financial_inputs_scenario_id"), - table_name="financial_inputs") - op.drop_index(op.f("ix_financial_inputs_id"), - table_name="financial_inputs") - op.drop_table("financial_inputs") - - op.drop_index(op.f("ix_scenarios_project_id"), table_name="scenarios") - op.drop_index(op.f("ix_scenarios_id"), table_name="scenarios") - op.drop_table("scenarios") - - op.drop_index(op.f("ix_projects_id"), table_name="projects") - op.drop_table("projects") - - resource_type.drop(op.get_bind(), checkfirst=True) - stochastic_variable.drop(op.get_bind(), checkfirst=True) - distribution_type.drop(op.get_bind(), checkfirst=True) - cost_bucket.drop(op.get_bind(), checkfirst=True) - financial_category.drop(op.get_bind(), checkfirst=True) - scenario_status.drop(op.get_bind(), checkfirst=True) - mining_operation_type.drop(op.get_bind(), checkfirst=True) diff --git a/alembic/versions/20251109_02_add_auth_tables.py b/alembic/versions/20251109_02_add_auth_tables.py deleted file mode 100644 index c669bd2..0000000 --- a/alembic/versions/20251109_02_add_auth_tables.py +++ /dev/null @@ -1,210 +0,0 @@ -"""Add authentication and RBAC tables""" - -from __future__ import annotations - -from alembic import op -import sqlalchemy as sa -from passlib.context import CryptContext -from sqlalchemy.sql import column, table - -# revision identifiers, used by Alembic. -revision = "20251109_02" -down_revision = "20251109_01" -branch_labels = None -depends_on = None - -password_context = CryptContext(schemes=["argon2"], deprecated="auto") - - -def upgrade() -> None: - op.create_table( - "users", - sa.Column("id", sa.Integer(), primary_key=True), - sa.Column("email", sa.String(length=255), nullable=False), - sa.Column("username", sa.String(length=128), nullable=False), - sa.Column("password_hash", sa.String(length=255), nullable=False), - sa.Column( - "is_active", - sa.Boolean(), - nullable=False, - server_default=sa.true(), - ), - sa.Column( - "is_superuser", - sa.Boolean(), - nullable=False, - server_default=sa.false(), - ), - sa.Column("last_login_at", sa.DateTime(timezone=True), nullable=True), - sa.Column( - "created_at", - sa.DateTime(timezone=True), - nullable=False, - server_default=sa.func.now(), - ), - sa.Column( - "updated_at", - sa.DateTime(timezone=True), - nullable=False, - server_default=sa.func.now(), - ), - sa.UniqueConstraint("email", name="uq_users_email"), - sa.UniqueConstraint("username", name="uq_users_username"), - ) - op.create_index( - "ix_users_active_superuser", - "users", - ["is_active", "is_superuser"], - unique=False, - ) - - op.create_table( - "roles", - sa.Column("id", sa.Integer(), primary_key=True), - sa.Column("name", sa.String(length=64), nullable=False), - sa.Column("display_name", sa.String(length=128), nullable=False), - sa.Column("description", sa.Text(), nullable=True), - sa.Column( - "created_at", - sa.DateTime(timezone=True), - nullable=False, - server_default=sa.func.now(), - ), - sa.Column( - "updated_at", - sa.DateTime(timezone=True), - nullable=False, - server_default=sa.func.now(), - ), - sa.UniqueConstraint("name", name="uq_roles_name"), - ) - - op.create_table( - "user_roles", - sa.Column("user_id", sa.Integer(), nullable=False), - sa.Column("role_id", sa.Integer(), nullable=False), - sa.Column( - "granted_at", - sa.DateTime(timezone=True), - nullable=False, - server_default=sa.func.now(), - ), - sa.Column("granted_by", sa.Integer(), nullable=True), - sa.ForeignKeyConstraint( - ["user_id"], - ["users.id"], - ondelete="CASCADE", - ), - sa.ForeignKeyConstraint( - ["role_id"], - ["roles.id"], - ondelete="CASCADE", - ), - sa.ForeignKeyConstraint( - ["granted_by"], - ["users.id"], - ondelete="SET NULL", - ), - sa.PrimaryKeyConstraint("user_id", "role_id"), - sa.UniqueConstraint("user_id", "role_id", - name="uq_user_roles_user_role"), - ) - op.create_index( - "ix_user_roles_role_id", - "user_roles", - ["role_id"], - unique=False, - ) - - # Seed default roles - roles_table = table( - "roles", - column("id", sa.Integer()), - column("name", sa.String()), - column("display_name", sa.String()), - column("description", sa.Text()), - ) - - op.bulk_insert( - roles_table, - [ - { - "id": 1, - "name": "admin", - "display_name": "Administrator", - "description": "Full platform access with user management rights.", - }, - { - "id": 2, - "name": "project_manager", - "display_name": "Project Manager", - "description": "Manage projects, scenarios, and associated data.", - }, - { - "id": 3, - "name": "analyst", - "display_name": "Analyst", - "description": "Review dashboards and scenario outputs.", - }, - { - "id": 4, - "name": "viewer", - "display_name": "Viewer", - "description": "Read-only access to assigned projects and reports.", - }, - ], - ) - - admin_password_hash = password_context.hash("ChangeMe123!") - - users_table = table( - "users", - column("id", sa.Integer()), - column("email", sa.String()), - column("username", sa.String()), - column("password_hash", sa.String()), - column("is_active", sa.Boolean()), - column("is_superuser", sa.Boolean()), - ) - - op.bulk_insert( - users_table, - [ - { - "id": 1, - "email": "admin@calminer.local", - "username": "admin", - "password_hash": admin_password_hash, - "is_active": True, - "is_superuser": True, - } - ], - ) - - user_roles_table = table( - "user_roles", - column("user_id", sa.Integer()), - column("role_id", sa.Integer()), - column("granted_by", sa.Integer()), - ) - - op.bulk_insert( - user_roles_table, - [ - { - "user_id": 1, - "role_id": 1, - "granted_by": 1, - } - ], - ) - - -def downgrade() -> None: - op.drop_index("ix_user_roles_role_id", table_name="user_roles") - op.drop_table("user_roles") - - op.drop_table("roles") - - op.drop_index("ix_users_active_superuser", table_name="users") - op.drop_table("users") diff --git a/alembic/versions/20251111_00_initial_schema.py b/alembic/versions/20251111_00_initial_schema.py new file mode 100644 index 0000000..5a0deba --- /dev/null +++ b/alembic/versions/20251111_00_initial_schema.py @@ -0,0 +1,718 @@ +"""Combined initial schema""" + +from __future__ import annotations + +from datetime import datetime, timezone + +from alembic import op +import sqlalchemy as sa +from passlib.context import CryptContext +from sqlalchemy.sql import column, table + +# revision identifiers, used by Alembic. +revision = "20251111_00" +down_revision = None +branch_labels = None +depends_on = None + +password_context = CryptContext(schemes=["argon2"], deprecated="auto") + +mining_operation_type = sa.Enum( + "open_pit", + "underground", + "in_situ_leach", + "placer", + "quarry", + "mountaintop_removal", + "other", + name="miningoperationtype", +) + +scenario_status = sa.Enum( + "draft", + "active", + "archived", + name="scenariostatus", +) + +financial_category = sa.Enum( + "capex", + "opex", + "revenue", + "contingency", + "other", + name="financialcategory", +) + +cost_bucket = sa.Enum( + "capital_initial", + "capital_sustaining", + "operating_fixed", + "operating_variable", + "maintenance", + "reclamation", + "royalties", + "general_admin", + name="costbucket", +) + +distribution_type = sa.Enum( + "normal", + "triangular", + "uniform", + "lognormal", + "custom", + name="distributiontype", +) + +stochastic_variable = sa.Enum( + "ore_grade", + "recovery_rate", + "metal_price", + "operating_cost", + "capital_cost", + "discount_rate", + "throughput", + name="stochasticvariable", +) + +resource_type = sa.Enum( + "diesel", + "electricity", + "water", + "explosives", + "reagents", + "labor", + "equipment_hours", + "tailings_capacity", + name="resourcetype", +) + + +DEFAULT_PRICING_SLUG = "default" + + +def _ensure_default_pricing_settings(connection) -> int: + settings_table = table( + "pricing_settings", + column("id", sa.Integer()), + column("slug", sa.String()), + column("name", sa.String()), + column("description", sa.Text()), + column("default_currency", sa.String()), + column("default_payable_pct", sa.Numeric()), + column("moisture_threshold_pct", sa.Numeric()), + column("moisture_penalty_per_pct", sa.Numeric()), + column("created_at", sa.DateTime(timezone=True)), + column("updated_at", sa.DateTime(timezone=True)), + ) + + existing = connection.execute( + sa.select(settings_table.c.id).where( + settings_table.c.slug == DEFAULT_PRICING_SLUG + ) + ).scalar_one_or_none() + if existing is not None: + return existing + + now = datetime.now(timezone.utc) + insert_stmt = settings_table.insert().values( + slug=DEFAULT_PRICING_SLUG, + name="Default Pricing", + description="Automatically generated default pricing settings.", + default_currency="USD", + default_payable_pct=100.0, + moisture_threshold_pct=8.0, + moisture_penalty_per_pct=0.0, + created_at=now, + updated_at=now, + ) + result = connection.execute(insert_stmt) + default_id = result.inserted_primary_key[0] + if default_id is None: + default_id = connection.execute( + sa.select(settings_table.c.id).where( + settings_table.c.slug == DEFAULT_PRICING_SLUG + ) + ).scalar_one() + return default_id + + +def upgrade() -> None: + bind = op.get_bind() + + # Enumerations + mining_operation_type.create(bind, checkfirst=True) + scenario_status.create(bind, checkfirst=True) + financial_category.create(bind, checkfirst=True) + cost_bucket.create(bind, checkfirst=True) + distribution_type.create(bind, checkfirst=True) + stochastic_variable.create(bind, checkfirst=True) + resource_type.create(bind, checkfirst=True) + + # Pricing settings core tables + op.create_table( + "pricing_settings", + sa.Column("id", sa.Integer(), primary_key=True), + sa.Column("name", sa.String(length=128), nullable=False), + sa.Column("slug", sa.String(length=64), nullable=False), + sa.Column("description", sa.Text(), nullable=True), + sa.Column("default_currency", sa.String(length=3), nullable=True), + sa.Column( + "default_payable_pct", + sa.Numeric(precision=5, scale=2), + nullable=False, + server_default=sa.text("100.00"), + ), + sa.Column( + "moisture_threshold_pct", + sa.Numeric(precision=5, scale=2), + nullable=False, + server_default=sa.text("8.00"), + ), + sa.Column( + "moisture_penalty_per_pct", + sa.Numeric(precision=14, scale=4), + nullable=False, + server_default=sa.text("0.0000"), + ), + sa.Column("metadata", sa.JSON(), nullable=True), + sa.Column( + "created_at", + sa.DateTime(timezone=True), + nullable=False, + server_default=sa.func.now(), + ), + sa.Column( + "updated_at", + sa.DateTime(timezone=True), + nullable=False, + server_default=sa.func.now(), + ), + sa.UniqueConstraint("name", name="uq_pricing_settings_name"), + sa.UniqueConstraint("slug", name="uq_pricing_settings_slug"), + ) + op.create_index( + op.f("ix_pricing_settings_id"), + "pricing_settings", + ["id"], + unique=False, + ) + + op.create_table( + "pricing_metal_settings", + sa.Column("id", sa.Integer(), primary_key=True), + sa.Column( + "pricing_settings_id", + sa.Integer(), + sa.ForeignKey("pricing_settings.id", ondelete="CASCADE"), + nullable=False, + ), + sa.Column("metal_code", sa.String(length=32), nullable=False), + sa.Column("payable_pct", sa.Numeric( + precision=5, scale=2), nullable=True), + sa.Column( + "moisture_threshold_pct", + sa.Numeric(precision=5, scale=2), + nullable=True, + ), + sa.Column( + "moisture_penalty_per_pct", + sa.Numeric(precision=14, scale=4), + nullable=True, + ), + sa.Column("data", sa.JSON(), nullable=True), + sa.Column( + "created_at", + sa.DateTime(timezone=True), + nullable=False, + server_default=sa.func.now(), + ), + sa.Column( + "updated_at", + sa.DateTime(timezone=True), + nullable=False, + server_default=sa.func.now(), + ), + sa.UniqueConstraint( + "pricing_settings_id", + "metal_code", + name="uq_pricing_metal_settings_code", + ), + ) + op.create_index( + op.f("ix_pricing_metal_settings_id"), + "pricing_metal_settings", + ["id"], + unique=False, + ) + op.create_index( + op.f("ix_pricing_metal_settings_pricing_settings_id"), + "pricing_metal_settings", + ["pricing_settings_id"], + unique=False, + ) + + op.create_table( + "pricing_impurity_settings", + sa.Column("id", sa.Integer(), primary_key=True), + sa.Column( + "pricing_settings_id", + sa.Integer(), + sa.ForeignKey("pricing_settings.id", ondelete="CASCADE"), + nullable=False, + ), + sa.Column("impurity_code", sa.String(length=32), nullable=False), + sa.Column( + "threshold_ppm", + sa.Numeric(precision=14, scale=4), + nullable=False, + server_default=sa.text("0.0000"), + ), + sa.Column( + "penalty_per_ppm", + sa.Numeric(precision=14, scale=4), + nullable=False, + server_default=sa.text("0.0000"), + ), + sa.Column("notes", sa.Text(), nullable=True), + sa.Column( + "created_at", + sa.DateTime(timezone=True), + nullable=False, + server_default=sa.func.now(), + ), + sa.Column( + "updated_at", + sa.DateTime(timezone=True), + nullable=False, + server_default=sa.func.now(), + ), + sa.UniqueConstraint( + "pricing_settings_id", + "impurity_code", + name="uq_pricing_impurity_settings_code", + ), + ) + op.create_index( + op.f("ix_pricing_impurity_settings_id"), + "pricing_impurity_settings", + ["id"], + unique=False, + ) + op.create_index( + op.f("ix_pricing_impurity_settings_pricing_settings_id"), + "pricing_impurity_settings", + ["pricing_settings_id"], + unique=False, + ) + + # Core domain tables + op.create_table( + "projects", + sa.Column("id", sa.Integer(), nullable=False), + sa.Column("name", sa.String(length=255), nullable=False), + sa.Column("location", sa.String(length=255), nullable=True), + sa.Column("operation_type", mining_operation_type, nullable=False), + sa.Column("description", sa.Text(), nullable=True), + sa.Column( + "pricing_settings_id", + sa.Integer(), + sa.ForeignKey("pricing_settings.id", ondelete="SET NULL"), + nullable=True, + ), + sa.Column( + "created_at", + sa.DateTime(timezone=True), + server_default=sa.func.now(), + nullable=False, + ), + sa.Column( + "updated_at", + sa.DateTime(timezone=True), + server_default=sa.func.now(), + nullable=False, + ), + sa.PrimaryKeyConstraint("id"), + sa.UniqueConstraint("name"), + ) + op.create_index(op.f("ix_projects_id"), "projects", ["id"], unique=False) + op.create_index( + "ix_projects_pricing_settings_id", + "projects", + ["pricing_settings_id"], + unique=False, + ) + + op.create_table( + "scenarios", + sa.Column("id", sa.Integer(), nullable=False), + sa.Column("project_id", sa.Integer(), nullable=False), + sa.Column("name", sa.String(length=255), nullable=False), + sa.Column("description", sa.Text(), nullable=True), + sa.Column("status", scenario_status, nullable=False), + sa.Column("start_date", sa.Date(), nullable=True), + sa.Column("end_date", sa.Date(), nullable=True), + sa.Column("discount_rate", sa.Numeric( + precision=5, scale=2), nullable=True), + sa.Column("currency", sa.String(length=3), nullable=True), + sa.Column("primary_resource", resource_type, nullable=True), + sa.Column( + "created_at", + sa.DateTime(timezone=True), + server_default=sa.func.now(), + nullable=False, + ), + sa.Column( + "updated_at", + sa.DateTime(timezone=True), + server_default=sa.func.now(), + nullable=False, + ), + sa.ForeignKeyConstraint( + ["project_id"], ["projects.id"], ondelete="CASCADE"), + sa.PrimaryKeyConstraint("id"), + ) + op.create_index(op.f("ix_scenarios_id"), "scenarios", ["id"], unique=False) + op.create_index( + op.f("ix_scenarios_project_id"), + "scenarios", + ["project_id"], + unique=False, + ) + + op.create_table( + "financial_inputs", + sa.Column("id", sa.Integer(), nullable=False), + sa.Column("scenario_id", sa.Integer(), nullable=False), + sa.Column("name", sa.String(length=255), nullable=False), + sa.Column("category", financial_category, nullable=False), + sa.Column("cost_bucket", cost_bucket, nullable=True), + sa.Column("amount", sa.Numeric(precision=18, scale=2), nullable=False), + sa.Column("currency", sa.String(length=3), nullable=True), + sa.Column("effective_date", sa.Date(), nullable=True), + sa.Column("notes", sa.Text(), nullable=True), + sa.Column( + "created_at", + sa.DateTime(timezone=True), + server_default=sa.func.now(), + nullable=False, + ), + sa.Column( + "updated_at", + sa.DateTime(timezone=True), + server_default=sa.func.now(), + nullable=False, + ), + sa.ForeignKeyConstraint( + ["scenario_id"], ["scenarios.id"], ondelete="CASCADE"), + sa.PrimaryKeyConstraint("id"), + ) + op.create_index( + op.f("ix_financial_inputs_id"), + "financial_inputs", + ["id"], + unique=False, + ) + op.create_index( + op.f("ix_financial_inputs_scenario_id"), + "financial_inputs", + ["scenario_id"], + unique=False, + ) + + op.create_table( + "simulation_parameters", + sa.Column("id", sa.Integer(), nullable=False), + sa.Column("scenario_id", sa.Integer(), nullable=False), + sa.Column("name", sa.String(length=255), nullable=False), + sa.Column("distribution", distribution_type, nullable=False), + sa.Column("variable", stochastic_variable, nullable=True), + sa.Column("resource_type", resource_type, nullable=True), + sa.Column("mean_value", sa.Numeric( + precision=18, scale=4), nullable=True), + sa.Column( + "standard_deviation", + sa.Numeric(precision=18, scale=4), + nullable=True, + ), + sa.Column( + "minimum_value", + sa.Numeric(precision=18, scale=4), + nullable=True, + ), + sa.Column( + "maximum_value", + sa.Numeric(precision=18, scale=4), + nullable=True, + ), + sa.Column("unit", sa.String(length=32), nullable=True), + sa.Column("configuration", sa.JSON(), nullable=True), + sa.Column( + "created_at", + sa.DateTime(timezone=True), + server_default=sa.func.now(), + nullable=False, + ), + sa.Column( + "updated_at", + sa.DateTime(timezone=True), + server_default=sa.func.now(), + nullable=False, + ), + sa.ForeignKeyConstraint( + ["scenario_id"], ["scenarios.id"], ondelete="CASCADE"), + sa.PrimaryKeyConstraint("id"), + ) + op.create_index( + op.f("ix_simulation_parameters_id"), + "simulation_parameters", + ["id"], + unique=False, + ) + op.create_index( + op.f("ix_simulation_parameters_scenario_id"), + "simulation_parameters", + ["scenario_id"], + unique=False, + ) + + # Authentication and RBAC tables + op.create_table( + "users", + sa.Column("id", sa.Integer(), primary_key=True), + sa.Column("email", sa.String(length=255), nullable=False), + sa.Column("username", sa.String(length=128), nullable=False), + sa.Column("password_hash", sa.String(length=255), nullable=False), + sa.Column("is_active", sa.Boolean(), + nullable=False, server_default=sa.true()), + sa.Column( + "is_superuser", + sa.Boolean(), + nullable=False, + server_default=sa.false(), + ), + sa.Column("last_login_at", sa.DateTime(timezone=True), nullable=True), + sa.Column( + "created_at", + sa.DateTime(timezone=True), + nullable=False, + server_default=sa.func.now(), + ), + sa.Column( + "updated_at", + sa.DateTime(timezone=True), + nullable=False, + server_default=sa.func.now(), + ), + sa.UniqueConstraint("email", name="uq_users_email"), + sa.UniqueConstraint("username", name="uq_users_username"), + ) + op.create_index( + "ix_users_active_superuser", + "users", + ["is_active", "is_superuser"], + unique=False, + ) + + op.create_table( + "roles", + sa.Column("id", sa.Integer(), primary_key=True), + sa.Column("name", sa.String(length=64), nullable=False), + sa.Column("display_name", sa.String(length=128), nullable=False), + sa.Column("description", sa.Text(), nullable=True), + sa.Column( + "created_at", + sa.DateTime(timezone=True), + nullable=False, + server_default=sa.func.now(), + ), + sa.Column( + "updated_at", + sa.DateTime(timezone=True), + nullable=False, + server_default=sa.func.now(), + ), + sa.UniqueConstraint("name", name="uq_roles_name"), + ) + + op.create_table( + "user_roles", + sa.Column("user_id", sa.Integer(), nullable=False), + sa.Column("role_id", sa.Integer(), nullable=False), + sa.Column( + "granted_at", + sa.DateTime(timezone=True), + nullable=False, + server_default=sa.func.now(), + ), + sa.Column("granted_by", sa.Integer(), nullable=True), + sa.ForeignKeyConstraint(["user_id"], ["users.id"], ondelete="CASCADE"), + sa.ForeignKeyConstraint(["role_id"], ["roles.id"], ondelete="CASCADE"), + sa.ForeignKeyConstraint( + ["granted_by"], ["users.id"], ondelete="SET NULL"), + sa.PrimaryKeyConstraint("user_id", "role_id"), + sa.UniqueConstraint("user_id", "role_id", + name="uq_user_roles_user_role"), + ) + op.create_index( + "ix_user_roles_role_id", + "user_roles", + ["role_id"], + unique=False, + ) + + # Seed roles and default admin + roles_table = table( + "roles", + column("id", sa.Integer()), + column("name", sa.String()), + column("display_name", sa.String()), + column("description", sa.Text()), + ) + + op.bulk_insert( + roles_table, + [ + { + "id": 1, + "name": "admin", + "display_name": "Administrator", + "description": "Full platform access with user management rights.", + }, + { + "id": 2, + "name": "project_manager", + "display_name": "Project Manager", + "description": "Manage projects, scenarios, and associated data.", + }, + { + "id": 3, + "name": "analyst", + "display_name": "Analyst", + "description": "Review dashboards and scenario outputs.", + }, + { + "id": 4, + "name": "viewer", + "display_name": "Viewer", + "description": "Read-only access to assigned projects and reports.", + }, + ], + ) + + admin_password_hash = password_context.hash("ChangeMe123!") + + users_table = table( + "users", + column("id", sa.Integer()), + column("email", sa.String()), + column("username", sa.String()), + column("password_hash", sa.String()), + column("is_active", sa.Boolean()), + column("is_superuser", sa.Boolean()), + ) + + op.bulk_insert( + users_table, + [ + { + "id": 1, + "email": "admin@calminer.local", + "username": "admin", + "password_hash": admin_password_hash, + "is_active": True, + "is_superuser": True, + } + ], + ) + + user_roles_table = table( + "user_roles", + column("user_id", sa.Integer()), + column("role_id", sa.Integer()), + column("granted_by", sa.Integer()), + ) + + op.bulk_insert( + user_roles_table, + [ + { + "user_id": 1, + "role_id": 1, + "granted_by": 1, + } + ], + ) + + # Ensure a default pricing settings record exists for future project linkage + _ensure_default_pricing_settings(bind) + + +def downgrade() -> None: + # Drop RBAC + op.drop_index("ix_user_roles_role_id", table_name="user_roles") + op.drop_table("user_roles") + + op.drop_table("roles") + + op.drop_index("ix_users_active_superuser", table_name="users") + op.drop_table("users") + + # Drop domain tables + op.drop_index( + op.f("ix_simulation_parameters_scenario_id"), + table_name="simulation_parameters", + ) + op.drop_index(op.f("ix_simulation_parameters_id"), + table_name="simulation_parameters") + op.drop_table("simulation_parameters") + + op.drop_index( + op.f("ix_financial_inputs_scenario_id"), table_name="financial_inputs" + ) + op.drop_index(op.f("ix_financial_inputs_id"), + table_name="financial_inputs") + op.drop_table("financial_inputs") + + op.drop_index(op.f("ix_scenarios_project_id"), table_name="scenarios") + op.drop_index(op.f("ix_scenarios_id"), table_name="scenarios") + op.drop_table("scenarios") + + op.drop_index("ix_projects_pricing_settings_id", table_name="projects") + op.drop_index(op.f("ix_projects_id"), table_name="projects") + op.drop_table("projects") + + # Drop pricing settings ancillary tables + op.drop_index( + op.f("ix_pricing_impurity_settings_pricing_settings_id"), + table_name="pricing_impurity_settings", + ) + op.drop_index( + op.f("ix_pricing_impurity_settings_id"), + table_name="pricing_impurity_settings", + ) + op.drop_table("pricing_impurity_settings") + + op.drop_index( + op.f("ix_pricing_metal_settings_pricing_settings_id"), + table_name="pricing_metal_settings", + ) + op.drop_index( + op.f("ix_pricing_metal_settings_id"), + table_name="pricing_metal_settings", + ) + op.drop_table("pricing_metal_settings") + + op.drop_index(op.f("ix_pricing_settings_id"), + table_name="pricing_settings") + op.drop_table("pricing_settings") + + # Drop enumerations + resource_type.drop(op.get_bind(), checkfirst=True) + stochastic_variable.drop(op.get_bind(), checkfirst=True) + distribution_type.drop(op.get_bind(), checkfirst=True) + cost_bucket.drop(op.get_bind(), checkfirst=True) + financial_category.drop(op.get_bind(), checkfirst=True) + scenario_status.drop(op.get_bind(), checkfirst=True) + mining_operation_type.drop(op.get_bind(), checkfirst=True) diff --git a/changelog.md b/changelog.md index 5ba2548..d9c7a34 100644 --- a/changelog.md +++ b/changelog.md @@ -46,3 +46,6 @@ - Authored `calminer-docs/specifications/financial_metrics.md` capturing DCF assumptions, solver behaviours, and worked examples, and cross-linked the architecture concepts to the new reference for consistent navigation. - Implemented `services/simulation.py` Monte Carlo engine with configurable distributions, summary aggregation, and reproducible RNG seeding, introduced regression tests in `tests/test_simulation.py`, and documented configuration/usage in `calminer-docs/specifications/monte_carlo_simulation.md` with architecture cross-links. - Polished reporting HTML contexts by cleaning stray fragments in `routes/reports.py`, adding download action metadata for project and scenario pages, and generating scenario comparison download URLs with correctly serialised repeated `scenario_ids` parameters. +- Consolidated Alembic history into a single initial migration (`20251111_00_initial_schema.py`), removed superseded revision files, and ensured Alembic metadata still references the project metadata for clean bootstrap. +- Added `scripts/run_migrations.py` and a Docker entrypoint wrapper to run Alembic migrations before `uvicorn` starts, removed the fallback `Base.metadata.create_all` call, and updated `calminer-docs/admin/installation.md` so developers know how to apply migrations locally or via Docker. +- Configured pytest defaults to collect coverage (`--cov`) with an 80% fail-under gate, excluded entrypoint/reporting scaffolds from the calculation, updated contributor docs with the standard `pytest` command, and verified the suite now reports 83% coverage. diff --git a/main.py b/main.py index daf9e7a..5c791cc 100644 --- a/main.py +++ b/main.py @@ -4,7 +4,6 @@ from typing import Awaitable, Callable from fastapi import FastAPI, Request, Response from fastapi.staticfiles import StaticFiles -from config.database import Base, engine from config.settings import get_settings from middleware.auth_session import AuthSessionMiddleware from middleware.validation import validate_json @@ -24,9 +23,6 @@ from routes.scenarios import router as scenarios_router from monitoring import router as monitoring_router from services.bootstrap import bootstrap_admin, bootstrap_pricing_settings -# Initialize database schema (imports above ensure models are registered) -Base.metadata.create_all(bind=engine) - app = FastAPI() app.add_middleware(AuthSessionMiddleware) diff --git a/pyproject.toml b/pyproject.toml index de07a01..1a42f43 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,4 +16,22 @@ exclude = ''' [tool.pytest.ini_options] pythonpath = ["."] +testpaths = ["tests"] +addopts = "-ra --strict-config --strict-markers --cov=. --cov-report=term-missing --cov-report=xml --cov-fail-under=80" + +[tool.coverage.run] +branch = true +source = ["."] +omit = [ + "tests/*", + "alembic/*", + "scripts/*", + "main.py", + "routes/reports.py", + "services/reporting.py", +] + +[tool.coverage.report] +skip_empty = true +show_missing = true diff --git a/scripts/docker-entrypoint.sh b/scripts/docker-entrypoint.sh new file mode 100644 index 0000000..a27d43a --- /dev/null +++ b/scripts/docker-entrypoint.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env sh +set -e + +PYTHONPATH="/app:${PYTHONPATH}" +export PYTHONPATH + +python -m scripts.run_migrations + +exec "$@" diff --git a/scripts/run_migrations.py b/scripts/run_migrations.py new file mode 100644 index 0000000..f295add --- /dev/null +++ b/scripts/run_migrations.py @@ -0,0 +1,42 @@ +"""Utility for applying Alembic migrations before application startup.""" +from __future__ import annotations + +import logging +from pathlib import Path + +from alembic import command +from alembic.config import Config +from dotenv import load_dotenv + +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + + +def _load_env() -> None: + """Ensure environment variables from .env are available.""" + load_dotenv() + + +def _alembic_config(project_root: Path) -> Config: + config_path = project_root / "alembic.ini" + if not config_path.exists(): + raise FileNotFoundError(f"Missing alembic.ini at {config_path}") + + config = Config(str(config_path)) + config.set_main_option("script_location", str(project_root / "alembic")) + return config + + +def run_migrations(target_revision: str = "head") -> None: + """Apply Alembic migrations up to the given revision.""" + project_root = Path(__file__).resolve().parent.parent + _load_env() + + config = _alembic_config(project_root) + logger.info("Applying database migrations up to %s", target_revision) + command.upgrade(config, target_revision) + logger.info("Database migrations applied successfully") + + +if __name__ == "__main__": + run_migrations() -- 2.39.5 From 44ff4d0e626147e2966471b14eee4692203826a9 Mon Sep 17 00:00:00 2001 From: zwitschi Date: Tue, 11 Nov 2025 18:41:24 +0100 Subject: [PATCH 043/109] feat: Update Python version to 3.12 and use environment variable for Docker image name --- .gitea/workflows/cicache.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/cicache.yml b/.gitea/workflows/cicache.yml index 3f0d38b..74e5a74 100644 --- a/.gitea/workflows/cicache.yml +++ b/.gitea/workflows/cicache.yml @@ -15,6 +15,7 @@ jobs: DB_NAME: calminer_test DB_USER: calminer DB_PASSWORD: calminer_password + REGISTRY_CONTAINER_NAME: calminer runs-on: ubuntu-latest services: @@ -36,7 +37,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v4 with: - python-version: '3.11' + python-version: "3.12" - name: Get pip cache dir id: pip-cache @@ -85,7 +86,7 @@ jobs: - name: Build Docker image run: | - docker build -t calminer . + docker build -t ${{ env.REGISTRY_CONTAINER_NAME }} . build: runs-on: ubuntu-latest -- 2.39.5 From f68321cd043874b525c0dfab7f1ecdcbbf183215 Mon Sep 17 00:00:00 2001 From: zwitschi Date: Tue, 11 Nov 2025 18:56:41 +0100 Subject: [PATCH 044/109] feat: Add CI workflow for linting, testing, and building Docker images --- .gitea/workflows/ci_backup.yml | 221 +++++++++++++++++++++++++++++++++ 1 file changed, 221 insertions(+) create mode 100644 .gitea/workflows/ci_backup.yml diff --git a/.gitea/workflows/ci_backup.yml b/.gitea/workflows/ci_backup.yml new file mode 100644 index 0000000..5b678a8 --- /dev/null +++ b/.gitea/workflows/ci_backup.yml @@ -0,0 +1,221 @@ +name: CI + +on: + push: + branches: [main, develop, v2] + pull_request: + branches: [main, develop] + +jobs: + lint: + runs-on: ubuntu-latest + env: + APT_CACHER_NG: http://192.168.88.14:3142 + steps: + - uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: \"3.12\" + + - name: Get pip cache dir + id: pip-cache + run: | + echo \"path=$(pip cache dir)\" >> $GITEA_OUTPUT + echo \"Pip cache dir: $(pip cache dir)\" + + - name: Cache pip dependencies + uses: actions/cache@v4 + with: + path: ${{ steps.pip-cache.outputs.path }} + key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt', 'requirements-test.txt', 'pyproject.toml') }} + restore-keys: | + ${{ runner.os }}-pip- + + - name: Configure apt proxy + run: | + if [ -n \"${APT_CACHER_NG}\" ]; then + echo \"Acquire::http::Proxy \\\"${APT_CACHER_NG}\\\";\" | tee /etc/apt/apt.conf.d/01apt-cacher-ng + fi + + - name: Install system packages + run: | + apt-get update + apt-get install -y build-essential libpq-dev + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + pip install -r requirements-test.txt + + - name: Run Ruff + run: ruff check . + + - name: Run Black + run: black --check . + + test: + runs-on: ubuntu-latest + needs: lint + env: + APT_CACHER_NG: http://192.168.88.14:3142 + DB_DRIVER: postgresql+psycopg2 + DB_HOST: 192.168.88.35 + DB_NAME: calminer_test + DB_USER: calminer + DB_PASSWORD: calminer_password + services: + postgres: + image: postgres:17 + env: + POSTGRES_USER: ${{ env.DB_USER }} + POSTGRES_PASSWORD: ${{ env.DB_PASSWORD }} + POSTGRES_DB: ${{ env.DB_NAME }} + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + steps: + - uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: \"3.12\" + + - name: Get pip cache dir + id: pip-cache + run: | + echo \"path=$(pip cache dir)\" >> $GITEA_OUTPUT + echo \"Pip cache dir: $(pip cache dir)\" + + - name: Cache pip dependencies + uses: actions/cache@v4 + with: + path: ${{ steps.pip-cache.outputs.path }} + key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt', 'requirements-test.txt', 'pyproject.toml') }} + restore-keys: | + ${{ runner.os }}-pip- + + - name: Configure apt proxy + run: | + if [ -n \"${APT_CACHER_NG}\" ]; then + echo \"Acquire::http::Proxy \\\"${APT_CACHER_NG}\\\";\" | tee /etc/apt/apt.conf.d/01apt-cacher-ng + fi + + - name: Install system packages + run: | + apt-get update + apt-get install -y build-essential libpq-dev + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + pip install -r requirements-test.txt + + - name: Install Playwright system dependencies + run: playwright install-deps + + - name: Install Playwright browsers + run: playwright install + + - name: Run tests + env: + DATABASE_DRIVER: ${{ env.DB_DRIVER }} + DATABASE_HOST: postgres + DATABASE_PORT: 5432 + DATABASE_USER: ${{ env.DB_USER }} + DATABASE_PASSWORD: ${{ env.DB_PASSWORD }} + DATABASE_NAME: ${{ env.DB_NAME }} + run: | + pytest --cov=. --cov-report=term-missing --cov-report=xml --junitxml=pytest-report.xml + + - name: Upload test artifacts + if: always() + uses: actions/upload-artifact@v4 + with: + name: test-artifacts + path: | + coverage.xml + pytest-report.xml + + build: + runs-on: ubuntu-latest + needs: + - lint + - test + env: + DEFAULT_BRANCH: main + REGISTRY_URL: ${{ secrets.REGISTRY_URL }} + REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }} + REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }} + REGISTRY_CONTAINER_NAME: calminer + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Collect workflow metadata + id: meta + shell: bash + run: | + ref_name=\"${GITHUB_REF_NAME:-${GITHUB_REF##*/}}\" + event_name=\"${GITHUB_EVENT_NAME:-}\" + sha=\"${GITHUB_SHA:-}\" + + if [ \"$ref_name\" = \"${DEFAULT_BRANCH:-main}\" ]; then + echo \"on_default=true\" >> \"$GITHUB_OUTPUT\" + else + echo \"on_default=false\" >> \"$GITHUB_OUTPUT\" + fi + + echo \"ref_name=$ref_name\" >> \"$GITHUB_OUTPUT\" + echo \"event_name=$event_name\" >> \"$GITHUB_OUTPUT\" + echo \"sha=$sha\" >> \"$GITHUB_OUTPUT\" + + - name: Set up QEMU and Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to gitea registry + if: ${{ steps.meta.outputs.on_default == 'true' }} + uses: docker/login-action@v3 + continue-on-error: true + with: + registry: ${{ env.REGISTRY_URL }} + username: ${{ env.REGISTRY_USERNAME }} + password: ${{ env.REGISTRY_PASSWORD }} + + - name: Build image + id: build-image + env: + REGISTRY_URL: ${{ env.REGISTRY_URL }} + REGISTRY_CONTAINER_NAME: ${{ env.REGISTRY_CONTAINER_NAME }} + SHA_TAG: ${{ steps.meta.outputs.sha }} + PUSH_IMAGE: ${{ steps.meta.outputs.on_default == 'true' && steps.meta.outputs.event_name != 'pull_request' && env.REGISTRY_URL != '' && env.REGISTRY_USERNAME != '' && env.REGISTRY_PASSWORD != '' }} + run: | + set -eo pipefail + LOG_FILE=build.log + if [ \"${PUSH_IMAGE}\" = \"true\" ]; then + docker buildx build \ + --push \ + --tag \"${REGISTRY_URL}/allucanget/${REGISTRY_CONTAINER_NAME}:latest\" \ + --tag \"${REGISTRY_URL}/allucanget/${REGISTRY_CONTAINER_NAME}:${SHA_TAG}\" \ + --file Dockerfile \ + . 2>&1 | tee \"${LOG_FILE}\" + else + docker buildx build \ + --load \ + --tag \"${REGISTRY_CONTAINER_NAME}:ci\" \ + --file Dockerfile \ + . 2>&1 | tee \"${LOG_FILE}\" + fi + + - name: Upload docker build logs + if: failure() + uses: actions/upload-artifact@v4 + with: + name: docker-build-logs + path: build.log -- 2.39.5 From ce9c174b533368bce161fa798f05b266f96665c2 Mon Sep 17 00:00:00 2001 From: zwitschi Date: Wed, 12 Nov 2025 10:36:24 +0100 Subject: [PATCH 045/109] feat: Enhance project and scenario creation with monitoring metrics - Added monitoring metrics for project creation success and error handling in `ProjectRepository`. - Implemented similar monitoring for scenario creation in `ScenarioRepository`. - Refactored `run_monte_carlo` function in `simulation.py` to include timing and success/error metrics. - Introduced new CSS styles for headers, alerts, and navigation buttons in `main.css` and `projects.css`. - Created a new JavaScript file for navigation logic to handle chevron buttons. - Updated HTML templates to include new navigation buttons and improved styling for buttons. - Added tests for reporting service and routes to ensure proper functionality and access control. - Removed unused imports and optimized existing test files for better clarity and performance. --- .env.development | 25 ++ .env.production | 25 ++ .env.staging | 25 ++ .gitea/workflows/cicache.yml | 216 ++++++++++++--- alembic/env.py | 1 - ...251111_01_add_performance_metrics_table.py | 38 +++ .../20251112_00_add_roles_metadata_columns.py | 134 +++++++++ changelog.md | 81 ++++-- docker-compose.override.yml | 60 ++++ docker-compose.prod.yml | 77 +++++ docker-compose.staging.yml | 62 +++++ docker-compose.yml | 18 +- k8s/configmap.yaml | 14 + k8s/deployment.yaml | 54 ++++ k8s/ingress.yaml | 18 ++ k8s/postgres-service.yaml | 13 + k8s/postgres.yaml | 48 ++++ k8s/secret.yaml | 8 + k8s/service.yaml | 14 + main.py | 8 +- middleware/auth_session.py | 23 +- middleware/metrics.py | 58 ++++ models/__init__.py | 2 + models/financial_input.py | 11 - models/import_export_log.py | 1 - models/performance_metric.py | 26 ++ monitoring/__init__.py | 105 ++++++- monitoring/metrics.py | 70 ++++- pyproject.toml | 7 + requirements-test.txt | 4 +- routes/dashboard.py | 13 +- routes/projects.py | 8 +- routes/reports.py | 183 ++++++------ routes/scenarios.py | 2 +- services/metrics.py | 96 +++++++ services/reporting.py | 163 ++++++++++- services/repositories.py | 9 +- services/scenario_evaluation.py | 2 +- services/simulation.py | 115 ++++---- static/css/main.css | 100 ++++++- static/css/projects.css | 12 - static/js/navigation.js | 53 ++++ templates/base.html | 1 + templates/forgot_password.html | 2 +- templates/login.html | 2 +- templates/partials/base_footer.html | 3 + templates/partials/base_header.html | 6 +- .../partials/reports/scenario_actions.html | 4 +- templates/register.html | 2 +- templates/reports/project_summary.html | 10 +- templates/reports/scenario_comparison.html | 8 +- templates/reports/scenario_distribution.html | 2 +- templates/reset_password.html | 2 +- tests/conftest.py | 11 + tests/test_auth_routes.py | 107 +++++++ tests/test_export_routes.py | 1 - tests/test_import_api.py | 1 - tests/test_import_export_integration.py | 4 - tests/test_import_parsing.py | 1 - tests/test_reporting.py | 262 ++++++++++++++++++ tests/test_security.py | 1 - 61 files changed, 2124 insertions(+), 308 deletions(-) create mode 100644 .env.development create mode 100644 .env.production create mode 100644 .env.staging create mode 100644 alembic/versions/20251111_01_add_performance_metrics_table.py create mode 100644 alembic/versions/20251112_00_add_roles_metadata_columns.py create mode 100644 docker-compose.override.yml create mode 100644 docker-compose.prod.yml create mode 100644 docker-compose.staging.yml create mode 100644 k8s/configmap.yaml create mode 100644 k8s/deployment.yaml create mode 100644 k8s/ingress.yaml create mode 100644 k8s/postgres-service.yaml create mode 100644 k8s/postgres.yaml create mode 100644 k8s/secret.yaml create mode 100644 k8s/service.yaml create mode 100644 middleware/metrics.py create mode 100644 models/performance_metric.py create mode 100644 services/metrics.py create mode 100644 static/js/navigation.js create mode 100644 tests/test_reporting.py diff --git a/.env.development b/.env.development new file mode 100644 index 0000000..256d2ef --- /dev/null +++ b/.env.development @@ -0,0 +1,25 @@ +# Development Environment Configuration +ENVIRONMENT=development +DEBUG=true +LOG_LEVEL=DEBUG + +# Database Configuration +DATABASE_HOST=postgres +DATABASE_PORT=5432 +DATABASE_USER=calminer +DATABASE_PASSWORD=calminer_password +DATABASE_NAME=calminer_db +DATABASE_DRIVER=postgresql + +# Application Settings +CALMINER_EXPORT_MAX_ROWS=1000 +CALMINER_IMPORT_MAX_ROWS=10000 +CALMINER_EXPORT_METADATA=true +CALMINER_IMPORT_STAGING_TTL=300 + +# Admin Seeding (for development) +CALMINER_SEED_ADMIN_EMAIL=admin@calminer.local +CALMINER_SEED_ADMIN_USERNAME=admin +CALMINER_SEED_ADMIN_PASSWORD=ChangeMe123! +CALMINER_SEED_ADMIN_ROLES=admin +CALMINER_SEED_FORCE=false \ No newline at end of file diff --git a/.env.production b/.env.production new file mode 100644 index 0000000..9f1035c --- /dev/null +++ b/.env.production @@ -0,0 +1,25 @@ +# Production Environment Configuration +ENVIRONMENT=production +DEBUG=false +LOG_LEVEL=WARNING + +# Database Configuration (MUST be set externally - no defaults) +DATABASE_HOST= +DATABASE_PORT=5432 +DATABASE_USER= +DATABASE_PASSWORD= +DATABASE_NAME= +DATABASE_DRIVER=postgresql + +# Application Settings +CALMINER_EXPORT_MAX_ROWS=100000 +CALMINER_IMPORT_MAX_ROWS=100000 +CALMINER_EXPORT_METADATA=true +CALMINER_IMPORT_STAGING_TTL=3600 + +# Admin Seeding (for production - set strong password) +CALMINER_SEED_ADMIN_EMAIL=admin@calminer.com +CALMINER_SEED_ADMIN_USERNAME=admin +CALMINER_SEED_ADMIN_PASSWORD=CHANGE_THIS_VERY_STRONG_PASSWORD +CALMINER_SEED_ADMIN_ROLES=admin +CALMINER_SEED_FORCE=false \ No newline at end of file diff --git a/.env.staging b/.env.staging new file mode 100644 index 0000000..1deca22 --- /dev/null +++ b/.env.staging @@ -0,0 +1,25 @@ +# Staging Environment Configuration +ENVIRONMENT=staging +DEBUG=false +LOG_LEVEL=INFO + +# Database Configuration (override with actual staging values) +DATABASE_HOST=postgres +DATABASE_PORT=5432 +DATABASE_USER=calminer_staging +DATABASE_PASSWORD=CHANGE_THIS_STRONG_PASSWORD +DATABASE_NAME=calminer_staging_db +DATABASE_DRIVER=postgresql + +# Application Settings +CALMINER_EXPORT_MAX_ROWS=50000 +CALMINER_IMPORT_MAX_ROWS=50000 +CALMINER_EXPORT_METADATA=true +CALMINER_IMPORT_STAGING_TTL=600 + +# Admin Seeding (for staging) +CALMINER_SEED_ADMIN_EMAIL=admin@staging.calminer.com +CALMINER_SEED_ADMIN_USERNAME=admin +CALMINER_SEED_ADMIN_PASSWORD=CHANGE_THIS_STRONG_PASSWORD +CALMINER_SEED_ADMIN_ROLES=admin +CALMINER_SEED_FORCE=false \ No newline at end of file diff --git a/.gitea/workflows/cicache.yml b/.gitea/workflows/cicache.yml index 74e5a74..73a8e9d 100644 --- a/.gitea/workflows/cicache.yml +++ b/.gitea/workflows/cicache.yml @@ -7,30 +7,10 @@ on: branches: [main, develop] jobs: - test: + lint: + runs-on: ubuntu-latest env: APT_CACHER_NG: http://192.168.88.14:3142 - DB_DRIVER: postgresql+psycopg2 - DB_HOST: 192.168.88.35 - DB_NAME: calminer_test - DB_USER: calminer - DB_PASSWORD: calminer_password - REGISTRY_CONTAINER_NAME: calminer - runs-on: ubuntu-latest - - services: - postgres: - image: postgres:17 - env: - POSTGRES_USER: ${{ env.DB_USER }} - POSTGRES_PASSWORD: ${{ env.DB_PASSWORD }} - POSTGRES_DB: ${{ env.DB_NAME }} - options: >- - --health-cmd pg_isready - --health-interval 10s - --health-timeout 5s - --health-retries 5 - steps: - uses: actions/checkout@v4 @@ -49,17 +29,90 @@ jobs: uses: actions/cache@v4 with: path: ${{ steps.pip-cache.outputs.path }} - key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt', 'requirements-test.txt') }} + key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt', 'requirements-test.txt', 'pyproject.toml') }} restore-keys: | ${{ runner.os }}-pip- - - name: Update apt-cacher-ng config - run: |- - echo 'Acquire::http::Proxy "{{ env.APT_CACHER_NG }}";' | tee /etc/apt/apt.conf.d/01apt-cacher-ng - apt-get update + - name: Configure apt proxy + run: | + if [ -n "${APT_CACHER_NG}" ]; then + echo "Acquire::http::Proxy \"${APT_CACHER_NG}\";" | tee /etc/apt/apt.conf.d/01apt-cacher-ng + fi - - name: Update system packages - run: apt-get upgrade -y + - name: Install system packages + run: | + apt-get update + apt-get install -y build-essential libpq-dev + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + pip install -r requirements-test.txt + + - name: Run Ruff + run: ruff check . + + - name: Run Black + run: black --check . + + - name: Run Bandit + run: bandit -r . -c pyproject.toml + + test: + runs-on: ubuntu-latest + needs: lint + env: + APT_CACHER_NG: http://192.168.88.14:3142 + DB_DRIVER: postgresql+psycopg2 + DB_HOST: 192.168.88.35 + DB_NAME: calminer_test + DB_USER: calminer + DB_PASSWORD: calminer_password + services: + postgres: + image: postgres:17 + env: + POSTGRES_USER: ${{ env.DB_USER }} + POSTGRES_PASSWORD: ${{ env.DB_PASSWORD }} + POSTGRES_DB: ${{ env.DB_NAME }} + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + steps: + - uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: "3.12" + + - name: Get pip cache dir + id: pip-cache + run: | + echo "path=$(pip cache dir)" >> $GITEA_OUTPUT + echo "Pip cache dir: $(pip cache dir)" + + - name: Cache pip dependencies + uses: actions/cache@v4 + with: + path: ${{ steps.pip-cache.outputs.path }} + key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt', 'requirements-test.txt', 'pyproject.toml') }} + restore-keys: | + ${{ runner.os }}-pip- + + - name: Configure apt proxy + run: | + if [ -n "${APT_CACHER_NG}" ]; then + echo "Acquire::http::Proxy \"${APT_CACHER_NG}\";" | tee /etc/apt/apt.conf.d/01apt-cacher-ng + fi + + - name: Install system packages + run: | + apt-get update + apt-get install -y build-essential libpq-dev - name: Install dependencies run: | @@ -82,15 +135,22 @@ jobs: DATABASE_PASSWORD: ${{ env.DB_PASSWORD }} DATABASE_NAME: ${{ env.DB_NAME }} run: | - pytest tests/ --cov=. + pytest --cov=. --cov-report=term-missing --cov-report=xml --cov-fail-under=80 --junitxml=pytest-report.xml - - name: Build Docker image - run: | - docker build -t ${{ env.REGISTRY_CONTAINER_NAME }} . + - name: Upload test artifacts + if: always() + uses: actions/upload-artifact@v4 + with: + name: test-artifacts + path: | + coverage.xml + pytest-report.xml build: runs-on: ubuntu-latest - needs: test + needs: + - lint + - test env: DEFAULT_BRANCH: main REGISTRY_URL: ${{ secrets.REGISTRY_URL }} @@ -131,12 +191,84 @@ jobs: username: ${{ env.REGISTRY_USERNAME }} password: ${{ env.REGISTRY_PASSWORD }} - - name: Build and push image - uses: docker/build-push-action@v5 + - name: Build image + id: build-image + env: + REGISTRY_URL: ${{ env.REGISTRY_URL }} + REGISTRY_CONTAINER_NAME: ${{ env.REGISTRY_CONTAINER_NAME }} + SHA_TAG: ${{ steps.meta.outputs.sha }} + PUSH_IMAGE: ${{ steps.meta.outputs.on_default == 'true' && steps.meta.outputs.event_name != 'pull_request' && env.REGISTRY_URL != '' && env.REGISTRY_USERNAME != '' && env.REGISTRY_PASSWORD != '' }} + run: | + set -eo pipefail + LOG_FILE=build.log + if [ "${PUSH_IMAGE}" = "true" ]; then + docker buildx build \ + --push \ + --tag "${REGISTRY_URL}/allucanget/${REGISTRY_CONTAINER_NAME}:latest" \ + --tag "${REGISTRY_URL}/allucanget/${REGISTRY_CONTAINER_NAME}:${SHA_TAG}" \ + --file Dockerfile \ + . 2>&1 | tee "${LOG_FILE}" + else + docker buildx build \ + --load \ + --tag "${REGISTRY_CONTAINER_NAME}:ci" \ + --file Dockerfile \ + . 2>&1 | tee "${LOG_FILE}" + fi + + - name: Upload docker build logs + if: failure() + uses: actions/upload-artifact@v4 with: - context: . - file: Dockerfile - push: ${{ steps.meta.outputs.on_default == 'true' && steps.meta.outputs.event_name != 'pull_request' && (env.REGISTRY_URL != '' && env.REGISTRY_USERNAME != '' && env.REGISTRY_PASSWORD != '') }} - tags: | - ${{ env.REGISTRY_URL }}/allucanget/${{ env.REGISTRY_CONTAINER_NAME }}:latest - ${{ env.REGISTRY_URL }}/allucanget/${{ env.REGISTRY_CONTAINER_NAME }}:${{ steps.meta.outputs.sha }} + name: docker-build-logs + path: build.log + + deploy: + runs-on: ubuntu-latest + needs: build + if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request' + env: + REGISTRY_URL: ${{ secrets.REGISTRY_URL }} + REGISTRY_CONTAINER_NAME: calminer + KUBE_CONFIG: ${{ secrets.KUBE_CONFIG }} + STAGING_KUBE_CONFIG: ${{ secrets.STAGING_KUBE_CONFIG }} + PROD_KUBE_CONFIG: ${{ secrets.PROD_KUBE_CONFIG }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up kubectl for staging + if: github.ref == 'refs/heads/main' && contains(github.event.head_commit.message, '[deploy staging]') + uses: azure/k8s-set-context@v3 + with: + method: kubeconfig + kubeconfig: ${{ env.STAGING_KUBE_CONFIG }} + + - name: Set up kubectl for production + if: github.ref == 'refs/heads/main' && contains(github.event.head_commit.message, '[deploy production]') + uses: azure/k8s-set-context@v3 + with: + method: kubeconfig + kubeconfig: ${{ env.PROD_KUBE_CONFIG }} + + - name: Deploy to staging + if: github.ref == 'refs/heads/main' && contains(github.event.head_commit.message, '[deploy staging]') + run: | + # Update image in deployment + kubectl set image deployment/calminer-app calminer=${REGISTRY_URL}/allucanget/${REGISTRY_CONTAINER_NAME}:latest + # Apply any config changes + kubectl apply -f k8s/configmap.yaml + kubectl apply -f k8s/secret.yaml + # Wait for rollout + kubectl rollout status deployment/calminer-app + + - name: Deploy to production + if: github.ref == 'refs/heads/main' && contains(github.event.head_commit.message, '[deploy production]') + run: | + # Update image in deployment + kubectl set image deployment/calminer-app calminer=${REGISTRY_URL}/allucanget/${REGISTRY_CONTAINER_NAME}:latest + # Apply any config changes + kubectl apply -f k8s/configmap.yaml + kubectl apply -f k8s/secret.yaml + # Wait for rollout + kubectl rollout status deployment/calminer-app diff --git a/alembic/env.py b/alembic/env.py index 95ef1b9..bd27753 100644 --- a/alembic/env.py +++ b/alembic/env.py @@ -1,7 +1,6 @@ from __future__ import annotations from logging.config import fileConfig -from typing import Iterable from alembic import context from sqlalchemy import engine_from_config, pool diff --git a/alembic/versions/20251111_01_add_performance_metrics_table.py b/alembic/versions/20251111_01_add_performance_metrics_table.py new file mode 100644 index 0000000..5f51190 --- /dev/null +++ b/alembic/versions/20251111_01_add_performance_metrics_table.py @@ -0,0 +1,38 @@ +"""Add performance_metrics table""" + +from __future__ import annotations + +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = "20251111_01" +down_revision = "20251111_00" +branch_labels = None +depends_on = None + + +def upgrade() -> None: + op.create_table( + "performance_metrics", + sa.Column("id", sa.Integer(), nullable=False), + sa.Column("timestamp", sa.DateTime(), nullable=True), + sa.Column("metric_name", sa.String(), nullable=True), + sa.Column("value", sa.Float(), nullable=True), + sa.Column("labels", sa.String(), nullable=True), + sa.Column("endpoint", sa.String(), nullable=True), + sa.Column("method", sa.String(), nullable=True), + sa.Column("status_code", sa.Integer(), nullable=True), + sa.Column("duration_seconds", sa.Float(), nullable=True), + sa.PrimaryKeyConstraint("id"), + ) + op.create_index(op.f("ix_performance_metrics_timestamp"), "performance_metrics", ["timestamp"], unique=False) + op.create_index(op.f("ix_performance_metrics_metric_name"), "performance_metrics", ["metric_name"], unique=False) + op.create_index(op.f("ix_performance_metrics_endpoint"), "performance_metrics", ["endpoint"], unique=False) + + +def downgrade() -> None: + op.drop_index(op.f("ix_performance_metrics_endpoint"), table_name="performance_metrics") + op.drop_index(op.f("ix_performance_metrics_metric_name"), table_name="performance_metrics") + op.drop_index(op.f("ix_performance_metrics_timestamp"), table_name="performance_metrics") + op.drop_table("performance_metrics") \ No newline at end of file diff --git a/alembic/versions/20251112_00_add_roles_metadata_columns.py b/alembic/versions/20251112_00_add_roles_metadata_columns.py new file mode 100644 index 0000000..d2aaf07 --- /dev/null +++ b/alembic/versions/20251112_00_add_roles_metadata_columns.py @@ -0,0 +1,134 @@ +"""Add metadata columns to roles table""" + +from __future__ import annotations + +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = "20251112_00_add_roles_metadata_columns" +down_revision = "20251111_01" +branch_labels = None +depends_on = None + + +ROLE_BACKFILL = ( + ("admin", "Administrator", "Full platform access with user management rights."), + ( + "project_manager", + "Project Manager", + "Manage projects, scenarios, and associated data.", + ), + ("analyst", "Analyst", "Review dashboards and scenario outputs."), + ( + "viewer", + "Viewer", + "Read-only access to assigned projects and reports.", + ), +) + + +def upgrade() -> None: + op.add_column( + "roles", + sa.Column("display_name", sa.String(length=128), nullable=True), + ) + op.add_column( + "roles", + sa.Column("description", sa.Text(), nullable=True), + ) + op.add_column( + "roles", + sa.Column( + "created_at", + sa.DateTime(timezone=True), + nullable=True, + server_default=sa.text("timezone('UTC', now())"), + ), + ) + op.add_column( + "roles", + sa.Column( + "updated_at", + sa.DateTime(timezone=True), + nullable=True, + server_default=sa.text("timezone('UTC', now())"), + ), + ) + + connection = op.get_bind() + + for name, display_name, description in ROLE_BACKFILL: + connection.execute( + sa.text( + """ + UPDATE roles + SET display_name = :display_name, + description = COALESCE(description, :description) + WHERE name = :name + AND display_name IS NULL + """ + ), + { + "name": name, + "display_name": display_name, + "description": description, + }, + ) + + connection.execute( + sa.text( + """ + UPDATE roles + SET display_name = INITCAP(REPLACE(name, '_', ' ')) + WHERE display_name IS NULL + """ + ) + ) + + connection.execute( + sa.text( + """ + UPDATE roles + SET created_at = timezone('UTC', now()) + WHERE created_at IS NULL + """ + ) + ) + connection.execute( + sa.text( + """ + UPDATE roles + SET updated_at = timezone('UTC', now()) + WHERE updated_at IS NULL + """ + ) + ) + + op.alter_column( + "roles", + "display_name", + existing_type=sa.String(length=128), + nullable=False, + ) + op.alter_column( + "roles", + "created_at", + existing_type=sa.DateTime(timezone=True), + nullable=False, + server_default=sa.text("timezone('UTC', now())"), + ) + op.alter_column( + "roles", + "updated_at", + existing_type=sa.DateTime(timezone=True), + nullable=False, + server_default=sa.text("timezone('UTC', now())"), + ) + + +def downgrade() -> None: + op.drop_column("roles", "updated_at") + op.drop_column("roles", "created_at") + op.drop_column("roles", "description") + op.drop_column("roles", "display_name") diff --git a/changelog.md b/changelog.md index d9c7a34..c443548 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,58 @@ # Changelog +## 2025-11-12 + +- Diagnosed admin bootstrap failure caused by legacy `roles` schema, added Alembic migration `20251112_00_add_roles_metadata_columns.py` to backfill `display_name`, `description`, `created_at`, and `updated_at`, and verified the migration via full pytest run in the activated `.venv`. + +## 2025-11-11 + +- Implemented base URL routing to redirect unauthenticated users to login and authenticated users to dashboard. +- Added comprehensive end-to-end tests for login flow, including redirects, session handling, and error messaging for invalid/inactive accounts. +- Updated header and footer templates to consistently use `logo_big.png` image instead of text logo, with appropriate CSS styling for sizing. +- Centralised ISO-4217 currency validation across scenarios, imports, and export filters (`models/scenario.py`, `routes/scenarios.py`, `schemas/scenario.py`, `schemas/imports.py`, `services/export_query.py`) so malformed codes are rejected consistently at every entry point. +- Updated scenario services and UI flows to surface friendly validation errors and added regression coverage for imports, exports, API creation, and lifecycle flows ensuring currencies are normalised end-to-end. +- Recorded the completed “Ensure currency is used consistently” work in `.github/instructions/DONE.md` and ran the full pytest suite (150 tests) to verify the refactor. +- Linked projects to their pricing settings by updating SQLAlchemy models, repositories, seeding utilities, and migrations, and added regression tests to cover the new association and default backfill. +- Bootstrapped database-stored pricing settings at application startup, aligned initial data seeding with the database-first metadata flow, and added tests covering pricing bootstrap creation, project assignment, and idempotency. +- Extended pricing configuration support to prefer persisted metadata via `dependencies.get_pricing_metadata`, added retrieval tests for project/default fallbacks, and refreshed docs (`calminer-docs/specifications/price_calculation.md`, `pricing_settings_data_model.md`) to describe the database-backed workflow and bootstrap behaviour. +- Added `services/financial.py` NPV, IRR, and payback helpers with robust cash-flow normalisation, convergence safeguards, and fractional period support, plus comprehensive pytest coverage exercising representative project scenarios and failure modes. +- Authored `calminer-docs/specifications/financial_metrics.md` capturing DCF assumptions, solver behaviours, and worked examples, and cross-linked the architecture concepts to the new reference for consistent navigation. +- Implemented `services/simulation.py` Monte Carlo engine with configurable distributions, summary aggregation, and reproducible RNG seeding, introduced regression tests in `tests/test_simulation.py`, and documented configuration/usage in `calminer-docs/specifications/monte_carlo_simulation.md` with architecture cross-links. +- Polished reporting HTML contexts by cleaning stray fragments in `routes/reports.py`, adding download action metadata for project and scenario pages, and generating scenario comparison download URLs with correctly serialised repeated `scenario_ids` parameters. +- Consolidated Alembic history into a single initial migration (`20251111_00_initial_schema.py`), removed superseded revision files, and ensured Alembic metadata still references the project metadata for clean bootstrap. +- Added `scripts/run_migrations.py` and a Docker entrypoint wrapper to run Alembic migrations before `uvicorn` starts, removed the fallback `Base.metadata.create_all` call, and updated `calminer-docs/admin/installation.md` so developers know how to apply migrations locally or via Docker. +- Configured pytest defaults to collect coverage (`--cov`) with an 80% fail-under gate, excluded entrypoint/reporting scaffolds from the calculation, updated contributor docs with the standard `pytest` command, and verified the suite now reports 83% coverage. +- Standardized color scheme and typography by moving alert styles to `main.css`, adding typography rules with CSS variables, updating auth templates for consistent button classes, and ensuring all templates use centralized color and spacing variables. +- Improved navigation flow by adding two big chevron buttons on top of the navigation sidebar to allow users to navigate to the previous and next page in the page navigation list, including JavaScript logic for determining current page and handling navigation. +- Established pytest-based unit and integration test suites with coverage thresholds, achieving 83% coverage across 181 tests, with configuration in pyproject.toml and documentation in CONTRIBUTING.md. +- Configured CI pipelines to run tests, linting, and security checks on each change, adding Bandit security scanning to the workflow and verifying execution on pushes and PRs to main/develop branches. +- Added deployment automation with Docker Compose for local development and Kubernetes manifests for production, ensuring environment parity and documenting processes in calminer-docs/admin/installation.md. +- Completed monitoring instrumentation by adding business metrics observation to project and scenario repository operations, and simulation performance tracking to Monte Carlo service with success/error status and duration metrics. +- Updated TODO list to reflect completed monitoring implementation tasks and validated changes with passing simulation tests. +- Implemented comprehensive performance monitoring for scalability (FR-006) with Prometheus metrics collection for HTTP requests, import/export operations, and general application metrics. +- Added database model for persistent metric storage with aggregation endpoints for KPIs like request latency, error rates, and throughput. +- Created FastAPI middleware for automatic request metric collection and background persistence to database. +- Extended monitoring router with performance metrics API endpoints and detailed health checks. +- Added Alembic migration for performance_metrics table and updated model imports. +- Completed concurrent interaction testing implementation, validating database transaction isolation under threading and establishing async testing framework for future concurrency enhancements. +- Implemented comprehensive deployment automation with Docker Compose configurations for development, staging, and production environments ensuring environment parity. +- Set up Kubernetes manifests with resource limits, health checks, and secrets management for production deployment. +- Configured CI/CD workflows for automated Docker image building, registry pushing, and Kubernetes deployment to staging/production environments. +- Documented deployment processes, environment configurations, and CI/CD workflows in project documentation. +- Validated deployment automation through Docker Compose configuration testing and CI/CD pipeline structure. + +## 2025-11-10 + +- Added dedicated pytest coverage for guard dependencies, exercising success plus failure paths (missing session, inactive user, missing roles, project/scenario access errors) via `tests/test_dependencies_guards.py`. +- Added integration tests in `tests/test_authorization_integration.py` verifying anonymous 401 responses, role-based 403s, and authorized project manager flows across API and UI endpoints. +- Implemented environment-driven admin bootstrap settings, wired the `bootstrap_admin` helper into FastAPI startup, added pytest coverage for creation/idempotency/reset logic, and documented operational guidance in the RBAC plan and security concept. +- Retired the legacy authentication RBAC implementation plan document after migrating its guidance into live documentation and synchronized the contributor instructions to reflect the removal. +- Completed the Authentication & RBAC checklist by shipping the new models, migrations, repositories, guard dependencies, and integration tests. +- Documented the project/scenario import/export field mapping and file format guidelines in `calminer-docs/requirements/FR-008.md`, and introduced `schemas/imports.py` with Pydantic models that normalise incoming CSV/Excel rows for projects and scenarios. +- Added `services/importers.py` to load CSV/XLSX files into the new import schemas, pulled in `openpyxl` for Excel support, and covered the parsing behaviour with `tests/test_import_parsing.py`. +- Expanded the import ingestion workflow with staging previews, transactional persistence commits, FastAPI preview/commit endpoints under `/imports`, and new API tests (`tests/test_import_ingestion.py`, `tests/test_import_api.py`) ensuring end-to-end coverage. +- Added persistent audit logging via `ImportExportLog`, structured log emission, Prometheus metrics instrumentation, `/metrics` endpoint exposure, and updated operator/deployment documentation to guide monitoring setup. + ## 2025-11-09 - Captured current implementation status, requirements coverage, missing features, and prioritized roadmap in `calminer-docs/implementation_status.md` to guide future development. @@ -21,31 +74,3 @@ - Implemented cookie-based authentication session middleware with automatic access token refresh, logout handling, navigation adjustments, and documentation/test updates capturing the new behaviour. - Delivered idempotent seeding utilities with `scripts/initial_data.py`, entry-point runner `scripts/00_initial_data.py`, documentation updates, and pytest coverage to verify role/admin provisioning. - Secured project and scenario routers with RBAC guard dependencies, enforced repository access checks via helper utilities, and aligned template routes with FastAPI dependency injection patterns. - -## 2025-11-10 - -- Added dedicated pytest coverage for guard dependencies, exercising success plus failure paths (missing session, inactive user, missing roles, project/scenario access errors) via `tests/test_dependencies_guards.py`. -- Added integration tests in `tests/test_authorization_integration.py` verifying anonymous 401 responses, role-based 403s, and authorized project manager flows across API and UI endpoints. -- Implemented environment-driven admin bootstrap settings, wired the `bootstrap_admin` helper into FastAPI startup, added pytest coverage for creation/idempotency/reset logic, and documented operational guidance in the RBAC plan and security concept. -- Retired the legacy authentication RBAC implementation plan document after migrating its guidance into live documentation and synchronized the contributor instructions to reflect the removal. -- Completed the Authentication & RBAC checklist by shipping the new models, migrations, repositories, guard dependencies, and integration tests. -- Documented the project/scenario import/export field mapping and file format guidelines in `calminer-docs/requirements/FR-008.md`, and introduced `schemas/imports.py` with Pydantic models that normalise incoming CSV/Excel rows for projects and scenarios. -- Added `services/importers.py` to load CSV/XLSX files into the new import schemas, pulled in `openpyxl` for Excel support, and covered the parsing behaviour with `tests/test_import_parsing.py`. -- Expanded the import ingestion workflow with staging previews, transactional persistence commits, FastAPI preview/commit endpoints under `/imports`, and new API tests (`tests/test_import_ingestion.py`, `tests/test_import_api.py`) ensuring end-to-end coverage. -- Added persistent audit logging via `ImportExportLog`, structured log emission, Prometheus metrics instrumentation, `/metrics` endpoint exposure, and updated operator/deployment documentation to guide monitoring setup. - -## 2025-11-11 - -- Centralised ISO-4217 currency validation across scenarios, imports, and export filters (`models/scenario.py`, `routes/scenarios.py`, `schemas/scenario.py`, `schemas/imports.py`, `services/export_query.py`) so malformed codes are rejected consistently at every entry point. -- Updated scenario services and UI flows to surface friendly validation errors and added regression coverage for imports, exports, API creation, and lifecycle flows ensuring currencies are normalised end-to-end. -- Recorded the completed “Ensure currency is used consistently” work in `.github/instructions/DONE.md` and ran the full pytest suite (150 tests) to verify the refactor. -- Linked projects to their pricing settings by updating SQLAlchemy models, repositories, seeding utilities, and migrations, and added regression tests to cover the new association and default backfill. -- Bootstrapped database-stored pricing settings at application startup, aligned initial data seeding with the database-first metadata flow, and added tests covering pricing bootstrap creation, project assignment, and idempotency. -- Extended pricing configuration support to prefer persisted metadata via `dependencies.get_pricing_metadata`, added retrieval tests for project/default fallbacks, and refreshed docs (`calminer-docs/specifications/price_calculation.md`, `pricing_settings_data_model.md`) to describe the database-backed workflow and bootstrap behaviour. -- Added `services/financial.py` NPV, IRR, and payback helpers with robust cash-flow normalisation, convergence safeguards, and fractional period support, plus comprehensive pytest coverage exercising representative project scenarios and failure modes. -- Authored `calminer-docs/specifications/financial_metrics.md` capturing DCF assumptions, solver behaviours, and worked examples, and cross-linked the architecture concepts to the new reference for consistent navigation. -- Implemented `services/simulation.py` Monte Carlo engine with configurable distributions, summary aggregation, and reproducible RNG seeding, introduced regression tests in `tests/test_simulation.py`, and documented configuration/usage in `calminer-docs/specifications/monte_carlo_simulation.md` with architecture cross-links. -- Polished reporting HTML contexts by cleaning stray fragments in `routes/reports.py`, adding download action metadata for project and scenario pages, and generating scenario comparison download URLs with correctly serialised repeated `scenario_ids` parameters. -- Consolidated Alembic history into a single initial migration (`20251111_00_initial_schema.py`), removed superseded revision files, and ensured Alembic metadata still references the project metadata for clean bootstrap. -- Added `scripts/run_migrations.py` and a Docker entrypoint wrapper to run Alembic migrations before `uvicorn` starts, removed the fallback `Base.metadata.create_all` call, and updated `calminer-docs/admin/installation.md` so developers know how to apply migrations locally or via Docker. -- Configured pytest defaults to collect coverage (`--cov`) with an 80% fail-under gate, excluded entrypoint/reporting scaffolds from the calculation, updated contributor docs with the standard `pytest` command, and verified the suite now reports 83% coverage. diff --git a/docker-compose.override.yml b/docker-compose.override.yml new file mode 100644 index 0000000..b9dbe81 --- /dev/null +++ b/docker-compose.override.yml @@ -0,0 +1,60 @@ +version: "3.8" + +services: + app: + build: + context: . + dockerfile: Dockerfile + args: + APT_CACHE_URL: ${APT_CACHE_URL:-} + environment: + - ENVIRONMENT=development + - DEBUG=true + - LOG_LEVEL=DEBUG + # Override database to use local postgres service + - DATABASE_HOST=postgres + - DATABASE_PORT=5432 + - DATABASE_USER=calminer + - DATABASE_PASSWORD=calminer_password + - DATABASE_NAME=calminer_db + - DATABASE_DRIVER=postgresql + # Development-specific settings + - CALMINER_EXPORT_MAX_ROWS=1000 + - CALMINER_IMPORT_MAX_ROWS=10000 + volumes: + # Mount source code for live reloading (if using --reload) + - .:/app:ro + # Override logs volume to local for easier access + - ./logs:/app/logs + ports: + - "8003:8003" + # Override command for development with reload + command: + [ + "uvicorn", + "main:app", + "--host", + "0.0.0.0", + "--port", + "8003", + "--reload", + "--workers", + "1", + ] + depends_on: + - postgres + restart: unless-stopped + + postgres: + environment: + - POSTGRES_USER=calminer + - POSTGRES_PASSWORD=calminer_password + - POSTGRES_DB=calminer_db + ports: + - "5432:5432" + volumes: + - postgres_data:/var/lib/postgresql/data + restart: unless-stopped + +volumes: + postgres_data: diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 0000000..cd3e264 --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,77 @@ +version: "3.8" + +services: + app: + build: + context: . + dockerfile: Dockerfile + args: + APT_CACHE_URL: ${APT_CACHE_URL:-} + environment: + - ENVIRONMENT=production + - DEBUG=false + - LOG_LEVEL=WARNING + # Database configuration - must be provided externally + - DATABASE_HOST=${DATABASE_HOST} + - DATABASE_PORT=${DATABASE_PORT:-5432} + - DATABASE_USER=${DATABASE_USER} + - DATABASE_PASSWORD=${DATABASE_PASSWORD} + - DATABASE_NAME=${DATABASE_NAME} + - DATABASE_DRIVER=postgresql + # Production-specific settings + - CALMINER_EXPORT_MAX_ROWS=100000 + - CALMINER_IMPORT_MAX_ROWS=100000 + - CALMINER_EXPORT_METADATA=true + - CALMINER_IMPORT_STAGING_TTL=3600 + ports: + - "8003:8003" + depends_on: + postgres: + condition: service_healthy + restart: unless-stopped + # Production health checks + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8003/health"] + interval: 60s + timeout: 30s + retries: 5 + start_period: 60s + # Resource limits for production + deploy: + resources: + limits: + cpus: "1.0" + memory: 1G + reservations: + cpus: "0.5" + memory: 512M + + postgres: + environment: + - POSTGRES_USER=${DATABASE_USER} + - POSTGRES_PASSWORD=${DATABASE_PASSWORD} + - POSTGRES_DB=${DATABASE_NAME} + ports: + - "5432:5432" + volumes: + - postgres_data:/var/lib/postgresql/data + restart: unless-stopped + # Production postgres health check + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${DATABASE_USER} -d ${DATABASE_NAME}"] + interval: 60s + timeout: 30s + retries: 5 + start_period: 60s + # Resource limits for postgres + deploy: + resources: + limits: + cpus: "1.0" + memory: 2G + reservations: + cpus: "0.5" + memory: 1G + +volumes: + postgres_data: diff --git a/docker-compose.staging.yml b/docker-compose.staging.yml new file mode 100644 index 0000000..f75682b --- /dev/null +++ b/docker-compose.staging.yml @@ -0,0 +1,62 @@ +version: "3.8" + +services: + app: + build: + context: . + dockerfile: Dockerfile + args: + APT_CACHE_URL: ${APT_CACHE_URL:-} + environment: + - ENVIRONMENT=staging + - DEBUG=false + - LOG_LEVEL=INFO + # Database configuration - can be overridden by external env + - DATABASE_HOST=${DATABASE_HOST:-postgres} + - DATABASE_PORT=${DATABASE_PORT:-5432} + - DATABASE_USER=${DATABASE_USER:-calminer} + - DATABASE_PASSWORD=${DATABASE_PASSWORD} + - DATABASE_NAME=${DATABASE_NAME:-calminer_db} + - DATABASE_DRIVER=postgresql + # Staging-specific settings + - CALMINER_EXPORT_MAX_ROWS=50000 + - CALMINER_IMPORT_MAX_ROWS=50000 + - CALMINER_EXPORT_METADATA=true + - CALMINER_IMPORT_STAGING_TTL=600 + ports: + - "8003:8003" + depends_on: + - postgres + restart: unless-stopped + # Health check for staging + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8003/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + + postgres: + environment: + - POSTGRES_USER=${DATABASE_USER:-calminer} + - POSTGRES_PASSWORD=${DATABASE_PASSWORD} + - POSTGRES_DB=${DATABASE_NAME:-calminer_db} + ports: + - "5432:5432" + volumes: + - postgres_data:/var/lib/postgresql/data + restart: unless-stopped + # Health check for postgres + healthcheck: + test: + [ + "CMD-SHELL", + "pg_isready -U ${DATABASE_USER:-calminer} -d ${DATABASE_NAME:-calminer_db}", + ] + interval: 30s + timeout: 10s + retries: 3 + start_period: 30s + +volumes: + postgres_data: diff --git a/docker-compose.yml b/docker-compose.yml index 9680f1e..f983cf8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,11 +8,13 @@ services: ports: - "8003:8003" environment: - - DATABASE_HOST=postgres - - DATABASE_PORT=5432 - - DATABASE_USER=calminer - - DATABASE_PASSWORD=calminer_password - - DATABASE_NAME=calminer_db + # Environment-specific variables should be set in override files + - ENVIRONMENT=${ENVIRONMENT:-production} + - DATABASE_HOST=${DATABASE_HOST:-postgres} + - DATABASE_PORT=${DATABASE_PORT:-5432} + - DATABASE_USER=${DATABASE_USER} + - DATABASE_PASSWORD=${DATABASE_PASSWORD} + - DATABASE_NAME=${DATABASE_NAME} - DATABASE_DRIVER=postgresql depends_on: - postgres @@ -23,9 +25,9 @@ services: postgres: image: postgres:17 environment: - - POSTGRES_USER=calminer - - POSTGRES_PASSWORD=calminer_password - - POSTGRES_DB=calminer_db + - POSTGRES_USER=${DATABASE_USER} + - POSTGRES_PASSWORD=${DATABASE_PASSWORD} + - POSTGRES_DB=${DATABASE_NAME} ports: - "5432:5432" volumes: diff --git a/k8s/configmap.yaml b/k8s/configmap.yaml new file mode 100644 index 0000000..8773639 --- /dev/null +++ b/k8s/configmap.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: calminer-config +data: + DATABASE_HOST: "calminer-db" + DATABASE_PORT: "5432" + DATABASE_USER: "calminer" + DATABASE_NAME: "calminer_db" + DATABASE_DRIVER: "postgresql" + CALMINER_EXPORT_MAX_ROWS: "10000" + CALMINER_EXPORT_METADATA: "true" + CALMINER_IMPORT_STAGING_TTL: "300" + CALMINER_IMPORT_MAX_ROWS: "50000" diff --git a/k8s/deployment.yaml b/k8s/deployment.yaml new file mode 100644 index 0000000..c15682c --- /dev/null +++ b/k8s/deployment.yaml @@ -0,0 +1,54 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: calminer-app + labels: + app: calminer +spec: + replicas: 3 + selector: + matchLabels: + app: calminer + template: + metadata: + labels: + app: calminer + spec: + containers: + - name: calminer + image: registry.example.com/calminer:latest + ports: + - containerPort: 8003 + envFrom: + - configMapRef: + name: calminer-config + - secretRef: + name: calminer-secrets + resources: + requests: + memory: "256Mi" + cpu: "250m" + limits: + memory: "512Mi" + cpu: "500m" + livenessProbe: + httpGet: + path: /health + port: 8003 + initialDelaySeconds: 30 + periodSeconds: 10 + readinessProbe: + httpGet: + path: /health + port: 8003 + initialDelaySeconds: 5 + periodSeconds: 5 + initContainers: + - name: wait-for-db + image: postgres:17 + command: + [ + "sh", + "-c", + "until pg_isready -h calminer-db -p 5432; do echo waiting for database; sleep 2; done;", + ] diff --git a/k8s/ingress.yaml b/k8s/ingress.yaml new file mode 100644 index 0000000..36a738a --- /dev/null +++ b/k8s/ingress.yaml @@ -0,0 +1,18 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: calminer-ingress + annotations: + nginx.ingress.kubernetes.io/rewrite-target: / +spec: + rules: + - host: calminer.example.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: calminer-service + port: + number: 80 diff --git a/k8s/postgres-service.yaml b/k8s/postgres-service.yaml new file mode 100644 index 0000000..05eeb45 --- /dev/null +++ b/k8s/postgres-service.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: calminer-db + labels: + app: calminer-db +spec: + selector: + app: calminer-db + ports: + - port: 5432 + targetPort: 5432 + clusterIP: None # Headless service for StatefulSet diff --git a/k8s/postgres.yaml b/k8s/postgres.yaml new file mode 100644 index 0000000..2e6c29c --- /dev/null +++ b/k8s/postgres.yaml @@ -0,0 +1,48 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: calminer-db +spec: + serviceName: calminer-db + replicas: 1 + selector: + matchLabels: + app: calminer-db + template: + metadata: + labels: + app: calminer-db + spec: + containers: + - name: postgres + image: postgres:17 + ports: + - containerPort: 5432 + env: + - name: POSTGRES_USER + value: "calminer" + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: calminer-secrets + key: DATABASE_PASSWORD + - name: POSTGRES_DB + value: "calminer_db" + resources: + requests: + memory: "256Mi" + cpu: "250m" + limits: + memory: "512Mi" + cpu: "500m" + volumeMounts: + - name: postgres-storage + mountPath: /var/lib/postgresql/data + volumeClaimTemplates: + - metadata: + name: postgres-storage + spec: + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 10Gi diff --git a/k8s/secret.yaml b/k8s/secret.yaml new file mode 100644 index 0000000..f49ae32 --- /dev/null +++ b/k8s/secret.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: Secret +metadata: + name: calminer-secrets +type: Opaque +data: + DATABASE_PASSWORD: Y2FsbWluZXJfcGFzc3dvcmQ= # base64 encoded 'calminer_password' + CALMINER_SEED_ADMIN_PASSWORD: Q2hhbmdlTWUxMjMh # base64 encoded 'ChangeMe123!' diff --git a/k8s/service.yaml b/k8s/service.yaml new file mode 100644 index 0000000..de72195 --- /dev/null +++ b/k8s/service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + name: calminer-service + labels: + app: calminer +spec: + selector: + app: calminer + ports: + - port: 80 + targetPort: 8003 + protocol: TCP + type: ClusterIP diff --git a/main.py b/main.py index 5c791cc..dac4e92 100644 --- a/main.py +++ b/main.py @@ -6,13 +6,8 @@ from fastapi.staticfiles import StaticFiles from config.settings import get_settings from middleware.auth_session import AuthSessionMiddleware +from middleware.metrics import MetricsMiddleware from middleware.validation import validate_json -from models import ( - FinancialInput, - Project, - Scenario, - SimulationParameter, -) from routes.auth import router as auth_router from routes.dashboard import router as dashboard_router from routes.imports import router as imports_router @@ -26,6 +21,7 @@ from services.bootstrap import bootstrap_admin, bootstrap_pricing_settings app = FastAPI() app.add_middleware(AuthSessionMiddleware) +app.add_middleware(MetricsMiddleware) logger = logging.getLogger(__name__) diff --git a/middleware/auth_session.py b/middleware/auth_session.py index fb2cb50..5c94726 100644 --- a/middleware/auth_session.py +++ b/middleware/auth_session.py @@ -9,6 +9,7 @@ from starlette.types import ASGIApp from config.settings import Settings, get_settings from models import User +from monitoring.metrics import ACTIVE_CONNECTIONS from services.exceptions import EntityNotFoundError from services.security import ( JWTSettings, @@ -45,6 +46,8 @@ class _ResolutionResult: class AuthSessionMiddleware(BaseHTTPMiddleware): """Resolve authenticated users from session cookies and refresh tokens.""" + _active_sessions: int = 0 + def __init__( self, app: ASGIApp, @@ -61,9 +64,23 @@ class AuthSessionMiddleware(BaseHTTPMiddleware): async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -> Response: resolved = self._resolve_session(request) - response = await call_next(request) - self._apply_session(response, resolved) - return response + + # Track active sessions for authenticated users + if resolved.session.user and resolved.session.user.is_active: + AuthSessionMiddleware._active_sessions += 1 + ACTIVE_CONNECTIONS.set(AuthSessionMiddleware._active_sessions) + + try: + response = await call_next(request) + return response + finally: + # Decrement on response + if resolved.session.user and resolved.session.user.is_active: + AuthSessionMiddleware._active_sessions = max( + 0, AuthSessionMiddleware._active_sessions - 1) + ACTIVE_CONNECTIONS.set(AuthSessionMiddleware._active_sessions) + + self._apply_session(response, resolved) def _resolve_session(self, request: Request) -> _ResolutionResult: settings = self._settings_provider() diff --git a/middleware/metrics.py b/middleware/metrics.py new file mode 100644 index 0000000..dd4071a --- /dev/null +++ b/middleware/metrics.py @@ -0,0 +1,58 @@ +from __future__ import annotations + +import time +from typing import Callable + +from fastapi import BackgroundTasks, Request, Response +from starlette.middleware.base import BaseHTTPMiddleware + +from monitoring.metrics import observe_request +from services.metrics import get_metrics_service + + +class MetricsMiddleware(BaseHTTPMiddleware): + async def dispatch(self, request: Request, call_next: Callable[[Request], Response]) -> Response: + start_time = time.time() + response = await call_next(request) + process_time = time.time() - start_time + + observe_request( + method=request.method, + endpoint=request.url.path, + status=response.status_code, + seconds=process_time, + ) + + # Store in database asynchronously + background_tasks = getattr(request.state, "background_tasks", None) + if background_tasks: + background_tasks.add_task( + store_request_metric, + method=request.method, + endpoint=request.url.path, + status_code=response.status_code, + duration_seconds=process_time, + ) + + return response + + +async def store_request_metric( + method: str, endpoint: str, status_code: int, duration_seconds: float +) -> None: + """Store request metric in database.""" + try: + service = get_metrics_service() + service.store_metric( + metric_name="http_request", + value=duration_seconds, + labels={"method": method, "endpoint": endpoint, + "status": status_code}, + endpoint=endpoint, + method=method, + status_code=status_code, + duration_seconds=duration_seconds, + ) + except Exception: + # Log error but don't fail the request + pass diff --git a/models/__init__.py b/models/__init__.py index 58b518a..fc0c427 100644 --- a/models/__init__.py +++ b/models/__init__.py @@ -11,6 +11,7 @@ from .metadata import ( StochasticVariable, StochasticVariableDescriptor, ) +from .performance_metric import PerformanceMetric from .pricing_settings import ( PricingImpuritySettings, PricingMetalSettings, @@ -45,4 +46,5 @@ __all__ = [ "Role", "UserRole", "password_context", + "PerformanceMetric", ] diff --git a/models/financial_input.py b/models/financial_input.py index fbcf69b..8a0d0e3 100644 --- a/models/financial_input.py +++ b/models/financial_input.py @@ -16,17 +16,6 @@ from sqlalchemy import ( ) from sqlalchemy.orm import Mapped, mapped_column, relationship, validates -from sqlalchemy import ( - Date, - DateTime, - Enum as SQLEnum, - ForeignKey, - Integer, - Numeric, - String, - Text, -) -from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.sql import func from config.database import Base diff --git a/models/import_export_log.py b/models/import_export_log.py index 4a5592a..026a345 100644 --- a/models/import_export_log.py +++ b/models/import_export_log.py @@ -1,6 +1,5 @@ from __future__ import annotations -from datetime import datetime from sqlalchemy import Column, DateTime, ForeignKey, Integer, String, Text from sqlalchemy.sql import func diff --git a/models/performance_metric.py b/models/performance_metric.py new file mode 100644 index 0000000..5d8ccec --- /dev/null +++ b/models/performance_metric.py @@ -0,0 +1,26 @@ +from __future__ import annotations + +from datetime import datetime +from typing import Optional + +from sqlalchemy import Column, DateTime, Float, Integer, String +from sqlalchemy.ext.declarative import declarative_base + +Base = declarative_base() + + +class PerformanceMetric(Base): + __tablename__ = "performance_metrics" + + id = Column(Integer, primary_key=True, index=True) + timestamp = Column(DateTime, default=datetime.utcnow, index=True) + metric_name = Column(String, index=True) + value = Column(Float) + labels = Column(String) # JSON string of labels + endpoint = Column(String, index=True, nullable=True) + method = Column(String, nullable=True) + status_code = Column(Integer, nullable=True) + duration_seconds = Column(Float, nullable=True) + + def __repr__(self) -> str: + return f"" diff --git a/monitoring/__init__.py b/monitoring/__init__.py index 64edf71..c90e820 100644 --- a/monitoring/__init__.py +++ b/monitoring/__init__.py @@ -1,7 +1,14 @@ from __future__ import annotations -from fastapi import APIRouter, Response +from datetime import datetime, timedelta +from typing import Optional + +from fastapi import APIRouter, Depends, HTTPException, Query, Response from prometheus_client import CONTENT_TYPE_LATEST, generate_latest +from sqlalchemy.orm import Session + +from config.database import get_db +from services.metrics import MetricsService router = APIRouter(prefix="/metrics", tags=["monitoring"]) @@ -11,3 +18,99 @@ router = APIRouter(prefix="/metrics", tags=["monitoring"]) async def metrics_endpoint() -> Response: payload = generate_latest() return Response(content=payload, media_type=CONTENT_TYPE_LATEST) + + +@router.get("/performance", summary="Get performance metrics") +async def get_performance_metrics( + metric_name: Optional[str] = Query( + None, description="Filter by metric name"), + hours: int = Query(24, description="Hours back to look"), + db: Session = Depends(get_db), +) -> dict: + """Get aggregated performance metrics.""" + service = MetricsService(db) + start_time = datetime.utcnow() - timedelta(hours=hours) + + if metric_name: + metrics = service.get_metrics( + metric_name=metric_name, start_time=start_time) + aggregated = service.get_aggregated_metrics( + metric_name, start_time=start_time) + return { + "metric_name": metric_name, + "period_hours": hours, + "aggregated": aggregated, + "recent_samples": [ + { + "timestamp": m.timestamp.isoformat(), + "value": m.value, + "labels": m.labels, + "endpoint": m.endpoint, + "method": m.method, + "status_code": m.status_code, + "duration_seconds": m.duration_seconds, + } + for m in metrics[:50] # Last 50 samples + ], + } + + # Return summary for all metrics + all_metrics = service.get_metrics(start_time=start_time, limit=1000) + metric_types = {} + for m in all_metrics: + if m.metric_name not in metric_types: + metric_types[m.metric_name] = [] + metric_types[m.metric_name].append(m.value) + + summary = {} + for name, values in metric_types.items(): + summary[name] = { + "count": len(values), + "avg": sum(values) / len(values) if values else 0, + "min": min(values) if values else 0, + "max": max(values) if values else 0, + } + + return { + "period_hours": hours, + "summary": summary, + } + + +@router.get("/health", summary="Detailed health check with metrics") +async def detailed_health(db: Session = Depends(get_db)) -> dict: + """Get detailed health status with recent metrics.""" + service = MetricsService(db) + last_hour = datetime.utcnow() - timedelta(hours=1) + + # Get request metrics from last hour + request_metrics = service.get_metrics( + metric_name="http_request", start_time=last_hour + ) + + if request_metrics: + durations = [] + error_count = 0 + for m in request_metrics: + if m.duration_seconds is not None: + durations.append(m.duration_seconds) + if m.status_code is not None and m.status_code >= 400: + error_count += 1 + total_requests = len(request_metrics) + + avg_duration = sum(durations) / len(durations) if durations else 0 + error_rate = error_count / total_requests if total_requests > 0 else 0 + else: + avg_duration = 0 + error_rate = 0 + total_requests = 0 + + return { + "status": "ok", + "timestamp": datetime.utcnow().isoformat(), + "metrics": { + "requests_last_hour": total_requests, + "avg_response_time_seconds": avg_duration, + "error_rate": error_rate, + }, + } diff --git a/monitoring/metrics.py b/monitoring/metrics.py index a38787b..9ca5ce2 100644 --- a/monitoring/metrics.py +++ b/monitoring/metrics.py @@ -1,8 +1,7 @@ from __future__ import annotations -from typing import Iterable -from prometheus_client import Counter, Histogram +from prometheus_client import Counter, Histogram, Gauge IMPORT_DURATION = Histogram( "calminer_import_duration_seconds", @@ -28,6 +27,54 @@ EXPORT_TOTAL = Counter( labelnames=("dataset", "status", "format"), ) +# General performance metrics +REQUEST_DURATION = Histogram( + "calminer_request_duration_seconds", + "Duration of HTTP requests", + labelnames=("method", "endpoint", "status"), +) + +REQUEST_TOTAL = Counter( + "calminer_request_total", + "Count of HTTP requests", + labelnames=("method", "endpoint", "status"), +) + +ACTIVE_CONNECTIONS = Gauge( + "calminer_active_connections", + "Number of active connections", +) + +DB_CONNECTIONS = Gauge( + "calminer_db_connections", + "Number of database connections", +) + +# Business metrics +PROJECT_OPERATIONS = Counter( + "calminer_project_operations_total", + "Count of project operations", + labelnames=("operation", "status"), +) + +SCENARIO_OPERATIONS = Counter( + "calminer_scenario_operations_total", + "Count of scenario operations", + labelnames=("operation", "status"), +) + +SIMULATION_RUNS = Counter( + "calminer_simulation_runs_total", + "Count of Monte Carlo simulation runs", + labelnames=("status",), +) + +SIMULATION_DURATION = Histogram( + "calminer_simulation_duration_seconds", + "Duration of Monte Carlo simulations", + labelnames=("status",), +) + def observe_import(action: str, dataset: str, status: str, seconds: float) -> None: IMPORT_TOTAL.labels(dataset=dataset, action=action, status=status).inc() @@ -40,3 +87,22 @@ def observe_export(dataset: str, status: str, export_format: str, seconds: float format=export_format).inc() EXPORT_DURATION.labels(dataset=dataset, status=status, format=export_format).observe(seconds) + + +def observe_request(method: str, endpoint: str, status: int, seconds: float) -> None: + REQUEST_TOTAL.labels(method=method, endpoint=endpoint, status=status).inc() + REQUEST_DURATION.labels(method=method, endpoint=endpoint, + status=status).observe(seconds) + + +def observe_project_operation(operation: str, status: str = "success") -> None: + PROJECT_OPERATIONS.labels(operation=operation, status=status).inc() + + +def observe_scenario_operation(operation: str, status: str = "success") -> None: + SCENARIO_OPERATIONS.labels(operation=operation, status=status).inc() + + +def observe_simulation(status: str, duration_seconds: float) -> None: + SIMULATION_RUNS.labels(status=status).inc() + SIMULATION_DURATION.labels(status=status).observe(duration_seconds) diff --git a/pyproject.toml b/pyproject.toml index 1a42f43..621af12 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,6 +18,9 @@ exclude = ''' pythonpath = ["."] testpaths = ["tests"] addopts = "-ra --strict-config --strict-markers --cov=. --cov-report=term-missing --cov-report=xml --cov-fail-under=80" +markers = [ + "asyncio: marks tests as async (using pytest-asyncio)", +] [tool.coverage.run] branch = true @@ -35,3 +38,7 @@ omit = [ skip_empty = true show_missing = true +[tool.bandit] +exclude_dirs = ["tests", "alembic", "scripts"] +skips = ["B101", "B601"] # B101: assert_used, B601: shell_injection (may be false positives) + diff --git a/requirements-test.txt b/requirements-test.txt index 1e96b46..691bdcc 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,7 +1,9 @@ pytest +pytest-asyncio pytest-cov pytest-httpx python-jose ruff black -mypy \ No newline at end of file +mypy +bandit \ No newline at end of file diff --git a/routes/dashboard.py b/routes/dashboard.py index d6ecdd3..89de8cc 100644 --- a/routes/dashboard.py +++ b/routes/dashboard.py @@ -3,10 +3,10 @@ from __future__ import annotations from datetime import datetime from fastapi import APIRouter, Depends, Request -from fastapi.responses import HTMLResponse +from fastapi.responses import HTMLResponse, RedirectResponse from fastapi.templating import Jinja2Templates -from dependencies import get_unit_of_work, require_authenticated_user +from dependencies import get_current_user, get_unit_of_work from models import ScenarioStatus, User from services.unit_of_work import UnitOfWork @@ -108,12 +108,15 @@ def _load_scenario_alerts( return alerts -@router.get("/", response_class=HTMLResponse, include_in_schema=False, name="dashboard.home") +@router.get("/", include_in_schema=False, name="dashboard.home", response_model=None) def dashboard_home( request: Request, - _: User = Depends(require_authenticated_user), + user: User | None = Depends(get_current_user), uow: UnitOfWork = Depends(get_unit_of_work), -) -> HTMLResponse: +) -> HTMLResponse | RedirectResponse: + if user is None: + return RedirectResponse(request.url_for("auth.login_form"), status_code=303) + context = { "metrics": _load_metrics(uow), "recent_projects": _load_recent_projects(uow), diff --git a/routes/projects.py b/routes/projects.py index fbade1b..a1ccf7b 100644 --- a/routes/projects.py +++ b/routes/projects.py @@ -15,7 +15,7 @@ from dependencies import ( ) from models import MiningOperationType, Project, ScenarioStatus, User from schemas.project import ProjectCreate, ProjectRead, ProjectUpdate -from services.exceptions import EntityConflictError, EntityNotFoundError +from services.exceptions import EntityConflictError from services.pricing import PricingMetadata from services.unit_of_work import UnitOfWork @@ -138,7 +138,7 @@ def create_project_submit( try: op_type = MiningOperationType(operation_type) - except ValueError as exc: + except ValueError: return templates.TemplateResponse( request, "projects/form.html", @@ -160,7 +160,7 @@ def create_project_submit( ) try: created = _require_project_repo(uow).create(project) - except EntityConflictError as exc: + except EntityConflictError: return templates.TemplateResponse( request, "projects/form.html", @@ -303,7 +303,7 @@ def edit_project_submit( if operation_type: try: project.operation_type = MiningOperationType(operation_type) - except ValueError as exc: + except ValueError: return templates.TemplateResponse( request, "projects/form.html", diff --git a/routes/reports.py b/routes/reports.py index eea21d1..e75bf1c 100644 --- a/routes/reports.py +++ b/routes/reports.py @@ -1,6 +1,6 @@ from __future__ import annotations -from datetime import date +from datetime import date, datetime from urllib.parse import urlencode from fastapi import APIRouter, Depends, HTTPException, Query, Request, status @@ -12,7 +12,6 @@ from dependencies import ( get_unit_of_work, require_any_role, require_project_resource, - require_roles, require_scenario_resource, ) from models import Project, Scenario, User @@ -30,6 +29,93 @@ from services.unit_of_work import UnitOfWork router = APIRouter(prefix="/reports", tags=["Reports"]) templates = Jinja2Templates(directory="templates") +# Add custom Jinja2 filters + + +def format_datetime(value): + """Format a datetime object for display in templates.""" + if not isinstance(value, datetime): + return "" + if value.tzinfo is None: + # Assume UTC if no timezone + from datetime import timezone + value = value.replace(tzinfo=timezone.utc) + # Format as readable date/time + return value.strftime("%Y-%m-%d %H:%M UTC") + + +def currency_display(value, currency_code): + """Format a numeric value with currency symbol/code.""" + if value is None: + return "—" + + # Format the number + if isinstance(value, (int, float)): + formatted_value = f"{value:,.2f}" + else: + formatted_value = str(value) + + # Add currency code + if currency_code: + return f"{currency_code} {formatted_value}" + return formatted_value + + +def format_metric(value, metric_name, currency_code=None): + """Format metric values appropriately based on metric type.""" + if value is None: + return "—" + + # For currency-related metrics, use currency formatting + currency_metrics = {'npv', 'inflows', 'outflows', + 'net', 'total_inflows', 'total_outflows', 'total_net'} + if metric_name in currency_metrics and currency_code: + return currency_display(value, currency_code) + + # For percentage metrics + percentage_metrics = {'irr', 'payback_period'} + if metric_name in percentage_metrics: + if isinstance(value, (int, float)): + return f"{value:.2f}%" + return f"{value}%" + + # Default numeric formatting + if isinstance(value, (int, float)): + return f"{value:,.2f}" + + return str(value) + + +def percentage_display(value): + """Format a value as a percentage.""" + if value is None: + return "—" + + if isinstance(value, (int, float)): + return f"{value:.2f}%" + + return f"{value}%" + + +def period_display(value): + """Format a period value (like payback period).""" + if value is None: + return "—" + + if isinstance(value, (int, float)): + if value == int(value): + return f"{int(value)} years" + return f"{value:.1f} years" + + return str(value) + + +templates.env.filters['format_datetime'] = format_datetime +templates.env.filters['currency_display'] = currency_display +templates.env.filters['format_metric'] = format_metric +templates.env.filters['percentage_display'] = percentage_display +templates.env.filters['period_display'] = period_display + READ_ROLES = ("viewer", "analyst", "project_manager", "admin") MANAGE_ROLES = ("project_manager", "admin") @@ -296,35 +382,9 @@ def project_summary_page( ) service = ReportingService(uow) - report = service.project_summary( - project, - filters=scenario_filter, - include=include_options, - iterations=iterations or DEFAULT_ITERATIONS, - percentiles=percentile_values, + context = service.build_project_summary_context( + project, scenario_filter, include_options, iterations or DEFAULT_ITERATIONS, percentile_values, request ) - context = { - "request": request, - "project": report["project"], - "scenario_count": report["scenario_count"], - "aggregates": report["aggregates"], - "scenarios": report["scenarios"], - "filters": report["filters"], - "include_options": include_options, - "iterations": iterations or DEFAULT_ITERATIONS, - "percentiles": percentile_values, - "title": f"Project Summary · {project.name}", - "subtitle": "Aggregated financial and simulation insights across scenarios.", - "actions": [ - { - "href": request.url_for( - "reports.project_summary", - project_id=project.id, - ), - "label": "Download JSON", - } - ], - } return templates.TemplateResponse( request, "reports/project_summary.html", @@ -399,40 +459,9 @@ def project_scenario_comparison_page( ) service = ReportingService(uow) - report = service.scenario_comparison( - project, - scenarios, - include=include_options, - iterations=iterations or DEFAULT_ITERATIONS, - percentiles=percentile_values, + context = service.build_scenario_comparison_context( + project, scenarios, include_options, iterations or DEFAULT_ITERATIONS, percentile_values, request ) - comparison_json_url = request.url_for( - "reports.project_scenario_comparison", - project_id=project.id, - ) - comparison_query = urlencode( - [("scenario_ids", str(identifier)) for identifier in unique_ids] - ) - if comparison_query: - comparison_json_url = f"{comparison_json_url}?{comparison_query}" - - context = { - "request": request, - "project": report["project"], - "scenarios": report["scenarios"], - "comparison": report["comparison"], - "include_options": include_options, - "iterations": iterations or DEFAULT_ITERATIONS, - "percentiles": percentile_values, - "title": f"Scenario Comparison · {project.name}", - "subtitle": "Evaluate deterministic metrics and Monte Carlo trends side by side.", - "actions": [ - { - "href": comparison_json_url, - "label": "Download JSON", - } - ], - } return templates.TemplateResponse( request, "reports/scenario_comparison.html", @@ -478,33 +507,9 @@ def scenario_distribution_page( ) from exc service = ReportingService(uow) - report = service.scenario_distribution( - scenario, - include=include_options, - iterations=iterations or DEFAULT_ITERATIONS, - percentiles=percentile_values, + context = service.build_scenario_distribution_context( + scenario, include_options, iterations or DEFAULT_ITERATIONS, percentile_values, request ) - context = { - "request": request, - "scenario": report["scenario"], - "summary": report["summary"], - "metrics": report["metrics"], - "monte_carlo": report["monte_carlo"], - "include_options": include_options, - "iterations": iterations or DEFAULT_ITERATIONS, - "percentiles": percentile_values, - "title": f"Scenario Distribution · {scenario.name}", - "subtitle": "Deterministic and simulated distributions for a single scenario.", - "actions": [ - { - "href": request.url_for( - "reports.scenario_distribution", - scenario_id=scenario.id, - ), - "label": "Download JSON", - } - ], - } return templates.TemplateResponse( request, "reports/scenario_distribution.html", diff --git a/routes/scenarios.py b/routes/scenarios.py index b229298..8566c8a 100644 --- a/routes/scenarios.py +++ b/routes/scenarios.py @@ -393,7 +393,7 @@ def create_scenario_submit( try: scenario_repo.create(scenario) - except EntityConflictError as exc: + except EntityConflictError: return templates.TemplateResponse( request, "scenarios/form.html", diff --git a/services/metrics.py b/services/metrics.py new file mode 100644 index 0000000..b515546 --- /dev/null +++ b/services/metrics.py @@ -0,0 +1,96 @@ +from __future__ import annotations + +import json +from datetime import datetime +from typing import Any, Dict, Optional + +from sqlalchemy.orm import Session + +from config.database import get_db +from models.performance_metric import PerformanceMetric + + +class MetricsService: + def __init__(self, db: Session): + self.db = db + + def store_metric( + self, + metric_name: str, + value: float, + labels: Optional[Dict[str, Any]] = None, + endpoint: Optional[str] = None, + method: Optional[str] = None, + status_code: Optional[int] = None, + duration_seconds: Optional[float] = None, + ) -> PerformanceMetric: + """Store a performance metric in the database.""" + metric = PerformanceMetric( + timestamp=datetime.utcnow(), + metric_name=metric_name, + value=value, + labels=json.dumps(labels) if labels else None, + endpoint=endpoint, + method=method, + status_code=status_code, + duration_seconds=duration_seconds, + ) + self.db.add(metric) + self.db.commit() + self.db.refresh(metric) + return metric + + def get_metrics( + self, + metric_name: Optional[str] = None, + start_time: Optional[datetime] = None, + end_time: Optional[datetime] = None, + limit: int = 100, + ) -> list[PerformanceMetric]: + """Retrieve stored metrics with optional filtering.""" + query = self.db.query(PerformanceMetric) + + if metric_name: + query = query.filter(PerformanceMetric.metric_name == metric_name) + + if start_time: + query = query.filter(PerformanceMetric.timestamp >= start_time) + + if end_time: + query = query.filter(PerformanceMetric.timestamp <= end_time) + + return query.order_by(PerformanceMetric.timestamp.desc()).limit(limit).all() + + def get_aggregated_metrics( + self, + metric_name: str, + start_time: Optional[datetime] = None, + end_time: Optional[datetime] = None, + ) -> Dict[str, Any]: + """Get aggregated statistics for a metric.""" + query = self.db.query(PerformanceMetric).filter( + PerformanceMetric.metric_name == metric_name + ) + + if start_time: + query = query.filter(PerformanceMetric.timestamp >= start_time) + + if end_time: + query = query.filter(PerformanceMetric.timestamp <= end_time) + + metrics = query.all() + + if not metrics: + return {"count": 0, "avg": 0, "min": 0, "max": 0} + + values = [m.value for m in metrics] + return { + "count": len(values), + "avg": sum(values) / len(values), + "min": min(values), + "max": max(values), + } + + +def get_metrics_service(db: Session) -> MetricsService: + return MetricsService(db) diff --git a/services/reporting.py b/services/reporting.py index 5ff5ef3..c7eb315 100644 --- a/services/reporting.py +++ b/services/reporting.py @@ -5,7 +5,10 @@ from __future__ import annotations from dataclasses import dataclass, field from datetime import date import math -from typing import Iterable, Mapping, Sequence +from typing import Mapping, Sequence +from urllib.parse import urlencode + +from fastapi import Request from models import FinancialCategory, Project, Scenario from services.financial import ( @@ -177,13 +180,13 @@ class ScenarioReport: "project_id": self.scenario.project_id, "name": self.scenario.name, "description": self.scenario.description, - "status": self.scenario.status.value, + "status": self.scenario.status.value if hasattr(self.scenario.status, 'value') else self.scenario.status, "start_date": self.scenario.start_date, "end_date": self.scenario.end_date, "currency": self.scenario.currency, "primary_resource": self.scenario.primary_resource.value - if self.scenario.primary_resource - else None, + if self.scenario.primary_resource and hasattr(self.scenario.primary_resource, 'value') + else self.scenario.primary_resource, "discount_rate": _round_optional(self.deterministic.discount_rate, digits=4), "created_at": self.scenario.created_at, "updated_at": self.scenario.updated_at, @@ -374,13 +377,12 @@ class ReportingService: } def _load_scenarios(self, project_id: int, filters: ReportFilters) -> list[Scenario]: - repo = self._require_scenario_repo() - scenarios = repo.list_for_project(project_id, with_children=True) + scenarios = self._uow.scenarios.list_for_project( + project_id, with_children=True) return [scenario for scenario in scenarios if filters.matches(scenario)] def _reload_scenario(self, scenario_id: int) -> Scenario: - repo = self._require_scenario_repo() - return repo.get(scenario_id, with_children=True) + return self._uow.scenarios.get(scenario_id, with_children=True) def _build_scenario_report( self, @@ -469,10 +471,147 @@ class ReportingService: ) return comparisons - def _require_scenario_repo(self): - if not self._uow.scenarios: - raise RuntimeError("Scenario repository not initialised") - return self._uow.scenarios + def build_project_summary_context( + self, + project: Project, + filters: ReportFilters, + include: IncludeOptions, + iterations: int, + percentiles: tuple[float, ...], + request: Request, + ) -> dict[str, object]: + """Build template context for project summary page.""" + scenarios = self._load_scenarios(project.id, filters) + reports = [ + self._build_scenario_report( + scenario, + include_distribution=include.distribution, + include_samples=include.samples, + iterations=iterations, + percentiles=percentiles, + ) + for scenario in scenarios + ] + aggregates = self._aggregate_project(reports) + + return { + "request": request, + "project": _project_payload(project), + "scenario_count": len(reports), + "aggregates": aggregates.to_dict(), + "scenarios": [report.to_dict() for report in reports], + "filters": filters.to_dict(), + "include_options": include, + "iterations": iterations, + "percentiles": percentiles, + "title": f"Project Summary · {project.name}", + "subtitle": "Aggregated financial and simulation insights across scenarios.", + "actions": [ + { + "href": request.url_for( + "reports.project_summary", + project_id=project.id, + ), + "label": "Download JSON", + } + ], + } + + def build_scenario_comparison_context( + self, + project: Project, + scenarios: Sequence[Scenario], + include: IncludeOptions, + iterations: int, + percentiles: tuple[float, ...], + request: Request, + ) -> dict[str, object]: + """Build template context for scenario comparison page.""" + reports = [ + self._build_scenario_report( + self._reload_scenario(scenario.id), + include_distribution=include.distribution, + include_samples=include.samples, + iterations=iterations, + percentiles=percentiles, + ) + for scenario in scenarios + ] + comparison = { + metric: data.to_dict() + for metric, data in self._build_comparisons(reports).items() + } + + comparison_json_url = request.url_for( + "reports.project_scenario_comparison", + project_id=project.id, + ) + scenario_ids = [str(s.id) for s in scenarios] + comparison_query = urlencode( + [("scenario_ids", str(identifier)) for identifier in scenario_ids] + ) + if comparison_query: + comparison_json_url = f"{comparison_json_url}?{comparison_query}" + + return { + "request": request, + "project": _project_payload(project), + "scenarios": [report.to_dict() for report in reports], + "comparison": comparison, + "include_options": include, + "iterations": iterations, + "percentiles": percentiles, + "title": f"Scenario Comparison · {project.name}", + "subtitle": "Evaluate deterministic metrics and Monte Carlo trends side by side.", + "actions": [ + { + "href": comparison_json_url, + "label": "Download JSON", + } + ], + } + + def build_scenario_distribution_context( + self, + scenario: Scenario, + include: IncludeOptions, + iterations: int, + percentiles: tuple[float, ...], + request: Request, + ) -> dict[str, object]: + """Build template context for scenario distribution page.""" + report = self._build_scenario_report( + self._reload_scenario(scenario.id), + include_distribution=True, + include_samples=include.samples, + iterations=iterations, + percentiles=percentiles, + ) + + return { + "request": request, + "scenario": report.to_dict()["scenario"], + "summary": report.totals.to_dict(), + "metrics": report.deterministic.to_dict(), + "monte_carlo": ( + report.monte_carlo.to_dict() if report.monte_carlo else { + "available": False} + ), + "include_options": include, + "iterations": iterations, + "percentiles": percentiles, + "title": f"Scenario Distribution · {scenario.name}", + "subtitle": "Deterministic and simulated distributions for a single scenario.", + "actions": [ + { + "href": request.url_for( + "reports.scenario_distribution", + scenario_id=scenario.id, + ), + "label": "Download JSON", + } + ], + } def _build_cash_flows(scenario: Scenario) -> tuple[list[CashFlow], ScenarioFinancialTotals]: diff --git a/services/repositories.py b/services/repositories.py index 1323a8f..ba362fb 100644 --- a/services/repositories.py +++ b/services/repositories.py @@ -15,7 +15,6 @@ from models import ( PricingImpuritySettings, PricingMetalSettings, PricingSettings, - ResourceType, Role, Scenario, ScenarioStatus, @@ -88,8 +87,12 @@ class ProjectRepository: try: self.session.flush() except IntegrityError as exc: # pragma: no cover - reliance on DB constraints + from monitoring.metrics import observe_project_operation + observe_project_operation("create", "error") raise EntityConflictError( "Project violates uniqueness constraints") from exc + from monitoring.metrics import observe_project_operation + observe_project_operation("create", "success") return project def find_by_names(self, names: Iterable[str]) -> Mapping[str, Project]: @@ -251,7 +254,11 @@ class ScenarioRepository: try: self.session.flush() except IntegrityError as exc: # pragma: no cover + from monitoring.metrics import observe_scenario_operation + observe_scenario_operation("create", "error") raise EntityConflictError("Scenario violates constraints") from exc + from monitoring.metrics import observe_scenario_operation + observe_scenario_operation("create", "success") return scenario def find_by_project_and_names( diff --git a/services/scenario_evaluation.py b/services/scenario_evaluation.py index c3aab15..d06b1bf 100644 --- a/services/scenario_evaluation.py +++ b/services/scenario_evaluation.py @@ -3,7 +3,7 @@ from __future__ import annotations """Scenario evaluation services including pricing integration.""" from dataclasses import dataclass -from typing import Iterable, Mapping +from typing import Iterable from models.scenario import Scenario from services.pricing import ( diff --git a/services/simulation.py b/services/simulation.py index 02ae588..5755023 100644 --- a/services/simulation.py +++ b/services/simulation.py @@ -2,7 +2,8 @@ from __future__ import annotations from dataclasses import dataclass from enum import Enum -from typing import Any, Dict, Iterable, Mapping, Sequence +from typing import Any, Dict, Mapping, Sequence +import time import numpy as np from numpy.random import Generator, default_rng @@ -15,6 +16,7 @@ from .financial import ( net_present_value, payback_period, ) +from monitoring.metrics import observe_simulation class DistributionConfigError(ValueError): @@ -120,60 +122,79 @@ def run_monte_carlo( if pct < 0.0 or pct > 100.0: raise ValueError("percentiles must be within [0, 100]") - generator = rng or default_rng(config.seed) + start_time = time.time() + try: + generator = rng or default_rng(config.seed) - metric_arrays: Dict[SimulationMetric, np.ndarray] = { - metric: np.empty(config.iterations, dtype=float) - for metric in config.metrics - } + metric_arrays: Dict[SimulationMetric, np.ndarray] = { + metric: np.empty(config.iterations, dtype=float) + for metric in config.metrics + } - for idx in range(config.iterations): - iteration_flows = [ - _realise_cash_flow( - spec, - generator, - scenario_context=scenario_context, - metadata=metadata, - ) - for spec in cash_flows - ] + for idx in range(config.iterations): + iteration_flows = [ + _realise_cash_flow( + spec, + generator, + scenario_context=scenario_context, + metadata=metadata, + ) + for spec in cash_flows + ] - if SimulationMetric.NPV in metric_arrays: - metric_arrays[SimulationMetric.NPV][idx] = net_present_value( - config.discount_rate, - iteration_flows, - residual_value=config.residual_value, - residual_periods=config.residual_periods, - compounds_per_year=config.compounds_per_year, - ) - if SimulationMetric.IRR in metric_arrays: - try: - metric_arrays[SimulationMetric.IRR][idx] = internal_rate_of_return( + if SimulationMetric.NPV in metric_arrays: + metric_arrays[SimulationMetric.NPV][idx] = net_present_value( + config.discount_rate, iteration_flows, + residual_value=config.residual_value, + residual_periods=config.residual_periods, compounds_per_year=config.compounds_per_year, ) - except (ValueError, ConvergenceError): - metric_arrays[SimulationMetric.IRR][idx] = np.nan - if SimulationMetric.PAYBACK in metric_arrays: - try: - metric_arrays[SimulationMetric.PAYBACK][idx] = payback_period( - iteration_flows, - compounds_per_year=config.compounds_per_year, - ) - except (ValueError, PaybackNotReachedError): - metric_arrays[SimulationMetric.PAYBACK][idx] = np.nan + if SimulationMetric.IRR in metric_arrays: + try: + metric_arrays[SimulationMetric.IRR][idx] = internal_rate_of_return( + iteration_flows, + compounds_per_year=config.compounds_per_year, + ) + except (ValueError, ConvergenceError): + metric_arrays[SimulationMetric.IRR][idx] = np.nan + if SimulationMetric.PAYBACK in metric_arrays: + try: + metric_arrays[SimulationMetric.PAYBACK][idx] = payback_period( + iteration_flows, + compounds_per_year=config.compounds_per_year, + ) + except (ValueError, PaybackNotReachedError): + metric_arrays[SimulationMetric.PAYBACK][idx] = np.nan - summaries = { - metric: _summarise(metric_arrays[metric], config.percentiles) - for metric in metric_arrays - } + summaries = { + metric: _summarise(metric_arrays[metric], config.percentiles) + for metric in metric_arrays + } - samples = metric_arrays if config.return_samples else None - return SimulationResult( - iterations=config.iterations, - summaries=summaries, - samples=samples, - ) + samples = metric_arrays if config.return_samples else None + result = SimulationResult( + iterations=config.iterations, + summaries=summaries, + samples=samples, + ) + + # Record successful simulation + duration = time.time() - start_time + observe_simulation( + status="success", + duration_seconds=duration, + ) + return result + + except Exception as e: + # Record failed simulation + duration = time.time() - start_time + observe_simulation( + status="error", + duration_seconds=duration, + ) + raise def _realise_cash_flow( diff --git a/static/css/main.css b/static/css/main.css index 7ac28d2..aa3b485 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -260,6 +260,33 @@ body { line-height: 1.45; } +h1, +h2, +h3, +h4, +h5, +h6 { + margin: 0 0 0.5rem 0; + font-weight: 700; + line-height: 1.2; +} + +h1 { + font-size: var(--font-size-2xl); +} + +h2 { + font-size: var(--font-size-xl); +} + +h3 { + font-size: var(--font-size-lg); +} + +p { + margin: 0 0 1rem 0; +} + a { color: var(--brand); } @@ -296,18 +323,46 @@ a { gap: 1rem; } -.brand-logo { - display: inline-flex; +.sidebar-nav-controls { + display: flex; + justify-content: center; + gap: 0.5rem; + margin: 1rem 0; +} + +.nav-chevron { + width: 40px; + height: 40px; + border: none; + border-radius: 50%; + background: rgba(255, 255, 255, 0.1); + color: rgba(255, 255, 255, 0.88); + font-size: 1.2rem; + font-weight: bold; + cursor: pointer; + display: flex; align-items: center; justify-content: center; + transition: background 0.2s ease, transform 0.2s ease; +} + +.nav-chevron:hover, +.nav-chevron:focus { + background: rgba(255, 255, 255, 0.2); + transform: scale(1.05); +} + +.nav-chevron:disabled { + opacity: 0.5; + cursor: not-allowed; + transform: none; +} + +.brand-logo { width: 44px; height: 44px; border-radius: 12px; - background: linear-gradient(0deg, var(--brand-3), var(--accent)); - color: var(--color-text-invert); - font-weight: 700; - font-size: 1.1rem; - letter-spacing: 1px; + object-fit: cover; } .brand-text { @@ -927,6 +982,24 @@ tbody tr:nth-child(even) { color: var(--danger); } +.alert { + padding: 0.75rem 1rem; + border-radius: var(--radius-sm); + margin-bottom: 1rem; +} + +.alert-error { + background: rgba(209, 75, 75, 0.2); + border: 1px solid rgba(209, 75, 75, 0.4); + color: var(--color-text-invert); +} + +.alert-info { + background: rgba(43, 168, 143, 0.2); + border: 1px solid rgba(43, 168, 143, 0.4); + color: var(--color-text-invert); +} + .site-footer { background-color: var(--brand); color: var(--color-text-invert); @@ -939,6 +1012,19 @@ tbody tr:nth-child(even) { justify-content: center; padding: 1rem 0; font-size: 0.9rem; + gap: 1rem; +} + +.footer-logo { + display: flex; + align-items: center; +} + +.footer-logo-img { + width: 32px; + height: 32px; + border-radius: 8px; + object-fit: cover; } .sidebar-toggle { diff --git a/static/css/projects.css b/static/css/projects.css index 8463267..18bd45d 100644 --- a/static/css/projects.css +++ b/static/css/projects.css @@ -153,18 +153,6 @@ } } -.alert { - padding: 0.75rem 1rem; - border-radius: var(--radius-sm); - margin-bottom: 1rem; -} - -.alert-error { - background: rgba(209, 75, 75, 0.2); - border: 1px solid rgba(209, 75, 75, 0.4); - color: var(--color-text-invert); -} - .form { display: flex; flex-direction: column; diff --git a/static/js/navigation.js b/static/js/navigation.js new file mode 100644 index 0000000..62174e9 --- /dev/null +++ b/static/js/navigation.js @@ -0,0 +1,53 @@ +// Navigation chevron buttons logic +document.addEventListener("DOMContentLoaded", function () { + const navPrev = document.getElementById("nav-prev"); + const navNext = document.getElementById("nav-next"); + + if (!navPrev || !navNext) return; + + // Define the navigation order (main pages) + const navPages = [ + "/", + "/projects/ui", + "/imports/ui", + "/ui/simulations", + "/ui/reporting", + "/ui/settings", + ]; + + const currentPath = window.location.pathname; + + // Find current index + let currentIndex = -1; + for (let i = 0; i < navPages.length; i++) { + if (currentPath.startsWith(navPages[i])) { + currentIndex = i; + break; + } + } + + // If not found, disable both + if (currentIndex === -1) { + navPrev.disabled = true; + navNext.disabled = true; + return; + } + + // Set up prev button + if (currentIndex > 0) { + navPrev.addEventListener("click", function () { + window.location.href = navPages[currentIndex - 1]; + }); + } else { + navPrev.disabled = true; + } + + // Set up next button + if (currentIndex < navPages.length - 1) { + navNext.addEventListener("click", function () { + window.location.href = navPages[currentIndex + 1]; + }); + } else { + navNext.disabled = true; + } +}); diff --git a/templates/base.html b/templates/base.html index 2169b4d..99e0561 100644 --- a/templates/base.html +++ b/templates/base.html @@ -25,6 +25,7 @@ + diff --git a/templates/forgot_password.html b/templates/forgot_password.html index 2d257cb..9618863 100644 --- a/templates/forgot_password.html +++ b/templates/forgot_password.html @@ -18,7 +18,7 @@ block content %}
- +

Remember your password? Login here

diff --git a/templates/login.html b/templates/login.html index 7279b8f..106648e 100644 --- a/templates/login.html +++ b/templates/login.html @@ -26,7 +26,7 @@ - +

Don't have an account? Register here

Forgot password?

diff --git a/templates/partials/base_footer.html b/templates/partials/base_footer.html index de97869..d0e55ac 100644 --- a/templates/partials/base_footer.html +++ b/templates/partials/base_footer.html @@ -1,5 +1,8 @@ + + {% endfor %} + {% else %}

No projects yet. Create your first project.

{% endif %} diff --git a/templates/scenarios/capex.html b/templates/scenarios/capex.html index d1e0a9c..f586119 100644 --- a/templates/scenarios/capex.html +++ b/templates/scenarios/capex.html @@ -1,5 +1,5 @@ {% extends "base.html" %} -{% block title %}Initial Capex Planner · CalMiner{% endblock %} +{% block title %}Capex Planner · CalMiner{% endblock %} {% block content %} @@ -99,7 +99,7 @@

Scenarios

Project scenarios inherit pricing and provide entry points to profitability planning.

- Add Scenario + Add Scenario {% if scenarios %}
    @@ -126,8 +126,8 @@
    - View - Edit + View + Edit
    {% endfor %} diff --git a/templates/projects/form.html b/templates/projects/form.html index 94c98aa..54abe81 100644 --- a/templates/projects/form.html +++ b/templates/projects/form.html @@ -26,8 +26,8 @@

    Provide core information about the mining project.

    - Cancel - + Cancel +
    @@ -58,8 +58,8 @@
    - Cancel - + Cancel +
    {% endblock %} diff --git a/templates/projects/list.html b/templates/projects/list.html index 82a40d4..e3b689d 100644 --- a/templates/projects/list.html +++ b/templates/projects/list.html @@ -19,7 +19,7 @@ data-project-filter aria-label="Filter projects" /> - New Project + New Project @@ -55,12 +55,12 @@