RoyAalekh's picture
refactored project structure. renamed scheduler dir to src
6a28f91
"""Court calendar utilities with working days and seasonality.
This module provides utilities for calculating working days considering
court holidays, seasonality, and Karnataka High Court calendar.
"""
from datetime import date, timedelta
from typing import List, Set
from src.data.config import (
SEASONALITY_FACTORS,
WORKING_DAYS_PER_YEAR,
)
class CourtCalendar:
"""Manages court working days and seasonality.
Attributes:
holidays: Set of holiday dates
working_days_per_year: Expected working days annually
"""
def __init__(self, working_days_per_year: int = WORKING_DAYS_PER_YEAR):
"""Initialize court calendar.
Args:
working_days_per_year: Annual working days (default 192)
"""
self.working_days_per_year = working_days_per_year
self.holidays: Set[date] = set()
def add_holiday(self, holiday_date: date) -> None:
"""Add a holiday to the calendar.
Args:
holiday_date: Date to mark as holiday
"""
self.holidays.add(holiday_date)
def add_holidays(self, holiday_dates: List[date]) -> None:
"""Add multiple holidays.
Args:
holiday_dates: List of dates to mark as holidays
"""
self.holidays.update(holiday_dates)
def is_working_day(self, check_date: date) -> bool:
"""Check if a date is a working day.
Args:
check_date: Date to check
Returns:
True if date is a working day (not weekend or holiday)
"""
# Saturday (5) and Sunday (6) are weekends
if check_date.weekday() in (5, 6):
return False
if check_date in self.holidays:
return False
return True
def next_working_day(self, start_date: date, days_ahead: int = 1) -> date:
"""Get the next working day after a given number of working days.
Args:
start_date: Starting date
days_ahead: Number of working days to advance
Returns:
Next working day date
"""
current = start_date
working_days_found = 0
while working_days_found < days_ahead:
current += timedelta(days=1)
if self.is_working_day(current):
working_days_found += 1
return current
def working_days_between(self, start_date: date, end_date: date) -> int:
"""Count working days between two dates (inclusive).
Args:
start_date: Start of range
end_date: End of range
Returns:
Number of working days
"""
if start_date > end_date:
return 0
count = 0
current = start_date
while current <= end_date:
if self.is_working_day(current):
count += 1
current += timedelta(days=1)
return count
def get_working_days_in_month(self, year: int, month: int) -> List[date]:
"""Get all working days in a specific month.
Args:
year: Year
month: Month (1-12)
Returns:
List of working day dates
"""
# Get first and last day of month
first_day = date(year, month, 1)
if month == 12:
last_day = date(year, 12, 31)
else:
last_day = date(year, month + 1, 1) - timedelta(days=1)
working_days = []
current = first_day
while current <= last_day:
if self.is_working_day(current):
working_days.append(current)
current += timedelta(days=1)
return working_days
def get_working_days_in_year(self, year: int) -> List[date]:
"""Get all working days in a year.
Args:
year: Year
Returns:
List of working day dates
"""
working_days = []
for month in range(1, 13):
working_days.extend(self.get_working_days_in_month(year, month))
return working_days
def get_seasonality_factor(self, check_date: date) -> float:
"""Get seasonality factor for a date based on month.
Args:
check_date: Date to check
Returns:
Seasonality multiplier (from config)
"""
return SEASONALITY_FACTORS.get(check_date.month, 1.0)
def get_expected_capacity(self, check_date: date, base_capacity: int) -> int:
"""Get expected capacity adjusted for seasonality.
Args:
check_date: Date to check
base_capacity: Base daily capacity
Returns:
Adjusted capacity
"""
factor = self.get_seasonality_factor(check_date)
return int(base_capacity * factor)
def generate_court_calendar(self, start_date: date, end_date: date) -> List[date]:
"""Generate list of all court working days in a date range.
Args:
start_date: Start of simulation
end_date: End of simulation
Returns:
List of working day dates
"""
working_days = []
current = start_date
while current <= end_date:
if self.is_working_day(current):
working_days.append(current)
current += timedelta(days=1)
return working_days
def add_standard_holidays(self, year: int) -> None:
"""Add standard Indian national holidays for a year.
This is a simplified set. In production, use actual court holiday calendar.
Args:
year: Year to add holidays for
"""
# Standard national holidays (simplified)
holidays = [
date(year, 1, 26), # Republic Day
date(year, 8, 15), # Independence Day
date(year, 10, 2), # Gandhi Jayanti
date(year, 12, 25), # Christmas
]
self.add_holidays(holidays)
def __repr__(self) -> str:
return f"CourtCalendar(working_days/year={self.working_days_per_year}, holidays={len(self.holidays)})"