| | from datetime import date, timedelta |
| | from enum import Enum |
| | from random import Random |
| | from typing import List |
| |
|
| | from .domain import ( |
| | Crew, |
| | Job, |
| | MaintenanceSchedule, |
| | WorkCalendar, |
| | calculate_end_date, |
| | create_start_date_range, |
| | ) |
| |
|
| |
|
| | class DemoData(Enum): |
| | SMALL = "SMALL" |
| | LARGE = "LARGE" |
| |
|
| |
|
| | |
| | JOB_AREA_NAMES = [ |
| | "Downtown", "Uptown", "Park", "Airport", "Bay", "Hill", "Forest", "Station", |
| | "Hospital", "Harbor", "Market", "Fort", "Beach", "Garden", "River", "Springs", |
| | "Tower", "Mountain" |
| | ] |
| |
|
| | |
| | JOB_TARGET_NAMES = [ |
| | "Street", "Bridge", "Tunnel", "Highway", "Boulevard", "Avenue", "Square", "Plaza" |
| | ] |
| |
|
| |
|
| | def _get_next_monday(from_date: date) -> date: |
| | """Get the next Monday on or after the given date.""" |
| | days_until_monday = (7 - from_date.weekday()) % 7 |
| | if days_until_monday == 0 and from_date.weekday() != 0: |
| | days_until_monday = 7 |
| | return from_date + timedelta(days=days_until_monday) |
| |
|
| |
|
| | def generate_demo_data(demo_data: DemoData) -> MaintenanceSchedule: |
| | """ |
| | Generate demo data for the maintenance scheduling problem. |
| | |
| | Args: |
| | demo_data: The demo data type (SMALL or LARGE) |
| | |
| | Returns: |
| | A MaintenanceSchedule with crews, work calendar, and jobs |
| | """ |
| | |
| | crews: List[Crew] = [ |
| | Crew(id="1", name="Alpha crew"), |
| | Crew(id="2", name="Beta crew"), |
| | Crew(id="3", name="Gamma crew"), |
| | ] |
| | if demo_data == DemoData.LARGE: |
| | crews.append(Crew(id="4", name="Delta crew")) |
| | crews.append(Crew(id="5", name="Epsilon crew")) |
| |
|
| | |
| | from_date = _get_next_monday(date.today()) |
| | week_list_size = 16 if demo_data == DemoData.LARGE else 8 |
| | to_date = from_date + timedelta(weeks=week_list_size) |
| | work_calendar = WorkCalendar(id="1", from_date=from_date, to_date=to_date) |
| |
|
| | workday_total = week_list_size * 5 |
| |
|
| | |
| | jobs: List[Job] = [] |
| | job_list_size = week_list_size * len(crews) * 3 // 5 |
| | job_area_target_limit = min(len(JOB_TARGET_NAMES), len(crews) * 2) |
| | random = Random(17) |
| |
|
| | for i in range(job_list_size): |
| | job_area = JOB_AREA_NAMES[i // job_area_target_limit] |
| | job_target = JOB_TARGET_NAMES[i % job_area_target_limit] |
| |
|
| | |
| | duration_in_days = 1 + random.randint(0, 9) |
| |
|
| | |
| | min_max_between_workdays = ( |
| | duration_in_days + 5 |
| | + random.randint(0, workday_total - (duration_in_days + 5) - 1) |
| | ) |
| | min_workday_offset = random.randint(0, workday_total - min_max_between_workdays) |
| | min_ideal_end_between_workdays = min_max_between_workdays - 1 - random.randint(0, 3) |
| |
|
| | |
| | min_start_date = calculate_end_date(from_date, min_workday_offset) |
| | max_end_date = calculate_end_date(min_start_date, min_max_between_workdays) |
| | ideal_end_date = calculate_end_date(min_start_date, min_ideal_end_between_workdays) |
| |
|
| | |
| | if random.random() < 0.1: |
| | tags = {job_area, "Subway"} |
| | else: |
| | tags = {job_area} |
| |
|
| | jobs.append(Job( |
| | id=str(i), |
| | name=f"{job_area} {job_target}", |
| | duration_in_days=duration_in_days, |
| | min_start_date=min_start_date, |
| | max_end_date=max_end_date, |
| | ideal_end_date=ideal_end_date, |
| | tags=tags, |
| | )) |
| |
|
| | |
| | schedule = MaintenanceSchedule( |
| | work_calendar=work_calendar, |
| | crews=crews, |
| | jobs=jobs, |
| | ) |
| |
|
| | |
| | |
| | if not schedule.start_date_range: |
| | schedule.start_date_range = create_start_date_range(from_date, to_date) |
| |
|
| | return schedule |
| |
|