BE-CAL-004: implement MinimumWorkload storage

- New model: minimum_workloads table with JSON config column (per-user)
- Schemas: MinimumWorkloadConfig, MinimumWorkloadUpdate, MinimumWorkloadResponse
- Service: CRUD operations + check_workload_warnings() entry point for BE-CAL-007
- API: GET/PUT/PATCH /calendar/workload-config (self + admin routes)
- Migration: auto-create minimum_workloads table on startup
- Registered calendar router in main.py
This commit is contained in:
zhi
2026-03-30 22:27:05 +00:00
parent 1c062ff4f1
commit eb57197020
5 changed files with 438 additions and 1 deletions

63
app/schemas/calendar.py Normal file
View File

@@ -0,0 +1,63 @@
"""Calendar-related Pydantic schemas.
BE-CAL-004: MinimumWorkload read/write schemas.
"""
from __future__ import annotations
from pydantic import BaseModel, Field, model_validator
from typing import Optional
# ---------------------------------------------------------------------------
# MinimumWorkload
# ---------------------------------------------------------------------------
class WorkloadCategoryThresholds(BaseModel):
"""Minutes thresholds per slot category within a single period."""
work: int = Field(0, ge=0, le=65535, description="Minutes of work-type slots")
on_call: int = Field(0, ge=0, le=65535, description="Minutes of on-call-type slots")
entertainment: int = Field(0, ge=0, le=65535, description="Minutes of entertainment-type slots")
class MinimumWorkloadConfig(BaseModel):
"""Full workload configuration across all four periods."""
daily: WorkloadCategoryThresholds = Field(default_factory=WorkloadCategoryThresholds)
weekly: WorkloadCategoryThresholds = Field(default_factory=WorkloadCategoryThresholds)
monthly: WorkloadCategoryThresholds = Field(default_factory=WorkloadCategoryThresholds)
yearly: WorkloadCategoryThresholds = Field(default_factory=WorkloadCategoryThresholds)
class MinimumWorkloadUpdate(BaseModel):
"""Partial update — only provided periods/categories are overwritten.
Accepts the same shape as ``MinimumWorkloadConfig`` but every field
is optional so callers can PATCH individual periods.
"""
daily: Optional[WorkloadCategoryThresholds] = None
weekly: Optional[WorkloadCategoryThresholds] = None
monthly: Optional[WorkloadCategoryThresholds] = None
yearly: Optional[WorkloadCategoryThresholds] = None
class MinimumWorkloadResponse(BaseModel):
"""API response for workload configuration."""
user_id: int
config: MinimumWorkloadConfig
class Config:
from_attributes = True
# ---------------------------------------------------------------------------
# Workload warning (used by future calendar validation endpoints)
# ---------------------------------------------------------------------------
class WorkloadWarningItem(BaseModel):
"""A single workload warning returned alongside a calendar mutation."""
period: str = Field(..., description="daily | weekly | monthly | yearly")
category: str = Field(..., description="work | on_call | entertainment")
current_minutes: int = Field(..., ge=0, description="Current scheduled minutes in the period")
minimum_minutes: int = Field(..., ge=0, description="Configured minimum threshold")
shortfall_minutes: int = Field(..., ge=0, description="How many minutes below threshold")
message: str = Field(..., description="Human-readable warning")