Phase 1: Add Availability model and API

- Create Availability model with date, start_time, end_time
- Add availability schemas with 15-minute boundary validation
- Add admin endpoints:
  - GET /api/admin/availability - query by date range
  - PUT /api/admin/availability - set slots for a date
  - POST /api/admin/availability/copy - copy to multiple days
- Add 26 tests covering permissions, CRUD, and validation
This commit is contained in:
counterweight 2025-12-20 23:36:11 +01:00
parent 6c1a05d93d
commit 64d2e99d73
Signed by: counterweight
GPG key ID: 883EDBAA726BD96C
5 changed files with 788 additions and 4 deletions

View file

@ -1,7 +1,7 @@
from datetime import datetime, UTC
from datetime import datetime, date, time, UTC
from enum import Enum as PyEnum
from typing import TypedDict
from sqlalchemy import Integer, String, Float, DateTime, ForeignKey, Table, Column, Enum, select
from sqlalchemy import Integer, String, Float, DateTime, Date, Time, ForeignKey, Table, Column, Enum, UniqueConstraint, select
from sqlalchemy.orm import Mapped, mapped_column, relationship
from sqlalchemy.ext.asyncio import AsyncSession
from database import Base
@ -248,3 +248,24 @@ class Invite(Base):
)
spent_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
revoked_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
class Availability(Base):
"""Admin availability slots for booking."""
__tablename__ = "availability"
__table_args__ = (
UniqueConstraint("date", "start_time", name="uq_availability_date_start"),
)
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
date: Mapped[date] = mapped_column(Date, nullable=False, index=True)
start_time: Mapped[time] = mapped_column(Time, nullable=False)
end_time: Mapped[time] = mapped_column(Time, nullable=False)
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=lambda: datetime.now(UTC)
)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True),
default=lambda: datetime.now(UTC),
onupdate=lambda: datetime.now(UTC)
)