| 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 |
|
|