HarborForge.Backend: dev-2026-03-29 -> main #13
@@ -7,6 +7,7 @@ BE-CAL-API-003: Calendar slot edit endpoints (real + virtual).
|
||||
BE-CAL-API-004: Calendar slot cancel endpoints (real + virtual).
|
||||
BE-CAL-API-005: Plan schedule / plan list endpoints.
|
||||
BE-CAL-API-006: Plan edit / plan cancel endpoints.
|
||||
BE-CAL-API-007: Date-list endpoint.
|
||||
"""
|
||||
|
||||
from datetime import date as date_type
|
||||
@@ -22,6 +23,7 @@ from app.models.models import User
|
||||
from app.schemas.calendar import (
|
||||
CalendarDayResponse,
|
||||
CalendarSlotItem,
|
||||
DateListResponse,
|
||||
MinimumWorkloadConfig,
|
||||
MinimumWorkloadResponse,
|
||||
MinimumWorkloadUpdate,
|
||||
@@ -841,6 +843,49 @@ def cancel_plan(
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Date list (BE-CAL-API-007)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# Statuses considered inactive — slots with these statuses are excluded
|
||||
# from the date-list result because they no longer occupy calendar time.
|
||||
_DATE_LIST_EXCLUDED_STATUSES = {SlotStatus.SKIPPED.value, SlotStatus.ABORTED.value}
|
||||
|
||||
|
||||
@router.get(
|
||||
"/dates",
|
||||
response_model=DateListResponse,
|
||||
summary="List future dates that have materialized slots",
|
||||
)
|
||||
def list_dates(
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
"""Return a sorted list of future dates that have at least one
|
||||
materialized (real) slot.
|
||||
|
||||
- Only dates **today or later** are included.
|
||||
- Only **active** slots are counted (skipped / aborted are excluded).
|
||||
- Pure plan-generated (virtual) dates that have not been materialized
|
||||
are **not** included.
|
||||
"""
|
||||
today = date_type.today()
|
||||
|
||||
rows = (
|
||||
db.query(TimeSlot.date)
|
||||
.filter(
|
||||
TimeSlot.user_id == current_user.id,
|
||||
TimeSlot.date >= today,
|
||||
TimeSlot.status.notin_(list(_DATE_LIST_EXCLUDED_STATUSES)),
|
||||
)
|
||||
.group_by(TimeSlot.date)
|
||||
.order_by(TimeSlot.date.asc())
|
||||
.all()
|
||||
)
|
||||
|
||||
return DateListResponse(dates=[r[0] for r in rows])
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# MinimumWorkload
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -391,3 +391,19 @@ class SchedulePlanCancelResponse(BaseModel):
|
||||
default_factory=list,
|
||||
description="IDs of past materialized slots that were NOT affected",
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Calendar date-list (BE-CAL-API-007)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class DateListResponse(BaseModel):
|
||||
"""Response for the date-list endpoint.
|
||||
|
||||
Returns only dates that have at least one materialized (real) future
|
||||
slot. Pure plan-generated (virtual) dates are excluded.
|
||||
"""
|
||||
dates: list[date] = Field(
|
||||
default_factory=list,
|
||||
description="Sorted list of future dates with materialized slots",
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user