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>
49 lines
1.1 KiB
Python
49 lines
1.1 KiB
Python
"""
|
|
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)),
|
|
)
|