"""SQLAlchemy ORM models.""" from datetime import datetime from sqlalchemy import ( Column, Integer, String, Float, Boolean, DateTime, ForeignKey, Text, Table, UniqueConstraint, ) from sqlalchemy.orm import relationship from app.database import Base # Many-to-many: admin users <-> communes admin_commune_table = Table( "admin_commune", Base.metadata, Column("admin_id", Integer, ForeignKey("admin_users.id"), primary_key=True), Column("commune_id", Integer, ForeignKey("communes.id"), primary_key=True), ) class Commune(Base): __tablename__ = "communes" id = Column(Integer, primary_key=True, index=True) name = Column(String(200), nullable=False) slug = Column(String(200), unique=True, nullable=False, index=True) description = Column(Text, default="") is_active = Column(Boolean, default=True) created_at = Column(DateTime, default=datetime.utcnow) vote_deadline = Column(DateTime, nullable=True) tariff_params = relationship("TariffParams", back_populates="commune", uselist=False) households = relationship("Household", back_populates="commune") votes = relationship("Vote", back_populates="commune") contents = relationship("CommuneContent", back_populates="commune") admins = relationship("AdminUser", secondary=admin_commune_table, back_populates="communes") class TariffParams(Base): __tablename__ = "tariff_params" id = Column(Integer, primary_key=True, index=True) commune_id = Column(Integer, ForeignKey("communes.id"), unique=True, nullable=False) abop = Column(Float, default=100.0) abos = Column(Float, default=100.0) recettes = Column(Float, default=75000.0) pmax = Column(Float, default=20.0) vmax = Column(Float, default=2100.0) differentiated_tariff = Column(Boolean, default=False) data_year = Column(Integer, nullable=True) data_imported_at = Column(DateTime, nullable=True) # Published Bézier curve (set by admin) published_vinf = Column(Float, nullable=True) published_a = Column(Float, nullable=True) published_b = Column(Float, nullable=True) published_c = Column(Float, nullable=True) published_d = Column(Float, nullable=True) published_e = Column(Float, nullable=True) published_p0 = Column(Float, nullable=True) published_at = Column(DateTime, nullable=True) commune = relationship("Commune", back_populates="tariff_params") class Household(Base): __tablename__ = "households" id = Column(Integer, primary_key=True, index=True) commune_id = Column(Integer, ForeignKey("communes.id"), nullable=False) identifier = Column(String(200), nullable=False) status = Column(String(10), nullable=False) # RS, RP, PRO volume_m3 = Column(Float, nullable=False) price_paid_eur = Column(Float, default=0.0) auth_code = Column(String(8), unique=True, nullable=False, index=True) has_voted = Column(Boolean, default=False) commune = relationship("Commune", back_populates="households") votes = relationship("Vote", back_populates="household") __table_args__ = ( UniqueConstraint("commune_id", "identifier", name="uq_household_commune_identifier"), ) class AdminUser(Base): __tablename__ = "admin_users" id = Column(Integer, primary_key=True, index=True) email = Column(String(200), unique=True, nullable=False, index=True) hashed_password = Column(String(200), nullable=False) full_name = Column(String(200), default="") role = Column(String(20), default="commune_admin") # super_admin / commune_admin communes = relationship("Commune", secondary=admin_commune_table, back_populates="admins") class Vote(Base): __tablename__ = "votes" id = Column(Integer, primary_key=True, index=True) commune_id = Column(Integer, ForeignKey("communes.id"), nullable=False) household_id = Column(Integer, ForeignKey("households.id"), nullable=False) vinf = Column(Float, nullable=False) a = Column(Float, nullable=False) b = Column(Float, nullable=False) c = Column(Float, nullable=False) d = Column(Float, nullable=False) e = Column(Float, nullable=False) computed_p0 = Column(Float, nullable=True) submitted_at = Column(DateTime, default=datetime.utcnow) is_active = Column(Boolean, default=True) commune = relationship("Commune", back_populates="votes") household = relationship("Household", back_populates="votes") class CommuneContent(Base): __tablename__ = "commune_contents" id = Column(Integer, primary_key=True, index=True) commune_id = Column(Integer, ForeignKey("communes.id"), nullable=False) slug = Column(String(200), nullable=False) # page identifier title = Column(String(200), default="") body_markdown = Column(Text, default="") updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) commune = relationship("Commune", back_populates="contents") __table_args__ = ( UniqueConstraint("commune_id", "slug", name="uq_content_commune_slug"), )