100 lines
3.0 KiB
Python
100 lines
3.0 KiB
Python
# mmc/src/schedule/schemas.py
|
||
|
||
from datetime import datetime, time
|
||
|
||
from pydantic import BaseModel, validator
|
||
|
||
|
||
class ScheduleItem(BaseModel):
|
||
"""单个日程项的Pydantic模型"""
|
||
|
||
time_range: str
|
||
activity: str
|
||
|
||
@validator("time_range")
|
||
def validate_time_range(cls, v):
|
||
"""验证时间范围格式"""
|
||
if not v or "-" not in v:
|
||
raise ValueError("时间范围必须包含'-'分隔符")
|
||
|
||
try:
|
||
start_str, end_str = v.split("-", 1)
|
||
start_str = start_str.strip()
|
||
end_str = end_str.strip()
|
||
|
||
# 验证时间格式
|
||
datetime.strptime(start_str, "%H:%M")
|
||
datetime.strptime(end_str, "%H:%M")
|
||
|
||
return v
|
||
except ValueError as e:
|
||
raise ValueError(f"时间格式无效,应为HH:MM-HH:MM格式: {e}") from e
|
||
|
||
@validator("activity")
|
||
def validate_activity(cls, v):
|
||
"""验证活动描述"""
|
||
if not v or not v.strip():
|
||
raise ValueError("活动描述不能为空")
|
||
return v.strip()
|
||
|
||
|
||
class ScheduleData(BaseModel):
|
||
"""完整日程数据的Pydantic模型"""
|
||
|
||
schedule: list[ScheduleItem]
|
||
|
||
@validator("schedule")
|
||
def validate_schedule_completeness(cls, v):
|
||
"""验证日程是否覆盖24小时"""
|
||
if not v:
|
||
raise ValueError("日程不能为空")
|
||
|
||
# 收集所有时间段
|
||
time_ranges = []
|
||
for item in v:
|
||
try:
|
||
start_str, end_str = item.time_range.split("-", 1)
|
||
start_time = datetime.strptime(start_str.strip(), "%H:%M").time()
|
||
end_time = datetime.strptime(end_str.strip(), "%H:%M").time()
|
||
time_ranges.append((start_time, end_time))
|
||
except ValueError:
|
||
continue
|
||
|
||
# 检查是否覆盖24小时
|
||
if not cls._check_24_hour_coverage(time_ranges):
|
||
raise ValueError("日程必须覆盖完整的24小时")
|
||
|
||
return v
|
||
|
||
@staticmethod
|
||
def _check_24_hour_coverage(time_ranges: list[tuple]) -> bool:
|
||
"""检查时间段是否覆盖24小时"""
|
||
if not time_ranges:
|
||
return False
|
||
|
||
# 将时间转换为分钟数进行计算
|
||
def time_to_minutes(t: time) -> int:
|
||
return t.hour * 60 + t.minute
|
||
|
||
# 创建覆盖情况数组 (1440分钟 = 24小时)
|
||
covered = [False] * 1440
|
||
|
||
for start_time, end_time in time_ranges:
|
||
start_min = time_to_minutes(start_time)
|
||
end_min = time_to_minutes(end_time)
|
||
|
||
if start_min <= end_min:
|
||
# 同一天内的时间段
|
||
for i in range(start_min, end_min):
|
||
if i < 1440:
|
||
covered[i] = True
|
||
else:
|
||
# 跨天的时间段
|
||
for i in range(start_min, 1440):
|
||
covered[i] = True
|
||
for i in range(0, end_min):
|
||
covered[i] = True
|
||
|
||
# 检查是否所有分钟都被覆盖
|
||
return all(covered)
|