Initial commit: SejeteralO water tarification platform

Full-stack app for participatory water pricing using Bezier curves.
- Backend: FastAPI + SQLAlchemy + SQLite with JWT auth
- Frontend: Nuxt 4 + TypeScript with interactive SVG editor
- Math engine: cubic Bezier tarification with Cardano solver
- Admin: commune management, household import, vote monitoring, CMS
- Citizen: interactive curve editor, vote submission
- Docker-compose deployment ready

Includes fixes for:
- Impact table snake_case/camelCase property mismatch
- CMS content backend API + frontend editor (was stub)
- Admin route protection middleware
- Public content display on commune page
- Vote confirmation page link fix

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Yvv
2026-02-21 15:26:02 +01:00
commit b30e54a8f7
67 changed files with 16723 additions and 0 deletions

View File

@@ -0,0 +1,48 @@
"""
Median computation for vote parameters.
Computes the element-wise median of (vinf, a, b, c, d, e) across all active votes.
This parametric median is chosen over geometric median because:
- It's transparent and politically explainable
- The result is itself a valid set of Bézier parameters
"""
import numpy as np
from dataclasses import dataclass
@dataclass
class VoteParams:
"""The 6 citizen-adjustable parameters."""
vinf: float
a: float
b: float
c: float
d: float
e: float
def compute_median(votes: list[VoteParams]) -> VoteParams | None:
"""
Compute element-wise median of vote parameters.
Returns None if no votes provided.
"""
if not votes:
return None
vinfs = [v.vinf for v in votes]
a_s = [v.a for v in votes]
b_s = [v.b for v in votes]
c_s = [v.c for v in votes]
d_s = [v.d for v in votes]
e_s = [v.e for v in votes]
return VoteParams(
vinf=float(np.median(vinfs)),
a=float(np.median(a_s)),
b=float(np.median(b_s)),
c=float(np.median(c_s)),
d=float(np.median(d_s)),
e=float(np.median(e_s)),
)