From 1e3f5bb0c7bb7f9c4f8d0287df02cd1d9a7ee6d1 Mon Sep 17 00:00:00 2001 From: Antoine Van Elstraete Date: Wed, 11 Mar 2026 16:33:38 +0100 Subject: [PATCH] feat: SQLAlchemy models for WorkEntry, TimeSlot, LeaveBalance --- app/models.py | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 app/models.py diff --git a/app/models.py b/app/models.py new file mode 100644 index 0000000..e57a0f5 --- /dev/null +++ b/app/models.py @@ -0,0 +1,56 @@ +from app import db +from datetime import date, time, datetime +import sqlalchemy as sa +import sqlalchemy.orm as so + + +class WorkEntry(db.Model): + __tablename__ = "work_entries" + + id: so.Mapped[int] = so.mapped_column(primary_key=True) + date: so.Mapped[date] = so.mapped_column(sa.Date, unique=True, nullable=False) + journey_profile_id: so.Mapped[str | None] = so.mapped_column(sa.String(64), nullable=True) + day_type: so.Mapped[str] = so.mapped_column(sa.String(16), nullable=False, default="WORK") + comment: so.Mapped[str | None] = so.mapped_column(sa.Text, nullable=True) + created_at: so.Mapped[datetime] = so.mapped_column(sa.DateTime, default=datetime.utcnow) + updated_at: so.Mapped[datetime] = so.mapped_column( + sa.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow + ) + + time_slots: so.Mapped[list["TimeSlot"]] = so.relationship( + back_populates="entry", cascade="all, delete-orphan", order_by="TimeSlot.start_time" + ) + + def total_minutes(self) -> int: + total = 0 + for slot in self.time_slots: + start = slot.start_time.hour * 60 + slot.start_time.minute + end = slot.end_time.hour * 60 + slot.end_time.minute + if end <= start: + end += 24 * 60 + total += end - start + return total + + def total_hours_str(self) -> str: + minutes = self.total_minutes() + return f"{minutes // 60}h{minutes % 60:02d}" + + +class TimeSlot(db.Model): + __tablename__ = "time_slots" + + id: so.Mapped[int] = so.mapped_column(primary_key=True) + entry_id: so.Mapped[int] = so.mapped_column(sa.ForeignKey("work_entries.id"), nullable=False) + start_time: so.Mapped[time] = so.mapped_column(sa.Time, nullable=False) + end_time: so.Mapped[time] = so.mapped_column(sa.Time, nullable=False) + + entry: so.Mapped["WorkEntry"] = so.relationship(back_populates="time_slots") + + +class LeaveBalance(db.Model): + __tablename__ = "leave_balance" + + id: so.Mapped[int] = so.mapped_column(primary_key=True) + year: so.Mapped[int] = so.mapped_column(sa.Integer, unique=True, nullable=False) + conges_total: so.Mapped[int] = so.mapped_column(sa.Integer, default=28) + rtt_total: so.Mapped[int] = so.mapped_column(sa.Integer, default=18)