# services/external/app/registry/calendar_registry.py """ Calendar Registry - Pre-configured school calendars and local events """ from dataclasses import dataclass from typing import List, Optional, Dict, Any from datetime import date from enum import Enum class SchoolType(str, Enum): PRIMARY = "primary" SECONDARY = "secondary" UNIVERSITY = "university" @dataclass class HolidayPeriod: """School holiday period definition""" name: str start_date: str # ISO format: "2024-12-20" end_date: str # ISO format: "2025-01-08" description: Optional[str] = None @dataclass class SchoolHours: """School operating hours configuration""" morning_start: str # "09:00" morning_end: str # "14:00" has_afternoon_session: bool # True/False afternoon_start: Optional[str] = None # "15:00" if has_afternoon_session afternoon_end: Optional[str] = None # "17:00" if has_afternoon_session @dataclass class CalendarDefinition: """School calendar configuration for a specific city and school type""" calendar_id: str calendar_name: str city_id: str school_type: SchoolType academic_year: str # "2024-2025" holiday_periods: List[HolidayPeriod] school_hours: SchoolHours source: str enabled: bool = True class CalendarRegistry: """Central registry of school calendars for forecasting""" # Madrid Primary School Calendar 2024-2025 (Official Comunidad de Madrid - ORDEN 1177/2024) MADRID_PRIMARY_2024_2025 = CalendarDefinition( calendar_id="madrid_primary_2024_2025", calendar_name="Madrid Primary School Calendar 2024-2025", city_id="madrid", school_type=SchoolType.PRIMARY, academic_year="2024-2025", holiday_periods=[ HolidayPeriod( name="Christmas Holiday", start_date="2024-12-21", end_date="2025-01-07", description="Official Christmas break - Comunidad de Madrid (Dec 21 - Jan 7)" ), HolidayPeriod( name="Easter Holiday (Semana Santa)", start_date="2025-04-11", end_date="2025-04-21", description="Official Easter break - Comunidad de Madrid (Apr 11-21)" ), HolidayPeriod( name="Summer Holiday", start_date="2025-06-21", end_date="2025-09-08", description="Summer vacation (Last day Jun 20, classes resume Sep 9)" ), HolidayPeriod( name="All Saints Long Weekend", start_date="2024-10-31", end_date="2024-11-03", description="October 31 - November 3 non-working days" ), HolidayPeriod( name="February Long Weekend", start_date="2025-02-28", end_date="2025-03-03", description="February 28 - March 3 non-working days" ), ], school_hours=SchoolHours( morning_start="09:00", morning_end="14:00", has_afternoon_session=False ), source="comunidad_madrid_orden_1177_2024", enabled=True ) # Madrid Secondary School Calendar 2024-2025 (Official Comunidad de Madrid - ORDEN 1177/2024) MADRID_SECONDARY_2024_2025 = CalendarDefinition( calendar_id="madrid_secondary_2024_2025", calendar_name="Madrid Secondary School Calendar 2024-2025", city_id="madrid", school_type=SchoolType.SECONDARY, academic_year="2024-2025", holiday_periods=[ HolidayPeriod( name="Christmas Holiday", start_date="2024-12-21", end_date="2025-01-07", description="Official Christmas break - Comunidad de Madrid (Dec 21 - Jan 7)" ), HolidayPeriod( name="Easter Holiday (Semana Santa)", start_date="2025-04-11", end_date="2025-04-21", description="Official Easter break - Comunidad de Madrid (Apr 11-21)" ), HolidayPeriod( name="Summer Holiday", start_date="2025-06-21", end_date="2025-09-09", description="Summer vacation (Last day Jun 20, classes resume Sep 10)" ), HolidayPeriod( name="All Saints Long Weekend", start_date="2024-10-31", end_date="2024-11-03", description="October 31 - November 3 non-working days" ), HolidayPeriod( name="February Long Weekend", start_date="2025-02-28", end_date="2025-03-03", description="February 28 - March 3 non-working days" ), ], school_hours=SchoolHours( morning_start="09:00", morning_end="14:00", has_afternoon_session=False ), source="comunidad_madrid_orden_1177_2024", enabled=True ) # Madrid Primary School Calendar 2025-2026 (Official Comunidad de Madrid - ORDEN 1476/2025) MADRID_PRIMARY_2025_2026 = CalendarDefinition( calendar_id="madrid_primary_2025_2026", calendar_name="Madrid Primary School Calendar 2025-2026", city_id="madrid", school_type=SchoolType.PRIMARY, academic_year="2025-2026", holiday_periods=[ HolidayPeriod( name="Christmas Holiday", start_date="2025-12-20", end_date="2026-01-07", description="Official Christmas break - Comunidad de Madrid (Dec 20 - Jan 7)" ), HolidayPeriod( name="Easter Holiday (Semana Santa)", start_date="2026-03-27", end_date="2026-04-06", description="Official Easter break - Comunidad de Madrid (Mar 27 - Apr 6)" ), HolidayPeriod( name="Summer Holiday", start_date="2026-06-21", end_date="2026-09-08", description="Summer vacation (classes resume Sep 9)" ), HolidayPeriod( name="October Long Weekend", start_date="2025-10-13", end_date="2025-10-13", description="October 13 non-working day (after Día de la Hispanidad)" ), HolidayPeriod( name="All Saints Long Weekend", start_date="2025-11-03", end_date="2025-11-03", description="November 3 non-working day (after All Saints)" ), ], school_hours=SchoolHours( morning_start="09:00", morning_end="14:00", has_afternoon_session=False ), source="comunidad_madrid_orden_1476_2025", enabled=True ) # Madrid Secondary School Calendar 2025-2026 (Official Comunidad de Madrid - ORDEN 1476/2025) MADRID_SECONDARY_2025_2026 = CalendarDefinition( calendar_id="madrid_secondary_2025_2026", calendar_name="Madrid Secondary School Calendar 2025-2026", city_id="madrid", school_type=SchoolType.SECONDARY, academic_year="2025-2026", holiday_periods=[ HolidayPeriod( name="Christmas Holiday", start_date="2025-12-20", end_date="2026-01-07", description="Official Christmas break - Comunidad de Madrid (Dec 20 - Jan 7)" ), HolidayPeriod( name="Easter Holiday (Semana Santa)", start_date="2026-03-27", end_date="2026-04-06", description="Official Easter break - Comunidad de Madrid (Mar 27 - Apr 6)" ), HolidayPeriod( name="Summer Holiday", start_date="2026-06-21", end_date="2026-09-09", description="Summer vacation (classes resume Sep 10)" ), HolidayPeriod( name="October Long Weekend", start_date="2025-10-13", end_date="2025-10-13", description="October 13 non-working day (after Día de la Hispanidad)" ), HolidayPeriod( name="All Saints Long Weekend", start_date="2025-11-03", end_date="2025-11-03", description="November 3 non-working day (after All Saints)" ), ], school_hours=SchoolHours( morning_start="09:00", morning_end="14:00", has_afternoon_session=False ), source="comunidad_madrid_orden_1476_2025", enabled=True ) # Registry of all calendars CALENDARS: List[CalendarDefinition] = [ MADRID_PRIMARY_2024_2025, MADRID_SECONDARY_2024_2025, MADRID_PRIMARY_2025_2026, MADRID_SECONDARY_2025_2026, ] @classmethod def get_all_calendars(cls) -> List[CalendarDefinition]: """Get all calendars""" return cls.CALENDARS @classmethod def get_enabled_calendars(cls) -> List[CalendarDefinition]: """Get all enabled calendars""" return [cal for cal in cls.CALENDARS if cal.enabled] @classmethod def get_calendar(cls, calendar_id: str) -> Optional[CalendarDefinition]: """Get calendar by ID""" for cal in cls.CALENDARS: if cal.calendar_id == calendar_id: return cal return None @classmethod def get_calendars_for_city(cls, city_id: str) -> List[CalendarDefinition]: """Get all enabled calendars for a specific city""" return [ cal for cal in cls.CALENDARS if cal.city_id == city_id and cal.enabled ] @classmethod def get_calendar_for_city_and_type( cls, city_id: str, school_type: SchoolType, academic_year: Optional[str] = None ) -> Optional[CalendarDefinition]: """Get specific calendar for city, type, and optionally year""" for cal in cls.CALENDARS: if (cal.city_id == city_id and cal.school_type == school_type and cal.enabled and (academic_year is None or cal.academic_year == academic_year)): return cal return None @classmethod def to_dict(cls, calendar: CalendarDefinition) -> Dict[str, Any]: """Convert calendar definition to dictionary for JSON serialization""" return { "calendar_id": calendar.calendar_id, "calendar_name": calendar.calendar_name, "city_id": calendar.city_id, "school_type": calendar.school_type.value, "academic_year": calendar.academic_year, "holiday_periods": [ { "name": hp.name, "start_date": hp.start_date, "end_date": hp.end_date, "description": hp.description } for hp in calendar.holiday_periods ], "school_hours": { "morning_start": calendar.school_hours.morning_start, "morning_end": calendar.school_hours.morning_end, "has_afternoon_session": calendar.school_hours.has_afternoon_session, "afternoon_start": calendar.school_hours.afternoon_start, "afternoon_end": calendar.school_hours.afternoon_end, }, "source": calendar.source, "enabled": calendar.enabled } # Local Events Registry for Madrid @dataclass class LocalEventDefinition: """Local event that impacts demand""" event_id: str name: str city_id: str date: str # ISO format or "annual-MM-DD" for recurring impact_level: str # "low", "medium", "high" description: Optional[str] = None recurring: bool = False # True for annual events class LocalEventsRegistry: """Registry of local events and festivals""" MADRID_EVENTS = [ LocalEventDefinition( event_id="madrid_san_isidro", name="San Isidro Festival", city_id="madrid", date="annual-05-15", impact_level="high", description="Madrid's patron saint festival - major citywide celebration", recurring=True ), LocalEventDefinition( event_id="madrid_dos_de_mayo", name="Dos de Mayo", city_id="madrid", date="annual-05-02", impact_level="medium", description="Madrid regional holiday", recurring=True ), LocalEventDefinition( event_id="madrid_almudena", name="Virgen de la Almudena", city_id="madrid", date="annual-11-09", impact_level="medium", description="Madrid patron saint day", recurring=True ), ] @classmethod def get_events_for_city(cls, city_id: str) -> List[LocalEventDefinition]: """Get all local events for a city""" if city_id == "madrid": return cls.MADRID_EVENTS return []