From ac4b2fd7fc13f68e9f5b792cd40554206da2520c Mon Sep 17 00:00:00 2001 From: Tom Wawerek Date: Mon, 1 Jun 2026 23:48:58 +0200 Subject: [PATCH 01/18] test: minimal integration tests --- Justfile | 16 +- pyproject.toml | 6 + .../free_day_after_night_shift_phase.py | 55 +++--- tests/integration/helpers/__init__.py | 0 .../helpers/solution_assertions.py | 138 +++++++++++++ tests/integration/helpers/solver_fixtures.py | 182 ++++++++++++++++++ tests/integration/test_solver_pipeline.py | 91 +++++++++ 7 files changed, 451 insertions(+), 37 deletions(-) create mode 100644 tests/integration/helpers/__init__.py create mode 100644 tests/integration/helpers/solution_assertions.py create mode 100644 tests/integration/helpers/solver_fixtures.py create mode 100644 tests/integration/test_solver_pipeline.py diff --git a/Justfile b/Justfile index 22ef324b..ccc28aa0 100644 --- a/Justfile +++ b/Justfile @@ -11,17 +11,17 @@ _default: sync: uv sync -test: - uv run pytest +test *args: + uv run pytest {{args}} -lint: - uv run ruff check . +lint *args: + uv run ruff check . {{args}} -format: - uv run ruff format . +format *args: + uv run ruff format . {{args}} -typecheck: - uv run pyright . +typecheck *args: + uv run pyright . {{args}} check: lint typecheck test diff --git a/pyproject.toml b/pyproject.toml index 71180d0a..a507fab3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -56,3 +56,9 @@ line-length = 120 [tool.ruff.lint] select = ["E", "F", "B", "W", "I", "C4", "ISC", "PT", "Q", "UP"] # Plan to add 'N' and 'ANN' later + +[tool.pytest.ini_options] +addopts = "-m 'not integration'" +markers = [ + "integration: tests that run the solver or cross module boundaries" +] diff --git a/src/cp/constraints/free_day_after_night_shift_phase.py b/src/cp/constraints/free_day_after_night_shift_phase.py index af45fffb..35654725 100644 --- a/src/cp/constraints/free_day_after_night_shift_phase.py +++ b/src/cp/constraints/free_day_after_night_shift_phase.py @@ -17,9 +17,6 @@ def KEY(self) -> str: return "free-day-after-night-shift-phase" def __init__(self, employees: list[Employee], days: list[Day], shifts: list[Shift]): - """ - Initializes the constraint that ensures an employee has a free day after a night shift phase. - """ super().__init__(employees, days, shifts) def create( @@ -28,36 +25,36 @@ def create( shift_assignment_variables: ShiftAssignmentVariables, employee_works_on_day_variables: EmployeeWorksOnDayVariables, ) -> None: - # This function falsely ignores special night shifts + regular_night_shift = self._find_shift_by_id(Shift.NIGHT) + special_night_shift = self._find_shift_by_id(SPECIAL_NIGHT_SHIFT_INDEX) + + night_shifts = [shift for shift in (regular_night_shift, special_night_shift) if shift is not None] + + if not night_shifts: + return for employee in self._employees: for day in self._days[:-1]: - night_shift_today_variable = shift_assignment_variables[employee][day][self._shifts[Shift.NIGHT]] - night_shift_tomorrow_variable = shift_assignment_variables[employee][day + timedelta(1)][ - self._shifts[Shift.NIGHT] - ] + tomorrow = day + timedelta(days=1) + day_tomorrow_variable = employee_works_on_day_variables[employee][tomorrow] - # N5 is a special form of night shifts - night_shift_today_variable_special = shift_assignment_variables[employee][day][ - self._shifts[SPECIAL_NIGHT_SHIFT_INDEX] + night_shift_today_variables = [ + shift_assignment_variables[employee][day][shift] for shift in night_shifts ] - night_shift_tomorrow_variable_special = shift_assignment_variables[employee][day + timedelta(1)][ - self._shifts[SPECIAL_NIGHT_SHIFT_INDEX] + night_shift_tomorrow_variables = [ + shift_assignment_variables[employee][tomorrow][shift] for shift in night_shifts ] - day_tomorrow_variable = employee_works_on_day_variables[employee][day + timedelta(1)] - # where are day_tomorrow_variables enforced? this may be the cause of the bug menitioned in the docs - model.add(day_tomorrow_variable == 0).only_enforce_if( - [ - night_shift_today_variable, - night_shift_tomorrow_variable.Not(), - night_shift_tomorrow_variable_special.Not(), - ] - ) - model.add(day_tomorrow_variable == 0).only_enforce_if( - [ - night_shift_today_variable_special, - night_shift_tomorrow_variable.Not(), - night_shift_tomorrow_variable_special.Not(), - ] - ) + for night_shift_today_variable in night_shift_today_variables: + model.add(day_tomorrow_variable == 0).only_enforce_if( + [ + night_shift_today_variable, + *[ + night_shift_tomorrow_variable.Not() + for night_shift_tomorrow_variable in night_shift_tomorrow_variables + ], + ] + ) + + def _find_shift_by_id(self, shift_id: int) -> Shift | None: + return next((shift for shift in self._shifts if shift.get_id() == shift_id), None) diff --git a/tests/integration/helpers/__init__.py b/tests/integration/helpers/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/integration/helpers/solution_assertions.py b/tests/integration/helpers/solution_assertions.py new file mode 100644 index 00000000..ea222cd6 --- /dev/null +++ b/tests/integration/helpers/solution_assertions.py @@ -0,0 +1,138 @@ +import ast +from collections import Counter +from dataclasses import dataclass +from datetime import date + +from src.employee import Employee +from src.shift import Shift +from tests.integration.helpers.solver_fixtures import MinStaffing + +WEEKDAY_ABBREVIATIONS: dict[int, str] = { + 1: "Mo", + 2: "Di", + 3: "Mi", + 4: "Do", + 5: "Fr", + 6: "Sa", + 7: "So", +} + + +@dataclass(frozen=True) +class Assignment: + employee_id: int + day: date + shift_id: int + + +def active_shift_assignments(solution_variables: dict[str, int]) -> list[Assignment]: + assignments: list[Assignment] = [] + + for variable_name, value in solution_variables.items(): + if value != 1: + continue + + try: + employee_id, day_value, shift_id = ast.literal_eval(variable_name) + except (SyntaxError, ValueError): + continue + + if not isinstance(employee_id, int): + continue + if not isinstance(day_value, str): + continue + if not isinstance(shift_id, int): + continue + + assignments.append( + Assignment( + employee_id=employee_id, + day=date.fromisoformat(day_value), + shift_id=shift_id, + ) + ) + + return assignments + + +def assert_solution_found(status_name: str) -> None: + assert status_name in {"FEASIBLE", "OPTIMAL"}, f"Expected feasible solver result, got {status_name}" + + +def assert_only_known_employees_assigned(assignments: list[Assignment], employees: list[Employee]) -> None: + known_employee_ids = {employee.get_key() for employee in employees} + assigned_employee_ids = {assignment.employee_id for assignment in assignments} + + assert assigned_employee_ids <= known_employee_ids, ( + f"Solution contains assignments for unknown employees: {sorted(assigned_employee_ids - known_employee_ids)}" + ) + + +def assert_no_more_than_one_shift_per_employee_day(assignments: list[Assignment]) -> None: + counts = Counter((assignment.employee_id, assignment.day) for assignment in assignments) + duplicates = {key: count for key, count in counts.items() if count > 1} + + assert not duplicates, f"Employees assigned to more than one shift per day: {duplicates}" + + +def assert_unavailable_employees_not_assigned(assignments: list[Assignment], employees: list[Employee]) -> None: + employee_by_id = {employee.get_key(): employee for employee in employees} + + invalid_assignments = [ + assignment + for assignment in assignments + if assignment.day.day in employee_by_id[assignment.employee_id].vacation_days + or employee_by_id[assignment.employee_id].unavailable(assignment.day) + ] + + assert not invalid_assignments, f"Unavailable employees were assigned: {invalid_assignments}" + + +def assert_no_exclusive_shift_assigned(assignments: list[Assignment], shifts: list[Shift]) -> None: + exclusive_shift_ids = {shift.get_id() for shift in shifts if shift.is_exclusive} + invalid_assignments = [assignment for assignment in assignments if assignment.shift_id in exclusive_shift_ids] + + assert not invalid_assignments, ( + f"Exclusive/preplanned shifts leaked into generated assignments: {invalid_assignments}" + ) + + +def assert_min_staffing_is_covered( + *, + assignments: list[Assignment], + employees: list[Employee], + shifts: list[Shift], + days: list[date], + min_staffing: MinStaffing, +) -> None: + employee_by_id = {employee.get_key(): employee for employee in employees} + shift_by_id = {shift.get_id(): shift for shift in shifts} + + assignment_counts: Counter[tuple[str, date, str]] = Counter() + + for assignment in assignments: + employee = employee_by_id[assignment.employee_id] + shift = shift_by_id[assignment.shift_id] + + if shift.is_exclusive: + continue + + assignment_counts[(employee.level, assignment.day, shift.abbreviation)] += 1 + + mismatches: list[str] = [] + + for employee_level, staffing_by_weekday in min_staffing.items(): + for day in days: + weekday = WEEKDAY_ABBREVIATIONS[day.isoweekday()] + required_by_shift = staffing_by_weekday[weekday] + + for shift_abbreviation, required_count in required_by_shift.items(): + actual_count = assignment_counts[(employee_level, day, shift_abbreviation)] + + if actual_count != required_count: + mismatches.append( + f"{employee_level} {day.isoformat()} {shift_abbreviation}: " + f"expected {required_count}, got {actual_count}" + ) + + assert not mismatches, "Minimum staffing mismatches:\n" + "\n".join(mismatches) diff --git a/tests/integration/helpers/solver_fixtures.py b/tests/integration/helpers/solver_fixtures.py new file mode 100644 index 00000000..958b5d4e --- /dev/null +++ b/tests/integration/helpers/solver_fixtures.py @@ -0,0 +1,182 @@ +from dataclasses import dataclass +from datetime import date, timedelta + +from src.employee import Employee +from src.shift import Shift + +type WeekdayAbbreviation = str +type EmployeeLevel = str +type ShiftAbbreviation = str +type MinStaffing = dict[EmployeeLevel, dict[WeekdayAbbreviation, dict[ShiftAbbreviation, int]]] + + +TEST_SOLVER_WEIGHTS: dict[str, int] = { + "free_weekend": 1, + "consecutive_nights": 1, + "hidden": 1, + "overtime": 1, + "consecutive_days": 1, + "rotate": 1, + "wishes": 1, + "after_night": 1, + "second_weekend": 1, + "preferred_block": 1, +} + + +@dataclass(frozen=True) +class CleanSolverFixture: + unit: int + start_date: date + end_date: date + days: list[date] + shifts: list[Shift] + employees: list[Employee] + min_staffing: MinStaffing + + +def make_standard_shifts() -> list[Shift]: + return [ + Shift(Shift.EARLY, "Früh", 360, 820), + Shift(Shift.INTERMEDIATE, "Zwischen", 480, 940), + Shift(Shift.LATE, "Spät", 805, 1265), + Shift(Shift.NIGHT, "Nacht", 1250, 375), + ] + + +def make_days(start_date: date, number_of_days: int) -> list[date]: + return [start_date + timedelta(days=offset) for offset in range(number_of_days)] + + +def make_employee( + *, + key: int, + name: str, + level: str, + target_working_time: int, + forbidden_days: list[int] | None = None, + wish_days: list[int] | None = None, + wish_shifts: list[tuple[int, str]] | None = None, + qualifications: list[str] | None = None, +) -> Employee: + return Employee( + key=key, + surname="Clean", + name=name, + level=level, + type=f"Test-{level}", + target_working_time=target_working_time, + actual_working_time=0, + forbidden_days=forbidden_days or [], + forbidden_shifts=[], + vacation_days=[], + vacation_shifts=[], + wish_days=wish_days or [], + wish_shifts=wish_shifts or [], + planned_shifts=[], + qualifications=qualifications or [], + ) + + +def empty_week_staffing() -> dict[str, dict[str, int]]: + return { + "Mo": {"F": 0, "Z": 0, "S": 0, "N": 0}, + "Di": {"F": 0, "Z": 0, "S": 0, "N": 0}, + "Mi": {"F": 0, "Z": 0, "S": 0, "N": 0}, + "Do": {"F": 0, "Z": 0, "S": 0, "N": 0}, + "Fr": {"F": 0, "Z": 0, "S": 0, "N": 0}, + "Sa": {"F": 0, "Z": 0, "S": 0, "N": 0}, + "So": {"F": 0, "Z": 0, "S": 0, "N": 0}, + } + + +def make_two_day_fachkraft_early_fixture() -> CleanSolverFixture: + """Smallest useful solver fixture that avoids polluted JSON/SQL data.""" + start_date = date(2024, 11, 2) + days = make_days(start_date, 2) + + fachkraft_staffing = empty_week_staffing() + fachkraft_staffing["Sa"]["F"] = 1 + fachkraft_staffing["So"]["F"] = 1 + + return CleanSolverFixture( + unit=999, + start_date=start_date, + end_date=days[-1], + days=days, + shifts=make_standard_shifts(), + employees=[ + make_employee( + key=1, + name="Alice", + level="Fachkraft", + target_working_time=460, + ), + make_employee( + key=2, + name="Bob", + level="Fachkraft", + target_working_time=460, + ), + ], + min_staffing={ + "Fachkraft": fachkraft_staffing, + }, + ) + + +def make_one_week_two_level_reference_fixture() -> CleanSolverFixture: + """Reference fixture for validating current hard scheduling invariants.""" + start_date = date(2024, 11, 4) + days = make_days(start_date, 7) + + fachkraft_staffing = empty_week_staffing() + hilfskraft_staffing = empty_week_staffing() + + for weekday in fachkraft_staffing: + fachkraft_staffing[weekday]["F"] = 1 + hilfskraft_staffing[weekday]["S"] = 1 + + return CleanSolverFixture( + unit=1000, + start_date=start_date, + end_date=days[-1], + days=days, + shifts=make_standard_shifts(), + employees=[ + make_employee( + key=1, + name="Alice", + level="Fachkraft", + target_working_time=1840, + forbidden_days=[6], + qualifications=["rounds"], + ), + make_employee( + key=2, + name="Bob", + level="Fachkraft", + target_working_time=1840, + wish_days=[10], + qualifications=["rounds"], + ), + make_employee( + key=3, + name="Carla", + level="Hilfskraft", + target_working_time=1840, + forbidden_days=[8], + ), + make_employee( + key=4, + name="David", + level="Hilfskraft", + target_working_time=1840, + wish_shifts=[(9, "S")], + ), + ], + min_staffing={ + "Fachkraft": fachkraft_staffing, + "Hilfskraft": hilfskraft_staffing, + }, + ) diff --git a/tests/integration/test_solver_pipeline.py b/tests/integration/test_solver_pipeline.py new file mode 100644 index 00000000..ea980a1b --- /dev/null +++ b/tests/integration/test_solver_pipeline.py @@ -0,0 +1,91 @@ +import pytest + +from src.solve import main as run_solver +from tests.integration.helpers.solution_assertions import ( + active_shift_assignments, + assert_min_staffing_is_covered, + assert_no_exclusive_shift_assigned, + assert_no_more_than_one_shift_per_employee_day, + assert_only_known_employees_assigned, + assert_solution_found, + assert_unavailable_employees_not_assigned, +) +from tests.integration.helpers.solver_fixtures import ( + TEST_SOLVER_WEIGHTS, + CleanSolverFixture, + make_one_week_two_level_reference_fixture, + make_two_day_fachkraft_early_fixture, +) + + +def inject_fixture_at_solver_loader_boundary( + monkeypatch: pytest.MonkeyPatch, + fixture: CleanSolverFixture, +) -> None: + """Inject clean test data while keeping the real solver/model path.""" + monkeypatch.setattr("src.solve.FSLoader.get_days", lambda self, start_date, end_date: fixture.days) + monkeypatch.setattr("src.solve.FSLoader.get_shifts", lambda self: fixture.shifts) + monkeypatch.setattr("src.solve.FSLoader.get_min_staffing", lambda self: fixture.min_staffing) + monkeypatch.setattr("src.solve.FSLoader.write_solution", lambda self, solution, solution_name: None) + + +@pytest.mark.integration +def test_two_day_fachkraft_early_fixture_solves_successfully(monkeypatch: pytest.MonkeyPatch) -> None: + fixture = make_two_day_fachkraft_early_fixture() + inject_fixture_at_solver_loader_boundary(monkeypatch, fixture) + + result = run_solver( + unit=fixture.unit, + start_date=fixture.start_date, + end_date=fixture.end_date, + timeout=10, + employees=fixture.employees, + weights=TEST_SOLVER_WEIGHTS, + ) + + assert_solution_found(result.solution.status_name) + + assignments = active_shift_assignments(result.solution.variables) + + assert_only_known_employees_assigned(assignments, fixture.employees) + assert_no_more_than_one_shift_per_employee_day(assignments) + assert_min_staffing_is_covered( + assignments=assignments, + employees=fixture.employees, + shifts=fixture.shifts, + days=fixture.days, + min_staffing=fixture.min_staffing, + ) + + +@pytest.mark.integration +def test_one_week_two_level_reference_fixture_satisfies_basic_invariants( + monkeypatch: pytest.MonkeyPatch, +) -> None: + fixture = make_one_week_two_level_reference_fixture() + inject_fixture_at_solver_loader_boundary(monkeypatch, fixture) + + result = run_solver( + unit=fixture.unit, + start_date=fixture.start_date, + end_date=fixture.end_date, + timeout=10, + employees=fixture.employees, + weights=TEST_SOLVER_WEIGHTS, + ) + + assert_solution_found(result.solution.status_name) + + assignments = active_shift_assignments(result.solution.variables) + + assert_only_known_employees_assigned(assignments, fixture.employees) + assert_no_more_than_one_shift_per_employee_day(assignments) + assert_unavailable_employees_not_assigned(assignments, fixture.employees) + assert_no_exclusive_shift_assigned(assignments, fixture.shifts) + assert_min_staffing_is_covered( + assignments=assignments, + employees=fixture.employees, + shifts=fixture.shifts, + days=fixture.days, + min_staffing=fixture.min_staffing, + ) From 555975a4ba60bc66d9627ae2795756bde7f1e05e Mon Sep 17 00:00:00 2001 From: Tom Wawerek Date: Wed, 3 Jun 2026 13:30:22 +0200 Subject: [PATCH 02/18] fix: merge --- .../free_day_after_night_shift_phase.py | 55 ++++++++++--------- 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/src/cp/constraints/free_day_after_night_shift_phase.py b/src/cp/constraints/free_day_after_night_shift_phase.py index 35654725..af45fffb 100644 --- a/src/cp/constraints/free_day_after_night_shift_phase.py +++ b/src/cp/constraints/free_day_after_night_shift_phase.py @@ -17,6 +17,9 @@ def KEY(self) -> str: return "free-day-after-night-shift-phase" def __init__(self, employees: list[Employee], days: list[Day], shifts: list[Shift]): + """ + Initializes the constraint that ensures an employee has a free day after a night shift phase. + """ super().__init__(employees, days, shifts) def create( @@ -25,36 +28,36 @@ def create( shift_assignment_variables: ShiftAssignmentVariables, employee_works_on_day_variables: EmployeeWorksOnDayVariables, ) -> None: - regular_night_shift = self._find_shift_by_id(Shift.NIGHT) - special_night_shift = self._find_shift_by_id(SPECIAL_NIGHT_SHIFT_INDEX) - - night_shifts = [shift for shift in (regular_night_shift, special_night_shift) if shift is not None] - - if not night_shifts: - return + # This function falsely ignores special night shifts for employee in self._employees: for day in self._days[:-1]: - tomorrow = day + timedelta(days=1) - day_tomorrow_variable = employee_works_on_day_variables[employee][tomorrow] + night_shift_today_variable = shift_assignment_variables[employee][day][self._shifts[Shift.NIGHT]] + night_shift_tomorrow_variable = shift_assignment_variables[employee][day + timedelta(1)][ + self._shifts[Shift.NIGHT] + ] - night_shift_today_variables = [ - shift_assignment_variables[employee][day][shift] for shift in night_shifts + # N5 is a special form of night shifts + night_shift_today_variable_special = shift_assignment_variables[employee][day][ + self._shifts[SPECIAL_NIGHT_SHIFT_INDEX] ] - night_shift_tomorrow_variables = [ - shift_assignment_variables[employee][tomorrow][shift] for shift in night_shifts + night_shift_tomorrow_variable_special = shift_assignment_variables[employee][day + timedelta(1)][ + self._shifts[SPECIAL_NIGHT_SHIFT_INDEX] ] - for night_shift_today_variable in night_shift_today_variables: - model.add(day_tomorrow_variable == 0).only_enforce_if( - [ - night_shift_today_variable, - *[ - night_shift_tomorrow_variable.Not() - for night_shift_tomorrow_variable in night_shift_tomorrow_variables - ], - ] - ) - - def _find_shift_by_id(self, shift_id: int) -> Shift | None: - return next((shift for shift in self._shifts if shift.get_id() == shift_id), None) + day_tomorrow_variable = employee_works_on_day_variables[employee][day + timedelta(1)] + # where are day_tomorrow_variables enforced? this may be the cause of the bug menitioned in the docs + model.add(day_tomorrow_variable == 0).only_enforce_if( + [ + night_shift_today_variable, + night_shift_tomorrow_variable.Not(), + night_shift_tomorrow_variable_special.Not(), + ] + ) + model.add(day_tomorrow_variable == 0).only_enforce_if( + [ + night_shift_today_variable_special, + night_shift_tomorrow_variable.Not(), + night_shift_tomorrow_variable_special.Not(), + ] + ) From 3511c7df37f134440d53e75dc01c2a950626eff7 Mon Sep 17 00:00:00 2001 From: Tom Wawerek Date: Mon, 8 Jun 2026 22:04:55 +0200 Subject: [PATCH 03/18] feat: cache timeoffice station-month scheduling data --- .gitignore | 1 + pyproject.toml | 2 + src/main.py | 66 +++++-- src/scheduling/__init__.py | 0 src/scheduling/models/__init__.py | 0 src/scheduling/models/core.py | 22 +++ src/scheduling/models/dataset.py | 55 ++++++ src/scheduling/models/demand.py | 21 ++ src/scheduling/models/employee.py | 19 ++ src/scheduling/models/relations.py | 114 +++++++++++ src/scheduling/models/shift.py | 32 +++ src/scheduling/models/station.py | 9 + src/scheduling/timeoffice/__init__.py | 0 src/scheduling/timeoffice/cache.py | 65 +++++++ src/scheduling/timeoffice/database.py | 29 +++ src/scheduling/timeoffice/mapping.py | 72 +++++++ src/scheduling/timeoffice/models.py | 53 +++++ src/scheduling/timeoffice/service.py | 76 ++++++++ src/scheduling/timeoffice/settings.py | 21 ++ tests/__init__.py | 0 tests/integration/__init__.py | 0 tests/integration/helpers/smoke_fixtures.py | 124 ++++++++++++ .../helpers/solution_assertions.py | 138 ------------- tests/integration/helpers/solver_fixtures.py | 182 ------------------ tests/integration/smoke_test.py | 62 ++++++ tests/integration/test_solver_pipeline.py | 91 --------- uv.lock | 21 +- 27 files changed, 845 insertions(+), 430 deletions(-) create mode 100644 src/scheduling/__init__.py create mode 100644 src/scheduling/models/__init__.py create mode 100644 src/scheduling/models/core.py create mode 100644 src/scheduling/models/dataset.py create mode 100644 src/scheduling/models/demand.py create mode 100644 src/scheduling/models/employee.py create mode 100644 src/scheduling/models/relations.py create mode 100644 src/scheduling/models/shift.py create mode 100644 src/scheduling/models/station.py create mode 100644 src/scheduling/timeoffice/__init__.py create mode 100644 src/scheduling/timeoffice/cache.py create mode 100644 src/scheduling/timeoffice/database.py create mode 100644 src/scheduling/timeoffice/mapping.py create mode 100644 src/scheduling/timeoffice/models.py create mode 100644 src/scheduling/timeoffice/service.py create mode 100644 src/scheduling/timeoffice/settings.py create mode 100644 tests/__init__.py create mode 100644 tests/integration/__init__.py create mode 100644 tests/integration/helpers/smoke_fixtures.py delete mode 100644 tests/integration/helpers/solution_assertions.py delete mode 100644 tests/integration/helpers/solver_fixtures.py create mode 100644 tests/integration/smoke_test.py delete mode 100644 tests/integration/test_solver_pipeline.py diff --git a/.gitignore b/.gitignore index 23260820..fc866e2d 100644 --- a/.gitignore +++ b/.gitignore @@ -57,6 +57,7 @@ logs/ found_solutions/*.json processed_solutions/* +.cache cases/*/ !cases/case_catalog.md diff --git a/pyproject.toml b/pyproject.toml index a507fab3..490ac1bd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,6 +17,8 @@ dependencies = [ "pytest>=9.0.1", "uvicorn>=0.34.0", "dotenv>=0.9.9", + "pydantic>=2.12.5", + "pydantic-settings>=2.14.1", ] [dependency-groups] diff --git a/src/main.py b/src/main.py index df8be579..9d1ae912 100644 --- a/src/main.py +++ b/src/main.py @@ -2,17 +2,64 @@ import click -from src.db.export_main import main as fetcher from src.db.import_main import main as inserter from src.loader import FSLoader +from src.scheduling.timeoffice.models import FetchStationsRequest, PlanningPeriod +from src.scheduling.timeoffice.service import create_timeoffice_service from src.services.solve_service import execute_solve, execute_solve_multiple from src.web import App @click.group() -def cli(): +@click.pass_context +def cli(ctx: click.Context): """Staff Scheduling CLI""" - pass + + +@cli.command() +@click.option( + "--station", + "stations", + multiple=True, + type=int, + required=True, + help="Planning unit/station to fetch. Can be passed multiple times.", +) +@click.option( + "--use-cache", + is_flag=True, + help="Try loading cached TimeOffice data before reading the database.", +) +@click.argument("start", type=click.DateTime(formats=["%d.%m.%Y"])) +@click.argument("end", type=click.DateTime(formats=["%d.%m.%Y"])) +def fetch( + stations: tuple[int, ...], + use_cache: bool, + start: datetime, + end: datetime, +): + """Fetch TimeOffice data and update the local cache.""" + request = FetchStationsRequest( + station_ids=stations, + period=PlanningPeriod( + start=start.date(), + end=end.date(), + ), + use_cache=use_cache, + ) + + timeoffice = create_timeoffice_service() + dataset = timeoffice.fetch(request) + + click.echo( + "Prepared scheduling dataset: " + f"stations={len(dataset.stations)}, " + f"regular={list(dataset.regular_station_ids)}, " + f"jump_pool={list(dataset.jump_pool_station_ids)}, " + f"employees={len(dataset.employees)}, " + f"shifts={len(dataset.shifts)}, " + f"demand={len(dataset.demand)}" + ) @cli.command() @@ -100,19 +147,6 @@ def plot(case: int, debug: bool): app.run(debug=debug) -@cli.command() -@click.argument("unit", type=click.INT) -@click.argument("start", type=click.DateTime(formats=["%d.%m.%Y"])) -@click.argument("end", type=click.DateTime(formats=["%d.%m.%Y"])) -def fetch(unit: int, start: datetime, end: datetime): - """ - Fetch data from the DB and write Json Files - """ - start_date = start.date() # convert datetime.datetime to datetime.date - end_date = end.date() - fetcher(planning_unit=unit, from_date=start_date, till_date=end_date) - - @cli.command() @click.argument("unit", type=click.INT) @click.argument("start", type=click.DateTime(formats=["%d.%m.%Y"])) diff --git a/src/scheduling/__init__.py b/src/scheduling/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/scheduling/models/__init__.py b/src/scheduling/models/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/scheduling/models/core.py b/src/scheduling/models/core.py new file mode 100644 index 00000000..78679272 --- /dev/null +++ b/src/scheduling/models/core.py @@ -0,0 +1,22 @@ +from datetime import date +from typing import Self + +from pydantic import BaseModel, model_validator + + +class PlanningPeriod(BaseModel): + """Inclusive planning period for a scheduling run.""" + + start: date + end: date + + @model_validator(mode="after") + def end_must_not_be_before_start(self) -> Self: + if self.end < self.start: + raise ValueError("Planning period end date must not be before start date.") + return self + + @property + def month_folder(self) -> str: + """Return the cache month folder for this period.""" + return f"{self.start.month:02d}_{self.start.year}" diff --git a/src/scheduling/models/dataset.py b/src/scheduling/models/dataset.py new file mode 100644 index 00000000..ad4a0555 --- /dev/null +++ b/src/scheduling/models/dataset.py @@ -0,0 +1,55 @@ +from pydantic import BaseModel + +from src.scheduling.models.core import PlanningPeriod +from src.scheduling.models.demand import Demand +from src.scheduling.models.employee import Employee +from src.scheduling.models.relations import ( + Assignment, + Availability, + Membership, + Preference, + Rule, +) +from src.scheduling.models.shift import Shift +from src.scheduling.models.station import Station + + +class StationMonthData(BaseModel): + """Scheduling-relevant data for one station and one planning period. + + This is the canonical cache payload. It is not TimeOffice-specific and it is + not solver-specific. + """ + + schema_version: int = 1 + + station: Station + period: PlanningPeriod + + employees: tuple[Employee, ...] = () + shifts: tuple[Shift, ...] = () + demand: tuple[Demand, ...] = () + memberships: tuple[Membership, ...] = () + assignments: tuple[Assignment, ...] = () + availability: tuple[Availability, ...] = () + rules: tuple[Rule, ...] = () + preferences: tuple[Preference, ...] = () + + +class SchedulingDataset(BaseModel): + """Combined scheduling data for one solver run.""" + + period: PlanningPeriod + + stations: tuple[Station, ...] + regular_station_ids: tuple[int, ...] + jump_pool_station_ids: tuple[int, ...] = () + + employees: tuple[Employee, ...] = () + shifts: tuple[Shift, ...] = () + demand: tuple[Demand, ...] = () + memberships: tuple[Membership, ...] = () + assignments: tuple[Assignment, ...] = () + availability: tuple[Availability, ...] = () + rules: tuple[Rule, ...] = () + preferences: tuple[Preference, ...] = () diff --git a/src/scheduling/models/demand.py b/src/scheduling/models/demand.py new file mode 100644 index 00000000..59b453b4 --- /dev/null +++ b/src/scheduling/models/demand.py @@ -0,0 +1,21 @@ +from datetime import date +from typing import Literal + +from pydantic import BaseModel, Field + + +class Demand(BaseModel): + """Staffing need or optional coverage goal for a station/date/shift.""" + + station_id: int = Field(gt=0) + date: date + shift_id: str + + required_count: int = Field(ge=0) + + required_group_id: str | None = None + required_qualification_id: str | None = None + + demand_type: Literal["minimum", "optional"] = "minimum" + priority: int = Field(default=0, ge=0) + weight: int = Field(default=1, ge=0) diff --git a/src/scheduling/models/employee.py b/src/scheduling/models/employee.py new file mode 100644 index 00000000..fde42a9c --- /dev/null +++ b/src/scheduling/models/employee.py @@ -0,0 +1,19 @@ +from pydantic import BaseModel, Field + + +class Employee(BaseModel): + """Employee identity and stable staffing classification. + + Period-specific facts like availability, assignments, wishes, and station + memberships are represented as separate relationship records. + """ + + employee_id: int = Field(gt=0) + personnel_number: str | None = None + + first_name: str | None = None + last_name: str | None = None + display_name: str + + group_id: str | None = None + active: bool = True diff --git a/src/scheduling/models/relations.py b/src/scheduling/models/relations.py new file mode 100644 index 00000000..8d740b3e --- /dev/null +++ b/src/scheduling/models/relations.py @@ -0,0 +1,114 @@ +from datetime import date as Date +from typing import Literal + +from pydantic import BaseModel, Field + + +class Membership(BaseModel): + """Employee membership in a station-local or jump-pool staffing pool.""" + + employee_id: int = Field(gt=0) + station_id: int = Field(gt=0) + + membership_type: Literal["local", "jump_pool", "external", "unknown"] = "local" + + valid_from: Date | None = None + valid_until: Date | None = None + + is_home_station: bool | None = None + is_substitute: bool | None = None + + +class Assignment(BaseModel): + """Known, planned, fixed, or externally blocking assignment.""" + + employee_id: int = Field(gt=0) + date: Date + shift_id: str + station_id: int | None = None + + assignment_type: Literal["planned", "fixed", "external", "management"] = "planned" + + counts_as_work: bool = True + counts_for_minimum_staffing: bool | None = None + + source: str | None = None + source_assignment_id: str | None = None + source_shift_id: int | None = None + source_code: str | None = None + + +class Availability(BaseModel): + """Employee availability or unavailability information.""" + + employee_id: int = Field(gt=0) + date: Date + + availability_type: Literal[ + "unavailable", + "vacation", + "training", + "free_weekend", + "available_only", + ] + + shift_ids: tuple[str, ...] | None = None + is_hard: bool = True + + source: str | None = None + source_code: str | None = None + source_id: str | None = None + + +class Rule(BaseModel): + """Generic scheduling rule or special-case restriction. + + Keep this typed by rule_type. If one rule type becomes complex, extract a + dedicated model later. + """ + + rule_id: str + rule_type: Literal[ + "medical_night_ban", + "night_watch_only", + "weekday_early_only", + "fixed_weekday_free", + "does_not_count_for_minimum_staffing", + "no_night_before_protected_free_time", + "max_consecutive_days", + "min_rest_time", + "other", + ] + + employee_id: int | None = None + station_id: int | None = None + + date: Date | None = None + weekdays: tuple[int, ...] | None = None + shift_ids: tuple[str, ...] | None = None + qualification_id: str | None = None + + is_hard: bool = True + description: str | None = None + + +class Preference(BaseModel): + """Soft employee wish or recurring preference.""" + + employee_id: int = Field(gt=0) + + preference_type: Literal[ + "day_off", + "shift_off", + "shift_on", + "work_any", + "avoid_shift", + "prefer_shift", + ] + + date: Date | None = None + weekdays: tuple[int, ...] | None = None + shift_ids: tuple[str, ...] | None = None + + weight: int = Field(default=1, ge=0) + source: str | None = None diff --git a/src/scheduling/models/shift.py b/src/scheduling/models/shift.py new file mode 100644 index 00000000..74e008e7 --- /dev/null +++ b/src/scheduling/models/shift.py @@ -0,0 +1,32 @@ +from typing import Literal + +from pydantic import BaseModel, Field + + +class Shift(BaseModel): + """Canonical assignable shift definition. + + One Shift represents one assignable shift variant. Similar shifts can be + grouped through shift_group_id, e.g. several TimeOffice night shifts can all + belong to group "night" while keeping their own shift_id/source metadata. + """ + + shift_id: str + shift_group_id: str | None = None + name: str + + source_shift_id: int | None = None + source_code: str | None = None + + kind: Literal["early", "intermediate", "late", "night", "management", "other"] + + start_minute: int = Field(ge=0, lt=24 * 60) + end_minute: int = Field(ge=0, lt=24 * 60) + ends_next_day: bool = False + + break_minutes: int = Field(default=0, ge=0) + net_work_minutes: int = Field(ge=0) + + counts_as_work: bool = True + counts_for_minimum_staffing: bool = True + is_night: bool = False diff --git a/src/scheduling/models/station.py b/src/scheduling/models/station.py new file mode 100644 index 00000000..0c306908 --- /dev/null +++ b/src/scheduling/models/station.py @@ -0,0 +1,9 @@ +from pydantic import BaseModel, Field + + +class Station(BaseModel): + """Hospital station or source planning unit.""" + + station_id: int = Field(gt=0) + name: str | None = None + source_planning_unit_id: int | None = None diff --git a/src/scheduling/timeoffice/__init__.py b/src/scheduling/timeoffice/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/scheduling/timeoffice/cache.py b/src/scheduling/timeoffice/cache.py new file mode 100644 index 00000000..0f264ecc --- /dev/null +++ b/src/scheduling/timeoffice/cache.py @@ -0,0 +1,65 @@ +from pathlib import Path + +from src.scheduling.models.core import PlanningPeriod +from src.scheduling.models.dataset import StationMonthData +from src.scheduling.timeoffice.models import CacheWriteResult +from src.scheduling.timeoffice.settings import TimeOfficeSettings + + +class TimeOfficeCache: + """Local file cache for mapped scheduling data from TimeOffice. + + The cache stores canonical StationMonthData objects, not raw TimeOffice table + dumps and not solver-specific objects. + """ + + STATION_MONTH_DATA_FILE = "station_month_data.json" + + def __init__(self, settings: TimeOfficeSettings): + self._settings = settings + + def station_month_directory(self, station_id: int, period: PlanningPeriod) -> Path: + """Return the cache directory for one station/month.""" + return self._settings.cache_root / str(station_id) / period.month_folder + + def station_month_data_path(self, station_id: int, period: PlanningPeriod) -> Path: + """Return the station-month data JSON path.""" + return self.station_month_directory(station_id, period) / self.STATION_MONTH_DATA_FILE + + def read_many(self, station_ids: tuple[int, ...], period: PlanningPeriod) -> tuple[StationMonthData, ...]: + """Read multiple station-month data objects from cache.""" + print("[timeoffice] cache.read_many") + return tuple(self.read(station_id, period) for station_id in station_ids) + + def read(self, station_id: int, period: PlanningPeriod) -> StationMonthData: + """Read one station-month data object from cache.""" + data_path = self.station_month_data_path(station_id, period) + + print(f"[timeoffice] cache.read station={station_id} path={data_path}") + + return StationMonthData.model_validate_json(data_path.read_text(encoding="utf-8")) + + def write_many(self, station_data: tuple[StationMonthData, ...]) -> tuple[CacheWriteResult, ...]: + """Write multiple station-month data objects.""" + print("[timeoffice] cache.write_many") + return tuple(self.write(data) for data in station_data) + + def write(self, data: StationMonthData) -> CacheWriteResult: + """Write one station-month data object.""" + station_id = data.station.station_id + cache_directory = self.station_month_directory(station_id, data.period) + cache_directory.mkdir(parents=True, exist_ok=True) + + data_path = self.station_month_data_path(station_id, data.period) + data_path.write_text( + data.model_dump_json(indent=2), + encoding="utf-8", + ) + + print(f"[timeoffice] cache.write station={station_id} path={data_path}") + + return CacheWriteResult( + station_id=station_id, + period=data.period, + cache_directory=cache_directory, + ) diff --git a/src/scheduling/timeoffice/database.py b/src/scheduling/timeoffice/database.py new file mode 100644 index 00000000..4d26a11b --- /dev/null +++ b/src/scheduling/timeoffice/database.py @@ -0,0 +1,29 @@ +from src.scheduling.timeoffice.models import FetchStationsRequest, TimeOfficeSourceData +from src.scheduling.timeoffice.settings import TimeOfficeSettings + + +class TimeOfficeDatabase: + """Read source data from the TimeOffice database. + + This class owns database access and SQL queries. + """ + + def __init__(self, settings: TimeOfficeSettings): + self._settings = settings + + def read(self, request: FetchStationsRequest) -> TimeOfficeSourceData: + """Read all source data needed to build StationMonthData. + + Current iteration: shallow placeholder. + Later iterations: run optimized multi-station SQL queries. + """ + print( + "[timeoffice] database.read " + f"stations={list(request.station_ids)} " + f"period={request.period.start.isoformat()}..{request.period.end.isoformat()}" + ) + + return TimeOfficeSourceData( + station_ids=request.station_ids, + period=request.period, + ) diff --git a/src/scheduling/timeoffice/mapping.py b/src/scheduling/timeoffice/mapping.py new file mode 100644 index 00000000..fa4c7940 --- /dev/null +++ b/src/scheduling/timeoffice/mapping.py @@ -0,0 +1,72 @@ +from src.scheduling.models.dataset import SchedulingDataset, StationMonthData +from src.scheduling.models.station import Station +from src.scheduling.timeoffice.models import TimeOfficeSourceData +from src.scheduling.timeoffice.settings import TimeOfficeSettings + + +class TimeOfficeMapper: + """Map TimeOffice source data to canonical scheduling data. + + Boundary rules: + - May know TimeOffice source meanings and mapping rules. + - Must not query the database. + - Must not read/write cache files. + - Must not create solver variables or solver input. + """ + + def __init__(self, settings: TimeOfficeSettings): + self._settings = settings + + def to_station_month_data(self, source_data: TimeOfficeSourceData) -> tuple[StationMonthData, ...]: + """Map TimeOffice source data into one StationMonthData object per station. + + Current iteration: shallow placeholder. + """ + print("[timeoffice] mapper.to_station_month_data") + + return tuple( + StationMonthData( + station=Station( + station_id=station_id, + source_planning_unit_id=station_id, + ), + period=source_data.period, + ) + for station_id in source_data.station_ids + ) + + def combine_station_month_data(self, station_month_data: tuple[StationMonthData, ...]) -> SchedulingDataset: + """Combine station-month data into one scheduling dataset. + + Current iteration: shallow concatenation without deduplication. + """ + print("[timeoffice] mapper.combine_station_month_data") + + if not station_month_data: + raise ValueError("Cannot combine empty station-month data.") + + period = station_month_data[0].period + station_ids = tuple(data.station.station_id for data in station_month_data) + + effective_jump_pool_ids = tuple( + station_id for station_id in station_ids if station_id in self._settings.jump_pool_station_ids + ) + + regular_station_ids = tuple( + station_id for station_id in station_ids if station_id not in effective_jump_pool_ids + ) + + return SchedulingDataset( + period=period, + stations=tuple(data.station for data in station_month_data), + regular_station_ids=regular_station_ids, + jump_pool_station_ids=effective_jump_pool_ids, + employees=tuple(employee for data in station_month_data for employee in data.employees), + shifts=tuple(shift for data in station_month_data for shift in data.shifts), + demand=tuple(demand for data in station_month_data for demand in data.demand), + memberships=tuple(membership for data in station_month_data for membership in data.memberships), + assignments=tuple(assignment for data in station_month_data for assignment in data.assignments), + availability=tuple(item for data in station_month_data for item in data.availability), + rules=tuple(rule for data in station_month_data for rule in data.rules), + preferences=tuple(preference for data in station_month_data for preference in data.preferences), + ) diff --git a/src/scheduling/timeoffice/models.py b/src/scheduling/timeoffice/models.py new file mode 100644 index 00000000..03c083a8 --- /dev/null +++ b/src/scheduling/timeoffice/models.py @@ -0,0 +1,53 @@ +from pathlib import Path + +from pydantic import BaseModel, field_validator + +from src.scheduling.models.core import PlanningPeriod + + +class FetchStationsRequest(BaseModel): + """Request to provide scheduling data for one or more TimeOffice stations. + + If use_cache is true, cached StationMonthData is attempted first. If cache + data is missing or invalid, the service falls back to database reads. + """ + + station_ids: tuple[int, ...] + period: PlanningPeriod + use_cache: bool = False + + @field_validator("station_ids") + @classmethod + def station_ids_must_not_be_empty(cls, station_ids: tuple[int, ...]) -> tuple[int, ...]: + if not station_ids: + raise ValueError("At least one station id is required.") + return station_ids + + @field_validator("station_ids") + @classmethod + def station_ids_must_be_positive(cls, station_ids: tuple[int, ...]) -> tuple[int, ...]: + invalid_station_ids = [station_id for station_id in station_ids if station_id <= 0] + if invalid_station_ids: + raise ValueError(f"Station ids must be positive: {invalid_station_ids}") + return station_ids + + +class TimeOfficeSourceData(BaseModel): + """Raw TimeOffice source data read from the database. + + Current iteration: shallow placeholder. + + Later this should hold typed source-record collections. It must stay + TimeOffice-facing and must not become the canonical scheduling model. + """ + + station_ids: tuple[int, ...] + period: PlanningPeriod + + +class CacheWriteResult(BaseModel): + """Result of writing one station-month cache payload.""" + + station_id: int + period: PlanningPeriod + cache_directory: Path diff --git a/src/scheduling/timeoffice/service.py b/src/scheduling/timeoffice/service.py new file mode 100644 index 00000000..c7d82a2e --- /dev/null +++ b/src/scheduling/timeoffice/service.py @@ -0,0 +1,76 @@ +from src.scheduling.models.dataset import SchedulingDataset, StationMonthData +from src.scheduling.timeoffice.cache import TimeOfficeCache +from src.scheduling.timeoffice.database import TimeOfficeDatabase +from src.scheduling.timeoffice.mapping import TimeOfficeMapper +from src.scheduling.timeoffice.models import FetchStationsRequest +from src.scheduling.timeoffice.settings import TimeOfficeSettings + + +class TimeOfficeService: + """Public facade for TimeOffice data transfer.""" + + def __init__( + self, + settings: TimeOfficeSettings, + database: TimeOfficeDatabase, + mapper: TimeOfficeMapper, + cache: TimeOfficeCache, + ): + self._settings = settings + self._database = database + self._mapper = mapper + self._cache = cache + + def fetch(self, request: FetchStationsRequest) -> SchedulingDataset: + """Provide scheduling data for the requested TimeOffice stations. + + If request.use_cache is true, cached StationMonthData is attempted first. + On cache miss or invalid cache data, the service falls back to database. + + If settings.enable_cache is true, database-derived StationMonthData is + written to cache for debugging and validation. + """ + print( + "[timeoffice] service.fetch " + f"stations={list(request.station_ids)} " + f"period={request.period.start.isoformat()}..{request.period.end.isoformat()} " + f"use_cache={request.use_cache} " + f"enable_cache={self._settings.enable_cache}" + ) + + station_month_data = self._get_station_month_data(request) + + return self._mapper.combine_station_month_data(station_month_data=station_month_data) + + def _get_station_month_data(self, request: FetchStationsRequest) -> tuple[StationMonthData, ...]: + """Load station-month data from cache or database according to policy.""" + if request.use_cache: + try: + return self._cache.read_many( + station_ids=request.station_ids, + period=request.period, + ) + except Exception as error: + print( + f"[timeoffice] cache unavailable; falling back to database reason={type(error).__name__}: {error}" + ) + + source_data = self._database.read(request) + station_month_data = self._mapper.to_station_month_data(source_data) + + if self._settings.enable_cache: + self._cache.write_many(station_month_data) + + return station_month_data + + +def create_timeoffice_service(settings: TimeOfficeSettings | None = None) -> TimeOfficeService: + """Create the default TimeOffice service.""" + settings = settings or TimeOfficeSettings() + + return TimeOfficeService( + settings=settings, + database=TimeOfficeDatabase(settings), + mapper=TimeOfficeMapper(settings), + cache=TimeOfficeCache(settings), + ) diff --git a/src/scheduling/timeoffice/settings.py b/src/scheduling/timeoffice/settings.py new file mode 100644 index 00000000..94a0a1d3 --- /dev/null +++ b/src/scheduling/timeoffice/settings.py @@ -0,0 +1,21 @@ +from pathlib import Path + +from pydantic import Field +from pydantic_settings import BaseSettings, SettingsConfigDict + + +class TimeOfficeSettings(BaseSettings): + """Settings for TimeOffice data transfer.""" + + model_config = SettingsConfigDict(extra="ignore") + + db_server: str | None = None + db_name: str | None = None + db_user: str | None = None + db_password: str | None = None + db_driver: str = "ODBC Driver 18 for SQL Server" + + enable_cache: bool = True + cache_root: Path = Path(".cache/timeoffice") + + jump_pool_station_ids: tuple[int, ...] = Field(default_factory=tuple) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/integration/__init__.py b/tests/integration/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/integration/helpers/smoke_fixtures.py b/tests/integration/helpers/smoke_fixtures.py new file mode 100644 index 00000000..54daa13a --- /dev/null +++ b/tests/integration/helpers/smoke_fixtures.py @@ -0,0 +1,124 @@ +from dataclasses import dataclass +from datetime import date, timedelta + +from src.employee import Employee +from src.shift import Shift + +type WeekdayAbbreviation = str +type EmployeeLevel = str +type ShiftAbbreviation = str +type MinStaffing = dict[EmployeeLevel, dict[WeekdayAbbreviation, dict[ShiftAbbreviation, int]]] + + +SMOKE_TEST_WEIGHTS: dict[str, int] = { + "free_weekend": 1, + "consecutive_nights": 1, + "hidden": 1, + "overtime": 1, + "consecutive_days": 1, + "rotate": 1, + "wishes": 1, + "after_night": 1, + "second_weekend": 1, + "preferred_block": 1, +} + + +@dataclass(frozen=True) +class SmokeSolveFixture: + unit: int + start_date: date + end_date: date + days: list[date] + shifts: list[Shift] + employees: list[Employee] + min_staffing: MinStaffing + + +def make_smoke_solve_fixture() -> SmokeSolveFixture: + """Small clean fixture for one full service-level solver run.""" + start_date = date(2024, 11, 2) # Saturday + days = [start_date + timedelta(days=offset) for offset in range(2)] + + return SmokeSolveFixture( + unit=999, + start_date=start_date, + end_date=days[-1], + days=days, + shifts=make_solver_compatible_shifts(), + employees=[ + make_employee( + key=1, + name="Alice", + level="Fachkraft", + target_working_time=460, + ), + make_employee( + key=2, + name="Bob", + level="Fachkraft", + target_working_time=460, + ), + ], + min_staffing={ + "Fachkraft": make_weekend_early_staffing(), + }, + ) + + +def make_solver_compatible_shifts() -> list[Shift]: + return [ + Shift(Shift.EARLY, "Früh", 360, 820), + Shift(Shift.INTERMEDIATE, "Zwischen", 480, 940), + Shift(Shift.LATE, "Spät", 805, 1265), + Shift(Shift.NIGHT, "Nacht", 1250, 375), + Shift(Shift.MANAGEMENT, "Z60", 480, 840), + Shift(5, "F2_", 360, 820), + Shift(6, "S2_", 805, 1265), + Shift(7, "N5", 1250, 375), + ] + + +def make_employee( + *, + key: int, + name: str, + level: str, + target_working_time: int, +) -> Employee: + return Employee( + key=key, + surname="Smoke", + name=name, + level=level, + type=f"Test-{level}", + target_working_time=target_working_time, + actual_working_time=0, + forbidden_days=[], + forbidden_shifts=[], + vacation_days=[], + vacation_shifts=[], + wish_days=[], + wish_shifts=[], + planned_shifts=[], + qualifications=[], + ) + + +def make_weekend_early_staffing() -> dict[str, dict[str, int]]: + staffing = make_empty_week_staffing() + staffing["Sa"]["F"] = 1 + staffing["So"]["F"] = 1 + return staffing + + +def make_empty_week_staffing() -> dict[str, dict[str, int]]: + return { + "Mo": {"F": 0, "Z": 0, "S": 0, "N": 0}, + "Di": {"F": 0, "Z": 0, "S": 0, "N": 0}, + "Mi": {"F": 0, "Z": 0, "S": 0, "N": 0}, + "Do": {"F": 0, "Z": 0, "S": 0, "N": 0}, + "Fr": {"F": 0, "Z": 0, "S": 0, "N": 0}, + "Sa": {"F": 0, "Z": 0, "S": 0, "N": 0}, + "So": {"F": 0, "Z": 0, "S": 0, "N": 0}, + } diff --git a/tests/integration/helpers/solution_assertions.py b/tests/integration/helpers/solution_assertions.py deleted file mode 100644 index ea222cd6..00000000 --- a/tests/integration/helpers/solution_assertions.py +++ /dev/null @@ -1,138 +0,0 @@ -import ast -from collections import Counter -from dataclasses import dataclass -from datetime import date - -from src.employee import Employee -from src.shift import Shift -from tests.integration.helpers.solver_fixtures import MinStaffing - -WEEKDAY_ABBREVIATIONS: dict[int, str] = { - 1: "Mo", - 2: "Di", - 3: "Mi", - 4: "Do", - 5: "Fr", - 6: "Sa", - 7: "So", -} - - -@dataclass(frozen=True) -class Assignment: - employee_id: int - day: date - shift_id: int - - -def active_shift_assignments(solution_variables: dict[str, int]) -> list[Assignment]: - assignments: list[Assignment] = [] - - for variable_name, value in solution_variables.items(): - if value != 1: - continue - - try: - employee_id, day_value, shift_id = ast.literal_eval(variable_name) - except (SyntaxError, ValueError): - continue - - if not isinstance(employee_id, int): - continue - if not isinstance(day_value, str): - continue - if not isinstance(shift_id, int): - continue - - assignments.append( - Assignment( - employee_id=employee_id, - day=date.fromisoformat(day_value), - shift_id=shift_id, - ) - ) - - return assignments - - -def assert_solution_found(status_name: str) -> None: - assert status_name in {"FEASIBLE", "OPTIMAL"}, f"Expected feasible solver result, got {status_name}" - - -def assert_only_known_employees_assigned(assignments: list[Assignment], employees: list[Employee]) -> None: - known_employee_ids = {employee.get_key() for employee in employees} - assigned_employee_ids = {assignment.employee_id for assignment in assignments} - - assert assigned_employee_ids <= known_employee_ids, ( - f"Solution contains assignments for unknown employees: {sorted(assigned_employee_ids - known_employee_ids)}" - ) - - -def assert_no_more_than_one_shift_per_employee_day(assignments: list[Assignment]) -> None: - counts = Counter((assignment.employee_id, assignment.day) for assignment in assignments) - duplicates = {key: count for key, count in counts.items() if count > 1} - - assert not duplicates, f"Employees assigned to more than one shift per day: {duplicates}" - - -def assert_unavailable_employees_not_assigned(assignments: list[Assignment], employees: list[Employee]) -> None: - employee_by_id = {employee.get_key(): employee for employee in employees} - - invalid_assignments = [ - assignment - for assignment in assignments - if assignment.day.day in employee_by_id[assignment.employee_id].vacation_days - or employee_by_id[assignment.employee_id].unavailable(assignment.day) - ] - - assert not invalid_assignments, f"Unavailable employees were assigned: {invalid_assignments}" - - -def assert_no_exclusive_shift_assigned(assignments: list[Assignment], shifts: list[Shift]) -> None: - exclusive_shift_ids = {shift.get_id() for shift in shifts if shift.is_exclusive} - invalid_assignments = [assignment for assignment in assignments if assignment.shift_id in exclusive_shift_ids] - - assert not invalid_assignments, ( - f"Exclusive/preplanned shifts leaked into generated assignments: {invalid_assignments}" - ) - - -def assert_min_staffing_is_covered( - *, - assignments: list[Assignment], - employees: list[Employee], - shifts: list[Shift], - days: list[date], - min_staffing: MinStaffing, -) -> None: - employee_by_id = {employee.get_key(): employee for employee in employees} - shift_by_id = {shift.get_id(): shift for shift in shifts} - - assignment_counts: Counter[tuple[str, date, str]] = Counter() - - for assignment in assignments: - employee = employee_by_id[assignment.employee_id] - shift = shift_by_id[assignment.shift_id] - - if shift.is_exclusive: - continue - - assignment_counts[(employee.level, assignment.day, shift.abbreviation)] += 1 - - mismatches: list[str] = [] - - for employee_level, staffing_by_weekday in min_staffing.items(): - for day in days: - weekday = WEEKDAY_ABBREVIATIONS[day.isoweekday()] - required_by_shift = staffing_by_weekday[weekday] - - for shift_abbreviation, required_count in required_by_shift.items(): - actual_count = assignment_counts[(employee_level, day, shift_abbreviation)] - - if actual_count != required_count: - mismatches.append( - f"{employee_level} {day.isoformat()} {shift_abbreviation}: " - f"expected {required_count}, got {actual_count}" - ) - - assert not mismatches, "Minimum staffing mismatches:\n" + "\n".join(mismatches) diff --git a/tests/integration/helpers/solver_fixtures.py b/tests/integration/helpers/solver_fixtures.py deleted file mode 100644 index 958b5d4e..00000000 --- a/tests/integration/helpers/solver_fixtures.py +++ /dev/null @@ -1,182 +0,0 @@ -from dataclasses import dataclass -from datetime import date, timedelta - -from src.employee import Employee -from src.shift import Shift - -type WeekdayAbbreviation = str -type EmployeeLevel = str -type ShiftAbbreviation = str -type MinStaffing = dict[EmployeeLevel, dict[WeekdayAbbreviation, dict[ShiftAbbreviation, int]]] - - -TEST_SOLVER_WEIGHTS: dict[str, int] = { - "free_weekend": 1, - "consecutive_nights": 1, - "hidden": 1, - "overtime": 1, - "consecutive_days": 1, - "rotate": 1, - "wishes": 1, - "after_night": 1, - "second_weekend": 1, - "preferred_block": 1, -} - - -@dataclass(frozen=True) -class CleanSolverFixture: - unit: int - start_date: date - end_date: date - days: list[date] - shifts: list[Shift] - employees: list[Employee] - min_staffing: MinStaffing - - -def make_standard_shifts() -> list[Shift]: - return [ - Shift(Shift.EARLY, "Früh", 360, 820), - Shift(Shift.INTERMEDIATE, "Zwischen", 480, 940), - Shift(Shift.LATE, "Spät", 805, 1265), - Shift(Shift.NIGHT, "Nacht", 1250, 375), - ] - - -def make_days(start_date: date, number_of_days: int) -> list[date]: - return [start_date + timedelta(days=offset) for offset in range(number_of_days)] - - -def make_employee( - *, - key: int, - name: str, - level: str, - target_working_time: int, - forbidden_days: list[int] | None = None, - wish_days: list[int] | None = None, - wish_shifts: list[tuple[int, str]] | None = None, - qualifications: list[str] | None = None, -) -> Employee: - return Employee( - key=key, - surname="Clean", - name=name, - level=level, - type=f"Test-{level}", - target_working_time=target_working_time, - actual_working_time=0, - forbidden_days=forbidden_days or [], - forbidden_shifts=[], - vacation_days=[], - vacation_shifts=[], - wish_days=wish_days or [], - wish_shifts=wish_shifts or [], - planned_shifts=[], - qualifications=qualifications or [], - ) - - -def empty_week_staffing() -> dict[str, dict[str, int]]: - return { - "Mo": {"F": 0, "Z": 0, "S": 0, "N": 0}, - "Di": {"F": 0, "Z": 0, "S": 0, "N": 0}, - "Mi": {"F": 0, "Z": 0, "S": 0, "N": 0}, - "Do": {"F": 0, "Z": 0, "S": 0, "N": 0}, - "Fr": {"F": 0, "Z": 0, "S": 0, "N": 0}, - "Sa": {"F": 0, "Z": 0, "S": 0, "N": 0}, - "So": {"F": 0, "Z": 0, "S": 0, "N": 0}, - } - - -def make_two_day_fachkraft_early_fixture() -> CleanSolverFixture: - """Smallest useful solver fixture that avoids polluted JSON/SQL data.""" - start_date = date(2024, 11, 2) - days = make_days(start_date, 2) - - fachkraft_staffing = empty_week_staffing() - fachkraft_staffing["Sa"]["F"] = 1 - fachkraft_staffing["So"]["F"] = 1 - - return CleanSolverFixture( - unit=999, - start_date=start_date, - end_date=days[-1], - days=days, - shifts=make_standard_shifts(), - employees=[ - make_employee( - key=1, - name="Alice", - level="Fachkraft", - target_working_time=460, - ), - make_employee( - key=2, - name="Bob", - level="Fachkraft", - target_working_time=460, - ), - ], - min_staffing={ - "Fachkraft": fachkraft_staffing, - }, - ) - - -def make_one_week_two_level_reference_fixture() -> CleanSolverFixture: - """Reference fixture for validating current hard scheduling invariants.""" - start_date = date(2024, 11, 4) - days = make_days(start_date, 7) - - fachkraft_staffing = empty_week_staffing() - hilfskraft_staffing = empty_week_staffing() - - for weekday in fachkraft_staffing: - fachkraft_staffing[weekday]["F"] = 1 - hilfskraft_staffing[weekday]["S"] = 1 - - return CleanSolverFixture( - unit=1000, - start_date=start_date, - end_date=days[-1], - days=days, - shifts=make_standard_shifts(), - employees=[ - make_employee( - key=1, - name="Alice", - level="Fachkraft", - target_working_time=1840, - forbidden_days=[6], - qualifications=["rounds"], - ), - make_employee( - key=2, - name="Bob", - level="Fachkraft", - target_working_time=1840, - wish_days=[10], - qualifications=["rounds"], - ), - make_employee( - key=3, - name="Carla", - level="Hilfskraft", - target_working_time=1840, - forbidden_days=[8], - ), - make_employee( - key=4, - name="David", - level="Hilfskraft", - target_working_time=1840, - wish_shifts=[(9, "S")], - ), - ], - min_staffing={ - "Fachkraft": fachkraft_staffing, - "Hilfskraft": hilfskraft_staffing, - }, - ) diff --git a/tests/integration/smoke_test.py b/tests/integration/smoke_test.py new file mode 100644 index 00000000..6b44c97f --- /dev/null +++ b/tests/integration/smoke_test.py @@ -0,0 +1,62 @@ +from typing import Any + +import pytest + +from src.services.solve_service import execute_solve +from tests.integration.helpers.smoke_fixtures import SMOKE_TEST_WEIGHTS, SmokeSolveFixture, make_smoke_solve_fixture + + +def inject_smoke_fixture( + monkeypatch: pytest.MonkeyPatch, + fixture: SmokeSolveFixture, +) -> None: + """Replace data loading with fixed sanitized data.""" + monkeypatch.setattr("src.solve.FSLoader.get_days", lambda self, start_date, end_date: fixture.days) + monkeypatch.setattr("src.solve.FSLoader.get_shifts", lambda self: fixture.shifts) + monkeypatch.setattr("src.solve.FSLoader.get_employees", lambda self, start=0: fixture.employees) + monkeypatch.setattr("src.solve.FSLoader.get_min_staffing", lambda self: fixture.min_staffing) + monkeypatch.setattr("src.solve.FSLoader.write_solution", lambda self, solution, solution_name: None) + + monkeypatch.setattr( + "src.services.solve_service.load_weights", + lambda unit, start_date: SMOKE_TEST_WEIGHTS, + ) + + +@pytest.mark.integration +def test_solve_service_generates_output_for_clean_smoke_fixture( + monkeypatch: pytest.MonkeyPatch, +) -> None: + fixture = make_smoke_solve_fixture() + inject_smoke_fixture(monkeypatch, fixture) + + generated_outputs: list[dict[str, Any]] = [] + + def fake_process_solution( + *, + loader: Any, + employees: Any, + output_filename: str, + solution_file_name: str, + ) -> dict[str, Any]: + generated_outputs.append( + { + "output_filename": output_filename, + "solution_file_name": solution_file_name, + "employee_count": len(employees), + } + ) + return {"generated": True} + + monkeypatch.setattr("src.services.solve_service.process_solution", fake_process_solution) + + result = execute_solve( + unit=fixture.unit, + start_date=fixture.start_date, + end_date=fixture.end_date, + timeout=10, + ) + + assert result["status"] in {"FEASIBLE", "OPTIMAL"} + assert result["solution_data"] == {"generated": True} + assert len(generated_outputs) == 1 diff --git a/tests/integration/test_solver_pipeline.py b/tests/integration/test_solver_pipeline.py deleted file mode 100644 index ea980a1b..00000000 --- a/tests/integration/test_solver_pipeline.py +++ /dev/null @@ -1,91 +0,0 @@ -import pytest - -from src.solve import main as run_solver -from tests.integration.helpers.solution_assertions import ( - active_shift_assignments, - assert_min_staffing_is_covered, - assert_no_exclusive_shift_assigned, - assert_no_more_than_one_shift_per_employee_day, - assert_only_known_employees_assigned, - assert_solution_found, - assert_unavailable_employees_not_assigned, -) -from tests.integration.helpers.solver_fixtures import ( - TEST_SOLVER_WEIGHTS, - CleanSolverFixture, - make_one_week_two_level_reference_fixture, - make_two_day_fachkraft_early_fixture, -) - - -def inject_fixture_at_solver_loader_boundary( - monkeypatch: pytest.MonkeyPatch, - fixture: CleanSolverFixture, -) -> None: - """Inject clean test data while keeping the real solver/model path.""" - monkeypatch.setattr("src.solve.FSLoader.get_days", lambda self, start_date, end_date: fixture.days) - monkeypatch.setattr("src.solve.FSLoader.get_shifts", lambda self: fixture.shifts) - monkeypatch.setattr("src.solve.FSLoader.get_min_staffing", lambda self: fixture.min_staffing) - monkeypatch.setattr("src.solve.FSLoader.write_solution", lambda self, solution, solution_name: None) - - -@pytest.mark.integration -def test_two_day_fachkraft_early_fixture_solves_successfully(monkeypatch: pytest.MonkeyPatch) -> None: - fixture = make_two_day_fachkraft_early_fixture() - inject_fixture_at_solver_loader_boundary(monkeypatch, fixture) - - result = run_solver( - unit=fixture.unit, - start_date=fixture.start_date, - end_date=fixture.end_date, - timeout=10, - employees=fixture.employees, - weights=TEST_SOLVER_WEIGHTS, - ) - - assert_solution_found(result.solution.status_name) - - assignments = active_shift_assignments(result.solution.variables) - - assert_only_known_employees_assigned(assignments, fixture.employees) - assert_no_more_than_one_shift_per_employee_day(assignments) - assert_min_staffing_is_covered( - assignments=assignments, - employees=fixture.employees, - shifts=fixture.shifts, - days=fixture.days, - min_staffing=fixture.min_staffing, - ) - - -@pytest.mark.integration -def test_one_week_two_level_reference_fixture_satisfies_basic_invariants( - monkeypatch: pytest.MonkeyPatch, -) -> None: - fixture = make_one_week_two_level_reference_fixture() - inject_fixture_at_solver_loader_boundary(monkeypatch, fixture) - - result = run_solver( - unit=fixture.unit, - start_date=fixture.start_date, - end_date=fixture.end_date, - timeout=10, - employees=fixture.employees, - weights=TEST_SOLVER_WEIGHTS, - ) - - assert_solution_found(result.solution.status_name) - - assignments = active_shift_assignments(result.solution.variables) - - assert_only_known_employees_assigned(assignments, fixture.employees) - assert_no_more_than_one_shift_per_employee_day(assignments) - assert_unavailable_employees_not_assigned(assignments, fixture.employees) - assert_no_exclusive_shift_assigned(assignments, fixture.shifts) - assert_min_staffing_is_covered( - assignments=assignments, - employees=fixture.employees, - shifts=fixture.shifts, - days=fixture.days, - min_staffing=fixture.min_staffing, - ) diff --git a/uv.lock b/uv.lock index 8cb8787f..9cad1580 100644 --- a/uv.lock +++ b/uv.lock @@ -378,7 +378,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/44/69/9b804adb5fd0671f367781560eb5eb586c4d495277c93bde4307b9e28068/greenlet-3.2.4-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3b67ca49f54cede0186854a008109d6ee71f66bd57bb36abd6d0a0267b540cdd", size = 274079, upload-time = "2025-08-07T13:15:45.033Z" }, { url = "https://files.pythonhosted.org/packages/46/e9/d2a80c99f19a153eff70bc451ab78615583b8dac0754cfb942223d2c1a0d/greenlet-3.2.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ddf9164e7a5b08e9d22511526865780a576f19ddd00d62f8a665949327fde8bb", size = 640997, upload-time = "2025-08-07T13:42:56.234Z" }, { url = "https://files.pythonhosted.org/packages/3b/16/035dcfcc48715ccd345f3a93183267167cdd162ad123cd93067d86f27ce4/greenlet-3.2.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f28588772bb5fb869a8eb331374ec06f24a83a9c25bfa1f38b6993afe9c1e968", size = 655185, upload-time = "2025-08-07T13:45:27.624Z" }, - { url = "https://files.pythonhosted.org/packages/31/da/0386695eef69ffae1ad726881571dfe28b41970173947e7c558d9998de0f/greenlet-3.2.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:5c9320971821a7cb77cfab8d956fa8e39cd07ca44b6070db358ceb7f8797c8c9", size = 649926, upload-time = "2025-08-07T13:53:15.251Z" }, { url = "https://files.pythonhosted.org/packages/68/88/69bf19fd4dc19981928ceacbc5fd4bb6bc2215d53199e367832e98d1d8fe/greenlet-3.2.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c60a6d84229b271d44b70fb6e5fa23781abb5d742af7b808ae3f6efd7c9c60f6", size = 651839, upload-time = "2025-08-07T13:18:30.281Z" }, { url = "https://files.pythonhosted.org/packages/19/0d/6660d55f7373b2ff8152401a83e02084956da23ae58cddbfb0b330978fe9/greenlet-3.2.4-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b3812d8d0c9579967815af437d96623f45c0f2ae5f04e366de62a12d83a8fb0", size = 607586, upload-time = "2025-08-07T13:18:28.544Z" }, { url = "https://files.pythonhosted.org/packages/8e/1a/c953fdedd22d81ee4629afbb38d2f9d71e37d23caace44775a3a969147d4/greenlet-3.2.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:abbf57b5a870d30c4675928c37278493044d7c14378350b3aa5d484fa65575f0", size = 1123281, upload-time = "2025-08-07T13:42:39.858Z" }, @@ -389,7 +388,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/49/e8/58c7f85958bda41dafea50497cbd59738c5c43dbbea5ee83d651234398f4/greenlet-3.2.4-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:1a921e542453fe531144e91e1feedf12e07351b1cf6c9e8a3325ea600a715a31", size = 272814, upload-time = "2025-08-07T13:15:50.011Z" }, { url = "https://files.pythonhosted.org/packages/62/dd/b9f59862e9e257a16e4e610480cfffd29e3fae018a68c2332090b53aac3d/greenlet-3.2.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd3c8e693bff0fff6ba55f140bf390fa92c994083f838fece0f63be121334945", size = 641073, upload-time = "2025-08-07T13:42:57.23Z" }, { url = "https://files.pythonhosted.org/packages/f7/0b/bc13f787394920b23073ca3b6c4a7a21396301ed75a655bcb47196b50e6e/greenlet-3.2.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:710638eb93b1fa52823aa91bf75326f9ecdfd5e0466f00789246a5280f4ba0fc", size = 655191, upload-time = "2025-08-07T13:45:29.752Z" }, - { url = "https://files.pythonhosted.org/packages/f2/d6/6adde57d1345a8d0f14d31e4ab9c23cfe8e2cd39c3baf7674b4b0338d266/greenlet-3.2.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c5111ccdc9c88f423426df3fd1811bfc40ed66264d35aa373420a34377efc98a", size = 649516, upload-time = "2025-08-07T13:53:16.314Z" }, { url = "https://files.pythonhosted.org/packages/7f/3b/3a3328a788d4a473889a2d403199932be55b1b0060f4ddd96ee7cdfcad10/greenlet-3.2.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d76383238584e9711e20ebe14db6c88ddcedc1829a9ad31a584389463b5aa504", size = 652169, upload-time = "2025-08-07T13:18:32.861Z" }, { url = "https://files.pythonhosted.org/packages/ee/43/3cecdc0349359e1a527cbf2e3e28e5f8f06d3343aaf82ca13437a9aa290f/greenlet-3.2.4-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23768528f2911bcd7e475210822ffb5254ed10d71f4028387e5a99b4c6699671", size = 610497, upload-time = "2025-08-07T13:18:31.636Z" }, { url = "https://files.pythonhosted.org/packages/b8/19/06b6cf5d604e2c382a6f31cafafd6f33d5dea706f4db7bdab184bad2b21d/greenlet-3.2.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:00fadb3fedccc447f517ee0d3fd8fe49eae949e1cd0f6a611818f4f6fb7dc83b", size = 1121662, upload-time = "2025-08-07T13:42:41.117Z" }, @@ -400,7 +398,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/22/5c/85273fd7cc388285632b0498dbbab97596e04b154933dfe0f3e68156c68c/greenlet-3.2.4-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:49a30d5fda2507ae77be16479bdb62a660fa51b1eb4928b524975b3bde77b3c0", size = 273586, upload-time = "2025-08-07T13:16:08.004Z" }, { url = "https://files.pythonhosted.org/packages/d1/75/10aeeaa3da9332c2e761e4c50d4c3556c21113ee3f0afa2cf5769946f7a3/greenlet-3.2.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:299fd615cd8fc86267b47597123e3f43ad79c9d8a22bebdce535e53550763e2f", size = 686346, upload-time = "2025-08-07T13:42:59.944Z" }, { url = "https://files.pythonhosted.org/packages/c0/aa/687d6b12ffb505a4447567d1f3abea23bd20e73a5bed63871178e0831b7a/greenlet-3.2.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:c17b6b34111ea72fc5a4e4beec9711d2226285f0386ea83477cbb97c30a3f3a5", size = 699218, upload-time = "2025-08-07T13:45:30.969Z" }, - { url = "https://files.pythonhosted.org/packages/dc/8b/29aae55436521f1d6f8ff4e12fb676f3400de7fcf27fccd1d4d17fd8fecd/greenlet-3.2.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b4a1870c51720687af7fa3e7cda6d08d801dae660f75a76f3845b642b4da6ee1", size = 694659, upload-time = "2025-08-07T13:53:17.759Z" }, { url = "https://files.pythonhosted.org/packages/92/2e/ea25914b1ebfde93b6fc4ff46d6864564fba59024e928bdc7de475affc25/greenlet-3.2.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:061dc4cf2c34852b052a8620d40f36324554bc192be474b9e9770e8c042fd735", size = 695355, upload-time = "2025-08-07T13:18:34.517Z" }, { url = "https://files.pythonhosted.org/packages/72/60/fc56c62046ec17f6b0d3060564562c64c862948c9d4bc8aa807cf5bd74f4/greenlet-3.2.4-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:44358b9bf66c8576a9f57a590d5f5d6e72fa4228b763d0e43fee6d3b06d3a337", size = 657512, upload-time = "2025-08-07T13:18:33.969Z" }, { url = "https://files.pythonhosted.org/packages/23/6e/74407aed965a4ab6ddd93a7ded3180b730d281c77b765788419484cdfeef/greenlet-3.2.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2917bdf657f5859fbf3386b12d68ede4cf1f04c90c3a6bc1f013dd68a22e2269", size = 1612508, upload-time = "2025-11-04T12:42:23.427Z" }, @@ -1202,6 +1199,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" }, ] +[[package]] +name = "pydantic-settings" +version = "2.14.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "python-dotenv" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/07/60/1d1e59c9c90d54591469ada7d268251f71c24bdb765f1a8a832cee8c6653/pydantic_settings-2.14.1.tar.gz", hash = "sha256:e874d3bec7e787b0c9958277956ed9b4dd5de6a80e162188fdaff7c5e26fd5fa", size = 235551, upload-time = "2026-05-08T13:40:06.542Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ae/8d/f1af3832f5e6eb13ba94ee809e72b8ecb5eef226d27ee0bef7d963d943c7/pydantic_settings-2.14.1-py3-none-any.whl", hash = "sha256:6e3c7edfd8277687cdc598f56e5cff0e9bfff0910a3749deaa8d4401c3a2b9de", size = 60964, upload-time = "2026-05-08T13:40:04.958Z" }, +] + [[package]] name = "pygments" version = "2.19.2" @@ -1486,6 +1497,8 @@ dependencies = [ { name = "numpy" }, { name = "ortools" }, { name = "pandas" }, + { name = "pydantic" }, + { name = "pydantic-settings" }, { name = "pyodbc" }, { name = "pytest" }, { name = "sqlalchemy" }, @@ -1527,6 +1540,8 @@ requires-dist = [ { name = "numpy", specifier = ">=2.2.5" }, { name = "ortools", specifier = ">=9.12.4544" }, { name = "pandas", specifier = ">=2.2.3" }, + { name = "pydantic", specifier = ">=2.12.5" }, + { name = "pydantic-settings", specifier = ">=2.14.1" }, { name = "pyodbc", specifier = ">=5.2.0" }, { name = "pytest", specifier = ">=9.0.1" }, { name = "sqlalchemy", specifier = ">=2.0.41" }, From e012ada1e3786419bb6f2ad3c57e0a15b2429710 Mon Sep 17 00:00:00 2001 From: Tom Wawerek Date: Tue, 9 Jun 2026 00:05:00 +0200 Subject: [PATCH 04/18] feat: read timeoffice plan employees --- src/scheduling/models/__init__.py | 28 +++ src/scheduling/models/dataset.py | 6 +- src/scheduling/sql/SELECT.sql | 13 ++ src/scheduling/timeoffice/cache.py | 5 +- src/scheduling/timeoffice/constants.py | 17 ++ src/scheduling/timeoffice/database.py | 187 ++++++++++++++++++++- src/scheduling/timeoffice/mapping.py | 119 +++++++++++-- src/scheduling/timeoffice/models.py | 11 +- src/scheduling/timeoffice/service.py | 6 +- src/scheduling/timeoffice/settings.py | 20 ++- src/scheduling/timeoffice/source_models.py | 42 +++++ 11 files changed, 417 insertions(+), 37 deletions(-) create mode 100644 src/scheduling/sql/SELECT.sql create mode 100644 src/scheduling/timeoffice/constants.py create mode 100644 src/scheduling/timeoffice/source_models.py diff --git a/src/scheduling/models/__init__.py b/src/scheduling/models/__init__.py index e69de29b..c81993b3 100644 --- a/src/scheduling/models/__init__.py +++ b/src/scheduling/models/__init__.py @@ -0,0 +1,28 @@ +from src.scheduling.models.core import PlanningPeriod +from src.scheduling.models.dataset import SchedulingDataset, StationMonthData +from src.scheduling.models.demand import Demand +from src.scheduling.models.employee import Employee +from src.scheduling.models.relations import ( + Assignment, + Availability, + Membership, + Preference, + Rule, +) +from src.scheduling.models.shift import Shift +from src.scheduling.models.station import Station + +__all__ = [ + "Assignment", + "Availability", + "Demand", + "Employee", + "Membership", + "PlanningPeriod", + "Preference", + "Rule", + "SchedulingDataset", + "Shift", + "Station", + "StationMonthData", +] diff --git a/src/scheduling/models/dataset.py b/src/scheduling/models/dataset.py index ad4a0555..21f16043 100644 --- a/src/scheduling/models/dataset.py +++ b/src/scheduling/models/dataset.py @@ -17,8 +17,8 @@ class StationMonthData(BaseModel): """Scheduling-relevant data for one station and one planning period. - This is the canonical cache payload. It is not TimeOffice-specific and it is - not solver-specific. + This is the canonical cache payload. + It is not TimeOffice-specific and it is not solver-specific. """ schema_version: int = 1 @@ -26,6 +26,8 @@ class StationMonthData(BaseModel): station: Station period: PlanningPeriod + source_plan_id: int | None = None + employees: tuple[Employee, ...] = () shifts: tuple[Shift, ...] = () demand: tuple[Demand, ...] = () diff --git a/src/scheduling/sql/SELECT.sql b/src/scheduling/sql/SELECT.sql new file mode 100644 index 00000000..2585b061 --- /dev/null +++ b/src/scheduling/sql/SELECT.sql @@ -0,0 +1,13 @@ +SELECT + ku.TABLE_NAME, + ku.COLUMN_NAME, + tc.CONSTRAINT_TYPE +FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc +JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE ku + ON tc.CONSTRAINT_NAME = ku.CONSTRAINT_NAME +WHERE ku.TABLE_NAME IN ( + 'TPlanPersonal', + 'TPersonal', + 'TPlanungseinheitenPersonal' +) +ORDER BY ku.TABLE_NAME, ku.ORDINAL_POSITION; diff --git a/src/scheduling/timeoffice/cache.py b/src/scheduling/timeoffice/cache.py index 0f264ecc..3b4b9c39 100644 --- a/src/scheduling/timeoffice/cache.py +++ b/src/scheduling/timeoffice/cache.py @@ -1,7 +1,6 @@ from pathlib import Path -from src.scheduling.models.core import PlanningPeriod -from src.scheduling.models.dataset import StationMonthData +from src.scheduling.models import PlanningPeriod, StationMonthData from src.scheduling.timeoffice.models import CacheWriteResult from src.scheduling.timeoffice.settings import TimeOfficeSettings @@ -52,7 +51,7 @@ def write(self, data: StationMonthData) -> CacheWriteResult: data_path = self.station_month_data_path(station_id, data.period) data_path.write_text( - data.model_dump_json(indent=2), + data.model_dump_json(indent=2) + "\n", encoding="utf-8", ) diff --git a/src/scheduling/timeoffice/constants.py b/src/scheduling/timeoffice/constants.py new file mode 100644 index 00000000..1d5a227e --- /dev/null +++ b/src/scheduling/timeoffice/constants.py @@ -0,0 +1,17 @@ +from enum import IntEnum + + +class TimeOfficePlanStatus(IntEnum): + """Known TimeOffice plan status ids.""" + + TARGET_PLANNING = 20 + ACTUAL = 50 + COMPLETED = 70 + SETTLED = 80 + + +class TimeOfficePlanningInterval(IntEnum): + """Known TimeOffice planning interval ids.""" + + MONTHLY = 1 + ANNUAL = 3 diff --git a/src/scheduling/timeoffice/database.py b/src/scheduling/timeoffice/database.py index 4d26a11b..66f7a86e 100644 --- a/src/scheduling/timeoffice/database.py +++ b/src/scheduling/timeoffice/database.py @@ -1,5 +1,10 @@ +from sqlalchemy import URL, bindparam, create_engine, text +from sqlalchemy.engine import Engine + +from src.scheduling.timeoffice.constants import TimeOfficePlanningInterval, TimeOfficePlanStatus from src.scheduling.timeoffice.models import FetchStationsRequest, TimeOfficeSourceData from src.scheduling.timeoffice.settings import TimeOfficeSettings +from src.scheduling.timeoffice.source_models import TimeOfficePlanEmployeeSource, TimeOfficePlanSource class TimeOfficeDatabase: @@ -10,20 +15,192 @@ class TimeOfficeDatabase: def __init__(self, settings: TimeOfficeSettings): self._settings = settings + self._engine: Engine | None = None - def read(self, request: FetchStationsRequest) -> TimeOfficeSourceData: - """Read all source data needed to build StationMonthData. + def _get_engine(self) -> Engine: + """Create or return the TimeOffice database engine.""" + if self._engine is None: + self._engine = create_engine(self._database_url()) + + return self._engine + + def _database_url(self) -> URL: + """Build the SQLAlchemy URL for the TimeOffice SQL Server database.""" + query: dict[str, str] = { + "driver": self._settings.db_driver, + "TrustServerCertificate": "yes", + } - Current iteration: shallow placeholder. - Later iterations: run optimized multi-station SQL queries. - """ + return URL.create( + drivername="mssql+pyodbc", + username=self._settings.db_user, + password=self._settings.db_password.get_secret_value(), + host=self._settings.db_server, + database=self._settings.db_name, + query=query, + ) + + def read(self, request: FetchStationsRequest) -> TimeOfficeSourceData: + """Read all source data needed to build StationMonthData.""" print( "[timeoffice] database.read " f"stations={list(request.station_ids)} " f"period={request.period.start.isoformat()}..{request.period.end.isoformat()}" ) + plans = self._read_plans(request) + plan_employees = self._read_plan_employees(plans) + return TimeOfficeSourceData( station_ids=request.station_ids, period=request.period, + plans=plans, + plan_employees=plan_employees, + ) + + def _read_plans(self, request: FetchStationsRequest) -> tuple[TimeOfficePlanSource, ...]: + """Read monthly target plans for all requested stations.""" + query = text( + """ + SELECT + p.Prim AS source_plan_id, + p.RefPlanungseinheiten AS source_planning_unit_id, + p.RefPlanungseinheiten AS station_id, + COALESCE(pe.Bezeichnung, pe.KurzBez) AS station_name, + p.RefStati AS status_id, + p.RefPlanungsIntervalle AS planning_interval_id + FROM TPlan p + LEFT JOIN TPlanungseinheiten pe + ON pe.Prim = p.RefPlanungseinheiten + WHERE p.RefPlanungseinheiten IN :station_ids + AND p.VonDat = :period_start + AND p.BisDat = :period_end + AND p.RefPlanungsIntervalle = :planning_interval_id + AND p.RefStati = :status_id + """ + ).bindparams(bindparam("station_ids", expanding=True)) + + with self._get_engine().connect() as connection: + rows = ( + connection.execute( + query, + { + "station_ids": request.station_ids, + "period_start": request.period.start, + "period_end": request.period.end, + "planning_interval_id": TimeOfficePlanningInterval.MONTHLY, + "status_id": TimeOfficePlanStatus.TARGET_PLANNING, + }, + ) + .mappings() + .all() + ) + + plans = tuple( + TimeOfficePlanSource( + station_id=row["station_id"], + source_plan_id=row["source_plan_id"], + source_planning_unit_id=row["source_planning_unit_id"], + station_name=row["station_name"], + status_id=row["status_id"], + planning_interval_id=row["planning_interval_id"], + period=request.period, + ) + for row in rows + ) + + self._ensure_one_plan_per_requested_station(request, plans) + + print(f"[timeoffice] database.read_plans rows={len(plans)}") + + return plans + + def _ensure_one_plan_per_requested_station( + self, request: FetchStationsRequest, plans: tuple[TimeOfficePlanSource, ...] + ) -> None: + """Ensure the plan query returned exactly one plan per requested station.""" + plans_by_station: dict[int, list[TimeOfficePlanSource]] = {station_id: [] for station_id in request.station_ids} + + for plan in plans: + plans_by_station.setdefault(plan.station_id, []).append(plan) + + missing_station_ids = [ + station_id for station_id, station_plans in plans_by_station.items() if not station_plans + ] + + if missing_station_ids: + raise ValueError( + "No monthly target TimeOffice plan found for station(s) " + f"{missing_station_ids} and period " + f"{request.period.start.isoformat()}..{request.period.end.isoformat()}." + ) + + ambiguous_station_ids = [ + station_id for station_id, station_plans in plans_by_station.items() if len(station_plans) > 1 + ] + + if ambiguous_station_ids: + details = { + station_id: [plan.source_plan_id for plan in station_plans] + for station_id, station_plans in plans_by_station.items() + if len(station_plans) > 1 + } + raise ValueError(f"Multiple monthly target TimeOffice plans found for station(s): {details}") + + def _read_plan_employees( + self, + plans: tuple[TimeOfficePlanSource, ...], + ) -> tuple[TimeOfficePlanEmployeeSource, ...]: + """Read employees assigned to the selected monthly TimeOffice plans.""" + plan_ids = tuple(plan.source_plan_id for plan in plans) + + query = text( + """ + SELECT + pp.Prim AS source_plan_employee_id, + pp.RefPlan AS source_plan_id, + tp.RefPlanungseinheiten AS station_id, + pp.RefPersonal AS employee_id, + + p.PersNr AS personnel_number, + p.Vorname AS first_name, + p.Name AS last_name, + p.KurzName AS short_name, + + pp.RefBerufe AS source_profession_id, + pp.VonDat AS valid_from, + pp.BisDat AS valid_until, + pp.IstVonErsatz AS is_substitute + FROM TPlanPersonal pp + JOIN TPlan tp + ON tp.Prim = pp.RefPlan + JOIN TPersonal p + ON p.Prim = pp.RefPersonal + WHERE pp.RefPlan IN :plan_ids + """ + ).bindparams(bindparam("plan_ids", expanding=True)) + + with self._get_engine().connect() as connection: + rows = connection.execute(query, {"plan_ids": plan_ids}).mappings().all() + + plan_employees = tuple( + TimeOfficePlanEmployeeSource( + source_plan_employee_id=row["source_plan_employee_id"], + source_plan_id=row["source_plan_id"], + station_id=row["station_id"], + employee_id=row["employee_id"], + personnel_number=row["personnel_number"], + first_name=row["first_name"], + last_name=row["last_name"], + short_name=row["short_name"], + source_profession_id=row["source_profession_id"], + valid_from=None if row["valid_from"] is None else row["valid_from"].date(), + valid_until=None if row["valid_until"] is None else row["valid_until"].date(), + is_substitute=None if row["is_substitute"] is None else bool(row["is_substitute"]), + ) + for row in rows ) + + print(f"[timeoffice] database.read_plan_employees rows={len(plan_employees)}") + + return plan_employees diff --git a/src/scheduling/timeoffice/mapping.py b/src/scheduling/timeoffice/mapping.py index fa4c7940..cf1259de 100644 --- a/src/scheduling/timeoffice/mapping.py +++ b/src/scheduling/timeoffice/mapping.py @@ -1,7 +1,9 @@ -from src.scheduling.models.dataset import SchedulingDataset, StationMonthData -from src.scheduling.models.station import Station +from src.scheduling.models import SchedulingDataset, Station, StationMonthData +from src.scheduling.models.employee import Employee +from src.scheduling.models.relations import Membership from src.scheduling.timeoffice.models import TimeOfficeSourceData from src.scheduling.timeoffice.settings import TimeOfficeSettings +from src.scheduling.timeoffice.source_models import TimeOfficePlanEmployeeSource, TimeOfficePlanSource class TimeOfficeMapper: @@ -18,23 +20,103 @@ def __init__(self, settings: TimeOfficeSettings): self._settings = settings def to_station_month_data(self, source_data: TimeOfficeSourceData) -> tuple[StationMonthData, ...]: - """Map TimeOffice source data into one StationMonthData object per station. - - Current iteration: shallow placeholder. - """ + """Map TimeOffice source data into one StationMonthData object per station.""" print("[timeoffice] mapper.to_station_month_data") + plans_by_station = {plan.station_id: plan for plan in source_data.plans} + return tuple( - StationMonthData( - station=Station( - station_id=station_id, - source_planning_unit_id=station_id, - ), - period=source_data.period, + self._map_station_month_data( + station_id=station_id, + source_data=source_data, + plan=plans_by_station.get(station_id), ) for station_id in source_data.station_ids ) + def _map_station_month_data( + self, + station_id: int, + source_data: TimeOfficeSourceData, + plan: TimeOfficePlanSource | None, + ) -> StationMonthData: + """Map one station's TimeOffice source data into StationMonthData.""" + plan_employees = tuple( + plan_employee for plan_employee in source_data.plan_employees if plan_employee.station_id == station_id + ) + + return StationMonthData( + station=Station( + station_id=station_id, + name=None if plan is None else plan.station_name, + source_planning_unit_id=station_id if plan is None else plan.source_planning_unit_id, + ), + period=source_data.period, + source_plan_id=None if plan is None else plan.source_plan_id, + employees=self._map_employees(plan_employees), + memberships=self._map_memberships(plan_employees), + ) + + def _map_employees( + self, + plan_employees: tuple[TimeOfficePlanEmployeeSource, ...], + ) -> tuple[Employee, ...]: + """Map TimeOffice plan employees to canonical Employee records.""" + employees_by_id: dict[int, Employee] = {} + + for source_employee in plan_employees: + employees_by_id[source_employee.employee_id] = Employee( + employee_id=source_employee.employee_id, + personnel_number=source_employee.personnel_number, + first_name=source_employee.first_name, + last_name=source_employee.last_name, + display_name=self._display_name(source_employee), + group_id=None, + active=True, + ) + + return tuple(employees_by_id.values()) + + def _map_memberships( + self, + plan_employees: tuple[TimeOfficePlanEmployeeSource, ...], + ) -> tuple[Membership, ...]: + """Map TimeOffice plan employees to local station memberships.""" + memberships_by_key: dict[tuple[int, int], Membership] = {} + + for source_employee in plan_employees: + key = (source_employee.employee_id, source_employee.station_id) + + memberships_by_key[key] = Membership( + employee_id=source_employee.employee_id, + station_id=source_employee.station_id, + membership_type="local", + valid_from=source_employee.valid_from, + valid_until=source_employee.valid_until, + is_substitute=source_employee.is_substitute, + ) + + return tuple(memberships_by_key.values()) + + def _display_name(self, source_employee: TimeOfficePlanEmployeeSource) -> str: + """Build a readable employee display name.""" + name_parts = [ + source_employee.first_name, + source_employee.last_name, + ] + display_name = " ".join(part for part in name_parts if part) + + if display_name: + return display_name + + if source_employee.short_name: + return source_employee.short_name + + if source_employee.personnel_number: + return source_employee.personnel_number + + return f"Employee {source_employee.employee_id}" + def combine_station_month_data(self, station_month_data: tuple[StationMonthData, ...]) -> SchedulingDataset: """Combine station-month data into one scheduling dataset. @@ -61,7 +143,9 @@ def combine_station_month_data(self, station_month_data: tuple[StationMonthData, stations=tuple(data.station for data in station_month_data), regular_station_ids=regular_station_ids, jump_pool_station_ids=effective_jump_pool_ids, - employees=tuple(employee for data in station_month_data for employee in data.employees), + employees=self._unique_employees( + tuple(employee for data in station_month_data for employee in data.employees) + ), shifts=tuple(shift for data in station_month_data for shift in data.shifts), demand=tuple(demand for data in station_month_data for demand in data.demand), memberships=tuple(membership for data in station_month_data for membership in data.memberships), @@ -70,3 +154,12 @@ def combine_station_month_data(self, station_month_data: tuple[StationMonthData, rules=tuple(rule for data in station_month_data for rule in data.rules), preferences=tuple(preference for data in station_month_data for preference in data.preferences), ) + + def _unique_employees(self, employees: tuple[Employee, ...]) -> tuple[Employee, ...]: + """Deduplicate employees by employee_id.""" + employees_by_id: dict[int, Employee] = {} + + for employee in employees: + employees_by_id[employee.employee_id] = employee + + return tuple(employees_by_id.values()) diff --git a/src/scheduling/timeoffice/models.py b/src/scheduling/timeoffice/models.py index 03c083a8..4cdf0d3e 100644 --- a/src/scheduling/timeoffice/models.py +++ b/src/scheduling/timeoffice/models.py @@ -2,7 +2,8 @@ from pydantic import BaseModel, field_validator -from src.scheduling.models.core import PlanningPeriod +from src.scheduling.models import PlanningPeriod +from src.scheduling.timeoffice.source_models import TimeOfficePlanEmployeeSource, TimeOfficePlanSource class FetchStationsRequest(BaseModel): @@ -35,14 +36,14 @@ def station_ids_must_be_positive(cls, station_ids: tuple[int, ...]) -> tuple[int class TimeOfficeSourceData(BaseModel): """Raw TimeOffice source data read from the database. - Current iteration: shallow placeholder. - - Later this should hold typed source-record collections. It must stay - TimeOffice-facing and must not become the canonical scheduling model. + This model stays TimeOffice-facing. It must not become the canonical + scheduling model. """ station_ids: tuple[int, ...] period: PlanningPeriod + plans: tuple[TimeOfficePlanSource, ...] = () + plan_employees: tuple[TimeOfficePlanEmployeeSource, ...] = () class CacheWriteResult(BaseModel): diff --git a/src/scheduling/timeoffice/service.py b/src/scheduling/timeoffice/service.py index c7d82a2e..6e911d52 100644 --- a/src/scheduling/timeoffice/service.py +++ b/src/scheduling/timeoffice/service.py @@ -1,9 +1,9 @@ -from src.scheduling.models.dataset import SchedulingDataset, StationMonthData +from src.scheduling.models import SchedulingDataset, StationMonthData from src.scheduling.timeoffice.cache import TimeOfficeCache from src.scheduling.timeoffice.database import TimeOfficeDatabase from src.scheduling.timeoffice.mapping import TimeOfficeMapper from src.scheduling.timeoffice.models import FetchStationsRequest -from src.scheduling.timeoffice.settings import TimeOfficeSettings +from src.scheduling.timeoffice.settings import TimeOfficeSettings, load_settings class TimeOfficeService: @@ -66,7 +66,7 @@ def _get_station_month_data(self, request: FetchStationsRequest) -> tuple[Statio def create_timeoffice_service(settings: TimeOfficeSettings | None = None) -> TimeOfficeService: """Create the default TimeOffice service.""" - settings = settings or TimeOfficeSettings() + settings = settings or load_settings() return TimeOfficeService( settings=settings, diff --git a/src/scheduling/timeoffice/settings.py b/src/scheduling/timeoffice/settings.py index 94a0a1d3..f6235e90 100644 --- a/src/scheduling/timeoffice/settings.py +++ b/src/scheduling/timeoffice/settings.py @@ -1,21 +1,29 @@ from pathlib import Path -from pydantic import Field +from pydantic import Field, SecretStr from pydantic_settings import BaseSettings, SettingsConfigDict class TimeOfficeSettings(BaseSettings): """Settings for TimeOffice data transfer.""" - model_config = SettingsConfigDict(extra="ignore") + model_config = SettingsConfigDict( + env_file=".env", + extra="ignore", + ) - db_server: str | None = None - db_name: str | None = None - db_user: str | None = None - db_password: str | None = None db_driver: str = "ODBC Driver 18 for SQL Server" + db_server: str + db_name: str + db_user: str + db_password: SecretStr enable_cache: bool = True cache_root: Path = Path(".cache/timeoffice") jump_pool_station_ids: tuple[int, ...] = Field(default_factory=tuple) + + +def load_settings() -> TimeOfficeSettings: + """Load TimeOffice settings from environment variables and .env files.""" + return TimeOfficeSettings() # type: ignore[call-arg] diff --git a/src/scheduling/timeoffice/source_models.py b/src/scheduling/timeoffice/source_models.py new file mode 100644 index 00000000..4f0b26a7 --- /dev/null +++ b/src/scheduling/timeoffice/source_models.py @@ -0,0 +1,42 @@ +from datetime import date as Date + +from pydantic import BaseModel, Field + +from src.scheduling.models.core import PlanningPeriod + + +class TimeOfficePlanSource(BaseModel): + """TimeOffice plan metadata for one station/planning unit and period.""" + + station_id: int = Field(gt=0) + source_plan_id: int = Field(gt=0) + source_planning_unit_id: int = Field(gt=0) + + station_name: str | None = None + + status_id: int | None = None + planning_interval_id: int | None = None + + period: PlanningPeriod + + +class TimeOfficePlanEmployeeSource(BaseModel): + """Employee assigned to a concrete TimeOffice monthly plan.""" + + source_plan_employee_id: int = Field(gt=0) + source_plan_id: int = Field(gt=0) + + station_id: int = Field(gt=0) + employee_id: int = Field(gt=0) + + personnel_number: str | None = None + first_name: str | None = None + last_name: str | None = None + short_name: str | None = None + + source_profession_id: int | None = None + + valid_from: Date | None = None + valid_until: Date | None = None + + is_substitute: bool | None = None From e8c6b61715d4338f1e03073d3e04a84d5d388131 Mon Sep 17 00:00:00 2001 From: Tom Wawerek Date: Sat, 13 Jun 2026 10:15:59 +0200 Subject: [PATCH 05/18] fix: fetch command --- src/main.py | 1 - src/scheduling/models/__init__.py | 19 +- src/scheduling/models/dataset.py | 36 +- src/scheduling/models/demand.py | 9 +- src/scheduling/models/relations.py | 78 ++-- src/scheduling/models/shift.py | 16 +- src/scheduling/sql/SELECT.sql | 13 - src/scheduling/timeoffice/cache.py | 64 ---- src/scheduling/timeoffice/config.py | 229 ++++++++++++ src/scheduling/timeoffice/constants.py | 17 - src/scheduling/timeoffice/database.py | 208 ++--------- src/scheduling/timeoffice/mapping.py | 165 --------- src/scheduling/timeoffice/models.py | 36 +- .../timeoffice/repositories/__init__.py | 13 + .../timeoffice/repositories/employees.py | 191 ++++++++++ .../timeoffice/repositories/helpers.py | 78 ++++ .../timeoffice/repositories/plans.py | 144 ++++++++ .../timeoffice/repositories/shifts.py | 333 ++++++++++++++++++ src/scheduling/timeoffice/service.py | 56 +-- src/scheduling/timeoffice/settings.py | 9 +- src/scheduling/timeoffice/source_models.py | 42 --- 21 files changed, 1133 insertions(+), 624 deletions(-) delete mode 100644 src/scheduling/sql/SELECT.sql delete mode 100644 src/scheduling/timeoffice/cache.py create mode 100644 src/scheduling/timeoffice/config.py delete mode 100644 src/scheduling/timeoffice/constants.py delete mode 100644 src/scheduling/timeoffice/mapping.py create mode 100644 src/scheduling/timeoffice/repositories/__init__.py create mode 100644 src/scheduling/timeoffice/repositories/employees.py create mode 100644 src/scheduling/timeoffice/repositories/helpers.py create mode 100644 src/scheduling/timeoffice/repositories/plans.py create mode 100644 src/scheduling/timeoffice/repositories/shifts.py delete mode 100644 src/scheduling/timeoffice/source_models.py diff --git a/src/main.py b/src/main.py index 9d1ae912..b281282b 100644 --- a/src/main.py +++ b/src/main.py @@ -45,7 +45,6 @@ def fetch( start=start.date(), end=end.date(), ), - use_cache=use_cache, ) timeoffice = create_timeoffice_service() diff --git a/src/scheduling/models/__init__.py b/src/scheduling/models/__init__.py index c81993b3..9c667243 100644 --- a/src/scheduling/models/__init__.py +++ b/src/scheduling/models/__init__.py @@ -1,28 +1,39 @@ from src.scheduling.models.core import PlanningPeriod -from src.scheduling.models.dataset import SchedulingDataset, StationMonthData -from src.scheduling.models.demand import Demand +from src.scheduling.models.dataset import SchedulingDataset +from src.scheduling.models.demand import Demand, DemandType from src.scheduling.models.employee import Employee from src.scheduling.models.relations import ( Assignment, + AssignmentType, Availability, + AvailabilityType, Membership, + MembershipType, Preference, + PreferenceType, Rule, + RuleType, ) -from src.scheduling.models.shift import Shift +from src.scheduling.models.shift import Shift, ShiftKind from src.scheduling.models.station import Station __all__ = [ "Assignment", + "AssignmentType", "Availability", + "AvailabilityType", "Demand", + "DemandType", "Employee", "Membership", + "MembershipType", "PlanningPeriod", "Preference", + "PreferenceType", "Rule", + "RuleType", "SchedulingDataset", "Shift", + "ShiftKind", "Station", - "StationMonthData", ] diff --git a/src/scheduling/models/dataset.py b/src/scheduling/models/dataset.py index 21f16043..f775faf3 100644 --- a/src/scheduling/models/dataset.py +++ b/src/scheduling/models/dataset.py @@ -3,44 +3,18 @@ from src.scheduling.models.core import PlanningPeriod from src.scheduling.models.demand import Demand from src.scheduling.models.employee import Employee -from src.scheduling.models.relations import ( - Assignment, - Availability, - Membership, - Preference, - Rule, -) +from src.scheduling.models.relations import Assignment, Availability, Membership, Preference, Rule from src.scheduling.models.shift import Shift from src.scheduling.models.station import Station -class StationMonthData(BaseModel): - """Scheduling-relevant data for one station and one planning period. +class SchedulingDataset(BaseModel): + """Combined scheduling data for one solver run. - This is the canonical cache payload. - It is not TimeOffice-specific and it is not solver-specific. + This is the main application-facing data model. It represents the scheduling + problem for one period and a selected set of stations. """ - schema_version: int = 1 - - station: Station - period: PlanningPeriod - - source_plan_id: int | None = None - - employees: tuple[Employee, ...] = () - shifts: tuple[Shift, ...] = () - demand: tuple[Demand, ...] = () - memberships: tuple[Membership, ...] = () - assignments: tuple[Assignment, ...] = () - availability: tuple[Availability, ...] = () - rules: tuple[Rule, ...] = () - preferences: tuple[Preference, ...] = () - - -class SchedulingDataset(BaseModel): - """Combined scheduling data for one solver run.""" - period: PlanningPeriod stations: tuple[Station, ...] diff --git a/src/scheduling/models/demand.py b/src/scheduling/models/demand.py index 59b453b4..cce7b58f 100644 --- a/src/scheduling/models/demand.py +++ b/src/scheduling/models/demand.py @@ -1,9 +1,14 @@ from datetime import date -from typing import Literal +from enum import StrEnum from pydantic import BaseModel, Field +class DemandType(StrEnum): + MINIMUM = "minimum" + OPTIONAL = "optional" + + class Demand(BaseModel): """Staffing need or optional coverage goal for a station/date/shift.""" @@ -16,6 +21,6 @@ class Demand(BaseModel): required_group_id: str | None = None required_qualification_id: str | None = None - demand_type: Literal["minimum", "optional"] = "minimum" + demand_type: DemandType = DemandType.MINIMUM priority: int = Field(default=0, ge=0) weight: int = Field(default=1, ge=0) diff --git a/src/scheduling/models/relations.py b/src/scheduling/models/relations.py index 8d740b3e..d61f721e 100644 --- a/src/scheduling/models/relations.py +++ b/src/scheduling/models/relations.py @@ -1,16 +1,59 @@ from datetime import date as Date -from typing import Literal +from enum import StrEnum from pydantic import BaseModel, Field +class MembershipType(StrEnum): + LOCAL = "local" + JUMP_POOL = "jump_pool" + EXTERNAL = "external" + UNKNOWN = "unknown" + + +class AssignmentType(StrEnum): + PLANNED = "planned" + FIXED = "fixed" + EXTERNAL = "external" + MANAGEMENT = "management" + + +class AvailabilityType(StrEnum): + UNAVAILABLE = "unavailable" + VACATION = "vacation" + TRAINING = "training" + FREE_WEEKEND = "free_weekend" + AVAILABLE_ONLY = "available_only" + + +class RuleType(StrEnum): + MEDICAL_NIGHT_BAN = "medical_night_ban" + NIGHT_WATCH_ONLY = "night_watch_only" + WEEKDAY_EARLY_ONLY = "weekday_early_only" + FIXED_WEEKDAY_FREE = "fixed_weekday_free" + DOES_NOT_COUNT_FOR_MINIMUM_STAFFING = "does_not_count_for_minimum_staffing" + NO_NIGHT_BEFORE_PROTECTED_FREE_TIME = "no_night_before_protected_free_time" + MAX_CONSECUTIVE_DAYS = "max_consecutive_days" + MIN_REST_TIME = "min_rest_time" + OTHER = "other" + + +class PreferenceType(StrEnum): + DAY_OFF = "day_off" + SHIFT_OFF = "shift_off" + SHIFT_ON = "shift_on" + WORK_ANY = "work_any" + AVOID_SHIFT = "avoid_shift" + PREFER_SHIFT = "prefer_shift" + + class Membership(BaseModel): """Employee membership in a station-local or jump-pool staffing pool.""" employee_id: int = Field(gt=0) station_id: int = Field(gt=0) - membership_type: Literal["local", "jump_pool", "external", "unknown"] = "local" + membership_type: MembershipType = MembershipType.LOCAL valid_from: Date | None = None valid_until: Date | None = None @@ -27,7 +70,7 @@ class Assignment(BaseModel): shift_id: str station_id: int | None = None - assignment_type: Literal["planned", "fixed", "external", "management"] = "planned" + assignment_type: AssignmentType = AssignmentType.PLANNED counts_as_work: bool = True counts_for_minimum_staffing: bool | None = None @@ -44,13 +87,7 @@ class Availability(BaseModel): employee_id: int = Field(gt=0) date: Date - availability_type: Literal[ - "unavailable", - "vacation", - "training", - "free_weekend", - "available_only", - ] + availability_type: AvailabilityType shift_ids: tuple[str, ...] | None = None is_hard: bool = True @@ -68,17 +105,7 @@ class Rule(BaseModel): """ rule_id: str - rule_type: Literal[ - "medical_night_ban", - "night_watch_only", - "weekday_early_only", - "fixed_weekday_free", - "does_not_count_for_minimum_staffing", - "no_night_before_protected_free_time", - "max_consecutive_days", - "min_rest_time", - "other", - ] + rule_type: RuleType employee_id: int | None = None station_id: int | None = None @@ -97,14 +124,7 @@ class Preference(BaseModel): employee_id: int = Field(gt=0) - preference_type: Literal[ - "day_off", - "shift_off", - "shift_on", - "work_any", - "avoid_shift", - "prefer_shift", - ] + preference_type: PreferenceType date: Date | None = None weekdays: tuple[int, ...] | None = None diff --git a/src/scheduling/models/shift.py b/src/scheduling/models/shift.py index 74e008e7..a6e1f26c 100644 --- a/src/scheduling/models/shift.py +++ b/src/scheduling/models/shift.py @@ -1,8 +1,19 @@ -from typing import Literal +from enum import StrEnum from pydantic import BaseModel, Field +class ShiftKind(StrEnum): + """Canonical solver-facing shift category.""" + + EARLY = "early" + INTERMEDIATE = "intermediate" + LATE = "late" + NIGHT = "night" + MANAGEMENT = "management" + OTHER = "other" + + class Shift(BaseModel): """Canonical assignable shift definition. @@ -18,7 +29,7 @@ class Shift(BaseModel): source_shift_id: int | None = None source_code: str | None = None - kind: Literal["early", "intermediate", "late", "night", "management", "other"] + kind: ShiftKind start_minute: int = Field(ge=0, lt=24 * 60) end_minute: int = Field(ge=0, lt=24 * 60) @@ -27,6 +38,7 @@ class Shift(BaseModel): break_minutes: int = Field(default=0, ge=0) net_work_minutes: int = Field(ge=0) + assignable: bool = True counts_as_work: bool = True counts_for_minimum_staffing: bool = True is_night: bool = False diff --git a/src/scheduling/sql/SELECT.sql b/src/scheduling/sql/SELECT.sql deleted file mode 100644 index 2585b061..00000000 --- a/src/scheduling/sql/SELECT.sql +++ /dev/null @@ -1,13 +0,0 @@ -SELECT - ku.TABLE_NAME, - ku.COLUMN_NAME, - tc.CONSTRAINT_TYPE -FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc -JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE ku - ON tc.CONSTRAINT_NAME = ku.CONSTRAINT_NAME -WHERE ku.TABLE_NAME IN ( - 'TPlanPersonal', - 'TPersonal', - 'TPlanungseinheitenPersonal' -) -ORDER BY ku.TABLE_NAME, ku.ORDINAL_POSITION; diff --git a/src/scheduling/timeoffice/cache.py b/src/scheduling/timeoffice/cache.py deleted file mode 100644 index 3b4b9c39..00000000 --- a/src/scheduling/timeoffice/cache.py +++ /dev/null @@ -1,64 +0,0 @@ -from pathlib import Path - -from src.scheduling.models import PlanningPeriod, StationMonthData -from src.scheduling.timeoffice.models import CacheWriteResult -from src.scheduling.timeoffice.settings import TimeOfficeSettings - - -class TimeOfficeCache: - """Local file cache for mapped scheduling data from TimeOffice. - - The cache stores canonical StationMonthData objects, not raw TimeOffice table - dumps and not solver-specific objects. - """ - - STATION_MONTH_DATA_FILE = "station_month_data.json" - - def __init__(self, settings: TimeOfficeSettings): - self._settings = settings - - def station_month_directory(self, station_id: int, period: PlanningPeriod) -> Path: - """Return the cache directory for one station/month.""" - return self._settings.cache_root / str(station_id) / period.month_folder - - def station_month_data_path(self, station_id: int, period: PlanningPeriod) -> Path: - """Return the station-month data JSON path.""" - return self.station_month_directory(station_id, period) / self.STATION_MONTH_DATA_FILE - - def read_many(self, station_ids: tuple[int, ...], period: PlanningPeriod) -> tuple[StationMonthData, ...]: - """Read multiple station-month data objects from cache.""" - print("[timeoffice] cache.read_many") - return tuple(self.read(station_id, period) for station_id in station_ids) - - def read(self, station_id: int, period: PlanningPeriod) -> StationMonthData: - """Read one station-month data object from cache.""" - data_path = self.station_month_data_path(station_id, period) - - print(f"[timeoffice] cache.read station={station_id} path={data_path}") - - return StationMonthData.model_validate_json(data_path.read_text(encoding="utf-8")) - - def write_many(self, station_data: tuple[StationMonthData, ...]) -> tuple[CacheWriteResult, ...]: - """Write multiple station-month data objects.""" - print("[timeoffice] cache.write_many") - return tuple(self.write(data) for data in station_data) - - def write(self, data: StationMonthData) -> CacheWriteResult: - """Write one station-month data object.""" - station_id = data.station.station_id - cache_directory = self.station_month_directory(station_id, data.period) - cache_directory.mkdir(parents=True, exist_ok=True) - - data_path = self.station_month_data_path(station_id, data.period) - data_path.write_text( - data.model_dump_json(indent=2) + "\n", - encoding="utf-8", - ) - - print(f"[timeoffice] cache.write station={station_id} path={data_path}") - - return CacheWriteResult( - station_id=station_id, - period=data.period, - cache_directory=cache_directory, - ) diff --git a/src/scheduling/timeoffice/config.py b/src/scheduling/timeoffice/config.py new file mode 100644 index 00000000..621a8818 --- /dev/null +++ b/src/scheduling/timeoffice/config.py @@ -0,0 +1,229 @@ +from enum import IntEnum, StrEnum + +from pydantic import BaseModel, Field + +from src.scheduling.models.shift import ShiftKind + + +class TimeOfficePlanStatus(IntEnum): + """Known TimeOffice plan status ids.""" + + TARGET_PLANNING = 20 + ACTUAL = 50 + COMPLETED = 70 + SETTLED = 80 + + +class TimeOfficePlanningInterval(IntEnum): + """Known TimeOffice planning interval ids.""" + + MONTHLY = 1 + ANNUAL = 3 + + +class TimeOfficeShiftType(IntEnum): + """Known TimeOffice shift type ids.""" + + WORK = 1 + + +class StationType(StrEnum): + """Configured station role for the TimeOffice import.""" + + REGULAR = "regular" + JUMP_POOL = "jump_pool" + + +class TimeOfficePlanSelection(BaseModel): + """Which TimeOffice plans are used as source for scheduling.""" + + planning_interval_id: TimeOfficePlanningInterval + plan_status_id: TimeOfficePlanStatus + + +class TimeOfficeStationConfig(BaseModel): + """Configured TimeOffice station relevant for the project.""" + + station_id: int = Field(gt=0) + label: str + station_type: StationType = StationType.REGULAR + area_hint: str | None = None + notes: str | None = None + + +class TimeOfficeShiftConfig(BaseModel): + """Configured TimeOffice shift relevant for the solver.""" + + source_shift_id: int = Field(gt=0) + expected_code: str + + kind: ShiftKind + group_id: str + + assignable: bool = True + counts_as_work: bool = True + counts_for_minimum_staffing: bool = True + + description: str | None = None + + +class TimeOfficeConfig(BaseModel): + """Source of truth for TimeOffice IDs and project-specific import semantics. + + Keep external TimeOffice IDs and project/domain decisions here instead of + scattering them through repositories, SQL queries, or solver code. + """ + + plan_selection: TimeOfficePlanSelection + stations: tuple[TimeOfficeStationConfig, ...] + solver_shifts: tuple[TimeOfficeShiftConfig, ...] + assignable_shift_type_ids: tuple[TimeOfficeShiftType, ...] + + @property + def station_ids(self) -> tuple[int, ...]: + """Return all configured TimeOffice station ids.""" + return tuple(station.station_id for station in self.stations) + + @property + def solver_shift_ids(self) -> tuple[int, ...]: + """Return all configured TimeOffice shift ids used by the solver.""" + return tuple(shift.source_shift_id for shift in self.solver_shifts) + + @property + def stations_by_id(self) -> dict[int, TimeOfficeStationConfig]: + """Return configured stations keyed by TimeOffice station id.""" + return {station.station_id: station for station in self.stations} + + @property + def shifts_by_id(self) -> dict[int, TimeOfficeShiftConfig]: + """Return configured solver shifts keyed by TimeOffice shift id.""" + return {shift.source_shift_id: shift for shift in self.solver_shifts} + + def regular_station_ids_for(self, station_ids: tuple[int, ...]) -> tuple[int, ...]: + """Return requested station ids that are configured as regular stations.""" + return tuple( + station_id for station_id in station_ids if self.station_type_for(station_id) == StationType.REGULAR + ) + + def jump_pool_station_ids_for(self, station_ids: tuple[int, ...]) -> tuple[int, ...]: + """Return requested station ids that are configured as jump-pool stations.""" + return tuple( + station_id for station_id in station_ids if self.station_type_for(station_id) == StationType.JUMP_POOL + ) + + def station_type_for(self, station_id: int) -> StationType: + """Return configured station type. + + Unknown stations default to regular to keep exploratory database reads + possible while we are still inspecting TimeOffice data. + """ + station = self.stations_by_id.get(station_id) + + if station is None: + return StationType.REGULAR + + return station.station_type + + +STATION_77 = 77 +STATION_79_LEGACY = 79 +STATION_337 = 337 +STATION_85 = 85 +STATION_239 = 239 +STATION_78 = 78 +SPRINGERPOOL_408 = 408 + +SHIFT_Z60 = 1406 +SHIFT_T75 = 2906 +SHIFT_F2 = 2939 +SHIFT_S2 = 2947 +SHIFT_N2 = 2953 + +TIMEOFFICE_CONFIG = TimeOfficeConfig( + plan_selection=TimeOfficePlanSelection( + planning_interval_id=TimeOfficePlanningInterval.MONTHLY, + plan_status_id=TimeOfficePlanStatus.TARGET_PLANNING, + ), + assignable_shift_type_ids=(TimeOfficeShiftType.WORK,), + stations=( + TimeOfficeStationConfig( + station_id=STATION_77, + label="Station 77", + area_hint="Bereich 5 / Bereich 32", + notes="Previously used station.", + ), + TimeOfficeStationConfig( + station_id=STATION_79_LEGACY, + label="Station 79", + notes="Legacy/development station used during refactoring; not listed as long-term station.", + ), + TimeOfficeStationConfig( + station_id=STATION_337, + label="Station 337", + area_hint="Bereich 5 / Bereich 32", + ), + TimeOfficeStationConfig( + station_id=STATION_85, + label="Station 85", + area_hint="Bereich 5 / Bereich 32", + ), + TimeOfficeStationConfig( + station_id=STATION_239, + label="Station 239", + area_hint="Bereich 17", + ), + TimeOfficeStationConfig( + station_id=STATION_78, + label="Station 78", + area_hint="Bereich 5 / Bereich 32", + ), + TimeOfficeStationConfig( + station_id=SPRINGERPOOL_408, + label="Springerpool", + station_type=StationType.JUMP_POOL, + area_hint="Bereich 546", + ), + ), + solver_shifts=( + TimeOfficeShiftConfig( + source_shift_id=SHIFT_F2, + expected_code="F2_", + kind=ShiftKind.EARLY, + group_id="early", + counts_for_minimum_staffing=True, + description="Required early shift F2.", + ), + TimeOfficeShiftConfig( + source_shift_id=SHIFT_S2, + expected_code="S2_", + kind=ShiftKind.LATE, + group_id="late", + counts_for_minimum_staffing=True, + description="Required late shift S2.", + ), + TimeOfficeShiftConfig( + source_shift_id=SHIFT_N2, + expected_code="N2_", + kind=ShiftKind.NIGHT, + group_id="night", + counts_for_minimum_staffing=True, + description="Required night shift N2.", + ), + TimeOfficeShiftConfig( + source_shift_id=SHIFT_T75, + expected_code="T75_", + kind=ShiftKind.INTERMEDIATE, + group_id="intermediate", + counts_for_minimum_staffing=True, + description="Optional/intermediate Zwischendienst T75.", + ), + TimeOfficeShiftConfig( + source_shift_id=SHIFT_Z60, + expected_code="Z60", + kind=ShiftKind.MANAGEMENT, + group_id="management", + counts_for_minimum_staffing=False, + description="Management shift Z60. Counts as work, not minimum staffing.", + ), + ), +) diff --git a/src/scheduling/timeoffice/constants.py b/src/scheduling/timeoffice/constants.py deleted file mode 100644 index 1d5a227e..00000000 --- a/src/scheduling/timeoffice/constants.py +++ /dev/null @@ -1,17 +0,0 @@ -from enum import IntEnum - - -class TimeOfficePlanStatus(IntEnum): - """Known TimeOffice plan status ids.""" - - TARGET_PLANNING = 20 - ACTUAL = 50 - COMPLETED = 70 - SETTLED = 80 - - -class TimeOfficePlanningInterval(IntEnum): - """Known TimeOffice planning interval ids.""" - - MONTHLY = 1 - ANNUAL = 3 diff --git a/src/scheduling/timeoffice/database.py b/src/scheduling/timeoffice/database.py index 66f7a86e..a521074e 100644 --- a/src/scheduling/timeoffice/database.py +++ b/src/scheduling/timeoffice/database.py @@ -1,28 +1,35 @@ -from sqlalchemy import URL, bindparam, create_engine, text +from sqlalchemy import URL, create_engine from sqlalchemy.engine import Engine -from src.scheduling.timeoffice.constants import TimeOfficePlanningInterval, TimeOfficePlanStatus -from src.scheduling.timeoffice.models import FetchStationsRequest, TimeOfficeSourceData +from src.scheduling.models.dataset import SchedulingDataset +from src.scheduling.timeoffice.config import TIMEOFFICE_CONFIG, TimeOfficeConfig +from src.scheduling.timeoffice.models import FetchStationsRequest +from src.scheduling.timeoffice.repositories.employees import TimeOfficeEmployeeRepository +from src.scheduling.timeoffice.repositories.plans import TimeOfficePlanRepository +from src.scheduling.timeoffice.repositories.shifts import TimeOfficeShiftRepository from src.scheduling.timeoffice.settings import TimeOfficeSettings -from src.scheduling.timeoffice.source_models import TimeOfficePlanEmployeeSource, TimeOfficePlanSource class TimeOfficeDatabase: - """Read source data from the TimeOffice database. + """Read TimeOffice data and build the canonical scheduling dataset. - This class owns database access and SQL queries. + This class owns the database connection and explicit repository orchestration. + Repositories own SQL access, source-local validation, and mapping to canonical + scheduling models. """ - def __init__(self, settings: TimeOfficeSettings): + def __init__( + self, + settings: TimeOfficeSettings, + config: TimeOfficeConfig = TIMEOFFICE_CONFIG, + ): self._settings = settings - self._engine: Engine | None = None - - def _get_engine(self) -> Engine: - """Create or return the TimeOffice database engine.""" - if self._engine is None: - self._engine = create_engine(self._database_url()) + self._config = config + self._engine: Engine = create_engine(self._database_url()) - return self._engine + self._plans = TimeOfficePlanRepository(config) + self._employees = TimeOfficeEmployeeRepository() + self._shifts = TimeOfficeShiftRepository(config) def _database_url(self) -> URL: """Build the SQLAlchemy URL for the TimeOffice SQL Server database.""" @@ -40,167 +47,30 @@ def _database_url(self) -> URL: query=query, ) - def read(self, request: FetchStationsRequest) -> TimeOfficeSourceData: - """Read all source data needed to build StationMonthData.""" + def read(self, request: FetchStationsRequest) -> SchedulingDataset: + """Read and map TimeOffice data into the canonical scheduling dataset.""" print( "[timeoffice] database.read " f"stations={list(request.station_ids)} " f"period={request.period.start.isoformat()}..{request.period.end.isoformat()}" ) - plans = self._read_plans(request) - plan_employees = self._read_plan_employees(plans) + with self._engine.connect() as connection: + plan_result = self._plans.fetch(connection, request) + employee_result = self._employees.fetch(connection, plan_result.plans) + shift_result = self._shifts.fetch(connection) - return TimeOfficeSourceData( - station_ids=request.station_ids, + return SchedulingDataset( period=request.period, - plans=plans, - plan_employees=plan_employees, + stations=plan_result.stations, + regular_station_ids=self._config.regular_station_ids_for(request.station_ids), + jump_pool_station_ids=self._config.jump_pool_station_ids_for(request.station_ids), + employees=employee_result.employees, + shifts=shift_result.shifts, + demand=(), + memberships=employee_result.memberships, + assignments=(), + availability=(), + rules=(), + preferences=(), ) - - def _read_plans(self, request: FetchStationsRequest) -> tuple[TimeOfficePlanSource, ...]: - """Read monthly target plans for all requested stations.""" - query = text( - """ - SELECT - p.Prim AS source_plan_id, - p.RefPlanungseinheiten AS source_planning_unit_id, - p.RefPlanungseinheiten AS station_id, - COALESCE(pe.Bezeichnung, pe.KurzBez) AS station_name, - p.RefStati AS status_id, - p.RefPlanungsIntervalle AS planning_interval_id - FROM TPlan p - LEFT JOIN TPlanungseinheiten pe - ON pe.Prim = p.RefPlanungseinheiten - WHERE p.RefPlanungseinheiten IN :station_ids - AND p.VonDat = :period_start - AND p.BisDat = :period_end - AND p.RefPlanungsIntervalle = :planning_interval_id - AND p.RefStati = :status_id - """ - ).bindparams(bindparam("station_ids", expanding=True)) - - with self._get_engine().connect() as connection: - rows = ( - connection.execute( - query, - { - "station_ids": request.station_ids, - "period_start": request.period.start, - "period_end": request.period.end, - "planning_interval_id": TimeOfficePlanningInterval.MONTHLY, - "status_id": TimeOfficePlanStatus.TARGET_PLANNING, - }, - ) - .mappings() - .all() - ) - - plans = tuple( - TimeOfficePlanSource( - station_id=row["station_id"], - source_plan_id=row["source_plan_id"], - source_planning_unit_id=row["source_planning_unit_id"], - station_name=row["station_name"], - status_id=row["status_id"], - planning_interval_id=row["planning_interval_id"], - period=request.period, - ) - for row in rows - ) - - self._ensure_one_plan_per_requested_station(request, plans) - - print(f"[timeoffice] database.read_plans rows={len(plans)}") - - return plans - - def _ensure_one_plan_per_requested_station( - self, request: FetchStationsRequest, plans: tuple[TimeOfficePlanSource, ...] - ) -> None: - """Ensure the plan query returned exactly one plan per requested station.""" - plans_by_station: dict[int, list[TimeOfficePlanSource]] = {station_id: [] for station_id in request.station_ids} - - for plan in plans: - plans_by_station.setdefault(plan.station_id, []).append(plan) - - missing_station_ids = [ - station_id for station_id, station_plans in plans_by_station.items() if not station_plans - ] - - if missing_station_ids: - raise ValueError( - "No monthly target TimeOffice plan found for station(s) " - f"{missing_station_ids} and period " - f"{request.period.start.isoformat()}..{request.period.end.isoformat()}." - ) - - ambiguous_station_ids = [ - station_id for station_id, station_plans in plans_by_station.items() if len(station_plans) > 1 - ] - - if ambiguous_station_ids: - details = { - station_id: [plan.source_plan_id for plan in station_plans] - for station_id, station_plans in plans_by_station.items() - if len(station_plans) > 1 - } - raise ValueError(f"Multiple monthly target TimeOffice plans found for station(s): {details}") - - def _read_plan_employees( - self, - plans: tuple[TimeOfficePlanSource, ...], - ) -> tuple[TimeOfficePlanEmployeeSource, ...]: - """Read employees assigned to the selected monthly TimeOffice plans.""" - plan_ids = tuple(plan.source_plan_id for plan in plans) - - query = text( - """ - SELECT - pp.Prim AS source_plan_employee_id, - pp.RefPlan AS source_plan_id, - tp.RefPlanungseinheiten AS station_id, - pp.RefPersonal AS employee_id, - - p.PersNr AS personnel_number, - p.Vorname AS first_name, - p.Name AS last_name, - p.KurzName AS short_name, - - pp.RefBerufe AS source_profession_id, - pp.VonDat AS valid_from, - pp.BisDat AS valid_until, - pp.IstVonErsatz AS is_substitute - FROM TPlanPersonal pp - JOIN TPlan tp - ON tp.Prim = pp.RefPlan - JOIN TPersonal p - ON p.Prim = pp.RefPersonal - WHERE pp.RefPlan IN :plan_ids - """ - ).bindparams(bindparam("plan_ids", expanding=True)) - - with self._get_engine().connect() as connection: - rows = connection.execute(query, {"plan_ids": plan_ids}).mappings().all() - - plan_employees = tuple( - TimeOfficePlanEmployeeSource( - source_plan_employee_id=row["source_plan_employee_id"], - source_plan_id=row["source_plan_id"], - station_id=row["station_id"], - employee_id=row["employee_id"], - personnel_number=row["personnel_number"], - first_name=row["first_name"], - last_name=row["last_name"], - short_name=row["short_name"], - source_profession_id=row["source_profession_id"], - valid_from=None if row["valid_from"] is None else row["valid_from"].date(), - valid_until=None if row["valid_until"] is None else row["valid_until"].date(), - is_substitute=None if row["is_substitute"] is None else bool(row["is_substitute"]), - ) - for row in rows - ) - - print(f"[timeoffice] database.read_plan_employees rows={len(plan_employees)}") - - return plan_employees diff --git a/src/scheduling/timeoffice/mapping.py b/src/scheduling/timeoffice/mapping.py deleted file mode 100644 index cf1259de..00000000 --- a/src/scheduling/timeoffice/mapping.py +++ /dev/null @@ -1,165 +0,0 @@ -from src.scheduling.models import SchedulingDataset, Station, StationMonthData -from src.scheduling.models.employee import Employee -from src.scheduling.models.relations import Membership -from src.scheduling.timeoffice.models import TimeOfficeSourceData -from src.scheduling.timeoffice.settings import TimeOfficeSettings -from src.scheduling.timeoffice.source_models import TimeOfficePlanEmployeeSource, TimeOfficePlanSource - - -class TimeOfficeMapper: - """Map TimeOffice source data to canonical scheduling data. - - Boundary rules: - - May know TimeOffice source meanings and mapping rules. - - Must not query the database. - - Must not read/write cache files. - - Must not create solver variables or solver input. - """ - - def __init__(self, settings: TimeOfficeSettings): - self._settings = settings - - def to_station_month_data(self, source_data: TimeOfficeSourceData) -> tuple[StationMonthData, ...]: - """Map TimeOffice source data into one StationMonthData object per station.""" - print("[timeoffice] mapper.to_station_month_data") - - plans_by_station = {plan.station_id: plan for plan in source_data.plans} - - return tuple( - self._map_station_month_data( - station_id=station_id, - source_data=source_data, - plan=plans_by_station.get(station_id), - ) - for station_id in source_data.station_ids - ) - - def _map_station_month_data( - self, - station_id: int, - source_data: TimeOfficeSourceData, - plan: TimeOfficePlanSource | None, - ) -> StationMonthData: - """Map one station's TimeOffice source data into StationMonthData.""" - plan_employees = tuple( - plan_employee for plan_employee in source_data.plan_employees if plan_employee.station_id == station_id - ) - - return StationMonthData( - station=Station( - station_id=station_id, - name=None if plan is None else plan.station_name, - source_planning_unit_id=station_id if plan is None else plan.source_planning_unit_id, - ), - period=source_data.period, - source_plan_id=None if plan is None else plan.source_plan_id, - employees=self._map_employees(plan_employees), - memberships=self._map_memberships(plan_employees), - ) - - def _map_employees( - self, - plan_employees: tuple[TimeOfficePlanEmployeeSource, ...], - ) -> tuple[Employee, ...]: - """Map TimeOffice plan employees to canonical Employee records.""" - employees_by_id: dict[int, Employee] = {} - - for source_employee in plan_employees: - employees_by_id[source_employee.employee_id] = Employee( - employee_id=source_employee.employee_id, - personnel_number=source_employee.personnel_number, - first_name=source_employee.first_name, - last_name=source_employee.last_name, - display_name=self._display_name(source_employee), - group_id=None, - active=True, - ) - - return tuple(employees_by_id.values()) - - def _map_memberships( - self, - plan_employees: tuple[TimeOfficePlanEmployeeSource, ...], - ) -> tuple[Membership, ...]: - """Map TimeOffice plan employees to local station memberships.""" - memberships_by_key: dict[tuple[int, int], Membership] = {} - - for source_employee in plan_employees: - key = (source_employee.employee_id, source_employee.station_id) - - memberships_by_key[key] = Membership( - employee_id=source_employee.employee_id, - station_id=source_employee.station_id, - membership_type="local", - valid_from=source_employee.valid_from, - valid_until=source_employee.valid_until, - is_substitute=source_employee.is_substitute, - ) - - return tuple(memberships_by_key.values()) - - def _display_name(self, source_employee: TimeOfficePlanEmployeeSource) -> str: - """Build a readable employee display name.""" - name_parts = [ - source_employee.first_name, - source_employee.last_name, - ] - display_name = " ".join(part for part in name_parts if part) - - if display_name: - return display_name - - if source_employee.short_name: - return source_employee.short_name - - if source_employee.personnel_number: - return source_employee.personnel_number - - return f"Employee {source_employee.employee_id}" - - def combine_station_month_data(self, station_month_data: tuple[StationMonthData, ...]) -> SchedulingDataset: - """Combine station-month data into one scheduling dataset. - - Current iteration: shallow concatenation without deduplication. - """ - print("[timeoffice] mapper.combine_station_month_data") - - if not station_month_data: - raise ValueError("Cannot combine empty station-month data.") - - period = station_month_data[0].period - station_ids = tuple(data.station.station_id for data in station_month_data) - - effective_jump_pool_ids = tuple( - station_id for station_id in station_ids if station_id in self._settings.jump_pool_station_ids - ) - - regular_station_ids = tuple( - station_id for station_id in station_ids if station_id not in effective_jump_pool_ids - ) - - return SchedulingDataset( - period=period, - stations=tuple(data.station for data in station_month_data), - regular_station_ids=regular_station_ids, - jump_pool_station_ids=effective_jump_pool_ids, - employees=self._unique_employees( - tuple(employee for data in station_month_data for employee in data.employees) - ), - shifts=tuple(shift for data in station_month_data for shift in data.shifts), - demand=tuple(demand for data in station_month_data for demand in data.demand), - memberships=tuple(membership for data in station_month_data for membership in data.memberships), - assignments=tuple(assignment for data in station_month_data for assignment in data.assignments), - availability=tuple(item for data in station_month_data for item in data.availability), - rules=tuple(rule for data in station_month_data for rule in data.rules), - preferences=tuple(preference for data in station_month_data for preference in data.preferences), - ) - - def _unique_employees(self, employees: tuple[Employee, ...]) -> tuple[Employee, ...]: - """Deduplicate employees by employee_id.""" - employees_by_id: dict[int, Employee] = {} - - for employee in employees: - employees_by_id[employee.employee_id] = employee - - return tuple(employees_by_id.values()) diff --git a/src/scheduling/timeoffice/models.py b/src/scheduling/timeoffice/models.py index 4cdf0d3e..5c92bb41 100644 --- a/src/scheduling/timeoffice/models.py +++ b/src/scheduling/timeoffice/models.py @@ -1,54 +1,28 @@ -from pathlib import Path - from pydantic import BaseModel, field_validator -from src.scheduling.models import PlanningPeriod -from src.scheduling.timeoffice.source_models import TimeOfficePlanEmployeeSource, TimeOfficePlanSource +from src.scheduling.models.core import PlanningPeriod class FetchStationsRequest(BaseModel): - """Request to provide scheduling data for one or more TimeOffice stations. - - If use_cache is true, cached StationMonthData is attempted first. If cache - data is missing or invalid, the service falls back to database reads. - """ + """Request to provide scheduling data for one or more TimeOffice stations.""" station_ids: tuple[int, ...] period: PlanningPeriod - use_cache: bool = False @field_validator("station_ids") @classmethod def station_ids_must_not_be_empty(cls, station_ids: tuple[int, ...]) -> tuple[int, ...]: if not station_ids: raise ValueError("At least one station id is required.") + return station_ids @field_validator("station_ids") @classmethod def station_ids_must_be_positive(cls, station_ids: tuple[int, ...]) -> tuple[int, ...]: invalid_station_ids = [station_id for station_id in station_ids if station_id <= 0] + if invalid_station_ids: raise ValueError(f"Station ids must be positive: {invalid_station_ids}") - return station_ids - - -class TimeOfficeSourceData(BaseModel): - """Raw TimeOffice source data read from the database. - - This model stays TimeOffice-facing. It must not become the canonical - scheduling model. - """ - station_ids: tuple[int, ...] - period: PlanningPeriod - plans: tuple[TimeOfficePlanSource, ...] = () - plan_employees: tuple[TimeOfficePlanEmployeeSource, ...] = () - - -class CacheWriteResult(BaseModel): - """Result of writing one station-month cache payload.""" - - station_id: int - period: PlanningPeriod - cache_directory: Path + return station_ids diff --git a/src/scheduling/timeoffice/repositories/__init__.py b/src/scheduling/timeoffice/repositories/__init__.py new file mode 100644 index 00000000..5d4bbe61 --- /dev/null +++ b/src/scheduling/timeoffice/repositories/__init__.py @@ -0,0 +1,13 @@ +from src.scheduling.timeoffice.repositories.employees import EmployeeRepositoryResult, TimeOfficeEmployeeRepository +from src.scheduling.timeoffice.repositories.plans import PlanRepositoryResult, TimeOfficePlan, TimeOfficePlanRepository +from src.scheduling.timeoffice.repositories.shifts import ShiftRepositoryResult, TimeOfficeShiftRepository + +__all__ = [ + "EmployeeRepositoryResult", + "PlanRepositoryResult", + "ShiftRepositoryResult", + "TimeOfficeEmployeeRepository", + "TimeOfficePlan", + "TimeOfficePlanRepository", + "TimeOfficeShiftRepository", +] diff --git a/src/scheduling/timeoffice/repositories/employees.py b/src/scheduling/timeoffice/repositories/employees.py new file mode 100644 index 00000000..c8b58467 --- /dev/null +++ b/src/scheduling/timeoffice/repositories/employees.py @@ -0,0 +1,191 @@ +from datetime import date as Date + +from pydantic import BaseModel, Field +from sqlalchemy import bindparam, text +from sqlalchemy.engine import Connection + +from src.scheduling.models.employee import Employee +from src.scheduling.models.relations import Membership, MembershipType +from src.scheduling.timeoffice.repositories.helpers import to_date +from src.scheduling.timeoffice.repositories.plans import TimeOfficePlan + + +class TimeOfficePlanEmployee(BaseModel): + """Employee assigned to a concrete TimeOffice monthly plan.""" + + source_plan_employee_id: int = Field(gt=0) + source_plan_id: int = Field(gt=0) + + station_id: int = Field(gt=0) + employee_id: int = Field(gt=0) + + personnel_number: str | None = None + first_name: str | None = None + last_name: str | None = None + short_name: str | None = None + + source_profession_id: int | None = None + + valid_from: Date | None = None + valid_until: Date | None = None + + is_substitute: bool | None = None + + +class EmployeeRepositoryResult(BaseModel): + """Canonical output of reading TimeOffice plan employees.""" + + employees: tuple[Employee, ...] + memberships: tuple[Membership, ...] + + +class TimeOfficeEmployeeRepository: + """Read TimeOffice plan employees and map them to employees/memberships.""" + + def fetch( + self, + connection: Connection, + plans: tuple[TimeOfficePlan, ...], + ) -> EmployeeRepositoryResult: + """Read employees assigned to selected monthly TimeOffice plans.""" + source_plan_ids = tuple(plan.source_plan_id for plan in plans) + + if not source_plan_ids: + raise ValueError("Cannot read TimeOffice employees without source plan ids.") + + query = text( + """ + SELECT + pp.Prim AS source_plan_employee_id, + pp.RefPlan AS source_plan_id, + tp.RefPlanungseinheiten AS station_id, + pp.RefPersonal AS employee_id, + + p.PersNr AS personnel_number, + p.Vorname AS first_name, + p.Name AS last_name, + p.KurzName AS short_name, + + pp.RefBerufe AS source_profession_id, + pp.VonDat AS valid_from, + pp.BisDat AS valid_until, + pp.IstVonErsatz AS is_substitute + FROM TPlanPersonal pp + JOIN TPlan tp + ON tp.Prim = pp.RefPlan + JOIN TPersonal p + ON p.Prim = pp.RefPersonal + WHERE pp.RefPlan IN :source_plan_ids + """ + ).bindparams(bindparam("source_plan_ids", expanding=True)) + + rows = ( + connection.execute( + query, + { + "source_plan_ids": source_plan_ids, + }, + ) + .mappings() + .all() + ) + + source_employees = tuple( + TimeOfficePlanEmployee( + source_plan_employee_id=row["source_plan_employee_id"], + source_plan_id=row["source_plan_id"], + station_id=row["station_id"], + employee_id=row["employee_id"], + personnel_number=row["personnel_number"], + first_name=row["first_name"], + last_name=row["last_name"], + short_name=row["short_name"], + source_profession_id=row["source_profession_id"], + valid_from=to_date(row["valid_from"]), + valid_until=to_date(row["valid_until"]), + is_substitute=None if row["is_substitute"] is None else bool(row["is_substitute"]), + ) + for row in rows + ) + + self._ensure_rows_reference_known_plans(source_plan_ids, source_employees) + + employees_by_id: dict[int, Employee] = {} + memberships_by_key: dict[tuple[int, int], Membership] = {} + + for source_employee in source_employees: + employees_by_id[source_employee.employee_id] = self._map_employee(source_employee) + + membership = self._map_membership(source_employee) + memberships_by_key[(membership.employee_id, membership.station_id)] = membership + + print(f"[timeoffice] database.repository.employees rows={len(source_employees)}") + + return EmployeeRepositoryResult( + employees=tuple(employees_by_id.values()), + memberships=tuple(memberships_by_key.values()), + ) + + def _map_employee(self, source_employee: TimeOfficePlanEmployee) -> Employee: + """Map a TimeOffice plan employee row to a canonical Employee.""" + return Employee( + employee_id=source_employee.employee_id, + personnel_number=source_employee.personnel_number, + first_name=source_employee.first_name, + last_name=source_employee.last_name, + display_name=self._display_name(source_employee), + group_id=None, + active=True, + ) + + def _map_membership(self, source_employee: TimeOfficePlanEmployee) -> Membership: + """Map a TimeOffice plan employee row to a local station membership.""" + return Membership( + employee_id=source_employee.employee_id, + station_id=source_employee.station_id, + membership_type=MembershipType.LOCAL, + valid_from=source_employee.valid_from, + valid_until=source_employee.valid_until, + is_substitute=source_employee.is_substitute, + ) + + def _display_name(self, source_employee: TimeOfficePlanEmployee) -> str: + """Build a readable employee display name.""" + display_name = " ".join( + part + for part in ( + source_employee.first_name, + source_employee.last_name, + ) + if part + ) + + if display_name: + return display_name + + if source_employee.short_name: + return source_employee.short_name + + if source_employee.personnel_number: + return source_employee.personnel_number + + return f"Employee {source_employee.employee_id}" + + def _ensure_rows_reference_known_plans( + self, + known_source_plan_ids: tuple[int, ...], + source_employees: tuple[TimeOfficePlanEmployee, ...], + ) -> None: + """Ensure employee rows only reference selected plans.""" + known_source_plan_id_set = set(known_source_plan_ids) + + unknown_source_plan_ids = sorted( + { + source_employee.source_plan_id + for source_employee in source_employees + if source_employee.source_plan_id not in known_source_plan_id_set + } + ) + + if unknown_source_plan_ids: + raise ValueError(f"Employee rows reference unknown TimeOffice plan ids: {unknown_source_plan_ids}") diff --git a/src/scheduling/timeoffice/repositories/helpers.py b/src/scheduling/timeoffice/repositories/helpers.py new file mode 100644 index 00000000..77ab3049 --- /dev/null +++ b/src/scheduling/timeoffice/repositories/helpers.py @@ -0,0 +1,78 @@ +from datetime import date as Date +from datetime import datetime as DateTime +from datetime import time as Time +from typing import Any + + +def clean_text(value: Any) -> str | None: + """Normalize empty source text values to None.""" + if value is None: + return None + + cleaned = str(value).strip() + + if not cleaned: + return None + + return cleaned + + +def required_text(value: Any, *, field_name: str, context: str) -> str: + """Return required non-empty text or raise a useful source error.""" + cleaned = clean_text(value) + + if cleaned is None: + raise ValueError(f"Missing required TimeOffice field {field_name} for {context}.") + + return cleaned + + +def normalize_code(value: str) -> str: + """Normalize TimeOffice code values for stable comparison.""" + return value.strip().upper() + + +def to_date(value: Any) -> Date | None: + """Convert SQL date/datetime values to date values.""" + if value is None: + return None + + if isinstance(value, Date) and not isinstance(value, DateTime): + return value + + if isinstance(value, DateTime): + return value.date() + + if hasattr(value, "date"): + return value.date() + + raise TypeError(f"Cannot convert TimeOffice value to date: {value!r}") + + +def to_datetime(value: Any) -> DateTime: + """Convert SQL datetime-like values to Python datetime.""" + if isinstance(value, DateTime): + return value + + if hasattr(value, "to_pydatetime"): + return value.to_pydatetime() + + raise TypeError(f"Cannot convert TimeOffice value to datetime: {value!r}") + + +def minute_of_day(value: DateTime | Time) -> int: + """Return minutes after midnight.""" + return value.hour * 60 + value.minute + + +def to_non_negative_int(value: Any) -> int: + """Convert nullable numeric source values to non-negative int.""" + if value is None: + return 0 + + result = int(value) + + if result < 0: + return 0 + + return result diff --git a/src/scheduling/timeoffice/repositories/plans.py b/src/scheduling/timeoffice/repositories/plans.py new file mode 100644 index 00000000..9c8272d8 --- /dev/null +++ b/src/scheduling/timeoffice/repositories/plans.py @@ -0,0 +1,144 @@ +from pydantic import BaseModel, Field +from sqlalchemy import bindparam, text +from sqlalchemy.engine import Connection + +from src.scheduling.models.core import PlanningPeriod +from src.scheduling.models.station import Station +from src.scheduling.timeoffice.config import TimeOfficeConfig +from src.scheduling.timeoffice.models import FetchStationsRequest + + +class TimeOfficePlan(BaseModel): + """TimeOffice monthly plan metadata for one station/planning unit.""" + + station_id: int = Field(gt=0) + source_plan_id: int = Field(gt=0) + source_planning_unit_id: int = Field(gt=0) + + station_name: str | None = None + + status_id: int | None = None + planning_interval_id: int | None = None + + period: PlanningPeriod + + +class PlanRepositoryResult(BaseModel): + """Canonical output of reading TimeOffice plans.""" + + plans: tuple[TimeOfficePlan, ...] + stations: tuple[Station, ...] + + +class TimeOfficePlanRepository: + """Read monthly TimeOffice plans and station metadata.""" + + def __init__(self, config: TimeOfficeConfig): + self._config = config + + def fetch( + self, + connection: Connection, + request: FetchStationsRequest, + ) -> PlanRepositoryResult: + """Read monthly target plans for all requested stations.""" + query = text( + """ + SELECT + p.Prim AS source_plan_id, + p.RefPlanungseinheiten AS source_planning_unit_id, + p.RefPlanungseinheiten AS station_id, + COALESCE(pe.Bezeichnung, pe.KurzBez) AS station_name, + p.RefStati AS status_id, + p.RefPlanungsIntervalle AS planning_interval_id + FROM TPlan p + LEFT JOIN TPlanungseinheiten pe + ON pe.Prim = p.RefPlanungseinheiten + WHERE p.RefPlanungseinheiten IN :station_ids + AND p.VonDat = :period_start + AND p.BisDat = :period_end + AND p.RefPlanungsIntervalle = :planning_interval_id + AND p.RefStati = :status_id + """ + ).bindparams(bindparam("station_ids", expanding=True)) + + rows = ( + connection.execute( + query, + { + "station_ids": request.station_ids, + "period_start": request.period.start, + "period_end": request.period.end, + "planning_interval_id": self._config.plan_selection.planning_interval_id, + "status_id": self._config.plan_selection.plan_status_id, + }, + ) + .mappings() + .all() + ) + + plans = tuple( + TimeOfficePlan( + station_id=row["station_id"], + source_plan_id=row["source_plan_id"], + source_planning_unit_id=row["source_planning_unit_id"], + station_name=row["station_name"], + status_id=row["status_id"], + planning_interval_id=row["planning_interval_id"], + period=request.period, + ) + for row in rows + ) + + self._ensure_one_plan_per_requested_station(request, plans) + + stations = tuple( + Station( + station_id=plan.station_id, + name=plan.station_name, + source_planning_unit_id=plan.source_planning_unit_id, + ) + for plan in plans + ) + + print(f"[timeoffice] database.repository.plans rows={len(plans)}") + + return PlanRepositoryResult( + plans=plans, + stations=stations, + ) + + def _ensure_one_plan_per_requested_station( + self, + request: FetchStationsRequest, + plans: tuple[TimeOfficePlan, ...], + ) -> None: + """Ensure the query returned exactly one plan per requested station.""" + plans_by_station: dict[int, list[TimeOfficePlan]] = {station_id: [] for station_id in request.station_ids} + + for plan in plans: + plans_by_station.setdefault(plan.station_id, []).append(plan) + + missing_station_ids = [ + station_id for station_id, station_plans in plans_by_station.items() if not station_plans + ] + + if missing_station_ids: + raise ValueError( + "No monthly target TimeOffice plan found for station(s) " + f"{missing_station_ids} and period " + f"{request.period.start.isoformat()}..{request.period.end.isoformat()}." + ) + + ambiguous_station_ids = [ + station_id for station_id, station_plans in plans_by_station.items() if len(station_plans) > 1 + ] + + if ambiguous_station_ids: + details = { + station_id: [plan.source_plan_id for plan in station_plans] + for station_id, station_plans in plans_by_station.items() + if len(station_plans) > 1 + } + + raise ValueError(f"Multiple monthly target TimeOffice plans found for station(s): {details}") diff --git a/src/scheduling/timeoffice/repositories/shifts.py b/src/scheduling/timeoffice/repositories/shifts.py new file mode 100644 index 00000000..096be7b4 --- /dev/null +++ b/src/scheduling/timeoffice/repositories/shifts.py @@ -0,0 +1,333 @@ +from collections import defaultdict +from collections.abc import Sequence +from datetime import datetime as DateTime +from itertools import pairwise +from typing import Any + +from pydantic import BaseModel, Field +from sqlalchemy import bindparam, text +from sqlalchemy.engine import Connection + +from src.scheduling.models.shift import Shift, ShiftKind +from src.scheduling.timeoffice.config import TimeOfficeConfig, TimeOfficeShiftConfig +from src.scheduling.timeoffice.repositories.helpers import ( + clean_text, + minute_of_day, + normalize_code, + required_text, + to_datetime, + to_non_negative_int, +) + + +class TimeOfficeShiftSegment(BaseModel): + """One TimeOffice timing segment for a shift.""" + + start: DateTime + end: DateTime + minutes: int = Field(ge=0) + + +class TimeOfficeShiftSource(BaseModel): + """Source data for one configured TimeOffice shift.""" + + source_shift_id: int = Field(gt=0) + source_code: str + name: str + + source_shift_type_id: int + source_statistics_group_id: int | None = None + source_facility_id: int | None = None + + ppug_relevant: bool = False + ppprl_relevant: bool = False + ppug_pause_counts: bool = False + + segments: tuple[TimeOfficeShiftSegment, ...] + + start_minute: int = Field(ge=0, lt=24 * 60) + end_minute: int = Field(ge=0, lt=24 * 60) + ends_next_day: bool = False + break_minutes: int = Field(default=0, ge=0) + net_work_minutes: int = Field(ge=0) + + +class ShiftRepositoryResult(BaseModel): + """Canonical output of reading TimeOffice shifts.""" + + shifts: tuple[Shift, ...] + + +class TimeOfficeShiftRepository: + """Read configured TimeOffice shift definitions.""" + + def __init__(self, config: TimeOfficeConfig): + self._config = config + + def fetch(self, connection: Connection) -> ShiftRepositoryResult: + """Read configured solver-relevant shifts from TimeOffice.""" + source_shift_ids = self._config.solver_shift_ids + + if not source_shift_ids: + raise ValueError("At least one TimeOffice solver shift id is required.") + + query = text( + """ + SELECT + d.Prim AS source_shift_id, + d.KurzBez AS source_code, + d.Bezeichnung AS name, + + d.RefDienstTypen AS source_shift_type_id, + d.RefDiensteStatistikGruppen AS source_statistics_group_id, + d.RefEinrichtungen AS source_facility_id, + + d.PpugRelevant AS ppug_relevant, + d.PpprlRelevant AS ppprl_relevant, + d.PpugPauseAnrechnen AS ppug_pause_counts, + + sz.Kommt AS segment_start, + sz.Geht AS segment_end, + sz.Minuten AS segment_minutes + FROM TDienste d + LEFT JOIN TDiensteSollzeiten sz + ON sz.RefDienste = d.Prim + WHERE d.Prim IN :source_shift_ids + ORDER BY + d.Prim, + sz.Kommt, + sz.Geht + """ + ).bindparams(bindparam("source_shift_ids", expanding=True)) + + rows = ( + connection.execute( + query, + { + "source_shift_ids": source_shift_ids, + }, + ) + .mappings() + .all() + ) + + sources = self._map_rows(rows) + + self._ensure_all_configured_shifts_found(sources) + self._warn_about_unexpected_codes(sources) + self._warn_about_unexpected_shift_types(sources) + + shifts = tuple(self._map_shift(source) for source in sources) + + print(f"[timeoffice] database.repository.shifts rows={len(shifts)}") + + return ShiftRepositoryResult(shifts=shifts) + + def _map_rows(self, rows: Sequence[Any]) -> tuple[TimeOfficeShiftSource, ...]: + """Map flat SQL rows into one source object per TimeOffice shift.""" + rows_by_shift_id: dict[int, list[Any]] = defaultdict(list) + + for row in rows: + rows_by_shift_id[row["source_shift_id"]].append(row) + + return tuple(self._map_shift_source(shift_rows) for _, shift_rows in sorted(rows_by_shift_id.items())) + + def _map_shift_source(self, rows: list[Any]) -> TimeOfficeShiftSource: + """Map all rows for one TimeOffice shift.""" + first_row = rows[0] + + segments = tuple(segment for segment in (self._map_segment(row) for row in rows) if segment is not None) + + if not segments: + raise ValueError( + "Configured TimeOffice shift has no timing segments: " + f"{first_row['source_shift_id']} / {first_row['source_code']}" + ) + + ordered_segments = tuple(sorted(segments, key=lambda segment: segment.start)) + + first_segment = ordered_segments[0] + last_segment = ordered_segments[-1] + + start_minute = minute_of_day(first_segment.start) + end_minute = minute_of_day(last_segment.end) + ends_next_day = last_segment.end.date() > first_segment.start.date() + + net_work_minutes = sum(segment.minutes for segment in ordered_segments) + break_minutes = self._break_minutes(ordered_segments) + + if net_work_minutes <= 0: + net_work_minutes = self._fallback_net_work_minutes( + start=first_segment.start, + end=last_segment.end, + break_minutes=break_minutes, + ) + + return TimeOfficeShiftSource( + source_shift_id=first_row["source_shift_id"], + source_code=required_text( + first_row["source_code"], + field_name="source_code", + context=f"shift {first_row['source_shift_id']}", + ), + name=self._name( + name=first_row["name"], + code=first_row["source_code"], + source_shift_id=first_row["source_shift_id"], + ), + source_shift_type_id=first_row["source_shift_type_id"], + source_statistics_group_id=first_row["source_statistics_group_id"], + source_facility_id=first_row["source_facility_id"], + ppug_relevant=bool(first_row["ppug_relevant"]), + ppprl_relevant=bool(first_row["ppprl_relevant"]), + ppug_pause_counts=bool(first_row["ppug_pause_counts"]), + segments=ordered_segments, + start_minute=start_minute, + end_minute=end_minute, + ends_next_day=ends_next_day, + break_minutes=break_minutes, + net_work_minutes=net_work_minutes, + ) + + def _map_segment(self, row: Any) -> TimeOfficeShiftSegment | None: + """Map one TDiensteSollzeiten row to a segment.""" + if row["segment_start"] is None or row["segment_end"] is None: + return None + + start = to_datetime(row["segment_start"]) + end = to_datetime(row["segment_end"]) + minutes = to_non_negative_int(row["segment_minutes"]) + + if minutes <= 0: + minutes = self._duration_minutes(start, end) + + return TimeOfficeShiftSegment( + start=start, + end=end, + minutes=minutes, + ) + + def _map_shift(self, source: TimeOfficeShiftSource) -> Shift: + """Map a TimeOffice shift source to a canonical solver-facing Shift.""" + configured_shift = self._configured_shift(source.source_shift_id) + + return Shift( + shift_id=f"timeoffice:{source.source_shift_id}", + shift_group_id=configured_shift.group_id, + name=source.name, + source_shift_id=source.source_shift_id, + source_code=normalize_code(source.source_code), + kind=configured_shift.kind, + start_minute=source.start_minute, + end_minute=source.end_minute, + ends_next_day=source.ends_next_day, + break_minutes=source.break_minutes, + net_work_minutes=source.net_work_minutes, + assignable=self._is_assignable(source, configured_shift), + counts_as_work=configured_shift.counts_as_work and source.net_work_minutes > 0, + counts_for_minimum_staffing=configured_shift.counts_for_minimum_staffing, + is_night=configured_shift.kind == ShiftKind.NIGHT, + ) + + def _configured_shift(self, source_shift_id: int) -> TimeOfficeShiftConfig: + """Return configured solver semantics for a TimeOffice shift id.""" + try: + return self._config.shifts_by_id[source_shift_id] + except KeyError as error: + raise KeyError(f"No TimeOffice shift configuration found for source id {source_shift_id}.") from error + + def _is_assignable( + self, + source: TimeOfficeShiftSource, + configured_shift: TimeOfficeShiftConfig, + ) -> bool: + """Return whether the solver may create decision variables for this shift.""" + if not configured_shift.assignable: + return False + + if source.net_work_minutes <= 0: + return False + + return source.source_shift_type_id in self._config.assignable_shift_type_ids + + def _break_minutes(self, segments: tuple[TimeOfficeShiftSegment, ...]) -> int: + """Compute break minutes as gaps between ordered work segments.""" + break_minutes = 0 + + for previous, current in pairwise(segments): + gap = self._duration_minutes(previous.end, current.start) + + if gap > 0: + break_minutes += gap + + return break_minutes + + def _fallback_net_work_minutes( + self, + start: DateTime, + end: DateTime, + break_minutes: int, + ) -> int: + """Fallback net minutes if TDiensteSollzeiten.Minuten is unavailable.""" + return max(0, self._duration_minutes(start, end) - break_minutes) + + def _duration_minutes(self, start: DateTime, end: DateTime) -> int: + """Return duration in minutes.""" + duration = int((end - start).total_seconds() // 60) + + if duration < 0: + raise ValueError(f"Negative TimeOffice segment duration: {start!r} -> {end!r}") + + return duration + + def _ensure_all_configured_shifts_found(self, sources: tuple[TimeOfficeShiftSource, ...]) -> None: + """Ensure configured shift ids exist in TimeOffice.""" + found_ids = {source.source_shift_id for source in sources} + missing_ids = sorted(set(self._config.solver_shift_ids) - found_ids) + + if missing_ids: + raise ValueError(f"Configured TimeOffice shift ids were not found: {missing_ids}") + + def _warn_about_unexpected_codes(self, sources: tuple[TimeOfficeShiftSource, ...]) -> None: + """Print a warning if TimeOffice code differs from configured expectation.""" + mismatches: list[str] = [] + + for source in sources: + configured_shift = self._configured_shift(source.source_shift_id) + actual_code = normalize_code(source.source_code) + expected_code = normalize_code(configured_shift.expected_code) + + if actual_code != expected_code: + mismatches.append(f"{source.source_shift_id}: expected={expected_code} actual={actual_code}") + + if not mismatches: + return + + print("[timeoffice] database.repository.shifts warning unexpected_codes=" + ", ".join(mismatches)) + + def _warn_about_unexpected_shift_types(self, sources: tuple[TimeOfficeShiftSource, ...]) -> None: + """Print a warning if a configured shift has an unexpected TimeOffice shift type.""" + unexpected: list[str] = [] + + for source in sources: + if source.source_shift_type_id not in self._config.assignable_shift_type_ids: + unexpected.append(f"{source.source_shift_id}: type={source.source_shift_type_id}") + + if not unexpected: + return + + print("[timeoffice] database.repository.shifts warning unexpected_shift_types=" + ", ".join(unexpected)) + + def _name(self, name: Any, code: Any, source_shift_id: int) -> str: + """Return a readable shift name.""" + cleaned_name = clean_text(name) + + if cleaned_name is not None: + return cleaned_name + + cleaned_code = clean_text(code) + + if cleaned_code is not None: + return cleaned_code + + return f"TimeOffice shift {source_shift_id}" diff --git a/src/scheduling/timeoffice/service.py b/src/scheduling/timeoffice/service.py index 6e911d52..a66ae486 100644 --- a/src/scheduling/timeoffice/service.py +++ b/src/scheduling/timeoffice/service.py @@ -1,7 +1,6 @@ -from src.scheduling.models import SchedulingDataset, StationMonthData -from src.scheduling.timeoffice.cache import TimeOfficeCache +from src.scheduling.models.dataset import SchedulingDataset +from src.scheduling.timeoffice.config import TIMEOFFICE_CONFIG, TimeOfficeConfig from src.scheduling.timeoffice.database import TimeOfficeDatabase -from src.scheduling.timeoffice.mapping import TimeOfficeMapper from src.scheduling.timeoffice.models import FetchStationsRequest from src.scheduling.timeoffice.settings import TimeOfficeSettings, load_settings @@ -13,64 +12,29 @@ def __init__( self, settings: TimeOfficeSettings, database: TimeOfficeDatabase, - mapper: TimeOfficeMapper, - cache: TimeOfficeCache, ): self._settings = settings self._database = database - self._mapper = mapper - self._cache = cache def fetch(self, request: FetchStationsRequest) -> SchedulingDataset: - """Provide scheduling data for the requested TimeOffice stations. - - If request.use_cache is true, cached StationMonthData is attempted first. - On cache miss or invalid cache data, the service falls back to database. - - If settings.enable_cache is true, database-derived StationMonthData is - written to cache for debugging and validation. - """ + """Provide scheduling data for the requested TimeOffice stations.""" print( "[timeoffice] service.fetch " f"stations={list(request.station_ids)} " - f"period={request.period.start.isoformat()}..{request.period.end.isoformat()} " - f"use_cache={request.use_cache} " - f"enable_cache={self._settings.enable_cache}" + f"period={request.period.start.isoformat()}..{request.period.end.isoformat()}" ) - station_month_data = self._get_station_month_data(request) - - return self._mapper.combine_station_month_data(station_month_data=station_month_data) - - def _get_station_month_data(self, request: FetchStationsRequest) -> tuple[StationMonthData, ...]: - """Load station-month data from cache or database according to policy.""" - if request.use_cache: - try: - return self._cache.read_many( - station_ids=request.station_ids, - period=request.period, - ) - except Exception as error: - print( - f"[timeoffice] cache unavailable; falling back to database reason={type(error).__name__}: {error}" - ) - - source_data = self._database.read(request) - station_month_data = self._mapper.to_station_month_data(source_data) - - if self._settings.enable_cache: - self._cache.write_many(station_month_data) - - return station_month_data + return self._database.read(request) -def create_timeoffice_service(settings: TimeOfficeSettings | None = None) -> TimeOfficeService: +def create_timeoffice_service( + settings: TimeOfficeSettings | None = None, + config: TimeOfficeConfig = TIMEOFFICE_CONFIG, +) -> TimeOfficeService: """Create the default TimeOffice service.""" settings = settings or load_settings() return TimeOfficeService( settings=settings, - database=TimeOfficeDatabase(settings), - mapper=TimeOfficeMapper(settings), - cache=TimeOfficeCache(settings), + database=TimeOfficeDatabase(settings, config), ) diff --git a/src/scheduling/timeoffice/settings.py b/src/scheduling/timeoffice/settings.py index f6235e90..5e35e4d4 100644 --- a/src/scheduling/timeoffice/settings.py +++ b/src/scheduling/timeoffice/settings.py @@ -1,6 +1,4 @@ -from pathlib import Path - -from pydantic import Field, SecretStr +from pydantic import SecretStr from pydantic_settings import BaseSettings, SettingsConfigDict @@ -18,11 +16,6 @@ class TimeOfficeSettings(BaseSettings): db_user: str db_password: SecretStr - enable_cache: bool = True - cache_root: Path = Path(".cache/timeoffice") - - jump_pool_station_ids: tuple[int, ...] = Field(default_factory=tuple) - def load_settings() -> TimeOfficeSettings: """Load TimeOffice settings from environment variables and .env files.""" diff --git a/src/scheduling/timeoffice/source_models.py b/src/scheduling/timeoffice/source_models.py deleted file mode 100644 index 4f0b26a7..00000000 --- a/src/scheduling/timeoffice/source_models.py +++ /dev/null @@ -1,42 +0,0 @@ -from datetime import date as Date - -from pydantic import BaseModel, Field - -from src.scheduling.models.core import PlanningPeriod - - -class TimeOfficePlanSource(BaseModel): - """TimeOffice plan metadata for one station/planning unit and period.""" - - station_id: int = Field(gt=0) - source_plan_id: int = Field(gt=0) - source_planning_unit_id: int = Field(gt=0) - - station_name: str | None = None - - status_id: int | None = None - planning_interval_id: int | None = None - - period: PlanningPeriod - - -class TimeOfficePlanEmployeeSource(BaseModel): - """Employee assigned to a concrete TimeOffice monthly plan.""" - - source_plan_employee_id: int = Field(gt=0) - source_plan_id: int = Field(gt=0) - - station_id: int = Field(gt=0) - employee_id: int = Field(gt=0) - - personnel_number: str | None = None - first_name: str | None = None - last_name: str | None = None - short_name: str | None = None - - source_profession_id: int | None = None - - valid_from: Date | None = None - valid_until: Date | None = None - - is_substitute: bool | None = None From b84ba7067b7234d78d5d217e40dc2056911e6c92 Mon Sep 17 00:00:00 2001 From: Tom Wawerek Date: Wed, 17 Jun 2026 13:57:28 +0200 Subject: [PATCH 06/18] refactor: domain model and data fetching pipeline --- Dockerfile | 6 +- Justfile | 24 +- .../cases/68/11_2024}/employee_types.json | 0 .../cases/68/11_2024/employees.json | 0 .../free_shifts_and_vacation_days.json | 3 + .../cases/68/11_2024}/general_settings.json | 0 .../68/11_2024}/minimal_number_of_staff.json | 0 .../cases/68/11_2024/shift_information.json | 47 + .../68/11_2024/target_working_minutes.json | 270 + legacy/cases/68/11_2024/web/jobs.json | 19 + .../cases/68/11_2024/wishes_and_blocked.json | 3 + legacy/cases/68/11_2024/worked_sundays.json | 130 + legacy/cases/77/02_2025/employee_types.json | 525 + legacy/cases/77/02_2025/employees.json | 244 + .../free_shifts_and_vacation_days.json | 1484 ++ legacy/cases/77/02_2025/general_settings.json | 18 + .../77/02_2025/minimal_number_of_staff.json | 113 + .../cases/77/02_2025/shift_information.json | 47 + .../77/02_2025/target_working_minutes.json | 158 + legacy/cases/77/02_2025/web/jobs.json | 116 + .../cases/77/02_2025/wishes_and_blocked.json | 3 + legacy/cases/77/02_2025/worked_sundays.json | 28 + legacy/cases/77/09_2024/employee_types.json | 525 + legacy/cases/77/09_2024/general_settings.json | 18 + .../77/09_2024/minimal_number_of_staff.json | 113 + .../cases/77/09_2024/wishes_and_blocked.json | 3 + legacy/cases/77/11_2024/employee_types.json | 525 + legacy/cases/77/11_2024/employees.json | 208 + .../free_shifts_and_vacation_days.json | 1369 ++ legacy/cases/77/11_2024/general_settings.json | 18 + .../77/11_2024/minimal_number_of_staff.json | 113 + .../cases/77/11_2024/shift_information.json | 47 + .../77/11_2024/target_working_minutes.json | 270 + legacy/cases/77/11_2024/web/jobs.json | 47 + .../cases/77/11_2024/wishes_and_blocked.json | 3 + legacy/cases/77/11_2024/worked_sundays.json | 130 + legacy/cases/77/12_2024/employee_types.json | 525 + legacy/cases/77/12_2024/employees.json | 250 + .../free_shifts_and_vacation_days.json | 1695 ++ legacy/cases/77/12_2024/general_settings.json | 18 + .../77/12_2024/minimal_number_of_staff.json | 113 + .../cases/77/12_2024/shift_information.json | 47 + .../77/12_2024/target_working_minutes.json | 291 + legacy/cases/77/12_2024/web/jobs.json | 221 + .../cases/77/12_2024/wishes_and_blocked.json | 3 + legacy/cases/77/12_2024/worked_sundays.json | 130 + {cases => legacy/cases}/case_catalog.md | 0 legacy/cases_static_jsons/employee_types.json | 525 + .../cases_static_jsons/general_settings.json | 18 + .../minimal_number_of_staff.json | 113 + .../wishes_and_blocked.json | 3 + .../found_solutions_are_stored_here.md | 0 ...ng_of_test_all_costraints_single_case.json | 12695 ++++++++++++ ...ion_77_2024-11-01-2024-11-30_wdefault.json | 4 + ...ion_77_2024-12-01-2024-12-31_wdefault.json | 4 + ...ion_77_2025-02-01-2025-02-28_wdefault.json | 4 + ..._all_costraints_single_case_processed.json | 16416 ++++++++++++++++ ...4-11-01-2024-11-30_wdefault_processed.json | 14438 ++++++++++++++ {src => legacy/src}/api/__init__.py | 0 {src => legacy/src}/api/main.py | 6 +- {src => legacy/src}/cp/__init__.py | 0 {src => legacy/src}/cp/constants.py | 0 .../src}/cp/constraints/__init__.py | 0 .../src}/cp/constraints/constraint.py | 6 +- .../free_day_after_night_shift_phase.py | 6 +- .../hierarchy_of_intermediate_shifts.py | 6 +- .../cp/constraints/max_one_shift_per_day.py | 6 +- .../src}/cp/constraints/min_rest_time.py | 6 +- .../src}/cp/constraints/min_staffing.py | 6 +- .../src}/cp/constraints/planned_shifts.py | 6 +- .../cp/constraints/rounds_in_early_shift.py | 6 +- .../cp/constraints/target_working_time.py | 6 +- .../constraints/vacation_days_and_shifts.py | 6 +- {src => legacy/src}/cp/model.py | 8 +- {src => legacy/src}/cp/objectives/__init__.py | 0 .../objectives/every_second_weekend_free.py | 4 +- .../free_days_after_night_shift_phase.py | 6 +- .../cp/objectives/free_days_near_weekend.py | 4 +- .../src}/cp/objectives/maximize_wishes.py | 0 .../minimize_consecutive_night_shifts.py | 6 +- .../minimize_hidden_employee_count.py | 6 +- .../objectives/minimize_hidden_employees.py | 6 +- .../src}/cp/objectives/minimize_overtime.py | 6 +- .../not_too_many_consecutive_days.py | 4 +- .../src}/cp/objectives/objective.py | 6 +- .../cp/objectives/preferred_block_length.py | 4 +- .../cp/objectives/rotate_shifts_forward.py | 6 +- {src => legacy/src}/cp/variables/__init__.py | 0 {src => legacy/src}/cp/variables/variable.py | 6 +- {src => legacy/src}/day.py | 0 {src => legacy/src}/db/connection_setup.py | 0 {src => legacy/src}/db/export_data.py | 0 {src => legacy/src}/db/export_main.py | 2 +- {src => legacy/src}/db/import_main.py | 3 +- {src => legacy/src}/db/import_solution.py | 0 {src => legacy/src}/employee.py | 4 +- {src => legacy/src}/loader/__init__.py | 0 .../src}/loader/filesystem_loader.py | 6 +- {src => legacy/src}/loader/loader.py | 6 +- {src => legacy/src}/main.py | 44 +- {src => legacy/src}/run.bat | 0 {src => legacy/src}/services/__init__.py | 0 {src => legacy/src}/services/solve_service.py | 6 +- {src => legacy/src}/shift.py | 0 {src => legacy/src}/solution.py | 0 {src => legacy/src}/solve.py | 14 +- {src => legacy/src}/web/__init__.py | 0 {src => legacy/src}/web/analyze_solution.py | 4 +- {src => legacy/src}/web/app.py | 0 {src => legacy/src}/web/process_solution.py | 0 {src => legacy/src}/web/templates/index.html | 0 pyproject.toml | 25 +- src/SELECT.sql | 18 + src/scheduling/api/__init__.py | 0 src/scheduling/api/app.py | 22 + src/scheduling/api/data_router.py | 48 + src/scheduling/api/dependencies.py | 46 + src/scheduling/api/solver_router.py | 65 + src/scheduling/api/types.py | 34 + src/scheduling/logging.py | 21 + src/scheduling/models/__init__.py | 62 +- src/scheduling/models/assignment.py | 48 + src/scheduling/models/availability.py | 45 + src/scheduling/models/core.py | 29 +- src/scheduling/models/dataset.py | 383 +- src/scheduling/models/demand.py | 34 +- src/scheduling/models/employee.py | 49 +- src/scheduling/models/plan.py | 28 + src/scheduling/models/planning_unit.py | 68 + src/scheduling/models/relations.py | 134 - src/scheduling/models/shift.py | 65 +- src/scheduling/models/station.py | 9 - src/scheduling/models/sunday_work_history.py | 11 + src/scheduling/settings.py | 26 + src/scheduling/timeoffice/config.py | 229 - src/scheduling/timeoffice/database.py | 155 +- src/scheduling/timeoffice/facts.py | 393 + src/scheduling/timeoffice/models.py | 28 - .../timeoffice/repositories/__init__.py | 30 +- .../timeoffice/repositories/container.py | 30 + .../timeoffice/repositories/demand.py | 158 + .../timeoffice/repositories/employees.py | 191 - .../timeoffice/repositories/helpers.py | 20 +- .../timeoffice/repositories/personnel.py | 338 + .../timeoffice/repositories/planning_units.py | 140 + .../timeoffice/repositories/plans.py | 144 - .../timeoffice/repositories/roster.py | 389 + .../timeoffice/repositories/shifts.py | 439 +- .../repositories/sunday_work_history.py | 133 + src/scheduling/timeoffice/service.py | 64 +- src/scheduling/timeoffice/settings.py | 22 - tests/cp/conftest.py | 10 +- tests/cp/constraints/test_all_constraints.py | 14 +- .../test_free_day_after_night_shift_phase.py | 8 +- .../test_hierarchy_of_intermediate_shifts.py | 10 +- .../constraints/test_max_one_shift_per_day.py | 6 +- tests/cp/constraints/test_min_rest_time.py | 8 +- tests/cp/constraints/test_min_staffing.py | 8 +- tests/cp/constraints/test_planned_shifts.py | 8 +- .../test_rounds_in_early_shifts.py | 10 +- .../constraints/test_target_working_time.py | 6 +- .../test_vaction_days_and_shifts.py | 8 +- tests/integration/helpers/smoke_fixtures.py | 4 +- tests/integration/smoke_test.py | 2 +- uv.lock | 912 +- 165 files changed, 57953 insertions(+), 1601 deletions(-) rename {cases_static_jsons => legacy/cases/68/11_2024}/employee_types.json (100%) rename cases_static_jsons/wishes_and_blocked.json => legacy/cases/68/11_2024/employees.json (100%) create mode 100644 legacy/cases/68/11_2024/free_shifts_and_vacation_days.json rename {cases_static_jsons => legacy/cases/68/11_2024}/general_settings.json (100%) rename {cases_static_jsons => legacy/cases/68/11_2024}/minimal_number_of_staff.json (100%) create mode 100644 legacy/cases/68/11_2024/shift_information.json create mode 100644 legacy/cases/68/11_2024/target_working_minutes.json create mode 100644 legacy/cases/68/11_2024/web/jobs.json create mode 100644 legacy/cases/68/11_2024/wishes_and_blocked.json create mode 100644 legacy/cases/68/11_2024/worked_sundays.json create mode 100644 legacy/cases/77/02_2025/employee_types.json create mode 100644 legacy/cases/77/02_2025/employees.json create mode 100644 legacy/cases/77/02_2025/free_shifts_and_vacation_days.json create mode 100644 legacy/cases/77/02_2025/general_settings.json create mode 100644 legacy/cases/77/02_2025/minimal_number_of_staff.json create mode 100644 legacy/cases/77/02_2025/shift_information.json create mode 100644 legacy/cases/77/02_2025/target_working_minutes.json create mode 100644 legacy/cases/77/02_2025/web/jobs.json create mode 100644 legacy/cases/77/02_2025/wishes_and_blocked.json create mode 100644 legacy/cases/77/02_2025/worked_sundays.json create mode 100644 legacy/cases/77/09_2024/employee_types.json create mode 100644 legacy/cases/77/09_2024/general_settings.json create mode 100644 legacy/cases/77/09_2024/minimal_number_of_staff.json create mode 100644 legacy/cases/77/09_2024/wishes_and_blocked.json create mode 100644 legacy/cases/77/11_2024/employee_types.json create mode 100644 legacy/cases/77/11_2024/employees.json create mode 100644 legacy/cases/77/11_2024/free_shifts_and_vacation_days.json create mode 100644 legacy/cases/77/11_2024/general_settings.json create mode 100644 legacy/cases/77/11_2024/minimal_number_of_staff.json create mode 100644 legacy/cases/77/11_2024/shift_information.json create mode 100644 legacy/cases/77/11_2024/target_working_minutes.json create mode 100644 legacy/cases/77/11_2024/web/jobs.json create mode 100644 legacy/cases/77/11_2024/wishes_and_blocked.json create mode 100644 legacy/cases/77/11_2024/worked_sundays.json create mode 100644 legacy/cases/77/12_2024/employee_types.json create mode 100644 legacy/cases/77/12_2024/employees.json create mode 100644 legacy/cases/77/12_2024/free_shifts_and_vacation_days.json create mode 100644 legacy/cases/77/12_2024/general_settings.json create mode 100644 legacy/cases/77/12_2024/minimal_number_of_staff.json create mode 100644 legacy/cases/77/12_2024/shift_information.json create mode 100644 legacy/cases/77/12_2024/target_working_minutes.json create mode 100644 legacy/cases/77/12_2024/web/jobs.json create mode 100644 legacy/cases/77/12_2024/wishes_and_blocked.json create mode 100644 legacy/cases/77/12_2024/worked_sundays.json rename {cases => legacy/cases}/case_catalog.md (100%) create mode 100644 legacy/cases_static_jsons/employee_types.json create mode 100644 legacy/cases_static_jsons/general_settings.json create mode 100644 legacy/cases_static_jsons/minimal_number_of_staff.json create mode 100644 legacy/cases_static_jsons/wishes_and_blocked.json rename {found_solutions => legacy/found_solutions}/found_solutions_are_stored_here.md (100%) create mode 100644 legacy/found_solutions/soluting_of_test_all_costraints_single_case.json create mode 100644 legacy/found_solutions/solution_77_2024-11-01-2024-11-30_wdefault.json create mode 100644 legacy/found_solutions/solution_77_2024-12-01-2024-12-31_wdefault.json create mode 100644 legacy/found_solutions/solution_77_2025-02-01-2025-02-28_wdefault.json create mode 100644 legacy/processed_solutions/soluting_of_test_all_costraints_single_case_processed.json create mode 100644 legacy/processed_solutions/solution_77_2024-11-01-2024-11-30_wdefault_processed.json rename {src => legacy/src}/api/__init__.py (100%) rename {src => legacy/src}/api/main.py (98%) rename {src => legacy/src}/cp/__init__.py (100%) rename {src => legacy/src}/cp/constants.py (100%) rename {src => legacy/src}/cp/constraints/__init__.py (100%) rename {src => legacy/src}/cp/constraints/constraint.py (92%) rename {src => legacy/src}/cp/constraints/free_day_after_night_shift_phase.py (96%) rename {src => legacy/src}/cp/constraints/hierarchy_of_intermediate_shifts.py (97%) rename {src => legacy/src}/cp/constraints/max_one_shift_per_day.py (89%) rename {src => legacy/src}/cp/constraints/min_rest_time.py (92%) rename {src => legacy/src}/cp/constraints/min_staffing.py (95%) rename {src => legacy/src}/cp/constraints/planned_shifts.py (97%) rename {src => legacy/src}/cp/constraints/rounds_in_early_shift.py (91%) rename {src => legacy/src}/cp/constraints/target_working_time.py (97%) rename {src => legacy/src}/cp/constraints/vacation_days_and_shifts.py (94%) rename {src => legacy/src}/cp/model.py (97%) rename {src => legacy/src}/cp/objectives/__init__.py (100%) rename {src => legacy/src}/cp/objectives/every_second_weekend_free.py (98%) rename {src => legacy/src}/cp/objectives/free_days_after_night_shift_phase.py (93%) rename {src => legacy/src}/cp/objectives/free_days_near_weekend.py (98%) rename {src => legacy/src}/cp/objectives/maximize_wishes.py (100%) rename {src => legacy/src}/cp/objectives/minimize_consecutive_night_shifts.py (95%) rename {src => legacy/src}/cp/objectives/minimize_hidden_employee_count.py (93%) rename {src => legacy/src}/cp/objectives/minimize_hidden_employees.py (94%) rename {src => legacy/src}/cp/objectives/minimize_overtime.py (95%) rename {src => legacy/src}/cp/objectives/not_too_many_consecutive_days.py (96%) rename {src => legacy/src}/cp/objectives/objective.py (91%) rename {src => legacy/src}/cp/objectives/preferred_block_length.py (98%) rename {src => legacy/src}/cp/objectives/rotate_shifts_forward.py (97%) rename {src => legacy/src}/cp/variables/__init__.py (100%) rename {src => legacy/src}/cp/variables/variable.py (97%) rename {src => legacy/src}/day.py (100%) rename {src => legacy/src}/db/connection_setup.py (100%) rename {src => legacy/src}/db/export_data.py (100%) rename {src => legacy/src}/db/export_main.py (97%) rename {src => legacy/src}/db/import_main.py (97%) rename {src => legacy/src}/db/import_solution.py (100%) rename {src => legacy/src}/employee.py (98%) rename {src => legacy/src}/loader/__init__.py (100%) rename {src => legacy/src}/loader/filesystem_loader.py (99%) rename {src => legacy/src}/loader/loader.py (94%) rename {src => legacy/src}/main.py (83%) rename {src => legacy/src}/run.bat (100%) rename {src => legacy/src}/services/__init__.py (100%) rename {src => legacy/src}/services/solve_service.py (97%) rename {src => legacy/src}/shift.py (100%) rename {src => legacy/src}/solution.py (100%) rename {src => legacy/src}/solve.py (97%) rename {src => legacy/src}/web/__init__.py (100%) rename {src => legacy/src}/web/analyze_solution.py (98%) rename {src => legacy/src}/web/app.py (100%) rename {src => legacy/src}/web/process_solution.py (100%) rename {src => legacy/src}/web/templates/index.html (100%) create mode 100644 src/SELECT.sql create mode 100644 src/scheduling/api/__init__.py create mode 100644 src/scheduling/api/app.py create mode 100644 src/scheduling/api/data_router.py create mode 100644 src/scheduling/api/dependencies.py create mode 100644 src/scheduling/api/solver_router.py create mode 100644 src/scheduling/api/types.py create mode 100644 src/scheduling/logging.py create mode 100644 src/scheduling/models/assignment.py create mode 100644 src/scheduling/models/availability.py create mode 100644 src/scheduling/models/plan.py create mode 100644 src/scheduling/models/planning_unit.py delete mode 100644 src/scheduling/models/relations.py delete mode 100644 src/scheduling/models/station.py create mode 100644 src/scheduling/models/sunday_work_history.py create mode 100644 src/scheduling/settings.py delete mode 100644 src/scheduling/timeoffice/config.py create mode 100644 src/scheduling/timeoffice/facts.py delete mode 100644 src/scheduling/timeoffice/models.py create mode 100644 src/scheduling/timeoffice/repositories/container.py create mode 100644 src/scheduling/timeoffice/repositories/demand.py delete mode 100644 src/scheduling/timeoffice/repositories/employees.py create mode 100644 src/scheduling/timeoffice/repositories/personnel.py create mode 100644 src/scheduling/timeoffice/repositories/planning_units.py delete mode 100644 src/scheduling/timeoffice/repositories/plans.py create mode 100644 src/scheduling/timeoffice/repositories/roster.py create mode 100644 src/scheduling/timeoffice/repositories/sunday_work_history.py delete mode 100644 src/scheduling/timeoffice/settings.py diff --git a/Dockerfile b/Dockerfile index 420c4a79..52c89d41 100644 --- a/Dockerfile +++ b/Dockerfile @@ -34,12 +34,10 @@ WORKDIR /app COPY pyproject.toml uv.lock README.md ./ COPY src ./src -RUN mkdir -p cases found_solutions - RUN --mount=type=cache,target=/root/.cache/uv \ - uv sync --locked + uv sync --locked --no-dev EXPOSE 8000 # Bind to 0.0.0.0 so the API is reachable from outside the container. -CMD ["uv", "run", "--no-sync", "uvicorn", "src.api.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"] +CMD ["fastapi", "run", "src/scheduling/api/app.py", "--host", "0.0.0.0", "--port", "8000"] diff --git a/Justfile b/Justfile index ccc28aa0..65845f2f 100644 --- a/Justfile +++ b/Justfile @@ -1,18 +1,14 @@ -APP_NAME := "staff-scheduling-api" IMAGE_NAME := "staff-scheduling-api" PORT := "8000" # Shared Docker args for dev commands -DOCKER_DEV_ARGS := "-p " + PORT + ":8000 --env-file .env -v $PWD:/app -v staff-scheduling-venv:/app/.venv -v staff-scheduling-uv-cache:/root/.cache/uv" +DOCKER_DEV_ARGS := "-p " + PORT + ":8000 --env-file .env -v $PWD/src:/app/src" _default: just --list sync: - uv sync - -test *args: - uv run pytest {{args}} + uv sync --all-extras lint *args: uv run ruff check . {{args}} @@ -23,23 +19,19 @@ format *args: typecheck *args: uv run pyright . {{args}} +test *args: + uv run pytest {{args}} + check: lint typecheck test build: docker build -t {{IMAGE_NAME}} . -# App CLI wrapper: -cli *args: - docker run --rm -it \ - {{DOCKER_DEV_ARGS}} \ - {{IMAGE_NAME}} \ - uv run staff-scheduling {{args}} - dev: docker run --rm -it \ {{DOCKER_DEV_ARGS}} \ {{IMAGE_NAME}} \ - uv run uvicorn src.api.main:app --host 0.0.0.0 --port 8000 --reload + uv run fastapi dev src/scheduling/api/app.py --host 0.0.0.0 --port 8000 docker-shell: docker run --rm -it \ @@ -47,5 +39,5 @@ docker-shell: {{IMAGE_NAME}} \ bash -status: - curl http://localhost:{{PORT}}/status +health: + curl http://localhost:{{PORT}}/health diff --git a/cases_static_jsons/employee_types.json b/legacy/cases/68/11_2024/employee_types.json similarity index 100% rename from cases_static_jsons/employee_types.json rename to legacy/cases/68/11_2024/employee_types.json diff --git a/cases_static_jsons/wishes_and_blocked.json b/legacy/cases/68/11_2024/employees.json similarity index 100% rename from cases_static_jsons/wishes_and_blocked.json rename to legacy/cases/68/11_2024/employees.json diff --git a/legacy/cases/68/11_2024/free_shifts_and_vacation_days.json b/legacy/cases/68/11_2024/free_shifts_and_vacation_days.json new file mode 100644 index 00000000..a2f63b3e --- /dev/null +++ b/legacy/cases/68/11_2024/free_shifts_and_vacation_days.json @@ -0,0 +1,3 @@ +{ + "employees": [] +} diff --git a/cases_static_jsons/general_settings.json b/legacy/cases/68/11_2024/general_settings.json similarity index 100% rename from cases_static_jsons/general_settings.json rename to legacy/cases/68/11_2024/general_settings.json diff --git a/cases_static_jsons/minimal_number_of_staff.json b/legacy/cases/68/11_2024/minimal_number_of_staff.json similarity index 100% rename from cases_static_jsons/minimal_number_of_staff.json rename to legacy/cases/68/11_2024/minimal_number_of_staff.json diff --git a/legacy/cases/68/11_2024/shift_information.json b/legacy/cases/68/11_2024/shift_information.json new file mode 100644 index 00000000..d2215694 --- /dev/null +++ b/legacy/cases/68/11_2024/shift_information.json @@ -0,0 +1,47 @@ +[ + { + "break_duration": 0.0, + "end_time": "2000-01-01T14:00:00", + "shift_duration": 360.0, + "shift_id": "1406", + "shift_name": "Z60", + "start_time": "2000-01-01T08:00:00", + "working_minutes": 360.0 + }, + { + "break_duration": 30.0, + "end_time": "2000-01-01T16:10:00", + "shift_duration": 490.0, + "shift_id": "2906", + "shift_name": "T75_", + "start_time": "2000-01-01T08:00:00", + "working_minutes": 460.0 + }, + { + "break_duration": 30.0, + "end_time": "2000-01-01T14:10:00", + "shift_duration": 490.0, + "shift_id": "2939", + "shift_name": "F2_", + "start_time": "2000-01-01T06:00:00", + "working_minutes": 460.0 + }, + { + "break_duration": 30.0, + "end_time": "2000-01-01T21:00:00", + "shift_duration": 490.0, + "shift_id": "2947", + "shift_name": "S2_", + "start_time": "2000-01-01T12:50:00", + "working_minutes": 460.0 + }, + { + "break_duration": 45.0, + "end_time": "2000-01-02T06:30:00", + "shift_duration": 610.0, + "shift_id": "2953", + "shift_name": "N2_", + "start_time": "2000-01-01T20:20:00", + "working_minutes": 565.0 + } +] diff --git a/legacy/cases/68/11_2024/target_working_minutes.json b/legacy/cases/68/11_2024/target_working_minutes.json new file mode 100644 index 00000000..5eaad00f --- /dev/null +++ b/legacy/cases/68/11_2024/target_working_minutes.json @@ -0,0 +1,270 @@ +{ + "employees": [ + { + "actual": 0.0, + "firstname": "Sandra", + "key": 459, + "name": "Shoemake", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Adriane", + "key": 790, + "name": "Mccomas", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Janett", + "key": 791, + "name": "Branz", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Nele", + "key": 822, + "name": "Sewell", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Liselotte", + "key": 839, + "name": "S\u00e4uffert", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Silvia", + "key": 914, + "name": "Harkins", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Ingbert", + "key": 917, + "name": "Catoe", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Jenny", + "key": 921, + "name": "Keese", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "B\u00e4rbl", + "key": 924, + "name": "Merriweather", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Lina", + "key": 925, + "name": "Farniok", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Margaritt", + "key": 927, + "name": "Mittrach", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Daniele", + "key": 928, + "name": "Wunderlich", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Constance", + "key": 1230, + "name": "Palacio", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Kersten", + "key": 2932, + "name": "Devers", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Renilde", + "key": 2963, + "name": "Hoots", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Elgine", + "key": 3566, + "name": "Seligman", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Eike", + "key": 3868, + "name": "Vanfleet", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Hannah", + "key": 4566, + "name": "Woodcock", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Henni", + "key": 5367, + "name": "Donis", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Augustin", + "key": 5920, + "name": "Carreras", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Heinz", + "key": 6475, + "name": "Binford", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Roseliese", + "key": 6507, + "name": "Rashid", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Christfri", + "key": 6677, + "name": "Fullerton", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Saskia", + "key": 6681, + "name": "Labelle", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Lioba", + "key": 6715, + "name": "Burris", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Trude", + "key": 6836, + "name": "Valentino", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Annelene", + "key": 6928, + "name": "Izzo", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Marcus", + "key": 7496, + "name": "Demarco", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Ludger", + "key": 7603, + "name": "Roberson", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Sieghardt", + "key": 7741, + "name": "Tharp", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Kilian", + "key": 7752, + "name": "Rodriques", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Julia", + "key": 7770, + "name": "Yeh", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Burkhild", + "key": 7796, + "name": "Hertzler", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Karena", + "key": 7835, + "name": "Driggers", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Gertraute", + "key": 7848, + "name": "Winters", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Janett", + "key": 7877, + "name": "Staggs", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Irma", + "key": 7919, + "name": "Weathers", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Loremarie", + "key": 7990, + "name": "Milburn", + "target": 0.0 + } + ] +} diff --git a/legacy/cases/68/11_2024/web/jobs.json b/legacy/cases/68/11_2024/web/jobs.json new file mode 100644 index 00000000..63a6baed --- /dev/null +++ b/legacy/cases/68/11_2024/web/jobs.json @@ -0,0 +1,19 @@ +{ + "jobs": [ + { + "caseId": 68, + "completedAt": "2026-06-10T12:25:47.362Z", + "consoleOutput": "Exporting planning data for planning unit 68 from 2024-11-01 to 2024-11-30.\n2026-06-10 12:25:46,658 - INFO - copied static JSON file: wishes_and_blocked.json\n2026-06-10 12:25:46,660 - INFO - copied static JSON file: minimal_number_of_staff.json\n2026-06-10 12:25:46,662 - INFO - copied static JSON file: employee_types.json\n2026-06-10 12:25:46,664 - INFO - copied static JSON file: general_settings.json\n2026-06-10 12:25:46,945 - INFO - \u2705 Export abgeschlossen \u2013 shift_information.json erstellt\n2026-06-10 12:25:46,990 - INFO - \u2705 Export abgeschlossen \u2013 employees.json erstellt\n2026-06-10 12:25:47,044 - INFO - \u2705 Export abgeschlossen \u2013 target_working_minutes.json erstellt\n2026-06-10 12:25:47,094 - INFO - \u2705 Export abgeschlossen \u2013 worked_sundays.json erstellt\n2026-06-10 12:25:47,357 - INFO - \u2705 Export abgeschlossen \u2013 free_shifts_and_vacation_days.json erstellt\n", + "createdAt": "2026-06-10T12:25:46.622Z", + "duration": 739, + "id": "049b9346-aa08-4cd7-8c12-9857fcf0add2", + "params": { + "end": "2024-11-30", + "start": "2024-11-01", + "unit": 68 + }, + "status": "completed", + "type": "fetch" + } + ] +} diff --git a/legacy/cases/68/11_2024/wishes_and_blocked.json b/legacy/cases/68/11_2024/wishes_and_blocked.json new file mode 100644 index 00000000..a2f63b3e --- /dev/null +++ b/legacy/cases/68/11_2024/wishes_and_blocked.json @@ -0,0 +1,3 @@ +{ + "employees": [] +} diff --git a/legacy/cases/68/11_2024/worked_sundays.json b/legacy/cases/68/11_2024/worked_sundays.json new file mode 100644 index 00000000..1c33992f --- /dev/null +++ b/legacy/cases/68/11_2024/worked_sundays.json @@ -0,0 +1,130 @@ +{ + "worked_sundays": [ + { + "firstname": "Margaritt", + "key": 927, + "name": "Mittrach", + "worked_sundays": 2 + }, + { + "firstname": "Daniele", + "key": 928, + "name": "Wunderlich", + "worked_sundays": 2 + }, + { + "firstname": "Kersten", + "key": 2932, + "name": "Devers", + "worked_sundays": 2 + }, + { + "firstname": "Renilde", + "key": 2963, + "name": "Hoots", + "worked_sundays": 2 + }, + { + "firstname": "Eleonore", + "key": 3004, + "name": "Lockett", + "worked_sundays": 2 + }, + { + "firstname": "Henni", + "key": 5367, + "name": "Donis", + "worked_sundays": 2 + }, + { + "firstname": "Bertram", + "key": 6180, + "name": "Putney", + "worked_sundays": 2 + }, + { + "firstname": "Roseliese", + "key": 6507, + "name": "Rashid", + "worked_sundays": 2 + }, + { + "firstname": "Hansmarti", + "key": 6538, + "name": "Guillaume", + "worked_sundays": 2 + }, + { + "firstname": "Noa", + "key": 6612, + "name": "Pettis", + "worked_sundays": 2 + }, + { + "firstname": "Edwin", + "key": 6616, + "name": "Avelar", + "worked_sundays": 2 + }, + { + "firstname": "Saskia", + "key": 6681, + "name": "Labelle", + "worked_sundays": 2 + }, + { + "firstname": "Waldfried", + "key": 637, + "name": "Heaney", + "worked_sundays": 2 + }, + { + "firstname": "Silvia", + "key": 914, + "name": "Harkins", + "worked_sundays": 2 + }, + { + "firstname": "Ingbert", + "key": 917, + "name": "Catoe", + "worked_sundays": 2 + }, + { + "firstname": "B\u00e4rbl", + "key": 924, + "name": "Merriweather", + "worked_sundays": 2 + }, + { + "firstname": "Kirstin", + "key": 6864, + "name": "Heise", + "worked_sundays": 2 + }, + { + "firstname": "Sandra", + "key": 459, + "name": "Shoemake", + "worked_sundays": 1 + }, + { + "firstname": "Lina", + "key": 925, + "name": "Farniok", + "worked_sundays": 1 + }, + { + "firstname": "Belinda", + "key": 6714, + "name": "Griffey", + "worked_sundays": 1 + }, + { + "firstname": "Sonnhilde", + "key": 6769, + "name": "Ingraham", + "worked_sundays": 1 + } + ] +} diff --git a/legacy/cases/77/02_2025/employee_types.json b/legacy/cases/77/02_2025/employee_types.json new file mode 100644 index 00000000..1981e587 --- /dev/null +++ b/legacy/cases/77/02_2025/employee_types.json @@ -0,0 +1,525 @@ +{ + "Azubi": [ + "A-Altenpflegehelfer/in (A-82101-002)", + "A-Altenpfleger/in (A-82102-002)", + "A-An\u00e4sthesietechnisch/r Assistent/in (A-81332-001)", + "A-CTA (Chirurgisch-technische/r Assistent/in) (A-81332-005)", + "A-Fachkaufkann/-frau-Verwaltung im Gesundheitswesen (A-73223-015)", + "A-Fachkraft-Lagerlogistik (A-51312-005)", + "A-Gesundheits- und Kinderkrankenpfleger/in (A-81302-003)", + "A-Gesundheits- und Krankenpfleger/in (A-81302-005)", + "A-Hebamme/Entbindungspfleger (A-81353-003)", + "A-Hilfskoch/-k\u00f6chin (A-29302-018)", + "A-Informatiker/in (Weiterbildung) (A-43103-008)", + "A-Kaufmann/-frau-Gesundheitswesen (A-73222-007)", + "A-Kinderkrankenschwester/-pfleger (A-81302-007)", + "A-Koch/-K\u00f6chin (29302-024)", + "A-Krankenschwester/-pfleger (A-81302-008)", + "A-Medizinische/r Fachangestellte/r (A-81102-004)", + "A-Medizinisch-technisch/r Radiologieassistent/in (A-81232-004)", + "A-Notfallsanit\u00e4ter A-31342-005", + "A-OTA (Operationstechnische/r Assistent/in) (A-81332-009)", + "A-Pflegeassistent/in (A-81302-014)", + "A-Pflegefachkraft - Gesundheits- und Krankenpfleger (81302-015)", + "A-Pflegefachkraft (Altenpflege) (A-81302-019)", + "A-Pflegefachkraft (Krankenpflege) (A-81302-018)", + "A-Pflegefachkraft-Kinderkrankenpflege (A-81302-016)", + "A-Pflegefachmann/-frau (A-81302-028)", + "A-Stationshelfer/in - Krankenpflege (A-81301-017)" + ], + "Fachkraft": [ + "Abteilungsleiter/in (71394-003)", + "Alleinsekret\u00e4r/in (71402-004)", + "Allgemeinarzt/-\u00e4rztin (81404-001)", + "Allgemeinchirurg/in (81434-001)", + "Altenpfleger/in (82102-002)", + "Ambulante Krankenschwester/-pfleger (81302-001)", + "Ambulanzpfleger/schwester (81382-001)", + "An\u00e4sthesietechnische/r Assistent/in (81332-001)", + "An\u00e4sthesist/in (81454-002)", + "Archivar/in (73314-001)", + "Archivfachkraft (73312-004)", + "Arzt/\u00c4rztin - Allgemeinmedizin (81404-003)", + "Arzt/\u00c4rztin - An\u00e4sthesiologie und Intensivtherapie (81454-003)", + "Arzt/\u00c4rztin - Chirurgie (81434-002)", + "Arzt/\u00c4rztin - Hygiene (81484-006)", + "Arzt/\u00c4rztin - Innere Medizin (81424-001)", + "Arzt/\u00c4rztin - Kinder- und Jugendpsychiatrie (81464-001)", + "Arzt/\u00c4rztin - klinische Strahlenphysik (81234-001)", + "Arzt/\u00c4rztin - Neurochirurgie (81434-004)", + "Arzt/\u00c4rztin - Neurologie (81464-002)", + "Arzt/\u00c4rztin - Orthop\u00e4die (81434-005)", + "Arzt/\u00c4rztin - Radiologie (81234-003)", + "Arzt/\u00c4rztin - Traumatologie und Orthop\u00e4die (81434-006)", + "Arzt/\u00c4rztin (81404-002)", + "\u00c4rztliche/r Direktor/in (Humanarzt/-\u00e4rztin) (81494-001)", + "\u00c4rztliche/r Leiter/in (81494-002)", + "Arztsekret\u00e4r/in (73222-001)", + "Assistent/in - Gesch\u00e4ftsleitung (71403-001)", + "Assistent/in - Gesundheitswesen (73222-003)", + "Assistent/in - Operationstechnik (81332-003)", + "Assistent/in - Rechnungswesen (72213-006)", + "Assistenzarzt/-\u00e4rztin - Kinder-/Jugendpsychiatrie und -psychologie (81464-006)", + "Assistenzarzt/-\u00e4rztin (81404-004)", + "Assistenzarzt/-\u00e4rztin (Uni) - An\u00e4sthesiologie (81454-004)", + "Assistenzarzt/-\u00e4rztin (Uni) - Chirurgie (81434-007)", + "Assistenzarzt/-\u00e4rztin (Uni) - Innere Medizin (81424-004)", + "Assistenzarzt/-\u00e4rztin (Uni) - Kinder-/Jugendmedizin (81414-002)", + "Assistenzarzt/-\u00e4rztin (Uni) - Neurologie (81464-009)", + "ATA (An\u00e4sthesietechnische/r Assistent/in) (81332-004)", + "Augenoptikermeister/in (82593-001)", + "Ausbilder Berufsbildungswerk (84223-013)", + "Ausbilder Berufsf\u00f6rderungswerk", + "Bachelor of Arts - Soziale Arbeit/Soziale Dienste (83123-003)", + "Bachelor of Arts - Sportwissenschaft (84503-002)", + "Bankkaufmann/-frau (72112-004)", + "Beikoch/-k\u00f6chin (29302-005)", + "Bereichsleiter/in (71394-006)", + "Berufsp\u00e4dagoge/-p\u00e4dagogin (84224-003)", + "Besch\u00e4ftigungstherapeut/in (81723-004)", + "Betriebselektriker/in (26252-007)", + "Betriebsg\u00e4rtner/in (12102-001)", + "Betriebstechniker/in (25103-011)", + "Betriebswirt/in (Weiterbildung) - Rechnungswesen (72213-014)", + "Bibliothekar/in (73324-004)", + "Bilanzbuchhalter/in (72213-016)", + "Buchhalter/in (72213-019)", + "B\u00fcrofachkraft (71402-010)", + "B\u00fcrokaufmann/-frau (71402-012)", + "Chefarzt/-\u00e4rztin (81494-003)", + "Chefsekret\u00e4r/in (71403-008)", + "Chirurg/in (81434-008)", + "Coach Erw.Bildung (84404-010)", + "Controller/in (72234-005)", + "Controllingleiter/in (72294-006)", + "CTA (Chirurgisch-technische/r Assistent/in) (81332-005)", + "Datenverarbeitungsfachmann/-frau (43103-005)", + "Dauernachtwache (Krankenschwester/-pfleger) (81302-002)", + "Diabetesberater/in (81783-002)", + "Di\u00e4tassistent/in (81762-001)", + "EDV Sachbearbeiter (41402-017)", + "EDV-Systemtechniker/in (26312-040)", + "EDV-Techniker/in (43103-007)", + "EEG-Assistent/in (81222-009)", + "Einkaufsleiter/in (61194-006)", + "Einkaufslogistiker/in (61113-015)", + "Einzelhandelskaufmann/-frau (62102-002)", + "Elektroanlagenelektroniker/in (26252-050)", + "Elektroinstallateur/in (26212-017)", + "Elektroniker/in - Energie- und Geb\u00e4udetechnik (26212-019)", + "Elektroniker/in - Ger\u00e4te und Systeme (26302-033)", + "Endoskopieschwester/-pfleger (81313-003)", + "Ergotherapeut/in (81723-006)", + "Ern\u00e4hrungsberater/in (82233-005)", + "Ern\u00e4hrungswissenschaftler/in (82284-003)", + "Erzieher/in - Heilp\u00e4dagogik (83133-002)", + "Erzieher/in (83112-006)", + "Erziehungswissenschaftler/in (91334-007)", + "EXAH-Pflegefachkraft (Altenpflege) (A-81302-019)", + "EXGK-Pflegefachkraft - Gesundheits- und Krankenpflege (A-81302-015)", + "EXKI-Pflegefachkraft.Kinderkrankenpflege (A-81302-016)", + "EXKP-Pflegefachkraft- (Krankenpflege) (A-81302-018)", + "EX-Pflegefachmann/- frau (A-81302-028)", + "Fachangestellte/r f\u00fcr B\u00fcrokommunikation (71402-021)", + "Facharzt Neurochirurgie", + "Facharzt Psychiatrie u. Psychotherapie", + "Facharzt/-\u00e4rztin - Allgemeinchirurgie (81434-027)", + "Facharzt/-\u00e4rztin - Allgemeine Chirurgie (81434-009)", + "Facharzt/-\u00e4rztin - Allgemeinmedizin (81404-005)", + "Facharzt/-\u00e4rztin - Allgemeinmedizin (Hausarzt/-\u00e4rztin) (81404-010)", + "Facharzt/-\u00e4rztin - An\u00e4sthesiologie (81454-005)", + "Facharzt/-\u00e4rztin - Frauenheilkunde und Geburtshilfe (81444-017)", + "Facharzt/-\u00e4rztin - Gef\u00e4\u00dfchirurgie (81434-010)", + "Facharzt/-\u00e4rztin - Hals-Nasen-Ohrenheilkunde (81444-018)", + "Facharzt/-\u00e4rztin - Innere Medizin (81424-006)", + "Facharzt/-\u00e4rztin - Innere Medizin u. Allgemeinm. (Hausarzt) (81404-006)", + "Facharzt/-\u00e4rztin - Innere Medizin u. H\u00e4matolog. u. Onkologie (81424-010)", + "Facharzt/-\u00e4rztin - Innere Medizin und Gastroenterologie (81424-009)", + "Facharzt/-\u00e4rztin - Innere Medizin und Kardiologie (81424-011)", + "Facharzt/-\u00e4rztin - Innere Medizin und Nephrologie (81424-012)", + "Facharzt/-\u00e4rztin - Innere Medizin und Pneumologie (81424-013)", + "Facharzt/-\u00e4rztin - Kinder- u. Jugendpsychiat. u. -psychoth. (81464-010)", + "Facharzt/-\u00e4rztin - Kinder- und Jugendmedizin (81414-003)", + "Facharzt/-\u00e4rztin - Neurologie (81464-012)", + "Facharzt/-\u00e4rztin - \u00d6ffentliches Gesundheitswesen (81484-028)", + "Facharzt/-\u00e4rztin - Orthop\u00e4die und Unfallchirurgie (81434-013)", + "Facharzt/-\u00e4rztin - P\u00e4diatrie (81414-005)", + "Facharzt/-\u00e4rztin - Plastische und \u00c4sthetische Chirurgie (81434-014)", + "Facharzt/-\u00e4rztin - Radiologie (81234-005)", + "Facharzt/-\u00e4rztin - Thoraxchirurgie (81434-015)", + "Facharzt/-\u00e4rztin - Unfallchirurgie (81434-016)", + "Facharzt/-\u00e4rztin - Viszeralchirurgie (81434-017)", + "Fachinformatiker/in - Systemintegration (43102-014)", + "Fachinformatiker/in (43102-013)", + "Fachkaufmann/-frau - Logistik (51623-005)", + "Fachkaufmann/-frau - Verwaltung im Gesundheitswesen (73223-015)", + "Fachkinderkrankenpfleger/in - Intensivpflege/An\u00e4sthesie (81323-005)", + "Fachkinderkrankenschwester/-pfleger - ambulante Pflege (81323-002)", + "Fachkinderkrankenschwester/-pfleger - P\u00e4diatrie und Intensivmedizin (81323-011)", + "Fachkinderkrankenschwester/-pfleger - Psychiatrie (81323-013)", + "Fachkraft - Altenpflege (82102-004)", + "Fachkraft - Factoring (72213-033)", + "Fachkraft - Lagerlogistik (51312-005)", + "Fachkraft - Lagerwirtschaft (51312-007)", + "Fachkraft - Sozialarbeit (83123-004)", + "Fachkrankenpfleger/in - Nephrologie", + "Fachkrankenpfleger/in - Notfallpflege (81313-059)", + "Fachkrankenpfleger/in - Operations-/Endoskopiedienst (81313-014)", + "Fachkrankenschwester/-pfleger - Endoskopie (81313-006)", + "Fachkrankenschwester/-pfleger - Intensivmedizin und An\u00e4sthesie (81313-008)", + "Fachkrankenschwester/-pfleger - Intensivpflege/An\u00e4sthesie (81313-009)", + "Fachkrankenschwester/-pfleger - Operationsdienst (81313-015)", + "Fachlehrer/in - arbeitstechnische F\u00e4cher (84214-008)", + "Fachlehrer/in - Pflegeberufe (84213-094)", + "Fachlehrer/in (84214-006)", + "Fachschwester/-pfleger - Intensivpflege und An\u00e4sthesie (81313-025)", + "Fachwirt Immobilien (61313-009)", + "Finanzbuchhalter/in (72213-038)", + "G\u00e4rtner/in (12102-007)", + "Gas- und Wasserinstallateur/in (34212-005)", + "Gastroenterologe/Gastroenterologin (81424-015)", + "Gehaltsabrechner/in (72213-042)", + "Gesch\u00e4ftsf\u00fchrende/r Direktor/in (\u00f6ffentliche Verwaltung) (73294-015)", + "Gesch\u00e4ftsf\u00fchrer/in (71104-008)", + "Gesundheits- und Kinderkrankenpfleger/in (81302-003)", + "Gesundheits- und Krankenpflegeassistent/in (81302-004)", + "Gesundheits- und Krankenpfleger/in (81302-005)", + "Gesundheitscoach (82212-001)", + "Gesundheits\u00f6konom/in (82214-004)", + "Gesundheitspfleger/in (81302-006)", + "Gymnastiklehrer/in (84553-016)", + "Haushaltsfachkraft (83212-005)", + "Hausmeister/in (34102-007)", + "Haustechniker/in (34102-008)", + "Hauswart/in (34102-009)", + "Hauswirtschafter/in (83212-007)", + "Hauswirtschaftsverwalter/in (83293-005)", + "Hebamme/Entbindungspfleger (81353-003)", + "Heilerziehungspfleger/in (83132-006)", + "Heilp\u00e4dagoge/-p\u00e4dagogin (83134-002)", + "Heilpraktiker/in (81752-002)", + "Heizungs- und Sanit\u00e4rinstallateur/in (834212-011)", + "Heizungsmonteur/in (34212-015)", + "Honorar-Arzt", + "Honorardozent/in (84304-032)", + "Hospizleiter/in (81394-002)", + "Hygienefachkraft (53322-010)", + "Industriekaufmann/-frau (71302-011)", + "Informatiker (Hochschule) (43104-002)", + "Informatiker/in (Weiterbildung) (43103-008)", + "Integrationsberater", + "Integrationsmanager/in (83134-005)", + "Internatsbetreuer", + "Internist/in (81424-017)", + "IT-Administrator/in (43343-031)", + "IT-Leiter/in (43394-007)", + "Jugenderzieher/in (83112-025)", + "Jugendpsychologe/-psychologin (81624-007)", + "Jurist/in (73104-001)", + "Kardiologe/Kardiologin (81424-018)", + "Kaufmann/-frau - Gesundheitswesen (73222-007)", + "Kaufm\u00e4nnische/r Angestellte/r (71302-015)", + "Kaufm\u00e4nnische/r Direktor/in (71104-015)", + "Kaufm\u00e4nnische/r Sachbearbeiter/in (71302-018)", + "Kinder- und Jugendlichenpsychotherapeut/in (81634-004)", + "Kinder- und Jugendpsychologe/-psychologin (81624-008)", + "Kinder- und Jugendpsychotherapeut/in (81634-005)", + "Kinderarzt/-\u00e4rztin (81414-006)", + "Kinderg\u00e4rtner/in (83112-032)", + "Kinderkrankenschwester/-pfleger - Psychiatrie (81323-035)", + "Kinderkrankenschwester/-pfleger (81302-007)", + "Kinderp\u00e4dagoge/-p\u00e4dagogin (83112-033)", + "Kinderpsychologe/-psychologin (81624-009)", + "Kindheitsp\u00e4dagoge/-p\u00e4dagogin (91334-005)", + "Klinische Kodierfachkraft (71442-004)", + "Klinische/r Neuropsychologe/-psychologin (81624-010)", + "Koch/K\u00f6chin (29302-024)", + "Kodierer/in (71442-005)", + "Kostenabrechner/in (72223-011)", + "Krankengymnast/in (81713-009)", + "Krankenhaussekret\u00e4r/in (73222-009)", + "Krankenschwester/-pfleger - An\u00e4sthesie (81313-037)", + "Krankenschwester/-pfleger - Nachtwache (81302-009)", + "Krankenschwester/-pfleger - Nephrologie (81313-043)", + "Krankenschwester/-pfleger (81302-008)", + "Krankentransportleiter/in (52182-021)", + "K\u00fcchenchef (29394-004)", + "K\u00fcchenleiter/in (29394-007)", + "K\u00fcster/in (83382-005)", + "Lagerverwalter/in (Warenlager) (51312-030)", + "Lehrer/in - Gesundheitsfachberufe (84213-031)", + "Lehrer/in - Krankenpflege (84213-038)", + "Lehrer/in - Pflegeberufe (84213-044)", + "Leitende Pflegefachkraft (81393-002)", + "Leitende/r Arzt/\u00c4rztin (81494-004)", + "Leitende/r Entbindungspfleger/Hebamme/Bereichsleitung Frauenklinik (81393-003)", + "Leitende/r Gesundheits- und Krankenschwester/-pfleger (81393-004)", + "Leiter/in - ambulante Sozialdienste (83194-027)", + "Leiter/in - Einkauf (61194-013)", + "Leiter/in - Finanz- und Rechnungswesen (72294-008)", + "Leiter/in - Krankenhausbetriebstechnik (34193-009)", + "Leiter/in - Medizincontrolling (82214-014)", + "Leiter/in - Presse- und \u00d6ffentlichkeitsarbeit (92294-001)", + "Leiter/in - soziale Einrichtung (83194-015)", + "Leiter/in - Technik (27394-016)", + "Logop\u00e4de/Logop\u00e4din (81733-006)", + "Logop\u00e4de/Logop\u00e4din (Hochschule) (81734-003)", + "Lohnsachbearbeiter/in (72213-063)", + "Maler/in (33212-019)", + "Marketingsekret\u00e4r/in (71402-042)", + "Masseur/in (81712-004)", + "Masseur/in und medizinische/r Bademeister/in (81712-005)", + "Master of Science - Gesundheits\u00f6konomie (82214-009)", + "Master of Science - Projektmanagement (71304-017)", + "Mediengestalter/in - Digital-/Printmedien - Medienberatung (61122-015)", + "Medientechniker/in (23213-015)", + "Medizinallaborant/in (Humanmedizin) (81212-018)", + "Medizincontroller/in (72234-025)", + "Mediziner/in (81404-009)", + "Medizinische/r Assistent/in (81212-022)", + "Medizinische/r Fachangestellte/r (81102-004)", + "Medizinische/r Praxisassistent/in (81102-006)", + "Medizinisch-kaufm\u00e4nnische/r Assistent/in (73222-010)", + "Medizinisch-technische/r Assistent/in (81212-019)", + "Medizinisch-technische/r Fachassistent/in - radiologische Diagnostik (81233-011)", + "Medizinisch-technische/r Fachassistent/in (81213-023)", + "Medizinisch-technische/r Laboratoriumsassistent/in (81212-021)", + "Medizinisch-technische/r Radiologieassistent/in MTRA (81232-004)", + "MTA (Medizinisch-technische/r Laboratoriumsassistent/in 81212-031)", + "Musiker/in (94114-044)", + "Netzadministrator/in (EDV) (43343-016)", + "Netzwerkadministrator (43343-018)", + "Neurologe/Neurologin (81464-020)", + "Notfallsanit\u00e4ter (81342-005)", + "Oberarzt/-\u00e4rztin (81494-007)", + "\u00d6ffentlichkeitsreferent/in (92203-014)", + "\u00d6kotrophologe/\u00d6kotrophologin (82284-009)", + "Operationstechnische/r Assistent/in (81332-008)", + "Organist/in (94114-059)", + "Orthop\u00e4de/Orthop\u00e4din (81434-021)", + "OTA (Operationstechnische/r Assistent/in) (81332-009)", + "P\u00e4dagogische/r Betreuer/in (83124-033)", + "P\u00e4dagogische/r Mitarbeiter/in (91334-024)", + "P\u00e4diatrie-Assistent/in (81182-008)", + "Personalbuchhalter/in (72213-071)", + "Personalchef/in (71594-011)", + "Personalfachkaufmann/-kauffrau (71513-010)", + "Personalkaufmann/-frau (71512-004)", + "Personalleiter/in (71594-013)", + "Personalsachbearbeiter/in (71512-005)", + "Pflegeassistent/in (81302-014)", + "Pflegebereichsleiter/in (81394-013)", + "Pflegedienstleiter/in (81394-014)", + "Pflegedirektor/in (81394-032)", + "Pflegefachkraft - An\u00e4sthesie/Intensivmedizin (81313-052)", + "Pflegefachkraft - Gesundheits- und Krankenpfleger (81302-015)", + "Pflegefachkraft - Kinderkrankenpflege (81302-016)", + "Pflegefachkraft - Kinderpflege (81302-017)", + "Pflegefachkraft - Sozialstation (81302-019)", + "Pflegefachkraft (Krankenpflege) (81302-018)", + "Pflegefachmann/-frau (81302-028)", + "Pflegeleiter/in (81394-017)", + "Pflegemanager/in (81394-018)", + "Pflegemanager/in (81394-019)", + "Pflegep\u00e4dagoge/-p\u00e4dagogin (84214-065)", + "Pf\u00f6rtner/in (53112-066)", + "Physician Assistant (81333-001)", + "Physiker/in (41404-002)", + "Physiotherapeut/in (81713-015)", + "Physiotherapeut/in (Hochschule) (81714-002)", + "Praxisanleiter/in - Pflegeberufe (84213-085)", + "Praxisanleiter/-innen (84223-081)", + "Projektmanager/in (71393-007)", + "Prokurist/in (71104-017)", + "Psychologe/Psychologin - allgemeine Psychologie (81624-016)", + "Psychologe/Psychologin (81624-015)", + "Psychologische/r Psychotherapeut/in (81634-014)", + "Qualit\u00e4tsbeauftragte/r - Gesundheits-/Sozialwesen (82243-003)", + "Qualit\u00e4tsbeauftragte/r (27313-022)", + "Qualit\u00e4tsbeauftragter/-beauftragte - Management (27313-023)", + "Radioonkologische/r Assistent/in (81232-010)", + "Rechnungswesensachbearbeiter/in (72212-026)", + "Referent/in - berufliche Fort- und Weiterbildung", + "Referent/in - \u00d6ffentlichkeitsarbeit/Marketing (92203-023)", + "Reha Berater (71524-028)", + "Rehabilitationsp\u00e4dagoge/-p\u00e4dagogin (83134-009)", + "Reinigungsfachkraft /54112-018)", + "Rettungsassistent/in", + "Rettungssanit\u00e4ter/in", + "Rezeptionsmitarbeiter/in (Arztpraxis) (73222-012)", + "Rohrinstallateur/in (34212-040)", + "R\u00f6ntgenassistent/in (81232-011)", + "Sachbearbeiter/in - B\u00fcro (71402-058)", + "Sachbearbeiter/in - Verwaltung (geh. nichttechn. Dienst) (73203-017)", + "Sachbearbeiter/in (71302-021)", + "Schreibkraft (71432-031)", + "Schularzt/-\u00e4rztin (81414-008)", + "Schulleiter/in - Berufsschulen (8494-033)", + "Schulsozialarbeiter/in (83124-035)", + "Schwester/Pfleger (Kinderkrankenpflege) (81302-025)", + "Schwester/Pfleger (Krankenpflege) (81302-026)", + "Seelsorger/in (83314-034)", + "Sekret\u00e4r/in - Gesundheitswesen (73222-013)", + "Sekret\u00e4r/in (71402-062)", + "Sekretariatsleiter/in (71493-011)", + "Seniorenbetreuer/in (82102-005)", + "Seniorenpfleger/in (82102-006)", + "Seniorenzentrumsleiter/in (82194-013)", + "Servicekraft (63302-045)", + "Sicherheitsfachkraft (53123-009)", + "Sozialarbeiter/in (83124-037)", + "Sozialarbeiter/in / Sozialp\u00e4dagoge/-p\u00e4dagogin (83124-052)", + "Sozialp\u00e4dagoge/-p\u00e4dagogin (83124-039)", + "Sozialwissenschaftler/in (91324-015)", + "Sportlehrer/in - Rehabilitation/Behindertensport (83133-016)", + "Sportlehrer/in (84503-023)", + "Sportphysiotherapeut/in (81713-016)", + "Sporttherapeut (81783-013)", + "Sprechstundenhilfe (81102-009)", + "Stationsassistent/in (Arzthilfe) (81102-010)", + "Stationsleiter/in - Kranken-/Alten-/Kinderkrankenpflege (81393-012)", + "Stationsleiter/in - Krankenpflege (81393-009)", + "Stationsleiter/in - Krankenpflege/Altenpflege (81393-010)", + "Stationsleiter/in - Pflegedienst (81393-011)", + "Stenograf/in (71432-036)", + "Apothekenhelfer/in (62412-001)", + "Sterilisationsassistent/in (81182-002)", + "Suchtpsychologe/-psychologin (81624-022)", + "Techn. Assistent/in - Bautechnik (31102-008)", + "Techn.Produktdesigner (27212-075)", + "Techniker/in - Anwendungs-/Betriebstechnik (Farben, Lacke) (22203-016)", + "Techniker/in - Elektrotechnik (26303-015)", + "Technische/r B\u00fcrosachbearbeiter/in (71402-068)", + "Technische/r Koordinator/in (27304-054)", + "Technische/r Leiter/in (27394-024)", + "General (01104-008)", + "Technische/r Sterilisationsassistent/in (81182-003)", + "Telefonist/in (71401-042)", + "Hauselektriker/in (26212-033)", + "Terminsachbearbeiter/in (27302-010)", + "Therapeut/in - Krankengymnastik (81713-018)", + "Thoraxchirurg/in (81434-024)", + "\u00dcbungsleiter/in (84503-031)", + "Uhrenmacher (24532-032)", + "Uhrmachermeister (24593-041)", + "Elektrochemiker/in (41384-009)", + "Unterrichtspfleger/-schwester (84213-078)", + "Farb- und Lacktechniker/in (22203-003)", + "Verwalter/in - Tierzucht (11294-009)", + "Verwaltungsangestellte/r - Krankenk.,Krankenh\u00e4user, Kliniken (73222-016)", + "Gesundheits- und Krankenschwester/-pfleger - Gerontopsych.", + "Verwaltungsangestellter (mittl.Dienst) kirchl. Dienst", + "Verwaltungsfachangestellte/-angestellter - Kirchenverwaltung -evangelische Kirche (73282-010)", + "Visceralchirurg/in (81434-026)", + "Vorarbeiter (27302-012)", + "Vorzimmersekret\u00e4r/in (71402-071)", + "Weiterbildungsassistent/in (Arzt/\u00c4rztin) (81404-011)", + "Werbedesigner/in (23224-072)", + "Betriebsschlosser/in (25102-010)", + "Wirtschaftswissenschaftler/in (91404-011)", + "Wundmanager/in (81383-005)", + "Apparate- und Maschinenschlosser/in (34342-005)", + "Arzthelfer/in (81102-001)", + "Zahnarzthelfer/in (81112-006)" + ], + "Hilfskraft": [ + "Ableger/in 871401-001", + "Abrechnungspr\u00fcfer/in (72214-001=", + "Abschlussagent/in (Versicherung) (72133-001)", + "Alltagsbetreuer/in (83142-001)", + "Altenbetreuerhelfer/in (82101-001)", + "Altenpflegeassistent/in (1 j\u00e4hrige A.) (82101-008)", + "Altenpflegehelfer/in (1 j\u00e4hrige Ausb.) (82101-002)", + "Altenpflegehilfskraft (82101-003)", + "Anmelder/in (71404-010)", + "Anstreicher/in (33212-001)", + "Archivhelfer/in (71401-011)", + "Archivsachbearbeiter/in", + "Aufr\u00e4umer/in (Raum-, Hausratreiniger/in) (54101-002)", + "Ausbaufacharbeiter/in - Malerarbeiten (33212-003)", + "Ausbauhelfer/in (33301-001)", + "Aushilfsfahrer/in (52182-006)", + "Aushilfskraft (K\u00fcche) (29301-002)", + "Auskunftsgehilfe/-gehilfin (71401-013)", + "Auslader/in (Transportarbeiter/in) (51311-015)", + "Azetylenschwei\u00dfer/in (24422-010)", + "Betreuungshelfer/in (83111-002)", + "Betreuungskraft / Alltagsbegleiter/in (83142-003)", + "Betriebshandweker/in (25102-007)", + "Betriebshilfering-Gesch\u00e4ftsf\u00fchrer/in (11124-002)", + "Bote/Botin (B\u00fcro) (51321-009)", + "Bundesfreiwilligendienst (BFD)", + "B\u00fcroassistent/in (71402-009)", + "B\u00fcrogehilf(e/in) (71402-011)", + "B\u00fcrohilfskraft (71401-019)", + "externes Pers., Honorar", + "Fachkraft - Pflegeassistenz (83142-004)", + "Fahrbetriebsregler/in (Stra\u00dfenverkehr) (51512-003)", + "Fernsprechvermittler/in (71401-027)", + "Freiwilliges Soziales Jahr (FSJ)", + "Geb\u00e4udeinnenreiniger/in (54112-012)", + "Gesundheits- und Krankenpflegehelfer/in (81301-001)", + "Glasreiniger/in (54122-003)", + "Gr\u00fcnanlagenpfleger/in (12101-012)", + "Hausarbeitsgehilfe/-gehilfin (83211-002)", + "Haushaltshilfe (83211-005)", + "Hauswirtschaftsgehilfe/-gehilfin (83212-014)", + "Hauswirtschaftshelfer/in/-assistent/in (83212-015)", + "Hebammenhelfer/in (81352-002)", + "Helfer Reinigung (54101-014)", + "Helfer/in - Altenpflege/Pers\u00f6nliche Assistenz (82101-004)", + "Helfer/in - B\u00fcro, Verwaltung (71401-033)", + "Helfer/in - B\u00fcro, Verwaltung (71401-034)", + "Helfer/in - Gartenbau (12101-016)", + "Helfer/in - Gr\u00fcnanlagen (12101-017)", + "Helfer/in - Hauswirtschaft (83211-001)", + "Helfer/in - K\u00fcche (29301-005)", + "Helfer/in - Rettungsdienst (81341-002)", + "Helfer/in - Schachtarbeiten (32201-010)", + "Helfer/in - station\u00e4re Krankenpflege (81301-002)", + "Helfer/in - Warenmalerei, -lackiererei (22201-004)", + "Hilfskoch/-k\u00f6chin (29302-018)", + "Hilfsmonteuer (Elektro) (26301-046)", + "Hilfsschwester/-pfleger (81301-003)", + "Jahrespraktikant/-in (JP)", + "Kaufm\u00e4nnische B\u00fcrokraft (71402-034)", + "Kochhelfer/in (29301-007)", + "Kranken- und Altenpflegehelfer/in (81301-005)", + "Krankenfahrer/in (52182-019)", + "Krankenpflegehelfer/in (1 j\u00e4hrige A.) (81301-006)", + "Krankentransporteur/in (52182-020)", + "K\u00fcchengehilfe/-gehilfin (29301-009)", + "K\u00fcchenhelfer/in (29301-010)", + "K\u00fcchenhilfe (29301-011)", + "Lagerarbeiter/in (51311-068)", + "Lagerhelfer/in (51311-070)", + "Lagerhilfsarbeiter/in (51311-071)", + "Maschinenhelfer/in (25101-014)", + "Medizinische/r Fachhelfer/in (81102-005)", + "Menu/Datenerfasser/-in (71401-022)", + "Pflegediensthelfer/in (81301-008)", + "Pflegefachhelfer/in (Krankenpflege)", + "Pflegehelfer Behindertenpflege", + "Pflegehelfer/in - Altenpflege (ohne 1 j\u00e4hrige A.) (82101-006)", + "Pflegehelfer/in - station\u00e4re Pflege (ohne 1 j\u00e4hrige A.) (81301-010)", + "Pflegehelfer/in (Krankenpflege) (ohne 1 j\u00e4hrige A.) (81301-011)", + "Pflegehilfskraft (Krankenpflege) (81301-012)", + "Praktikant/-in (P)", + "Praktisches Jahr (PJ)", + "Raumpfleger/in (54101-023)", + "Rettungshelfer/in (81341-005)", + "R\u00f6ntgenhelfer/in (81232-012)", + "Saisonhelfer/in (11101-035)", + "Schwesterhelfer/in (81301-014)", + "Schwestern-/Pflegediensthelfer/in (81301-015)", + "Servicehilfskraft (63301-044)", + "Sozialassistent/in (83142-006)", + "Sportassistent/in (63122-005=", + "Sp\u00fclmann/-frau (Hausratreiniger/in) (54101-031)", + "Stationshelfer/in - Krankenpflege (81301-017)", + "Stationshilfe (81301-018)", + "Transporthelfer/in (51311-109)", + "Verwaltungsgehilfe/-gehilfin (73201-009)", + "W\u00e4scheschneider/in (28222-155)" + ] +} diff --git a/legacy/cases/77/02_2025/employees.json b/legacy/cases/77/02_2025/employees.json new file mode 100644 index 00000000..ed3c9636 --- /dev/null +++ b/legacy/cases/77/02_2025/employees.json @@ -0,0 +1,244 @@ +{ + "employees": [ + { + "firstname": "Adriane", + "key": 790, + "name": "Mccomas", + "type": "Stationshilfe (81301-018)" + }, + { + "firstname": "Janett", + "key": 791, + "name": "Branz", + "type": "Altenpfleger/in (82102-002)" + }, + { + "firstname": "Liselotte", + "key": 839, + "name": "S\u00e4uffert", + "type": "Krankenpflegehelfer/in (1 j\u00e4hrige A.) (81301-006)" + }, + { + "firstname": "Mirjam", + "key": 843, + "name": "Flanagan", + "type": "Arzthelfer/in (81102-001)" + }, + { + "firstname": "Elia", + "key": 844, + "name": "Greenman", + "type": "Gesundheits- und Krankenpfleger/in (81302-005)" + }, + { + "firstname": "Silvia", + "key": 914, + "name": "Harkins", + "type": "Krankenpflegehelfer/in (1 j\u00e4hrige A.) (81301-006)" + }, + { + "firstname": "Ingbert", + "key": 917, + "name": "Catoe", + "type": "Krankenschwester/-pfleger (81302-008)" + }, + { + "firstname": "Jenny", + "key": 921, + "name": "Keese", + "type": "Altenpfleger/in (82102-002)" + }, + { + "firstname": "B\u00e4rbl", + "key": 924, + "name": "Merriweather", + "type": "Gesundheits- und Krankenpfleger/in (81302-005)" + }, + { + "firstname": "Lina", + "key": 925, + "name": "Farniok", + "type": "Gesundheits- und Krankenpfleger/in (81302-005)" + }, + { + "firstname": "Margaritt", + "key": 927, + "name": "Mittrach", + "type": "Stationshilfe (81301-018)" + }, + { + "firstname": "Daniele", + "key": 928, + "name": "Wunderlich", + "type": "Gesundheits- und Krankenpfleger/in (81302-005)" + }, + { + "firstname": "Constance", + "key": 1230, + "name": "Palacio", + "type": "Pflegefachkraft (Krankenpflege) (81302-018)" + }, + { + "firstname": "Kersten", + "key": 2932, + "name": "Devers", + "type": "Pflegefachkraft (Krankenpflege) (81302-018)" + }, + { + "firstname": "Renilde", + "key": 2963, + "name": "Hoots", + "type": "Altenpfleger/in (82102-002)" + }, + { + "firstname": "Elgine", + "key": 3566, + "name": "Seligman", + "type": "Altenpfleger/in (82102-002)" + }, + { + "firstname": "Eike", + "key": 3868, + "name": "Vanfleet", + "type": "Pflegefachkraft (Krankenpflege) (81302-018)" + }, + { + "firstname": "Henni", + "key": 5367, + "name": "Donis", + "type": "Krankenpflegehelfer/in (1 j\u00e4hrige A.) (81301-006)" + }, + { + "firstname": "Sturmius", + "key": 5652, + "name": "Sherrard", + "type": "A-Pflegefachkraft (Krankenpflege) (A-81302-018)" + }, + { + "firstname": "Liliane", + "key": 5656, + "name": "Bow", + "type": "A-Pflegefachkraft (Krankenpflege) (A-81302-018)" + }, + { + "firstname": "Swantje", + "key": 5668, + "name": "Greer", + "type": "A-Pflegefachkraft (Krankenpflege) (A-81302-018)" + }, + { + "firstname": "Augustin", + "key": 5920, + "name": "Carreras", + "type": "Pflegefachkraft (Krankenpflege) (81302-018)" + }, + { + "firstname": "Annette", + "key": 6259, + "name": "Trovato", + "type": "Altenpfleger/in (82102-002)" + }, + { + "firstname": "Svetlana", + "key": 6316, + "name": "Montejano", + "type": "Helfer/in - station\u00e4re Krankenpflege (81301-002)" + }, + { + "firstname": "Uschi", + "key": 6318, + "name": "Gamblin", + "type": "Gesundheits- und Krankenpfleger/in (81302-005)" + }, + { + "firstname": "Heinz", + "key": 6475, + "name": "Binford", + "type": "Helfer/in - station\u00e4re Krankenpflege (81301-002)" + }, + { + "firstname": "Edwin", + "key": 6616, + "name": "Avelar", + "type": "A-Pflegefachkraft (Krankenpflege) (A-81302-018)" + }, + { + "firstname": "Christfri", + "key": 6677, + "name": "Fullerton", + "type": "Krankenpflegehelfer/in (1 j\u00e4hrige A.) (81301-006)" + }, + { + "firstname": "Liebhardt", + "key": 6762, + "name": "Deshields", + "type": "A-Pflegeassistent/in (A-81302-014)" + }, + { + "firstname": "Trude", + "key": 6836, + "name": "Valentino", + "type": "Pflegefachkraft (Krankenpflege) (81302-018)" + }, + { + "firstname": "Annelene", + "key": 6928, + "name": "Izzo", + "type": "Medizinische/r Fachangestellte/r (81102-004)" + }, + { + "firstname": "Annemargr", + "key": 7490, + "name": "Waldinger", + "type": "Gesundheits- und Krankenpfleger/in (81302-005)" + }, + { + "firstname": "Ludger", + "key": 7603, + "name": "Roberson", + "type": "Altenpfleger/in (82102-002)" + }, + { + "firstname": "Sieghardt", + "key": 7741, + "name": "Tharp", + "type": "Gesundheits- und Krankenpfleger/in (81302-005)" + }, + { + "firstname": "Kilian", + "key": 7752, + "name": "Rodriques", + "type": "Bundesfreiwilligendienst (BFD)" + }, + { + "firstname": "Julia", + "key": 7770, + "name": "Yeh", + "type": "A-Pflegefachkraft (Krankenpflege) (A-81302-018)" + }, + { + "firstname": "Burkhild", + "key": 7796, + "name": "Hertzler", + "type": "A-Pflegefachkraft (Krankenpflege) (A-81302-018)" + }, + { + "firstname": "Karena", + "key": 7835, + "name": "Driggers", + "type": "A-Pflegefachkraft (Krankenpflege) (A-81302-018)" + }, + { + "firstname": "Janett", + "key": 7877, + "name": "Staggs", + "type": "A-Pflegefachkraft (Krankenpflege) (A-81302-018)" + }, + { + "firstname": "Irma", + "key": 7919, + "name": "Weathers", + "type": "Gesundheits- und Krankenpfleger/in (81302-005)" + } + ] +} diff --git a/legacy/cases/77/02_2025/free_shifts_and_vacation_days.json b/legacy/cases/77/02_2025/free_shifts_and_vacation_days.json new file mode 100644 index 00000000..f291b341 --- /dev/null +++ b/legacy/cases/77/02_2025/free_shifts_and_vacation_days.json @@ -0,0 +1,1484 @@ +{ + "employees": [ + { + "firstname": "Adriane", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28 + ], + "key": 790, + "name": "Mccomas", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Janett", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28 + ], + "key": 791, + "name": "Branz", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Liselotte", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28 + ], + "key": 839, + "name": "S\u00e4uffert", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Mirjam", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28 + ], + "key": 843, + "name": "Flanagan", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Elia", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28 + ], + "key": 844, + "name": "Greenman", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Silvia", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28 + ], + "key": 914, + "name": "Harkins", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Ingbert", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28 + ], + "key": 917, + "name": "Catoe", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Jenny", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28 + ], + "key": 921, + "name": "Keese", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "B\u00e4rbl", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28 + ], + "key": 924, + "name": "Merriweather", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Lina", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28 + ], + "key": 925, + "name": "Farniok", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Margaritt", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28 + ], + "key": 927, + "name": "Mittrach", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Daniele", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28 + ], + "key": 928, + "name": "Wunderlich", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Constance", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28 + ], + "key": 1230, + "name": "Palacio", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Kersten", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28 + ], + "key": 2932, + "name": "Devers", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Renilde", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28 + ], + "key": 2963, + "name": "Hoots", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Elgine", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28 + ], + "key": 3566, + "name": "Seligman", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Eike", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28 + ], + "key": 3868, + "name": "Vanfleet", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Henni", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28 + ], + "key": 5367, + "name": "Donis", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Sturmius", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16 + ], + "key": 5652, + "name": "Sherrard", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Liliane", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16 + ], + "key": 5656, + "name": "Bow", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Swantje", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16 + ], + "key": 5668, + "name": "Greer", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Augustin", + "forbidden_days": [], + "key": 5920, + "name": "Carreras", + "planned_shifts": [ + [ + 1, + "F2_" + ], + [ + 2, + "F2_" + ], + [ + 3, + "F2_" + ], + [ + 4, + "F2_" + ], + [ + 5, + "F2_" + ], + [ + 6, + "F2_" + ], + [ + 10, + "F2_" + ], + [ + 11, + "F2_" + ], + [ + 12, + "F2_" + ], + [ + 13, + "F2_" + ], + [ + 14, + "F2_" + ], + [ + 15, + "F2_" + ], + [ + 16, + "F2_" + ], + [ + 17, + "F2_" + ], + [ + 19, + "F2_" + ], + [ + 20, + "F2_" + ], + [ + 21, + "F2_" + ], + [ + 22, + "F2_" + ], + [ + 23, + "F2_" + ], + [ + 24, + "F2_" + ] + ], + "vacation_days": [] + }, + { + "firstname": "Annette", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28 + ], + "key": 6259, + "name": "Trovato", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Svetlana", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 6, + 7, + 8, + 9, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28 + ], + "key": 6316, + "name": "Montejano", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Uschi", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28 + ], + "key": 6318, + "name": "Gamblin", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Heinz", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28 + ], + "key": 6475, + "name": "Binford", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Edwin", + "forbidden_days": [ + 1, + 2, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28 + ], + "key": 6616, + "name": "Avelar", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Christfri", + "forbidden_days": [], + "key": 6677, + "name": "Fullerton", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Liebhardt", + "forbidden_days": [ + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28 + ], + "key": 6762, + "name": "Deshields", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Trude", + "forbidden_days": [], + "key": 6836, + "name": "Valentino", + "planned_shifts": [ + [ + 1, + "F2_" + ], + [ + 2, + "F2_" + ], + [ + 3, + "F2_" + ], + [ + 4, + "F2_" + ], + [ + 8, + "F2_" + ], + [ + 9, + "F2_" + ], + [ + 10, + "F2_" + ], + [ + 11, + "F2_" + ], + [ + 12, + "F2_" + ], + [ + 13, + "F2_" + ], + [ + 18, + "F2_" + ], + [ + 19, + "F2_" + ], + [ + 20, + "F2_" + ], + [ + 21, + "F2_" + ], + [ + 22, + "F2_" + ] + ], + "vacation_days": [] + }, + { + "firstname": "Annelene", + "forbidden_days": [], + "key": 6928, + "name": "Izzo", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Annemargr", + "forbidden_days": [ + 1, + 2, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28 + ], + "key": 7490, + "name": "Waldinger", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Ludger", + "forbidden_days": [], + "key": 7603, + "name": "Roberson", + "planned_shifts": [ + [ + 10, + "F2_" + ], + [ + 11, + "F2_" + ], + [ + 12, + "F2_" + ], + [ + 13, + "F2_" + ], + [ + 14, + "F2_" + ], + [ + 15, + "F2_" + ], + [ + 16, + "F2_" + ] + ], + "vacation_days": [] + }, + { + "firstname": "Sieghardt", + "forbidden_days": [ + 2, + 3, + 4, + 5, + 6, + 7, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28 + ], + "key": 7741, + "name": "Tharp", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Kilian", + "forbidden_days": [], + "key": 7752, + "name": "Rodriques", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Julia", + "forbidden_days": [ + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28 + ], + "key": 7770, + "name": "Yeh", + "planned_shifts": [], + "vacation_days": [ + 27, + 28 + ] + }, + { + "firstname": "Burkhild", + "forbidden_days": [ + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28 + ], + "key": 7796, + "name": "Hertzler", + "planned_shifts": [], + "vacation_days": [ + 27, + 28 + ] + }, + { + "firstname": "Karena", + "forbidden_days": [ + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28 + ], + "key": 7835, + "name": "Driggers", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Janett", + "forbidden_days": [ + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28 + ], + "key": 7877, + "name": "Staggs", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Irma", + "forbidden_days": [], + "key": 7919, + "name": "Weathers", + "planned_shifts": [ + [ + 1, + "S2_" + ], + [ + 2, + "S2_" + ], + [ + 3, + "S2_" + ], + [ + 4, + "S2_" + ], + [ + 5, + "S2_" + ], + [ + 6, + "S2_" + ], + [ + 7, + "S2_" + ], + [ + 8, + "S2_" + ], + [ + 13, + "S2_" + ], + [ + 14, + "S2_" + ], + [ + 15, + "S2_" + ], + [ + 16, + "S2_" + ], + [ + 17, + "S2_" + ], + [ + 18, + "S2_" + ], + [ + 19, + "S2_" + ], + [ + 21, + "S2_" + ], + [ + 24, + "S2_" + ], + [ + 25, + "S2_" + ], + [ + 26, + "S2_" + ], + [ + 27, + "S2_" + ] + ], + "vacation_days": [] + } + ] +} diff --git a/legacy/cases/77/02_2025/general_settings.json b/legacy/cases/77/02_2025/general_settings.json new file mode 100644 index 00000000..122e86a1 --- /dev/null +++ b/legacy/cases/77/02_2025/general_settings.json @@ -0,0 +1,18 @@ +{ + "SHIFT_NAME_TO_INDEX": { + "Early": 0, + "Late": 1, + "Night": 2 + }, + "qualifications": { + "2963": [ + "rounds" + ], + "3868": [ + "rounds" + ], + "791": [ + "rounds" + ] + } +} diff --git a/legacy/cases/77/02_2025/minimal_number_of_staff.json b/legacy/cases/77/02_2025/minimal_number_of_staff.json new file mode 100644 index 00000000..ce9daf74 --- /dev/null +++ b/legacy/cases/77/02_2025/minimal_number_of_staff.json @@ -0,0 +1,113 @@ +{ + "Azubi": { + "Di": { + "F": 1, + "N": 0, + "S": 1 + }, + "Do": { + "F": 1, + "N": 0, + "S": 1 + }, + "Fr": { + "F": 1, + "N": 0, + "S": 1 + }, + "Mi": { + "F": 1, + "N": 0, + "S": 1 + }, + "Mo": { + "F": 1, + "N": 0, + "S": 1 + }, + "Sa": { + "F": 1, + "N": 0, + "S": 1 + }, + "So": { + "F": 1, + "N": 0, + "S": 1 + } + }, + "Fachkraft": { + "Di": { + "F": 3, + "N": 2, + "S": 2 + }, + "Do": { + "F": 3, + "N": 2, + "S": 2 + }, + "Fr": { + "F": 3, + "N": 2, + "S": 2 + }, + "Mi": { + "F": 4, + "N": 2, + "S": 2 + }, + "Mo": { + "F": 3, + "N": 2, + "S": 2 + }, + "Sa": { + "F": 2, + "N": 1, + "S": 2 + }, + "So": { + "F": 2, + "N": 1, + "S": 2 + } + }, + "Hilfskraft": { + "Di": { + "F": 2, + "N": 0, + "S": 2 + }, + "Do": { + "F": 2, + "N": 0, + "S": 2 + }, + "Fr": { + "F": 2, + "N": 0, + "S": 2 + }, + "Mi": { + "F": 2, + "N": 0, + "S": 2 + }, + "Mo": { + "F": 2, + "N": 0, + "S": 2 + }, + "Sa": { + "F": 2, + "N": 1, + "S": 2 + }, + "So": { + "F": 2, + "N": 1, + "S": 2 + } + } +} diff --git a/legacy/cases/77/02_2025/shift_information.json b/legacy/cases/77/02_2025/shift_information.json new file mode 100644 index 00000000..d2215694 --- /dev/null +++ b/legacy/cases/77/02_2025/shift_information.json @@ -0,0 +1,47 @@ +[ + { + "break_duration": 0.0, + "end_time": "2000-01-01T14:00:00", + "shift_duration": 360.0, + "shift_id": "1406", + "shift_name": "Z60", + "start_time": "2000-01-01T08:00:00", + "working_minutes": 360.0 + }, + { + "break_duration": 30.0, + "end_time": "2000-01-01T16:10:00", + "shift_duration": 490.0, + "shift_id": "2906", + "shift_name": "T75_", + "start_time": "2000-01-01T08:00:00", + "working_minutes": 460.0 + }, + { + "break_duration": 30.0, + "end_time": "2000-01-01T14:10:00", + "shift_duration": 490.0, + "shift_id": "2939", + "shift_name": "F2_", + "start_time": "2000-01-01T06:00:00", + "working_minutes": 460.0 + }, + { + "break_duration": 30.0, + "end_time": "2000-01-01T21:00:00", + "shift_duration": 490.0, + "shift_id": "2947", + "shift_name": "S2_", + "start_time": "2000-01-01T12:50:00", + "working_minutes": 460.0 + }, + { + "break_duration": 45.0, + "end_time": "2000-01-02T06:30:00", + "shift_duration": 610.0, + "shift_id": "2953", + "shift_name": "N2_", + "start_time": "2000-01-01T20:20:00", + "working_minutes": 565.0 + } +] diff --git a/legacy/cases/77/02_2025/target_working_minutes.json b/legacy/cases/77/02_2025/target_working_minutes.json new file mode 100644 index 00000000..3bd615e9 --- /dev/null +++ b/legacy/cases/77/02_2025/target_working_minutes.json @@ -0,0 +1,158 @@ +{ + "employees": [ + { + "actual": 4620.0, + "firstname": "Sturmius", + "key": 5652, + "name": "Sherrard", + "target": 9240.0 + }, + { + "actual": 4620.0, + "firstname": "Liliane", + "key": 5656, + "name": "Bow", + "target": 9240.0 + }, + { + "actual": 4620.0, + "firstname": "Swantje", + "key": 5668, + "name": "Greer", + "target": 9240.0 + }, + { + "actual": 9200.0, + "firstname": "Augustin", + "key": 5920, + "name": "Carreras", + "target": 9240.0 + }, + { + "actual": 0.0, + "firstname": "Annette", + "key": 6259, + "name": "Trovato", + "target": 8316.0 + }, + { + "actual": 0.0, + "firstname": "Svetlana", + "key": 6316, + "name": "Montejano", + "target": 2.0 + }, + { + "actual": 0.0, + "firstname": "Uschi", + "key": 6318, + "name": "Gamblin", + "target": 9240.0 + }, + { + "actual": 0.0, + "firstname": "Heinz", + "key": 6475, + "name": "Binford", + "target": 2.0 + }, + { + "actual": 0.0, + "firstname": "Edwin", + "key": 6616, + "name": "Avelar", + "target": 9240.0 + }, + { + "actual": 0.0, + "firstname": "Christfri", + "key": 6677, + "name": "Fullerton", + "target": 9240.0 + }, + { + "actual": 0.0, + "firstname": "Liebhardt", + "key": 6762, + "name": "Deshields", + "target": 9240.0 + }, + { + "actual": 6900.0, + "firstname": "Trude", + "key": 6836, + "name": "Valentino", + "target": 9240.0 + }, + { + "actual": 0.0, + "firstname": "Annelene", + "key": 6928, + "name": "Izzo", + "target": 9360.0 + }, + { + "actual": 0.0, + "firstname": "Annemargr", + "key": 7490, + "name": "Waldinger", + "target": 8323.0 + }, + { + "actual": 3220.0, + "firstname": "Ludger", + "key": 7603, + "name": "Roberson", + "target": 9240.0 + }, + { + "actual": 0.0, + "firstname": "Sieghardt", + "key": 7741, + "name": "Tharp", + "target": 960.0 + }, + { + "actual": 0.0, + "firstname": "Kilian", + "key": 7752, + "name": "Rodriques", + "target": 9240.0 + }, + { + "actual": 9240.0, + "firstname": "Julia", + "key": 7770, + "name": "Yeh", + "target": 9240.0 + }, + { + "actual": 9240.0, + "firstname": "Burkhild", + "key": 7796, + "name": "Hertzler", + "target": 9240.0 + }, + { + "actual": 0.0, + "firstname": "Karena", + "key": 7835, + "name": "Driggers", + "target": 9240.0 + }, + { + "actual": 0.0, + "firstname": "Janett", + "key": 7877, + "name": "Staggs", + "target": 9240.0 + }, + { + "actual": 9200.0, + "firstname": "Irma", + "key": 7919, + "name": "Weathers", + "target": 9240.0 + } + ] +} diff --git a/legacy/cases/77/02_2025/web/jobs.json b/legacy/cases/77/02_2025/web/jobs.json new file mode 100644 index 00000000..db195510 --- /dev/null +++ b/legacy/cases/77/02_2025/web/jobs.json @@ -0,0 +1,116 @@ +{ + "jobs": [ + { + "caseId": 77, + "completedAt": "2026-05-05T15:07:43.342Z", + "consoleOutput": "Exporting planning data for planning unit 77 from 2024-12-01 to 2024-12-31.\n2026-05-05 17:07:42,035 - INFO - Deleting file or folder: /Users/tom/University/StaffSchedulingLab/StaffScheduling/cases/77/12_2024/web/jobs.json\n2026-05-05 17:07:42,036 - INFO - copied static JSON file: wishes_and_blocked.json\n2026-05-05 17:07:42,036 - INFO - copied static JSON file: minimal_number_of_staff.json\n2026-05-05 17:07:42,036 - INFO - copied static JSON file: employee_types.json\n2026-05-05 17:07:42,037 - INFO - copied static JSON file: general_settings.json\n2026-05-05 17:07:42,554 - INFO - \u2705 Export abgeschlossen \u2013 shift_information.json erstellt\n2026-05-05 17:07:42,628 - INFO - \u2705 Export abgeschlossen \u2013 employees.json erstellt\n2026-05-05 17:07:42,712 - INFO - \u2705 Export abgeschlossen \u2013 target_working_minutes.json erstellt\n2026-05-05 17:07:42,862 - INFO - \u2705 Export abgeschlossen \u2013 worked_sundays.json erstellt\n2026-05-05 17:07:43,339 - INFO - \u2705 Export abgeschlossen \u2013 free_shifts_and_vacation_days.json erstellt\n", + "createdAt": "2026-05-05T15:07:42.032Z", + "duration": 1309, + "id": "79f2d02a-13f3-438a-b37f-149e5ca2b1dc", + "params": { + "end": "2024-12-31", + "start": "2024-12-01", + "unit": 77 + }, + "status": "completed", + "type": "fetch" + }, + { + "caseId": 77, + "completedAt": "2026-05-05T15:07:33.481Z", + "createdAt": "2026-05-05T15:07:33.096Z", + "duration": 385, + "error": "HTTP 500: {\"detail\":\"single positional indexer is out-of-bounds\"}", + "id": "f7bd0bd9-425c-4c93-adfe-ea96fab59ee0", + "params": { + "end": "2024-09-30", + "start": "2024-09-01", + "unit": 77 + }, + "status": "failed", + "type": "fetch" + }, + { + "caseId": 77, + "completedAt": "2026-05-05T15:07:28.511Z", + "createdAt": "2026-05-05T15:07:27.973Z", + "duration": 538, + "error": "HTTP 500: {\"detail\":\"single positional indexer is out-of-bounds\"}", + "id": "7dee7877-f5fe-4d76-b595-adb2f87c3197", + "params": { + "end": "2024-09-30", + "start": "2024-09-01", + "unit": 77 + }, + "status": "failed", + "type": "fetch" + }, + { + "caseId": 77, + "completedAt": "2026-05-05T15:07:22.787Z", + "createdAt": "2026-05-05T15:07:21.871Z", + "duration": 916, + "error": "HTTP 500: {\"detail\":\"single positional indexer is out-of-bounds\"}", + "id": "c1176c99-8343-4dab-940a-1d1a37e54d82", + "params": { + "end": "2024-09-30", + "start": "2024-09-01", + "unit": 77 + }, + "status": "failed", + "type": "fetch" + }, + { + "caseId": 77, + "completedAt": "2026-05-05T15:05:50.515Z", + "createdAt": "2026-05-05T15:05:50.197Z", + "duration": 318, + "error": "HTTP 500: {\"detail\":\"single positional indexer is out-of-bounds\"}", + "id": "bcf72211-561b-4597-9e98-d30509b797af", + "params": { + "end": "2024-09-30", + "start": "2024-09-01", + "unit": 77 + }, + "status": "failed", + "type": "fetch" + }, + { + "caseId": 77, + "completedAt": "2026-05-05T15:05:45.444Z", + "createdAt": "2026-05-05T15:05:45.024Z", + "duration": 420, + "error": "HTTP 500: {\"detail\":\"single positional indexer is out-of-bounds\"}", + "id": "6e24842d-9242-4bcd-be16-528b0de9ea91", + "params": { + "end": "2024-09-30", + "start": "2024-09-01", + "unit": 77 + }, + "status": "failed", + "type": "fetch" + }, + { + "caseId": 77, + "completedAt": "2026-05-05T14:49:46.645Z", + "consoleOutput": "\nStatistics\n - conflicts : 0\n - branches : 0\n - wall time : 0.487368 s\n - objective value: 0.0\n - status : INFEASIBLE\n - objective value: 0.0\n - info : \n2026-05-05 16:49:32,971 - INFO - Weights file not found \u2013 using default weights instead.\n2026-05-05 16:49:32,974 - INFO - Minimizing Hidden Employee Count Phase 1\n2026-05-05 16:49:32,974 - INFO - Trying to solve with {'Azubi': 0, 'Fachkraft': 0, 'Hilfskraft': 0}\n2026-05-05 16:49:33,365 - INFO - Wall time: 0.028208017349243164\n2026-05-05 16:49:33,368 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 16:49:33,368 - INFO - Trying to solve with {'Azubi': 5, 'Fachkraft': 5, 'Hilfskraft': 5}\n2026-05-05 16:49:34,145 - INFO - Wall time: 0.055181026458740234\n2026-05-05 16:49:34,155 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 16:49:34,156 - INFO - Trying to solve with {'Azubi': 10, 'Fachkraft': 10, 'Hilfskraft': 10}\n2026-05-05 16:49:34,875 - INFO - Wall time: 0.0816187858581543\n2026-05-05 16:49:34,880 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 16:49:34,880 - INFO - Trying to solve with {'Azubi': 15, 'Fachkraft': 15, 'Hilfskraft': 15}\n2026-05-05 16:49:35,754 - INFO - Wall time: 0.07904887199401855\n2026-05-05 16:49:35,773 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 16:49:35,773 - INFO - Trying to solve with {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 20}\n2026-05-05 16:49:36,831 - INFO - Wall time: 0.10423707962036133\n2026-05-05 16:49:36,853 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 16:49:36,853 - INFO - Hidden Employee Upper Bound: {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 20}\n\n2026-05-05 16:49:36,853 - INFO - Minimizing Hidden Employee Count Phase 2\n2026-05-05 16:49:36,853 - INFO - Trying to solve with {'Azubi': 19, 'Fachkraft': 20, 'Hilfskraft': 20}\n2026-05-05 16:49:37,943 - INFO - Wall time: 0.08040714263916016\n2026-05-05 16:49:37,968 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 16:49:37,968 - INFO - Trying to solve with {'Azubi': 20, 'Fachkraft': 19, 'Hilfskraft': 20}\n2026-05-05 16:49:38,974 - INFO - Wall time: 0.11797595024108887\n2026-05-05 16:49:38,996 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 16:49:38,997 - INFO - Trying to solve with {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 19}\n2026-05-05 16:49:40,092 - INFO - Wall time: 0.07974696159362793\n2026-05-05 16:49:40,106 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 16:49:40,106 - INFO - Hidden Employee Tight Bound: {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 20}\n\n2026-05-05 16:49:40,106 - INFO - General information:\n2026-05-05 16:49:40,106 - INFO - - planning unit: 77\n2026-05-05 16:49:40,106 - INFO - - start date: 2025-02-01\n2026-05-05 16:49:40,106 - INFO - - end date: 2025-02-28\n2026-05-05 16:49:40,106 - INFO - - number of employees: 100\n2026-05-05 16:49:40,106 - INFO - - number of days: 28\n2026-05-05 16:49:40,106 - INFO - - number of shifts: 8\n2026-05-05 16:49:43,559 - INFO - Found 4 complete weekends in the planning period\n2026-05-05 16:49:45,720 - INFO - Solving model...\n2026-05-05 16:49:45,721 - INFO - - number of variables: 25200\n2026-05-05 16:49:45,721 - INFO - - number of objectives: 10\n2026-05-05 16:49:45,721 - INFO - - number of constraints: 9\n2026-05-05 16:49:45,721 - INFO - Constraints:\n2026-05-05 16:49:45,721 - INFO - - Free Day After Night Shift Phase\n2026-05-05 16:49:45,721 - INFO - - Minimum Rest Time\n2026-05-05 16:49:45,721 - INFO - - Min Staffing\n2026-05-05 16:49:45,721 - INFO - - Rounds In Early Shift\n2026-05-05 16:49:45,721 - INFO - - One Shift Per Day\n2026-05-05 16:49:45,721 - INFO - - Target Working Time\n2026-05-05 16:49:45,721 - INFO - - Vacation Days And Shifts\n2026-05-05 16:49:45,721 - INFO - - Hierarchy Of Intermediate Shifts\n2026-05-05 16:49:45,721 - INFO - - Planned Shifts\n2026-05-05 16:49:45,721 - INFO - Objectives:\n2026-05-05 16:49:45,721 - INFO - - Free Days Near Weekend (weight: 2)\n2026-05-05 16:49:45,721 - INFO - - Minimize Consecutive Night Shifts (weight: 2)\n2026-05-05 16:49:45,721 - INFO - - Minimize Hidden Employees (weight: 100)\n2026-05-05 16:49:45,721 - INFO - - Minimize Overtime (weight: 4)\n2026-05-05 16:49:45,721 - INFO - - Not Too Many Consecutive Days (weight: 1)\n2026-05-05 16:49:45,721 - INFO - - Rotate Shifts Forward (weight: 1)\n2026-05-05 16:49:45,721 - INFO - - Maximize Employee Wishes (weight: 3)\n2026-05-05 16:49:45,721 - INFO - - Free Day After Night Shift Phase (weight: 3)\n2026-05-05 16:49:45,721 - INFO - - Every Second Weekend Free (weight: 1)\n2026-05-05 16:49:45,721 - INFO - - Preferred Block Length (weight: 1)\n2026-05-05 16:49:45,872 - INFO - Timeout set to 600 seconds\n2026-05-05 16:49:46,565 - INFO - Solving completed in 0.69 seconds\n", + "createdAt": "2026-05-05T14:49:32.953Z", + "duration": 13689, + "error": "2026-05-05 16:49:32,971 - INFO - Weights file not found \u2013 using default weights instead.\n2026-05-05 16:49:32,974 - INFO - Minimizing Hidden Employee Count Phase 1\n2026-05-05 16:49:32,974 - INFO - Trying to solve with {'Azubi': 0, 'Fachkraft': 0, 'Hilfskraft': 0}\n2026-05-05 16:49:33,365 - INFO - Wall time: 0.028208017349243164\n2026-05-05 16:49:33,368 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 16:49:33,368 - INFO - Trying to solve with {'Azubi': 5, 'Fachkraft': 5, 'Hilfskraft': 5}\n2026-05-05 16:49:34,145 - INFO - Wall time: 0.055181026458740234\n2026-05-05 16:49:34,155 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 16:49:34,156 - INFO - Trying to solve with {'Azubi': 10, 'Fachkraft': 10, 'Hilfskraft': 10}\n2026-05-05 16:49:34,875 - INFO - Wall time: 0.0816187858581543\n2026-05-05 16:49:34,880 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 16:49:34,880 - INFO - Trying to solve with {'Azubi': 15, 'Fachkraft': 15, 'Hilfskraft': 15}\n2026-05-05 16:49:35,754 - INFO - Wall time: 0.07904887199401855\n2026-05-05 16:49:35,773 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 16:49:35,773 - INFO - Trying to solve with {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 20}\n2026-05-05 16:49:36,831 - INFO - Wall time: 0.10423707962036133\n2026-05-05 16:49:36,853 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 16:49:36,853 - INFO - Hidden Employee Upper Bound: {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 20}\n\n2026-05-05 16:49:36,853 - INFO - Minimizing Hidden Employee Count Phase 2\n2026-05-05 16:49:36,853 - INFO - Trying to solve with {'Azubi': 19, 'Fachkraft': 20, 'Hilfskraft': 20}\n2026-05-05 16:49:37,943 - INFO - Wall time: 0.08040714263916016\n2026-05-05 16:49:37,968 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 16:49:37,968 - INFO - Trying to solve with {'Azubi': 20, 'Fachkraft': 19, 'Hilfskraft': 20}\n2026-05-05 16:49:38,974 - INFO - Wall time: 0.11797595024108887\n2026-05-05 16:49:38,996 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 16:49:38,997 - INFO - Trying to solve with {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 19}\n2026-05-05 16:49:40,092 - INFO - Wall time: 0.07974696159362793\n2026-05-05 16:49:40,106 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 16:49:40,106 - INFO - Hidden Employee Tight Bound: {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 20}\n\n2026-05-05 16:49:40,106 - INFO - General information:\n2026-05-05 16:49:40,106 - INFO - - planning unit: 77\n2026-05-05 16:49:40,106 - INFO - - start date: 2025-02-01\n2026-05-05 16:49:40,106 - INFO - - end date: 2025-02-28\n2026-05-05 16:49:40,106 - INFO - - number of employees: 100\n2026-05-05 16:49:40,106 - INFO - - number of days: 28\n2026-05-05 16:49:40,106 - INFO - - number of shifts: 8\n2026-05-05 16:49:43,559 - INFO - Found 4 complete weekends in the planning period\n2026-05-05 16:49:45,720 - INFO - Solving model...\n2026-05-05 16:49:45,721 - INFO - - number of variables: 25200\n2026-05-05 16:49:45,721 - INFO - - number of objectives: 10\n2026-05-05 16:49:45,721 - INFO - - number of constraints: 9\n2026-05-05 16:49:45,721 - INFO - Constraints:\n2026-05-05 16:49:45,721 - INFO - - Free Day After Night Shift Phase\n2026-05-05 16:49:45,721 - INFO - - Minimum Rest Time\n2026-05-05 16:49:45,721 - INFO - - Min Staffing\n2026-05-05 16:49:45,721 - INFO - - Rounds In Early Shift\n2026-05-05 16:49:45,721 - INFO - - One Shift Per Day\n2026-05-05 16:49:45,721 - INFO - - Target Working Time\n2026-05-05 16:49:45,721 - INFO - - Vacation Days And Shifts\n2026-05-05 16:49:45,721 - INFO - - Hierarchy Of Intermediate Shifts\n2026-05-05 16:49:45,721 - INFO - - Planned Shifts\n2026-05-05 16:49:45,721 - INFO - Objectives:\n2026-05-05 16:49:45,721 - INFO - - Free Days Near Weekend (weight: 2)\n2026-05-05 16:49:45,721 - INFO - - Minimize Consecutive Night Shifts (weight: 2)\n2026-05-05 16:49:45,721 - INFO - - Minimize Hidden Employees (weight: 100)\n2026-05-05 16:49:45,721 - INFO - - Minimize Overtime (weight: 4)\n2026-05-05 16:49:45,721 - INFO - - Not Too Many Consecutive Days (weight: 1)\n2026-05-05 16:49:45,721 - INFO - - Rotate Shifts Forward (weight: 1)\n2026-05-05 16:49:45,721 - INFO - - Maximize Employee Wishes (weight: 3)\n2026-05-05 16:49:45,721 - INFO - - Free Day After Night Shift Phase (weight: 3)\n2026-05-05 16:49:45,721 - INFO - - Every Second Weekend Free (weight: 1)\n2026-05-05 16:49:45,721 - INFO - - Preferred Block Length (weight: 1)\n2026-05-05 16:49:45,872 - INFO - Timeout set to 600 seconds\n2026-05-05 16:49:46,565 - INFO - Solving completed in 0.69 seconds\n", + "id": "1475e654-4864-4108-b8b9-928e335f354f", + "metadata": { + "expectedSolutions": 1, + "feasibleSolutions": [], + "solutionsGenerated": 0 + }, + "params": { + "end": "2025-02-28", + "start": "2025-02-01", + "timeout": 600, + "unit": 77 + }, + "status": "failed", + "type": "solve" + } + ] +} diff --git a/legacy/cases/77/02_2025/wishes_and_blocked.json b/legacy/cases/77/02_2025/wishes_and_blocked.json new file mode 100644 index 00000000..a2f63b3e --- /dev/null +++ b/legacy/cases/77/02_2025/wishes_and_blocked.json @@ -0,0 +1,3 @@ +{ + "employees": [] +} diff --git a/legacy/cases/77/02_2025/worked_sundays.json b/legacy/cases/77/02_2025/worked_sundays.json new file mode 100644 index 00000000..a9d62228 --- /dev/null +++ b/legacy/cases/77/02_2025/worked_sundays.json @@ -0,0 +1,28 @@ +{ + "worked_sundays": [ + { + "firstname": "Augustin", + "key": 5920, + "name": "Carreras", + "worked_sundays": 3 + }, + { + "firstname": "Trude", + "key": 6836, + "name": "Valentino", + "worked_sundays": 2 + }, + { + "firstname": "Irma", + "key": 7919, + "name": "Weathers", + "worked_sundays": 2 + }, + { + "firstname": "Ludger", + "key": 7603, + "name": "Roberson", + "worked_sundays": 1 + } + ] +} diff --git a/legacy/cases/77/09_2024/employee_types.json b/legacy/cases/77/09_2024/employee_types.json new file mode 100644 index 00000000..1981e587 --- /dev/null +++ b/legacy/cases/77/09_2024/employee_types.json @@ -0,0 +1,525 @@ +{ + "Azubi": [ + "A-Altenpflegehelfer/in (A-82101-002)", + "A-Altenpfleger/in (A-82102-002)", + "A-An\u00e4sthesietechnisch/r Assistent/in (A-81332-001)", + "A-CTA (Chirurgisch-technische/r Assistent/in) (A-81332-005)", + "A-Fachkaufkann/-frau-Verwaltung im Gesundheitswesen (A-73223-015)", + "A-Fachkraft-Lagerlogistik (A-51312-005)", + "A-Gesundheits- und Kinderkrankenpfleger/in (A-81302-003)", + "A-Gesundheits- und Krankenpfleger/in (A-81302-005)", + "A-Hebamme/Entbindungspfleger (A-81353-003)", + "A-Hilfskoch/-k\u00f6chin (A-29302-018)", + "A-Informatiker/in (Weiterbildung) (A-43103-008)", + "A-Kaufmann/-frau-Gesundheitswesen (A-73222-007)", + "A-Kinderkrankenschwester/-pfleger (A-81302-007)", + "A-Koch/-K\u00f6chin (29302-024)", + "A-Krankenschwester/-pfleger (A-81302-008)", + "A-Medizinische/r Fachangestellte/r (A-81102-004)", + "A-Medizinisch-technisch/r Radiologieassistent/in (A-81232-004)", + "A-Notfallsanit\u00e4ter A-31342-005", + "A-OTA (Operationstechnische/r Assistent/in) (A-81332-009)", + "A-Pflegeassistent/in (A-81302-014)", + "A-Pflegefachkraft - Gesundheits- und Krankenpfleger (81302-015)", + "A-Pflegefachkraft (Altenpflege) (A-81302-019)", + "A-Pflegefachkraft (Krankenpflege) (A-81302-018)", + "A-Pflegefachkraft-Kinderkrankenpflege (A-81302-016)", + "A-Pflegefachmann/-frau (A-81302-028)", + "A-Stationshelfer/in - Krankenpflege (A-81301-017)" + ], + "Fachkraft": [ + "Abteilungsleiter/in (71394-003)", + "Alleinsekret\u00e4r/in (71402-004)", + "Allgemeinarzt/-\u00e4rztin (81404-001)", + "Allgemeinchirurg/in (81434-001)", + "Altenpfleger/in (82102-002)", + "Ambulante Krankenschwester/-pfleger (81302-001)", + "Ambulanzpfleger/schwester (81382-001)", + "An\u00e4sthesietechnische/r Assistent/in (81332-001)", + "An\u00e4sthesist/in (81454-002)", + "Archivar/in (73314-001)", + "Archivfachkraft (73312-004)", + "Arzt/\u00c4rztin - Allgemeinmedizin (81404-003)", + "Arzt/\u00c4rztin - An\u00e4sthesiologie und Intensivtherapie (81454-003)", + "Arzt/\u00c4rztin - Chirurgie (81434-002)", + "Arzt/\u00c4rztin - Hygiene (81484-006)", + "Arzt/\u00c4rztin - Innere Medizin (81424-001)", + "Arzt/\u00c4rztin - Kinder- und Jugendpsychiatrie (81464-001)", + "Arzt/\u00c4rztin - klinische Strahlenphysik (81234-001)", + "Arzt/\u00c4rztin - Neurochirurgie (81434-004)", + "Arzt/\u00c4rztin - Neurologie (81464-002)", + "Arzt/\u00c4rztin - Orthop\u00e4die (81434-005)", + "Arzt/\u00c4rztin - Radiologie (81234-003)", + "Arzt/\u00c4rztin - Traumatologie und Orthop\u00e4die (81434-006)", + "Arzt/\u00c4rztin (81404-002)", + "\u00c4rztliche/r Direktor/in (Humanarzt/-\u00e4rztin) (81494-001)", + "\u00c4rztliche/r Leiter/in (81494-002)", + "Arztsekret\u00e4r/in (73222-001)", + "Assistent/in - Gesch\u00e4ftsleitung (71403-001)", + "Assistent/in - Gesundheitswesen (73222-003)", + "Assistent/in - Operationstechnik (81332-003)", + "Assistent/in - Rechnungswesen (72213-006)", + "Assistenzarzt/-\u00e4rztin - Kinder-/Jugendpsychiatrie und -psychologie (81464-006)", + "Assistenzarzt/-\u00e4rztin (81404-004)", + "Assistenzarzt/-\u00e4rztin (Uni) - An\u00e4sthesiologie (81454-004)", + "Assistenzarzt/-\u00e4rztin (Uni) - Chirurgie (81434-007)", + "Assistenzarzt/-\u00e4rztin (Uni) - Innere Medizin (81424-004)", + "Assistenzarzt/-\u00e4rztin (Uni) - Kinder-/Jugendmedizin (81414-002)", + "Assistenzarzt/-\u00e4rztin (Uni) - Neurologie (81464-009)", + "ATA (An\u00e4sthesietechnische/r Assistent/in) (81332-004)", + "Augenoptikermeister/in (82593-001)", + "Ausbilder Berufsbildungswerk (84223-013)", + "Ausbilder Berufsf\u00f6rderungswerk", + "Bachelor of Arts - Soziale Arbeit/Soziale Dienste (83123-003)", + "Bachelor of Arts - Sportwissenschaft (84503-002)", + "Bankkaufmann/-frau (72112-004)", + "Beikoch/-k\u00f6chin (29302-005)", + "Bereichsleiter/in (71394-006)", + "Berufsp\u00e4dagoge/-p\u00e4dagogin (84224-003)", + "Besch\u00e4ftigungstherapeut/in (81723-004)", + "Betriebselektriker/in (26252-007)", + "Betriebsg\u00e4rtner/in (12102-001)", + "Betriebstechniker/in (25103-011)", + "Betriebswirt/in (Weiterbildung) - Rechnungswesen (72213-014)", + "Bibliothekar/in (73324-004)", + "Bilanzbuchhalter/in (72213-016)", + "Buchhalter/in (72213-019)", + "B\u00fcrofachkraft (71402-010)", + "B\u00fcrokaufmann/-frau (71402-012)", + "Chefarzt/-\u00e4rztin (81494-003)", + "Chefsekret\u00e4r/in (71403-008)", + "Chirurg/in (81434-008)", + "Coach Erw.Bildung (84404-010)", + "Controller/in (72234-005)", + "Controllingleiter/in (72294-006)", + "CTA (Chirurgisch-technische/r Assistent/in) (81332-005)", + "Datenverarbeitungsfachmann/-frau (43103-005)", + "Dauernachtwache (Krankenschwester/-pfleger) (81302-002)", + "Diabetesberater/in (81783-002)", + "Di\u00e4tassistent/in (81762-001)", + "EDV Sachbearbeiter (41402-017)", + "EDV-Systemtechniker/in (26312-040)", + "EDV-Techniker/in (43103-007)", + "EEG-Assistent/in (81222-009)", + "Einkaufsleiter/in (61194-006)", + "Einkaufslogistiker/in (61113-015)", + "Einzelhandelskaufmann/-frau (62102-002)", + "Elektroanlagenelektroniker/in (26252-050)", + "Elektroinstallateur/in (26212-017)", + "Elektroniker/in - Energie- und Geb\u00e4udetechnik (26212-019)", + "Elektroniker/in - Ger\u00e4te und Systeme (26302-033)", + "Endoskopieschwester/-pfleger (81313-003)", + "Ergotherapeut/in (81723-006)", + "Ern\u00e4hrungsberater/in (82233-005)", + "Ern\u00e4hrungswissenschaftler/in (82284-003)", + "Erzieher/in - Heilp\u00e4dagogik (83133-002)", + "Erzieher/in (83112-006)", + "Erziehungswissenschaftler/in (91334-007)", + "EXAH-Pflegefachkraft (Altenpflege) (A-81302-019)", + "EXGK-Pflegefachkraft - Gesundheits- und Krankenpflege (A-81302-015)", + "EXKI-Pflegefachkraft.Kinderkrankenpflege (A-81302-016)", + "EXKP-Pflegefachkraft- (Krankenpflege) (A-81302-018)", + "EX-Pflegefachmann/- frau (A-81302-028)", + "Fachangestellte/r f\u00fcr B\u00fcrokommunikation (71402-021)", + "Facharzt Neurochirurgie", + "Facharzt Psychiatrie u. Psychotherapie", + "Facharzt/-\u00e4rztin - Allgemeinchirurgie (81434-027)", + "Facharzt/-\u00e4rztin - Allgemeine Chirurgie (81434-009)", + "Facharzt/-\u00e4rztin - Allgemeinmedizin (81404-005)", + "Facharzt/-\u00e4rztin - Allgemeinmedizin (Hausarzt/-\u00e4rztin) (81404-010)", + "Facharzt/-\u00e4rztin - An\u00e4sthesiologie (81454-005)", + "Facharzt/-\u00e4rztin - Frauenheilkunde und Geburtshilfe (81444-017)", + "Facharzt/-\u00e4rztin - Gef\u00e4\u00dfchirurgie (81434-010)", + "Facharzt/-\u00e4rztin - Hals-Nasen-Ohrenheilkunde (81444-018)", + "Facharzt/-\u00e4rztin - Innere Medizin (81424-006)", + "Facharzt/-\u00e4rztin - Innere Medizin u. Allgemeinm. (Hausarzt) (81404-006)", + "Facharzt/-\u00e4rztin - Innere Medizin u. H\u00e4matolog. u. Onkologie (81424-010)", + "Facharzt/-\u00e4rztin - Innere Medizin und Gastroenterologie (81424-009)", + "Facharzt/-\u00e4rztin - Innere Medizin und Kardiologie (81424-011)", + "Facharzt/-\u00e4rztin - Innere Medizin und Nephrologie (81424-012)", + "Facharzt/-\u00e4rztin - Innere Medizin und Pneumologie (81424-013)", + "Facharzt/-\u00e4rztin - Kinder- u. Jugendpsychiat. u. -psychoth. (81464-010)", + "Facharzt/-\u00e4rztin - Kinder- und Jugendmedizin (81414-003)", + "Facharzt/-\u00e4rztin - Neurologie (81464-012)", + "Facharzt/-\u00e4rztin - \u00d6ffentliches Gesundheitswesen (81484-028)", + "Facharzt/-\u00e4rztin - Orthop\u00e4die und Unfallchirurgie (81434-013)", + "Facharzt/-\u00e4rztin - P\u00e4diatrie (81414-005)", + "Facharzt/-\u00e4rztin - Plastische und \u00c4sthetische Chirurgie (81434-014)", + "Facharzt/-\u00e4rztin - Radiologie (81234-005)", + "Facharzt/-\u00e4rztin - Thoraxchirurgie (81434-015)", + "Facharzt/-\u00e4rztin - Unfallchirurgie (81434-016)", + "Facharzt/-\u00e4rztin - Viszeralchirurgie (81434-017)", + "Fachinformatiker/in - Systemintegration (43102-014)", + "Fachinformatiker/in (43102-013)", + "Fachkaufmann/-frau - Logistik (51623-005)", + "Fachkaufmann/-frau - Verwaltung im Gesundheitswesen (73223-015)", + "Fachkinderkrankenpfleger/in - Intensivpflege/An\u00e4sthesie (81323-005)", + "Fachkinderkrankenschwester/-pfleger - ambulante Pflege (81323-002)", + "Fachkinderkrankenschwester/-pfleger - P\u00e4diatrie und Intensivmedizin (81323-011)", + "Fachkinderkrankenschwester/-pfleger - Psychiatrie (81323-013)", + "Fachkraft - Altenpflege (82102-004)", + "Fachkraft - Factoring (72213-033)", + "Fachkraft - Lagerlogistik (51312-005)", + "Fachkraft - Lagerwirtschaft (51312-007)", + "Fachkraft - Sozialarbeit (83123-004)", + "Fachkrankenpfleger/in - Nephrologie", + "Fachkrankenpfleger/in - Notfallpflege (81313-059)", + "Fachkrankenpfleger/in - Operations-/Endoskopiedienst (81313-014)", + "Fachkrankenschwester/-pfleger - Endoskopie (81313-006)", + "Fachkrankenschwester/-pfleger - Intensivmedizin und An\u00e4sthesie (81313-008)", + "Fachkrankenschwester/-pfleger - Intensivpflege/An\u00e4sthesie (81313-009)", + "Fachkrankenschwester/-pfleger - Operationsdienst (81313-015)", + "Fachlehrer/in - arbeitstechnische F\u00e4cher (84214-008)", + "Fachlehrer/in - Pflegeberufe (84213-094)", + "Fachlehrer/in (84214-006)", + "Fachschwester/-pfleger - Intensivpflege und An\u00e4sthesie (81313-025)", + "Fachwirt Immobilien (61313-009)", + "Finanzbuchhalter/in (72213-038)", + "G\u00e4rtner/in (12102-007)", + "Gas- und Wasserinstallateur/in (34212-005)", + "Gastroenterologe/Gastroenterologin (81424-015)", + "Gehaltsabrechner/in (72213-042)", + "Gesch\u00e4ftsf\u00fchrende/r Direktor/in (\u00f6ffentliche Verwaltung) (73294-015)", + "Gesch\u00e4ftsf\u00fchrer/in (71104-008)", + "Gesundheits- und Kinderkrankenpfleger/in (81302-003)", + "Gesundheits- und Krankenpflegeassistent/in (81302-004)", + "Gesundheits- und Krankenpfleger/in (81302-005)", + "Gesundheitscoach (82212-001)", + "Gesundheits\u00f6konom/in (82214-004)", + "Gesundheitspfleger/in (81302-006)", + "Gymnastiklehrer/in (84553-016)", + "Haushaltsfachkraft (83212-005)", + "Hausmeister/in (34102-007)", + "Haustechniker/in (34102-008)", + "Hauswart/in (34102-009)", + "Hauswirtschafter/in (83212-007)", + "Hauswirtschaftsverwalter/in (83293-005)", + "Hebamme/Entbindungspfleger (81353-003)", + "Heilerziehungspfleger/in (83132-006)", + "Heilp\u00e4dagoge/-p\u00e4dagogin (83134-002)", + "Heilpraktiker/in (81752-002)", + "Heizungs- und Sanit\u00e4rinstallateur/in (834212-011)", + "Heizungsmonteur/in (34212-015)", + "Honorar-Arzt", + "Honorardozent/in (84304-032)", + "Hospizleiter/in (81394-002)", + "Hygienefachkraft (53322-010)", + "Industriekaufmann/-frau (71302-011)", + "Informatiker (Hochschule) (43104-002)", + "Informatiker/in (Weiterbildung) (43103-008)", + "Integrationsberater", + "Integrationsmanager/in (83134-005)", + "Internatsbetreuer", + "Internist/in (81424-017)", + "IT-Administrator/in (43343-031)", + "IT-Leiter/in (43394-007)", + "Jugenderzieher/in (83112-025)", + "Jugendpsychologe/-psychologin (81624-007)", + "Jurist/in (73104-001)", + "Kardiologe/Kardiologin (81424-018)", + "Kaufmann/-frau - Gesundheitswesen (73222-007)", + "Kaufm\u00e4nnische/r Angestellte/r (71302-015)", + "Kaufm\u00e4nnische/r Direktor/in (71104-015)", + "Kaufm\u00e4nnische/r Sachbearbeiter/in (71302-018)", + "Kinder- und Jugendlichenpsychotherapeut/in (81634-004)", + "Kinder- und Jugendpsychologe/-psychologin (81624-008)", + "Kinder- und Jugendpsychotherapeut/in (81634-005)", + "Kinderarzt/-\u00e4rztin (81414-006)", + "Kinderg\u00e4rtner/in (83112-032)", + "Kinderkrankenschwester/-pfleger - Psychiatrie (81323-035)", + "Kinderkrankenschwester/-pfleger (81302-007)", + "Kinderp\u00e4dagoge/-p\u00e4dagogin (83112-033)", + "Kinderpsychologe/-psychologin (81624-009)", + "Kindheitsp\u00e4dagoge/-p\u00e4dagogin (91334-005)", + "Klinische Kodierfachkraft (71442-004)", + "Klinische/r Neuropsychologe/-psychologin (81624-010)", + "Koch/K\u00f6chin (29302-024)", + "Kodierer/in (71442-005)", + "Kostenabrechner/in (72223-011)", + "Krankengymnast/in (81713-009)", + "Krankenhaussekret\u00e4r/in (73222-009)", + "Krankenschwester/-pfleger - An\u00e4sthesie (81313-037)", + "Krankenschwester/-pfleger - Nachtwache (81302-009)", + "Krankenschwester/-pfleger - Nephrologie (81313-043)", + "Krankenschwester/-pfleger (81302-008)", + "Krankentransportleiter/in (52182-021)", + "K\u00fcchenchef (29394-004)", + "K\u00fcchenleiter/in (29394-007)", + "K\u00fcster/in (83382-005)", + "Lagerverwalter/in (Warenlager) (51312-030)", + "Lehrer/in - Gesundheitsfachberufe (84213-031)", + "Lehrer/in - Krankenpflege (84213-038)", + "Lehrer/in - Pflegeberufe (84213-044)", + "Leitende Pflegefachkraft (81393-002)", + "Leitende/r Arzt/\u00c4rztin (81494-004)", + "Leitende/r Entbindungspfleger/Hebamme/Bereichsleitung Frauenklinik (81393-003)", + "Leitende/r Gesundheits- und Krankenschwester/-pfleger (81393-004)", + "Leiter/in - ambulante Sozialdienste (83194-027)", + "Leiter/in - Einkauf (61194-013)", + "Leiter/in - Finanz- und Rechnungswesen (72294-008)", + "Leiter/in - Krankenhausbetriebstechnik (34193-009)", + "Leiter/in - Medizincontrolling (82214-014)", + "Leiter/in - Presse- und \u00d6ffentlichkeitsarbeit (92294-001)", + "Leiter/in - soziale Einrichtung (83194-015)", + "Leiter/in - Technik (27394-016)", + "Logop\u00e4de/Logop\u00e4din (81733-006)", + "Logop\u00e4de/Logop\u00e4din (Hochschule) (81734-003)", + "Lohnsachbearbeiter/in (72213-063)", + "Maler/in (33212-019)", + "Marketingsekret\u00e4r/in (71402-042)", + "Masseur/in (81712-004)", + "Masseur/in und medizinische/r Bademeister/in (81712-005)", + "Master of Science - Gesundheits\u00f6konomie (82214-009)", + "Master of Science - Projektmanagement (71304-017)", + "Mediengestalter/in - Digital-/Printmedien - Medienberatung (61122-015)", + "Medientechniker/in (23213-015)", + "Medizinallaborant/in (Humanmedizin) (81212-018)", + "Medizincontroller/in (72234-025)", + "Mediziner/in (81404-009)", + "Medizinische/r Assistent/in (81212-022)", + "Medizinische/r Fachangestellte/r (81102-004)", + "Medizinische/r Praxisassistent/in (81102-006)", + "Medizinisch-kaufm\u00e4nnische/r Assistent/in (73222-010)", + "Medizinisch-technische/r Assistent/in (81212-019)", + "Medizinisch-technische/r Fachassistent/in - radiologische Diagnostik (81233-011)", + "Medizinisch-technische/r Fachassistent/in (81213-023)", + "Medizinisch-technische/r Laboratoriumsassistent/in (81212-021)", + "Medizinisch-technische/r Radiologieassistent/in MTRA (81232-004)", + "MTA (Medizinisch-technische/r Laboratoriumsassistent/in 81212-031)", + "Musiker/in (94114-044)", + "Netzadministrator/in (EDV) (43343-016)", + "Netzwerkadministrator (43343-018)", + "Neurologe/Neurologin (81464-020)", + "Notfallsanit\u00e4ter (81342-005)", + "Oberarzt/-\u00e4rztin (81494-007)", + "\u00d6ffentlichkeitsreferent/in (92203-014)", + "\u00d6kotrophologe/\u00d6kotrophologin (82284-009)", + "Operationstechnische/r Assistent/in (81332-008)", + "Organist/in (94114-059)", + "Orthop\u00e4de/Orthop\u00e4din (81434-021)", + "OTA (Operationstechnische/r Assistent/in) (81332-009)", + "P\u00e4dagogische/r Betreuer/in (83124-033)", + "P\u00e4dagogische/r Mitarbeiter/in (91334-024)", + "P\u00e4diatrie-Assistent/in (81182-008)", + "Personalbuchhalter/in (72213-071)", + "Personalchef/in (71594-011)", + "Personalfachkaufmann/-kauffrau (71513-010)", + "Personalkaufmann/-frau (71512-004)", + "Personalleiter/in (71594-013)", + "Personalsachbearbeiter/in (71512-005)", + "Pflegeassistent/in (81302-014)", + "Pflegebereichsleiter/in (81394-013)", + "Pflegedienstleiter/in (81394-014)", + "Pflegedirektor/in (81394-032)", + "Pflegefachkraft - An\u00e4sthesie/Intensivmedizin (81313-052)", + "Pflegefachkraft - Gesundheits- und Krankenpfleger (81302-015)", + "Pflegefachkraft - Kinderkrankenpflege (81302-016)", + "Pflegefachkraft - Kinderpflege (81302-017)", + "Pflegefachkraft - Sozialstation (81302-019)", + "Pflegefachkraft (Krankenpflege) (81302-018)", + "Pflegefachmann/-frau (81302-028)", + "Pflegeleiter/in (81394-017)", + "Pflegemanager/in (81394-018)", + "Pflegemanager/in (81394-019)", + "Pflegep\u00e4dagoge/-p\u00e4dagogin (84214-065)", + "Pf\u00f6rtner/in (53112-066)", + "Physician Assistant (81333-001)", + "Physiker/in (41404-002)", + "Physiotherapeut/in (81713-015)", + "Physiotherapeut/in (Hochschule) (81714-002)", + "Praxisanleiter/in - Pflegeberufe (84213-085)", + "Praxisanleiter/-innen (84223-081)", + "Projektmanager/in (71393-007)", + "Prokurist/in (71104-017)", + "Psychologe/Psychologin - allgemeine Psychologie (81624-016)", + "Psychologe/Psychologin (81624-015)", + "Psychologische/r Psychotherapeut/in (81634-014)", + "Qualit\u00e4tsbeauftragte/r - Gesundheits-/Sozialwesen (82243-003)", + "Qualit\u00e4tsbeauftragte/r (27313-022)", + "Qualit\u00e4tsbeauftragter/-beauftragte - Management (27313-023)", + "Radioonkologische/r Assistent/in (81232-010)", + "Rechnungswesensachbearbeiter/in (72212-026)", + "Referent/in - berufliche Fort- und Weiterbildung", + "Referent/in - \u00d6ffentlichkeitsarbeit/Marketing (92203-023)", + "Reha Berater (71524-028)", + "Rehabilitationsp\u00e4dagoge/-p\u00e4dagogin (83134-009)", + "Reinigungsfachkraft /54112-018)", + "Rettungsassistent/in", + "Rettungssanit\u00e4ter/in", + "Rezeptionsmitarbeiter/in (Arztpraxis) (73222-012)", + "Rohrinstallateur/in (34212-040)", + "R\u00f6ntgenassistent/in (81232-011)", + "Sachbearbeiter/in - B\u00fcro (71402-058)", + "Sachbearbeiter/in - Verwaltung (geh. nichttechn. Dienst) (73203-017)", + "Sachbearbeiter/in (71302-021)", + "Schreibkraft (71432-031)", + "Schularzt/-\u00e4rztin (81414-008)", + "Schulleiter/in - Berufsschulen (8494-033)", + "Schulsozialarbeiter/in (83124-035)", + "Schwester/Pfleger (Kinderkrankenpflege) (81302-025)", + "Schwester/Pfleger (Krankenpflege) (81302-026)", + "Seelsorger/in (83314-034)", + "Sekret\u00e4r/in - Gesundheitswesen (73222-013)", + "Sekret\u00e4r/in (71402-062)", + "Sekretariatsleiter/in (71493-011)", + "Seniorenbetreuer/in (82102-005)", + "Seniorenpfleger/in (82102-006)", + "Seniorenzentrumsleiter/in (82194-013)", + "Servicekraft (63302-045)", + "Sicherheitsfachkraft (53123-009)", + "Sozialarbeiter/in (83124-037)", + "Sozialarbeiter/in / Sozialp\u00e4dagoge/-p\u00e4dagogin (83124-052)", + "Sozialp\u00e4dagoge/-p\u00e4dagogin (83124-039)", + "Sozialwissenschaftler/in (91324-015)", + "Sportlehrer/in - Rehabilitation/Behindertensport (83133-016)", + "Sportlehrer/in (84503-023)", + "Sportphysiotherapeut/in (81713-016)", + "Sporttherapeut (81783-013)", + "Sprechstundenhilfe (81102-009)", + "Stationsassistent/in (Arzthilfe) (81102-010)", + "Stationsleiter/in - Kranken-/Alten-/Kinderkrankenpflege (81393-012)", + "Stationsleiter/in - Krankenpflege (81393-009)", + "Stationsleiter/in - Krankenpflege/Altenpflege (81393-010)", + "Stationsleiter/in - Pflegedienst (81393-011)", + "Stenograf/in (71432-036)", + "Apothekenhelfer/in (62412-001)", + "Sterilisationsassistent/in (81182-002)", + "Suchtpsychologe/-psychologin (81624-022)", + "Techn. Assistent/in - Bautechnik (31102-008)", + "Techn.Produktdesigner (27212-075)", + "Techniker/in - Anwendungs-/Betriebstechnik (Farben, Lacke) (22203-016)", + "Techniker/in - Elektrotechnik (26303-015)", + "Technische/r B\u00fcrosachbearbeiter/in (71402-068)", + "Technische/r Koordinator/in (27304-054)", + "Technische/r Leiter/in (27394-024)", + "General (01104-008)", + "Technische/r Sterilisationsassistent/in (81182-003)", + "Telefonist/in (71401-042)", + "Hauselektriker/in (26212-033)", + "Terminsachbearbeiter/in (27302-010)", + "Therapeut/in - Krankengymnastik (81713-018)", + "Thoraxchirurg/in (81434-024)", + "\u00dcbungsleiter/in (84503-031)", + "Uhrenmacher (24532-032)", + "Uhrmachermeister (24593-041)", + "Elektrochemiker/in (41384-009)", + "Unterrichtspfleger/-schwester (84213-078)", + "Farb- und Lacktechniker/in (22203-003)", + "Verwalter/in - Tierzucht (11294-009)", + "Verwaltungsangestellte/r - Krankenk.,Krankenh\u00e4user, Kliniken (73222-016)", + "Gesundheits- und Krankenschwester/-pfleger - Gerontopsych.", + "Verwaltungsangestellter (mittl.Dienst) kirchl. Dienst", + "Verwaltungsfachangestellte/-angestellter - Kirchenverwaltung -evangelische Kirche (73282-010)", + "Visceralchirurg/in (81434-026)", + "Vorarbeiter (27302-012)", + "Vorzimmersekret\u00e4r/in (71402-071)", + "Weiterbildungsassistent/in (Arzt/\u00c4rztin) (81404-011)", + "Werbedesigner/in (23224-072)", + "Betriebsschlosser/in (25102-010)", + "Wirtschaftswissenschaftler/in (91404-011)", + "Wundmanager/in (81383-005)", + "Apparate- und Maschinenschlosser/in (34342-005)", + "Arzthelfer/in (81102-001)", + "Zahnarzthelfer/in (81112-006)" + ], + "Hilfskraft": [ + "Ableger/in 871401-001", + "Abrechnungspr\u00fcfer/in (72214-001=", + "Abschlussagent/in (Versicherung) (72133-001)", + "Alltagsbetreuer/in (83142-001)", + "Altenbetreuerhelfer/in (82101-001)", + "Altenpflegeassistent/in (1 j\u00e4hrige A.) (82101-008)", + "Altenpflegehelfer/in (1 j\u00e4hrige Ausb.) (82101-002)", + "Altenpflegehilfskraft (82101-003)", + "Anmelder/in (71404-010)", + "Anstreicher/in (33212-001)", + "Archivhelfer/in (71401-011)", + "Archivsachbearbeiter/in", + "Aufr\u00e4umer/in (Raum-, Hausratreiniger/in) (54101-002)", + "Ausbaufacharbeiter/in - Malerarbeiten (33212-003)", + "Ausbauhelfer/in (33301-001)", + "Aushilfsfahrer/in (52182-006)", + "Aushilfskraft (K\u00fcche) (29301-002)", + "Auskunftsgehilfe/-gehilfin (71401-013)", + "Auslader/in (Transportarbeiter/in) (51311-015)", + "Azetylenschwei\u00dfer/in (24422-010)", + "Betreuungshelfer/in (83111-002)", + "Betreuungskraft / Alltagsbegleiter/in (83142-003)", + "Betriebshandweker/in (25102-007)", + "Betriebshilfering-Gesch\u00e4ftsf\u00fchrer/in (11124-002)", + "Bote/Botin (B\u00fcro) (51321-009)", + "Bundesfreiwilligendienst (BFD)", + "B\u00fcroassistent/in (71402-009)", + "B\u00fcrogehilf(e/in) (71402-011)", + "B\u00fcrohilfskraft (71401-019)", + "externes Pers., Honorar", + "Fachkraft - Pflegeassistenz (83142-004)", + "Fahrbetriebsregler/in (Stra\u00dfenverkehr) (51512-003)", + "Fernsprechvermittler/in (71401-027)", + "Freiwilliges Soziales Jahr (FSJ)", + "Geb\u00e4udeinnenreiniger/in (54112-012)", + "Gesundheits- und Krankenpflegehelfer/in (81301-001)", + "Glasreiniger/in (54122-003)", + "Gr\u00fcnanlagenpfleger/in (12101-012)", + "Hausarbeitsgehilfe/-gehilfin (83211-002)", + "Haushaltshilfe (83211-005)", + "Hauswirtschaftsgehilfe/-gehilfin (83212-014)", + "Hauswirtschaftshelfer/in/-assistent/in (83212-015)", + "Hebammenhelfer/in (81352-002)", + "Helfer Reinigung (54101-014)", + "Helfer/in - Altenpflege/Pers\u00f6nliche Assistenz (82101-004)", + "Helfer/in - B\u00fcro, Verwaltung (71401-033)", + "Helfer/in - B\u00fcro, Verwaltung (71401-034)", + "Helfer/in - Gartenbau (12101-016)", + "Helfer/in - Gr\u00fcnanlagen (12101-017)", + "Helfer/in - Hauswirtschaft (83211-001)", + "Helfer/in - K\u00fcche (29301-005)", + "Helfer/in - Rettungsdienst (81341-002)", + "Helfer/in - Schachtarbeiten (32201-010)", + "Helfer/in - station\u00e4re Krankenpflege (81301-002)", + "Helfer/in - Warenmalerei, -lackiererei (22201-004)", + "Hilfskoch/-k\u00f6chin (29302-018)", + "Hilfsmonteuer (Elektro) (26301-046)", + "Hilfsschwester/-pfleger (81301-003)", + "Jahrespraktikant/-in (JP)", + "Kaufm\u00e4nnische B\u00fcrokraft (71402-034)", + "Kochhelfer/in (29301-007)", + "Kranken- und Altenpflegehelfer/in (81301-005)", + "Krankenfahrer/in (52182-019)", + "Krankenpflegehelfer/in (1 j\u00e4hrige A.) (81301-006)", + "Krankentransporteur/in (52182-020)", + "K\u00fcchengehilfe/-gehilfin (29301-009)", + "K\u00fcchenhelfer/in (29301-010)", + "K\u00fcchenhilfe (29301-011)", + "Lagerarbeiter/in (51311-068)", + "Lagerhelfer/in (51311-070)", + "Lagerhilfsarbeiter/in (51311-071)", + "Maschinenhelfer/in (25101-014)", + "Medizinische/r Fachhelfer/in (81102-005)", + "Menu/Datenerfasser/-in (71401-022)", + "Pflegediensthelfer/in (81301-008)", + "Pflegefachhelfer/in (Krankenpflege)", + "Pflegehelfer Behindertenpflege", + "Pflegehelfer/in - Altenpflege (ohne 1 j\u00e4hrige A.) (82101-006)", + "Pflegehelfer/in - station\u00e4re Pflege (ohne 1 j\u00e4hrige A.) (81301-010)", + "Pflegehelfer/in (Krankenpflege) (ohne 1 j\u00e4hrige A.) (81301-011)", + "Pflegehilfskraft (Krankenpflege) (81301-012)", + "Praktikant/-in (P)", + "Praktisches Jahr (PJ)", + "Raumpfleger/in (54101-023)", + "Rettungshelfer/in (81341-005)", + "R\u00f6ntgenhelfer/in (81232-012)", + "Saisonhelfer/in (11101-035)", + "Schwesterhelfer/in (81301-014)", + "Schwestern-/Pflegediensthelfer/in (81301-015)", + "Servicehilfskraft (63301-044)", + "Sozialassistent/in (83142-006)", + "Sportassistent/in (63122-005=", + "Sp\u00fclmann/-frau (Hausratreiniger/in) (54101-031)", + "Stationshelfer/in - Krankenpflege (81301-017)", + "Stationshilfe (81301-018)", + "Transporthelfer/in (51311-109)", + "Verwaltungsgehilfe/-gehilfin (73201-009)", + "W\u00e4scheschneider/in (28222-155)" + ] +} diff --git a/legacy/cases/77/09_2024/general_settings.json b/legacy/cases/77/09_2024/general_settings.json new file mode 100644 index 00000000..122e86a1 --- /dev/null +++ b/legacy/cases/77/09_2024/general_settings.json @@ -0,0 +1,18 @@ +{ + "SHIFT_NAME_TO_INDEX": { + "Early": 0, + "Late": 1, + "Night": 2 + }, + "qualifications": { + "2963": [ + "rounds" + ], + "3868": [ + "rounds" + ], + "791": [ + "rounds" + ] + } +} diff --git a/legacy/cases/77/09_2024/minimal_number_of_staff.json b/legacy/cases/77/09_2024/minimal_number_of_staff.json new file mode 100644 index 00000000..ce9daf74 --- /dev/null +++ b/legacy/cases/77/09_2024/minimal_number_of_staff.json @@ -0,0 +1,113 @@ +{ + "Azubi": { + "Di": { + "F": 1, + "N": 0, + "S": 1 + }, + "Do": { + "F": 1, + "N": 0, + "S": 1 + }, + "Fr": { + "F": 1, + "N": 0, + "S": 1 + }, + "Mi": { + "F": 1, + "N": 0, + "S": 1 + }, + "Mo": { + "F": 1, + "N": 0, + "S": 1 + }, + "Sa": { + "F": 1, + "N": 0, + "S": 1 + }, + "So": { + "F": 1, + "N": 0, + "S": 1 + } + }, + "Fachkraft": { + "Di": { + "F": 3, + "N": 2, + "S": 2 + }, + "Do": { + "F": 3, + "N": 2, + "S": 2 + }, + "Fr": { + "F": 3, + "N": 2, + "S": 2 + }, + "Mi": { + "F": 4, + "N": 2, + "S": 2 + }, + "Mo": { + "F": 3, + "N": 2, + "S": 2 + }, + "Sa": { + "F": 2, + "N": 1, + "S": 2 + }, + "So": { + "F": 2, + "N": 1, + "S": 2 + } + }, + "Hilfskraft": { + "Di": { + "F": 2, + "N": 0, + "S": 2 + }, + "Do": { + "F": 2, + "N": 0, + "S": 2 + }, + "Fr": { + "F": 2, + "N": 0, + "S": 2 + }, + "Mi": { + "F": 2, + "N": 0, + "S": 2 + }, + "Mo": { + "F": 2, + "N": 0, + "S": 2 + }, + "Sa": { + "F": 2, + "N": 1, + "S": 2 + }, + "So": { + "F": 2, + "N": 1, + "S": 2 + } + } +} diff --git a/legacy/cases/77/09_2024/wishes_and_blocked.json b/legacy/cases/77/09_2024/wishes_and_blocked.json new file mode 100644 index 00000000..a2f63b3e --- /dev/null +++ b/legacy/cases/77/09_2024/wishes_and_blocked.json @@ -0,0 +1,3 @@ +{ + "employees": [] +} diff --git a/legacy/cases/77/11_2024/employee_types.json b/legacy/cases/77/11_2024/employee_types.json new file mode 100644 index 00000000..1981e587 --- /dev/null +++ b/legacy/cases/77/11_2024/employee_types.json @@ -0,0 +1,525 @@ +{ + "Azubi": [ + "A-Altenpflegehelfer/in (A-82101-002)", + "A-Altenpfleger/in (A-82102-002)", + "A-An\u00e4sthesietechnisch/r Assistent/in (A-81332-001)", + "A-CTA (Chirurgisch-technische/r Assistent/in) (A-81332-005)", + "A-Fachkaufkann/-frau-Verwaltung im Gesundheitswesen (A-73223-015)", + "A-Fachkraft-Lagerlogistik (A-51312-005)", + "A-Gesundheits- und Kinderkrankenpfleger/in (A-81302-003)", + "A-Gesundheits- und Krankenpfleger/in (A-81302-005)", + "A-Hebamme/Entbindungspfleger (A-81353-003)", + "A-Hilfskoch/-k\u00f6chin (A-29302-018)", + "A-Informatiker/in (Weiterbildung) (A-43103-008)", + "A-Kaufmann/-frau-Gesundheitswesen (A-73222-007)", + "A-Kinderkrankenschwester/-pfleger (A-81302-007)", + "A-Koch/-K\u00f6chin (29302-024)", + "A-Krankenschwester/-pfleger (A-81302-008)", + "A-Medizinische/r Fachangestellte/r (A-81102-004)", + "A-Medizinisch-technisch/r Radiologieassistent/in (A-81232-004)", + "A-Notfallsanit\u00e4ter A-31342-005", + "A-OTA (Operationstechnische/r Assistent/in) (A-81332-009)", + "A-Pflegeassistent/in (A-81302-014)", + "A-Pflegefachkraft - Gesundheits- und Krankenpfleger (81302-015)", + "A-Pflegefachkraft (Altenpflege) (A-81302-019)", + "A-Pflegefachkraft (Krankenpflege) (A-81302-018)", + "A-Pflegefachkraft-Kinderkrankenpflege (A-81302-016)", + "A-Pflegefachmann/-frau (A-81302-028)", + "A-Stationshelfer/in - Krankenpflege (A-81301-017)" + ], + "Fachkraft": [ + "Abteilungsleiter/in (71394-003)", + "Alleinsekret\u00e4r/in (71402-004)", + "Allgemeinarzt/-\u00e4rztin (81404-001)", + "Allgemeinchirurg/in (81434-001)", + "Altenpfleger/in (82102-002)", + "Ambulante Krankenschwester/-pfleger (81302-001)", + "Ambulanzpfleger/schwester (81382-001)", + "An\u00e4sthesietechnische/r Assistent/in (81332-001)", + "An\u00e4sthesist/in (81454-002)", + "Archivar/in (73314-001)", + "Archivfachkraft (73312-004)", + "Arzt/\u00c4rztin - Allgemeinmedizin (81404-003)", + "Arzt/\u00c4rztin - An\u00e4sthesiologie und Intensivtherapie (81454-003)", + "Arzt/\u00c4rztin - Chirurgie (81434-002)", + "Arzt/\u00c4rztin - Hygiene (81484-006)", + "Arzt/\u00c4rztin - Innere Medizin (81424-001)", + "Arzt/\u00c4rztin - Kinder- und Jugendpsychiatrie (81464-001)", + "Arzt/\u00c4rztin - klinische Strahlenphysik (81234-001)", + "Arzt/\u00c4rztin - Neurochirurgie (81434-004)", + "Arzt/\u00c4rztin - Neurologie (81464-002)", + "Arzt/\u00c4rztin - Orthop\u00e4die (81434-005)", + "Arzt/\u00c4rztin - Radiologie (81234-003)", + "Arzt/\u00c4rztin - Traumatologie und Orthop\u00e4die (81434-006)", + "Arzt/\u00c4rztin (81404-002)", + "\u00c4rztliche/r Direktor/in (Humanarzt/-\u00e4rztin) (81494-001)", + "\u00c4rztliche/r Leiter/in (81494-002)", + "Arztsekret\u00e4r/in (73222-001)", + "Assistent/in - Gesch\u00e4ftsleitung (71403-001)", + "Assistent/in - Gesundheitswesen (73222-003)", + "Assistent/in - Operationstechnik (81332-003)", + "Assistent/in - Rechnungswesen (72213-006)", + "Assistenzarzt/-\u00e4rztin - Kinder-/Jugendpsychiatrie und -psychologie (81464-006)", + "Assistenzarzt/-\u00e4rztin (81404-004)", + "Assistenzarzt/-\u00e4rztin (Uni) - An\u00e4sthesiologie (81454-004)", + "Assistenzarzt/-\u00e4rztin (Uni) - Chirurgie (81434-007)", + "Assistenzarzt/-\u00e4rztin (Uni) - Innere Medizin (81424-004)", + "Assistenzarzt/-\u00e4rztin (Uni) - Kinder-/Jugendmedizin (81414-002)", + "Assistenzarzt/-\u00e4rztin (Uni) - Neurologie (81464-009)", + "ATA (An\u00e4sthesietechnische/r Assistent/in) (81332-004)", + "Augenoptikermeister/in (82593-001)", + "Ausbilder Berufsbildungswerk (84223-013)", + "Ausbilder Berufsf\u00f6rderungswerk", + "Bachelor of Arts - Soziale Arbeit/Soziale Dienste (83123-003)", + "Bachelor of Arts - Sportwissenschaft (84503-002)", + "Bankkaufmann/-frau (72112-004)", + "Beikoch/-k\u00f6chin (29302-005)", + "Bereichsleiter/in (71394-006)", + "Berufsp\u00e4dagoge/-p\u00e4dagogin (84224-003)", + "Besch\u00e4ftigungstherapeut/in (81723-004)", + "Betriebselektriker/in (26252-007)", + "Betriebsg\u00e4rtner/in (12102-001)", + "Betriebstechniker/in (25103-011)", + "Betriebswirt/in (Weiterbildung) - Rechnungswesen (72213-014)", + "Bibliothekar/in (73324-004)", + "Bilanzbuchhalter/in (72213-016)", + "Buchhalter/in (72213-019)", + "B\u00fcrofachkraft (71402-010)", + "B\u00fcrokaufmann/-frau (71402-012)", + "Chefarzt/-\u00e4rztin (81494-003)", + "Chefsekret\u00e4r/in (71403-008)", + "Chirurg/in (81434-008)", + "Coach Erw.Bildung (84404-010)", + "Controller/in (72234-005)", + "Controllingleiter/in (72294-006)", + "CTA (Chirurgisch-technische/r Assistent/in) (81332-005)", + "Datenverarbeitungsfachmann/-frau (43103-005)", + "Dauernachtwache (Krankenschwester/-pfleger) (81302-002)", + "Diabetesberater/in (81783-002)", + "Di\u00e4tassistent/in (81762-001)", + "EDV Sachbearbeiter (41402-017)", + "EDV-Systemtechniker/in (26312-040)", + "EDV-Techniker/in (43103-007)", + "EEG-Assistent/in (81222-009)", + "Einkaufsleiter/in (61194-006)", + "Einkaufslogistiker/in (61113-015)", + "Einzelhandelskaufmann/-frau (62102-002)", + "Elektroanlagenelektroniker/in (26252-050)", + "Elektroinstallateur/in (26212-017)", + "Elektroniker/in - Energie- und Geb\u00e4udetechnik (26212-019)", + "Elektroniker/in - Ger\u00e4te und Systeme (26302-033)", + "Endoskopieschwester/-pfleger (81313-003)", + "Ergotherapeut/in (81723-006)", + "Ern\u00e4hrungsberater/in (82233-005)", + "Ern\u00e4hrungswissenschaftler/in (82284-003)", + "Erzieher/in - Heilp\u00e4dagogik (83133-002)", + "Erzieher/in (83112-006)", + "Erziehungswissenschaftler/in (91334-007)", + "EXAH-Pflegefachkraft (Altenpflege) (A-81302-019)", + "EXGK-Pflegefachkraft - Gesundheits- und Krankenpflege (A-81302-015)", + "EXKI-Pflegefachkraft.Kinderkrankenpflege (A-81302-016)", + "EXKP-Pflegefachkraft- (Krankenpflege) (A-81302-018)", + "EX-Pflegefachmann/- frau (A-81302-028)", + "Fachangestellte/r f\u00fcr B\u00fcrokommunikation (71402-021)", + "Facharzt Neurochirurgie", + "Facharzt Psychiatrie u. Psychotherapie", + "Facharzt/-\u00e4rztin - Allgemeinchirurgie (81434-027)", + "Facharzt/-\u00e4rztin - Allgemeine Chirurgie (81434-009)", + "Facharzt/-\u00e4rztin - Allgemeinmedizin (81404-005)", + "Facharzt/-\u00e4rztin - Allgemeinmedizin (Hausarzt/-\u00e4rztin) (81404-010)", + "Facharzt/-\u00e4rztin - An\u00e4sthesiologie (81454-005)", + "Facharzt/-\u00e4rztin - Frauenheilkunde und Geburtshilfe (81444-017)", + "Facharzt/-\u00e4rztin - Gef\u00e4\u00dfchirurgie (81434-010)", + "Facharzt/-\u00e4rztin - Hals-Nasen-Ohrenheilkunde (81444-018)", + "Facharzt/-\u00e4rztin - Innere Medizin (81424-006)", + "Facharzt/-\u00e4rztin - Innere Medizin u. Allgemeinm. (Hausarzt) (81404-006)", + "Facharzt/-\u00e4rztin - Innere Medizin u. H\u00e4matolog. u. Onkologie (81424-010)", + "Facharzt/-\u00e4rztin - Innere Medizin und Gastroenterologie (81424-009)", + "Facharzt/-\u00e4rztin - Innere Medizin und Kardiologie (81424-011)", + "Facharzt/-\u00e4rztin - Innere Medizin und Nephrologie (81424-012)", + "Facharzt/-\u00e4rztin - Innere Medizin und Pneumologie (81424-013)", + "Facharzt/-\u00e4rztin - Kinder- u. Jugendpsychiat. u. -psychoth. (81464-010)", + "Facharzt/-\u00e4rztin - Kinder- und Jugendmedizin (81414-003)", + "Facharzt/-\u00e4rztin - Neurologie (81464-012)", + "Facharzt/-\u00e4rztin - \u00d6ffentliches Gesundheitswesen (81484-028)", + "Facharzt/-\u00e4rztin - Orthop\u00e4die und Unfallchirurgie (81434-013)", + "Facharzt/-\u00e4rztin - P\u00e4diatrie (81414-005)", + "Facharzt/-\u00e4rztin - Plastische und \u00c4sthetische Chirurgie (81434-014)", + "Facharzt/-\u00e4rztin - Radiologie (81234-005)", + "Facharzt/-\u00e4rztin - Thoraxchirurgie (81434-015)", + "Facharzt/-\u00e4rztin - Unfallchirurgie (81434-016)", + "Facharzt/-\u00e4rztin - Viszeralchirurgie (81434-017)", + "Fachinformatiker/in - Systemintegration (43102-014)", + "Fachinformatiker/in (43102-013)", + "Fachkaufmann/-frau - Logistik (51623-005)", + "Fachkaufmann/-frau - Verwaltung im Gesundheitswesen (73223-015)", + "Fachkinderkrankenpfleger/in - Intensivpflege/An\u00e4sthesie (81323-005)", + "Fachkinderkrankenschwester/-pfleger - ambulante Pflege (81323-002)", + "Fachkinderkrankenschwester/-pfleger - P\u00e4diatrie und Intensivmedizin (81323-011)", + "Fachkinderkrankenschwester/-pfleger - Psychiatrie (81323-013)", + "Fachkraft - Altenpflege (82102-004)", + "Fachkraft - Factoring (72213-033)", + "Fachkraft - Lagerlogistik (51312-005)", + "Fachkraft - Lagerwirtschaft (51312-007)", + "Fachkraft - Sozialarbeit (83123-004)", + "Fachkrankenpfleger/in - Nephrologie", + "Fachkrankenpfleger/in - Notfallpflege (81313-059)", + "Fachkrankenpfleger/in - Operations-/Endoskopiedienst (81313-014)", + "Fachkrankenschwester/-pfleger - Endoskopie (81313-006)", + "Fachkrankenschwester/-pfleger - Intensivmedizin und An\u00e4sthesie (81313-008)", + "Fachkrankenschwester/-pfleger - Intensivpflege/An\u00e4sthesie (81313-009)", + "Fachkrankenschwester/-pfleger - Operationsdienst (81313-015)", + "Fachlehrer/in - arbeitstechnische F\u00e4cher (84214-008)", + "Fachlehrer/in - Pflegeberufe (84213-094)", + "Fachlehrer/in (84214-006)", + "Fachschwester/-pfleger - Intensivpflege und An\u00e4sthesie (81313-025)", + "Fachwirt Immobilien (61313-009)", + "Finanzbuchhalter/in (72213-038)", + "G\u00e4rtner/in (12102-007)", + "Gas- und Wasserinstallateur/in (34212-005)", + "Gastroenterologe/Gastroenterologin (81424-015)", + "Gehaltsabrechner/in (72213-042)", + "Gesch\u00e4ftsf\u00fchrende/r Direktor/in (\u00f6ffentliche Verwaltung) (73294-015)", + "Gesch\u00e4ftsf\u00fchrer/in (71104-008)", + "Gesundheits- und Kinderkrankenpfleger/in (81302-003)", + "Gesundheits- und Krankenpflegeassistent/in (81302-004)", + "Gesundheits- und Krankenpfleger/in (81302-005)", + "Gesundheitscoach (82212-001)", + "Gesundheits\u00f6konom/in (82214-004)", + "Gesundheitspfleger/in (81302-006)", + "Gymnastiklehrer/in (84553-016)", + "Haushaltsfachkraft (83212-005)", + "Hausmeister/in (34102-007)", + "Haustechniker/in (34102-008)", + "Hauswart/in (34102-009)", + "Hauswirtschafter/in (83212-007)", + "Hauswirtschaftsverwalter/in (83293-005)", + "Hebamme/Entbindungspfleger (81353-003)", + "Heilerziehungspfleger/in (83132-006)", + "Heilp\u00e4dagoge/-p\u00e4dagogin (83134-002)", + "Heilpraktiker/in (81752-002)", + "Heizungs- und Sanit\u00e4rinstallateur/in (834212-011)", + "Heizungsmonteur/in (34212-015)", + "Honorar-Arzt", + "Honorardozent/in (84304-032)", + "Hospizleiter/in (81394-002)", + "Hygienefachkraft (53322-010)", + "Industriekaufmann/-frau (71302-011)", + "Informatiker (Hochschule) (43104-002)", + "Informatiker/in (Weiterbildung) (43103-008)", + "Integrationsberater", + "Integrationsmanager/in (83134-005)", + "Internatsbetreuer", + "Internist/in (81424-017)", + "IT-Administrator/in (43343-031)", + "IT-Leiter/in (43394-007)", + "Jugenderzieher/in (83112-025)", + "Jugendpsychologe/-psychologin (81624-007)", + "Jurist/in (73104-001)", + "Kardiologe/Kardiologin (81424-018)", + "Kaufmann/-frau - Gesundheitswesen (73222-007)", + "Kaufm\u00e4nnische/r Angestellte/r (71302-015)", + "Kaufm\u00e4nnische/r Direktor/in (71104-015)", + "Kaufm\u00e4nnische/r Sachbearbeiter/in (71302-018)", + "Kinder- und Jugendlichenpsychotherapeut/in (81634-004)", + "Kinder- und Jugendpsychologe/-psychologin (81624-008)", + "Kinder- und Jugendpsychotherapeut/in (81634-005)", + "Kinderarzt/-\u00e4rztin (81414-006)", + "Kinderg\u00e4rtner/in (83112-032)", + "Kinderkrankenschwester/-pfleger - Psychiatrie (81323-035)", + "Kinderkrankenschwester/-pfleger (81302-007)", + "Kinderp\u00e4dagoge/-p\u00e4dagogin (83112-033)", + "Kinderpsychologe/-psychologin (81624-009)", + "Kindheitsp\u00e4dagoge/-p\u00e4dagogin (91334-005)", + "Klinische Kodierfachkraft (71442-004)", + "Klinische/r Neuropsychologe/-psychologin (81624-010)", + "Koch/K\u00f6chin (29302-024)", + "Kodierer/in (71442-005)", + "Kostenabrechner/in (72223-011)", + "Krankengymnast/in (81713-009)", + "Krankenhaussekret\u00e4r/in (73222-009)", + "Krankenschwester/-pfleger - An\u00e4sthesie (81313-037)", + "Krankenschwester/-pfleger - Nachtwache (81302-009)", + "Krankenschwester/-pfleger - Nephrologie (81313-043)", + "Krankenschwester/-pfleger (81302-008)", + "Krankentransportleiter/in (52182-021)", + "K\u00fcchenchef (29394-004)", + "K\u00fcchenleiter/in (29394-007)", + "K\u00fcster/in (83382-005)", + "Lagerverwalter/in (Warenlager) (51312-030)", + "Lehrer/in - Gesundheitsfachberufe (84213-031)", + "Lehrer/in - Krankenpflege (84213-038)", + "Lehrer/in - Pflegeberufe (84213-044)", + "Leitende Pflegefachkraft (81393-002)", + "Leitende/r Arzt/\u00c4rztin (81494-004)", + "Leitende/r Entbindungspfleger/Hebamme/Bereichsleitung Frauenklinik (81393-003)", + "Leitende/r Gesundheits- und Krankenschwester/-pfleger (81393-004)", + "Leiter/in - ambulante Sozialdienste (83194-027)", + "Leiter/in - Einkauf (61194-013)", + "Leiter/in - Finanz- und Rechnungswesen (72294-008)", + "Leiter/in - Krankenhausbetriebstechnik (34193-009)", + "Leiter/in - Medizincontrolling (82214-014)", + "Leiter/in - Presse- und \u00d6ffentlichkeitsarbeit (92294-001)", + "Leiter/in - soziale Einrichtung (83194-015)", + "Leiter/in - Technik (27394-016)", + "Logop\u00e4de/Logop\u00e4din (81733-006)", + "Logop\u00e4de/Logop\u00e4din (Hochschule) (81734-003)", + "Lohnsachbearbeiter/in (72213-063)", + "Maler/in (33212-019)", + "Marketingsekret\u00e4r/in (71402-042)", + "Masseur/in (81712-004)", + "Masseur/in und medizinische/r Bademeister/in (81712-005)", + "Master of Science - Gesundheits\u00f6konomie (82214-009)", + "Master of Science - Projektmanagement (71304-017)", + "Mediengestalter/in - Digital-/Printmedien - Medienberatung (61122-015)", + "Medientechniker/in (23213-015)", + "Medizinallaborant/in (Humanmedizin) (81212-018)", + "Medizincontroller/in (72234-025)", + "Mediziner/in (81404-009)", + "Medizinische/r Assistent/in (81212-022)", + "Medizinische/r Fachangestellte/r (81102-004)", + "Medizinische/r Praxisassistent/in (81102-006)", + "Medizinisch-kaufm\u00e4nnische/r Assistent/in (73222-010)", + "Medizinisch-technische/r Assistent/in (81212-019)", + "Medizinisch-technische/r Fachassistent/in - radiologische Diagnostik (81233-011)", + "Medizinisch-technische/r Fachassistent/in (81213-023)", + "Medizinisch-technische/r Laboratoriumsassistent/in (81212-021)", + "Medizinisch-technische/r Radiologieassistent/in MTRA (81232-004)", + "MTA (Medizinisch-technische/r Laboratoriumsassistent/in 81212-031)", + "Musiker/in (94114-044)", + "Netzadministrator/in (EDV) (43343-016)", + "Netzwerkadministrator (43343-018)", + "Neurologe/Neurologin (81464-020)", + "Notfallsanit\u00e4ter (81342-005)", + "Oberarzt/-\u00e4rztin (81494-007)", + "\u00d6ffentlichkeitsreferent/in (92203-014)", + "\u00d6kotrophologe/\u00d6kotrophologin (82284-009)", + "Operationstechnische/r Assistent/in (81332-008)", + "Organist/in (94114-059)", + "Orthop\u00e4de/Orthop\u00e4din (81434-021)", + "OTA (Operationstechnische/r Assistent/in) (81332-009)", + "P\u00e4dagogische/r Betreuer/in (83124-033)", + "P\u00e4dagogische/r Mitarbeiter/in (91334-024)", + "P\u00e4diatrie-Assistent/in (81182-008)", + "Personalbuchhalter/in (72213-071)", + "Personalchef/in (71594-011)", + "Personalfachkaufmann/-kauffrau (71513-010)", + "Personalkaufmann/-frau (71512-004)", + "Personalleiter/in (71594-013)", + "Personalsachbearbeiter/in (71512-005)", + "Pflegeassistent/in (81302-014)", + "Pflegebereichsleiter/in (81394-013)", + "Pflegedienstleiter/in (81394-014)", + "Pflegedirektor/in (81394-032)", + "Pflegefachkraft - An\u00e4sthesie/Intensivmedizin (81313-052)", + "Pflegefachkraft - Gesundheits- und Krankenpfleger (81302-015)", + "Pflegefachkraft - Kinderkrankenpflege (81302-016)", + "Pflegefachkraft - Kinderpflege (81302-017)", + "Pflegefachkraft - Sozialstation (81302-019)", + "Pflegefachkraft (Krankenpflege) (81302-018)", + "Pflegefachmann/-frau (81302-028)", + "Pflegeleiter/in (81394-017)", + "Pflegemanager/in (81394-018)", + "Pflegemanager/in (81394-019)", + "Pflegep\u00e4dagoge/-p\u00e4dagogin (84214-065)", + "Pf\u00f6rtner/in (53112-066)", + "Physician Assistant (81333-001)", + "Physiker/in (41404-002)", + "Physiotherapeut/in (81713-015)", + "Physiotherapeut/in (Hochschule) (81714-002)", + "Praxisanleiter/in - Pflegeberufe (84213-085)", + "Praxisanleiter/-innen (84223-081)", + "Projektmanager/in (71393-007)", + "Prokurist/in (71104-017)", + "Psychologe/Psychologin - allgemeine Psychologie (81624-016)", + "Psychologe/Psychologin (81624-015)", + "Psychologische/r Psychotherapeut/in (81634-014)", + "Qualit\u00e4tsbeauftragte/r - Gesundheits-/Sozialwesen (82243-003)", + "Qualit\u00e4tsbeauftragte/r (27313-022)", + "Qualit\u00e4tsbeauftragter/-beauftragte - Management (27313-023)", + "Radioonkologische/r Assistent/in (81232-010)", + "Rechnungswesensachbearbeiter/in (72212-026)", + "Referent/in - berufliche Fort- und Weiterbildung", + "Referent/in - \u00d6ffentlichkeitsarbeit/Marketing (92203-023)", + "Reha Berater (71524-028)", + "Rehabilitationsp\u00e4dagoge/-p\u00e4dagogin (83134-009)", + "Reinigungsfachkraft /54112-018)", + "Rettungsassistent/in", + "Rettungssanit\u00e4ter/in", + "Rezeptionsmitarbeiter/in (Arztpraxis) (73222-012)", + "Rohrinstallateur/in (34212-040)", + "R\u00f6ntgenassistent/in (81232-011)", + "Sachbearbeiter/in - B\u00fcro (71402-058)", + "Sachbearbeiter/in - Verwaltung (geh. nichttechn. Dienst) (73203-017)", + "Sachbearbeiter/in (71302-021)", + "Schreibkraft (71432-031)", + "Schularzt/-\u00e4rztin (81414-008)", + "Schulleiter/in - Berufsschulen (8494-033)", + "Schulsozialarbeiter/in (83124-035)", + "Schwester/Pfleger (Kinderkrankenpflege) (81302-025)", + "Schwester/Pfleger (Krankenpflege) (81302-026)", + "Seelsorger/in (83314-034)", + "Sekret\u00e4r/in - Gesundheitswesen (73222-013)", + "Sekret\u00e4r/in (71402-062)", + "Sekretariatsleiter/in (71493-011)", + "Seniorenbetreuer/in (82102-005)", + "Seniorenpfleger/in (82102-006)", + "Seniorenzentrumsleiter/in (82194-013)", + "Servicekraft (63302-045)", + "Sicherheitsfachkraft (53123-009)", + "Sozialarbeiter/in (83124-037)", + "Sozialarbeiter/in / Sozialp\u00e4dagoge/-p\u00e4dagogin (83124-052)", + "Sozialp\u00e4dagoge/-p\u00e4dagogin (83124-039)", + "Sozialwissenschaftler/in (91324-015)", + "Sportlehrer/in - Rehabilitation/Behindertensport (83133-016)", + "Sportlehrer/in (84503-023)", + "Sportphysiotherapeut/in (81713-016)", + "Sporttherapeut (81783-013)", + "Sprechstundenhilfe (81102-009)", + "Stationsassistent/in (Arzthilfe) (81102-010)", + "Stationsleiter/in - Kranken-/Alten-/Kinderkrankenpflege (81393-012)", + "Stationsleiter/in - Krankenpflege (81393-009)", + "Stationsleiter/in - Krankenpflege/Altenpflege (81393-010)", + "Stationsleiter/in - Pflegedienst (81393-011)", + "Stenograf/in (71432-036)", + "Apothekenhelfer/in (62412-001)", + "Sterilisationsassistent/in (81182-002)", + "Suchtpsychologe/-psychologin (81624-022)", + "Techn. Assistent/in - Bautechnik (31102-008)", + "Techn.Produktdesigner (27212-075)", + "Techniker/in - Anwendungs-/Betriebstechnik (Farben, Lacke) (22203-016)", + "Techniker/in - Elektrotechnik (26303-015)", + "Technische/r B\u00fcrosachbearbeiter/in (71402-068)", + "Technische/r Koordinator/in (27304-054)", + "Technische/r Leiter/in (27394-024)", + "General (01104-008)", + "Technische/r Sterilisationsassistent/in (81182-003)", + "Telefonist/in (71401-042)", + "Hauselektriker/in (26212-033)", + "Terminsachbearbeiter/in (27302-010)", + "Therapeut/in - Krankengymnastik (81713-018)", + "Thoraxchirurg/in (81434-024)", + "\u00dcbungsleiter/in (84503-031)", + "Uhrenmacher (24532-032)", + "Uhrmachermeister (24593-041)", + "Elektrochemiker/in (41384-009)", + "Unterrichtspfleger/-schwester (84213-078)", + "Farb- und Lacktechniker/in (22203-003)", + "Verwalter/in - Tierzucht (11294-009)", + "Verwaltungsangestellte/r - Krankenk.,Krankenh\u00e4user, Kliniken (73222-016)", + "Gesundheits- und Krankenschwester/-pfleger - Gerontopsych.", + "Verwaltungsangestellter (mittl.Dienst) kirchl. Dienst", + "Verwaltungsfachangestellte/-angestellter - Kirchenverwaltung -evangelische Kirche (73282-010)", + "Visceralchirurg/in (81434-026)", + "Vorarbeiter (27302-012)", + "Vorzimmersekret\u00e4r/in (71402-071)", + "Weiterbildungsassistent/in (Arzt/\u00c4rztin) (81404-011)", + "Werbedesigner/in (23224-072)", + "Betriebsschlosser/in (25102-010)", + "Wirtschaftswissenschaftler/in (91404-011)", + "Wundmanager/in (81383-005)", + "Apparate- und Maschinenschlosser/in (34342-005)", + "Arzthelfer/in (81102-001)", + "Zahnarzthelfer/in (81112-006)" + ], + "Hilfskraft": [ + "Ableger/in 871401-001", + "Abrechnungspr\u00fcfer/in (72214-001=", + "Abschlussagent/in (Versicherung) (72133-001)", + "Alltagsbetreuer/in (83142-001)", + "Altenbetreuerhelfer/in (82101-001)", + "Altenpflegeassistent/in (1 j\u00e4hrige A.) (82101-008)", + "Altenpflegehelfer/in (1 j\u00e4hrige Ausb.) (82101-002)", + "Altenpflegehilfskraft (82101-003)", + "Anmelder/in (71404-010)", + "Anstreicher/in (33212-001)", + "Archivhelfer/in (71401-011)", + "Archivsachbearbeiter/in", + "Aufr\u00e4umer/in (Raum-, Hausratreiniger/in) (54101-002)", + "Ausbaufacharbeiter/in - Malerarbeiten (33212-003)", + "Ausbauhelfer/in (33301-001)", + "Aushilfsfahrer/in (52182-006)", + "Aushilfskraft (K\u00fcche) (29301-002)", + "Auskunftsgehilfe/-gehilfin (71401-013)", + "Auslader/in (Transportarbeiter/in) (51311-015)", + "Azetylenschwei\u00dfer/in (24422-010)", + "Betreuungshelfer/in (83111-002)", + "Betreuungskraft / Alltagsbegleiter/in (83142-003)", + "Betriebshandweker/in (25102-007)", + "Betriebshilfering-Gesch\u00e4ftsf\u00fchrer/in (11124-002)", + "Bote/Botin (B\u00fcro) (51321-009)", + "Bundesfreiwilligendienst (BFD)", + "B\u00fcroassistent/in (71402-009)", + "B\u00fcrogehilf(e/in) (71402-011)", + "B\u00fcrohilfskraft (71401-019)", + "externes Pers., Honorar", + "Fachkraft - Pflegeassistenz (83142-004)", + "Fahrbetriebsregler/in (Stra\u00dfenverkehr) (51512-003)", + "Fernsprechvermittler/in (71401-027)", + "Freiwilliges Soziales Jahr (FSJ)", + "Geb\u00e4udeinnenreiniger/in (54112-012)", + "Gesundheits- und Krankenpflegehelfer/in (81301-001)", + "Glasreiniger/in (54122-003)", + "Gr\u00fcnanlagenpfleger/in (12101-012)", + "Hausarbeitsgehilfe/-gehilfin (83211-002)", + "Haushaltshilfe (83211-005)", + "Hauswirtschaftsgehilfe/-gehilfin (83212-014)", + "Hauswirtschaftshelfer/in/-assistent/in (83212-015)", + "Hebammenhelfer/in (81352-002)", + "Helfer Reinigung (54101-014)", + "Helfer/in - Altenpflege/Pers\u00f6nliche Assistenz (82101-004)", + "Helfer/in - B\u00fcro, Verwaltung (71401-033)", + "Helfer/in - B\u00fcro, Verwaltung (71401-034)", + "Helfer/in - Gartenbau (12101-016)", + "Helfer/in - Gr\u00fcnanlagen (12101-017)", + "Helfer/in - Hauswirtschaft (83211-001)", + "Helfer/in - K\u00fcche (29301-005)", + "Helfer/in - Rettungsdienst (81341-002)", + "Helfer/in - Schachtarbeiten (32201-010)", + "Helfer/in - station\u00e4re Krankenpflege (81301-002)", + "Helfer/in - Warenmalerei, -lackiererei (22201-004)", + "Hilfskoch/-k\u00f6chin (29302-018)", + "Hilfsmonteuer (Elektro) (26301-046)", + "Hilfsschwester/-pfleger (81301-003)", + "Jahrespraktikant/-in (JP)", + "Kaufm\u00e4nnische B\u00fcrokraft (71402-034)", + "Kochhelfer/in (29301-007)", + "Kranken- und Altenpflegehelfer/in (81301-005)", + "Krankenfahrer/in (52182-019)", + "Krankenpflegehelfer/in (1 j\u00e4hrige A.) (81301-006)", + "Krankentransporteur/in (52182-020)", + "K\u00fcchengehilfe/-gehilfin (29301-009)", + "K\u00fcchenhelfer/in (29301-010)", + "K\u00fcchenhilfe (29301-011)", + "Lagerarbeiter/in (51311-068)", + "Lagerhelfer/in (51311-070)", + "Lagerhilfsarbeiter/in (51311-071)", + "Maschinenhelfer/in (25101-014)", + "Medizinische/r Fachhelfer/in (81102-005)", + "Menu/Datenerfasser/-in (71401-022)", + "Pflegediensthelfer/in (81301-008)", + "Pflegefachhelfer/in (Krankenpflege)", + "Pflegehelfer Behindertenpflege", + "Pflegehelfer/in - Altenpflege (ohne 1 j\u00e4hrige A.) (82101-006)", + "Pflegehelfer/in - station\u00e4re Pflege (ohne 1 j\u00e4hrige A.) (81301-010)", + "Pflegehelfer/in (Krankenpflege) (ohne 1 j\u00e4hrige A.) (81301-011)", + "Pflegehilfskraft (Krankenpflege) (81301-012)", + "Praktikant/-in (P)", + "Praktisches Jahr (PJ)", + "Raumpfleger/in (54101-023)", + "Rettungshelfer/in (81341-005)", + "R\u00f6ntgenhelfer/in (81232-012)", + "Saisonhelfer/in (11101-035)", + "Schwesterhelfer/in (81301-014)", + "Schwestern-/Pflegediensthelfer/in (81301-015)", + "Servicehilfskraft (63301-044)", + "Sozialassistent/in (83142-006)", + "Sportassistent/in (63122-005=", + "Sp\u00fclmann/-frau (Hausratreiniger/in) (54101-031)", + "Stationshelfer/in - Krankenpflege (81301-017)", + "Stationshilfe (81301-018)", + "Transporthelfer/in (51311-109)", + "Verwaltungsgehilfe/-gehilfin (73201-009)", + "W\u00e4scheschneider/in (28222-155)" + ] +} diff --git a/legacy/cases/77/11_2024/employees.json b/legacy/cases/77/11_2024/employees.json new file mode 100644 index 00000000..a9d16473 --- /dev/null +++ b/legacy/cases/77/11_2024/employees.json @@ -0,0 +1,208 @@ +{ + "employees": [ + { + "firstname": "Sandra", + "key": 459, + "name": "Shoemake", + "type": "Krankenschwester/-pfleger (81302-008)" + }, + { + "firstname": "Adriane", + "key": 790, + "name": "Mccomas", + "type": "Stationshilfe (81301-018)" + }, + { + "firstname": "Janett", + "key": 791, + "name": "Branz", + "type": "Altenpfleger/in (82102-002)" + }, + { + "firstname": "Silvia", + "key": 914, + "name": "Harkins", + "type": "Krankenpflegehelfer/in (1 j\u00e4hrige A.) (81301-006)" + }, + { + "firstname": "Ingbert", + "key": 917, + "name": "Catoe", + "type": "Krankenschwester/-pfleger (81302-008)" + }, + { + "firstname": "Jenny", + "key": 921, + "name": "Keese", + "type": "Altenpfleger/in (82102-002)" + }, + { + "firstname": "B\u00e4rbl", + "key": 924, + "name": "Merriweather", + "type": "Gesundheits- und Krankenpfleger/in (81302-005)" + }, + { + "firstname": "Lina", + "key": 925, + "name": "Farniok", + "type": "Gesundheits- und Krankenpfleger/in (81302-005)" + }, + { + "firstname": "Margaritt", + "key": 927, + "name": "Mittrach", + "type": "Stationshilfe (81301-018)" + }, + { + "firstname": "Daniele", + "key": 928, + "name": "Wunderlich", + "type": "Gesundheits- und Krankenpfleger/in (81302-005)" + }, + { + "firstname": "Constance", + "key": 1230, + "name": "Palacio", + "type": "Pflegefachkraft (Krankenpflege) (81302-018)" + }, + { + "firstname": "Kersten", + "key": 2932, + "name": "Devers", + "type": "Pflegefachkraft (Krankenpflege) (81302-018)" + }, + { + "firstname": "Renilde", + "key": 2963, + "name": "Hoots", + "type": "Altenpfleger/in (82102-002)" + }, + { + "firstname": "Elgine", + "key": 3566, + "name": "Seligman", + "type": "Altenpfleger/in (82102-002)" + }, + { + "firstname": "Eike", + "key": 3868, + "name": "Vanfleet", + "type": "Pflegefachkraft (Krankenpflege) (81302-018)" + }, + { + "firstname": "Hannah", + "key": 4566, + "name": "Woodcock", + "type": "A-Pflegeassistent/in (A-81302-014)" + }, + { + "firstname": "Henni", + "key": 5367, + "name": "Donis", + "type": "Krankenpflegehelfer/in (1 j\u00e4hrige A.) (81301-006)" + }, + { + "firstname": "Augustin", + "key": 5920, + "name": "Carreras", + "type": "Pflegefachkraft (Krankenpflege) (81302-018)" + }, + { + "firstname": "Roseliese", + "key": 6507, + "name": "Rashid", + "type": "Altenpfleger/in (82102-002)" + }, + { + "firstname": "Christfri", + "key": 6677, + "name": "Fullerton", + "type": "Krankenpflegehelfer/in (1 j\u00e4hrige A.) (81301-006)" + }, + { + "firstname": "Saskia", + "key": 6681, + "name": "Labelle", + "type": "Gesundheits- und Krankenpfleger/in (81302-005)" + }, + { + "firstname": "Lioba", + "key": 6715, + "name": "Burris", + "type": "A-Pflegefachkraft (Krankenpflege) (A-81302-018)" + }, + { + "firstname": "Trude", + "key": 6836, + "name": "Valentino", + "type": "Pflegefachkraft (Krankenpflege) (81302-018)" + }, + { + "firstname": "Annelene", + "key": 6928, + "name": "Izzo", + "type": "Medizinische/r Fachangestellte/r (81102-004)" + }, + { + "firstname": "Marcus", + "key": 7496, + "name": "Demarco", + "type": "Gesundheits- und Krankenpfleger/in (81302-005)" + }, + { + "firstname": "Ludger", + "key": 7603, + "name": "Roberson", + "type": "Altenpfleger/in (82102-002)" + }, + { + "firstname": "Kilian", + "key": 7752, + "name": "Rodriques", + "type": "Bundesfreiwilligendienst (BFD)" + }, + { + "firstname": "Julia", + "key": 7770, + "name": "Yeh", + "type": "A-Pflegefachkraft (Krankenpflege) (A-81302-018)" + }, + { + "firstname": "Burkhild", + "key": 7796, + "name": "Hertzler", + "type": "A-Pflegefachkraft (Krankenpflege) (A-81302-018)" + }, + { + "firstname": "Karena", + "key": 7835, + "name": "Driggers", + "type": "A-Pflegefachkraft (Krankenpflege) (A-81302-018)" + }, + { + "firstname": "Gertraute", + "key": 7848, + "name": "Winters", + "type": "A-Pflegeassistent/in (A-81302-014)" + }, + { + "firstname": "Janett", + "key": 7877, + "name": "Staggs", + "type": "A-Pflegefachkraft (Krankenpflege) (A-81302-018)" + }, + { + "firstname": "Irma", + "key": 7919, + "name": "Weathers", + "type": "Gesundheits- und Krankenpfleger/in (81302-005)" + }, + { + "firstname": "Loremarie", + "key": 7990, + "name": "Milburn", + "type": "A-Pflegeassistent/in (A-81302-014)" + } + ] +} diff --git a/legacy/cases/77/11_2024/free_shifts_and_vacation_days.json b/legacy/cases/77/11_2024/free_shifts_and_vacation_days.json new file mode 100644 index 00000000..07aa5dee --- /dev/null +++ b/legacy/cases/77/11_2024/free_shifts_and_vacation_days.json @@ -0,0 +1,1369 @@ +{ + "employees": [ + { + "firstname": "Sandra", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "key": 459, + "name": "Shoemake", + "planned_shifts": [], + "vacation_days": [ + 12 + ] + }, + { + "firstname": "Adriane", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "key": 790, + "name": "Mccomas", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Janett", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "key": 791, + "name": "Branz", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Silvia", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "key": 914, + "name": "Harkins", + "planned_shifts": [], + "vacation_days": [ + 21, + 22, + 25, + 26, + 27, + 28, + 29 + ] + }, + { + "firstname": "Ingbert", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "key": 917, + "name": "Catoe", + "planned_shifts": [], + "vacation_days": [ + 4, + 5 + ] + }, + { + "firstname": "Jenny", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "key": 921, + "name": "Keese", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "B\u00e4rbl", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "key": 924, + "name": "Merriweather", + "planned_shifts": [], + "vacation_days": [ + 4, + 5, + 6, + 7 + ] + }, + { + "firstname": "Lina", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "key": 925, + "name": "Farniok", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Margaritt", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "key": 927, + "name": "Mittrach", + "planned_shifts": [], + "vacation_days": [ + 18 + ] + }, + { + "firstname": "Daniele", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "key": 928, + "name": "Wunderlich", + "planned_shifts": [], + "vacation_days": [ + 25, + 26, + 27, + 28, + 29 + ] + }, + { + "firstname": "Constance", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "key": 1230, + "name": "Palacio", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Kersten", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "key": 2932, + "name": "Devers", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Renilde", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "key": 2963, + "name": "Hoots", + "planned_shifts": [], + "vacation_days": [ + 11 + ] + }, + { + "firstname": "Elgine", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "key": 3566, + "name": "Seligman", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Eike", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "key": 3868, + "name": "Vanfleet", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Hannah", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "key": 4566, + "name": "Woodcock", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Henni", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "key": 5367, + "name": "Donis", + "planned_shifts": [], + "vacation_days": [ + 7, + 8 + ] + }, + { + "firstname": "Augustin", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "key": 5920, + "name": "Carreras", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Roseliese", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "key": 6507, + "name": "Rashid", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Christfri", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "key": 6677, + "name": "Fullerton", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Saskia", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "key": 6681, + "name": "Labelle", + "planned_shifts": [], + "vacation_days": [ + 18, + 19, + 20, + 21, + 22, + 25, + 26 + ] + }, + { + "firstname": "Lioba", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "key": 6715, + "name": "Burris", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Trude", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "key": 6836, + "name": "Valentino", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Annelene", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "key": 6928, + "name": "Izzo", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Marcus", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "key": 7496, + "name": "Demarco", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Ludger", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "key": 7603, + "name": "Roberson", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Kilian", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "key": 7752, + "name": "Rodriques", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Julia", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "key": 7770, + "name": "Yeh", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Burkhild", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "key": 7796, + "name": "Hertzler", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Karena", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "key": 7835, + "name": "Driggers", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Gertraute", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "key": 7848, + "name": "Winters", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Janett", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "key": 7877, + "name": "Staggs", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Irma", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "key": 7919, + "name": "Weathers", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Loremarie", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "key": 7990, + "name": "Milburn", + "planned_shifts": [], + "vacation_days": [] + } + ] +} diff --git a/legacy/cases/77/11_2024/general_settings.json b/legacy/cases/77/11_2024/general_settings.json new file mode 100644 index 00000000..122e86a1 --- /dev/null +++ b/legacy/cases/77/11_2024/general_settings.json @@ -0,0 +1,18 @@ +{ + "SHIFT_NAME_TO_INDEX": { + "Early": 0, + "Late": 1, + "Night": 2 + }, + "qualifications": { + "2963": [ + "rounds" + ], + "3868": [ + "rounds" + ], + "791": [ + "rounds" + ] + } +} diff --git a/legacy/cases/77/11_2024/minimal_number_of_staff.json b/legacy/cases/77/11_2024/minimal_number_of_staff.json new file mode 100644 index 00000000..ce9daf74 --- /dev/null +++ b/legacy/cases/77/11_2024/minimal_number_of_staff.json @@ -0,0 +1,113 @@ +{ + "Azubi": { + "Di": { + "F": 1, + "N": 0, + "S": 1 + }, + "Do": { + "F": 1, + "N": 0, + "S": 1 + }, + "Fr": { + "F": 1, + "N": 0, + "S": 1 + }, + "Mi": { + "F": 1, + "N": 0, + "S": 1 + }, + "Mo": { + "F": 1, + "N": 0, + "S": 1 + }, + "Sa": { + "F": 1, + "N": 0, + "S": 1 + }, + "So": { + "F": 1, + "N": 0, + "S": 1 + } + }, + "Fachkraft": { + "Di": { + "F": 3, + "N": 2, + "S": 2 + }, + "Do": { + "F": 3, + "N": 2, + "S": 2 + }, + "Fr": { + "F": 3, + "N": 2, + "S": 2 + }, + "Mi": { + "F": 4, + "N": 2, + "S": 2 + }, + "Mo": { + "F": 3, + "N": 2, + "S": 2 + }, + "Sa": { + "F": 2, + "N": 1, + "S": 2 + }, + "So": { + "F": 2, + "N": 1, + "S": 2 + } + }, + "Hilfskraft": { + "Di": { + "F": 2, + "N": 0, + "S": 2 + }, + "Do": { + "F": 2, + "N": 0, + "S": 2 + }, + "Fr": { + "F": 2, + "N": 0, + "S": 2 + }, + "Mi": { + "F": 2, + "N": 0, + "S": 2 + }, + "Mo": { + "F": 2, + "N": 0, + "S": 2 + }, + "Sa": { + "F": 2, + "N": 1, + "S": 2 + }, + "So": { + "F": 2, + "N": 1, + "S": 2 + } + } +} diff --git a/legacy/cases/77/11_2024/shift_information.json b/legacy/cases/77/11_2024/shift_information.json new file mode 100644 index 00000000..d2215694 --- /dev/null +++ b/legacy/cases/77/11_2024/shift_information.json @@ -0,0 +1,47 @@ +[ + { + "break_duration": 0.0, + "end_time": "2000-01-01T14:00:00", + "shift_duration": 360.0, + "shift_id": "1406", + "shift_name": "Z60", + "start_time": "2000-01-01T08:00:00", + "working_minutes": 360.0 + }, + { + "break_duration": 30.0, + "end_time": "2000-01-01T16:10:00", + "shift_duration": 490.0, + "shift_id": "2906", + "shift_name": "T75_", + "start_time": "2000-01-01T08:00:00", + "working_minutes": 460.0 + }, + { + "break_duration": 30.0, + "end_time": "2000-01-01T14:10:00", + "shift_duration": 490.0, + "shift_id": "2939", + "shift_name": "F2_", + "start_time": "2000-01-01T06:00:00", + "working_minutes": 460.0 + }, + { + "break_duration": 30.0, + "end_time": "2000-01-01T21:00:00", + "shift_duration": 490.0, + "shift_id": "2947", + "shift_name": "S2_", + "start_time": "2000-01-01T12:50:00", + "working_minutes": 460.0 + }, + { + "break_duration": 45.0, + "end_time": "2000-01-02T06:30:00", + "shift_duration": 610.0, + "shift_id": "2953", + "shift_name": "N2_", + "start_time": "2000-01-01T20:20:00", + "working_minutes": 565.0 + } +] diff --git a/legacy/cases/77/11_2024/target_working_minutes.json b/legacy/cases/77/11_2024/target_working_minutes.json new file mode 100644 index 00000000..5eaad00f --- /dev/null +++ b/legacy/cases/77/11_2024/target_working_minutes.json @@ -0,0 +1,270 @@ +{ + "employees": [ + { + "actual": 0.0, + "firstname": "Sandra", + "key": 459, + "name": "Shoemake", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Adriane", + "key": 790, + "name": "Mccomas", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Janett", + "key": 791, + "name": "Branz", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Nele", + "key": 822, + "name": "Sewell", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Liselotte", + "key": 839, + "name": "S\u00e4uffert", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Silvia", + "key": 914, + "name": "Harkins", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Ingbert", + "key": 917, + "name": "Catoe", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Jenny", + "key": 921, + "name": "Keese", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "B\u00e4rbl", + "key": 924, + "name": "Merriweather", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Lina", + "key": 925, + "name": "Farniok", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Margaritt", + "key": 927, + "name": "Mittrach", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Daniele", + "key": 928, + "name": "Wunderlich", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Constance", + "key": 1230, + "name": "Palacio", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Kersten", + "key": 2932, + "name": "Devers", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Renilde", + "key": 2963, + "name": "Hoots", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Elgine", + "key": 3566, + "name": "Seligman", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Eike", + "key": 3868, + "name": "Vanfleet", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Hannah", + "key": 4566, + "name": "Woodcock", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Henni", + "key": 5367, + "name": "Donis", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Augustin", + "key": 5920, + "name": "Carreras", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Heinz", + "key": 6475, + "name": "Binford", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Roseliese", + "key": 6507, + "name": "Rashid", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Christfri", + "key": 6677, + "name": "Fullerton", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Saskia", + "key": 6681, + "name": "Labelle", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Lioba", + "key": 6715, + "name": "Burris", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Trude", + "key": 6836, + "name": "Valentino", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Annelene", + "key": 6928, + "name": "Izzo", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Marcus", + "key": 7496, + "name": "Demarco", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Ludger", + "key": 7603, + "name": "Roberson", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Sieghardt", + "key": 7741, + "name": "Tharp", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Kilian", + "key": 7752, + "name": "Rodriques", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Julia", + "key": 7770, + "name": "Yeh", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Burkhild", + "key": 7796, + "name": "Hertzler", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Karena", + "key": 7835, + "name": "Driggers", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Gertraute", + "key": 7848, + "name": "Winters", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Janett", + "key": 7877, + "name": "Staggs", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Irma", + "key": 7919, + "name": "Weathers", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Loremarie", + "key": 7990, + "name": "Milburn", + "target": 0.0 + } + ] +} diff --git a/legacy/cases/77/11_2024/web/jobs.json b/legacy/cases/77/11_2024/web/jobs.json new file mode 100644 index 00000000..24fa5e09 --- /dev/null +++ b/legacy/cases/77/11_2024/web/jobs.json @@ -0,0 +1,47 @@ +{ + "jobs": [ + { + "caseId": 77, + "completedAt": "2026-05-05T15:51:55.028Z", + "createdAt": "2026-05-05T15:46:53.054Z", + "duration": 301974, + "error": "fetch failed", + "id": "61ccbd5b-e11c-43ec-bdaf-f53b31f70ab5", + "metadata": { + "expectedSolutions": 1, + "feasibleSolutions": [], + "solutionsGenerated": 0 + }, + "params": { + "end": "2024-11-30", + "start": "2024-11-01", + "timeout": 300, + "unit": 77 + }, + "status": "failed", + "type": "solve" + }, + { + "caseId": 77, + "completedAt": "2026-05-05T15:46:32.098Z", + "consoleOutput": "\nStatistics\n - conflicts : 0\n - branches : 0\n - wall time : 0.571084 s\n - objective value: 0.0\n - status : INFEASIBLE\n - objective value: 0.0\n - info : \n2026-05-05 17:46:16,002 - INFO - Weights file not found \u2013 using default weights instead.\n2026-05-05 17:46:16,005 - INFO - Minimizing Hidden Employee Count Phase 1\n2026-05-05 17:46:16,005 - INFO - Trying to solve with {'Azubi': 0, 'Fachkraft': 0, 'Hilfskraft': 0}\n2026-05-05 17:46:16,261 - INFO - Wall time: 0.018747806549072266\n2026-05-05 17:46:16,261 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:46:16,261 - INFO - Trying to solve with {'Azubi': 5, 'Fachkraft': 5, 'Hilfskraft': 5}\n2026-05-05 17:46:16,608 - INFO - Wall time: 0.030666112899780273\n2026-05-05 17:46:16,608 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:46:16,608 - INFO - Trying to solve with {'Azubi': 10, 'Fachkraft': 10, 'Hilfskraft': 10}\n2026-05-05 17:46:17,065 - INFO - Wall time: 0.03982186317443848\n2026-05-05 17:46:17,065 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:46:17,065 - INFO - Trying to solve with {'Azubi': 15, 'Fachkraft': 15, 'Hilfskraft': 15}\n2026-05-05 17:46:17,562 - INFO - Wall time: 0.04955434799194336\n2026-05-05 17:46:17,562 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:46:17,562 - INFO - Trying to solve with {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 20}\n2026-05-05 17:46:18,189 - INFO - Wall time: 0.05961799621582031\n2026-05-05 17:46:18,189 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:46:18,189 - INFO - Trying to solve with {'Azubi': 25, 'Fachkraft': 25, 'Hilfskraft': 25}\n2026-05-05 17:46:18,903 - INFO - Wall time: 0.06971311569213867\n2026-05-05 17:46:18,903 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:46:18,903 - INFO - Trying to solve with {'Azubi': 30, 'Fachkraft': 30, 'Hilfskraft': 30}\n2026-05-05 17:46:19,692 - INFO - Wall time: 0.07868385314941406\n2026-05-05 17:46:19,692 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:46:19,692 - INFO - Trying to solve with {'Azubi': 35, 'Fachkraft': 35, 'Hilfskraft': 35}\n2026-05-05 17:46:20,569 - INFO - Wall time: 0.08717799186706543\n2026-05-05 17:46:20,569 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:46:23,848 - INFO - Hidden Employee Upper Bound: {'Azubi': 35, 'Fachkraft': 35, 'Hilfskraft': 35}\n\n2026-05-05 17:46:23,850 - INFO - Minimizing Hidden Employee Count Phase 2\n2026-05-05 17:46:23,852 - INFO - Trying to solve with {'Azubi': 34, 'Fachkraft': 35, 'Hilfskraft': 35}\n2026-05-05 17:46:24,794 - INFO - Wall time: 0.10677194595336914\n2026-05-05 17:46:24,795 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:46:24,795 - INFO - Trying to solve with {'Azubi': 35, 'Fachkraft': 34, 'Hilfskraft': 35}\n2026-05-05 17:46:25,700 - INFO - Wall time: 0.08619189262390137\n2026-05-05 17:46:25,700 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:46:25,700 - INFO - Trying to solve with {'Azubi': 35, 'Fachkraft': 35, 'Hilfskraft': 34}\n2026-05-05 17:46:26,594 - INFO - Wall time: 0.08446002006530762\n2026-05-05 17:46:26,594 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:46:26,594 - INFO - Hidden Employee Tight Bound: {'Azubi': 35, 'Fachkraft': 35, 'Hilfskraft': 35}\n\n2026-05-05 17:46:26,594 - INFO - General information:\n2026-05-05 17:46:26,594 - INFO - - planning unit: 77\n2026-05-05 17:46:26,594 - INFO - - start date: 2024-12-01\n2026-05-05 17:46:26,594 - INFO - - end date: 2024-12-31\n2026-05-05 17:46:26,594 - INFO - - number of employees: 146\n2026-05-05 17:46:26,594 - INFO - - number of days: 31\n2026-05-05 17:46:26,594 - INFO - - number of shifts: 8\n2026-05-05 17:46:29,494 - INFO - Found 4 complete weekends in the planning period\n2026-05-05 17:46:31,134 - INFO - Solving model...\n2026-05-05 17:46:31,135 - INFO - - number of variables: 40734\n2026-05-05 17:46:31,135 - INFO - - number of objectives: 10\n2026-05-05 17:46:31,135 - INFO - - number of constraints: 9\n2026-05-05 17:46:31,135 - INFO - Constraints:\n2026-05-05 17:46:31,135 - INFO - - Free Day After Night Shift Phase\n2026-05-05 17:46:31,135 - INFO - - Minimum Rest Time\n2026-05-05 17:46:31,135 - INFO - - Min Staffing\n2026-05-05 17:46:31,135 - INFO - - Rounds In Early Shift\n2026-05-05 17:46:31,135 - INFO - - One Shift Per Day\n2026-05-05 17:46:31,135 - INFO - - Target Working Time\n2026-05-05 17:46:31,135 - INFO - - Vacation Days And Shifts\n2026-05-05 17:46:31,135 - INFO - - Hierarchy Of Intermediate Shifts\n2026-05-05 17:46:31,135 - INFO - - Planned Shifts\n2026-05-05 17:46:31,135 - INFO - Objectives:\n2026-05-05 17:46:31,135 - INFO - - Free Days Near Weekend (weight: 2)\n2026-05-05 17:46:31,135 - INFO - - Minimize Consecutive Night Shifts (weight: 2)\n2026-05-05 17:46:31,135 - INFO - - Minimize Hidden Employees (weight: 100)\n2026-05-05 17:46:31,135 - INFO - - Minimize Overtime (weight: 4)\n2026-05-05 17:46:31,135 - INFO - - Not Too Many Consecutive Days (weight: 1)\n2026-05-05 17:46:31,135 - INFO - - Rotate Shifts Forward (weight: 1)\n2026-05-05 17:46:31,135 - INFO - - Maximize Employee Wishes (weight: 3)\n2026-05-05 17:46:31,135 - INFO - - Free Day After Night Shift Phase (weight: 3)\n2026-05-05 17:46:31,135 - INFO - - Every Second Weekend Free (weight: 1)\n2026-05-05 17:46:31,135 - INFO - - Preferred Block Length (weight: 1)\n2026-05-05 17:46:31,293 - INFO - Timeout set to 300 seconds\n2026-05-05 17:46:32,091 - INFO - Solving completed in 0.80 seconds\n", + "createdAt": "2026-05-05T15:46:15.889Z", + "duration": 16208, + "error": "2026-05-05 17:46:16,002 - INFO - Weights file not found \u2013 using default weights instead.\n2026-05-05 17:46:16,005 - INFO - Minimizing Hidden Employee Count Phase 1\n2026-05-05 17:46:16,005 - INFO - Trying to solve with {'Azubi': 0, 'Fachkraft': 0, 'Hilfskraft': 0}\n2026-05-05 17:46:16,261 - INFO - Wall time: 0.018747806549072266\n2026-05-05 17:46:16,261 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:46:16,261 - INFO - Trying to solve with {'Azubi': 5, 'Fachkraft': 5, 'Hilfskraft': 5}\n2026-05-05 17:46:16,608 - INFO - Wall time: 0.030666112899780273\n2026-05-05 17:46:16,608 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:46:16,608 - INFO - Trying to solve with {'Azubi': 10, 'Fachkraft': 10, 'Hilfskraft': 10}\n2026-05-05 17:46:17,065 - INFO - Wall time: 0.03982186317443848\n2026-05-05 17:46:17,065 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:46:17,065 - INFO - Trying to solve with {'Azubi': 15, 'Fachkraft': 15, 'Hilfskraft': 15}\n2026-05-05 17:46:17,562 - INFO - Wall time: 0.04955434799194336\n2026-05-05 17:46:17,562 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:46:17,562 - INFO - Trying to solve with {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 20}\n2026-05-05 17:46:18,189 - INFO - Wall time: 0.05961799621582031\n2026-05-05 17:46:18,189 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:46:18,189 - INFO - Trying to solve with {'Azubi': 25, 'Fachkraft': 25, 'Hilfskraft': 25}\n2026-05-05 17:46:18,903 - INFO - Wall time: 0.06971311569213867\n2026-05-05 17:46:18,903 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:46:18,903 - INFO - Trying to solve with {'Azubi': 30, 'Fachkraft': 30, 'Hilfskraft': 30}\n2026-05-05 17:46:19,692 - INFO - Wall time: 0.07868385314941406\n2026-05-05 17:46:19,692 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:46:19,692 - INFO - Trying to solve with {'Azubi': 35, 'Fachkraft': 35, 'Hilfskraft': 35}\n2026-05-05 17:46:20,569 - INFO - Wall time: 0.08717799186706543\n2026-05-05 17:46:20,569 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:46:23,848 - INFO - Hidden Employee Upper Bound: {'Azubi': 35, 'Fachkraft': 35, 'Hilfskraft': 35}\n\n2026-05-05 17:46:23,850 - INFO - Minimizing Hidden Employee Count Phase 2\n2026-05-05 17:46:23,852 - INFO - Trying to solve with {'Azubi': 34, 'Fachkraft': 35, 'Hilfskraft': 35}\n2026-05-05 17:46:24,794 - INFO - Wall time: 0.10677194595336914\n2026-05-05 17:46:24,795 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:46:24,795 - INFO - Trying to solve with {'Azubi': 35, 'Fachkraft': 34, 'Hilfskraft': 35}\n2026-05-05 17:46:25,700 - INFO - Wall time: 0.08619189262390137\n2026-05-05 17:46:25,700 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:46:25,700 - INFO - Trying to solve with {'Azubi': 35, 'Fachkraft': 35, 'Hilfskraft': 34}\n2026-05-05 17:46:26,594 - INFO - Wall time: 0.08446002006530762\n2026-05-05 17:46:26,594 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:46:26,594 - INFO - Hidden Employee Tight Bound: {'Azubi': 35, 'Fachkraft': 35, 'Hilfskraft': 35}\n\n2026-05-05 17:46:26,594 - INFO - General information:\n2026-05-05 17:46:26,594 - INFO - - planning unit: 77\n2026-05-05 17:46:26,594 - INFO - - start date: 2024-12-01\n2026-05-05 17:46:26,594 - INFO - - end date: 2024-12-31\n2026-05-05 17:46:26,594 - INFO - - number of employees: 146\n2026-05-05 17:46:26,594 - INFO - - number of days: 31\n2026-05-05 17:46:26,594 - INFO - - number of shifts: 8\n2026-05-05 17:46:29,494 - INFO - Found 4 complete weekends in the planning period\n2026-05-05 17:46:31,134 - INFO - Solving model...\n2026-05-05 17:46:31,135 - INFO - - number of variables: 40734\n2026-05-05 17:46:31,135 - INFO - - number of objectives: 10\n2026-05-05 17:46:31,135 - INFO - - number of constraints: 9\n2026-05-05 17:46:31,135 - INFO - Constraints:\n2026-05-05 17:46:31,135 - INFO - - Free Day After Night Shift Phase\n2026-05-05 17:46:31,135 - INFO - - Minimum Rest Time\n2026-05-05 17:46:31,135 - INFO - - Min Staffing\n2026-05-05 17:46:31,135 - INFO - - Rounds In Early Shift\n2026-05-05 17:46:31,135 - INFO - - One Shift Per Day\n2026-05-05 17:46:31,135 - INFO - - Target Working Time\n2026-05-05 17:46:31,135 - INFO - - Vacation Days And Shifts\n2026-05-05 17:46:31,135 - INFO - - Hierarchy Of Intermediate Shifts\n2026-05-05 17:46:31,135 - INFO - - Planned Shifts\n2026-05-05 17:46:31,135 - INFO - Objectives:\n2026-05-05 17:46:31,135 - INFO - - Free Days Near Weekend (weight: 2)\n2026-05-05 17:46:31,135 - INFO - - Minimize Consecutive Night Shifts (weight: 2)\n2026-05-05 17:46:31,135 - INFO - - Minimize Hidden Employees (weight: 100)\n2026-05-05 17:46:31,135 - INFO - - Minimize Overtime (weight: 4)\n2026-05-05 17:46:31,135 - INFO - - Not Too Many Consecutive Days (weight: 1)\n2026-05-05 17:46:31,135 - INFO - - Rotate Shifts Forward (weight: 1)\n2026-05-05 17:46:31,135 - INFO - - Maximize Employee Wishes (weight: 3)\n2026-05-05 17:46:31,135 - INFO - - Free Day After Night Shift Phase (weight: 3)\n2026-05-05 17:46:31,135 - INFO - - Every Second Weekend Free (weight: 1)\n2026-05-05 17:46:31,135 - INFO - - Preferred Block Length (weight: 1)\n2026-05-05 17:46:31,293 - INFO - Timeout set to 300 seconds\n2026-05-05 17:46:32,091 - INFO - Solving completed in 0.80 seconds\n", + "id": "e6af6c48-a1e0-465b-bf56-9bcd6bddc3d1", + "metadata": { + "expectedSolutions": 1, + "feasibleSolutions": [], + "solutionsGenerated": 0 + }, + "params": { + "end": "2024-12-31", + "start": "2024-12-01", + "timeout": 300, + "unit": 77 + }, + "status": "failed", + "type": "solve" + } + ] +} diff --git a/legacy/cases/77/11_2024/wishes_and_blocked.json b/legacy/cases/77/11_2024/wishes_and_blocked.json new file mode 100644 index 00000000..a2f63b3e --- /dev/null +++ b/legacy/cases/77/11_2024/wishes_and_blocked.json @@ -0,0 +1,3 @@ +{ + "employees": [] +} diff --git a/legacy/cases/77/11_2024/worked_sundays.json b/legacy/cases/77/11_2024/worked_sundays.json new file mode 100644 index 00000000..1c33992f --- /dev/null +++ b/legacy/cases/77/11_2024/worked_sundays.json @@ -0,0 +1,130 @@ +{ + "worked_sundays": [ + { + "firstname": "Margaritt", + "key": 927, + "name": "Mittrach", + "worked_sundays": 2 + }, + { + "firstname": "Daniele", + "key": 928, + "name": "Wunderlich", + "worked_sundays": 2 + }, + { + "firstname": "Kersten", + "key": 2932, + "name": "Devers", + "worked_sundays": 2 + }, + { + "firstname": "Renilde", + "key": 2963, + "name": "Hoots", + "worked_sundays": 2 + }, + { + "firstname": "Eleonore", + "key": 3004, + "name": "Lockett", + "worked_sundays": 2 + }, + { + "firstname": "Henni", + "key": 5367, + "name": "Donis", + "worked_sundays": 2 + }, + { + "firstname": "Bertram", + "key": 6180, + "name": "Putney", + "worked_sundays": 2 + }, + { + "firstname": "Roseliese", + "key": 6507, + "name": "Rashid", + "worked_sundays": 2 + }, + { + "firstname": "Hansmarti", + "key": 6538, + "name": "Guillaume", + "worked_sundays": 2 + }, + { + "firstname": "Noa", + "key": 6612, + "name": "Pettis", + "worked_sundays": 2 + }, + { + "firstname": "Edwin", + "key": 6616, + "name": "Avelar", + "worked_sundays": 2 + }, + { + "firstname": "Saskia", + "key": 6681, + "name": "Labelle", + "worked_sundays": 2 + }, + { + "firstname": "Waldfried", + "key": 637, + "name": "Heaney", + "worked_sundays": 2 + }, + { + "firstname": "Silvia", + "key": 914, + "name": "Harkins", + "worked_sundays": 2 + }, + { + "firstname": "Ingbert", + "key": 917, + "name": "Catoe", + "worked_sundays": 2 + }, + { + "firstname": "B\u00e4rbl", + "key": 924, + "name": "Merriweather", + "worked_sundays": 2 + }, + { + "firstname": "Kirstin", + "key": 6864, + "name": "Heise", + "worked_sundays": 2 + }, + { + "firstname": "Sandra", + "key": 459, + "name": "Shoemake", + "worked_sundays": 1 + }, + { + "firstname": "Lina", + "key": 925, + "name": "Farniok", + "worked_sundays": 1 + }, + { + "firstname": "Belinda", + "key": 6714, + "name": "Griffey", + "worked_sundays": 1 + }, + { + "firstname": "Sonnhilde", + "key": 6769, + "name": "Ingraham", + "worked_sundays": 1 + } + ] +} diff --git a/legacy/cases/77/12_2024/employee_types.json b/legacy/cases/77/12_2024/employee_types.json new file mode 100644 index 00000000..1981e587 --- /dev/null +++ b/legacy/cases/77/12_2024/employee_types.json @@ -0,0 +1,525 @@ +{ + "Azubi": [ + "A-Altenpflegehelfer/in (A-82101-002)", + "A-Altenpfleger/in (A-82102-002)", + "A-An\u00e4sthesietechnisch/r Assistent/in (A-81332-001)", + "A-CTA (Chirurgisch-technische/r Assistent/in) (A-81332-005)", + "A-Fachkaufkann/-frau-Verwaltung im Gesundheitswesen (A-73223-015)", + "A-Fachkraft-Lagerlogistik (A-51312-005)", + "A-Gesundheits- und Kinderkrankenpfleger/in (A-81302-003)", + "A-Gesundheits- und Krankenpfleger/in (A-81302-005)", + "A-Hebamme/Entbindungspfleger (A-81353-003)", + "A-Hilfskoch/-k\u00f6chin (A-29302-018)", + "A-Informatiker/in (Weiterbildung) (A-43103-008)", + "A-Kaufmann/-frau-Gesundheitswesen (A-73222-007)", + "A-Kinderkrankenschwester/-pfleger (A-81302-007)", + "A-Koch/-K\u00f6chin (29302-024)", + "A-Krankenschwester/-pfleger (A-81302-008)", + "A-Medizinische/r Fachangestellte/r (A-81102-004)", + "A-Medizinisch-technisch/r Radiologieassistent/in (A-81232-004)", + "A-Notfallsanit\u00e4ter A-31342-005", + "A-OTA (Operationstechnische/r Assistent/in) (A-81332-009)", + "A-Pflegeassistent/in (A-81302-014)", + "A-Pflegefachkraft - Gesundheits- und Krankenpfleger (81302-015)", + "A-Pflegefachkraft (Altenpflege) (A-81302-019)", + "A-Pflegefachkraft (Krankenpflege) (A-81302-018)", + "A-Pflegefachkraft-Kinderkrankenpflege (A-81302-016)", + "A-Pflegefachmann/-frau (A-81302-028)", + "A-Stationshelfer/in - Krankenpflege (A-81301-017)" + ], + "Fachkraft": [ + "Abteilungsleiter/in (71394-003)", + "Alleinsekret\u00e4r/in (71402-004)", + "Allgemeinarzt/-\u00e4rztin (81404-001)", + "Allgemeinchirurg/in (81434-001)", + "Altenpfleger/in (82102-002)", + "Ambulante Krankenschwester/-pfleger (81302-001)", + "Ambulanzpfleger/schwester (81382-001)", + "An\u00e4sthesietechnische/r Assistent/in (81332-001)", + "An\u00e4sthesist/in (81454-002)", + "Archivar/in (73314-001)", + "Archivfachkraft (73312-004)", + "Arzt/\u00c4rztin - Allgemeinmedizin (81404-003)", + "Arzt/\u00c4rztin - An\u00e4sthesiologie und Intensivtherapie (81454-003)", + "Arzt/\u00c4rztin - Chirurgie (81434-002)", + "Arzt/\u00c4rztin - Hygiene (81484-006)", + "Arzt/\u00c4rztin - Innere Medizin (81424-001)", + "Arzt/\u00c4rztin - Kinder- und Jugendpsychiatrie (81464-001)", + "Arzt/\u00c4rztin - klinische Strahlenphysik (81234-001)", + "Arzt/\u00c4rztin - Neurochirurgie (81434-004)", + "Arzt/\u00c4rztin - Neurologie (81464-002)", + "Arzt/\u00c4rztin - Orthop\u00e4die (81434-005)", + "Arzt/\u00c4rztin - Radiologie (81234-003)", + "Arzt/\u00c4rztin - Traumatologie und Orthop\u00e4die (81434-006)", + "Arzt/\u00c4rztin (81404-002)", + "\u00c4rztliche/r Direktor/in (Humanarzt/-\u00e4rztin) (81494-001)", + "\u00c4rztliche/r Leiter/in (81494-002)", + "Arztsekret\u00e4r/in (73222-001)", + "Assistent/in - Gesch\u00e4ftsleitung (71403-001)", + "Assistent/in - Gesundheitswesen (73222-003)", + "Assistent/in - Operationstechnik (81332-003)", + "Assistent/in - Rechnungswesen (72213-006)", + "Assistenzarzt/-\u00e4rztin - Kinder-/Jugendpsychiatrie und -psychologie (81464-006)", + "Assistenzarzt/-\u00e4rztin (81404-004)", + "Assistenzarzt/-\u00e4rztin (Uni) - An\u00e4sthesiologie (81454-004)", + "Assistenzarzt/-\u00e4rztin (Uni) - Chirurgie (81434-007)", + "Assistenzarzt/-\u00e4rztin (Uni) - Innere Medizin (81424-004)", + "Assistenzarzt/-\u00e4rztin (Uni) - Kinder-/Jugendmedizin (81414-002)", + "Assistenzarzt/-\u00e4rztin (Uni) - Neurologie (81464-009)", + "ATA (An\u00e4sthesietechnische/r Assistent/in) (81332-004)", + "Augenoptikermeister/in (82593-001)", + "Ausbilder Berufsbildungswerk (84223-013)", + "Ausbilder Berufsf\u00f6rderungswerk", + "Bachelor of Arts - Soziale Arbeit/Soziale Dienste (83123-003)", + "Bachelor of Arts - Sportwissenschaft (84503-002)", + "Bankkaufmann/-frau (72112-004)", + "Beikoch/-k\u00f6chin (29302-005)", + "Bereichsleiter/in (71394-006)", + "Berufsp\u00e4dagoge/-p\u00e4dagogin (84224-003)", + "Besch\u00e4ftigungstherapeut/in (81723-004)", + "Betriebselektriker/in (26252-007)", + "Betriebsg\u00e4rtner/in (12102-001)", + "Betriebstechniker/in (25103-011)", + "Betriebswirt/in (Weiterbildung) - Rechnungswesen (72213-014)", + "Bibliothekar/in (73324-004)", + "Bilanzbuchhalter/in (72213-016)", + "Buchhalter/in (72213-019)", + "B\u00fcrofachkraft (71402-010)", + "B\u00fcrokaufmann/-frau (71402-012)", + "Chefarzt/-\u00e4rztin (81494-003)", + "Chefsekret\u00e4r/in (71403-008)", + "Chirurg/in (81434-008)", + "Coach Erw.Bildung (84404-010)", + "Controller/in (72234-005)", + "Controllingleiter/in (72294-006)", + "CTA (Chirurgisch-technische/r Assistent/in) (81332-005)", + "Datenverarbeitungsfachmann/-frau (43103-005)", + "Dauernachtwache (Krankenschwester/-pfleger) (81302-002)", + "Diabetesberater/in (81783-002)", + "Di\u00e4tassistent/in (81762-001)", + "EDV Sachbearbeiter (41402-017)", + "EDV-Systemtechniker/in (26312-040)", + "EDV-Techniker/in (43103-007)", + "EEG-Assistent/in (81222-009)", + "Einkaufsleiter/in (61194-006)", + "Einkaufslogistiker/in (61113-015)", + "Einzelhandelskaufmann/-frau (62102-002)", + "Elektroanlagenelektroniker/in (26252-050)", + "Elektroinstallateur/in (26212-017)", + "Elektroniker/in - Energie- und Geb\u00e4udetechnik (26212-019)", + "Elektroniker/in - Ger\u00e4te und Systeme (26302-033)", + "Endoskopieschwester/-pfleger (81313-003)", + "Ergotherapeut/in (81723-006)", + "Ern\u00e4hrungsberater/in (82233-005)", + "Ern\u00e4hrungswissenschaftler/in (82284-003)", + "Erzieher/in - Heilp\u00e4dagogik (83133-002)", + "Erzieher/in (83112-006)", + "Erziehungswissenschaftler/in (91334-007)", + "EXAH-Pflegefachkraft (Altenpflege) (A-81302-019)", + "EXGK-Pflegefachkraft - Gesundheits- und Krankenpflege (A-81302-015)", + "EXKI-Pflegefachkraft.Kinderkrankenpflege (A-81302-016)", + "EXKP-Pflegefachkraft- (Krankenpflege) (A-81302-018)", + "EX-Pflegefachmann/- frau (A-81302-028)", + "Fachangestellte/r f\u00fcr B\u00fcrokommunikation (71402-021)", + "Facharzt Neurochirurgie", + "Facharzt Psychiatrie u. Psychotherapie", + "Facharzt/-\u00e4rztin - Allgemeinchirurgie (81434-027)", + "Facharzt/-\u00e4rztin - Allgemeine Chirurgie (81434-009)", + "Facharzt/-\u00e4rztin - Allgemeinmedizin (81404-005)", + "Facharzt/-\u00e4rztin - Allgemeinmedizin (Hausarzt/-\u00e4rztin) (81404-010)", + "Facharzt/-\u00e4rztin - An\u00e4sthesiologie (81454-005)", + "Facharzt/-\u00e4rztin - Frauenheilkunde und Geburtshilfe (81444-017)", + "Facharzt/-\u00e4rztin - Gef\u00e4\u00dfchirurgie (81434-010)", + "Facharzt/-\u00e4rztin - Hals-Nasen-Ohrenheilkunde (81444-018)", + "Facharzt/-\u00e4rztin - Innere Medizin (81424-006)", + "Facharzt/-\u00e4rztin - Innere Medizin u. Allgemeinm. (Hausarzt) (81404-006)", + "Facharzt/-\u00e4rztin - Innere Medizin u. H\u00e4matolog. u. Onkologie (81424-010)", + "Facharzt/-\u00e4rztin - Innere Medizin und Gastroenterologie (81424-009)", + "Facharzt/-\u00e4rztin - Innere Medizin und Kardiologie (81424-011)", + "Facharzt/-\u00e4rztin - Innere Medizin und Nephrologie (81424-012)", + "Facharzt/-\u00e4rztin - Innere Medizin und Pneumologie (81424-013)", + "Facharzt/-\u00e4rztin - Kinder- u. Jugendpsychiat. u. -psychoth. (81464-010)", + "Facharzt/-\u00e4rztin - Kinder- und Jugendmedizin (81414-003)", + "Facharzt/-\u00e4rztin - Neurologie (81464-012)", + "Facharzt/-\u00e4rztin - \u00d6ffentliches Gesundheitswesen (81484-028)", + "Facharzt/-\u00e4rztin - Orthop\u00e4die und Unfallchirurgie (81434-013)", + "Facharzt/-\u00e4rztin - P\u00e4diatrie (81414-005)", + "Facharzt/-\u00e4rztin - Plastische und \u00c4sthetische Chirurgie (81434-014)", + "Facharzt/-\u00e4rztin - Radiologie (81234-005)", + "Facharzt/-\u00e4rztin - Thoraxchirurgie (81434-015)", + "Facharzt/-\u00e4rztin - Unfallchirurgie (81434-016)", + "Facharzt/-\u00e4rztin - Viszeralchirurgie (81434-017)", + "Fachinformatiker/in - Systemintegration (43102-014)", + "Fachinformatiker/in (43102-013)", + "Fachkaufmann/-frau - Logistik (51623-005)", + "Fachkaufmann/-frau - Verwaltung im Gesundheitswesen (73223-015)", + "Fachkinderkrankenpfleger/in - Intensivpflege/An\u00e4sthesie (81323-005)", + "Fachkinderkrankenschwester/-pfleger - ambulante Pflege (81323-002)", + "Fachkinderkrankenschwester/-pfleger - P\u00e4diatrie und Intensivmedizin (81323-011)", + "Fachkinderkrankenschwester/-pfleger - Psychiatrie (81323-013)", + "Fachkraft - Altenpflege (82102-004)", + "Fachkraft - Factoring (72213-033)", + "Fachkraft - Lagerlogistik (51312-005)", + "Fachkraft - Lagerwirtschaft (51312-007)", + "Fachkraft - Sozialarbeit (83123-004)", + "Fachkrankenpfleger/in - Nephrologie", + "Fachkrankenpfleger/in - Notfallpflege (81313-059)", + "Fachkrankenpfleger/in - Operations-/Endoskopiedienst (81313-014)", + "Fachkrankenschwester/-pfleger - Endoskopie (81313-006)", + "Fachkrankenschwester/-pfleger - Intensivmedizin und An\u00e4sthesie (81313-008)", + "Fachkrankenschwester/-pfleger - Intensivpflege/An\u00e4sthesie (81313-009)", + "Fachkrankenschwester/-pfleger - Operationsdienst (81313-015)", + "Fachlehrer/in - arbeitstechnische F\u00e4cher (84214-008)", + "Fachlehrer/in - Pflegeberufe (84213-094)", + "Fachlehrer/in (84214-006)", + "Fachschwester/-pfleger - Intensivpflege und An\u00e4sthesie (81313-025)", + "Fachwirt Immobilien (61313-009)", + "Finanzbuchhalter/in (72213-038)", + "G\u00e4rtner/in (12102-007)", + "Gas- und Wasserinstallateur/in (34212-005)", + "Gastroenterologe/Gastroenterologin (81424-015)", + "Gehaltsabrechner/in (72213-042)", + "Gesch\u00e4ftsf\u00fchrende/r Direktor/in (\u00f6ffentliche Verwaltung) (73294-015)", + "Gesch\u00e4ftsf\u00fchrer/in (71104-008)", + "Gesundheits- und Kinderkrankenpfleger/in (81302-003)", + "Gesundheits- und Krankenpflegeassistent/in (81302-004)", + "Gesundheits- und Krankenpfleger/in (81302-005)", + "Gesundheitscoach (82212-001)", + "Gesundheits\u00f6konom/in (82214-004)", + "Gesundheitspfleger/in (81302-006)", + "Gymnastiklehrer/in (84553-016)", + "Haushaltsfachkraft (83212-005)", + "Hausmeister/in (34102-007)", + "Haustechniker/in (34102-008)", + "Hauswart/in (34102-009)", + "Hauswirtschafter/in (83212-007)", + "Hauswirtschaftsverwalter/in (83293-005)", + "Hebamme/Entbindungspfleger (81353-003)", + "Heilerziehungspfleger/in (83132-006)", + "Heilp\u00e4dagoge/-p\u00e4dagogin (83134-002)", + "Heilpraktiker/in (81752-002)", + "Heizungs- und Sanit\u00e4rinstallateur/in (834212-011)", + "Heizungsmonteur/in (34212-015)", + "Honorar-Arzt", + "Honorardozent/in (84304-032)", + "Hospizleiter/in (81394-002)", + "Hygienefachkraft (53322-010)", + "Industriekaufmann/-frau (71302-011)", + "Informatiker (Hochschule) (43104-002)", + "Informatiker/in (Weiterbildung) (43103-008)", + "Integrationsberater", + "Integrationsmanager/in (83134-005)", + "Internatsbetreuer", + "Internist/in (81424-017)", + "IT-Administrator/in (43343-031)", + "IT-Leiter/in (43394-007)", + "Jugenderzieher/in (83112-025)", + "Jugendpsychologe/-psychologin (81624-007)", + "Jurist/in (73104-001)", + "Kardiologe/Kardiologin (81424-018)", + "Kaufmann/-frau - Gesundheitswesen (73222-007)", + "Kaufm\u00e4nnische/r Angestellte/r (71302-015)", + "Kaufm\u00e4nnische/r Direktor/in (71104-015)", + "Kaufm\u00e4nnische/r Sachbearbeiter/in (71302-018)", + "Kinder- und Jugendlichenpsychotherapeut/in (81634-004)", + "Kinder- und Jugendpsychologe/-psychologin (81624-008)", + "Kinder- und Jugendpsychotherapeut/in (81634-005)", + "Kinderarzt/-\u00e4rztin (81414-006)", + "Kinderg\u00e4rtner/in (83112-032)", + "Kinderkrankenschwester/-pfleger - Psychiatrie (81323-035)", + "Kinderkrankenschwester/-pfleger (81302-007)", + "Kinderp\u00e4dagoge/-p\u00e4dagogin (83112-033)", + "Kinderpsychologe/-psychologin (81624-009)", + "Kindheitsp\u00e4dagoge/-p\u00e4dagogin (91334-005)", + "Klinische Kodierfachkraft (71442-004)", + "Klinische/r Neuropsychologe/-psychologin (81624-010)", + "Koch/K\u00f6chin (29302-024)", + "Kodierer/in (71442-005)", + "Kostenabrechner/in (72223-011)", + "Krankengymnast/in (81713-009)", + "Krankenhaussekret\u00e4r/in (73222-009)", + "Krankenschwester/-pfleger - An\u00e4sthesie (81313-037)", + "Krankenschwester/-pfleger - Nachtwache (81302-009)", + "Krankenschwester/-pfleger - Nephrologie (81313-043)", + "Krankenschwester/-pfleger (81302-008)", + "Krankentransportleiter/in (52182-021)", + "K\u00fcchenchef (29394-004)", + "K\u00fcchenleiter/in (29394-007)", + "K\u00fcster/in (83382-005)", + "Lagerverwalter/in (Warenlager) (51312-030)", + "Lehrer/in - Gesundheitsfachberufe (84213-031)", + "Lehrer/in - Krankenpflege (84213-038)", + "Lehrer/in - Pflegeberufe (84213-044)", + "Leitende Pflegefachkraft (81393-002)", + "Leitende/r Arzt/\u00c4rztin (81494-004)", + "Leitende/r Entbindungspfleger/Hebamme/Bereichsleitung Frauenklinik (81393-003)", + "Leitende/r Gesundheits- und Krankenschwester/-pfleger (81393-004)", + "Leiter/in - ambulante Sozialdienste (83194-027)", + "Leiter/in - Einkauf (61194-013)", + "Leiter/in - Finanz- und Rechnungswesen (72294-008)", + "Leiter/in - Krankenhausbetriebstechnik (34193-009)", + "Leiter/in - Medizincontrolling (82214-014)", + "Leiter/in - Presse- und \u00d6ffentlichkeitsarbeit (92294-001)", + "Leiter/in - soziale Einrichtung (83194-015)", + "Leiter/in - Technik (27394-016)", + "Logop\u00e4de/Logop\u00e4din (81733-006)", + "Logop\u00e4de/Logop\u00e4din (Hochschule) (81734-003)", + "Lohnsachbearbeiter/in (72213-063)", + "Maler/in (33212-019)", + "Marketingsekret\u00e4r/in (71402-042)", + "Masseur/in (81712-004)", + "Masseur/in und medizinische/r Bademeister/in (81712-005)", + "Master of Science - Gesundheits\u00f6konomie (82214-009)", + "Master of Science - Projektmanagement (71304-017)", + "Mediengestalter/in - Digital-/Printmedien - Medienberatung (61122-015)", + "Medientechniker/in (23213-015)", + "Medizinallaborant/in (Humanmedizin) (81212-018)", + "Medizincontroller/in (72234-025)", + "Mediziner/in (81404-009)", + "Medizinische/r Assistent/in (81212-022)", + "Medizinische/r Fachangestellte/r (81102-004)", + "Medizinische/r Praxisassistent/in (81102-006)", + "Medizinisch-kaufm\u00e4nnische/r Assistent/in (73222-010)", + "Medizinisch-technische/r Assistent/in (81212-019)", + "Medizinisch-technische/r Fachassistent/in - radiologische Diagnostik (81233-011)", + "Medizinisch-technische/r Fachassistent/in (81213-023)", + "Medizinisch-technische/r Laboratoriumsassistent/in (81212-021)", + "Medizinisch-technische/r Radiologieassistent/in MTRA (81232-004)", + "MTA (Medizinisch-technische/r Laboratoriumsassistent/in 81212-031)", + "Musiker/in (94114-044)", + "Netzadministrator/in (EDV) (43343-016)", + "Netzwerkadministrator (43343-018)", + "Neurologe/Neurologin (81464-020)", + "Notfallsanit\u00e4ter (81342-005)", + "Oberarzt/-\u00e4rztin (81494-007)", + "\u00d6ffentlichkeitsreferent/in (92203-014)", + "\u00d6kotrophologe/\u00d6kotrophologin (82284-009)", + "Operationstechnische/r Assistent/in (81332-008)", + "Organist/in (94114-059)", + "Orthop\u00e4de/Orthop\u00e4din (81434-021)", + "OTA (Operationstechnische/r Assistent/in) (81332-009)", + "P\u00e4dagogische/r Betreuer/in (83124-033)", + "P\u00e4dagogische/r Mitarbeiter/in (91334-024)", + "P\u00e4diatrie-Assistent/in (81182-008)", + "Personalbuchhalter/in (72213-071)", + "Personalchef/in (71594-011)", + "Personalfachkaufmann/-kauffrau (71513-010)", + "Personalkaufmann/-frau (71512-004)", + "Personalleiter/in (71594-013)", + "Personalsachbearbeiter/in (71512-005)", + "Pflegeassistent/in (81302-014)", + "Pflegebereichsleiter/in (81394-013)", + "Pflegedienstleiter/in (81394-014)", + "Pflegedirektor/in (81394-032)", + "Pflegefachkraft - An\u00e4sthesie/Intensivmedizin (81313-052)", + "Pflegefachkraft - Gesundheits- und Krankenpfleger (81302-015)", + "Pflegefachkraft - Kinderkrankenpflege (81302-016)", + "Pflegefachkraft - Kinderpflege (81302-017)", + "Pflegefachkraft - Sozialstation (81302-019)", + "Pflegefachkraft (Krankenpflege) (81302-018)", + "Pflegefachmann/-frau (81302-028)", + "Pflegeleiter/in (81394-017)", + "Pflegemanager/in (81394-018)", + "Pflegemanager/in (81394-019)", + "Pflegep\u00e4dagoge/-p\u00e4dagogin (84214-065)", + "Pf\u00f6rtner/in (53112-066)", + "Physician Assistant (81333-001)", + "Physiker/in (41404-002)", + "Physiotherapeut/in (81713-015)", + "Physiotherapeut/in (Hochschule) (81714-002)", + "Praxisanleiter/in - Pflegeberufe (84213-085)", + "Praxisanleiter/-innen (84223-081)", + "Projektmanager/in (71393-007)", + "Prokurist/in (71104-017)", + "Psychologe/Psychologin - allgemeine Psychologie (81624-016)", + "Psychologe/Psychologin (81624-015)", + "Psychologische/r Psychotherapeut/in (81634-014)", + "Qualit\u00e4tsbeauftragte/r - Gesundheits-/Sozialwesen (82243-003)", + "Qualit\u00e4tsbeauftragte/r (27313-022)", + "Qualit\u00e4tsbeauftragter/-beauftragte - Management (27313-023)", + "Radioonkologische/r Assistent/in (81232-010)", + "Rechnungswesensachbearbeiter/in (72212-026)", + "Referent/in - berufliche Fort- und Weiterbildung", + "Referent/in - \u00d6ffentlichkeitsarbeit/Marketing (92203-023)", + "Reha Berater (71524-028)", + "Rehabilitationsp\u00e4dagoge/-p\u00e4dagogin (83134-009)", + "Reinigungsfachkraft /54112-018)", + "Rettungsassistent/in", + "Rettungssanit\u00e4ter/in", + "Rezeptionsmitarbeiter/in (Arztpraxis) (73222-012)", + "Rohrinstallateur/in (34212-040)", + "R\u00f6ntgenassistent/in (81232-011)", + "Sachbearbeiter/in - B\u00fcro (71402-058)", + "Sachbearbeiter/in - Verwaltung (geh. nichttechn. Dienst) (73203-017)", + "Sachbearbeiter/in (71302-021)", + "Schreibkraft (71432-031)", + "Schularzt/-\u00e4rztin (81414-008)", + "Schulleiter/in - Berufsschulen (8494-033)", + "Schulsozialarbeiter/in (83124-035)", + "Schwester/Pfleger (Kinderkrankenpflege) (81302-025)", + "Schwester/Pfleger (Krankenpflege) (81302-026)", + "Seelsorger/in (83314-034)", + "Sekret\u00e4r/in - Gesundheitswesen (73222-013)", + "Sekret\u00e4r/in (71402-062)", + "Sekretariatsleiter/in (71493-011)", + "Seniorenbetreuer/in (82102-005)", + "Seniorenpfleger/in (82102-006)", + "Seniorenzentrumsleiter/in (82194-013)", + "Servicekraft (63302-045)", + "Sicherheitsfachkraft (53123-009)", + "Sozialarbeiter/in (83124-037)", + "Sozialarbeiter/in / Sozialp\u00e4dagoge/-p\u00e4dagogin (83124-052)", + "Sozialp\u00e4dagoge/-p\u00e4dagogin (83124-039)", + "Sozialwissenschaftler/in (91324-015)", + "Sportlehrer/in - Rehabilitation/Behindertensport (83133-016)", + "Sportlehrer/in (84503-023)", + "Sportphysiotherapeut/in (81713-016)", + "Sporttherapeut (81783-013)", + "Sprechstundenhilfe (81102-009)", + "Stationsassistent/in (Arzthilfe) (81102-010)", + "Stationsleiter/in - Kranken-/Alten-/Kinderkrankenpflege (81393-012)", + "Stationsleiter/in - Krankenpflege (81393-009)", + "Stationsleiter/in - Krankenpflege/Altenpflege (81393-010)", + "Stationsleiter/in - Pflegedienst (81393-011)", + "Stenograf/in (71432-036)", + "Apothekenhelfer/in (62412-001)", + "Sterilisationsassistent/in (81182-002)", + "Suchtpsychologe/-psychologin (81624-022)", + "Techn. Assistent/in - Bautechnik (31102-008)", + "Techn.Produktdesigner (27212-075)", + "Techniker/in - Anwendungs-/Betriebstechnik (Farben, Lacke) (22203-016)", + "Techniker/in - Elektrotechnik (26303-015)", + "Technische/r B\u00fcrosachbearbeiter/in (71402-068)", + "Technische/r Koordinator/in (27304-054)", + "Technische/r Leiter/in (27394-024)", + "General (01104-008)", + "Technische/r Sterilisationsassistent/in (81182-003)", + "Telefonist/in (71401-042)", + "Hauselektriker/in (26212-033)", + "Terminsachbearbeiter/in (27302-010)", + "Therapeut/in - Krankengymnastik (81713-018)", + "Thoraxchirurg/in (81434-024)", + "\u00dcbungsleiter/in (84503-031)", + "Uhrenmacher (24532-032)", + "Uhrmachermeister (24593-041)", + "Elektrochemiker/in (41384-009)", + "Unterrichtspfleger/-schwester (84213-078)", + "Farb- und Lacktechniker/in (22203-003)", + "Verwalter/in - Tierzucht (11294-009)", + "Verwaltungsangestellte/r - Krankenk.,Krankenh\u00e4user, Kliniken (73222-016)", + "Gesundheits- und Krankenschwester/-pfleger - Gerontopsych.", + "Verwaltungsangestellter (mittl.Dienst) kirchl. Dienst", + "Verwaltungsfachangestellte/-angestellter - Kirchenverwaltung -evangelische Kirche (73282-010)", + "Visceralchirurg/in (81434-026)", + "Vorarbeiter (27302-012)", + "Vorzimmersekret\u00e4r/in (71402-071)", + "Weiterbildungsassistent/in (Arzt/\u00c4rztin) (81404-011)", + "Werbedesigner/in (23224-072)", + "Betriebsschlosser/in (25102-010)", + "Wirtschaftswissenschaftler/in (91404-011)", + "Wundmanager/in (81383-005)", + "Apparate- und Maschinenschlosser/in (34342-005)", + "Arzthelfer/in (81102-001)", + "Zahnarzthelfer/in (81112-006)" + ], + "Hilfskraft": [ + "Ableger/in 871401-001", + "Abrechnungspr\u00fcfer/in (72214-001=", + "Abschlussagent/in (Versicherung) (72133-001)", + "Alltagsbetreuer/in (83142-001)", + "Altenbetreuerhelfer/in (82101-001)", + "Altenpflegeassistent/in (1 j\u00e4hrige A.) (82101-008)", + "Altenpflegehelfer/in (1 j\u00e4hrige Ausb.) (82101-002)", + "Altenpflegehilfskraft (82101-003)", + "Anmelder/in (71404-010)", + "Anstreicher/in (33212-001)", + "Archivhelfer/in (71401-011)", + "Archivsachbearbeiter/in", + "Aufr\u00e4umer/in (Raum-, Hausratreiniger/in) (54101-002)", + "Ausbaufacharbeiter/in - Malerarbeiten (33212-003)", + "Ausbauhelfer/in (33301-001)", + "Aushilfsfahrer/in (52182-006)", + "Aushilfskraft (K\u00fcche) (29301-002)", + "Auskunftsgehilfe/-gehilfin (71401-013)", + "Auslader/in (Transportarbeiter/in) (51311-015)", + "Azetylenschwei\u00dfer/in (24422-010)", + "Betreuungshelfer/in (83111-002)", + "Betreuungskraft / Alltagsbegleiter/in (83142-003)", + "Betriebshandweker/in (25102-007)", + "Betriebshilfering-Gesch\u00e4ftsf\u00fchrer/in (11124-002)", + "Bote/Botin (B\u00fcro) (51321-009)", + "Bundesfreiwilligendienst (BFD)", + "B\u00fcroassistent/in (71402-009)", + "B\u00fcrogehilf(e/in) (71402-011)", + "B\u00fcrohilfskraft (71401-019)", + "externes Pers., Honorar", + "Fachkraft - Pflegeassistenz (83142-004)", + "Fahrbetriebsregler/in (Stra\u00dfenverkehr) (51512-003)", + "Fernsprechvermittler/in (71401-027)", + "Freiwilliges Soziales Jahr (FSJ)", + "Geb\u00e4udeinnenreiniger/in (54112-012)", + "Gesundheits- und Krankenpflegehelfer/in (81301-001)", + "Glasreiniger/in (54122-003)", + "Gr\u00fcnanlagenpfleger/in (12101-012)", + "Hausarbeitsgehilfe/-gehilfin (83211-002)", + "Haushaltshilfe (83211-005)", + "Hauswirtschaftsgehilfe/-gehilfin (83212-014)", + "Hauswirtschaftshelfer/in/-assistent/in (83212-015)", + "Hebammenhelfer/in (81352-002)", + "Helfer Reinigung (54101-014)", + "Helfer/in - Altenpflege/Pers\u00f6nliche Assistenz (82101-004)", + "Helfer/in - B\u00fcro, Verwaltung (71401-033)", + "Helfer/in - B\u00fcro, Verwaltung (71401-034)", + "Helfer/in - Gartenbau (12101-016)", + "Helfer/in - Gr\u00fcnanlagen (12101-017)", + "Helfer/in - Hauswirtschaft (83211-001)", + "Helfer/in - K\u00fcche (29301-005)", + "Helfer/in - Rettungsdienst (81341-002)", + "Helfer/in - Schachtarbeiten (32201-010)", + "Helfer/in - station\u00e4re Krankenpflege (81301-002)", + "Helfer/in - Warenmalerei, -lackiererei (22201-004)", + "Hilfskoch/-k\u00f6chin (29302-018)", + "Hilfsmonteuer (Elektro) (26301-046)", + "Hilfsschwester/-pfleger (81301-003)", + "Jahrespraktikant/-in (JP)", + "Kaufm\u00e4nnische B\u00fcrokraft (71402-034)", + "Kochhelfer/in (29301-007)", + "Kranken- und Altenpflegehelfer/in (81301-005)", + "Krankenfahrer/in (52182-019)", + "Krankenpflegehelfer/in (1 j\u00e4hrige A.) (81301-006)", + "Krankentransporteur/in (52182-020)", + "K\u00fcchengehilfe/-gehilfin (29301-009)", + "K\u00fcchenhelfer/in (29301-010)", + "K\u00fcchenhilfe (29301-011)", + "Lagerarbeiter/in (51311-068)", + "Lagerhelfer/in (51311-070)", + "Lagerhilfsarbeiter/in (51311-071)", + "Maschinenhelfer/in (25101-014)", + "Medizinische/r Fachhelfer/in (81102-005)", + "Menu/Datenerfasser/-in (71401-022)", + "Pflegediensthelfer/in (81301-008)", + "Pflegefachhelfer/in (Krankenpflege)", + "Pflegehelfer Behindertenpflege", + "Pflegehelfer/in - Altenpflege (ohne 1 j\u00e4hrige A.) (82101-006)", + "Pflegehelfer/in - station\u00e4re Pflege (ohne 1 j\u00e4hrige A.) (81301-010)", + "Pflegehelfer/in (Krankenpflege) (ohne 1 j\u00e4hrige A.) (81301-011)", + "Pflegehilfskraft (Krankenpflege) (81301-012)", + "Praktikant/-in (P)", + "Praktisches Jahr (PJ)", + "Raumpfleger/in (54101-023)", + "Rettungshelfer/in (81341-005)", + "R\u00f6ntgenhelfer/in (81232-012)", + "Saisonhelfer/in (11101-035)", + "Schwesterhelfer/in (81301-014)", + "Schwestern-/Pflegediensthelfer/in (81301-015)", + "Servicehilfskraft (63301-044)", + "Sozialassistent/in (83142-006)", + "Sportassistent/in (63122-005=", + "Sp\u00fclmann/-frau (Hausratreiniger/in) (54101-031)", + "Stationshelfer/in - Krankenpflege (81301-017)", + "Stationshilfe (81301-018)", + "Transporthelfer/in (51311-109)", + "Verwaltungsgehilfe/-gehilfin (73201-009)", + "W\u00e4scheschneider/in (28222-155)" + ] +} diff --git a/legacy/cases/77/12_2024/employees.json b/legacy/cases/77/12_2024/employees.json new file mode 100644 index 00000000..ced1c127 --- /dev/null +++ b/legacy/cases/77/12_2024/employees.json @@ -0,0 +1,250 @@ +{ + "employees": [ + { + "firstname": "Adriane", + "key": 790, + "name": "Mccomas", + "type": "Stationshilfe (81301-018)" + }, + { + "firstname": "Janett", + "key": 791, + "name": "Branz", + "type": "Altenpfleger/in (82102-002)" + }, + { + "firstname": "Mirjam", + "key": 843, + "name": "Flanagan", + "type": "Arzthelfer/in (81102-001)" + }, + { + "firstname": "Silvia", + "key": 914, + "name": "Harkins", + "type": "Krankenpflegehelfer/in (1 j\u00e4hrige A.) (81301-006)" + }, + { + "firstname": "Ingbert", + "key": 917, + "name": "Catoe", + "type": "Krankenschwester/-pfleger (81302-008)" + }, + { + "firstname": "Jenny", + "key": 921, + "name": "Keese", + "type": "Altenpfleger/in (82102-002)" + }, + { + "firstname": "B\u00e4rbl", + "key": 924, + "name": "Merriweather", + "type": "Gesundheits- und Krankenpfleger/in (81302-005)" + }, + { + "firstname": "Lina", + "key": 925, + "name": "Farniok", + "type": "Gesundheits- und Krankenpfleger/in (81302-005)" + }, + { + "firstname": "Margaritt", + "key": 927, + "name": "Mittrach", + "type": "Stationshilfe (81301-018)" + }, + { + "firstname": "Daniele", + "key": 928, + "name": "Wunderlich", + "type": "Gesundheits- und Krankenpfleger/in (81302-005)" + }, + { + "firstname": "Lidia", + "key": 946, + "name": "Macdowell", + "type": "Stationsleiter/in - Pflegedienst (81393-011)" + }, + { + "firstname": "Daniela", + "key": 1144, + "name": "Dalessandro", + "type": "Gesundheits- und Krankenpfleger/in (81302-005)" + }, + { + "firstname": "Constance", + "key": 1230, + "name": "Palacio", + "type": "Pflegefachkraft (Krankenpflege) (81302-018)" + }, + { + "firstname": "Kersten", + "key": 2932, + "name": "Devers", + "type": "Pflegefachkraft (Krankenpflege) (81302-018)" + }, + { + "firstname": "Renilde", + "key": 2963, + "name": "Hoots", + "type": "Altenpfleger/in (82102-002)" + }, + { + "firstname": "Elgine", + "key": 3566, + "name": "Seligman", + "type": "Altenpfleger/in (82102-002)" + }, + { + "firstname": "Eike", + "key": 3868, + "name": "Vanfleet", + "type": "Pflegefachkraft (Krankenpflege) (81302-018)" + }, + { + "firstname": "Hannah", + "key": 4566, + "name": "Woodcock", + "type": "A-Pflegeassistent/in (A-81302-014)" + }, + { + "firstname": "Henni", + "key": 5367, + "name": "Donis", + "type": "Krankenpflegehelfer/in (1 j\u00e4hrige A.) (81301-006)" + }, + { + "firstname": "Sturmius", + "key": 5652, + "name": "Sherrard", + "type": "A-Pflegefachkraft (Krankenpflege) (A-81302-018)" + }, + { + "firstname": "Ernestine", + "key": 5663, + "name": "Cerna", + "type": "A-Pflegefachkraft (Krankenpflege) (A-81302-018)" + }, + { + "firstname": "Siegmar", + "key": 5866, + "name": "Mcbrayer", + "type": "Gesundheits- und Krankenpfleger/in (81302-005)" + }, + { + "firstname": "Augustin", + "key": 5920, + "name": "Carreras", + "type": "Pflegefachkraft (Krankenpflege) (81302-018)" + }, + { + "firstname": "Roseliese", + "key": 6507, + "name": "Rashid", + "type": "Altenpfleger/in (82102-002)" + }, + { + "firstname": "Christfri", + "key": 6677, + "name": "Fullerton", + "type": "Krankenpflegehelfer/in (1 j\u00e4hrige A.) (81301-006)" + }, + { + "firstname": "Liebhardt", + "key": 6762, + "name": "Deshields", + "type": "A-Pflegeassistent/in (A-81302-014)" + }, + { + "firstname": "Trude", + "key": 6836, + "name": "Valentino", + "type": "Pflegefachkraft (Krankenpflege) (81302-018)" + }, + { + "firstname": "Annelene", + "key": 6928, + "name": "Izzo", + "type": "Medizinische/r Fachangestellte/r (81102-004)" + }, + { + "firstname": "Sigrid", + "key": 7006, + "name": "Cormier", + "type": "Altenpfleger/in (82102-002)" + }, + { + "firstname": "Annemargr", + "key": 7490, + "name": "Waldinger", + "type": "Gesundheits- und Krankenpfleger/in (81302-005)" + }, + { + "firstname": "Marcus", + "key": 7496, + "name": "Demarco", + "type": "Gesundheits- und Krankenpfleger/in (81302-005)" + }, + { + "firstname": "Ludger", + "key": 7603, + "name": "Roberson", + "type": "Altenpfleger/in (82102-002)" + }, + { + "firstname": "Kilian", + "key": 7752, + "name": "Rodriques", + "type": "Bundesfreiwilligendienst (BFD)" + }, + { + "firstname": "Julia", + "key": 7770, + "name": "Yeh", + "type": "A-Pflegefachkraft (Krankenpflege) (A-81302-018)" + }, + { + "firstname": "Burkhild", + "key": 7796, + "name": "Hertzler", + "type": "A-Pflegefachkraft (Krankenpflege) (A-81302-018)" + }, + { + "firstname": "Karena", + "key": 7835, + "name": "Driggers", + "type": "A-Pflegefachkraft (Krankenpflege) (A-81302-018)" + }, + { + "firstname": "Gertraute", + "key": 7848, + "name": "Winters", + "type": "A-Pflegeassistent/in (A-81302-014)" + }, + { + "firstname": "Janett", + "key": 7877, + "name": "Staggs", + "type": "A-Pflegefachkraft (Krankenpflege) (A-81302-018)" + }, + { + "firstname": "Irma", + "key": 7919, + "name": "Weathers", + "type": "Gesundheits- und Krankenpfleger/in (81302-005)" + }, + { + "firstname": "Loremarie", + "key": 7990, + "name": "Milburn", + "type": "A-Pflegeassistent/in (A-81302-014)" + }, + { + "firstname": "Borwin", + "key": 8034, + "name": "Burkhardt", + "type": "Praktikant/-in (P)" + } + ] +} diff --git a/legacy/cases/77/12_2024/free_shifts_and_vacation_days.json b/legacy/cases/77/12_2024/free_shifts_and_vacation_days.json new file mode 100644 index 00000000..8ee4fff1 --- /dev/null +++ b/legacy/cases/77/12_2024/free_shifts_and_vacation_days.json @@ -0,0 +1,1695 @@ +{ + "employees": [ + { + "firstname": "Adriane", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31 + ], + "key": 790, + "name": "Mccomas", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Janett", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31 + ], + "key": 791, + "name": "Branz", + "planned_shifts": [], + "vacation_days": [ + 2, + 3, + 4, + 5, + 6 + ] + }, + { + "firstname": "Mirjam", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31 + ], + "key": 843, + "name": "Flanagan", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Silvia", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31 + ], + "key": 914, + "name": "Harkins", + "planned_shifts": [], + "vacation_days": [ + 2, + 3, + 4, + 5, + 6 + ] + }, + { + "firstname": "Ingbert", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31 + ], + "key": 917, + "name": "Catoe", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Jenny", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31 + ], + "key": 921, + "name": "Keese", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "B\u00e4rbl", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31 + ], + "key": 924, + "name": "Merriweather", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Lina", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31 + ], + "key": 925, + "name": "Farniok", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Margaritt", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31 + ], + "key": 927, + "name": "Mittrach", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Daniele", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31 + ], + "key": 928, + "name": "Wunderlich", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Lidia", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31 + ], + "key": 946, + "name": "Macdowell", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Daniela", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31 + ], + "key": 1144, + "name": "Dalessandro", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Constance", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31 + ], + "key": 1230, + "name": "Palacio", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Kersten", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31 + ], + "key": 2932, + "name": "Devers", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Renilde", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31 + ], + "key": 2963, + "name": "Hoots", + "planned_shifts": [], + "vacation_days": [ + 16, + 17, + 18, + 19, + 20 + ] + }, + { + "firstname": "Elgine", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31 + ], + "key": 3566, + "name": "Seligman", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Eike", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31 + ], + "key": 3868, + "name": "Vanfleet", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Hannah", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31 + ], + "key": 4566, + "name": "Woodcock", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Henni", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31 + ], + "key": 5367, + "name": "Donis", + "planned_shifts": [], + "vacation_days": [ + 16, + 17, + 18, + 19, + 20 + ] + }, + { + "firstname": "Sturmius", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31 + ], + "key": 5652, + "name": "Sherrard", + "planned_shifts": [], + "vacation_days": [ + 27, + 28, + 29, + 30, + 31 + ] + }, + { + "firstname": "Ernestine", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31 + ], + "key": 5663, + "name": "Cerna", + "planned_shifts": [], + "vacation_days": [ + 30, + 31 + ] + }, + { + "firstname": "Siegmar", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31 + ], + "key": 5866, + "name": "Mcbrayer", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Augustin", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31 + ], + "key": 5920, + "name": "Carreras", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Roseliese", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31 + ], + "key": 6507, + "name": "Rashid", + "planned_shifts": [], + "vacation_days": [ + 9, + 10, + 11, + 12 + ] + }, + { + "firstname": "Christfri", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31 + ], + "key": 6677, + "name": "Fullerton", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Liebhardt", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31 + ], + "key": 6762, + "name": "Deshields", + "planned_shifts": [], + "vacation_days": [ + 30, + 31 + ] + }, + { + "firstname": "Trude", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31 + ], + "key": 6836, + "name": "Valentino", + "planned_shifts": [], + "vacation_days": [ + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31 + ] + }, + { + "firstname": "Annelene", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31 + ], + "key": 6928, + "name": "Izzo", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Sigrid", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31 + ], + "key": 7006, + "name": "Cormier", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Annemargr", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31 + ], + "key": 7490, + "name": "Waldinger", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Marcus", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31 + ], + "key": 7496, + "name": "Demarco", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Ludger", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31 + ], + "key": 7603, + "name": "Roberson", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Kilian", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31 + ], + "key": 7752, + "name": "Rodriques", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Julia", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31 + ], + "key": 7770, + "name": "Yeh", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Burkhild", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31 + ], + "key": 7796, + "name": "Hertzler", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Karena", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31 + ], + "key": 7835, + "name": "Driggers", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Gertraute", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31 + ], + "key": 7848, + "name": "Winters", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Janett", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31 + ], + "key": 7877, + "name": "Staggs", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Irma", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31 + ], + "key": 7919, + "name": "Weathers", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Loremarie", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31 + ], + "key": 7990, + "name": "Milburn", + "planned_shifts": [], + "vacation_days": [] + }, + { + "firstname": "Borwin", + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31 + ], + "key": 8034, + "name": "Burkhardt", + "planned_shifts": [], + "vacation_days": [] + } + ] +} diff --git a/legacy/cases/77/12_2024/general_settings.json b/legacy/cases/77/12_2024/general_settings.json new file mode 100644 index 00000000..122e86a1 --- /dev/null +++ b/legacy/cases/77/12_2024/general_settings.json @@ -0,0 +1,18 @@ +{ + "SHIFT_NAME_TO_INDEX": { + "Early": 0, + "Late": 1, + "Night": 2 + }, + "qualifications": { + "2963": [ + "rounds" + ], + "3868": [ + "rounds" + ], + "791": [ + "rounds" + ] + } +} diff --git a/legacy/cases/77/12_2024/minimal_number_of_staff.json b/legacy/cases/77/12_2024/minimal_number_of_staff.json new file mode 100644 index 00000000..ce9daf74 --- /dev/null +++ b/legacy/cases/77/12_2024/minimal_number_of_staff.json @@ -0,0 +1,113 @@ +{ + "Azubi": { + "Di": { + "F": 1, + "N": 0, + "S": 1 + }, + "Do": { + "F": 1, + "N": 0, + "S": 1 + }, + "Fr": { + "F": 1, + "N": 0, + "S": 1 + }, + "Mi": { + "F": 1, + "N": 0, + "S": 1 + }, + "Mo": { + "F": 1, + "N": 0, + "S": 1 + }, + "Sa": { + "F": 1, + "N": 0, + "S": 1 + }, + "So": { + "F": 1, + "N": 0, + "S": 1 + } + }, + "Fachkraft": { + "Di": { + "F": 3, + "N": 2, + "S": 2 + }, + "Do": { + "F": 3, + "N": 2, + "S": 2 + }, + "Fr": { + "F": 3, + "N": 2, + "S": 2 + }, + "Mi": { + "F": 4, + "N": 2, + "S": 2 + }, + "Mo": { + "F": 3, + "N": 2, + "S": 2 + }, + "Sa": { + "F": 2, + "N": 1, + "S": 2 + }, + "So": { + "F": 2, + "N": 1, + "S": 2 + } + }, + "Hilfskraft": { + "Di": { + "F": 2, + "N": 0, + "S": 2 + }, + "Do": { + "F": 2, + "N": 0, + "S": 2 + }, + "Fr": { + "F": 2, + "N": 0, + "S": 2 + }, + "Mi": { + "F": 2, + "N": 0, + "S": 2 + }, + "Mo": { + "F": 2, + "N": 0, + "S": 2 + }, + "Sa": { + "F": 2, + "N": 1, + "S": 2 + }, + "So": { + "F": 2, + "N": 1, + "S": 2 + } + } +} diff --git a/legacy/cases/77/12_2024/shift_information.json b/legacy/cases/77/12_2024/shift_information.json new file mode 100644 index 00000000..d2215694 --- /dev/null +++ b/legacy/cases/77/12_2024/shift_information.json @@ -0,0 +1,47 @@ +[ + { + "break_duration": 0.0, + "end_time": "2000-01-01T14:00:00", + "shift_duration": 360.0, + "shift_id": "1406", + "shift_name": "Z60", + "start_time": "2000-01-01T08:00:00", + "working_minutes": 360.0 + }, + { + "break_duration": 30.0, + "end_time": "2000-01-01T16:10:00", + "shift_duration": 490.0, + "shift_id": "2906", + "shift_name": "T75_", + "start_time": "2000-01-01T08:00:00", + "working_minutes": 460.0 + }, + { + "break_duration": 30.0, + "end_time": "2000-01-01T14:10:00", + "shift_duration": 490.0, + "shift_id": "2939", + "shift_name": "F2_", + "start_time": "2000-01-01T06:00:00", + "working_minutes": 460.0 + }, + { + "break_duration": 30.0, + "end_time": "2000-01-01T21:00:00", + "shift_duration": 490.0, + "shift_id": "2947", + "shift_name": "S2_", + "start_time": "2000-01-01T12:50:00", + "working_minutes": 460.0 + }, + { + "break_duration": 45.0, + "end_time": "2000-01-02T06:30:00", + "shift_duration": 610.0, + "shift_id": "2953", + "shift_name": "N2_", + "start_time": "2000-01-01T20:20:00", + "working_minutes": 565.0 + } +] diff --git a/legacy/cases/77/12_2024/target_working_minutes.json b/legacy/cases/77/12_2024/target_working_minutes.json new file mode 100644 index 00000000..78b304f2 --- /dev/null +++ b/legacy/cases/77/12_2024/target_working_minutes.json @@ -0,0 +1,291 @@ +{ + "employees": [ + { + "actual": 0.0, + "firstname": "Adriane", + "key": 790, + "name": "Mccomas", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Janett", + "key": 791, + "name": "Branz", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Mirjam", + "key": 843, + "name": "Flanagan", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Silvia", + "key": 914, + "name": "Harkins", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Ingbert", + "key": 917, + "name": "Catoe", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Jenny", + "key": 921, + "name": "Keese", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "B\u00e4rbl", + "key": 924, + "name": "Merriweather", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Lina", + "key": 925, + "name": "Farniok", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Margaritt", + "key": 927, + "name": "Mittrach", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Daniele", + "key": 928, + "name": "Wunderlich", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Lidia", + "key": 946, + "name": "Macdowell", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Daniela", + "key": 1144, + "name": "Dalessandro", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Constance", + "key": 1230, + "name": "Palacio", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Kersten", + "key": 2932, + "name": "Devers", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Renilde", + "key": 2963, + "name": "Hoots", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Elgine", + "key": 3566, + "name": "Seligman", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Eike", + "key": 3868, + "name": "Vanfleet", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Hannah", + "key": 4566, + "name": "Woodcock", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Henni", + "key": 5367, + "name": "Donis", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Sturmius", + "key": 5652, + "name": "Sherrard", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Ernestine", + "key": 5663, + "name": "Cerna", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Siegmar", + "key": 5866, + "name": "Mcbrayer", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Augustin", + "key": 5920, + "name": "Carreras", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Roseliese", + "key": 6507, + "name": "Rashid", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Christfri", + "key": 6677, + "name": "Fullerton", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Liebhardt", + "key": 6762, + "name": "Deshields", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Trude", + "key": 6836, + "name": "Valentino", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Annelene", + "key": 6928, + "name": "Izzo", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Sigrid", + "key": 7006, + "name": "Cormier", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Annemargr", + "key": 7490, + "name": "Waldinger", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Marcus", + "key": 7496, + "name": "Demarco", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Ludger", + "key": 7603, + "name": "Roberson", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Kilian", + "key": 7752, + "name": "Rodriques", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Julia", + "key": 7770, + "name": "Yeh", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Burkhild", + "key": 7796, + "name": "Hertzler", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Karena", + "key": 7835, + "name": "Driggers", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Gertraute", + "key": 7848, + "name": "Winters", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Janett", + "key": 7877, + "name": "Staggs", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Irma", + "key": 7919, + "name": "Weathers", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Loremarie", + "key": 7990, + "name": "Milburn", + "target": 0.0 + }, + { + "actual": 0.0, + "firstname": "Borwin", + "key": 8034, + "name": "Burkhardt", + "target": 0.0 + } + ] +} diff --git a/legacy/cases/77/12_2024/web/jobs.json b/legacy/cases/77/12_2024/web/jobs.json new file mode 100644 index 00000000..74020659 --- /dev/null +++ b/legacy/cases/77/12_2024/web/jobs.json @@ -0,0 +1,221 @@ +{ + "jobs": [ + { + "caseId": 77, + "completedAt": "2026-05-05T15:44:32.985Z", + "consoleOutput": "\nStatistics\n - conflicts : 0\n - branches : 0\n - wall time : 0.41646900000000003 s\n - objective value: 0.0\n - status : INFEASIBLE\n - objective value: 0.0\n - info : \n2026-05-05 17:44:10,089 - INFO - Weights file not found \u2013 using default weights instead.\n2026-05-05 17:44:10,092 - INFO - Minimizing Hidden Employee Count Phase 1\n2026-05-05 17:44:12,223 - INFO - Trying to solve with {'Azubi': 0, 'Fachkraft': 0, 'Hilfskraft': 0}\n2026-05-05 17:44:12,512 - INFO - Wall time: 0.04627108573913574\n2026-05-05 17:44:12,512 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:44:13,131 - INFO - Trying to solve with {'Azubi': 5, 'Fachkraft': 5, 'Hilfskraft': 5}\n2026-05-05 17:44:13,494 - INFO - Wall time: 0.034980058670043945\n2026-05-05 17:44:13,494 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:44:15,735 - INFO - Trying to solve with {'Azubi': 10, 'Fachkraft': 10, 'Hilfskraft': 10}\n2026-05-05 17:44:16,246 - INFO - Wall time: 0.04128003120422363\n2026-05-05 17:44:16,246 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:44:16,246 - INFO - Trying to solve with {'Azubi': 15, 'Fachkraft': 15, 'Hilfskraft': 15}\n2026-05-05 17:44:16,750 - INFO - Wall time: 0.0514070987701416\n2026-05-05 17:44:16,750 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:44:16,750 - INFO - Trying to solve with {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 20}\n2026-05-05 17:44:17,370 - INFO - Wall time: 0.058524131774902344\n2026-05-05 17:44:17,371 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:44:17,371 - INFO - Trying to solve with {'Azubi': 25, 'Fachkraft': 25, 'Hilfskraft': 25}\n2026-05-05 17:44:18,088 - INFO - Wall time: 0.06844377517700195\n2026-05-05 17:44:18,088 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:44:18,088 - INFO - Trying to solve with {'Azubi': 30, 'Fachkraft': 30, 'Hilfskraft': 30}\n2026-05-05 17:44:18,944 - INFO - Wall time: 0.07780003547668457\n2026-05-05 17:44:18,944 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:44:18,944 - INFO - Trying to solve with {'Azubi': 35, 'Fachkraft': 35, 'Hilfskraft': 35}\n2026-05-05 17:44:19,819 - INFO - Wall time: 0.0895998477935791\n2026-05-05 17:44:19,819 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:44:24,922 - INFO - Hidden Employee Upper Bound: {'Azubi': 35, 'Fachkraft': 35, 'Hilfskraft': 35}\n\n2026-05-05 17:44:24,923 - INFO - Minimizing Hidden Employee Count Phase 2\n2026-05-05 17:44:24,923 - INFO - Trying to solve with {'Azubi': 34, 'Fachkraft': 35, 'Hilfskraft': 35}\n2026-05-05 17:44:25,820 - INFO - Wall time: 0.09123611450195312\n2026-05-05 17:44:25,820 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:44:25,820 - INFO - Trying to solve with {'Azubi': 35, 'Fachkraft': 34, 'Hilfskraft': 35}\n2026-05-05 17:44:26,741 - INFO - Wall time: 0.08684015274047852\n2026-05-05 17:44:26,741 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:44:26,741 - INFO - Trying to solve with {'Azubi': 35, 'Fachkraft': 35, 'Hilfskraft': 34}\n2026-05-05 17:44:27,602 - INFO - Wall time: 0.08331894874572754\n2026-05-05 17:44:27,602 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:44:27,602 - INFO - Hidden Employee Tight Bound: {'Azubi': 35, 'Fachkraft': 35, 'Hilfskraft': 35}\n\n2026-05-05 17:44:27,603 - INFO - General information:\n2026-05-05 17:44:27,603 - INFO - - planning unit: 77\n2026-05-05 17:44:27,603 - INFO - - start date: 2024-12-01\n2026-05-05 17:44:27,603 - INFO - - end date: 2024-12-31\n2026-05-05 17:44:27,603 - INFO - - number of employees: 146\n2026-05-05 17:44:27,603 - INFO - - number of days: 31\n2026-05-05 17:44:27,603 - INFO - - number of shifts: 8\n2026-05-05 17:44:30,560 - INFO - Found 4 complete weekends in the planning period\n2026-05-05 17:44:32,215 - INFO - Solving model...\n2026-05-05 17:44:32,215 - INFO - - number of variables: 40734\n2026-05-05 17:44:32,215 - INFO - - number of objectives: 10\n2026-05-05 17:44:32,215 - INFO - - number of constraints: 9\n2026-05-05 17:44:32,215 - INFO - Constraints:\n2026-05-05 17:44:32,215 - INFO - - Free Day After Night Shift Phase\n2026-05-05 17:44:32,215 - INFO - - Minimum Rest Time\n2026-05-05 17:44:32,215 - INFO - - Min Staffing\n2026-05-05 17:44:32,215 - INFO - - Rounds In Early Shift\n2026-05-05 17:44:32,215 - INFO - - One Shift Per Day\n2026-05-05 17:44:32,215 - INFO - - Target Working Time\n2026-05-05 17:44:32,216 - INFO - - Vacation Days And Shifts\n2026-05-05 17:44:32,216 - INFO - - Hierarchy Of Intermediate Shifts\n2026-05-05 17:44:32,216 - INFO - - Planned Shifts\n2026-05-05 17:44:32,216 - INFO - Objectives:\n2026-05-05 17:44:32,216 - INFO - - Free Days Near Weekend (weight: 2)\n2026-05-05 17:44:32,216 - INFO - - Minimize Consecutive Night Shifts (weight: 2)\n2026-05-05 17:44:32,216 - INFO - - Minimize Hidden Employees (weight: 100)\n2026-05-05 17:44:32,216 - INFO - - Minimize Overtime (weight: 4)\n2026-05-05 17:44:32,216 - INFO - - Not Too Many Consecutive Days (weight: 1)\n2026-05-05 17:44:32,216 - INFO - - Rotate Shifts Forward (weight: 1)\n2026-05-05 17:44:32,216 - INFO - - Maximize Employee Wishes (weight: 3)\n2026-05-05 17:44:32,216 - INFO - - Free Day After Night Shift Phase (weight: 3)\n2026-05-05 17:44:32,216 - INFO - - Every Second Weekend Free (weight: 1)\n2026-05-05 17:44:32,216 - INFO - - Preferred Block Length (weight: 1)\n2026-05-05 17:44:32,364 - INFO - Timeout set to 300 seconds\n2026-05-05 17:44:32,974 - INFO - Solving completed in 0.61 seconds\n", + "createdAt": "2026-05-05T15:44:09.938Z", + "duration": 23046, + "error": "2026-05-05 17:44:10,089 - INFO - Weights file not found \u2013 using default weights instead.\n2026-05-05 17:44:10,092 - INFO - Minimizing Hidden Employee Count Phase 1\n2026-05-05 17:44:12,223 - INFO - Trying to solve with {'Azubi': 0, 'Fachkraft': 0, 'Hilfskraft': 0}\n2026-05-05 17:44:12,512 - INFO - Wall time: 0.04627108573913574\n2026-05-05 17:44:12,512 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:44:13,131 - INFO - Trying to solve with {'Azubi': 5, 'Fachkraft': 5, 'Hilfskraft': 5}\n2026-05-05 17:44:13,494 - INFO - Wall time: 0.034980058670043945\n2026-05-05 17:44:13,494 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:44:15,735 - INFO - Trying to solve with {'Azubi': 10, 'Fachkraft': 10, 'Hilfskraft': 10}\n2026-05-05 17:44:16,246 - INFO - Wall time: 0.04128003120422363\n2026-05-05 17:44:16,246 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:44:16,246 - INFO - Trying to solve with {'Azubi': 15, 'Fachkraft': 15, 'Hilfskraft': 15}\n2026-05-05 17:44:16,750 - INFO - Wall time: 0.0514070987701416\n2026-05-05 17:44:16,750 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:44:16,750 - INFO - Trying to solve with {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 20}\n2026-05-05 17:44:17,370 - INFO - Wall time: 0.058524131774902344\n2026-05-05 17:44:17,371 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:44:17,371 - INFO - Trying to solve with {'Azubi': 25, 'Fachkraft': 25, 'Hilfskraft': 25}\n2026-05-05 17:44:18,088 - INFO - Wall time: 0.06844377517700195\n2026-05-05 17:44:18,088 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:44:18,088 - INFO - Trying to solve with {'Azubi': 30, 'Fachkraft': 30, 'Hilfskraft': 30}\n2026-05-05 17:44:18,944 - INFO - Wall time: 0.07780003547668457\n2026-05-05 17:44:18,944 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:44:18,944 - INFO - Trying to solve with {'Azubi': 35, 'Fachkraft': 35, 'Hilfskraft': 35}\n2026-05-05 17:44:19,819 - INFO - Wall time: 0.0895998477935791\n2026-05-05 17:44:19,819 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:44:24,922 - INFO - Hidden Employee Upper Bound: {'Azubi': 35, 'Fachkraft': 35, 'Hilfskraft': 35}\n\n2026-05-05 17:44:24,923 - INFO - Minimizing Hidden Employee Count Phase 2\n2026-05-05 17:44:24,923 - INFO - Trying to solve with {'Azubi': 34, 'Fachkraft': 35, 'Hilfskraft': 35}\n2026-05-05 17:44:25,820 - INFO - Wall time: 0.09123611450195312\n2026-05-05 17:44:25,820 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:44:25,820 - INFO - Trying to solve with {'Azubi': 35, 'Fachkraft': 34, 'Hilfskraft': 35}\n2026-05-05 17:44:26,741 - INFO - Wall time: 0.08684015274047852\n2026-05-05 17:44:26,741 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:44:26,741 - INFO - Trying to solve with {'Azubi': 35, 'Fachkraft': 35, 'Hilfskraft': 34}\n2026-05-05 17:44:27,602 - INFO - Wall time: 0.08331894874572754\n2026-05-05 17:44:27,602 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:44:27,602 - INFO - Hidden Employee Tight Bound: {'Azubi': 35, 'Fachkraft': 35, 'Hilfskraft': 35}\n\n2026-05-05 17:44:27,603 - INFO - General information:\n2026-05-05 17:44:27,603 - INFO - - planning unit: 77\n2026-05-05 17:44:27,603 - INFO - - start date: 2024-12-01\n2026-05-05 17:44:27,603 - INFO - - end date: 2024-12-31\n2026-05-05 17:44:27,603 - INFO - - number of employees: 146\n2026-05-05 17:44:27,603 - INFO - - number of days: 31\n2026-05-05 17:44:27,603 - INFO - - number of shifts: 8\n2026-05-05 17:44:30,560 - INFO - Found 4 complete weekends in the planning period\n2026-05-05 17:44:32,215 - INFO - Solving model...\n2026-05-05 17:44:32,215 - INFO - - number of variables: 40734\n2026-05-05 17:44:32,215 - INFO - - number of objectives: 10\n2026-05-05 17:44:32,215 - INFO - - number of constraints: 9\n2026-05-05 17:44:32,215 - INFO - Constraints:\n2026-05-05 17:44:32,215 - INFO - - Free Day After Night Shift Phase\n2026-05-05 17:44:32,215 - INFO - - Minimum Rest Time\n2026-05-05 17:44:32,215 - INFO - - Min Staffing\n2026-05-05 17:44:32,215 - INFO - - Rounds In Early Shift\n2026-05-05 17:44:32,215 - INFO - - One Shift Per Day\n2026-05-05 17:44:32,215 - INFO - - Target Working Time\n2026-05-05 17:44:32,216 - INFO - - Vacation Days And Shifts\n2026-05-05 17:44:32,216 - INFO - - Hierarchy Of Intermediate Shifts\n2026-05-05 17:44:32,216 - INFO - - Planned Shifts\n2026-05-05 17:44:32,216 - INFO - Objectives:\n2026-05-05 17:44:32,216 - INFO - - Free Days Near Weekend (weight: 2)\n2026-05-05 17:44:32,216 - INFO - - Minimize Consecutive Night Shifts (weight: 2)\n2026-05-05 17:44:32,216 - INFO - - Minimize Hidden Employees (weight: 100)\n2026-05-05 17:44:32,216 - INFO - - Minimize Overtime (weight: 4)\n2026-05-05 17:44:32,216 - INFO - - Not Too Many Consecutive Days (weight: 1)\n2026-05-05 17:44:32,216 - INFO - - Rotate Shifts Forward (weight: 1)\n2026-05-05 17:44:32,216 - INFO - - Maximize Employee Wishes (weight: 3)\n2026-05-05 17:44:32,216 - INFO - - Free Day After Night Shift Phase (weight: 3)\n2026-05-05 17:44:32,216 - INFO - - Every Second Weekend Free (weight: 1)\n2026-05-05 17:44:32,216 - INFO - - Preferred Block Length (weight: 1)\n2026-05-05 17:44:32,364 - INFO - Timeout set to 300 seconds\n2026-05-05 17:44:32,974 - INFO - Solving completed in 0.61 seconds\n", + "id": "e1574b1e-a373-4022-9022-f30cc9fbc3f2", + "metadata": { + "expectedSolutions": 1, + "feasibleSolutions": [], + "solutionsGenerated": 0 + }, + "params": { + "end": "2024-12-31", + "start": "2024-12-01", + "timeout": 300, + "unit": 77 + }, + "status": "failed", + "type": "solve" + }, + { + "caseId": 77, + "completedAt": "2026-05-05T15:35:06.569Z", + "consoleOutput": "\nStatistics\n - conflicts : 0\n - branches : 0\n - wall time : 0.410013 s\n - objective value: 0.0\n - status : INFEASIBLE\n - objective value: 0.0\n - info : \n2026-05-05 17:34:34,621 - INFO - Weights file not found \u2013 using default weights instead.\n2026-05-05 17:34:34,624 - INFO - Minimizing Hidden Employee Count Phase 1\n2026-05-05 17:34:35,582 - INFO - Trying to solve with {'Azubi': 0, 'Fachkraft': 0, 'Hilfskraft': 0}\n2026-05-05 17:34:35,841 - INFO - Wall time: 0.026441097259521484\n2026-05-05 17:34:35,841 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:34:36,874 - INFO - Trying to solve with {'Azubi': 5, 'Fachkraft': 5, 'Hilfskraft': 5}\n2026-05-05 17:34:37,241 - INFO - Wall time: 0.03624224662780762\n2026-05-05 17:34:37,241 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:34:38,074 - INFO - Trying to solve with {'Azubi': 10, 'Fachkraft': 10, 'Hilfskraft': 10}\n2026-05-05 17:34:38,556 - INFO - Wall time: 0.04122591018676758\n2026-05-05 17:34:38,556 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:34:40,401 - INFO - Trying to solve with {'Azubi': 15, 'Fachkraft': 15, 'Hilfskraft': 15}\n2026-05-05 17:34:40,971 - INFO - Wall time: 0.07665419578552246\n2026-05-05 17:34:40,971 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:34:44,549 - INFO - Trying to solve with {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 20}\n2026-05-05 17:34:45,317 - INFO - Wall time: 0.0688624382019043\n2026-05-05 17:34:45,317 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:34:46,547 - INFO - Trying to solve with {'Azubi': 25, 'Fachkraft': 25, 'Hilfskraft': 25}\n2026-05-05 17:34:47,285 - INFO - Wall time: 0.0806269645690918\n2026-05-05 17:34:47,286 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:34:48,310 - INFO - Trying to solve with {'Azubi': 30, 'Fachkraft': 30, 'Hilfskraft': 30}\n2026-05-05 17:34:49,127 - INFO - Wall time: 0.08417081832885742\n2026-05-05 17:34:49,127 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:34:50,146 - INFO - Trying to solve with {'Azubi': 35, 'Fachkraft': 35, 'Hilfskraft': 35}\n2026-05-05 17:34:51,010 - INFO - Wall time: 0.09458780288696289\n2026-05-05 17:34:51,010 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:34:58,524 - INFO - Hidden Employee Upper Bound: {'Azubi': 35, 'Fachkraft': 35, 'Hilfskraft': 35}\n\n2026-05-05 17:34:58,525 - INFO - Minimizing Hidden Employee Count Phase 2\n2026-05-05 17:34:58,525 - INFO - Trying to solve with {'Azubi': 34, 'Fachkraft': 35, 'Hilfskraft': 35}\n2026-05-05 17:34:59,557 - INFO - Wall time: 0.10633087158203125\n2026-05-05 17:34:59,557 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:34:59,557 - INFO - Trying to solve with {'Azubi': 35, 'Fachkraft': 34, 'Hilfskraft': 35}\n2026-05-05 17:35:00,431 - INFO - Wall time: 0.08524298667907715\n2026-05-05 17:35:00,431 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:35:00,431 - INFO - Trying to solve with {'Azubi': 35, 'Fachkraft': 35, 'Hilfskraft': 34}\n2026-05-05 17:35:01,313 - INFO - Wall time: 0.08767175674438477\n2026-05-05 17:35:01,314 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:35:01,314 - INFO - Hidden Employee Tight Bound: {'Azubi': 35, 'Fachkraft': 35, 'Hilfskraft': 35}\n\n2026-05-05 17:35:01,314 - INFO - General information:\n2026-05-05 17:35:01,314 - INFO - - planning unit: 77\n2026-05-05 17:35:01,314 - INFO - - start date: 2024-12-01\n2026-05-05 17:35:01,314 - INFO - - end date: 2024-12-31\n2026-05-05 17:35:01,314 - INFO - - number of employees: 146\n2026-05-05 17:35:01,314 - INFO - - number of days: 31\n2026-05-05 17:35:01,314 - INFO - - number of shifts: 8\n2026-05-05 17:35:04,220 - INFO - Found 4 complete weekends in the planning period\n2026-05-05 17:35:05,814 - INFO - Solving model...\n2026-05-05 17:35:05,815 - INFO - - number of variables: 40734\n2026-05-05 17:35:05,815 - INFO - - number of objectives: 10\n2026-05-05 17:35:05,815 - INFO - - number of constraints: 9\n2026-05-05 17:35:05,815 - INFO - Constraints:\n2026-05-05 17:35:05,815 - INFO - - Free Day After Night Shift Phase\n2026-05-05 17:35:05,815 - INFO - - Minimum Rest Time\n2026-05-05 17:35:05,815 - INFO - - Min Staffing\n2026-05-05 17:35:05,815 - INFO - - Rounds In Early Shift\n2026-05-05 17:35:05,815 - INFO - - One Shift Per Day\n2026-05-05 17:35:05,815 - INFO - - Target Working Time\n2026-05-05 17:35:05,815 - INFO - - Vacation Days And Shifts\n2026-05-05 17:35:05,815 - INFO - - Hierarchy Of Intermediate Shifts\n2026-05-05 17:35:05,815 - INFO - - Planned Shifts\n2026-05-05 17:35:05,815 - INFO - Objectives:\n2026-05-05 17:35:05,815 - INFO - - Free Days Near Weekend (weight: 2)\n2026-05-05 17:35:05,815 - INFO - - Minimize Consecutive Night Shifts (weight: 2)\n2026-05-05 17:35:05,815 - INFO - - Minimize Hidden Employees (weight: 100)\n2026-05-05 17:35:05,815 - INFO - - Minimize Overtime (weight: 4)\n2026-05-05 17:35:05,816 - INFO - - Not Too Many Consecutive Days (weight: 1)\n2026-05-05 17:35:05,816 - INFO - - Rotate Shifts Forward (weight: 1)\n2026-05-05 17:35:05,816 - INFO - - Maximize Employee Wishes (weight: 3)\n2026-05-05 17:35:05,816 - INFO - - Free Day After Night Shift Phase (weight: 3)\n2026-05-05 17:35:05,816 - INFO - - Every Second Weekend Free (weight: 1)\n2026-05-05 17:35:05,816 - INFO - - Preferred Block Length (weight: 1)\n2026-05-05 17:35:05,967 - INFO - Timeout set to 600 seconds\n2026-05-05 17:35:06,560 - INFO - Solving completed in 0.59 seconds\n", + "createdAt": "2026-05-05T15:34:30.950Z", + "duration": 35617, + "error": "2026-05-05 17:34:34,621 - INFO - Weights file not found \u2013 using default weights instead.\n2026-05-05 17:34:34,624 - INFO - Minimizing Hidden Employee Count Phase 1\n2026-05-05 17:34:35,582 - INFO - Trying to solve with {'Azubi': 0, 'Fachkraft': 0, 'Hilfskraft': 0}\n2026-05-05 17:34:35,841 - INFO - Wall time: 0.026441097259521484\n2026-05-05 17:34:35,841 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:34:36,874 - INFO - Trying to solve with {'Azubi': 5, 'Fachkraft': 5, 'Hilfskraft': 5}\n2026-05-05 17:34:37,241 - INFO - Wall time: 0.03624224662780762\n2026-05-05 17:34:37,241 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:34:38,074 - INFO - Trying to solve with {'Azubi': 10, 'Fachkraft': 10, 'Hilfskraft': 10}\n2026-05-05 17:34:38,556 - INFO - Wall time: 0.04122591018676758\n2026-05-05 17:34:38,556 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:34:40,401 - INFO - Trying to solve with {'Azubi': 15, 'Fachkraft': 15, 'Hilfskraft': 15}\n2026-05-05 17:34:40,971 - INFO - Wall time: 0.07665419578552246\n2026-05-05 17:34:40,971 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:34:44,549 - INFO - Trying to solve with {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 20}\n2026-05-05 17:34:45,317 - INFO - Wall time: 0.0688624382019043\n2026-05-05 17:34:45,317 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:34:46,547 - INFO - Trying to solve with {'Azubi': 25, 'Fachkraft': 25, 'Hilfskraft': 25}\n2026-05-05 17:34:47,285 - INFO - Wall time: 0.0806269645690918\n2026-05-05 17:34:47,286 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:34:48,310 - INFO - Trying to solve with {'Azubi': 30, 'Fachkraft': 30, 'Hilfskraft': 30}\n2026-05-05 17:34:49,127 - INFO - Wall time: 0.08417081832885742\n2026-05-05 17:34:49,127 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:34:50,146 - INFO - Trying to solve with {'Azubi': 35, 'Fachkraft': 35, 'Hilfskraft': 35}\n2026-05-05 17:34:51,010 - INFO - Wall time: 0.09458780288696289\n2026-05-05 17:34:51,010 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:34:58,524 - INFO - Hidden Employee Upper Bound: {'Azubi': 35, 'Fachkraft': 35, 'Hilfskraft': 35}\n\n2026-05-05 17:34:58,525 - INFO - Minimizing Hidden Employee Count Phase 2\n2026-05-05 17:34:58,525 - INFO - Trying to solve with {'Azubi': 34, 'Fachkraft': 35, 'Hilfskraft': 35}\n2026-05-05 17:34:59,557 - INFO - Wall time: 0.10633087158203125\n2026-05-05 17:34:59,557 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:34:59,557 - INFO - Trying to solve with {'Azubi': 35, 'Fachkraft': 34, 'Hilfskraft': 35}\n2026-05-05 17:35:00,431 - INFO - Wall time: 0.08524298667907715\n2026-05-05 17:35:00,431 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:35:00,431 - INFO - Trying to solve with {'Azubi': 35, 'Fachkraft': 35, 'Hilfskraft': 34}\n2026-05-05 17:35:01,313 - INFO - Wall time: 0.08767175674438477\n2026-05-05 17:35:01,314 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:35:01,314 - INFO - Hidden Employee Tight Bound: {'Azubi': 35, 'Fachkraft': 35, 'Hilfskraft': 35}\n\n2026-05-05 17:35:01,314 - INFO - General information:\n2026-05-05 17:35:01,314 - INFO - - planning unit: 77\n2026-05-05 17:35:01,314 - INFO - - start date: 2024-12-01\n2026-05-05 17:35:01,314 - INFO - - end date: 2024-12-31\n2026-05-05 17:35:01,314 - INFO - - number of employees: 146\n2026-05-05 17:35:01,314 - INFO - - number of days: 31\n2026-05-05 17:35:01,314 - INFO - - number of shifts: 8\n2026-05-05 17:35:04,220 - INFO - Found 4 complete weekends in the planning period\n2026-05-05 17:35:05,814 - INFO - Solving model...\n2026-05-05 17:35:05,815 - INFO - - number of variables: 40734\n2026-05-05 17:35:05,815 - INFO - - number of objectives: 10\n2026-05-05 17:35:05,815 - INFO - - number of constraints: 9\n2026-05-05 17:35:05,815 - INFO - Constraints:\n2026-05-05 17:35:05,815 - INFO - - Free Day After Night Shift Phase\n2026-05-05 17:35:05,815 - INFO - - Minimum Rest Time\n2026-05-05 17:35:05,815 - INFO - - Min Staffing\n2026-05-05 17:35:05,815 - INFO - - Rounds In Early Shift\n2026-05-05 17:35:05,815 - INFO - - One Shift Per Day\n2026-05-05 17:35:05,815 - INFO - - Target Working Time\n2026-05-05 17:35:05,815 - INFO - - Vacation Days And Shifts\n2026-05-05 17:35:05,815 - INFO - - Hierarchy Of Intermediate Shifts\n2026-05-05 17:35:05,815 - INFO - - Planned Shifts\n2026-05-05 17:35:05,815 - INFO - Objectives:\n2026-05-05 17:35:05,815 - INFO - - Free Days Near Weekend (weight: 2)\n2026-05-05 17:35:05,815 - INFO - - Minimize Consecutive Night Shifts (weight: 2)\n2026-05-05 17:35:05,815 - INFO - - Minimize Hidden Employees (weight: 100)\n2026-05-05 17:35:05,815 - INFO - - Minimize Overtime (weight: 4)\n2026-05-05 17:35:05,816 - INFO - - Not Too Many Consecutive Days (weight: 1)\n2026-05-05 17:35:05,816 - INFO - - Rotate Shifts Forward (weight: 1)\n2026-05-05 17:35:05,816 - INFO - - Maximize Employee Wishes (weight: 3)\n2026-05-05 17:35:05,816 - INFO - - Free Day After Night Shift Phase (weight: 3)\n2026-05-05 17:35:05,816 - INFO - - Every Second Weekend Free (weight: 1)\n2026-05-05 17:35:05,816 - INFO - - Preferred Block Length (weight: 1)\n2026-05-05 17:35:05,967 - INFO - Timeout set to 600 seconds\n2026-05-05 17:35:06,560 - INFO - Solving completed in 0.59 seconds\n", + "id": "f72358d2-8c34-45a2-8942-31fd5891eafe", + "metadata": { + "expectedSolutions": 1, + "feasibleSolutions": [], + "solutionsGenerated": 0 + }, + "params": { + "end": "2024-12-31", + "start": "2024-12-01", + "timeout": 600, + "unit": 77 + }, + "status": "failed", + "type": "solve" + }, + { + "caseId": 77, + "completedAt": "2026-05-05T15:34:25.230Z", + "createdAt": "2026-05-05T15:32:58.986Z", + "duration": 86244, + "error": "fetch failed", + "id": "afdd2862-7f19-4306-b9a2-caf0e5622f46", + "metadata": { + "expectedSolutions": 1, + "feasibleSolutions": [], + "solutionsGenerated": 0 + }, + "params": { + "end": "2024-12-31", + "start": "2024-12-01", + "timeout": 600, + "unit": 77 + }, + "status": "failed", + "type": "solve" + }, + { + "caseId": 77, + "completedAt": "2026-05-05T15:32:57.839Z", + "consoleOutput": "\nStatistics\n - conflicts : 0\n - branches : 0\n - wall time : 0.260208 s\n - objective value: 0.0\n - status : INFEASIBLE\n - objective value: 0.0\n - info : \n2026-05-05 17:30:57,892 - INFO - Weights file not found \u2013 using default weights instead.\n2026-05-05 17:30:57,895 - INFO - Minimizing Hidden Employee Count Phase 1\n2026-05-05 17:30:57,895 - INFO - Trying to solve with {'Azubi': 0, 'Fachkraft': 0, 'Hilfskraft': 0}\n2026-05-05 17:30:58,290 - INFO - Wall time: 0.02165079116821289\n2026-05-05 17:30:58,290 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:30:58,290 - INFO - Trying to solve with {'Azubi': 5, 'Fachkraft': 5, 'Hilfskraft': 5}\n2026-05-05 17:30:58,626 - INFO - Wall time: 0.0316920280456543\n2026-05-05 17:30:58,626 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:30:58,626 - INFO - Trying to solve with {'Azubi': 10, 'Fachkraft': 10, 'Hilfskraft': 10}\n2026-05-05 17:30:59,041 - INFO - Wall time: 0.041519880294799805\n2026-05-05 17:30:59,041 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:30:59,041 - INFO - Trying to solve with {'Azubi': 15, 'Fachkraft': 15, 'Hilfskraft': 15}\n2026-05-05 17:30:59,574 - INFO - Wall time: 0.049916982650756836\n2026-05-05 17:30:59,575 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:30:59,575 - INFO - Trying to solve with {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 20}\n2026-05-05 17:31:00,191 - INFO - Wall time: 0.05871319770812988\n2026-05-05 17:31:00,191 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:32:51,886 - INFO - Hidden Employee Upper Bound: {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 20}\n\n2026-05-05 17:32:51,889 - INFO - Minimizing Hidden Employee Count Phase 2\n2026-05-05 17:32:51,889 - INFO - Trying to solve with {'Azubi': 19, 'Fachkraft': 20, 'Hilfskraft': 20}\n2026-05-05 17:32:52,682 - INFO - Wall time: 0.0943601131439209\n2026-05-05 17:32:52,683 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:32:52,683 - INFO - Trying to solve with {'Azubi': 20, 'Fachkraft': 19, 'Hilfskraft': 20}\n2026-05-05 17:32:53,380 - INFO - Wall time: 0.06046795845031738\n2026-05-05 17:32:53,380 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:32:53,380 - INFO - Trying to solve with {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 19}\n2026-05-05 17:32:53,955 - INFO - Wall time: 0.05666399002075195\n2026-05-05 17:32:53,955 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:32:53,955 - INFO - Hidden Employee Tight Bound: {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 20}\n\n2026-05-05 17:32:53,955 - INFO - General information:\n2026-05-05 17:32:53,955 - INFO - - planning unit: 77\n2026-05-05 17:32:53,955 - INFO - - start date: 2024-12-01\n2026-05-05 17:32:53,955 - INFO - - end date: 2024-12-31\n2026-05-05 17:32:53,955 - INFO - - number of employees: 101\n2026-05-05 17:32:53,955 - INFO - - number of days: 31\n2026-05-05 17:32:53,955 - INFO - - number of shifts: 8\n2026-05-05 17:32:56,071 - INFO - Found 4 complete weekends in the planning period\n2026-05-05 17:32:57,254 - INFO - Solving model...\n2026-05-05 17:32:57,255 - INFO - - number of variables: 28179\n2026-05-05 17:32:57,255 - INFO - - number of objectives: 10\n2026-05-05 17:32:57,255 - INFO - - number of constraints: 9\n2026-05-05 17:32:57,255 - INFO - Constraints:\n2026-05-05 17:32:57,255 - INFO - - Free Day After Night Shift Phase\n2026-05-05 17:32:57,255 - INFO - - Minimum Rest Time\n2026-05-05 17:32:57,255 - INFO - - Min Staffing\n2026-05-05 17:32:57,255 - INFO - - Rounds In Early Shift\n2026-05-05 17:32:57,255 - INFO - - One Shift Per Day\n2026-05-05 17:32:57,255 - INFO - - Target Working Time\n2026-05-05 17:32:57,255 - INFO - - Vacation Days And Shifts\n2026-05-05 17:32:57,255 - INFO - - Hierarchy Of Intermediate Shifts\n2026-05-05 17:32:57,255 - INFO - - Planned Shifts\n2026-05-05 17:32:57,255 - INFO - Objectives:\n2026-05-05 17:32:57,255 - INFO - - Free Days Near Weekend (weight: 2)\n2026-05-05 17:32:57,255 - INFO - - Minimize Consecutive Night Shifts (weight: 2)\n2026-05-05 17:32:57,255 - INFO - - Minimize Hidden Employees (weight: 100)\n2026-05-05 17:32:57,255 - INFO - - Minimize Overtime (weight: 4)\n2026-05-05 17:32:57,255 - INFO - - Not Too Many Consecutive Days (weight: 1)\n2026-05-05 17:32:57,255 - INFO - - Rotate Shifts Forward (weight: 1)\n2026-05-05 17:32:57,255 - INFO - - Maximize Employee Wishes (weight: 3)\n2026-05-05 17:32:57,255 - INFO - - Free Day After Night Shift Phase (weight: 3)\n2026-05-05 17:32:57,255 - INFO - - Every Second Weekend Free (weight: 1)\n2026-05-05 17:32:57,255 - INFO - - Preferred Block Length (weight: 1)\n2026-05-05 17:32:57,426 - INFO - Timeout set to 600 seconds\n2026-05-05 17:32:57,834 - INFO - Solving completed in 0.41 seconds\n", + "createdAt": "2026-05-05T15:30:50.363Z", + "duration": 127475, + "error": "2026-05-05 17:30:57,892 - INFO - Weights file not found \u2013 using default weights instead.\n2026-05-05 17:30:57,895 - INFO - Minimizing Hidden Employee Count Phase 1\n2026-05-05 17:30:57,895 - INFO - Trying to solve with {'Azubi': 0, 'Fachkraft': 0, 'Hilfskraft': 0}\n2026-05-05 17:30:58,290 - INFO - Wall time: 0.02165079116821289\n2026-05-05 17:30:58,290 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:30:58,290 - INFO - Trying to solve with {'Azubi': 5, 'Fachkraft': 5, 'Hilfskraft': 5}\n2026-05-05 17:30:58,626 - INFO - Wall time: 0.0316920280456543\n2026-05-05 17:30:58,626 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:30:58,626 - INFO - Trying to solve with {'Azubi': 10, 'Fachkraft': 10, 'Hilfskraft': 10}\n2026-05-05 17:30:59,041 - INFO - Wall time: 0.041519880294799805\n2026-05-05 17:30:59,041 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:30:59,041 - INFO - Trying to solve with {'Azubi': 15, 'Fachkraft': 15, 'Hilfskraft': 15}\n2026-05-05 17:30:59,574 - INFO - Wall time: 0.049916982650756836\n2026-05-05 17:30:59,575 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:30:59,575 - INFO - Trying to solve with {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 20}\n2026-05-05 17:31:00,191 - INFO - Wall time: 0.05871319770812988\n2026-05-05 17:31:00,191 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:32:51,886 - INFO - Hidden Employee Upper Bound: {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 20}\n\n2026-05-05 17:32:51,889 - INFO - Minimizing Hidden Employee Count Phase 2\n2026-05-05 17:32:51,889 - INFO - Trying to solve with {'Azubi': 19, 'Fachkraft': 20, 'Hilfskraft': 20}\n2026-05-05 17:32:52,682 - INFO - Wall time: 0.0943601131439209\n2026-05-05 17:32:52,683 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:32:52,683 - INFO - Trying to solve with {'Azubi': 20, 'Fachkraft': 19, 'Hilfskraft': 20}\n2026-05-05 17:32:53,380 - INFO - Wall time: 0.06046795845031738\n2026-05-05 17:32:53,380 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:32:53,380 - INFO - Trying to solve with {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 19}\n2026-05-05 17:32:53,955 - INFO - Wall time: 0.05666399002075195\n2026-05-05 17:32:53,955 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:32:53,955 - INFO - Hidden Employee Tight Bound: {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 20}\n\n2026-05-05 17:32:53,955 - INFO - General information:\n2026-05-05 17:32:53,955 - INFO - - planning unit: 77\n2026-05-05 17:32:53,955 - INFO - - start date: 2024-12-01\n2026-05-05 17:32:53,955 - INFO - - end date: 2024-12-31\n2026-05-05 17:32:53,955 - INFO - - number of employees: 101\n2026-05-05 17:32:53,955 - INFO - - number of days: 31\n2026-05-05 17:32:53,955 - INFO - - number of shifts: 8\n2026-05-05 17:32:56,071 - INFO - Found 4 complete weekends in the planning period\n2026-05-05 17:32:57,254 - INFO - Solving model...\n2026-05-05 17:32:57,255 - INFO - - number of variables: 28179\n2026-05-05 17:32:57,255 - INFO - - number of objectives: 10\n2026-05-05 17:32:57,255 - INFO - - number of constraints: 9\n2026-05-05 17:32:57,255 - INFO - Constraints:\n2026-05-05 17:32:57,255 - INFO - - Free Day After Night Shift Phase\n2026-05-05 17:32:57,255 - INFO - - Minimum Rest Time\n2026-05-05 17:32:57,255 - INFO - - Min Staffing\n2026-05-05 17:32:57,255 - INFO - - Rounds In Early Shift\n2026-05-05 17:32:57,255 - INFO - - One Shift Per Day\n2026-05-05 17:32:57,255 - INFO - - Target Working Time\n2026-05-05 17:32:57,255 - INFO - - Vacation Days And Shifts\n2026-05-05 17:32:57,255 - INFO - - Hierarchy Of Intermediate Shifts\n2026-05-05 17:32:57,255 - INFO - - Planned Shifts\n2026-05-05 17:32:57,255 - INFO - Objectives:\n2026-05-05 17:32:57,255 - INFO - - Free Days Near Weekend (weight: 2)\n2026-05-05 17:32:57,255 - INFO - - Minimize Consecutive Night Shifts (weight: 2)\n2026-05-05 17:32:57,255 - INFO - - Minimize Hidden Employees (weight: 100)\n2026-05-05 17:32:57,255 - INFO - - Minimize Overtime (weight: 4)\n2026-05-05 17:32:57,255 - INFO - - Not Too Many Consecutive Days (weight: 1)\n2026-05-05 17:32:57,255 - INFO - - Rotate Shifts Forward (weight: 1)\n2026-05-05 17:32:57,255 - INFO - - Maximize Employee Wishes (weight: 3)\n2026-05-05 17:32:57,255 - INFO - - Free Day After Night Shift Phase (weight: 3)\n2026-05-05 17:32:57,255 - INFO - - Every Second Weekend Free (weight: 1)\n2026-05-05 17:32:57,255 - INFO - - Preferred Block Length (weight: 1)\n2026-05-05 17:32:57,426 - INFO - Timeout set to 600 seconds\n2026-05-05 17:32:57,834 - INFO - Solving completed in 0.41 seconds\n", + "id": "2822fc9d-41aa-4201-a4ed-07573efba6a2", + "metadata": { + "expectedSolutions": 1, + "feasibleSolutions": [], + "solutionsGenerated": 0 + }, + "params": { + "end": "2024-12-31", + "start": "2024-12-01", + "timeout": 600, + "unit": 77 + }, + "status": "failed", + "type": "solve" + }, + { + "caseId": 77, + "completedAt": "2026-05-05T15:30:49.085Z", + "consoleOutput": "\nStatistics\n - conflicts : 0\n - branches : 0\n - wall time : 0.294793 s\n - objective value: 0.0\n - status : INFEASIBLE\n - objective value: 0.0\n - info : \n2026-05-05 17:26:35,133 - INFO - Weights file not found \u2013 using default weights instead.\n2026-05-05 17:26:35,160 - INFO - Minimizing Hidden Employee Count Phase 1\n2026-05-05 17:26:41,751 - INFO - Trying to solve with {'Azubi': 0, 'Fachkraft': 0, 'Hilfskraft': 0}\n2026-05-05 17:26:45,411 - INFO - Wall time: 0.038923025131225586\n2026-05-05 17:26:48,299 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:26:53,650 - INFO - Trying to solve with {'Azubi': 5, 'Fachkraft': 5, 'Hilfskraft': 5}\n2026-05-05 17:26:56,004 - INFO - Wall time: 0.03494715690612793\n2026-05-05 17:26:57,217 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:27:00,951 - INFO - Trying to solve with {'Azubi': 10, 'Fachkraft': 10, 'Hilfskraft': 10}\n2026-05-05 17:27:03,573 - INFO - Wall time: 0.046434879302978516\n2026-05-05 17:27:04,031 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:27:07,731 - INFO - Trying to solve with {'Azubi': 15, 'Fachkraft': 15, 'Hilfskraft': 15}\n2026-05-05 17:27:16,781 - INFO - Wall time: 0.05709409713745117\n2026-05-05 17:27:16,781 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:27:16,781 - INFO - Trying to solve with {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 20}\n2026-05-05 17:27:17,456 - INFO - Wall time: 0.060022830963134766\n2026-05-05 17:27:17,456 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:27:21,504 - INFO - Hidden Employee Upper Bound: {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 20}\n\n2026-05-05 17:30:43,130 - INFO - Minimizing Hidden Employee Count Phase 2\n2026-05-05 17:30:43,130 - INFO - Trying to solve with {'Azubi': 19, 'Fachkraft': 20, 'Hilfskraft': 20}\n2026-05-05 17:30:43,925 - INFO - Wall time: 0.09692597389221191\n2026-05-05 17:30:43,925 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:30:43,925 - INFO - Trying to solve with {'Azubi': 20, 'Fachkraft': 19, 'Hilfskraft': 20}\n2026-05-05 17:30:44,671 - INFO - Wall time: 0.06854128837585449\n2026-05-05 17:30:44,672 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:30:44,672 - INFO - Trying to solve with {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 19}\n2026-05-05 17:30:45,428 - INFO - Wall time: 0.060633182525634766\n2026-05-05 17:30:45,429 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:30:45,429 - INFO - Hidden Employee Tight Bound: {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 20}\n\n2026-05-05 17:30:45,429 - INFO - General information:\n2026-05-05 17:30:45,429 - INFO - - planning unit: 77\n2026-05-05 17:30:45,429 - INFO - - start date: 2024-12-01\n2026-05-05 17:30:45,429 - INFO - - end date: 2024-12-31\n2026-05-05 17:30:45,429 - INFO - - number of employees: 101\n2026-05-05 17:30:45,429 - INFO - - number of days: 31\n2026-05-05 17:30:45,429 - INFO - - number of shifts: 8\n2026-05-05 17:30:47,459 - INFO - Found 4 complete weekends in the planning period\n2026-05-05 17:30:48,544 - INFO - Solving model...\n2026-05-05 17:30:48,544 - INFO - - number of variables: 28179\n2026-05-05 17:30:48,544 - INFO - - number of objectives: 10\n2026-05-05 17:30:48,544 - INFO - - number of constraints: 9\n2026-05-05 17:30:48,544 - INFO - Constraints:\n2026-05-05 17:30:48,544 - INFO - - Free Day After Night Shift Phase\n2026-05-05 17:30:48,544 - INFO - - Minimum Rest Time\n2026-05-05 17:30:48,544 - INFO - - Min Staffing\n2026-05-05 17:30:48,544 - INFO - - Rounds In Early Shift\n2026-05-05 17:30:48,544 - INFO - - One Shift Per Day\n2026-05-05 17:30:48,544 - INFO - - Target Working Time\n2026-05-05 17:30:48,545 - INFO - - Vacation Days And Shifts\n2026-05-05 17:30:48,545 - INFO - - Hierarchy Of Intermediate Shifts\n2026-05-05 17:30:48,545 - INFO - - Planned Shifts\n2026-05-05 17:30:48,545 - INFO - Objectives:\n2026-05-05 17:30:48,545 - INFO - - Free Days Near Weekend (weight: 2)\n2026-05-05 17:30:48,545 - INFO - - Minimize Consecutive Night Shifts (weight: 2)\n2026-05-05 17:30:48,545 - INFO - - Minimize Hidden Employees (weight: 100)\n2026-05-05 17:30:48,545 - INFO - - Minimize Overtime (weight: 4)\n2026-05-05 17:30:48,545 - INFO - - Not Too Many Consecutive Days (weight: 1)\n2026-05-05 17:30:48,545 - INFO - - Rotate Shifts Forward (weight: 1)\n2026-05-05 17:30:48,545 - INFO - - Maximize Employee Wishes (weight: 3)\n2026-05-05 17:30:48,545 - INFO - - Free Day After Night Shift Phase (weight: 3)\n2026-05-05 17:30:48,545 - INFO - - Every Second Weekend Free (weight: 1)\n2026-05-05 17:30:48,545 - INFO - - Preferred Block Length (weight: 1)\n2026-05-05 17:30:48,647 - INFO - Timeout set to 600 seconds\n2026-05-05 17:30:49,075 - INFO - Solving completed in 0.43 seconds\n", + "createdAt": "2026-05-05T15:26:14.210Z", + "duration": 274870, + "error": "2026-05-05 17:26:35,133 - INFO - Weights file not found \u2013 using default weights instead.\n2026-05-05 17:26:35,160 - INFO - Minimizing Hidden Employee Count Phase 1\n2026-05-05 17:26:41,751 - INFO - Trying to solve with {'Azubi': 0, 'Fachkraft': 0, 'Hilfskraft': 0}\n2026-05-05 17:26:45,411 - INFO - Wall time: 0.038923025131225586\n2026-05-05 17:26:48,299 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:26:53,650 - INFO - Trying to solve with {'Azubi': 5, 'Fachkraft': 5, 'Hilfskraft': 5}\n2026-05-05 17:26:56,004 - INFO - Wall time: 0.03494715690612793\n2026-05-05 17:26:57,217 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:27:00,951 - INFO - Trying to solve with {'Azubi': 10, 'Fachkraft': 10, 'Hilfskraft': 10}\n2026-05-05 17:27:03,573 - INFO - Wall time: 0.046434879302978516\n2026-05-05 17:27:04,031 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:27:07,731 - INFO - Trying to solve with {'Azubi': 15, 'Fachkraft': 15, 'Hilfskraft': 15}\n2026-05-05 17:27:16,781 - INFO - Wall time: 0.05709409713745117\n2026-05-05 17:27:16,781 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:27:16,781 - INFO - Trying to solve with {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 20}\n2026-05-05 17:27:17,456 - INFO - Wall time: 0.060022830963134766\n2026-05-05 17:27:17,456 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:27:21,504 - INFO - Hidden Employee Upper Bound: {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 20}\n\n2026-05-05 17:30:43,130 - INFO - Minimizing Hidden Employee Count Phase 2\n2026-05-05 17:30:43,130 - INFO - Trying to solve with {'Azubi': 19, 'Fachkraft': 20, 'Hilfskraft': 20}\n2026-05-05 17:30:43,925 - INFO - Wall time: 0.09692597389221191\n2026-05-05 17:30:43,925 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:30:43,925 - INFO - Trying to solve with {'Azubi': 20, 'Fachkraft': 19, 'Hilfskraft': 20}\n2026-05-05 17:30:44,671 - INFO - Wall time: 0.06854128837585449\n2026-05-05 17:30:44,672 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:30:44,672 - INFO - Trying to solve with {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 19}\n2026-05-05 17:30:45,428 - INFO - Wall time: 0.060633182525634766\n2026-05-05 17:30:45,429 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:30:45,429 - INFO - Hidden Employee Tight Bound: {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 20}\n\n2026-05-05 17:30:45,429 - INFO - General information:\n2026-05-05 17:30:45,429 - INFO - - planning unit: 77\n2026-05-05 17:30:45,429 - INFO - - start date: 2024-12-01\n2026-05-05 17:30:45,429 - INFO - - end date: 2024-12-31\n2026-05-05 17:30:45,429 - INFO - - number of employees: 101\n2026-05-05 17:30:45,429 - INFO - - number of days: 31\n2026-05-05 17:30:45,429 - INFO - - number of shifts: 8\n2026-05-05 17:30:47,459 - INFO - Found 4 complete weekends in the planning period\n2026-05-05 17:30:48,544 - INFO - Solving model...\n2026-05-05 17:30:48,544 - INFO - - number of variables: 28179\n2026-05-05 17:30:48,544 - INFO - - number of objectives: 10\n2026-05-05 17:30:48,544 - INFO - - number of constraints: 9\n2026-05-05 17:30:48,544 - INFO - Constraints:\n2026-05-05 17:30:48,544 - INFO - - Free Day After Night Shift Phase\n2026-05-05 17:30:48,544 - INFO - - Minimum Rest Time\n2026-05-05 17:30:48,544 - INFO - - Min Staffing\n2026-05-05 17:30:48,544 - INFO - - Rounds In Early Shift\n2026-05-05 17:30:48,544 - INFO - - One Shift Per Day\n2026-05-05 17:30:48,544 - INFO - - Target Working Time\n2026-05-05 17:30:48,545 - INFO - - Vacation Days And Shifts\n2026-05-05 17:30:48,545 - INFO - - Hierarchy Of Intermediate Shifts\n2026-05-05 17:30:48,545 - INFO - - Planned Shifts\n2026-05-05 17:30:48,545 - INFO - Objectives:\n2026-05-05 17:30:48,545 - INFO - - Free Days Near Weekend (weight: 2)\n2026-05-05 17:30:48,545 - INFO - - Minimize Consecutive Night Shifts (weight: 2)\n2026-05-05 17:30:48,545 - INFO - - Minimize Hidden Employees (weight: 100)\n2026-05-05 17:30:48,545 - INFO - - Minimize Overtime (weight: 4)\n2026-05-05 17:30:48,545 - INFO - - Not Too Many Consecutive Days (weight: 1)\n2026-05-05 17:30:48,545 - INFO - - Rotate Shifts Forward (weight: 1)\n2026-05-05 17:30:48,545 - INFO - - Maximize Employee Wishes (weight: 3)\n2026-05-05 17:30:48,545 - INFO - - Free Day After Night Shift Phase (weight: 3)\n2026-05-05 17:30:48,545 - INFO - - Every Second Weekend Free (weight: 1)\n2026-05-05 17:30:48,545 - INFO - - Preferred Block Length (weight: 1)\n2026-05-05 17:30:48,647 - INFO - Timeout set to 600 seconds\n2026-05-05 17:30:49,075 - INFO - Solving completed in 0.43 seconds\n", + "id": "4c1e4964-702b-4539-bac3-9052439d931a", + "metadata": { + "expectedSolutions": 1, + "feasibleSolutions": [], + "solutionsGenerated": 0 + }, + "params": { + "end": "2024-12-31", + "start": "2024-12-01", + "timeout": 600, + "unit": 77 + }, + "status": "failed", + "type": "solve" + }, + { + "caseId": 77, + "completedAt": "2026-05-05T15:20:02.864Z", + "consoleOutput": "\nStatistics\n - conflicts : 0\n - branches : 0\n - wall time : 0.267527 s\n - objective value: 0.0\n - status : INFEASIBLE\n - objective value: 0.0\n - info : \n2026-05-05 17:16:12,509 - INFO - Weights file not found \u2013 using default weights instead.\n2026-05-05 17:16:14,830 - INFO - Minimizing Hidden Employee Count Phase 1\n2026-05-05 17:16:14,830 - INFO - Trying to solve with {'Azubi': 0, 'Fachkraft': 0, 'Hilfskraft': 0}\n2026-05-05 17:16:15,529 - INFO - Wall time: 0.043415069580078125\n2026-05-05 17:16:15,529 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:16:15,530 - INFO - Trying to solve with {'Azubi': 5, 'Fachkraft': 5, 'Hilfskraft': 5}\n2026-05-05 17:16:16,252 - INFO - Wall time: 0.03508806228637695\n2026-05-05 17:16:16,253 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:16:16,253 - INFO - Trying to solve with {'Azubi': 10, 'Fachkraft': 10, 'Hilfskraft': 10}\n2026-05-05 17:16:17,260 - INFO - Wall time: 0.03985786437988281\n2026-05-05 17:16:17,260 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:16:17,260 - INFO - Trying to solve with {'Azubi': 15, 'Fachkraft': 15, 'Hilfskraft': 15}\n2026-05-05 17:16:18,327 - INFO - Wall time: 0.051287174224853516\n2026-05-05 17:16:18,327 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:16:18,327 - INFO - Trying to solve with {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 20}\n2026-05-05 17:16:19,610 - INFO - Wall time: 0.05968880653381348\n2026-05-05 17:16:19,610 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:16:19,610 - INFO - Hidden Employee Upper Bound: {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 20}\n\n2026-05-05 17:16:19,610 - INFO - Minimizing Hidden Employee Count Phase 2\n2026-05-05 17:16:19,610 - INFO - Trying to solve with {'Azubi': 19, 'Fachkraft': 20, 'Hilfskraft': 20}\n2026-05-05 17:16:20,863 - INFO - Wall time: 0.05706596374511719\n2026-05-05 17:16:20,864 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:16:20,864 - INFO - Trying to solve with {'Azubi': 20, 'Fachkraft': 19, 'Hilfskraft': 20}\n2026-05-05 17:16:22,111 - INFO - Wall time: 0.06203413009643555\n2026-05-05 17:16:22,111 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:16:22,111 - INFO - Trying to solve with {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 19}\n2026-05-05 17:16:23,454 - INFO - Wall time: 0.05847501754760742\n2026-05-05 17:16:23,454 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:16:23,454 - INFO - Hidden Employee Tight Bound: {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 20}\n\n2026-05-05 17:16:23,454 - INFO - General information:\n2026-05-05 17:16:23,454 - INFO - - planning unit: 77\n2026-05-05 17:16:23,454 - INFO - - start date: 2024-12-01\n2026-05-05 17:16:23,454 - INFO - - end date: 2024-12-31\n2026-05-05 17:16:23,454 - INFO - - number of employees: 101\n2026-05-05 17:16:23,454 - INFO - - number of days: 31\n2026-05-05 17:16:23,454 - INFO - - number of shifts: 8\n2026-05-05 17:16:28,033 - INFO - Found 4 complete weekends in the planning period\n2026-05-05 17:16:29,998 - INFO - Solving model...\n2026-05-05 17:16:29,999 - INFO - - number of variables: 28179\n2026-05-05 17:16:29,999 - INFO - - number of objectives: 10\n2026-05-05 17:16:29,999 - INFO - - number of constraints: 9\n2026-05-05 17:16:29,999 - INFO - Constraints:\n2026-05-05 17:16:29,999 - INFO - - Free Day After Night Shift Phase\n2026-05-05 17:16:29,999 - INFO - - Minimum Rest Time\n2026-05-05 17:16:29,999 - INFO - - Min Staffing\n2026-05-05 17:16:29,999 - INFO - - Rounds In Early Shift\n2026-05-05 17:16:29,999 - INFO - - One Shift Per Day\n2026-05-05 17:16:29,999 - INFO - - Target Working Time\n2026-05-05 17:16:30,000 - INFO - - Vacation Days And Shifts\n2026-05-05 17:16:30,000 - INFO - - Hierarchy Of Intermediate Shifts\n2026-05-05 17:16:30,000 - INFO - - Planned Shifts\n2026-05-05 17:16:30,000 - INFO - Objectives:\n2026-05-05 17:16:30,000 - INFO - - Free Days Near Weekend (weight: 2)\n2026-05-05 17:16:30,000 - INFO - - Minimize Consecutive Night Shifts (weight: 2)\n2026-05-05 17:16:30,000 - INFO - - Minimize Hidden Employees (weight: 100)\n2026-05-05 17:16:30,000 - INFO - - Minimize Overtime (weight: 4)\n2026-05-05 17:16:30,000 - INFO - - Not Too Many Consecutive Days (weight: 1)\n2026-05-05 17:16:30,000 - INFO - - Rotate Shifts Forward (weight: 1)\n2026-05-05 17:16:30,000 - INFO - - Maximize Employee Wishes (weight: 3)\n2026-05-05 17:16:30,000 - INFO - - Free Day After Night Shift Phase (weight: 3)\n2026-05-05 17:16:30,000 - INFO - - Every Second Weekend Free (weight: 1)\n2026-05-05 17:16:30,000 - INFO - - Preferred Block Length (weight: 1)\n2026-05-05 17:16:30,112 - INFO - Timeout set to 600 seconds\n2026-05-05 17:16:30,540 - INFO - Solving completed in 0.43 seconds\n", + "createdAt": "2026-05-05T15:15:27.051Z", + "duration": 275794, + "error": "2026-05-05 17:16:12,509 - INFO - Weights file not found \u2013 using default weights instead.\n2026-05-05 17:16:14,830 - INFO - Minimizing Hidden Employee Count Phase 1\n2026-05-05 17:16:14,830 - INFO - Trying to solve with {'Azubi': 0, 'Fachkraft': 0, 'Hilfskraft': 0}\n2026-05-05 17:16:15,529 - INFO - Wall time: 0.043415069580078125\n2026-05-05 17:16:15,529 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:16:15,530 - INFO - Trying to solve with {'Azubi': 5, 'Fachkraft': 5, 'Hilfskraft': 5}\n2026-05-05 17:16:16,252 - INFO - Wall time: 0.03508806228637695\n2026-05-05 17:16:16,253 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:16:16,253 - INFO - Trying to solve with {'Azubi': 10, 'Fachkraft': 10, 'Hilfskraft': 10}\n2026-05-05 17:16:17,260 - INFO - Wall time: 0.03985786437988281\n2026-05-05 17:16:17,260 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:16:17,260 - INFO - Trying to solve with {'Azubi': 15, 'Fachkraft': 15, 'Hilfskraft': 15}\n2026-05-05 17:16:18,327 - INFO - Wall time: 0.051287174224853516\n2026-05-05 17:16:18,327 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:16:18,327 - INFO - Trying to solve with {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 20}\n2026-05-05 17:16:19,610 - INFO - Wall time: 0.05968880653381348\n2026-05-05 17:16:19,610 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:16:19,610 - INFO - Hidden Employee Upper Bound: {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 20}\n\n2026-05-05 17:16:19,610 - INFO - Minimizing Hidden Employee Count Phase 2\n2026-05-05 17:16:19,610 - INFO - Trying to solve with {'Azubi': 19, 'Fachkraft': 20, 'Hilfskraft': 20}\n2026-05-05 17:16:20,863 - INFO - Wall time: 0.05706596374511719\n2026-05-05 17:16:20,864 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:16:20,864 - INFO - Trying to solve with {'Azubi': 20, 'Fachkraft': 19, 'Hilfskraft': 20}\n2026-05-05 17:16:22,111 - INFO - Wall time: 0.06203413009643555\n2026-05-05 17:16:22,111 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:16:22,111 - INFO - Trying to solve with {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 19}\n2026-05-05 17:16:23,454 - INFO - Wall time: 0.05847501754760742\n2026-05-05 17:16:23,454 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:16:23,454 - INFO - Hidden Employee Tight Bound: {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 20}\n\n2026-05-05 17:16:23,454 - INFO - General information:\n2026-05-05 17:16:23,454 - INFO - - planning unit: 77\n2026-05-05 17:16:23,454 - INFO - - start date: 2024-12-01\n2026-05-05 17:16:23,454 - INFO - - end date: 2024-12-31\n2026-05-05 17:16:23,454 - INFO - - number of employees: 101\n2026-05-05 17:16:23,454 - INFO - - number of days: 31\n2026-05-05 17:16:23,454 - INFO - - number of shifts: 8\n2026-05-05 17:16:28,033 - INFO - Found 4 complete weekends in the planning period\n2026-05-05 17:16:29,998 - INFO - Solving model...\n2026-05-05 17:16:29,999 - INFO - - number of variables: 28179\n2026-05-05 17:16:29,999 - INFO - - number of objectives: 10\n2026-05-05 17:16:29,999 - INFO - - number of constraints: 9\n2026-05-05 17:16:29,999 - INFO - Constraints:\n2026-05-05 17:16:29,999 - INFO - - Free Day After Night Shift Phase\n2026-05-05 17:16:29,999 - INFO - - Minimum Rest Time\n2026-05-05 17:16:29,999 - INFO - - Min Staffing\n2026-05-05 17:16:29,999 - INFO - - Rounds In Early Shift\n2026-05-05 17:16:29,999 - INFO - - One Shift Per Day\n2026-05-05 17:16:29,999 - INFO - - Target Working Time\n2026-05-05 17:16:30,000 - INFO - - Vacation Days And Shifts\n2026-05-05 17:16:30,000 - INFO - - Hierarchy Of Intermediate Shifts\n2026-05-05 17:16:30,000 - INFO - - Planned Shifts\n2026-05-05 17:16:30,000 - INFO - Objectives:\n2026-05-05 17:16:30,000 - INFO - - Free Days Near Weekend (weight: 2)\n2026-05-05 17:16:30,000 - INFO - - Minimize Consecutive Night Shifts (weight: 2)\n2026-05-05 17:16:30,000 - INFO - - Minimize Hidden Employees (weight: 100)\n2026-05-05 17:16:30,000 - INFO - - Minimize Overtime (weight: 4)\n2026-05-05 17:16:30,000 - INFO - - Not Too Many Consecutive Days (weight: 1)\n2026-05-05 17:16:30,000 - INFO - - Rotate Shifts Forward (weight: 1)\n2026-05-05 17:16:30,000 - INFO - - Maximize Employee Wishes (weight: 3)\n2026-05-05 17:16:30,000 - INFO - - Free Day After Night Shift Phase (weight: 3)\n2026-05-05 17:16:30,000 - INFO - - Every Second Weekend Free (weight: 1)\n2026-05-05 17:16:30,000 - INFO - - Preferred Block Length (weight: 1)\n2026-05-05 17:16:30,112 - INFO - Timeout set to 600 seconds\n2026-05-05 17:16:30,540 - INFO - Solving completed in 0.43 seconds\n", + "id": "9c25b5b1-16cc-428e-a3df-27864c73a550", + "metadata": { + "expectedSolutions": 1, + "feasibleSolutions": [], + "solutionsGenerated": 0 + }, + "params": { + "end": "2024-12-31", + "start": "2024-12-01", + "timeout": 600, + "unit": 77 + }, + "status": "failed", + "type": "solve" + }, + { + "caseId": 77, + "completedAt": "2026-05-05T15:15:17.309Z", + "createdAt": "2026-05-05T15:15:17.305Z", + "duration": 4, + "error": "fetch failed", + "id": "8df7e071-4b56-45e5-9103-32e91fd9f2f6", + "metadata": { + "expectedSolutions": 1, + "feasibleSolutions": [], + "solutionsGenerated": 0 + }, + "params": { + "end": "2024-12-31", + "start": "2024-12-01", + "timeout": 600, + "unit": 77 + }, + "status": "failed", + "type": "solve" + }, + { + "caseId": 77, + "completedAt": "2026-05-05T15:15:14.586Z", + "createdAt": "2026-05-05T15:15:14.544Z", + "duration": 42, + "error": "fetch failed", + "id": "cd37f9ac-b49e-4753-bc88-dba30bc376dc", + "metadata": { + "expectedSolutions": 1, + "feasibleSolutions": [], + "solutionsGenerated": 0 + }, + "params": { + "end": "2024-12-31", + "start": "2024-12-01", + "timeout": 600, + "unit": 77 + }, + "status": "failed", + "type": "solve" + }, + { + "caseId": 77, + "completedAt": "2026-05-05T15:13:11.482Z", + "consoleOutput": "\nStatistics\n - conflicts : 0\n - branches : 0\n - wall time : 0.31291800000000003 s\n - objective value: 0.0\n - status : INFEASIBLE\n - objective value: 0.0\n - info : \n2026-05-05 17:13:03,148 - INFO - Weights file not found \u2013 using default weights instead.\n2026-05-05 17:13:03,153 - INFO - Minimizing Hidden Employee Count Phase 1\n2026-05-05 17:13:03,153 - INFO - Trying to solve with {'Azubi': 0, 'Fachkraft': 0, 'Hilfskraft': 0}\n2026-05-05 17:13:03,465 - INFO - Wall time: 0.03724503517150879\n2026-05-05 17:13:03,466 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:13:03,466 - INFO - Trying to solve with {'Azubi': 5, 'Fachkraft': 5, 'Hilfskraft': 5}\n2026-05-05 17:13:03,884 - INFO - Wall time: 0.03327608108520508\n2026-05-05 17:13:03,884 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:13:03,884 - INFO - Trying to solve with {'Azubi': 10, 'Fachkraft': 10, 'Hilfskraft': 10}\n2026-05-05 17:13:04,306 - INFO - Wall time: 0.03998303413391113\n2026-05-05 17:13:04,306 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:13:04,306 - INFO - Trying to solve with {'Azubi': 15, 'Fachkraft': 15, 'Hilfskraft': 15}\n2026-05-05 17:13:04,842 - INFO - Wall time: 0.05156087875366211\n2026-05-05 17:13:04,843 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:13:04,843 - INFO - Trying to solve with {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 20}\n2026-05-05 17:13:05,438 - INFO - Wall time: 0.06091904640197754\n2026-05-05 17:13:05,438 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:13:05,438 - INFO - Hidden Employee Upper Bound: {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 20}\n\n2026-05-05 17:13:05,438 - INFO - Minimizing Hidden Employee Count Phase 2\n2026-05-05 17:13:05,438 - INFO - Trying to solve with {'Azubi': 19, 'Fachkraft': 20, 'Hilfskraft': 20}\n2026-05-05 17:13:06,202 - INFO - Wall time: 0.07690310478210449\n2026-05-05 17:13:06,202 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:13:06,203 - INFO - Trying to solve with {'Azubi': 20, 'Fachkraft': 19, 'Hilfskraft': 20}\n2026-05-05 17:13:06,809 - INFO - Wall time: 0.056910037994384766\n2026-05-05 17:13:06,809 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:13:06,810 - INFO - Trying to solve with {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 19}\n2026-05-05 17:13:07,496 - INFO - Wall time: 0.05707907676696777\n2026-05-05 17:13:07,496 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:13:07,496 - INFO - Hidden Employee Tight Bound: {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 20}\n\n2026-05-05 17:13:07,496 - INFO - General information:\n2026-05-05 17:13:07,496 - INFO - - planning unit: 77\n2026-05-05 17:13:07,496 - INFO - - start date: 2024-12-01\n2026-05-05 17:13:07,496 - INFO - - end date: 2024-12-31\n2026-05-05 17:13:07,496 - INFO - - number of employees: 101\n2026-05-05 17:13:07,496 - INFO - - number of days: 31\n2026-05-05 17:13:07,496 - INFO - - number of shifts: 8\n2026-05-05 17:13:09,696 - INFO - Found 4 complete weekends in the planning period\n2026-05-05 17:13:10,859 - INFO - Solving model...\n2026-05-05 17:13:10,860 - INFO - - number of variables: 28179\n2026-05-05 17:13:10,860 - INFO - - number of objectives: 10\n2026-05-05 17:13:10,860 - INFO - - number of constraints: 9\n2026-05-05 17:13:10,860 - INFO - Constraints:\n2026-05-05 17:13:10,860 - INFO - - Free Day After Night Shift Phase\n2026-05-05 17:13:10,860 - INFO - - Minimum Rest Time\n2026-05-05 17:13:10,860 - INFO - - Min Staffing\n2026-05-05 17:13:10,860 - INFO - - Rounds In Early Shift\n2026-05-05 17:13:10,860 - INFO - - One Shift Per Day\n2026-05-05 17:13:10,860 - INFO - - Target Working Time\n2026-05-05 17:13:10,860 - INFO - - Vacation Days And Shifts\n2026-05-05 17:13:10,860 - INFO - - Hierarchy Of Intermediate Shifts\n2026-05-05 17:13:10,860 - INFO - - Planned Shifts\n2026-05-05 17:13:10,860 - INFO - Objectives:\n2026-05-05 17:13:10,860 - INFO - - Free Days Near Weekend (weight: 2)\n2026-05-05 17:13:10,860 - INFO - - Minimize Consecutive Night Shifts (weight: 2)\n2026-05-05 17:13:10,860 - INFO - - Minimize Hidden Employees (weight: 100)\n2026-05-05 17:13:10,860 - INFO - - Minimize Overtime (weight: 4)\n2026-05-05 17:13:10,860 - INFO - - Not Too Many Consecutive Days (weight: 1)\n2026-05-05 17:13:10,860 - INFO - - Rotate Shifts Forward (weight: 1)\n2026-05-05 17:13:10,860 - INFO - - Maximize Employee Wishes (weight: 3)\n2026-05-05 17:13:10,860 - INFO - - Free Day After Night Shift Phase (weight: 3)\n2026-05-05 17:13:10,860 - INFO - - Every Second Weekend Free (weight: 1)\n2026-05-05 17:13:10,860 - INFO - - Preferred Block Length (weight: 1)\n2026-05-05 17:13:10,971 - INFO - Timeout set to 600 seconds\n2026-05-05 17:13:11,477 - INFO - Solving completed in 0.51 seconds\n", + "createdAt": "2026-05-05T15:13:03.103Z", + "duration": 8378, + "error": "2026-05-05 17:13:03,148 - INFO - Weights file not found \u2013 using default weights instead.\n2026-05-05 17:13:03,153 - INFO - Minimizing Hidden Employee Count Phase 1\n2026-05-05 17:13:03,153 - INFO - Trying to solve with {'Azubi': 0, 'Fachkraft': 0, 'Hilfskraft': 0}\n2026-05-05 17:13:03,465 - INFO - Wall time: 0.03724503517150879\n2026-05-05 17:13:03,466 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:13:03,466 - INFO - Trying to solve with {'Azubi': 5, 'Fachkraft': 5, 'Hilfskraft': 5}\n2026-05-05 17:13:03,884 - INFO - Wall time: 0.03327608108520508\n2026-05-05 17:13:03,884 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:13:03,884 - INFO - Trying to solve with {'Azubi': 10, 'Fachkraft': 10, 'Hilfskraft': 10}\n2026-05-05 17:13:04,306 - INFO - Wall time: 0.03998303413391113\n2026-05-05 17:13:04,306 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:13:04,306 - INFO - Trying to solve with {'Azubi': 15, 'Fachkraft': 15, 'Hilfskraft': 15}\n2026-05-05 17:13:04,842 - INFO - Wall time: 0.05156087875366211\n2026-05-05 17:13:04,843 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:13:04,843 - INFO - Trying to solve with {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 20}\n2026-05-05 17:13:05,438 - INFO - Wall time: 0.06091904640197754\n2026-05-05 17:13:05,438 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:13:05,438 - INFO - Hidden Employee Upper Bound: {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 20}\n\n2026-05-05 17:13:05,438 - INFO - Minimizing Hidden Employee Count Phase 2\n2026-05-05 17:13:05,438 - INFO - Trying to solve with {'Azubi': 19, 'Fachkraft': 20, 'Hilfskraft': 20}\n2026-05-05 17:13:06,202 - INFO - Wall time: 0.07690310478210449\n2026-05-05 17:13:06,202 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:13:06,203 - INFO - Trying to solve with {'Azubi': 20, 'Fachkraft': 19, 'Hilfskraft': 20}\n2026-05-05 17:13:06,809 - INFO - Wall time: 0.056910037994384766\n2026-05-05 17:13:06,809 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:13:06,810 - INFO - Trying to solve with {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 19}\n2026-05-05 17:13:07,496 - INFO - Wall time: 0.05707907676696777\n2026-05-05 17:13:07,496 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:13:07,496 - INFO - Hidden Employee Tight Bound: {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 20}\n\n2026-05-05 17:13:07,496 - INFO - General information:\n2026-05-05 17:13:07,496 - INFO - - planning unit: 77\n2026-05-05 17:13:07,496 - INFO - - start date: 2024-12-01\n2026-05-05 17:13:07,496 - INFO - - end date: 2024-12-31\n2026-05-05 17:13:07,496 - INFO - - number of employees: 101\n2026-05-05 17:13:07,496 - INFO - - number of days: 31\n2026-05-05 17:13:07,496 - INFO - - number of shifts: 8\n2026-05-05 17:13:09,696 - INFO - Found 4 complete weekends in the planning period\n2026-05-05 17:13:10,859 - INFO - Solving model...\n2026-05-05 17:13:10,860 - INFO - - number of variables: 28179\n2026-05-05 17:13:10,860 - INFO - - number of objectives: 10\n2026-05-05 17:13:10,860 - INFO - - number of constraints: 9\n2026-05-05 17:13:10,860 - INFO - Constraints:\n2026-05-05 17:13:10,860 - INFO - - Free Day After Night Shift Phase\n2026-05-05 17:13:10,860 - INFO - - Minimum Rest Time\n2026-05-05 17:13:10,860 - INFO - - Min Staffing\n2026-05-05 17:13:10,860 - INFO - - Rounds In Early Shift\n2026-05-05 17:13:10,860 - INFO - - One Shift Per Day\n2026-05-05 17:13:10,860 - INFO - - Target Working Time\n2026-05-05 17:13:10,860 - INFO - - Vacation Days And Shifts\n2026-05-05 17:13:10,860 - INFO - - Hierarchy Of Intermediate Shifts\n2026-05-05 17:13:10,860 - INFO - - Planned Shifts\n2026-05-05 17:13:10,860 - INFO - Objectives:\n2026-05-05 17:13:10,860 - INFO - - Free Days Near Weekend (weight: 2)\n2026-05-05 17:13:10,860 - INFO - - Minimize Consecutive Night Shifts (weight: 2)\n2026-05-05 17:13:10,860 - INFO - - Minimize Hidden Employees (weight: 100)\n2026-05-05 17:13:10,860 - INFO - - Minimize Overtime (weight: 4)\n2026-05-05 17:13:10,860 - INFO - - Not Too Many Consecutive Days (weight: 1)\n2026-05-05 17:13:10,860 - INFO - - Rotate Shifts Forward (weight: 1)\n2026-05-05 17:13:10,860 - INFO - - Maximize Employee Wishes (weight: 3)\n2026-05-05 17:13:10,860 - INFO - - Free Day After Night Shift Phase (weight: 3)\n2026-05-05 17:13:10,860 - INFO - - Every Second Weekend Free (weight: 1)\n2026-05-05 17:13:10,860 - INFO - - Preferred Block Length (weight: 1)\n2026-05-05 17:13:10,971 - INFO - Timeout set to 600 seconds\n2026-05-05 17:13:11,477 - INFO - Solving completed in 0.51 seconds\n", + "id": "771ac16e-a2af-49bc-89d3-6689ac80a9bf", + "metadata": { + "expectedSolutions": 1, + "feasibleSolutions": [], + "solutionsGenerated": 0 + }, + "params": { + "end": "2024-12-31", + "start": "2024-12-01", + "timeout": 600, + "unit": 77 + }, + "status": "failed", + "type": "solve" + }, + { + "caseId": 77, + "completedAt": "2026-05-05T15:07:56.257Z", + "consoleOutput": "\nStatistics\n - conflicts : 0\n - branches : 0\n - wall time : 0.251724 s\n - objective value: 0.0\n - status : INFEASIBLE\n - objective value: 0.0\n - info : \n2026-05-05 17:07:48,784 - INFO - Weights file not found \u2013 using default weights instead.\n2026-05-05 17:07:48,787 - INFO - Minimizing Hidden Employee Count Phase 1\n2026-05-05 17:07:48,787 - INFO - Trying to solve with {'Azubi': 0, 'Fachkraft': 0, 'Hilfskraft': 0}\n2026-05-05 17:07:49,065 - INFO - Wall time: 0.03700995445251465\n2026-05-05 17:07:49,067 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:07:49,067 - INFO - Trying to solve with {'Azubi': 5, 'Fachkraft': 5, 'Hilfskraft': 5}\n2026-05-05 17:07:49,452 - INFO - Wall time: 0.0325932502746582\n2026-05-05 17:07:49,454 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:07:49,455 - INFO - Trying to solve with {'Azubi': 10, 'Fachkraft': 10, 'Hilfskraft': 10}\n2026-05-05 17:07:49,889 - INFO - Wall time: 0.03990006446838379\n2026-05-05 17:07:49,892 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:07:49,892 - INFO - Trying to solve with {'Azubi': 15, 'Fachkraft': 15, 'Hilfskraft': 15}\n2026-05-05 17:07:50,383 - INFO - Wall time: 0.051725149154663086\n2026-05-05 17:07:50,389 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:07:50,389 - INFO - Trying to solve with {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 20}\n2026-05-05 17:07:50,983 - INFO - Wall time: 0.059905052185058594\n2026-05-05 17:07:50,988 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:07:50,988 - INFO - Hidden Employee Upper Bound: {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 20}\n\n2026-05-05 17:07:50,988 - INFO - Minimizing Hidden Employee Count Phase 2\n2026-05-05 17:07:50,988 - INFO - Trying to solve with {'Azubi': 19, 'Fachkraft': 20, 'Hilfskraft': 20}\n2026-05-05 17:07:51,565 - INFO - Wall time: 0.056684017181396484\n2026-05-05 17:07:51,570 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:07:51,570 - INFO - Trying to solve with {'Azubi': 20, 'Fachkraft': 19, 'Hilfskraft': 20}\n2026-05-05 17:07:52,126 - INFO - Wall time: 0.05614209175109863\n2026-05-05 17:07:52,131 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:07:52,131 - INFO - Trying to solve with {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 19}\n2026-05-05 17:07:52,704 - INFO - Wall time: 0.055531978607177734\n2026-05-05 17:07:52,709 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:07:52,709 - INFO - Hidden Employee Tight Bound: {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 20}\n\n2026-05-05 17:07:52,709 - INFO - General information:\n2026-05-05 17:07:52,709 - INFO - - planning unit: 77\n2026-05-05 17:07:52,709 - INFO - - start date: 2024-12-01\n2026-05-05 17:07:52,709 - INFO - - end date: 2024-12-31\n2026-05-05 17:07:52,709 - INFO - - number of employees: 101\n2026-05-05 17:07:52,709 - INFO - - number of days: 31\n2026-05-05 17:07:52,709 - INFO - - number of shifts: 8\n2026-05-05 17:07:54,641 - INFO - Found 4 complete weekends in the planning period\n2026-05-05 17:07:55,735 - INFO - Solving model...\n2026-05-05 17:07:55,735 - INFO - - number of variables: 28179\n2026-05-05 17:07:55,735 - INFO - - number of objectives: 10\n2026-05-05 17:07:55,735 - INFO - - number of constraints: 9\n2026-05-05 17:07:55,735 - INFO - Constraints:\n2026-05-05 17:07:55,735 - INFO - - Free Day After Night Shift Phase\n2026-05-05 17:07:55,735 - INFO - - Minimum Rest Time\n2026-05-05 17:07:55,735 - INFO - - Min Staffing\n2026-05-05 17:07:55,735 - INFO - - Rounds In Early Shift\n2026-05-05 17:07:55,735 - INFO - - One Shift Per Day\n2026-05-05 17:07:55,735 - INFO - - Target Working Time\n2026-05-05 17:07:55,735 - INFO - - Vacation Days And Shifts\n2026-05-05 17:07:55,735 - INFO - - Hierarchy Of Intermediate Shifts\n2026-05-05 17:07:55,735 - INFO - - Planned Shifts\n2026-05-05 17:07:55,735 - INFO - Objectives:\n2026-05-05 17:07:55,735 - INFO - - Free Days Near Weekend (weight: 2)\n2026-05-05 17:07:55,735 - INFO - - Minimize Consecutive Night Shifts (weight: 2)\n2026-05-05 17:07:55,735 - INFO - - Minimize Hidden Employees (weight: 100)\n2026-05-05 17:07:55,735 - INFO - - Minimize Overtime (weight: 4)\n2026-05-05 17:07:55,735 - INFO - - Not Too Many Consecutive Days (weight: 1)\n2026-05-05 17:07:55,735 - INFO - - Rotate Shifts Forward (weight: 1)\n2026-05-05 17:07:55,735 - INFO - - Maximize Employee Wishes (weight: 3)\n2026-05-05 17:07:55,735 - INFO - - Free Day After Night Shift Phase (weight: 3)\n2026-05-05 17:07:55,735 - INFO - - Every Second Weekend Free (weight: 1)\n2026-05-05 17:07:55,735 - INFO - - Preferred Block Length (weight: 1)\n2026-05-05 17:07:55,837 - INFO - Timeout set to 600 seconds\n2026-05-05 17:07:56,218 - INFO - Solving completed in 0.38 seconds\n", + "createdAt": "2026-05-05T15:07:48.769Z", + "duration": 7486, + "error": "2026-05-05 17:07:48,784 - INFO - Weights file not found \u2013 using default weights instead.\n2026-05-05 17:07:48,787 - INFO - Minimizing Hidden Employee Count Phase 1\n2026-05-05 17:07:48,787 - INFO - Trying to solve with {'Azubi': 0, 'Fachkraft': 0, 'Hilfskraft': 0}\n2026-05-05 17:07:49,065 - INFO - Wall time: 0.03700995445251465\n2026-05-05 17:07:49,067 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:07:49,067 - INFO - Trying to solve with {'Azubi': 5, 'Fachkraft': 5, 'Hilfskraft': 5}\n2026-05-05 17:07:49,452 - INFO - Wall time: 0.0325932502746582\n2026-05-05 17:07:49,454 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:07:49,455 - INFO - Trying to solve with {'Azubi': 10, 'Fachkraft': 10, 'Hilfskraft': 10}\n2026-05-05 17:07:49,889 - INFO - Wall time: 0.03990006446838379\n2026-05-05 17:07:49,892 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:07:49,892 - INFO - Trying to solve with {'Azubi': 15, 'Fachkraft': 15, 'Hilfskraft': 15}\n2026-05-05 17:07:50,383 - INFO - Wall time: 0.051725149154663086\n2026-05-05 17:07:50,389 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:07:50,389 - INFO - Trying to solve with {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 20}\n2026-05-05 17:07:50,983 - INFO - Wall time: 0.059905052185058594\n2026-05-05 17:07:50,988 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:07:50,988 - INFO - Hidden Employee Upper Bound: {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 20}\n\n2026-05-05 17:07:50,988 - INFO - Minimizing Hidden Employee Count Phase 2\n2026-05-05 17:07:50,988 - INFO - Trying to solve with {'Azubi': 19, 'Fachkraft': 20, 'Hilfskraft': 20}\n2026-05-05 17:07:51,565 - INFO - Wall time: 0.056684017181396484\n2026-05-05 17:07:51,570 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:07:51,570 - INFO - Trying to solve with {'Azubi': 20, 'Fachkraft': 19, 'Hilfskraft': 20}\n2026-05-05 17:07:52,126 - INFO - Wall time: 0.05614209175109863\n2026-05-05 17:07:52,131 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:07:52,131 - INFO - Trying to solve with {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 19}\n2026-05-05 17:07:52,704 - INFO - Wall time: 0.055531978607177734\n2026-05-05 17:07:52,709 - INFO - Solver returned status = \"INFEASIBLE\"\n2026-05-05 17:07:52,709 - INFO - Hidden Employee Tight Bound: {'Azubi': 20, 'Fachkraft': 20, 'Hilfskraft': 20}\n\n2026-05-05 17:07:52,709 - INFO - General information:\n2026-05-05 17:07:52,709 - INFO - - planning unit: 77\n2026-05-05 17:07:52,709 - INFO - - start date: 2024-12-01\n2026-05-05 17:07:52,709 - INFO - - end date: 2024-12-31\n2026-05-05 17:07:52,709 - INFO - - number of employees: 101\n2026-05-05 17:07:52,709 - INFO - - number of days: 31\n2026-05-05 17:07:52,709 - INFO - - number of shifts: 8\n2026-05-05 17:07:54,641 - INFO - Found 4 complete weekends in the planning period\n2026-05-05 17:07:55,735 - INFO - Solving model...\n2026-05-05 17:07:55,735 - INFO - - number of variables: 28179\n2026-05-05 17:07:55,735 - INFO - - number of objectives: 10\n2026-05-05 17:07:55,735 - INFO - - number of constraints: 9\n2026-05-05 17:07:55,735 - INFO - Constraints:\n2026-05-05 17:07:55,735 - INFO - - Free Day After Night Shift Phase\n2026-05-05 17:07:55,735 - INFO - - Minimum Rest Time\n2026-05-05 17:07:55,735 - INFO - - Min Staffing\n2026-05-05 17:07:55,735 - INFO - - Rounds In Early Shift\n2026-05-05 17:07:55,735 - INFO - - One Shift Per Day\n2026-05-05 17:07:55,735 - INFO - - Target Working Time\n2026-05-05 17:07:55,735 - INFO - - Vacation Days And Shifts\n2026-05-05 17:07:55,735 - INFO - - Hierarchy Of Intermediate Shifts\n2026-05-05 17:07:55,735 - INFO - - Planned Shifts\n2026-05-05 17:07:55,735 - INFO - Objectives:\n2026-05-05 17:07:55,735 - INFO - - Free Days Near Weekend (weight: 2)\n2026-05-05 17:07:55,735 - INFO - - Minimize Consecutive Night Shifts (weight: 2)\n2026-05-05 17:07:55,735 - INFO - - Minimize Hidden Employees (weight: 100)\n2026-05-05 17:07:55,735 - INFO - - Minimize Overtime (weight: 4)\n2026-05-05 17:07:55,735 - INFO - - Not Too Many Consecutive Days (weight: 1)\n2026-05-05 17:07:55,735 - INFO - - Rotate Shifts Forward (weight: 1)\n2026-05-05 17:07:55,735 - INFO - - Maximize Employee Wishes (weight: 3)\n2026-05-05 17:07:55,735 - INFO - - Free Day After Night Shift Phase (weight: 3)\n2026-05-05 17:07:55,735 - INFO - - Every Second Weekend Free (weight: 1)\n2026-05-05 17:07:55,735 - INFO - - Preferred Block Length (weight: 1)\n2026-05-05 17:07:55,837 - INFO - Timeout set to 600 seconds\n2026-05-05 17:07:56,218 - INFO - Solving completed in 0.38 seconds\n", + "id": "e6e1f145-37b6-42ae-a0f6-827dee6eccf1", + "metadata": { + "expectedSolutions": 1, + "feasibleSolutions": [], + "solutionsGenerated": 0 + }, + "params": { + "end": "2024-12-31", + "start": "2024-12-01", + "timeout": 600, + "unit": 77 + }, + "status": "failed", + "type": "solve" + } + ] +} diff --git a/legacy/cases/77/12_2024/wishes_and_blocked.json b/legacy/cases/77/12_2024/wishes_and_blocked.json new file mode 100644 index 00000000..a2f63b3e --- /dev/null +++ b/legacy/cases/77/12_2024/wishes_and_blocked.json @@ -0,0 +1,3 @@ +{ + "employees": [] +} diff --git a/legacy/cases/77/12_2024/worked_sundays.json b/legacy/cases/77/12_2024/worked_sundays.json new file mode 100644 index 00000000..1c33992f --- /dev/null +++ b/legacy/cases/77/12_2024/worked_sundays.json @@ -0,0 +1,130 @@ +{ + "worked_sundays": [ + { + "firstname": "Margaritt", + "key": 927, + "name": "Mittrach", + "worked_sundays": 2 + }, + { + "firstname": "Daniele", + "key": 928, + "name": "Wunderlich", + "worked_sundays": 2 + }, + { + "firstname": "Kersten", + "key": 2932, + "name": "Devers", + "worked_sundays": 2 + }, + { + "firstname": "Renilde", + "key": 2963, + "name": "Hoots", + "worked_sundays": 2 + }, + { + "firstname": "Eleonore", + "key": 3004, + "name": "Lockett", + "worked_sundays": 2 + }, + { + "firstname": "Henni", + "key": 5367, + "name": "Donis", + "worked_sundays": 2 + }, + { + "firstname": "Bertram", + "key": 6180, + "name": "Putney", + "worked_sundays": 2 + }, + { + "firstname": "Roseliese", + "key": 6507, + "name": "Rashid", + "worked_sundays": 2 + }, + { + "firstname": "Hansmarti", + "key": 6538, + "name": "Guillaume", + "worked_sundays": 2 + }, + { + "firstname": "Noa", + "key": 6612, + "name": "Pettis", + "worked_sundays": 2 + }, + { + "firstname": "Edwin", + "key": 6616, + "name": "Avelar", + "worked_sundays": 2 + }, + { + "firstname": "Saskia", + "key": 6681, + "name": "Labelle", + "worked_sundays": 2 + }, + { + "firstname": "Waldfried", + "key": 637, + "name": "Heaney", + "worked_sundays": 2 + }, + { + "firstname": "Silvia", + "key": 914, + "name": "Harkins", + "worked_sundays": 2 + }, + { + "firstname": "Ingbert", + "key": 917, + "name": "Catoe", + "worked_sundays": 2 + }, + { + "firstname": "B\u00e4rbl", + "key": 924, + "name": "Merriweather", + "worked_sundays": 2 + }, + { + "firstname": "Kirstin", + "key": 6864, + "name": "Heise", + "worked_sundays": 2 + }, + { + "firstname": "Sandra", + "key": 459, + "name": "Shoemake", + "worked_sundays": 1 + }, + { + "firstname": "Lina", + "key": 925, + "name": "Farniok", + "worked_sundays": 1 + }, + { + "firstname": "Belinda", + "key": 6714, + "name": "Griffey", + "worked_sundays": 1 + }, + { + "firstname": "Sonnhilde", + "key": 6769, + "name": "Ingraham", + "worked_sundays": 1 + } + ] +} diff --git a/cases/case_catalog.md b/legacy/cases/case_catalog.md similarity index 100% rename from cases/case_catalog.md rename to legacy/cases/case_catalog.md diff --git a/legacy/cases_static_jsons/employee_types.json b/legacy/cases_static_jsons/employee_types.json new file mode 100644 index 00000000..1981e587 --- /dev/null +++ b/legacy/cases_static_jsons/employee_types.json @@ -0,0 +1,525 @@ +{ + "Azubi": [ + "A-Altenpflegehelfer/in (A-82101-002)", + "A-Altenpfleger/in (A-82102-002)", + "A-An\u00e4sthesietechnisch/r Assistent/in (A-81332-001)", + "A-CTA (Chirurgisch-technische/r Assistent/in) (A-81332-005)", + "A-Fachkaufkann/-frau-Verwaltung im Gesundheitswesen (A-73223-015)", + "A-Fachkraft-Lagerlogistik (A-51312-005)", + "A-Gesundheits- und Kinderkrankenpfleger/in (A-81302-003)", + "A-Gesundheits- und Krankenpfleger/in (A-81302-005)", + "A-Hebamme/Entbindungspfleger (A-81353-003)", + "A-Hilfskoch/-k\u00f6chin (A-29302-018)", + "A-Informatiker/in (Weiterbildung) (A-43103-008)", + "A-Kaufmann/-frau-Gesundheitswesen (A-73222-007)", + "A-Kinderkrankenschwester/-pfleger (A-81302-007)", + "A-Koch/-K\u00f6chin (29302-024)", + "A-Krankenschwester/-pfleger (A-81302-008)", + "A-Medizinische/r Fachangestellte/r (A-81102-004)", + "A-Medizinisch-technisch/r Radiologieassistent/in (A-81232-004)", + "A-Notfallsanit\u00e4ter A-31342-005", + "A-OTA (Operationstechnische/r Assistent/in) (A-81332-009)", + "A-Pflegeassistent/in (A-81302-014)", + "A-Pflegefachkraft - Gesundheits- und Krankenpfleger (81302-015)", + "A-Pflegefachkraft (Altenpflege) (A-81302-019)", + "A-Pflegefachkraft (Krankenpflege) (A-81302-018)", + "A-Pflegefachkraft-Kinderkrankenpflege (A-81302-016)", + "A-Pflegefachmann/-frau (A-81302-028)", + "A-Stationshelfer/in - Krankenpflege (A-81301-017)" + ], + "Fachkraft": [ + "Abteilungsleiter/in (71394-003)", + "Alleinsekret\u00e4r/in (71402-004)", + "Allgemeinarzt/-\u00e4rztin (81404-001)", + "Allgemeinchirurg/in (81434-001)", + "Altenpfleger/in (82102-002)", + "Ambulante Krankenschwester/-pfleger (81302-001)", + "Ambulanzpfleger/schwester (81382-001)", + "An\u00e4sthesietechnische/r Assistent/in (81332-001)", + "An\u00e4sthesist/in (81454-002)", + "Archivar/in (73314-001)", + "Archivfachkraft (73312-004)", + "Arzt/\u00c4rztin - Allgemeinmedizin (81404-003)", + "Arzt/\u00c4rztin - An\u00e4sthesiologie und Intensivtherapie (81454-003)", + "Arzt/\u00c4rztin - Chirurgie (81434-002)", + "Arzt/\u00c4rztin - Hygiene (81484-006)", + "Arzt/\u00c4rztin - Innere Medizin (81424-001)", + "Arzt/\u00c4rztin - Kinder- und Jugendpsychiatrie (81464-001)", + "Arzt/\u00c4rztin - klinische Strahlenphysik (81234-001)", + "Arzt/\u00c4rztin - Neurochirurgie (81434-004)", + "Arzt/\u00c4rztin - Neurologie (81464-002)", + "Arzt/\u00c4rztin - Orthop\u00e4die (81434-005)", + "Arzt/\u00c4rztin - Radiologie (81234-003)", + "Arzt/\u00c4rztin - Traumatologie und Orthop\u00e4die (81434-006)", + "Arzt/\u00c4rztin (81404-002)", + "\u00c4rztliche/r Direktor/in (Humanarzt/-\u00e4rztin) (81494-001)", + "\u00c4rztliche/r Leiter/in (81494-002)", + "Arztsekret\u00e4r/in (73222-001)", + "Assistent/in - Gesch\u00e4ftsleitung (71403-001)", + "Assistent/in - Gesundheitswesen (73222-003)", + "Assistent/in - Operationstechnik (81332-003)", + "Assistent/in - Rechnungswesen (72213-006)", + "Assistenzarzt/-\u00e4rztin - Kinder-/Jugendpsychiatrie und -psychologie (81464-006)", + "Assistenzarzt/-\u00e4rztin (81404-004)", + "Assistenzarzt/-\u00e4rztin (Uni) - An\u00e4sthesiologie (81454-004)", + "Assistenzarzt/-\u00e4rztin (Uni) - Chirurgie (81434-007)", + "Assistenzarzt/-\u00e4rztin (Uni) - Innere Medizin (81424-004)", + "Assistenzarzt/-\u00e4rztin (Uni) - Kinder-/Jugendmedizin (81414-002)", + "Assistenzarzt/-\u00e4rztin (Uni) - Neurologie (81464-009)", + "ATA (An\u00e4sthesietechnische/r Assistent/in) (81332-004)", + "Augenoptikermeister/in (82593-001)", + "Ausbilder Berufsbildungswerk (84223-013)", + "Ausbilder Berufsf\u00f6rderungswerk", + "Bachelor of Arts - Soziale Arbeit/Soziale Dienste (83123-003)", + "Bachelor of Arts - Sportwissenschaft (84503-002)", + "Bankkaufmann/-frau (72112-004)", + "Beikoch/-k\u00f6chin (29302-005)", + "Bereichsleiter/in (71394-006)", + "Berufsp\u00e4dagoge/-p\u00e4dagogin (84224-003)", + "Besch\u00e4ftigungstherapeut/in (81723-004)", + "Betriebselektriker/in (26252-007)", + "Betriebsg\u00e4rtner/in (12102-001)", + "Betriebstechniker/in (25103-011)", + "Betriebswirt/in (Weiterbildung) - Rechnungswesen (72213-014)", + "Bibliothekar/in (73324-004)", + "Bilanzbuchhalter/in (72213-016)", + "Buchhalter/in (72213-019)", + "B\u00fcrofachkraft (71402-010)", + "B\u00fcrokaufmann/-frau (71402-012)", + "Chefarzt/-\u00e4rztin (81494-003)", + "Chefsekret\u00e4r/in (71403-008)", + "Chirurg/in (81434-008)", + "Coach Erw.Bildung (84404-010)", + "Controller/in (72234-005)", + "Controllingleiter/in (72294-006)", + "CTA (Chirurgisch-technische/r Assistent/in) (81332-005)", + "Datenverarbeitungsfachmann/-frau (43103-005)", + "Dauernachtwache (Krankenschwester/-pfleger) (81302-002)", + "Diabetesberater/in (81783-002)", + "Di\u00e4tassistent/in (81762-001)", + "EDV Sachbearbeiter (41402-017)", + "EDV-Systemtechniker/in (26312-040)", + "EDV-Techniker/in (43103-007)", + "EEG-Assistent/in (81222-009)", + "Einkaufsleiter/in (61194-006)", + "Einkaufslogistiker/in (61113-015)", + "Einzelhandelskaufmann/-frau (62102-002)", + "Elektroanlagenelektroniker/in (26252-050)", + "Elektroinstallateur/in (26212-017)", + "Elektroniker/in - Energie- und Geb\u00e4udetechnik (26212-019)", + "Elektroniker/in - Ger\u00e4te und Systeme (26302-033)", + "Endoskopieschwester/-pfleger (81313-003)", + "Ergotherapeut/in (81723-006)", + "Ern\u00e4hrungsberater/in (82233-005)", + "Ern\u00e4hrungswissenschaftler/in (82284-003)", + "Erzieher/in - Heilp\u00e4dagogik (83133-002)", + "Erzieher/in (83112-006)", + "Erziehungswissenschaftler/in (91334-007)", + "EXAH-Pflegefachkraft (Altenpflege) (A-81302-019)", + "EXGK-Pflegefachkraft - Gesundheits- und Krankenpflege (A-81302-015)", + "EXKI-Pflegefachkraft.Kinderkrankenpflege (A-81302-016)", + "EXKP-Pflegefachkraft- (Krankenpflege) (A-81302-018)", + "EX-Pflegefachmann/- frau (A-81302-028)", + "Fachangestellte/r f\u00fcr B\u00fcrokommunikation (71402-021)", + "Facharzt Neurochirurgie", + "Facharzt Psychiatrie u. Psychotherapie", + "Facharzt/-\u00e4rztin - Allgemeinchirurgie (81434-027)", + "Facharzt/-\u00e4rztin - Allgemeine Chirurgie (81434-009)", + "Facharzt/-\u00e4rztin - Allgemeinmedizin (81404-005)", + "Facharzt/-\u00e4rztin - Allgemeinmedizin (Hausarzt/-\u00e4rztin) (81404-010)", + "Facharzt/-\u00e4rztin - An\u00e4sthesiologie (81454-005)", + "Facharzt/-\u00e4rztin - Frauenheilkunde und Geburtshilfe (81444-017)", + "Facharzt/-\u00e4rztin - Gef\u00e4\u00dfchirurgie (81434-010)", + "Facharzt/-\u00e4rztin - Hals-Nasen-Ohrenheilkunde (81444-018)", + "Facharzt/-\u00e4rztin - Innere Medizin (81424-006)", + "Facharzt/-\u00e4rztin - Innere Medizin u. Allgemeinm. (Hausarzt) (81404-006)", + "Facharzt/-\u00e4rztin - Innere Medizin u. H\u00e4matolog. u. Onkologie (81424-010)", + "Facharzt/-\u00e4rztin - Innere Medizin und Gastroenterologie (81424-009)", + "Facharzt/-\u00e4rztin - Innere Medizin und Kardiologie (81424-011)", + "Facharzt/-\u00e4rztin - Innere Medizin und Nephrologie (81424-012)", + "Facharzt/-\u00e4rztin - Innere Medizin und Pneumologie (81424-013)", + "Facharzt/-\u00e4rztin - Kinder- u. Jugendpsychiat. u. -psychoth. (81464-010)", + "Facharzt/-\u00e4rztin - Kinder- und Jugendmedizin (81414-003)", + "Facharzt/-\u00e4rztin - Neurologie (81464-012)", + "Facharzt/-\u00e4rztin - \u00d6ffentliches Gesundheitswesen (81484-028)", + "Facharzt/-\u00e4rztin - Orthop\u00e4die und Unfallchirurgie (81434-013)", + "Facharzt/-\u00e4rztin - P\u00e4diatrie (81414-005)", + "Facharzt/-\u00e4rztin - Plastische und \u00c4sthetische Chirurgie (81434-014)", + "Facharzt/-\u00e4rztin - Radiologie (81234-005)", + "Facharzt/-\u00e4rztin - Thoraxchirurgie (81434-015)", + "Facharzt/-\u00e4rztin - Unfallchirurgie (81434-016)", + "Facharzt/-\u00e4rztin - Viszeralchirurgie (81434-017)", + "Fachinformatiker/in - Systemintegration (43102-014)", + "Fachinformatiker/in (43102-013)", + "Fachkaufmann/-frau - Logistik (51623-005)", + "Fachkaufmann/-frau - Verwaltung im Gesundheitswesen (73223-015)", + "Fachkinderkrankenpfleger/in - Intensivpflege/An\u00e4sthesie (81323-005)", + "Fachkinderkrankenschwester/-pfleger - ambulante Pflege (81323-002)", + "Fachkinderkrankenschwester/-pfleger - P\u00e4diatrie und Intensivmedizin (81323-011)", + "Fachkinderkrankenschwester/-pfleger - Psychiatrie (81323-013)", + "Fachkraft - Altenpflege (82102-004)", + "Fachkraft - Factoring (72213-033)", + "Fachkraft - Lagerlogistik (51312-005)", + "Fachkraft - Lagerwirtschaft (51312-007)", + "Fachkraft - Sozialarbeit (83123-004)", + "Fachkrankenpfleger/in - Nephrologie", + "Fachkrankenpfleger/in - Notfallpflege (81313-059)", + "Fachkrankenpfleger/in - Operations-/Endoskopiedienst (81313-014)", + "Fachkrankenschwester/-pfleger - Endoskopie (81313-006)", + "Fachkrankenschwester/-pfleger - Intensivmedizin und An\u00e4sthesie (81313-008)", + "Fachkrankenschwester/-pfleger - Intensivpflege/An\u00e4sthesie (81313-009)", + "Fachkrankenschwester/-pfleger - Operationsdienst (81313-015)", + "Fachlehrer/in - arbeitstechnische F\u00e4cher (84214-008)", + "Fachlehrer/in - Pflegeberufe (84213-094)", + "Fachlehrer/in (84214-006)", + "Fachschwester/-pfleger - Intensivpflege und An\u00e4sthesie (81313-025)", + "Fachwirt Immobilien (61313-009)", + "Finanzbuchhalter/in (72213-038)", + "G\u00e4rtner/in (12102-007)", + "Gas- und Wasserinstallateur/in (34212-005)", + "Gastroenterologe/Gastroenterologin (81424-015)", + "Gehaltsabrechner/in (72213-042)", + "Gesch\u00e4ftsf\u00fchrende/r Direktor/in (\u00f6ffentliche Verwaltung) (73294-015)", + "Gesch\u00e4ftsf\u00fchrer/in (71104-008)", + "Gesundheits- und Kinderkrankenpfleger/in (81302-003)", + "Gesundheits- und Krankenpflegeassistent/in (81302-004)", + "Gesundheits- und Krankenpfleger/in (81302-005)", + "Gesundheitscoach (82212-001)", + "Gesundheits\u00f6konom/in (82214-004)", + "Gesundheitspfleger/in (81302-006)", + "Gymnastiklehrer/in (84553-016)", + "Haushaltsfachkraft (83212-005)", + "Hausmeister/in (34102-007)", + "Haustechniker/in (34102-008)", + "Hauswart/in (34102-009)", + "Hauswirtschafter/in (83212-007)", + "Hauswirtschaftsverwalter/in (83293-005)", + "Hebamme/Entbindungspfleger (81353-003)", + "Heilerziehungspfleger/in (83132-006)", + "Heilp\u00e4dagoge/-p\u00e4dagogin (83134-002)", + "Heilpraktiker/in (81752-002)", + "Heizungs- und Sanit\u00e4rinstallateur/in (834212-011)", + "Heizungsmonteur/in (34212-015)", + "Honorar-Arzt", + "Honorardozent/in (84304-032)", + "Hospizleiter/in (81394-002)", + "Hygienefachkraft (53322-010)", + "Industriekaufmann/-frau (71302-011)", + "Informatiker (Hochschule) (43104-002)", + "Informatiker/in (Weiterbildung) (43103-008)", + "Integrationsberater", + "Integrationsmanager/in (83134-005)", + "Internatsbetreuer", + "Internist/in (81424-017)", + "IT-Administrator/in (43343-031)", + "IT-Leiter/in (43394-007)", + "Jugenderzieher/in (83112-025)", + "Jugendpsychologe/-psychologin (81624-007)", + "Jurist/in (73104-001)", + "Kardiologe/Kardiologin (81424-018)", + "Kaufmann/-frau - Gesundheitswesen (73222-007)", + "Kaufm\u00e4nnische/r Angestellte/r (71302-015)", + "Kaufm\u00e4nnische/r Direktor/in (71104-015)", + "Kaufm\u00e4nnische/r Sachbearbeiter/in (71302-018)", + "Kinder- und Jugendlichenpsychotherapeut/in (81634-004)", + "Kinder- und Jugendpsychologe/-psychologin (81624-008)", + "Kinder- und Jugendpsychotherapeut/in (81634-005)", + "Kinderarzt/-\u00e4rztin (81414-006)", + "Kinderg\u00e4rtner/in (83112-032)", + "Kinderkrankenschwester/-pfleger - Psychiatrie (81323-035)", + "Kinderkrankenschwester/-pfleger (81302-007)", + "Kinderp\u00e4dagoge/-p\u00e4dagogin (83112-033)", + "Kinderpsychologe/-psychologin (81624-009)", + "Kindheitsp\u00e4dagoge/-p\u00e4dagogin (91334-005)", + "Klinische Kodierfachkraft (71442-004)", + "Klinische/r Neuropsychologe/-psychologin (81624-010)", + "Koch/K\u00f6chin (29302-024)", + "Kodierer/in (71442-005)", + "Kostenabrechner/in (72223-011)", + "Krankengymnast/in (81713-009)", + "Krankenhaussekret\u00e4r/in (73222-009)", + "Krankenschwester/-pfleger - An\u00e4sthesie (81313-037)", + "Krankenschwester/-pfleger - Nachtwache (81302-009)", + "Krankenschwester/-pfleger - Nephrologie (81313-043)", + "Krankenschwester/-pfleger (81302-008)", + "Krankentransportleiter/in (52182-021)", + "K\u00fcchenchef (29394-004)", + "K\u00fcchenleiter/in (29394-007)", + "K\u00fcster/in (83382-005)", + "Lagerverwalter/in (Warenlager) (51312-030)", + "Lehrer/in - Gesundheitsfachberufe (84213-031)", + "Lehrer/in - Krankenpflege (84213-038)", + "Lehrer/in - Pflegeberufe (84213-044)", + "Leitende Pflegefachkraft (81393-002)", + "Leitende/r Arzt/\u00c4rztin (81494-004)", + "Leitende/r Entbindungspfleger/Hebamme/Bereichsleitung Frauenklinik (81393-003)", + "Leitende/r Gesundheits- und Krankenschwester/-pfleger (81393-004)", + "Leiter/in - ambulante Sozialdienste (83194-027)", + "Leiter/in - Einkauf (61194-013)", + "Leiter/in - Finanz- und Rechnungswesen (72294-008)", + "Leiter/in - Krankenhausbetriebstechnik (34193-009)", + "Leiter/in - Medizincontrolling (82214-014)", + "Leiter/in - Presse- und \u00d6ffentlichkeitsarbeit (92294-001)", + "Leiter/in - soziale Einrichtung (83194-015)", + "Leiter/in - Technik (27394-016)", + "Logop\u00e4de/Logop\u00e4din (81733-006)", + "Logop\u00e4de/Logop\u00e4din (Hochschule) (81734-003)", + "Lohnsachbearbeiter/in (72213-063)", + "Maler/in (33212-019)", + "Marketingsekret\u00e4r/in (71402-042)", + "Masseur/in (81712-004)", + "Masseur/in und medizinische/r Bademeister/in (81712-005)", + "Master of Science - Gesundheits\u00f6konomie (82214-009)", + "Master of Science - Projektmanagement (71304-017)", + "Mediengestalter/in - Digital-/Printmedien - Medienberatung (61122-015)", + "Medientechniker/in (23213-015)", + "Medizinallaborant/in (Humanmedizin) (81212-018)", + "Medizincontroller/in (72234-025)", + "Mediziner/in (81404-009)", + "Medizinische/r Assistent/in (81212-022)", + "Medizinische/r Fachangestellte/r (81102-004)", + "Medizinische/r Praxisassistent/in (81102-006)", + "Medizinisch-kaufm\u00e4nnische/r Assistent/in (73222-010)", + "Medizinisch-technische/r Assistent/in (81212-019)", + "Medizinisch-technische/r Fachassistent/in - radiologische Diagnostik (81233-011)", + "Medizinisch-technische/r Fachassistent/in (81213-023)", + "Medizinisch-technische/r Laboratoriumsassistent/in (81212-021)", + "Medizinisch-technische/r Radiologieassistent/in MTRA (81232-004)", + "MTA (Medizinisch-technische/r Laboratoriumsassistent/in 81212-031)", + "Musiker/in (94114-044)", + "Netzadministrator/in (EDV) (43343-016)", + "Netzwerkadministrator (43343-018)", + "Neurologe/Neurologin (81464-020)", + "Notfallsanit\u00e4ter (81342-005)", + "Oberarzt/-\u00e4rztin (81494-007)", + "\u00d6ffentlichkeitsreferent/in (92203-014)", + "\u00d6kotrophologe/\u00d6kotrophologin (82284-009)", + "Operationstechnische/r Assistent/in (81332-008)", + "Organist/in (94114-059)", + "Orthop\u00e4de/Orthop\u00e4din (81434-021)", + "OTA (Operationstechnische/r Assistent/in) (81332-009)", + "P\u00e4dagogische/r Betreuer/in (83124-033)", + "P\u00e4dagogische/r Mitarbeiter/in (91334-024)", + "P\u00e4diatrie-Assistent/in (81182-008)", + "Personalbuchhalter/in (72213-071)", + "Personalchef/in (71594-011)", + "Personalfachkaufmann/-kauffrau (71513-010)", + "Personalkaufmann/-frau (71512-004)", + "Personalleiter/in (71594-013)", + "Personalsachbearbeiter/in (71512-005)", + "Pflegeassistent/in (81302-014)", + "Pflegebereichsleiter/in (81394-013)", + "Pflegedienstleiter/in (81394-014)", + "Pflegedirektor/in (81394-032)", + "Pflegefachkraft - An\u00e4sthesie/Intensivmedizin (81313-052)", + "Pflegefachkraft - Gesundheits- und Krankenpfleger (81302-015)", + "Pflegefachkraft - Kinderkrankenpflege (81302-016)", + "Pflegefachkraft - Kinderpflege (81302-017)", + "Pflegefachkraft - Sozialstation (81302-019)", + "Pflegefachkraft (Krankenpflege) (81302-018)", + "Pflegefachmann/-frau (81302-028)", + "Pflegeleiter/in (81394-017)", + "Pflegemanager/in (81394-018)", + "Pflegemanager/in (81394-019)", + "Pflegep\u00e4dagoge/-p\u00e4dagogin (84214-065)", + "Pf\u00f6rtner/in (53112-066)", + "Physician Assistant (81333-001)", + "Physiker/in (41404-002)", + "Physiotherapeut/in (81713-015)", + "Physiotherapeut/in (Hochschule) (81714-002)", + "Praxisanleiter/in - Pflegeberufe (84213-085)", + "Praxisanleiter/-innen (84223-081)", + "Projektmanager/in (71393-007)", + "Prokurist/in (71104-017)", + "Psychologe/Psychologin - allgemeine Psychologie (81624-016)", + "Psychologe/Psychologin (81624-015)", + "Psychologische/r Psychotherapeut/in (81634-014)", + "Qualit\u00e4tsbeauftragte/r - Gesundheits-/Sozialwesen (82243-003)", + "Qualit\u00e4tsbeauftragte/r (27313-022)", + "Qualit\u00e4tsbeauftragter/-beauftragte - Management (27313-023)", + "Radioonkologische/r Assistent/in (81232-010)", + "Rechnungswesensachbearbeiter/in (72212-026)", + "Referent/in - berufliche Fort- und Weiterbildung", + "Referent/in - \u00d6ffentlichkeitsarbeit/Marketing (92203-023)", + "Reha Berater (71524-028)", + "Rehabilitationsp\u00e4dagoge/-p\u00e4dagogin (83134-009)", + "Reinigungsfachkraft /54112-018)", + "Rettungsassistent/in", + "Rettungssanit\u00e4ter/in", + "Rezeptionsmitarbeiter/in (Arztpraxis) (73222-012)", + "Rohrinstallateur/in (34212-040)", + "R\u00f6ntgenassistent/in (81232-011)", + "Sachbearbeiter/in - B\u00fcro (71402-058)", + "Sachbearbeiter/in - Verwaltung (geh. nichttechn. Dienst) (73203-017)", + "Sachbearbeiter/in (71302-021)", + "Schreibkraft (71432-031)", + "Schularzt/-\u00e4rztin (81414-008)", + "Schulleiter/in - Berufsschulen (8494-033)", + "Schulsozialarbeiter/in (83124-035)", + "Schwester/Pfleger (Kinderkrankenpflege) (81302-025)", + "Schwester/Pfleger (Krankenpflege) (81302-026)", + "Seelsorger/in (83314-034)", + "Sekret\u00e4r/in - Gesundheitswesen (73222-013)", + "Sekret\u00e4r/in (71402-062)", + "Sekretariatsleiter/in (71493-011)", + "Seniorenbetreuer/in (82102-005)", + "Seniorenpfleger/in (82102-006)", + "Seniorenzentrumsleiter/in (82194-013)", + "Servicekraft (63302-045)", + "Sicherheitsfachkraft (53123-009)", + "Sozialarbeiter/in (83124-037)", + "Sozialarbeiter/in / Sozialp\u00e4dagoge/-p\u00e4dagogin (83124-052)", + "Sozialp\u00e4dagoge/-p\u00e4dagogin (83124-039)", + "Sozialwissenschaftler/in (91324-015)", + "Sportlehrer/in - Rehabilitation/Behindertensport (83133-016)", + "Sportlehrer/in (84503-023)", + "Sportphysiotherapeut/in (81713-016)", + "Sporttherapeut (81783-013)", + "Sprechstundenhilfe (81102-009)", + "Stationsassistent/in (Arzthilfe) (81102-010)", + "Stationsleiter/in - Kranken-/Alten-/Kinderkrankenpflege (81393-012)", + "Stationsleiter/in - Krankenpflege (81393-009)", + "Stationsleiter/in - Krankenpflege/Altenpflege (81393-010)", + "Stationsleiter/in - Pflegedienst (81393-011)", + "Stenograf/in (71432-036)", + "Apothekenhelfer/in (62412-001)", + "Sterilisationsassistent/in (81182-002)", + "Suchtpsychologe/-psychologin (81624-022)", + "Techn. Assistent/in - Bautechnik (31102-008)", + "Techn.Produktdesigner (27212-075)", + "Techniker/in - Anwendungs-/Betriebstechnik (Farben, Lacke) (22203-016)", + "Techniker/in - Elektrotechnik (26303-015)", + "Technische/r B\u00fcrosachbearbeiter/in (71402-068)", + "Technische/r Koordinator/in (27304-054)", + "Technische/r Leiter/in (27394-024)", + "General (01104-008)", + "Technische/r Sterilisationsassistent/in (81182-003)", + "Telefonist/in (71401-042)", + "Hauselektriker/in (26212-033)", + "Terminsachbearbeiter/in (27302-010)", + "Therapeut/in - Krankengymnastik (81713-018)", + "Thoraxchirurg/in (81434-024)", + "\u00dcbungsleiter/in (84503-031)", + "Uhrenmacher (24532-032)", + "Uhrmachermeister (24593-041)", + "Elektrochemiker/in (41384-009)", + "Unterrichtspfleger/-schwester (84213-078)", + "Farb- und Lacktechniker/in (22203-003)", + "Verwalter/in - Tierzucht (11294-009)", + "Verwaltungsangestellte/r - Krankenk.,Krankenh\u00e4user, Kliniken (73222-016)", + "Gesundheits- und Krankenschwester/-pfleger - Gerontopsych.", + "Verwaltungsangestellter (mittl.Dienst) kirchl. Dienst", + "Verwaltungsfachangestellte/-angestellter - Kirchenverwaltung -evangelische Kirche (73282-010)", + "Visceralchirurg/in (81434-026)", + "Vorarbeiter (27302-012)", + "Vorzimmersekret\u00e4r/in (71402-071)", + "Weiterbildungsassistent/in (Arzt/\u00c4rztin) (81404-011)", + "Werbedesigner/in (23224-072)", + "Betriebsschlosser/in (25102-010)", + "Wirtschaftswissenschaftler/in (91404-011)", + "Wundmanager/in (81383-005)", + "Apparate- und Maschinenschlosser/in (34342-005)", + "Arzthelfer/in (81102-001)", + "Zahnarzthelfer/in (81112-006)" + ], + "Hilfskraft": [ + "Ableger/in 871401-001", + "Abrechnungspr\u00fcfer/in (72214-001=", + "Abschlussagent/in (Versicherung) (72133-001)", + "Alltagsbetreuer/in (83142-001)", + "Altenbetreuerhelfer/in (82101-001)", + "Altenpflegeassistent/in (1 j\u00e4hrige A.) (82101-008)", + "Altenpflegehelfer/in (1 j\u00e4hrige Ausb.) (82101-002)", + "Altenpflegehilfskraft (82101-003)", + "Anmelder/in (71404-010)", + "Anstreicher/in (33212-001)", + "Archivhelfer/in (71401-011)", + "Archivsachbearbeiter/in", + "Aufr\u00e4umer/in (Raum-, Hausratreiniger/in) (54101-002)", + "Ausbaufacharbeiter/in - Malerarbeiten (33212-003)", + "Ausbauhelfer/in (33301-001)", + "Aushilfsfahrer/in (52182-006)", + "Aushilfskraft (K\u00fcche) (29301-002)", + "Auskunftsgehilfe/-gehilfin (71401-013)", + "Auslader/in (Transportarbeiter/in) (51311-015)", + "Azetylenschwei\u00dfer/in (24422-010)", + "Betreuungshelfer/in (83111-002)", + "Betreuungskraft / Alltagsbegleiter/in (83142-003)", + "Betriebshandweker/in (25102-007)", + "Betriebshilfering-Gesch\u00e4ftsf\u00fchrer/in (11124-002)", + "Bote/Botin (B\u00fcro) (51321-009)", + "Bundesfreiwilligendienst (BFD)", + "B\u00fcroassistent/in (71402-009)", + "B\u00fcrogehilf(e/in) (71402-011)", + "B\u00fcrohilfskraft (71401-019)", + "externes Pers., Honorar", + "Fachkraft - Pflegeassistenz (83142-004)", + "Fahrbetriebsregler/in (Stra\u00dfenverkehr) (51512-003)", + "Fernsprechvermittler/in (71401-027)", + "Freiwilliges Soziales Jahr (FSJ)", + "Geb\u00e4udeinnenreiniger/in (54112-012)", + "Gesundheits- und Krankenpflegehelfer/in (81301-001)", + "Glasreiniger/in (54122-003)", + "Gr\u00fcnanlagenpfleger/in (12101-012)", + "Hausarbeitsgehilfe/-gehilfin (83211-002)", + "Haushaltshilfe (83211-005)", + "Hauswirtschaftsgehilfe/-gehilfin (83212-014)", + "Hauswirtschaftshelfer/in/-assistent/in (83212-015)", + "Hebammenhelfer/in (81352-002)", + "Helfer Reinigung (54101-014)", + "Helfer/in - Altenpflege/Pers\u00f6nliche Assistenz (82101-004)", + "Helfer/in - B\u00fcro, Verwaltung (71401-033)", + "Helfer/in - B\u00fcro, Verwaltung (71401-034)", + "Helfer/in - Gartenbau (12101-016)", + "Helfer/in - Gr\u00fcnanlagen (12101-017)", + "Helfer/in - Hauswirtschaft (83211-001)", + "Helfer/in - K\u00fcche (29301-005)", + "Helfer/in - Rettungsdienst (81341-002)", + "Helfer/in - Schachtarbeiten (32201-010)", + "Helfer/in - station\u00e4re Krankenpflege (81301-002)", + "Helfer/in - Warenmalerei, -lackiererei (22201-004)", + "Hilfskoch/-k\u00f6chin (29302-018)", + "Hilfsmonteuer (Elektro) (26301-046)", + "Hilfsschwester/-pfleger (81301-003)", + "Jahrespraktikant/-in (JP)", + "Kaufm\u00e4nnische B\u00fcrokraft (71402-034)", + "Kochhelfer/in (29301-007)", + "Kranken- und Altenpflegehelfer/in (81301-005)", + "Krankenfahrer/in (52182-019)", + "Krankenpflegehelfer/in (1 j\u00e4hrige A.) (81301-006)", + "Krankentransporteur/in (52182-020)", + "K\u00fcchengehilfe/-gehilfin (29301-009)", + "K\u00fcchenhelfer/in (29301-010)", + "K\u00fcchenhilfe (29301-011)", + "Lagerarbeiter/in (51311-068)", + "Lagerhelfer/in (51311-070)", + "Lagerhilfsarbeiter/in (51311-071)", + "Maschinenhelfer/in (25101-014)", + "Medizinische/r Fachhelfer/in (81102-005)", + "Menu/Datenerfasser/-in (71401-022)", + "Pflegediensthelfer/in (81301-008)", + "Pflegefachhelfer/in (Krankenpflege)", + "Pflegehelfer Behindertenpflege", + "Pflegehelfer/in - Altenpflege (ohne 1 j\u00e4hrige A.) (82101-006)", + "Pflegehelfer/in - station\u00e4re Pflege (ohne 1 j\u00e4hrige A.) (81301-010)", + "Pflegehelfer/in (Krankenpflege) (ohne 1 j\u00e4hrige A.) (81301-011)", + "Pflegehilfskraft (Krankenpflege) (81301-012)", + "Praktikant/-in (P)", + "Praktisches Jahr (PJ)", + "Raumpfleger/in (54101-023)", + "Rettungshelfer/in (81341-005)", + "R\u00f6ntgenhelfer/in (81232-012)", + "Saisonhelfer/in (11101-035)", + "Schwesterhelfer/in (81301-014)", + "Schwestern-/Pflegediensthelfer/in (81301-015)", + "Servicehilfskraft (63301-044)", + "Sozialassistent/in (83142-006)", + "Sportassistent/in (63122-005=", + "Sp\u00fclmann/-frau (Hausratreiniger/in) (54101-031)", + "Stationshelfer/in - Krankenpflege (81301-017)", + "Stationshilfe (81301-018)", + "Transporthelfer/in (51311-109)", + "Verwaltungsgehilfe/-gehilfin (73201-009)", + "W\u00e4scheschneider/in (28222-155)" + ] +} diff --git a/legacy/cases_static_jsons/general_settings.json b/legacy/cases_static_jsons/general_settings.json new file mode 100644 index 00000000..122e86a1 --- /dev/null +++ b/legacy/cases_static_jsons/general_settings.json @@ -0,0 +1,18 @@ +{ + "SHIFT_NAME_TO_INDEX": { + "Early": 0, + "Late": 1, + "Night": 2 + }, + "qualifications": { + "2963": [ + "rounds" + ], + "3868": [ + "rounds" + ], + "791": [ + "rounds" + ] + } +} diff --git a/legacy/cases_static_jsons/minimal_number_of_staff.json b/legacy/cases_static_jsons/minimal_number_of_staff.json new file mode 100644 index 00000000..ce9daf74 --- /dev/null +++ b/legacy/cases_static_jsons/minimal_number_of_staff.json @@ -0,0 +1,113 @@ +{ + "Azubi": { + "Di": { + "F": 1, + "N": 0, + "S": 1 + }, + "Do": { + "F": 1, + "N": 0, + "S": 1 + }, + "Fr": { + "F": 1, + "N": 0, + "S": 1 + }, + "Mi": { + "F": 1, + "N": 0, + "S": 1 + }, + "Mo": { + "F": 1, + "N": 0, + "S": 1 + }, + "Sa": { + "F": 1, + "N": 0, + "S": 1 + }, + "So": { + "F": 1, + "N": 0, + "S": 1 + } + }, + "Fachkraft": { + "Di": { + "F": 3, + "N": 2, + "S": 2 + }, + "Do": { + "F": 3, + "N": 2, + "S": 2 + }, + "Fr": { + "F": 3, + "N": 2, + "S": 2 + }, + "Mi": { + "F": 4, + "N": 2, + "S": 2 + }, + "Mo": { + "F": 3, + "N": 2, + "S": 2 + }, + "Sa": { + "F": 2, + "N": 1, + "S": 2 + }, + "So": { + "F": 2, + "N": 1, + "S": 2 + } + }, + "Hilfskraft": { + "Di": { + "F": 2, + "N": 0, + "S": 2 + }, + "Do": { + "F": 2, + "N": 0, + "S": 2 + }, + "Fr": { + "F": 2, + "N": 0, + "S": 2 + }, + "Mi": { + "F": 2, + "N": 0, + "S": 2 + }, + "Mo": { + "F": 2, + "N": 0, + "S": 2 + }, + "Sa": { + "F": 2, + "N": 1, + "S": 2 + }, + "So": { + "F": 2, + "N": 1, + "S": 2 + } + } +} diff --git a/legacy/cases_static_jsons/wishes_and_blocked.json b/legacy/cases_static_jsons/wishes_and_blocked.json new file mode 100644 index 00000000..a2f63b3e --- /dev/null +++ b/legacy/cases_static_jsons/wishes_and_blocked.json @@ -0,0 +1,3 @@ +{ + "employees": [] +} diff --git a/found_solutions/found_solutions_are_stored_here.md b/legacy/found_solutions/found_solutions_are_stored_here.md similarity index 100% rename from found_solutions/found_solutions_are_stored_here.md rename to legacy/found_solutions/found_solutions_are_stored_here.md diff --git a/legacy/found_solutions/soluting_of_test_all_costraints_single_case.json b/legacy/found_solutions/soluting_of_test_all_costraints_single_case.json new file mode 100644 index 00000000..4e066436 --- /dev/null +++ b/legacy/found_solutions/soluting_of_test_all_costraints_single_case.json @@ -0,0 +1,12695 @@ +{ + "objective": 0.0, + "variables": { + "(0, '2024-11-01', 0)": 0, + "(0, '2024-11-01', 1)": 0, + "(0, '2024-11-01', 2)": 0, + "(0, '2024-11-01', 3)": 0, + "(0, '2024-11-01', 4)": 0, + "(0, '2024-11-01', 5)": 0, + "(0, '2024-11-01', 6)": 0, + "(0, '2024-11-01', 7)": 0, + "(0, '2024-11-02', 0)": 0, + "(0, '2024-11-02', 1)": 0, + "(0, '2024-11-02', 2)": 0, + "(0, '2024-11-02', 3)": 0, + "(0, '2024-11-02', 4)": 0, + "(0, '2024-11-02', 5)": 0, + "(0, '2024-11-02', 6)": 0, + "(0, '2024-11-02', 7)": 0, + "(0, '2024-11-03', 0)": 0, + "(0, '2024-11-03', 1)": 0, + "(0, '2024-11-03', 2)": 0, + "(0, '2024-11-03', 3)": 0, + "(0, '2024-11-03', 4)": 0, + "(0, '2024-11-03', 5)": 0, + "(0, '2024-11-03', 6)": 0, + "(0, '2024-11-03', 7)": 0, + "(0, '2024-11-04', 0)": 0, + "(0, '2024-11-04', 1)": 0, + "(0, '2024-11-04', 2)": 0, + "(0, '2024-11-04', 3)": 0, + "(0, '2024-11-04', 4)": 0, + "(0, '2024-11-04', 5)": 0, + "(0, '2024-11-04', 6)": 0, + "(0, '2024-11-04', 7)": 0, + "(0, '2024-11-05', 0)": 0, + "(0, '2024-11-05', 1)": 0, + "(0, '2024-11-05', 2)": 0, + "(0, '2024-11-05', 3)": 0, + "(0, '2024-11-05', 4)": 0, + "(0, '2024-11-05', 5)": 0, + "(0, '2024-11-05', 6)": 0, + "(0, '2024-11-05', 7)": 0, + "(0, '2024-11-06', 0)": 0, + "(0, '2024-11-06', 1)": 0, + "(0, '2024-11-06', 2)": 1, + "(0, '2024-11-06', 3)": 0, + "(0, '2024-11-06', 4)": 0, + "(0, '2024-11-06', 5)": 0, + "(0, '2024-11-06', 6)": 0, + "(0, '2024-11-06', 7)": 0, + "(0, '2024-11-07', 0)": 0, + "(0, '2024-11-07', 1)": 0, + "(0, '2024-11-07', 2)": 0, + "(0, '2024-11-07', 3)": 0, + "(0, '2024-11-07', 4)": 0, + "(0, '2024-11-07', 5)": 0, + "(0, '2024-11-07', 6)": 0, + "(0, '2024-11-07', 7)": 0, + "(0, '2024-11-08', 0)": 0, + "(0, '2024-11-08', 1)": 0, + "(0, '2024-11-08', 2)": 0, + "(0, '2024-11-08', 3)": 0, + "(0, '2024-11-08', 4)": 0, + "(0, '2024-11-08', 5)": 0, + "(0, '2024-11-08', 6)": 0, + "(0, '2024-11-08', 7)": 0, + "(0, '2024-11-09', 0)": 0, + "(0, '2024-11-09', 1)": 0, + "(0, '2024-11-09', 2)": 0, + "(0, '2024-11-09', 3)": 0, + "(0, '2024-11-09', 4)": 0, + "(0, '2024-11-09', 5)": 0, + "(0, '2024-11-09', 6)": 0, + "(0, '2024-11-09', 7)": 0, + "(0, '2024-11-10', 0)": 0, + "(0, '2024-11-10', 1)": 0, + "(0, '2024-11-10', 2)": 0, + "(0, '2024-11-10', 3)": 0, + "(0, '2024-11-10', 4)": 0, + "(0, '2024-11-10', 5)": 0, + "(0, '2024-11-10', 6)": 0, + "(0, '2024-11-10', 7)": 0, + "(0, '2024-11-11', 0)": 0, + "(0, '2024-11-11', 1)": 0, + "(0, '2024-11-11', 2)": 0, + "(0, '2024-11-11', 3)": 0, + "(0, '2024-11-11', 4)": 0, + "(0, '2024-11-11', 5)": 0, + "(0, '2024-11-11', 6)": 0, + "(0, '2024-11-11', 7)": 0, + "(0, '2024-11-12', 0)": 0, + "(0, '2024-11-12', 1)": 0, + "(0, '2024-11-12', 2)": 0, + "(0, '2024-11-12', 3)": 0, + "(0, '2024-11-12', 4)": 0, + "(0, '2024-11-12', 5)": 0, + "(0, '2024-11-12', 6)": 0, + "(0, '2024-11-12', 7)": 0, + "(0, '2024-11-13', 0)": 1, + "(0, '2024-11-13', 1)": 0, + "(0, '2024-11-13', 2)": 0, + "(0, '2024-11-13', 3)": 0, + "(0, '2024-11-13', 4)": 0, + "(0, '2024-11-13', 5)": 0, + "(0, '2024-11-13', 6)": 0, + "(0, '2024-11-13', 7)": 0, + "(0, '2024-11-14', 0)": 0, + "(0, '2024-11-14', 1)": 0, + "(0, '2024-11-14', 2)": 0, + "(0, '2024-11-14', 3)": 0, + "(0, '2024-11-14', 4)": 0, + "(0, '2024-11-14', 5)": 0, + "(0, '2024-11-14', 6)": 0, + "(0, '2024-11-14', 7)": 0, + "(0, '2024-11-15', 0)": 0, + "(0, '2024-11-15', 1)": 0, + "(0, '2024-11-15', 2)": 0, + "(0, '2024-11-15', 3)": 0, + "(0, '2024-11-15', 4)": 0, + "(0, '2024-11-15', 5)": 0, + "(0, '2024-11-15', 6)": 0, + "(0, '2024-11-15', 7)": 0, + "(0, '2024-11-16', 0)": 0, + "(0, '2024-11-16', 1)": 0, + "(0, '2024-11-16', 2)": 0, + "(0, '2024-11-16', 3)": 0, + "(0, '2024-11-16', 4)": 0, + "(0, '2024-11-16', 5)": 0, + "(0, '2024-11-16', 6)": 0, + "(0, '2024-11-16', 7)": 0, + "(0, '2024-11-17', 0)": 0, + "(0, '2024-11-17', 1)": 0, + "(0, '2024-11-17', 2)": 0, + "(0, '2024-11-17', 3)": 0, + "(0, '2024-11-17', 4)": 0, + "(0, '2024-11-17', 5)": 0, + "(0, '2024-11-17', 6)": 0, + "(0, '2024-11-17', 7)": 0, + "(0, '2024-11-18', 0)": 0, + "(0, '2024-11-18', 1)": 0, + "(0, '2024-11-18', 2)": 0, + "(0, '2024-11-18', 3)": 0, + "(0, '2024-11-18', 4)": 0, + "(0, '2024-11-18', 5)": 0, + "(0, '2024-11-18', 6)": 0, + "(0, '2024-11-18', 7)": 0, + "(0, '2024-11-19', 0)": 0, + "(0, '2024-11-19', 1)": 0, + "(0, '2024-11-19', 2)": 0, + "(0, '2024-11-19', 3)": 0, + "(0, '2024-11-19', 4)": 0, + "(0, '2024-11-19', 5)": 0, + "(0, '2024-11-19', 6)": 0, + "(0, '2024-11-19', 7)": 0, + "(0, '2024-11-20', 0)": 0, + "(0, '2024-11-20', 1)": 0, + "(0, '2024-11-20', 2)": 0, + "(0, '2024-11-20', 3)": 0, + "(0, '2024-11-20', 4)": 0, + "(0, '2024-11-20', 5)": 0, + "(0, '2024-11-20', 6)": 0, + "(0, '2024-11-20', 7)": 0, + "(0, '2024-11-21', 0)": 0, + "(0, '2024-11-21', 1)": 0, + "(0, '2024-11-21', 2)": 1, + "(0, '2024-11-21', 3)": 0, + "(0, '2024-11-21', 4)": 0, + "(0, '2024-11-21', 5)": 0, + "(0, '2024-11-21', 6)": 0, + "(0, '2024-11-21', 7)": 0, + "(0, '2024-11-22', 0)": 0, + "(0, '2024-11-22', 1)": 0, + "(0, '2024-11-22', 2)": 0, + "(0, '2024-11-22', 3)": 0, + "(0, '2024-11-22', 4)": 0, + "(0, '2024-11-22', 5)": 0, + "(0, '2024-11-22', 6)": 0, + "(0, '2024-11-22', 7)": 0, + "(0, '2024-11-23', 0)": 0, + "(0, '2024-11-23', 1)": 0, + "(0, '2024-11-23', 2)": 0, + "(0, '2024-11-23', 3)": 0, + "(0, '2024-11-23', 4)": 0, + "(0, '2024-11-23', 5)": 0, + "(0, '2024-11-23', 6)": 0, + "(0, '2024-11-23', 7)": 0, + "(0, '2024-11-24', 0)": 0, + "(0, '2024-11-24', 1)": 0, + "(0, '2024-11-24', 2)": 0, + "(0, '2024-11-24', 3)": 0, + "(0, '2024-11-24', 4)": 0, + "(0, '2024-11-24', 5)": 0, + "(0, '2024-11-24', 6)": 0, + "(0, '2024-11-24', 7)": 0, + "(0, '2024-11-25', 0)": 0, + "(0, '2024-11-25', 1)": 0, + "(0, '2024-11-25', 2)": 0, + "(0, '2024-11-25', 3)": 0, + "(0, '2024-11-25', 4)": 0, + "(0, '2024-11-25', 5)": 0, + "(0, '2024-11-25', 6)": 0, + "(0, '2024-11-25', 7)": 0, + "(0, '2024-11-26', 0)": 0, + "(0, '2024-11-26', 1)": 0, + "(0, '2024-11-26', 2)": 0, + "(0, '2024-11-26', 3)": 0, + "(0, '2024-11-26', 4)": 0, + "(0, '2024-11-26', 5)": 0, + "(0, '2024-11-26', 6)": 0, + "(0, '2024-11-26', 7)": 0, + "(0, '2024-11-27', 0)": 0, + "(0, '2024-11-27', 1)": 0, + "(0, '2024-11-27', 2)": 0, + "(0, '2024-11-27', 3)": 0, + "(0, '2024-11-27', 4)": 0, + "(0, '2024-11-27', 5)": 0, + "(0, '2024-11-27', 6)": 0, + "(0, '2024-11-27', 7)": 0, + "(0, '2024-11-28', 0)": 0, + "(0, '2024-11-28', 1)": 0, + "(0, '2024-11-28', 2)": 0, + "(0, '2024-11-28', 3)": 0, + "(0, '2024-11-28', 4)": 0, + "(0, '2024-11-28', 5)": 0, + "(0, '2024-11-28', 6)": 0, + "(0, '2024-11-28', 7)": 0, + "(0, '2024-11-29', 0)": 0, + "(0, '2024-11-29', 1)": 0, + "(0, '2024-11-29', 2)": 0, + "(0, '2024-11-29', 3)": 0, + "(0, '2024-11-29', 4)": 0, + "(0, '2024-11-29', 5)": 0, + "(0, '2024-11-29', 6)": 0, + "(0, '2024-11-29', 7)": 0, + "(0, '2024-11-30', 0)": 0, + "(0, '2024-11-30', 1)": 0, + "(0, '2024-11-30', 2)": 0, + "(0, '2024-11-30', 3)": 0, + "(0, '2024-11-30', 4)": 0, + "(0, '2024-11-30', 5)": 0, + "(0, '2024-11-30', 6)": 0, + "(0, '2024-11-30', 7)": 0, + "(1, '2024-11-01', 0)": 0, + "(1, '2024-11-01', 1)": 0, + "(1, '2024-11-01', 2)": 0, + "(1, '2024-11-01', 3)": 0, + "(1, '2024-11-01', 4)": 0, + "(1, '2024-11-01', 5)": 0, + "(1, '2024-11-01', 6)": 0, + "(1, '2024-11-01', 7)": 0, + "(1, '2024-11-02', 0)": 0, + "(1, '2024-11-02', 1)": 0, + "(1, '2024-11-02', 2)": 1, + "(1, '2024-11-02', 3)": 0, + "(1, '2024-11-02', 4)": 0, + "(1, '2024-11-02', 5)": 0, + "(1, '2024-11-02', 6)": 0, + "(1, '2024-11-02', 7)": 0, + "(1, '2024-11-03', 0)": 0, + "(1, '2024-11-03', 1)": 0, + "(1, '2024-11-03', 2)": 0, + "(1, '2024-11-03', 3)": 0, + "(1, '2024-11-03', 4)": 0, + "(1, '2024-11-03', 5)": 0, + "(1, '2024-11-03', 6)": 0, + "(1, '2024-11-03', 7)": 0, + "(1, '2024-11-04', 0)": 0, + "(1, '2024-11-04', 1)": 0, + "(1, '2024-11-04', 2)": 0, + "(1, '2024-11-04', 3)": 0, + "(1, '2024-11-04', 4)": 0, + "(1, '2024-11-04', 5)": 0, + "(1, '2024-11-04', 6)": 0, + "(1, '2024-11-04', 7)": 0, + "(1, '2024-11-05', 0)": 0, + "(1, '2024-11-05', 1)": 0, + "(1, '2024-11-05', 2)": 0, + "(1, '2024-11-05', 3)": 0, + "(1, '2024-11-05', 4)": 0, + "(1, '2024-11-05', 5)": 0, + "(1, '2024-11-05', 6)": 0, + "(1, '2024-11-05', 7)": 0, + "(1, '2024-11-06', 0)": 0, + "(1, '2024-11-06', 1)": 0, + "(1, '2024-11-06', 2)": 0, + "(1, '2024-11-06', 3)": 0, + "(1, '2024-11-06', 4)": 0, + "(1, '2024-11-06', 5)": 0, + "(1, '2024-11-06', 6)": 0, + "(1, '2024-11-06', 7)": 0, + "(1, '2024-11-07', 0)": 0, + "(1, '2024-11-07', 1)": 0, + "(1, '2024-11-07', 2)": 0, + "(1, '2024-11-07', 3)": 0, + "(1, '2024-11-07', 4)": 0, + "(1, '2024-11-07', 5)": 0, + "(1, '2024-11-07', 6)": 0, + "(1, '2024-11-07', 7)": 0, + "(1, '2024-11-08', 0)": 0, + "(1, '2024-11-08', 1)": 0, + "(1, '2024-11-08', 2)": 0, + "(1, '2024-11-08', 3)": 0, + "(1, '2024-11-08', 4)": 0, + "(1, '2024-11-08', 5)": 0, + "(1, '2024-11-08', 6)": 0, + "(1, '2024-11-08', 7)": 0, + "(1, '2024-11-09', 0)": 0, + "(1, '2024-11-09', 1)": 0, + "(1, '2024-11-09', 2)": 0, + "(1, '2024-11-09', 3)": 0, + "(1, '2024-11-09', 4)": 0, + "(1, '2024-11-09', 5)": 0, + "(1, '2024-11-09', 6)": 0, + "(1, '2024-11-09', 7)": 0, + "(1, '2024-11-10', 0)": 0, + "(1, '2024-11-10', 1)": 0, + "(1, '2024-11-10', 2)": 0, + "(1, '2024-11-10', 3)": 0, + "(1, '2024-11-10', 4)": 0, + "(1, '2024-11-10', 5)": 0, + "(1, '2024-11-10', 6)": 0, + "(1, '2024-11-10', 7)": 0, + "(1, '2024-11-11', 0)": 0, + "(1, '2024-11-11', 1)": 0, + "(1, '2024-11-11', 2)": 0, + "(1, '2024-11-11', 3)": 0, + "(1, '2024-11-11', 4)": 0, + "(1, '2024-11-11', 5)": 0, + "(1, '2024-11-11', 6)": 0, + "(1, '2024-11-11', 7)": 0, + "(1, '2024-11-12', 0)": 0, + "(1, '2024-11-12', 1)": 0, + "(1, '2024-11-12', 2)": 0, + "(1, '2024-11-12', 3)": 0, + "(1, '2024-11-12', 4)": 0, + "(1, '2024-11-12', 5)": 0, + "(1, '2024-11-12', 6)": 0, + "(1, '2024-11-12', 7)": 0, + "(1, '2024-11-13', 0)": 0, + "(1, '2024-11-13', 1)": 0, + "(1, '2024-11-13', 2)": 0, + "(1, '2024-11-13', 3)": 0, + "(1, '2024-11-13', 4)": 0, + "(1, '2024-11-13', 5)": 0, + "(1, '2024-11-13', 6)": 0, + "(1, '2024-11-13', 7)": 0, + "(1, '2024-11-14', 0)": 0, + "(1, '2024-11-14', 1)": 0, + "(1, '2024-11-14', 2)": 0, + "(1, '2024-11-14', 3)": 0, + "(1, '2024-11-14', 4)": 0, + "(1, '2024-11-14', 5)": 0, + "(1, '2024-11-14', 6)": 0, + "(1, '2024-11-14', 7)": 0, + "(1, '2024-11-15', 0)": 0, + "(1, '2024-11-15', 1)": 0, + "(1, '2024-11-15', 2)": 0, + "(1, '2024-11-15', 3)": 0, + "(1, '2024-11-15', 4)": 0, + "(1, '2024-11-15', 5)": 0, + "(1, '2024-11-15', 6)": 0, + "(1, '2024-11-15', 7)": 0, + "(1, '2024-11-16', 0)": 0, + "(1, '2024-11-16', 1)": 0, + "(1, '2024-11-16', 2)": 0, + "(1, '2024-11-16', 3)": 0, + "(1, '2024-11-16', 4)": 0, + "(1, '2024-11-16', 5)": 0, + "(1, '2024-11-16', 6)": 0, + "(1, '2024-11-16', 7)": 0, + "(1, '2024-11-17', 0)": 0, + "(1, '2024-11-17', 1)": 0, + "(1, '2024-11-17', 2)": 0, + "(1, '2024-11-17', 3)": 0, + "(1, '2024-11-17', 4)": 0, + "(1, '2024-11-17', 5)": 0, + "(1, '2024-11-17', 6)": 0, + "(1, '2024-11-17', 7)": 0, + "(1, '2024-11-18', 0)": 0, + "(1, '2024-11-18', 1)": 0, + "(1, '2024-11-18', 2)": 0, + "(1, '2024-11-18', 3)": 0, + "(1, '2024-11-18', 4)": 0, + "(1, '2024-11-18', 5)": 0, + "(1, '2024-11-18', 6)": 0, + "(1, '2024-11-18', 7)": 0, + "(1, '2024-11-19', 0)": 0, + "(1, '2024-11-19', 1)": 0, + "(1, '2024-11-19', 2)": 0, + "(1, '2024-11-19', 3)": 0, + "(1, '2024-11-19', 4)": 0, + "(1, '2024-11-19', 5)": 0, + "(1, '2024-11-19', 6)": 0, + "(1, '2024-11-19', 7)": 0, + "(1, '2024-11-20', 0)": 0, + "(1, '2024-11-20', 1)": 0, + "(1, '2024-11-20', 2)": 0, + "(1, '2024-11-20', 3)": 0, + "(1, '2024-11-20', 4)": 0, + "(1, '2024-11-20', 5)": 0, + "(1, '2024-11-20', 6)": 0, + "(1, '2024-11-20', 7)": 0, + "(1, '2024-11-21', 0)": 0, + "(1, '2024-11-21', 1)": 0, + "(1, '2024-11-21', 2)": 0, + "(1, '2024-11-21', 3)": 0, + "(1, '2024-11-21', 4)": 0, + "(1, '2024-11-21', 5)": 0, + "(1, '2024-11-21', 6)": 0, + "(1, '2024-11-21', 7)": 0, + "(1, '2024-11-22', 0)": 0, + "(1, '2024-11-22', 1)": 0, + "(1, '2024-11-22', 2)": 1, + "(1, '2024-11-22', 3)": 0, + "(1, '2024-11-22', 4)": 0, + "(1, '2024-11-22', 5)": 0, + "(1, '2024-11-22', 6)": 0, + "(1, '2024-11-22', 7)": 0, + "(1, '2024-11-23', 0)": 0, + "(1, '2024-11-23', 1)": 0, + "(1, '2024-11-23', 2)": 0, + "(1, '2024-11-23', 3)": 0, + "(1, '2024-11-23', 4)": 0, + "(1, '2024-11-23', 5)": 0, + "(1, '2024-11-23', 6)": 0, + "(1, '2024-11-23', 7)": 0, + "(1, '2024-11-24', 0)": 0, + "(1, '2024-11-24', 1)": 0, + "(1, '2024-11-24', 2)": 0, + "(1, '2024-11-24', 3)": 0, + "(1, '2024-11-24', 4)": 0, + "(1, '2024-11-24', 5)": 0, + "(1, '2024-11-24', 6)": 0, + "(1, '2024-11-24', 7)": 0, + "(1, '2024-11-25', 0)": 1, + "(1, '2024-11-25', 1)": 0, + "(1, '2024-11-25', 2)": 0, + "(1, '2024-11-25', 3)": 0, + "(1, '2024-11-25', 4)": 0, + "(1, '2024-11-25', 5)": 0, + "(1, '2024-11-25', 6)": 0, + "(1, '2024-11-25', 7)": 0, + "(1, '2024-11-26', 0)": 0, + "(1, '2024-11-26', 1)": 0, + "(1, '2024-11-26', 2)": 0, + "(1, '2024-11-26', 3)": 0, + "(1, '2024-11-26', 4)": 0, + "(1, '2024-11-26', 5)": 0, + "(1, '2024-11-26', 6)": 0, + "(1, '2024-11-26', 7)": 0, + "(1, '2024-11-27', 0)": 0, + "(1, '2024-11-27', 1)": 0, + "(1, '2024-11-27', 2)": 0, + "(1, '2024-11-27', 3)": 0, + "(1, '2024-11-27', 4)": 0, + "(1, '2024-11-27', 5)": 0, + "(1, '2024-11-27', 6)": 0, + "(1, '2024-11-27', 7)": 0, + "(1, '2024-11-28', 0)": 0, + "(1, '2024-11-28', 1)": 0, + "(1, '2024-11-28', 2)": 0, + "(1, '2024-11-28', 3)": 0, + "(1, '2024-11-28', 4)": 0, + "(1, '2024-11-28', 5)": 0, + "(1, '2024-11-28', 6)": 0, + "(1, '2024-11-28', 7)": 0, + "(1, '2024-11-29', 0)": 0, + "(1, '2024-11-29', 1)": 0, + "(1, '2024-11-29', 2)": 0, + "(1, '2024-11-29', 3)": 0, + "(1, '2024-11-29', 4)": 0, + "(1, '2024-11-29', 5)": 0, + "(1, '2024-11-29', 6)": 0, + "(1, '2024-11-29', 7)": 0, + "(1, '2024-11-30', 0)": 0, + "(1, '2024-11-30', 1)": 0, + "(1, '2024-11-30', 2)": 0, + "(1, '2024-11-30', 3)": 0, + "(1, '2024-11-30', 4)": 0, + "(1, '2024-11-30', 5)": 0, + "(1, '2024-11-30', 6)": 0, + "(1, '2024-11-30', 7)": 0, + "(1230, '2024-11-01', 0)": 0, + "(1230, '2024-11-01', 1)": 0, + "(1230, '2024-11-01', 2)": 0, + "(1230, '2024-11-01', 3)": 1, + "(1230, '2024-11-01', 4)": 0, + "(1230, '2024-11-01', 5)": 0, + "(1230, '2024-11-01', 6)": 0, + "(1230, '2024-11-01', 7)": 0, + "(1230, '2024-11-02', 0)": 0, + "(1230, '2024-11-02', 1)": 0, + "(1230, '2024-11-02', 2)": 0, + "(1230, '2024-11-02', 3)": 0, + "(1230, '2024-11-02', 4)": 0, + "(1230, '2024-11-02', 5)": 0, + "(1230, '2024-11-02', 6)": 0, + "(1230, '2024-11-02', 7)": 0, + "(1230, '2024-11-03', 0)": 0, + "(1230, '2024-11-03', 1)": 0, + "(1230, '2024-11-03', 2)": 0, + "(1230, '2024-11-03', 3)": 0, + "(1230, '2024-11-03', 4)": 0, + "(1230, '2024-11-03', 5)": 0, + "(1230, '2024-11-03', 6)": 0, + "(1230, '2024-11-03', 7)": 0, + "(1230, '2024-11-04', 0)": 0, + "(1230, '2024-11-04', 1)": 0, + "(1230, '2024-11-04', 2)": 1, + "(1230, '2024-11-04', 3)": 0, + "(1230, '2024-11-04', 4)": 0, + "(1230, '2024-11-04', 5)": 0, + "(1230, '2024-11-04', 6)": 0, + "(1230, '2024-11-04', 7)": 0, + "(1230, '2024-11-05', 0)": 0, + "(1230, '2024-11-05', 1)": 0, + "(1230, '2024-11-05', 2)": 0, + "(1230, '2024-11-05', 3)": 0, + "(1230, '2024-11-05', 4)": 0, + "(1230, '2024-11-05', 5)": 0, + "(1230, '2024-11-05', 6)": 0, + "(1230, '2024-11-05', 7)": 0, + "(1230, '2024-11-06', 0)": 1, + "(1230, '2024-11-06', 1)": 0, + "(1230, '2024-11-06', 2)": 0, + "(1230, '2024-11-06', 3)": 0, + "(1230, '2024-11-06', 4)": 0, + "(1230, '2024-11-06', 5)": 0, + "(1230, '2024-11-06', 6)": 0, + "(1230, '2024-11-06', 7)": 0, + "(1230, '2024-11-07', 0)": 1, + "(1230, '2024-11-07', 1)": 0, + "(1230, '2024-11-07', 2)": 0, + "(1230, '2024-11-07', 3)": 0, + "(1230, '2024-11-07', 4)": 0, + "(1230, '2024-11-07', 5)": 0, + "(1230, '2024-11-07', 6)": 0, + "(1230, '2024-11-07', 7)": 0, + "(1230, '2024-11-08', 0)": 0, + "(1230, '2024-11-08', 1)": 0, + "(1230, '2024-11-08', 2)": 0, + "(1230, '2024-11-08', 3)": 0, + "(1230, '2024-11-08', 4)": 0, + "(1230, '2024-11-08', 5)": 0, + "(1230, '2024-11-08', 6)": 0, + "(1230, '2024-11-08', 7)": 0, + "(1230, '2024-11-09', 0)": 1, + "(1230, '2024-11-09', 1)": 0, + "(1230, '2024-11-09', 2)": 0, + "(1230, '2024-11-09', 3)": 0, + "(1230, '2024-11-09', 4)": 0, + "(1230, '2024-11-09', 5)": 0, + "(1230, '2024-11-09', 6)": 0, + "(1230, '2024-11-09', 7)": 0, + "(1230, '2024-11-10', 0)": 1, + "(1230, '2024-11-10', 1)": 0, + "(1230, '2024-11-10', 2)": 0, + "(1230, '2024-11-10', 3)": 0, + "(1230, '2024-11-10', 4)": 0, + "(1230, '2024-11-10', 5)": 0, + "(1230, '2024-11-10', 6)": 0, + "(1230, '2024-11-10', 7)": 0, + "(1230, '2024-11-11', 0)": 0, + "(1230, '2024-11-11', 1)": 0, + "(1230, '2024-11-11', 2)": 0, + "(1230, '2024-11-11', 3)": 0, + "(1230, '2024-11-11', 4)": 0, + "(1230, '2024-11-11', 5)": 0, + "(1230, '2024-11-11', 6)": 0, + "(1230, '2024-11-11', 7)": 0, + "(1230, '2024-11-12', 0)": 1, + "(1230, '2024-11-12', 1)": 0, + "(1230, '2024-11-12', 2)": 0, + "(1230, '2024-11-12', 3)": 0, + "(1230, '2024-11-12', 4)": 0, + "(1230, '2024-11-12', 5)": 0, + "(1230, '2024-11-12', 6)": 0, + "(1230, '2024-11-12', 7)": 0, + "(1230, '2024-11-13', 0)": 0, + "(1230, '2024-11-13', 1)": 0, + "(1230, '2024-11-13', 2)": 1, + "(1230, '2024-11-13', 3)": 0, + "(1230, '2024-11-13', 4)": 0, + "(1230, '2024-11-13', 5)": 0, + "(1230, '2024-11-13', 6)": 0, + "(1230, '2024-11-13', 7)": 0, + "(1230, '2024-11-14', 0)": 0, + "(1230, '2024-11-14', 1)": 0, + "(1230, '2024-11-14', 2)": 0, + "(1230, '2024-11-14', 3)": 0, + "(1230, '2024-11-14', 4)": 0, + "(1230, '2024-11-14', 5)": 0, + "(1230, '2024-11-14', 6)": 0, + "(1230, '2024-11-14', 7)": 0, + "(1230, '2024-11-15', 0)": 0, + "(1230, '2024-11-15', 1)": 0, + "(1230, '2024-11-15', 2)": 0, + "(1230, '2024-11-15', 3)": 0, + "(1230, '2024-11-15', 4)": 0, + "(1230, '2024-11-15', 5)": 0, + "(1230, '2024-11-15', 6)": 0, + "(1230, '2024-11-15', 7)": 0, + "(1230, '2024-11-16', 0)": 0, + "(1230, '2024-11-16', 1)": 0, + "(1230, '2024-11-16', 2)": 0, + "(1230, '2024-11-16', 3)": 0, + "(1230, '2024-11-16', 4)": 0, + "(1230, '2024-11-16', 5)": 0, + "(1230, '2024-11-16', 6)": 0, + "(1230, '2024-11-16', 7)": 0, + "(1230, '2024-11-17', 0)": 0, + "(1230, '2024-11-17', 1)": 0, + "(1230, '2024-11-17', 2)": 0, + "(1230, '2024-11-17', 3)": 0, + "(1230, '2024-11-17', 4)": 0, + "(1230, '2024-11-17', 5)": 0, + "(1230, '2024-11-17', 6)": 0, + "(1230, '2024-11-17', 7)": 0, + "(1230, '2024-11-18', 0)": 0, + "(1230, '2024-11-18', 1)": 0, + "(1230, '2024-11-18', 2)": 1, + "(1230, '2024-11-18', 3)": 0, + "(1230, '2024-11-18', 4)": 0, + "(1230, '2024-11-18', 5)": 0, + "(1230, '2024-11-18', 6)": 0, + "(1230, '2024-11-18', 7)": 0, + "(1230, '2024-11-19', 0)": 0, + "(1230, '2024-11-19', 1)": 0, + "(1230, '2024-11-19', 2)": 0, + "(1230, '2024-11-19', 3)": 0, + "(1230, '2024-11-19', 4)": 0, + "(1230, '2024-11-19', 5)": 0, + "(1230, '2024-11-19', 6)": 0, + "(1230, '2024-11-19', 7)": 0, + "(1230, '2024-11-20', 0)": 0, + "(1230, '2024-11-20', 1)": 0, + "(1230, '2024-11-20', 2)": 1, + "(1230, '2024-11-20', 3)": 0, + "(1230, '2024-11-20', 4)": 0, + "(1230, '2024-11-20', 5)": 0, + "(1230, '2024-11-20', 6)": 0, + "(1230, '2024-11-20', 7)": 0, + "(1230, '2024-11-21', 0)": 0, + "(1230, '2024-11-21', 1)": 0, + "(1230, '2024-11-21', 2)": 0, + "(1230, '2024-11-21', 3)": 0, + "(1230, '2024-11-21', 4)": 0, + "(1230, '2024-11-21', 5)": 0, + "(1230, '2024-11-21', 6)": 0, + "(1230, '2024-11-21', 7)": 0, + "(1230, '2024-11-22', 0)": 0, + "(1230, '2024-11-22', 1)": 0, + "(1230, '2024-11-22', 2)": 0, + "(1230, '2024-11-22', 3)": 1, + "(1230, '2024-11-22', 4)": 0, + "(1230, '2024-11-22', 5)": 0, + "(1230, '2024-11-22', 6)": 0, + "(1230, '2024-11-22', 7)": 0, + "(1230, '2024-11-23', 0)": 0, + "(1230, '2024-11-23', 1)": 0, + "(1230, '2024-11-23', 2)": 0, + "(1230, '2024-11-23', 3)": 0, + "(1230, '2024-11-23', 4)": 0, + "(1230, '2024-11-23', 5)": 0, + "(1230, '2024-11-23', 6)": 0, + "(1230, '2024-11-23', 7)": 0, + "(1230, '2024-11-24', 0)": 1, + "(1230, '2024-11-24', 1)": 0, + "(1230, '2024-11-24', 2)": 0, + "(1230, '2024-11-24', 3)": 0, + "(1230, '2024-11-24', 4)": 0, + "(1230, '2024-11-24', 5)": 0, + "(1230, '2024-11-24', 6)": 0, + "(1230, '2024-11-24', 7)": 0, + "(1230, '2024-11-25', 0)": 0, + "(1230, '2024-11-25', 1)": 0, + "(1230, '2024-11-25', 2)": 1, + "(1230, '2024-11-25', 3)": 0, + "(1230, '2024-11-25', 4)": 0, + "(1230, '2024-11-25', 5)": 0, + "(1230, '2024-11-25', 6)": 0, + "(1230, '2024-11-25', 7)": 0, + "(1230, '2024-11-26', 0)": 0, + "(1230, '2024-11-26', 1)": 0, + "(1230, '2024-11-26', 2)": 1, + "(1230, '2024-11-26', 3)": 0, + "(1230, '2024-11-26', 4)": 0, + "(1230, '2024-11-26', 5)": 0, + "(1230, '2024-11-26', 6)": 0, + "(1230, '2024-11-26', 7)": 0, + "(1230, '2024-11-27', 0)": 0, + "(1230, '2024-11-27', 1)": 0, + "(1230, '2024-11-27', 2)": 0, + "(1230, '2024-11-27', 3)": 0, + "(1230, '2024-11-27', 4)": 0, + "(1230, '2024-11-27', 5)": 0, + "(1230, '2024-11-27', 6)": 0, + "(1230, '2024-11-27', 7)": 0, + "(1230, '2024-11-28', 0)": 1, + "(1230, '2024-11-28', 1)": 0, + "(1230, '2024-11-28', 2)": 0, + "(1230, '2024-11-28', 3)": 0, + "(1230, '2024-11-28', 4)": 0, + "(1230, '2024-11-28', 5)": 0, + "(1230, '2024-11-28', 6)": 0, + "(1230, '2024-11-28', 7)": 0, + "(1230, '2024-11-29', 0)": 1, + "(1230, '2024-11-29', 1)": 0, + "(1230, '2024-11-29', 2)": 0, + "(1230, '2024-11-29', 3)": 0, + "(1230, '2024-11-29', 4)": 0, + "(1230, '2024-11-29', 5)": 0, + "(1230, '2024-11-29', 6)": 0, + "(1230, '2024-11-29', 7)": 0, + "(1230, '2024-11-30', 0)": 0, + "(1230, '2024-11-30', 1)": 0, + "(1230, '2024-11-30', 2)": 1, + "(1230, '2024-11-30', 3)": 0, + "(1230, '2024-11-30', 4)": 0, + "(1230, '2024-11-30', 5)": 0, + "(1230, '2024-11-30', 6)": 0, + "(1230, '2024-11-30', 7)": 0, + "(2, '2024-11-01', 0)": 0, + "(2, '2024-11-01', 1)": 0, + "(2, '2024-11-01', 2)": 0, + "(2, '2024-11-01', 3)": 0, + "(2, '2024-11-01', 4)": 0, + "(2, '2024-11-01', 5)": 0, + "(2, '2024-11-01', 6)": 0, + "(2, '2024-11-01', 7)": 0, + "(2, '2024-11-02', 0)": 1, + "(2, '2024-11-02', 1)": 0, + "(2, '2024-11-02', 2)": 0, + "(2, '2024-11-02', 3)": 0, + "(2, '2024-11-02', 4)": 0, + "(2, '2024-11-02', 5)": 0, + "(2, '2024-11-02', 6)": 0, + "(2, '2024-11-02', 7)": 0, + "(2, '2024-11-03', 0)": 0, + "(2, '2024-11-03', 1)": 0, + "(2, '2024-11-03', 2)": 0, + "(2, '2024-11-03', 3)": 0, + "(2, '2024-11-03', 4)": 0, + "(2, '2024-11-03', 5)": 0, + "(2, '2024-11-03', 6)": 0, + "(2, '2024-11-03', 7)": 0, + "(2, '2024-11-04', 0)": 0, + "(2, '2024-11-04', 1)": 0, + "(2, '2024-11-04', 2)": 0, + "(2, '2024-11-04', 3)": 0, + "(2, '2024-11-04', 4)": 0, + "(2, '2024-11-04', 5)": 0, + "(2, '2024-11-04', 6)": 0, + "(2, '2024-11-04', 7)": 0, + "(2, '2024-11-05', 0)": 0, + "(2, '2024-11-05', 1)": 0, + "(2, '2024-11-05', 2)": 0, + "(2, '2024-11-05', 3)": 0, + "(2, '2024-11-05', 4)": 0, + "(2, '2024-11-05', 5)": 0, + "(2, '2024-11-05', 6)": 0, + "(2, '2024-11-05', 7)": 0, + "(2, '2024-11-06', 0)": 1, + "(2, '2024-11-06', 1)": 0, + "(2, '2024-11-06', 2)": 0, + "(2, '2024-11-06', 3)": 0, + "(2, '2024-11-06', 4)": 0, + "(2, '2024-11-06', 5)": 0, + "(2, '2024-11-06', 6)": 0, + "(2, '2024-11-06', 7)": 0, + "(2, '2024-11-07', 0)": 0, + "(2, '2024-11-07', 1)": 0, + "(2, '2024-11-07', 2)": 0, + "(2, '2024-11-07', 3)": 0, + "(2, '2024-11-07', 4)": 0, + "(2, '2024-11-07', 5)": 0, + "(2, '2024-11-07', 6)": 0, + "(2, '2024-11-07', 7)": 0, + "(2, '2024-11-08', 0)": 0, + "(2, '2024-11-08', 1)": 0, + "(2, '2024-11-08', 2)": 1, + "(2, '2024-11-08', 3)": 0, + "(2, '2024-11-08', 4)": 0, + "(2, '2024-11-08', 5)": 0, + "(2, '2024-11-08', 6)": 0, + "(2, '2024-11-08', 7)": 0, + "(2, '2024-11-09', 0)": 0, + "(2, '2024-11-09', 1)": 0, + "(2, '2024-11-09', 2)": 0, + "(2, '2024-11-09', 3)": 0, + "(2, '2024-11-09', 4)": 0, + "(2, '2024-11-09', 5)": 0, + "(2, '2024-11-09', 6)": 0, + "(2, '2024-11-09', 7)": 0, + "(2, '2024-11-10', 0)": 0, + "(2, '2024-11-10', 1)": 0, + "(2, '2024-11-10', 2)": 0, + "(2, '2024-11-10', 3)": 0, + "(2, '2024-11-10', 4)": 0, + "(2, '2024-11-10', 5)": 0, + "(2, '2024-11-10', 6)": 0, + "(2, '2024-11-10', 7)": 0, + "(2, '2024-11-11', 0)": 0, + "(2, '2024-11-11', 1)": 0, + "(2, '2024-11-11', 2)": 0, + "(2, '2024-11-11', 3)": 0, + "(2, '2024-11-11', 4)": 0, + "(2, '2024-11-11', 5)": 0, + "(2, '2024-11-11', 6)": 0, + "(2, '2024-11-11', 7)": 0, + "(2, '2024-11-12', 0)": 0, + "(2, '2024-11-12', 1)": 0, + "(2, '2024-11-12', 2)": 0, + "(2, '2024-11-12', 3)": 0, + "(2, '2024-11-12', 4)": 0, + "(2, '2024-11-12', 5)": 0, + "(2, '2024-11-12', 6)": 0, + "(2, '2024-11-12', 7)": 0, + "(2, '2024-11-13', 0)": 0, + "(2, '2024-11-13', 1)": 0, + "(2, '2024-11-13', 2)": 0, + "(2, '2024-11-13', 3)": 0, + "(2, '2024-11-13', 4)": 0, + "(2, '2024-11-13', 5)": 0, + "(2, '2024-11-13', 6)": 0, + "(2, '2024-11-13', 7)": 0, + "(2, '2024-11-14', 0)": 0, + "(2, '2024-11-14', 1)": 0, + "(2, '2024-11-14', 2)": 0, + "(2, '2024-11-14', 3)": 0, + "(2, '2024-11-14', 4)": 0, + "(2, '2024-11-14', 5)": 0, + "(2, '2024-11-14', 6)": 0, + "(2, '2024-11-14', 7)": 0, + "(2, '2024-11-15', 0)": 0, + "(2, '2024-11-15', 1)": 0, + "(2, '2024-11-15', 2)": 0, + "(2, '2024-11-15', 3)": 0, + "(2, '2024-11-15', 4)": 0, + "(2, '2024-11-15', 5)": 0, + "(2, '2024-11-15', 6)": 0, + "(2, '2024-11-15', 7)": 0, + "(2, '2024-11-16', 0)": 0, + "(2, '2024-11-16', 1)": 0, + "(2, '2024-11-16', 2)": 1, + "(2, '2024-11-16', 3)": 0, + "(2, '2024-11-16', 4)": 0, + "(2, '2024-11-16', 5)": 0, + "(2, '2024-11-16', 6)": 0, + "(2, '2024-11-16', 7)": 0, + "(2, '2024-11-17', 0)": 0, + "(2, '2024-11-17', 1)": 0, + "(2, '2024-11-17', 2)": 0, + "(2, '2024-11-17', 3)": 0, + "(2, '2024-11-17', 4)": 0, + "(2, '2024-11-17', 5)": 0, + "(2, '2024-11-17', 6)": 0, + "(2, '2024-11-17', 7)": 0, + "(2, '2024-11-18', 0)": 0, + "(2, '2024-11-18', 1)": 0, + "(2, '2024-11-18', 2)": 0, + "(2, '2024-11-18', 3)": 0, + "(2, '2024-11-18', 4)": 0, + "(2, '2024-11-18', 5)": 0, + "(2, '2024-11-18', 6)": 0, + "(2, '2024-11-18', 7)": 0, + "(2, '2024-11-19', 0)": 0, + "(2, '2024-11-19', 1)": 0, + "(2, '2024-11-19', 2)": 0, + "(2, '2024-11-19', 3)": 0, + "(2, '2024-11-19', 4)": 0, + "(2, '2024-11-19', 5)": 0, + "(2, '2024-11-19', 6)": 0, + "(2, '2024-11-19', 7)": 0, + "(2, '2024-11-20', 0)": 0, + "(2, '2024-11-20', 1)": 0, + "(2, '2024-11-20', 2)": 1, + "(2, '2024-11-20', 3)": 0, + "(2, '2024-11-20', 4)": 0, + "(2, '2024-11-20', 5)": 0, + "(2, '2024-11-20', 6)": 0, + "(2, '2024-11-20', 7)": 0, + "(2, '2024-11-21', 0)": 0, + "(2, '2024-11-21', 1)": 0, + "(2, '2024-11-21', 2)": 0, + "(2, '2024-11-21', 3)": 0, + "(2, '2024-11-21', 4)": 0, + "(2, '2024-11-21', 5)": 0, + "(2, '2024-11-21', 6)": 0, + "(2, '2024-11-21', 7)": 0, + "(2, '2024-11-22', 0)": 0, + "(2, '2024-11-22', 1)": 0, + "(2, '2024-11-22', 2)": 0, + "(2, '2024-11-22', 3)": 0, + "(2, '2024-11-22', 4)": 0, + "(2, '2024-11-22', 5)": 0, + "(2, '2024-11-22', 6)": 0, + "(2, '2024-11-22', 7)": 0, + "(2, '2024-11-23', 0)": 0, + "(2, '2024-11-23', 1)": 0, + "(2, '2024-11-23', 2)": 0, + "(2, '2024-11-23', 3)": 0, + "(2, '2024-11-23', 4)": 0, + "(2, '2024-11-23', 5)": 0, + "(2, '2024-11-23', 6)": 0, + "(2, '2024-11-23', 7)": 0, + "(2, '2024-11-24', 0)": 0, + "(2, '2024-11-24', 1)": 0, + "(2, '2024-11-24', 2)": 0, + "(2, '2024-11-24', 3)": 0, + "(2, '2024-11-24', 4)": 0, + "(2, '2024-11-24', 5)": 0, + "(2, '2024-11-24', 6)": 0, + "(2, '2024-11-24', 7)": 0, + "(2, '2024-11-25', 0)": 0, + "(2, '2024-11-25', 1)": 0, + "(2, '2024-11-25', 2)": 0, + "(2, '2024-11-25', 3)": 0, + "(2, '2024-11-25', 4)": 0, + "(2, '2024-11-25', 5)": 0, + "(2, '2024-11-25', 6)": 0, + "(2, '2024-11-25', 7)": 0, + "(2, '2024-11-26', 0)": 0, + "(2, '2024-11-26', 1)": 0, + "(2, '2024-11-26', 2)": 0, + "(2, '2024-11-26', 3)": 0, + "(2, '2024-11-26', 4)": 0, + "(2, '2024-11-26', 5)": 0, + "(2, '2024-11-26', 6)": 0, + "(2, '2024-11-26', 7)": 0, + "(2, '2024-11-27', 0)": 0, + "(2, '2024-11-27', 1)": 0, + "(2, '2024-11-27', 2)": 0, + "(2, '2024-11-27', 3)": 0, + "(2, '2024-11-27', 4)": 0, + "(2, '2024-11-27', 5)": 0, + "(2, '2024-11-27', 6)": 0, + "(2, '2024-11-27', 7)": 0, + "(2, '2024-11-28', 0)": 0, + "(2, '2024-11-28', 1)": 0, + "(2, '2024-11-28', 2)": 0, + "(2, '2024-11-28', 3)": 0, + "(2, '2024-11-28', 4)": 0, + "(2, '2024-11-28', 5)": 0, + "(2, '2024-11-28', 6)": 0, + "(2, '2024-11-28', 7)": 0, + "(2, '2024-11-29', 0)": 0, + "(2, '2024-11-29', 1)": 0, + "(2, '2024-11-29', 2)": 0, + "(2, '2024-11-29', 3)": 0, + "(2, '2024-11-29', 4)": 0, + "(2, '2024-11-29', 5)": 0, + "(2, '2024-11-29', 6)": 0, + "(2, '2024-11-29', 7)": 0, + "(2, '2024-11-30', 0)": 0, + "(2, '2024-11-30', 1)": 0, + "(2, '2024-11-30', 2)": 0, + "(2, '2024-11-30', 3)": 0, + "(2, '2024-11-30', 4)": 0, + "(2, '2024-11-30', 5)": 0, + "(2, '2024-11-30', 6)": 0, + "(2, '2024-11-30', 7)": 0, + "(2932, '2024-11-01', 0)": 0, + "(2932, '2024-11-01', 1)": 0, + "(2932, '2024-11-01', 2)": 1, + "(2932, '2024-11-01', 3)": 0, + "(2932, '2024-11-01', 4)": 0, + "(2932, '2024-11-01', 5)": 0, + "(2932, '2024-11-01', 6)": 0, + "(2932, '2024-11-01', 7)": 0, + "(2932, '2024-11-02', 0)": 0, + "(2932, '2024-11-02', 1)": 0, + "(2932, '2024-11-02', 2)": 0, + "(2932, '2024-11-02', 3)": 0, + "(2932, '2024-11-02', 4)": 0, + "(2932, '2024-11-02', 5)": 0, + "(2932, '2024-11-02', 6)": 0, + "(2932, '2024-11-02', 7)": 0, + "(2932, '2024-11-03', 0)": 1, + "(2932, '2024-11-03', 1)": 0, + "(2932, '2024-11-03', 2)": 0, + "(2932, '2024-11-03', 3)": 0, + "(2932, '2024-11-03', 4)": 0, + "(2932, '2024-11-03', 5)": 0, + "(2932, '2024-11-03', 6)": 0, + "(2932, '2024-11-03', 7)": 0, + "(2932, '2024-11-04', 0)": 0, + "(2932, '2024-11-04', 1)": 0, + "(2932, '2024-11-04', 2)": 1, + "(2932, '2024-11-04', 3)": 0, + "(2932, '2024-11-04', 4)": 0, + "(2932, '2024-11-04', 5)": 0, + "(2932, '2024-11-04', 6)": 0, + "(2932, '2024-11-04', 7)": 0, + "(2932, '2024-11-05', 0)": 0, + "(2932, '2024-11-05', 1)": 1, + "(2932, '2024-11-05', 2)": 0, + "(2932, '2024-11-05', 3)": 0, + "(2932, '2024-11-05', 4)": 0, + "(2932, '2024-11-05', 5)": 0, + "(2932, '2024-11-05', 6)": 0, + "(2932, '2024-11-05', 7)": 0, + "(2932, '2024-11-06', 0)": 0, + "(2932, '2024-11-06', 1)": 0, + "(2932, '2024-11-06', 2)": 0, + "(2932, '2024-11-06', 3)": 1, + "(2932, '2024-11-06', 4)": 0, + "(2932, '2024-11-06', 5)": 0, + "(2932, '2024-11-06', 6)": 0, + "(2932, '2024-11-06', 7)": 0, + "(2932, '2024-11-07', 0)": 0, + "(2932, '2024-11-07', 1)": 0, + "(2932, '2024-11-07', 2)": 0, + "(2932, '2024-11-07', 3)": 0, + "(2932, '2024-11-07', 4)": 0, + "(2932, '2024-11-07', 5)": 0, + "(2932, '2024-11-07', 6)": 0, + "(2932, '2024-11-07', 7)": 0, + "(2932, '2024-11-08', 0)": 0, + "(2932, '2024-11-08', 1)": 0, + "(2932, '2024-11-08', 2)": 0, + "(2932, '2024-11-08', 3)": 0, + "(2932, '2024-11-08', 4)": 0, + "(2932, '2024-11-08', 5)": 0, + "(2932, '2024-11-08', 6)": 0, + "(2932, '2024-11-08', 7)": 0, + "(2932, '2024-11-09', 0)": 0, + "(2932, '2024-11-09', 1)": 0, + "(2932, '2024-11-09', 2)": 0, + "(2932, '2024-11-09', 3)": 0, + "(2932, '2024-11-09', 4)": 0, + "(2932, '2024-11-09', 5)": 0, + "(2932, '2024-11-09', 6)": 0, + "(2932, '2024-11-09', 7)": 0, + "(2932, '2024-11-10', 0)": 0, + "(2932, '2024-11-10', 1)": 1, + "(2932, '2024-11-10', 2)": 0, + "(2932, '2024-11-10', 3)": 0, + "(2932, '2024-11-10', 4)": 0, + "(2932, '2024-11-10', 5)": 0, + "(2932, '2024-11-10', 6)": 0, + "(2932, '2024-11-10', 7)": 0, + "(2932, '2024-11-11', 0)": 0, + "(2932, '2024-11-11', 1)": 0, + "(2932, '2024-11-11', 2)": 0, + "(2932, '2024-11-11', 3)": 1, + "(2932, '2024-11-11', 4)": 0, + "(2932, '2024-11-11', 5)": 0, + "(2932, '2024-11-11', 6)": 0, + "(2932, '2024-11-11', 7)": 0, + "(2932, '2024-11-12', 0)": 0, + "(2932, '2024-11-12', 1)": 0, + "(2932, '2024-11-12', 2)": 0, + "(2932, '2024-11-12', 3)": 0, + "(2932, '2024-11-12', 4)": 0, + "(2932, '2024-11-12', 5)": 0, + "(2932, '2024-11-12', 6)": 0, + "(2932, '2024-11-12', 7)": 0, + "(2932, '2024-11-13', 0)": 0, + "(2932, '2024-11-13', 1)": 0, + "(2932, '2024-11-13', 2)": 0, + "(2932, '2024-11-13', 3)": 0, + "(2932, '2024-11-13', 4)": 0, + "(2932, '2024-11-13', 5)": 0, + "(2932, '2024-11-13', 6)": 0, + "(2932, '2024-11-13', 7)": 0, + "(2932, '2024-11-14', 0)": 0, + "(2932, '2024-11-14', 1)": 0, + "(2932, '2024-11-14', 2)": 1, + "(2932, '2024-11-14', 3)": 0, + "(2932, '2024-11-14', 4)": 0, + "(2932, '2024-11-14', 5)": 0, + "(2932, '2024-11-14', 6)": 0, + "(2932, '2024-11-14', 7)": 0, + "(2932, '2024-11-15', 0)": 0, + "(2932, '2024-11-15', 1)": 0, + "(2932, '2024-11-15', 2)": 1, + "(2932, '2024-11-15', 3)": 0, + "(2932, '2024-11-15', 4)": 0, + "(2932, '2024-11-15', 5)": 0, + "(2932, '2024-11-15', 6)": 0, + "(2932, '2024-11-15', 7)": 0, + "(2932, '2024-11-16', 0)": 0, + "(2932, '2024-11-16', 1)": 1, + "(2932, '2024-11-16', 2)": 0, + "(2932, '2024-11-16', 3)": 0, + "(2932, '2024-11-16', 4)": 0, + "(2932, '2024-11-16', 5)": 0, + "(2932, '2024-11-16', 6)": 0, + "(2932, '2024-11-16', 7)": 0, + "(2932, '2024-11-17', 0)": 1, + "(2932, '2024-11-17', 1)": 0, + "(2932, '2024-11-17', 2)": 0, + "(2932, '2024-11-17', 3)": 0, + "(2932, '2024-11-17', 4)": 0, + "(2932, '2024-11-17', 5)": 0, + "(2932, '2024-11-17', 6)": 0, + "(2932, '2024-11-17', 7)": 0, + "(2932, '2024-11-18', 0)": 0, + "(2932, '2024-11-18', 1)": 1, + "(2932, '2024-11-18', 2)": 0, + "(2932, '2024-11-18', 3)": 0, + "(2932, '2024-11-18', 4)": 0, + "(2932, '2024-11-18', 5)": 0, + "(2932, '2024-11-18', 6)": 0, + "(2932, '2024-11-18', 7)": 0, + "(2932, '2024-11-19', 0)": 0, + "(2932, '2024-11-19', 1)": 0, + "(2932, '2024-11-19', 2)": 1, + "(2932, '2024-11-19', 3)": 0, + "(2932, '2024-11-19', 4)": 0, + "(2932, '2024-11-19', 5)": 0, + "(2932, '2024-11-19', 6)": 0, + "(2932, '2024-11-19', 7)": 0, + "(2932, '2024-11-20', 0)": 0, + "(2932, '2024-11-20', 1)": 0, + "(2932, '2024-11-20', 2)": 0, + "(2932, '2024-11-20', 3)": 0, + "(2932, '2024-11-20', 4)": 0, + "(2932, '2024-11-20', 5)": 0, + "(2932, '2024-11-20', 6)": 0, + "(2932, '2024-11-20', 7)": 0, + "(2932, '2024-11-21', 0)": 0, + "(2932, '2024-11-21', 1)": 0, + "(2932, '2024-11-21', 2)": 1, + "(2932, '2024-11-21', 3)": 0, + "(2932, '2024-11-21', 4)": 0, + "(2932, '2024-11-21', 5)": 0, + "(2932, '2024-11-21', 6)": 0, + "(2932, '2024-11-21', 7)": 0, + "(2932, '2024-11-22', 0)": 0, + "(2932, '2024-11-22', 1)": 0, + "(2932, '2024-11-22', 2)": 0, + "(2932, '2024-11-22', 3)": 0, + "(2932, '2024-11-22', 4)": 0, + "(2932, '2024-11-22', 5)": 0, + "(2932, '2024-11-22', 6)": 0, + "(2932, '2024-11-22', 7)": 0, + "(2932, '2024-11-23', 0)": 0, + "(2932, '2024-11-23', 1)": 0, + "(2932, '2024-11-23', 2)": 0, + "(2932, '2024-11-23', 3)": 0, + "(2932, '2024-11-23', 4)": 0, + "(2932, '2024-11-23', 5)": 0, + "(2932, '2024-11-23', 6)": 0, + "(2932, '2024-11-23', 7)": 0, + "(2932, '2024-11-24', 0)": 0, + "(2932, '2024-11-24', 1)": 0, + "(2932, '2024-11-24', 2)": 1, + "(2932, '2024-11-24', 3)": 0, + "(2932, '2024-11-24', 4)": 0, + "(2932, '2024-11-24', 5)": 0, + "(2932, '2024-11-24', 6)": 0, + "(2932, '2024-11-24', 7)": 0, + "(2932, '2024-11-25', 0)": 0, + "(2932, '2024-11-25', 1)": 0, + "(2932, '2024-11-25', 2)": 0, + "(2932, '2024-11-25', 3)": 1, + "(2932, '2024-11-25', 4)": 0, + "(2932, '2024-11-25', 5)": 0, + "(2932, '2024-11-25', 6)": 0, + "(2932, '2024-11-25', 7)": 0, + "(2932, '2024-11-26', 0)": 0, + "(2932, '2024-11-26', 1)": 0, + "(2932, '2024-11-26', 2)": 0, + "(2932, '2024-11-26', 3)": 1, + "(2932, '2024-11-26', 4)": 0, + "(2932, '2024-11-26', 5)": 0, + "(2932, '2024-11-26', 6)": 0, + "(2932, '2024-11-26', 7)": 0, + "(2932, '2024-11-27', 0)": 0, + "(2932, '2024-11-27', 1)": 0, + "(2932, '2024-11-27', 2)": 0, + "(2932, '2024-11-27', 3)": 1, + "(2932, '2024-11-27', 4)": 0, + "(2932, '2024-11-27', 5)": 0, + "(2932, '2024-11-27', 6)": 0, + "(2932, '2024-11-27', 7)": 0, + "(2932, '2024-11-28', 0)": 0, + "(2932, '2024-11-28', 1)": 0, + "(2932, '2024-11-28', 2)": 0, + "(2932, '2024-11-28', 3)": 0, + "(2932, '2024-11-28', 4)": 0, + "(2932, '2024-11-28', 5)": 0, + "(2932, '2024-11-28', 6)": 0, + "(2932, '2024-11-28', 7)": 0, + "(2932, '2024-11-29', 0)": 0, + "(2932, '2024-11-29', 1)": 0, + "(2932, '2024-11-29', 2)": 0, + "(2932, '2024-11-29', 3)": 0, + "(2932, '2024-11-29', 4)": 0, + "(2932, '2024-11-29', 5)": 0, + "(2932, '2024-11-29', 6)": 0, + "(2932, '2024-11-29', 7)": 0, + "(2932, '2024-11-30', 0)": 0, + "(2932, '2024-11-30', 1)": 0, + "(2932, '2024-11-30', 2)": 0, + "(2932, '2024-11-30', 3)": 0, + "(2932, '2024-11-30', 4)": 0, + "(2932, '2024-11-30', 5)": 0, + "(2932, '2024-11-30', 6)": 0, + "(2932, '2024-11-30', 7)": 0, + "(2963, '2024-11-01', 0)": 1, + "(2963, '2024-11-01', 1)": 0, + "(2963, '2024-11-01', 2)": 0, + "(2963, '2024-11-01', 3)": 0, + "(2963, '2024-11-01', 4)": 0, + "(2963, '2024-11-01', 5)": 0, + "(2963, '2024-11-01', 6)": 0, + "(2963, '2024-11-01', 7)": 0, + "(2963, '2024-11-02', 0)": 0, + "(2963, '2024-11-02', 1)": 0, + "(2963, '2024-11-02', 2)": 1, + "(2963, '2024-11-02', 3)": 0, + "(2963, '2024-11-02', 4)": 0, + "(2963, '2024-11-02', 5)": 0, + "(2963, '2024-11-02', 6)": 0, + "(2963, '2024-11-02', 7)": 0, + "(2963, '2024-11-03', 0)": 0, + "(2963, '2024-11-03', 1)": 0, + "(2963, '2024-11-03', 2)": 1, + "(2963, '2024-11-03', 3)": 0, + "(2963, '2024-11-03', 4)": 0, + "(2963, '2024-11-03', 5)": 0, + "(2963, '2024-11-03', 6)": 0, + "(2963, '2024-11-03', 7)": 0, + "(2963, '2024-11-04', 0)": 0, + "(2963, '2024-11-04', 1)": 0, + "(2963, '2024-11-04', 2)": 0, + "(2963, '2024-11-04', 3)": 0, + "(2963, '2024-11-04', 4)": 0, + "(2963, '2024-11-04', 5)": 0, + "(2963, '2024-11-04', 6)": 0, + "(2963, '2024-11-04', 7)": 0, + "(2963, '2024-11-05', 0)": 1, + "(2963, '2024-11-05', 1)": 0, + "(2963, '2024-11-05', 2)": 0, + "(2963, '2024-11-05', 3)": 0, + "(2963, '2024-11-05', 4)": 0, + "(2963, '2024-11-05', 5)": 0, + "(2963, '2024-11-05', 6)": 0, + "(2963, '2024-11-05', 7)": 0, + "(2963, '2024-11-06', 0)": 1, + "(2963, '2024-11-06', 1)": 0, + "(2963, '2024-11-06', 2)": 0, + "(2963, '2024-11-06', 3)": 0, + "(2963, '2024-11-06', 4)": 0, + "(2963, '2024-11-06', 5)": 0, + "(2963, '2024-11-06', 6)": 0, + "(2963, '2024-11-06', 7)": 0, + "(2963, '2024-11-07', 0)": 0, + "(2963, '2024-11-07', 1)": 0, + "(2963, '2024-11-07', 2)": 0, + "(2963, '2024-11-07', 3)": 0, + "(2963, '2024-11-07', 4)": 0, + "(2963, '2024-11-07', 5)": 0, + "(2963, '2024-11-07', 6)": 0, + "(2963, '2024-11-07', 7)": 0, + "(2963, '2024-11-08', 0)": 0, + "(2963, '2024-11-08', 1)": 0, + "(2963, '2024-11-08', 2)": 0, + "(2963, '2024-11-08', 3)": 0, + "(2963, '2024-11-08', 4)": 0, + "(2963, '2024-11-08', 5)": 0, + "(2963, '2024-11-08', 6)": 0, + "(2963, '2024-11-08', 7)": 0, + "(2963, '2024-11-09', 0)": 0, + "(2963, '2024-11-09', 1)": 0, + "(2963, '2024-11-09', 2)": 0, + "(2963, '2024-11-09', 3)": 1, + "(2963, '2024-11-09', 4)": 0, + "(2963, '2024-11-09', 5)": 0, + "(2963, '2024-11-09', 6)": 0, + "(2963, '2024-11-09', 7)": 0, + "(2963, '2024-11-10', 0)": 0, + "(2963, '2024-11-10', 1)": 0, + "(2963, '2024-11-10', 2)": 0, + "(2963, '2024-11-10', 3)": 0, + "(2963, '2024-11-10', 4)": 0, + "(2963, '2024-11-10', 5)": 0, + "(2963, '2024-11-10', 6)": 0, + "(2963, '2024-11-10', 7)": 0, + "(2963, '2024-11-11', 0)": 0, + "(2963, '2024-11-11', 1)": 0, + "(2963, '2024-11-11', 2)": 0, + "(2963, '2024-11-11', 3)": 0, + "(2963, '2024-11-11', 4)": 0, + "(2963, '2024-11-11', 5)": 0, + "(2963, '2024-11-11', 6)": 0, + "(2963, '2024-11-11', 7)": 0, + "(2963, '2024-11-12', 0)": 0, + "(2963, '2024-11-12', 1)": 0, + "(2963, '2024-11-12', 2)": 0, + "(2963, '2024-11-12', 3)": 0, + "(2963, '2024-11-12', 4)": 0, + "(2963, '2024-11-12', 5)": 0, + "(2963, '2024-11-12', 6)": 0, + "(2963, '2024-11-12', 7)": 0, + "(2963, '2024-11-13', 0)": 0, + "(2963, '2024-11-13', 1)": 0, + "(2963, '2024-11-13', 2)": 0, + "(2963, '2024-11-13', 3)": 0, + "(2963, '2024-11-13', 4)": 0, + "(2963, '2024-11-13', 5)": 0, + "(2963, '2024-11-13', 6)": 0, + "(2963, '2024-11-13', 7)": 0, + "(2963, '2024-11-14', 0)": 1, + "(2963, '2024-11-14', 1)": 0, + "(2963, '2024-11-14', 2)": 0, + "(2963, '2024-11-14', 3)": 0, + "(2963, '2024-11-14', 4)": 0, + "(2963, '2024-11-14', 5)": 0, + "(2963, '2024-11-14', 6)": 0, + "(2963, '2024-11-14', 7)": 0, + "(2963, '2024-11-15', 0)": 0, + "(2963, '2024-11-15', 1)": 0, + "(2963, '2024-11-15', 2)": 0, + "(2963, '2024-11-15', 3)": 0, + "(2963, '2024-11-15', 4)": 0, + "(2963, '2024-11-15', 5)": 0, + "(2963, '2024-11-15', 6)": 0, + "(2963, '2024-11-15', 7)": 0, + "(2963, '2024-11-16', 0)": 0, + "(2963, '2024-11-16', 1)": 0, + "(2963, '2024-11-16', 2)": 0, + "(2963, '2024-11-16', 3)": 1, + "(2963, '2024-11-16', 4)": 0, + "(2963, '2024-11-16', 5)": 0, + "(2963, '2024-11-16', 6)": 0, + "(2963, '2024-11-16', 7)": 0, + "(2963, '2024-11-17', 0)": 0, + "(2963, '2024-11-17', 1)": 0, + "(2963, '2024-11-17', 2)": 0, + "(2963, '2024-11-17', 3)": 0, + "(2963, '2024-11-17', 4)": 0, + "(2963, '2024-11-17', 5)": 0, + "(2963, '2024-11-17', 6)": 0, + "(2963, '2024-11-17', 7)": 0, + "(2963, '2024-11-18', 0)": 1, + "(2963, '2024-11-18', 1)": 0, + "(2963, '2024-11-18', 2)": 0, + "(2963, '2024-11-18', 3)": 0, + "(2963, '2024-11-18', 4)": 0, + "(2963, '2024-11-18', 5)": 0, + "(2963, '2024-11-18', 6)": 0, + "(2963, '2024-11-18', 7)": 0, + "(2963, '2024-11-19', 0)": 1, + "(2963, '2024-11-19', 1)": 0, + "(2963, '2024-11-19', 2)": 0, + "(2963, '2024-11-19', 3)": 0, + "(2963, '2024-11-19', 4)": 0, + "(2963, '2024-11-19', 5)": 0, + "(2963, '2024-11-19', 6)": 0, + "(2963, '2024-11-19', 7)": 0, + "(2963, '2024-11-20', 0)": 1, + "(2963, '2024-11-20', 1)": 0, + "(2963, '2024-11-20', 2)": 0, + "(2963, '2024-11-20', 3)": 0, + "(2963, '2024-11-20', 4)": 0, + "(2963, '2024-11-20', 5)": 0, + "(2963, '2024-11-20', 6)": 0, + "(2963, '2024-11-20', 7)": 0, + "(2963, '2024-11-21', 0)": 1, + "(2963, '2024-11-21', 1)": 0, + "(2963, '2024-11-21', 2)": 0, + "(2963, '2024-11-21', 3)": 0, + "(2963, '2024-11-21', 4)": 0, + "(2963, '2024-11-21', 5)": 0, + "(2963, '2024-11-21', 6)": 0, + "(2963, '2024-11-21', 7)": 0, + "(2963, '2024-11-22', 0)": 1, + "(2963, '2024-11-22', 1)": 0, + "(2963, '2024-11-22', 2)": 0, + "(2963, '2024-11-22', 3)": 0, + "(2963, '2024-11-22', 4)": 0, + "(2963, '2024-11-22', 5)": 0, + "(2963, '2024-11-22', 6)": 0, + "(2963, '2024-11-22', 7)": 0, + "(2963, '2024-11-23', 0)": 1, + "(2963, '2024-11-23', 1)": 0, + "(2963, '2024-11-23', 2)": 0, + "(2963, '2024-11-23', 3)": 0, + "(2963, '2024-11-23', 4)": 0, + "(2963, '2024-11-23', 5)": 0, + "(2963, '2024-11-23', 6)": 0, + "(2963, '2024-11-23', 7)": 0, + "(2963, '2024-11-24', 0)": 1, + "(2963, '2024-11-24', 1)": 0, + "(2963, '2024-11-24', 2)": 0, + "(2963, '2024-11-24', 3)": 0, + "(2963, '2024-11-24', 4)": 0, + "(2963, '2024-11-24', 5)": 0, + "(2963, '2024-11-24', 6)": 0, + "(2963, '2024-11-24', 7)": 0, + "(2963, '2024-11-25', 0)": 1, + "(2963, '2024-11-25', 1)": 0, + "(2963, '2024-11-25', 2)": 0, + "(2963, '2024-11-25', 3)": 0, + "(2963, '2024-11-25', 4)": 0, + "(2963, '2024-11-25', 5)": 0, + "(2963, '2024-11-25', 6)": 0, + "(2963, '2024-11-25', 7)": 0, + "(2963, '2024-11-26', 0)": 0, + "(2963, '2024-11-26', 1)": 0, + "(2963, '2024-11-26', 2)": 0, + "(2963, '2024-11-26', 3)": 0, + "(2963, '2024-11-26', 4)": 0, + "(2963, '2024-11-26', 5)": 0, + "(2963, '2024-11-26', 6)": 0, + "(2963, '2024-11-26', 7)": 0, + "(2963, '2024-11-27', 0)": 1, + "(2963, '2024-11-27', 1)": 0, + "(2963, '2024-11-27', 2)": 0, + "(2963, '2024-11-27', 3)": 0, + "(2963, '2024-11-27', 4)": 0, + "(2963, '2024-11-27', 5)": 0, + "(2963, '2024-11-27', 6)": 0, + "(2963, '2024-11-27', 7)": 0, + "(2963, '2024-11-28', 0)": 1, + "(2963, '2024-11-28', 1)": 0, + "(2963, '2024-11-28', 2)": 0, + "(2963, '2024-11-28', 3)": 0, + "(2963, '2024-11-28', 4)": 0, + "(2963, '2024-11-28', 5)": 0, + "(2963, '2024-11-28', 6)": 0, + "(2963, '2024-11-28', 7)": 0, + "(2963, '2024-11-29', 0)": 1, + "(2963, '2024-11-29', 1)": 0, + "(2963, '2024-11-29', 2)": 0, + "(2963, '2024-11-29', 3)": 0, + "(2963, '2024-11-29', 4)": 0, + "(2963, '2024-11-29', 5)": 0, + "(2963, '2024-11-29', 6)": 0, + "(2963, '2024-11-29', 7)": 0, + "(2963, '2024-11-30', 0)": 0, + "(2963, '2024-11-30', 1)": 0, + "(2963, '2024-11-30', 2)": 0, + "(2963, '2024-11-30', 3)": 0, + "(2963, '2024-11-30', 4)": 0, + "(2963, '2024-11-30', 5)": 0, + "(2963, '2024-11-30', 6)": 0, + "(2963, '2024-11-30', 7)": 0, + "(3, '2024-11-01', 0)": 0, + "(3, '2024-11-01', 1)": 0, + "(3, '2024-11-01', 2)": 0, + "(3, '2024-11-01', 3)": 0, + "(3, '2024-11-01', 4)": 0, + "(3, '2024-11-01', 5)": 0, + "(3, '2024-11-01', 6)": 0, + "(3, '2024-11-01', 7)": 0, + "(3, '2024-11-02', 0)": 0, + "(3, '2024-11-02', 1)": 0, + "(3, '2024-11-02', 2)": 0, + "(3, '2024-11-02', 3)": 0, + "(3, '2024-11-02', 4)": 0, + "(3, '2024-11-02', 5)": 0, + "(3, '2024-11-02', 6)": 0, + "(3, '2024-11-02', 7)": 0, + "(3, '2024-11-03', 0)": 0, + "(3, '2024-11-03', 1)": 0, + "(3, '2024-11-03', 2)": 0, + "(3, '2024-11-03', 3)": 0, + "(3, '2024-11-03', 4)": 0, + "(3, '2024-11-03', 5)": 0, + "(3, '2024-11-03', 6)": 0, + "(3, '2024-11-03', 7)": 0, + "(3, '2024-11-04', 0)": 0, + "(3, '2024-11-04', 1)": 0, + "(3, '2024-11-04', 2)": 0, + "(3, '2024-11-04', 3)": 0, + "(3, '2024-11-04', 4)": 0, + "(3, '2024-11-04', 5)": 0, + "(3, '2024-11-04', 6)": 0, + "(3, '2024-11-04', 7)": 0, + "(3, '2024-11-05', 0)": 0, + "(3, '2024-11-05', 1)": 0, + "(3, '2024-11-05', 2)": 0, + "(3, '2024-11-05', 3)": 0, + "(3, '2024-11-05', 4)": 0, + "(3, '2024-11-05', 5)": 0, + "(3, '2024-11-05', 6)": 0, + "(3, '2024-11-05', 7)": 0, + "(3, '2024-11-06', 0)": 0, + "(3, '2024-11-06', 1)": 0, + "(3, '2024-11-06', 2)": 0, + "(3, '2024-11-06', 3)": 0, + "(3, '2024-11-06', 4)": 0, + "(3, '2024-11-06', 5)": 0, + "(3, '2024-11-06', 6)": 0, + "(3, '2024-11-06', 7)": 0, + "(3, '2024-11-07', 0)": 0, + "(3, '2024-11-07', 1)": 0, + "(3, '2024-11-07', 2)": 0, + "(3, '2024-11-07', 3)": 0, + "(3, '2024-11-07', 4)": 0, + "(3, '2024-11-07', 5)": 0, + "(3, '2024-11-07', 6)": 0, + "(3, '2024-11-07', 7)": 0, + "(3, '2024-11-08', 0)": 0, + "(3, '2024-11-08', 1)": 0, + "(3, '2024-11-08', 2)": 0, + "(3, '2024-11-08', 3)": 0, + "(3, '2024-11-08', 4)": 0, + "(3, '2024-11-08', 5)": 0, + "(3, '2024-11-08', 6)": 0, + "(3, '2024-11-08', 7)": 0, + "(3, '2024-11-09', 0)": 0, + "(3, '2024-11-09', 1)": 0, + "(3, '2024-11-09', 2)": 0, + "(3, '2024-11-09', 3)": 0, + "(3, '2024-11-09', 4)": 0, + "(3, '2024-11-09', 5)": 0, + "(3, '2024-11-09', 6)": 0, + "(3, '2024-11-09', 7)": 0, + "(3, '2024-11-10', 0)": 0, + "(3, '2024-11-10', 1)": 0, + "(3, '2024-11-10', 2)": 0, + "(3, '2024-11-10', 3)": 0, + "(3, '2024-11-10', 4)": 0, + "(3, '2024-11-10', 5)": 0, + "(3, '2024-11-10', 6)": 0, + "(3, '2024-11-10', 7)": 0, + "(3, '2024-11-11', 0)": 0, + "(3, '2024-11-11', 1)": 0, + "(3, '2024-11-11', 2)": 0, + "(3, '2024-11-11', 3)": 0, + "(3, '2024-11-11', 4)": 0, + "(3, '2024-11-11', 5)": 0, + "(3, '2024-11-11', 6)": 0, + "(3, '2024-11-11', 7)": 0, + "(3, '2024-11-12', 0)": 0, + "(3, '2024-11-12', 1)": 0, + "(3, '2024-11-12', 2)": 0, + "(3, '2024-11-12', 3)": 0, + "(3, '2024-11-12', 4)": 0, + "(3, '2024-11-12', 5)": 0, + "(3, '2024-11-12', 6)": 0, + "(3, '2024-11-12', 7)": 0, + "(3, '2024-11-13', 0)": 0, + "(3, '2024-11-13', 1)": 0, + "(3, '2024-11-13', 2)": 0, + "(3, '2024-11-13', 3)": 0, + "(3, '2024-11-13', 4)": 0, + "(3, '2024-11-13', 5)": 0, + "(3, '2024-11-13', 6)": 0, + "(3, '2024-11-13', 7)": 0, + "(3, '2024-11-14', 0)": 0, + "(3, '2024-11-14', 1)": 0, + "(3, '2024-11-14', 2)": 0, + "(3, '2024-11-14', 3)": 0, + "(3, '2024-11-14', 4)": 0, + "(3, '2024-11-14', 5)": 0, + "(3, '2024-11-14', 6)": 0, + "(3, '2024-11-14', 7)": 0, + "(3, '2024-11-15', 0)": 0, + "(3, '2024-11-15', 1)": 0, + "(3, '2024-11-15', 2)": 0, + "(3, '2024-11-15', 3)": 0, + "(3, '2024-11-15', 4)": 0, + "(3, '2024-11-15', 5)": 0, + "(3, '2024-11-15', 6)": 0, + "(3, '2024-11-15', 7)": 0, + "(3, '2024-11-16', 0)": 0, + "(3, '2024-11-16', 1)": 0, + "(3, '2024-11-16', 2)": 0, + "(3, '2024-11-16', 3)": 0, + "(3, '2024-11-16', 4)": 0, + "(3, '2024-11-16', 5)": 0, + "(3, '2024-11-16', 6)": 0, + "(3, '2024-11-16', 7)": 0, + "(3, '2024-11-17', 0)": 0, + "(3, '2024-11-17', 1)": 0, + "(3, '2024-11-17', 2)": 0, + "(3, '2024-11-17', 3)": 0, + "(3, '2024-11-17', 4)": 0, + "(3, '2024-11-17', 5)": 0, + "(3, '2024-11-17', 6)": 0, + "(3, '2024-11-17', 7)": 0, + "(3, '2024-11-18', 0)": 0, + "(3, '2024-11-18', 1)": 0, + "(3, '2024-11-18', 2)": 0, + "(3, '2024-11-18', 3)": 0, + "(3, '2024-11-18', 4)": 0, + "(3, '2024-11-18', 5)": 0, + "(3, '2024-11-18', 6)": 0, + "(3, '2024-11-18', 7)": 0, + "(3, '2024-11-19', 0)": 0, + "(3, '2024-11-19', 1)": 0, + "(3, '2024-11-19', 2)": 0, + "(3, '2024-11-19', 3)": 0, + "(3, '2024-11-19', 4)": 0, + "(3, '2024-11-19', 5)": 0, + "(3, '2024-11-19', 6)": 0, + "(3, '2024-11-19', 7)": 0, + "(3, '2024-11-20', 0)": 0, + "(3, '2024-11-20', 1)": 0, + "(3, '2024-11-20', 2)": 0, + "(3, '2024-11-20', 3)": 0, + "(3, '2024-11-20', 4)": 0, + "(3, '2024-11-20', 5)": 0, + "(3, '2024-11-20', 6)": 0, + "(3, '2024-11-20', 7)": 0, + "(3, '2024-11-21', 0)": 0, + "(3, '2024-11-21', 1)": 0, + "(3, '2024-11-21', 2)": 0, + "(3, '2024-11-21', 3)": 0, + "(3, '2024-11-21', 4)": 0, + "(3, '2024-11-21', 5)": 0, + "(3, '2024-11-21', 6)": 0, + "(3, '2024-11-21', 7)": 0, + "(3, '2024-11-22', 0)": 0, + "(3, '2024-11-22', 1)": 0, + "(3, '2024-11-22', 2)": 0, + "(3, '2024-11-22', 3)": 0, + "(3, '2024-11-22', 4)": 0, + "(3, '2024-11-22', 5)": 0, + "(3, '2024-11-22', 6)": 0, + "(3, '2024-11-22', 7)": 0, + "(3, '2024-11-23', 0)": 0, + "(3, '2024-11-23', 1)": 0, + "(3, '2024-11-23', 2)": 0, + "(3, '2024-11-23', 3)": 0, + "(3, '2024-11-23', 4)": 0, + "(3, '2024-11-23', 5)": 0, + "(3, '2024-11-23', 6)": 0, + "(3, '2024-11-23', 7)": 0, + "(3, '2024-11-24', 0)": 0, + "(3, '2024-11-24', 1)": 0, + "(3, '2024-11-24', 2)": 0, + "(3, '2024-11-24', 3)": 0, + "(3, '2024-11-24', 4)": 0, + "(3, '2024-11-24', 5)": 0, + "(3, '2024-11-24', 6)": 0, + "(3, '2024-11-24', 7)": 0, + "(3, '2024-11-25', 0)": 0, + "(3, '2024-11-25', 1)": 0, + "(3, '2024-11-25', 2)": 0, + "(3, '2024-11-25', 3)": 0, + "(3, '2024-11-25', 4)": 0, + "(3, '2024-11-25', 5)": 0, + "(3, '2024-11-25', 6)": 0, + "(3, '2024-11-25', 7)": 0, + "(3, '2024-11-26', 0)": 0, + "(3, '2024-11-26', 1)": 0, + "(3, '2024-11-26', 2)": 0, + "(3, '2024-11-26', 3)": 0, + "(3, '2024-11-26', 4)": 0, + "(3, '2024-11-26', 5)": 0, + "(3, '2024-11-26', 6)": 0, + "(3, '2024-11-26', 7)": 0, + "(3, '2024-11-27', 0)": 0, + "(3, '2024-11-27', 1)": 0, + "(3, '2024-11-27', 2)": 0, + "(3, '2024-11-27', 3)": 0, + "(3, '2024-11-27', 4)": 0, + "(3, '2024-11-27', 5)": 0, + "(3, '2024-11-27', 6)": 0, + "(3, '2024-11-27', 7)": 0, + "(3, '2024-11-28', 0)": 0, + "(3, '2024-11-28', 1)": 0, + "(3, '2024-11-28', 2)": 0, + "(3, '2024-11-28', 3)": 0, + "(3, '2024-11-28', 4)": 0, + "(3, '2024-11-28', 5)": 0, + "(3, '2024-11-28', 6)": 0, + "(3, '2024-11-28', 7)": 0, + "(3, '2024-11-29', 0)": 0, + "(3, '2024-11-29', 1)": 0, + "(3, '2024-11-29', 2)": 0, + "(3, '2024-11-29', 3)": 0, + "(3, '2024-11-29', 4)": 0, + "(3, '2024-11-29', 5)": 0, + "(3, '2024-11-29', 6)": 0, + "(3, '2024-11-29', 7)": 0, + "(3, '2024-11-30', 0)": 0, + "(3, '2024-11-30', 1)": 0, + "(3, '2024-11-30', 2)": 0, + "(3, '2024-11-30', 3)": 0, + "(3, '2024-11-30', 4)": 0, + "(3, '2024-11-30', 5)": 0, + "(3, '2024-11-30', 6)": 0, + "(3, '2024-11-30', 7)": 0, + "(3566, '2024-11-01', 0)": 0, + "(3566, '2024-11-01', 1)": 0, + "(3566, '2024-11-01', 2)": 0, + "(3566, '2024-11-01', 3)": 0, + "(3566, '2024-11-01', 4)": 0, + "(3566, '2024-11-01', 5)": 0, + "(3566, '2024-11-01', 6)": 0, + "(3566, '2024-11-01', 7)": 0, + "(3566, '2024-11-02', 0)": 0, + "(3566, '2024-11-02', 1)": 0, + "(3566, '2024-11-02', 2)": 0, + "(3566, '2024-11-02', 3)": 0, + "(3566, '2024-11-02', 4)": 0, + "(3566, '2024-11-02', 5)": 0, + "(3566, '2024-11-02', 6)": 0, + "(3566, '2024-11-02', 7)": 0, + "(3566, '2024-11-03', 0)": 0, + "(3566, '2024-11-03', 1)": 0, + "(3566, '2024-11-03', 2)": 0, + "(3566, '2024-11-03', 3)": 0, + "(3566, '2024-11-03', 4)": 0, + "(3566, '2024-11-03', 5)": 0, + "(3566, '2024-11-03', 6)": 0, + "(3566, '2024-11-03', 7)": 0, + "(3566, '2024-11-04', 0)": 0, + "(3566, '2024-11-04', 1)": 0, + "(3566, '2024-11-04', 2)": 0, + "(3566, '2024-11-04', 3)": 0, + "(3566, '2024-11-04', 4)": 0, + "(3566, '2024-11-04', 5)": 0, + "(3566, '2024-11-04', 6)": 0, + "(3566, '2024-11-04', 7)": 0, + "(3566, '2024-11-05', 0)": 0, + "(3566, '2024-11-05', 1)": 0, + "(3566, '2024-11-05', 2)": 0, + "(3566, '2024-11-05', 3)": 0, + "(3566, '2024-11-05', 4)": 0, + "(3566, '2024-11-05', 5)": 0, + "(3566, '2024-11-05', 6)": 0, + "(3566, '2024-11-05', 7)": 0, + "(3566, '2024-11-06', 0)": 0, + "(3566, '2024-11-06', 1)": 0, + "(3566, '2024-11-06', 2)": 0, + "(3566, '2024-11-06', 3)": 0, + "(3566, '2024-11-06', 4)": 0, + "(3566, '2024-11-06', 5)": 0, + "(3566, '2024-11-06', 6)": 0, + "(3566, '2024-11-06', 7)": 0, + "(3566, '2024-11-07', 0)": 0, + "(3566, '2024-11-07', 1)": 0, + "(3566, '2024-11-07', 2)": 0, + "(3566, '2024-11-07', 3)": 0, + "(3566, '2024-11-07', 4)": 0, + "(3566, '2024-11-07', 5)": 0, + "(3566, '2024-11-07', 6)": 0, + "(3566, '2024-11-07', 7)": 0, + "(3566, '2024-11-08', 0)": 0, + "(3566, '2024-11-08', 1)": 0, + "(3566, '2024-11-08', 2)": 0, + "(3566, '2024-11-08', 3)": 0, + "(3566, '2024-11-08', 4)": 0, + "(3566, '2024-11-08', 5)": 0, + "(3566, '2024-11-08', 6)": 0, + "(3566, '2024-11-08', 7)": 0, + "(3566, '2024-11-09', 0)": 0, + "(3566, '2024-11-09', 1)": 0, + "(3566, '2024-11-09', 2)": 0, + "(3566, '2024-11-09', 3)": 0, + "(3566, '2024-11-09', 4)": 0, + "(3566, '2024-11-09', 5)": 0, + "(3566, '2024-11-09', 6)": 0, + "(3566, '2024-11-09', 7)": 0, + "(3566, '2024-11-10', 0)": 0, + "(3566, '2024-11-10', 1)": 0, + "(3566, '2024-11-10', 2)": 0, + "(3566, '2024-11-10', 3)": 0, + "(3566, '2024-11-10', 4)": 0, + "(3566, '2024-11-10', 5)": 0, + "(3566, '2024-11-10', 6)": 0, + "(3566, '2024-11-10', 7)": 0, + "(3566, '2024-11-11', 0)": 0, + "(3566, '2024-11-11', 1)": 0, + "(3566, '2024-11-11', 2)": 0, + "(3566, '2024-11-11', 3)": 0, + "(3566, '2024-11-11', 4)": 0, + "(3566, '2024-11-11', 5)": 0, + "(3566, '2024-11-11', 6)": 0, + "(3566, '2024-11-11', 7)": 0, + "(3566, '2024-11-12', 0)": 0, + "(3566, '2024-11-12', 1)": 0, + "(3566, '2024-11-12', 2)": 0, + "(3566, '2024-11-12', 3)": 0, + "(3566, '2024-11-12', 4)": 0, + "(3566, '2024-11-12', 5)": 0, + "(3566, '2024-11-12', 6)": 0, + "(3566, '2024-11-12', 7)": 0, + "(3566, '2024-11-13', 0)": 0, + "(3566, '2024-11-13', 1)": 0, + "(3566, '2024-11-13', 2)": 0, + "(3566, '2024-11-13', 3)": 0, + "(3566, '2024-11-13', 4)": 0, + "(3566, '2024-11-13', 5)": 0, + "(3566, '2024-11-13', 6)": 0, + "(3566, '2024-11-13', 7)": 0, + "(3566, '2024-11-14', 0)": 0, + "(3566, '2024-11-14', 1)": 0, + "(3566, '2024-11-14', 2)": 0, + "(3566, '2024-11-14', 3)": 0, + "(3566, '2024-11-14', 4)": 0, + "(3566, '2024-11-14', 5)": 0, + "(3566, '2024-11-14', 6)": 0, + "(3566, '2024-11-14', 7)": 0, + "(3566, '2024-11-15', 0)": 0, + "(3566, '2024-11-15', 1)": 0, + "(3566, '2024-11-15', 2)": 0, + "(3566, '2024-11-15', 3)": 0, + "(3566, '2024-11-15', 4)": 0, + "(3566, '2024-11-15', 5)": 0, + "(3566, '2024-11-15', 6)": 0, + "(3566, '2024-11-15', 7)": 0, + "(3566, '2024-11-16', 0)": 0, + "(3566, '2024-11-16', 1)": 0, + "(3566, '2024-11-16', 2)": 0, + "(3566, '2024-11-16', 3)": 0, + "(3566, '2024-11-16', 4)": 0, + "(3566, '2024-11-16', 5)": 0, + "(3566, '2024-11-16', 6)": 0, + "(3566, '2024-11-16', 7)": 0, + "(3566, '2024-11-17', 0)": 0, + "(3566, '2024-11-17', 1)": 0, + "(3566, '2024-11-17', 2)": 0, + "(3566, '2024-11-17', 3)": 0, + "(3566, '2024-11-17', 4)": 0, + "(3566, '2024-11-17', 5)": 0, + "(3566, '2024-11-17', 6)": 0, + "(3566, '2024-11-17', 7)": 0, + "(3566, '2024-11-18', 0)": 0, + "(3566, '2024-11-18', 1)": 0, + "(3566, '2024-11-18', 2)": 0, + "(3566, '2024-11-18', 3)": 0, + "(3566, '2024-11-18', 4)": 0, + "(3566, '2024-11-18', 5)": 0, + "(3566, '2024-11-18', 6)": 0, + "(3566, '2024-11-18', 7)": 0, + "(3566, '2024-11-19', 0)": 0, + "(3566, '2024-11-19', 1)": 0, + "(3566, '2024-11-19', 2)": 0, + "(3566, '2024-11-19', 3)": 0, + "(3566, '2024-11-19', 4)": 0, + "(3566, '2024-11-19', 5)": 0, + "(3566, '2024-11-19', 6)": 0, + "(3566, '2024-11-19', 7)": 0, + "(3566, '2024-11-20', 0)": 0, + "(3566, '2024-11-20', 1)": 0, + "(3566, '2024-11-20', 2)": 0, + "(3566, '2024-11-20', 3)": 0, + "(3566, '2024-11-20', 4)": 0, + "(3566, '2024-11-20', 5)": 0, + "(3566, '2024-11-20', 6)": 0, + "(3566, '2024-11-20', 7)": 0, + "(3566, '2024-11-21', 0)": 0, + "(3566, '2024-11-21', 1)": 0, + "(3566, '2024-11-21', 2)": 0, + "(3566, '2024-11-21', 3)": 0, + "(3566, '2024-11-21', 4)": 0, + "(3566, '2024-11-21', 5)": 0, + "(3566, '2024-11-21', 6)": 0, + "(3566, '2024-11-21', 7)": 0, + "(3566, '2024-11-22', 0)": 0, + "(3566, '2024-11-22', 1)": 0, + "(3566, '2024-11-22', 2)": 0, + "(3566, '2024-11-22', 3)": 0, + "(3566, '2024-11-22', 4)": 0, + "(3566, '2024-11-22', 5)": 0, + "(3566, '2024-11-22', 6)": 0, + "(3566, '2024-11-22', 7)": 0, + "(3566, '2024-11-23', 0)": 0, + "(3566, '2024-11-23', 1)": 0, + "(3566, '2024-11-23', 2)": 0, + "(3566, '2024-11-23', 3)": 0, + "(3566, '2024-11-23', 4)": 0, + "(3566, '2024-11-23', 5)": 0, + "(3566, '2024-11-23', 6)": 0, + "(3566, '2024-11-23', 7)": 0, + "(3566, '2024-11-24', 0)": 0, + "(3566, '2024-11-24', 1)": 0, + "(3566, '2024-11-24', 2)": 0, + "(3566, '2024-11-24', 3)": 0, + "(3566, '2024-11-24', 4)": 0, + "(3566, '2024-11-24', 5)": 0, + "(3566, '2024-11-24', 6)": 0, + "(3566, '2024-11-24', 7)": 0, + "(3566, '2024-11-25', 0)": 0, + "(3566, '2024-11-25', 1)": 0, + "(3566, '2024-11-25', 2)": 0, + "(3566, '2024-11-25', 3)": 0, + "(3566, '2024-11-25', 4)": 0, + "(3566, '2024-11-25', 5)": 0, + "(3566, '2024-11-25', 6)": 0, + "(3566, '2024-11-25', 7)": 0, + "(3566, '2024-11-26', 0)": 0, + "(3566, '2024-11-26', 1)": 0, + "(3566, '2024-11-26', 2)": 0, + "(3566, '2024-11-26', 3)": 0, + "(3566, '2024-11-26', 4)": 0, + "(3566, '2024-11-26', 5)": 0, + "(3566, '2024-11-26', 6)": 0, + "(3566, '2024-11-26', 7)": 0, + "(3566, '2024-11-27', 0)": 0, + "(3566, '2024-11-27', 1)": 0, + "(3566, '2024-11-27', 2)": 0, + "(3566, '2024-11-27', 3)": 0, + "(3566, '2024-11-27', 4)": 0, + "(3566, '2024-11-27', 5)": 0, + "(3566, '2024-11-27', 6)": 0, + "(3566, '2024-11-27', 7)": 0, + "(3566, '2024-11-28', 0)": 0, + "(3566, '2024-11-28', 1)": 0, + "(3566, '2024-11-28', 2)": 0, + "(3566, '2024-11-28', 3)": 0, + "(3566, '2024-11-28', 4)": 0, + "(3566, '2024-11-28', 5)": 0, + "(3566, '2024-11-28', 6)": 0, + "(3566, '2024-11-28', 7)": 0, + "(3566, '2024-11-29', 0)": 0, + "(3566, '2024-11-29', 1)": 0, + "(3566, '2024-11-29', 2)": 0, + "(3566, '2024-11-29', 3)": 0, + "(3566, '2024-11-29', 4)": 0, + "(3566, '2024-11-29', 5)": 0, + "(3566, '2024-11-29', 6)": 0, + "(3566, '2024-11-29', 7)": 0, + "(3566, '2024-11-30', 0)": 0, + "(3566, '2024-11-30', 1)": 0, + "(3566, '2024-11-30', 2)": 0, + "(3566, '2024-11-30', 3)": 0, + "(3566, '2024-11-30', 4)": 0, + "(3566, '2024-11-30', 5)": 0, + "(3566, '2024-11-30', 6)": 0, + "(3566, '2024-11-30', 7)": 0, + "(3868, '2024-11-01', 0)": 0, + "(3868, '2024-11-01', 1)": 0, + "(3868, '2024-11-01', 2)": 0, + "(3868, '2024-11-01', 3)": 0, + "(3868, '2024-11-01', 4)": 0, + "(3868, '2024-11-01', 5)": 0, + "(3868, '2024-11-01', 6)": 0, + "(3868, '2024-11-01', 7)": 0, + "(3868, '2024-11-02', 0)": 0, + "(3868, '2024-11-02', 1)": 0, + "(3868, '2024-11-02', 2)": 0, + "(3868, '2024-11-02', 3)": 0, + "(3868, '2024-11-02', 4)": 0, + "(3868, '2024-11-02', 5)": 0, + "(3868, '2024-11-02', 6)": 0, + "(3868, '2024-11-02', 7)": 0, + "(3868, '2024-11-03', 0)": 0, + "(3868, '2024-11-03', 1)": 0, + "(3868, '2024-11-03', 2)": 0, + "(3868, '2024-11-03', 3)": 0, + "(3868, '2024-11-03', 4)": 0, + "(3868, '2024-11-03', 5)": 0, + "(3868, '2024-11-03', 6)": 0, + "(3868, '2024-11-03', 7)": 0, + "(3868, '2024-11-04', 0)": 1, + "(3868, '2024-11-04', 1)": 0, + "(3868, '2024-11-04', 2)": 0, + "(3868, '2024-11-04', 3)": 0, + "(3868, '2024-11-04', 4)": 0, + "(3868, '2024-11-04', 5)": 0, + "(3868, '2024-11-04', 6)": 0, + "(3868, '2024-11-04', 7)": 0, + "(3868, '2024-11-05', 0)": 0, + "(3868, '2024-11-05', 1)": 0, + "(3868, '2024-11-05', 2)": 1, + "(3868, '2024-11-05', 3)": 0, + "(3868, '2024-11-05', 4)": 0, + "(3868, '2024-11-05', 5)": 0, + "(3868, '2024-11-05', 6)": 0, + "(3868, '2024-11-05', 7)": 0, + "(3868, '2024-11-06', 0)": 0, + "(3868, '2024-11-06', 1)": 0, + "(3868, '2024-11-06', 2)": 0, + "(3868, '2024-11-06', 3)": 0, + "(3868, '2024-11-06', 4)": 0, + "(3868, '2024-11-06', 5)": 0, + "(3868, '2024-11-06', 6)": 0, + "(3868, '2024-11-06', 7)": 0, + "(3868, '2024-11-07', 0)": 0, + "(3868, '2024-11-07', 1)": 0, + "(3868, '2024-11-07', 2)": 0, + "(3868, '2024-11-07', 3)": 0, + "(3868, '2024-11-07', 4)": 0, + "(3868, '2024-11-07', 5)": 0, + "(3868, '2024-11-07', 6)": 0, + "(3868, '2024-11-07', 7)": 0, + "(3868, '2024-11-08', 0)": 1, + "(3868, '2024-11-08', 1)": 0, + "(3868, '2024-11-08', 2)": 0, + "(3868, '2024-11-08', 3)": 0, + "(3868, '2024-11-08', 4)": 0, + "(3868, '2024-11-08', 5)": 0, + "(3868, '2024-11-08', 6)": 0, + "(3868, '2024-11-08', 7)": 0, + "(3868, '2024-11-09', 0)": 0, + "(3868, '2024-11-09', 1)": 0, + "(3868, '2024-11-09', 2)": 0, + "(3868, '2024-11-09', 3)": 0, + "(3868, '2024-11-09', 4)": 0, + "(3868, '2024-11-09', 5)": 0, + "(3868, '2024-11-09', 6)": 0, + "(3868, '2024-11-09', 7)": 0, + "(3868, '2024-11-10', 0)": 1, + "(3868, '2024-11-10', 1)": 0, + "(3868, '2024-11-10', 2)": 0, + "(3868, '2024-11-10', 3)": 0, + "(3868, '2024-11-10', 4)": 0, + "(3868, '2024-11-10', 5)": 0, + "(3868, '2024-11-10', 6)": 0, + "(3868, '2024-11-10', 7)": 0, + "(3868, '2024-11-11', 0)": 1, + "(3868, '2024-11-11', 1)": 0, + "(3868, '2024-11-11', 2)": 0, + "(3868, '2024-11-11', 3)": 0, + "(3868, '2024-11-11', 4)": 0, + "(3868, '2024-11-11', 5)": 0, + "(3868, '2024-11-11', 6)": 0, + "(3868, '2024-11-11', 7)": 0, + "(3868, '2024-11-12', 0)": 0, + "(3868, '2024-11-12', 1)": 0, + "(3868, '2024-11-12', 2)": 1, + "(3868, '2024-11-12', 3)": 0, + "(3868, '2024-11-12', 4)": 0, + "(3868, '2024-11-12', 5)": 0, + "(3868, '2024-11-12', 6)": 0, + "(3868, '2024-11-12', 7)": 0, + "(3868, '2024-11-13', 0)": 0, + "(3868, '2024-11-13', 1)": 0, + "(3868, '2024-11-13', 2)": 0, + "(3868, '2024-11-13', 3)": 1, + "(3868, '2024-11-13', 4)": 0, + "(3868, '2024-11-13', 5)": 0, + "(3868, '2024-11-13', 6)": 0, + "(3868, '2024-11-13', 7)": 0, + "(3868, '2024-11-14', 0)": 0, + "(3868, '2024-11-14', 1)": 0, + "(3868, '2024-11-14', 2)": 0, + "(3868, '2024-11-14', 3)": 0, + "(3868, '2024-11-14', 4)": 0, + "(3868, '2024-11-14', 5)": 0, + "(3868, '2024-11-14', 6)": 0, + "(3868, '2024-11-14', 7)": 0, + "(3868, '2024-11-15', 0)": 0, + "(3868, '2024-11-15', 1)": 0, + "(3868, '2024-11-15', 2)": 0, + "(3868, '2024-11-15', 3)": 1, + "(3868, '2024-11-15', 4)": 0, + "(3868, '2024-11-15', 5)": 0, + "(3868, '2024-11-15', 6)": 0, + "(3868, '2024-11-15', 7)": 0, + "(3868, '2024-11-16', 0)": 0, + "(3868, '2024-11-16', 1)": 0, + "(3868, '2024-11-16', 2)": 0, + "(3868, '2024-11-16', 3)": 0, + "(3868, '2024-11-16', 4)": 0, + "(3868, '2024-11-16', 5)": 0, + "(3868, '2024-11-16', 6)": 0, + "(3868, '2024-11-16', 7)": 0, + "(3868, '2024-11-17', 0)": 0, + "(3868, '2024-11-17', 1)": 0, + "(3868, '2024-11-17', 2)": 0, + "(3868, '2024-11-17', 3)": 1, + "(3868, '2024-11-17', 4)": 0, + "(3868, '2024-11-17', 5)": 0, + "(3868, '2024-11-17', 6)": 0, + "(3868, '2024-11-17', 7)": 0, + "(3868, '2024-11-18', 0)": 0, + "(3868, '2024-11-18', 1)": 0, + "(3868, '2024-11-18', 2)": 0, + "(3868, '2024-11-18', 3)": 1, + "(3868, '2024-11-18', 4)": 0, + "(3868, '2024-11-18', 5)": 0, + "(3868, '2024-11-18', 6)": 0, + "(3868, '2024-11-18', 7)": 0, + "(3868, '2024-11-19', 0)": 0, + "(3868, '2024-11-19', 1)": 0, + "(3868, '2024-11-19', 2)": 0, + "(3868, '2024-11-19', 3)": 0, + "(3868, '2024-11-19', 4)": 0, + "(3868, '2024-11-19', 5)": 0, + "(3868, '2024-11-19', 6)": 0, + "(3868, '2024-11-19', 7)": 0, + "(3868, '2024-11-20', 0)": 0, + "(3868, '2024-11-20', 1)": 0, + "(3868, '2024-11-20', 2)": 0, + "(3868, '2024-11-20', 3)": 0, + "(3868, '2024-11-20', 4)": 0, + "(3868, '2024-11-20', 5)": 0, + "(3868, '2024-11-20', 6)": 0, + "(3868, '2024-11-20', 7)": 0, + "(3868, '2024-11-21', 0)": 1, + "(3868, '2024-11-21', 1)": 0, + "(3868, '2024-11-21', 2)": 0, + "(3868, '2024-11-21', 3)": 0, + "(3868, '2024-11-21', 4)": 0, + "(3868, '2024-11-21', 5)": 0, + "(3868, '2024-11-21', 6)": 0, + "(3868, '2024-11-21', 7)": 0, + "(3868, '2024-11-22', 0)": 0, + "(3868, '2024-11-22', 1)": 1, + "(3868, '2024-11-22', 2)": 0, + "(3868, '2024-11-22', 3)": 0, + "(3868, '2024-11-22', 4)": 0, + "(3868, '2024-11-22', 5)": 0, + "(3868, '2024-11-22', 6)": 0, + "(3868, '2024-11-22', 7)": 0, + "(3868, '2024-11-23', 0)": 1, + "(3868, '2024-11-23', 1)": 0, + "(3868, '2024-11-23', 2)": 0, + "(3868, '2024-11-23', 3)": 0, + "(3868, '2024-11-23', 4)": 0, + "(3868, '2024-11-23', 5)": 0, + "(3868, '2024-11-23', 6)": 0, + "(3868, '2024-11-23', 7)": 0, + "(3868, '2024-11-24', 0)": 0, + "(3868, '2024-11-24', 1)": 1, + "(3868, '2024-11-24', 2)": 0, + "(3868, '2024-11-24', 3)": 0, + "(3868, '2024-11-24', 4)": 0, + "(3868, '2024-11-24', 5)": 0, + "(3868, '2024-11-24', 6)": 0, + "(3868, '2024-11-24', 7)": 0, + "(3868, '2024-11-25', 0)": 0, + "(3868, '2024-11-25', 1)": 0, + "(3868, '2024-11-25', 2)": 0, + "(3868, '2024-11-25', 3)": 0, + "(3868, '2024-11-25', 4)": 0, + "(3868, '2024-11-25', 5)": 0, + "(3868, '2024-11-25', 6)": 1, + "(3868, '2024-11-25', 7)": 0, + "(3868, '2024-11-26', 0)": 0, + "(3868, '2024-11-26', 1)": 0, + "(3868, '2024-11-26', 2)": 0, + "(3868, '2024-11-26', 3)": 0, + "(3868, '2024-11-26', 4)": 0, + "(3868, '2024-11-26', 5)": 0, + "(3868, '2024-11-26', 6)": 0, + "(3868, '2024-11-26', 7)": 0, + "(3868, '2024-11-27', 0)": 0, + "(3868, '2024-11-27', 1)": 0, + "(3868, '2024-11-27', 2)": 1, + "(3868, '2024-11-27', 3)": 0, + "(3868, '2024-11-27', 4)": 0, + "(3868, '2024-11-27', 5)": 0, + "(3868, '2024-11-27', 6)": 0, + "(3868, '2024-11-27', 7)": 0, + "(3868, '2024-11-28', 0)": 0, + "(3868, '2024-11-28', 1)": 0, + "(3868, '2024-11-28', 2)": 0, + "(3868, '2024-11-28', 3)": 1, + "(3868, '2024-11-28', 4)": 0, + "(3868, '2024-11-28', 5)": 0, + "(3868, '2024-11-28', 6)": 0, + "(3868, '2024-11-28', 7)": 0, + "(3868, '2024-11-29', 0)": 0, + "(3868, '2024-11-29', 1)": 0, + "(3868, '2024-11-29', 2)": 0, + "(3868, '2024-11-29', 3)": 1, + "(3868, '2024-11-29', 4)": 0, + "(3868, '2024-11-29', 5)": 0, + "(3868, '2024-11-29', 6)": 0, + "(3868, '2024-11-29', 7)": 0, + "(3868, '2024-11-30', 0)": 0, + "(3868, '2024-11-30', 1)": 0, + "(3868, '2024-11-30', 2)": 0, + "(3868, '2024-11-30', 3)": 0, + "(3868, '2024-11-30', 4)": 0, + "(3868, '2024-11-30', 5)": 0, + "(3868, '2024-11-30', 6)": 0, + "(3868, '2024-11-30', 7)": 0, + "(4, '2024-11-01', 0)": 0, + "(4, '2024-11-01', 1)": 0, + "(4, '2024-11-01', 2)": 0, + "(4, '2024-11-01', 3)": 0, + "(4, '2024-11-01', 4)": 0, + "(4, '2024-11-01', 5)": 0, + "(4, '2024-11-01', 6)": 0, + "(4, '2024-11-01', 7)": 0, + "(4, '2024-11-02', 0)": 0, + "(4, '2024-11-02', 1)": 0, + "(4, '2024-11-02', 2)": 0, + "(4, '2024-11-02', 3)": 0, + "(4, '2024-11-02', 4)": 0, + "(4, '2024-11-02', 5)": 0, + "(4, '2024-11-02', 6)": 0, + "(4, '2024-11-02', 7)": 0, + "(4, '2024-11-03', 0)": 0, + "(4, '2024-11-03', 1)": 0, + "(4, '2024-11-03', 2)": 0, + "(4, '2024-11-03', 3)": 0, + "(4, '2024-11-03', 4)": 0, + "(4, '2024-11-03', 5)": 0, + "(4, '2024-11-03', 6)": 0, + "(4, '2024-11-03', 7)": 0, + "(4, '2024-11-04', 0)": 0, + "(4, '2024-11-04', 1)": 0, + "(4, '2024-11-04', 2)": 0, + "(4, '2024-11-04', 3)": 0, + "(4, '2024-11-04', 4)": 0, + "(4, '2024-11-04', 5)": 0, + "(4, '2024-11-04', 6)": 0, + "(4, '2024-11-04', 7)": 0, + "(4, '2024-11-05', 0)": 0, + "(4, '2024-11-05', 1)": 0, + "(4, '2024-11-05', 2)": 0, + "(4, '2024-11-05', 3)": 0, + "(4, '2024-11-05', 4)": 0, + "(4, '2024-11-05', 5)": 0, + "(4, '2024-11-05', 6)": 0, + "(4, '2024-11-05', 7)": 0, + "(4, '2024-11-06', 0)": 0, + "(4, '2024-11-06', 1)": 0, + "(4, '2024-11-06', 2)": 0, + "(4, '2024-11-06', 3)": 0, + "(4, '2024-11-06', 4)": 0, + "(4, '2024-11-06', 5)": 0, + "(4, '2024-11-06', 6)": 0, + "(4, '2024-11-06', 7)": 0, + "(4, '2024-11-07', 0)": 0, + "(4, '2024-11-07', 1)": 0, + "(4, '2024-11-07', 2)": 0, + "(4, '2024-11-07', 3)": 0, + "(4, '2024-11-07', 4)": 0, + "(4, '2024-11-07', 5)": 0, + "(4, '2024-11-07', 6)": 0, + "(4, '2024-11-07', 7)": 0, + "(4, '2024-11-08', 0)": 0, + "(4, '2024-11-08', 1)": 0, + "(4, '2024-11-08', 2)": 0, + "(4, '2024-11-08', 3)": 0, + "(4, '2024-11-08', 4)": 0, + "(4, '2024-11-08', 5)": 0, + "(4, '2024-11-08', 6)": 0, + "(4, '2024-11-08', 7)": 0, + "(4, '2024-11-09', 0)": 0, + "(4, '2024-11-09', 1)": 0, + "(4, '2024-11-09', 2)": 0, + "(4, '2024-11-09', 3)": 0, + "(4, '2024-11-09', 4)": 0, + "(4, '2024-11-09', 5)": 0, + "(4, '2024-11-09', 6)": 0, + "(4, '2024-11-09', 7)": 0, + "(4, '2024-11-10', 0)": 0, + "(4, '2024-11-10', 1)": 0, + "(4, '2024-11-10', 2)": 0, + "(4, '2024-11-10', 3)": 0, + "(4, '2024-11-10', 4)": 0, + "(4, '2024-11-10', 5)": 0, + "(4, '2024-11-10', 6)": 0, + "(4, '2024-11-10', 7)": 0, + "(4, '2024-11-11', 0)": 0, + "(4, '2024-11-11', 1)": 0, + "(4, '2024-11-11', 2)": 0, + "(4, '2024-11-11', 3)": 0, + "(4, '2024-11-11', 4)": 0, + "(4, '2024-11-11', 5)": 0, + "(4, '2024-11-11', 6)": 0, + "(4, '2024-11-11', 7)": 0, + "(4, '2024-11-12', 0)": 0, + "(4, '2024-11-12', 1)": 0, + "(4, '2024-11-12', 2)": 0, + "(4, '2024-11-12', 3)": 0, + "(4, '2024-11-12', 4)": 0, + "(4, '2024-11-12', 5)": 0, + "(4, '2024-11-12', 6)": 0, + "(4, '2024-11-12', 7)": 0, + "(4, '2024-11-13', 0)": 0, + "(4, '2024-11-13', 1)": 0, + "(4, '2024-11-13', 2)": 0, + "(4, '2024-11-13', 3)": 0, + "(4, '2024-11-13', 4)": 0, + "(4, '2024-11-13', 5)": 0, + "(4, '2024-11-13', 6)": 0, + "(4, '2024-11-13', 7)": 0, + "(4, '2024-11-14', 0)": 0, + "(4, '2024-11-14', 1)": 0, + "(4, '2024-11-14', 2)": 0, + "(4, '2024-11-14', 3)": 0, + "(4, '2024-11-14', 4)": 0, + "(4, '2024-11-14', 5)": 0, + "(4, '2024-11-14', 6)": 0, + "(4, '2024-11-14', 7)": 0, + "(4, '2024-11-15', 0)": 0, + "(4, '2024-11-15', 1)": 0, + "(4, '2024-11-15', 2)": 0, + "(4, '2024-11-15', 3)": 0, + "(4, '2024-11-15', 4)": 0, + "(4, '2024-11-15', 5)": 0, + "(4, '2024-11-15', 6)": 0, + "(4, '2024-11-15', 7)": 0, + "(4, '2024-11-16', 0)": 0, + "(4, '2024-11-16', 1)": 0, + "(4, '2024-11-16', 2)": 0, + "(4, '2024-11-16', 3)": 0, + "(4, '2024-11-16', 4)": 0, + "(4, '2024-11-16', 5)": 0, + "(4, '2024-11-16', 6)": 0, + "(4, '2024-11-16', 7)": 0, + "(4, '2024-11-17', 0)": 0, + "(4, '2024-11-17', 1)": 0, + "(4, '2024-11-17', 2)": 0, + "(4, '2024-11-17', 3)": 0, + "(4, '2024-11-17', 4)": 0, + "(4, '2024-11-17', 5)": 0, + "(4, '2024-11-17', 6)": 0, + "(4, '2024-11-17', 7)": 0, + "(4, '2024-11-18', 0)": 0, + "(4, '2024-11-18', 1)": 0, + "(4, '2024-11-18', 2)": 0, + "(4, '2024-11-18', 3)": 0, + "(4, '2024-11-18', 4)": 0, + "(4, '2024-11-18', 5)": 0, + "(4, '2024-11-18', 6)": 0, + "(4, '2024-11-18', 7)": 0, + "(4, '2024-11-19', 0)": 0, + "(4, '2024-11-19', 1)": 0, + "(4, '2024-11-19', 2)": 0, + "(4, '2024-11-19', 3)": 0, + "(4, '2024-11-19', 4)": 0, + "(4, '2024-11-19', 5)": 0, + "(4, '2024-11-19', 6)": 0, + "(4, '2024-11-19', 7)": 0, + "(4, '2024-11-20', 0)": 0, + "(4, '2024-11-20', 1)": 0, + "(4, '2024-11-20', 2)": 0, + "(4, '2024-11-20', 3)": 0, + "(4, '2024-11-20', 4)": 0, + "(4, '2024-11-20', 5)": 0, + "(4, '2024-11-20', 6)": 0, + "(4, '2024-11-20', 7)": 0, + "(4, '2024-11-21', 0)": 0, + "(4, '2024-11-21', 1)": 0, + "(4, '2024-11-21', 2)": 0, + "(4, '2024-11-21', 3)": 0, + "(4, '2024-11-21', 4)": 0, + "(4, '2024-11-21', 5)": 0, + "(4, '2024-11-21', 6)": 0, + "(4, '2024-11-21', 7)": 0, + "(4, '2024-11-22', 0)": 0, + "(4, '2024-11-22', 1)": 0, + "(4, '2024-11-22', 2)": 0, + "(4, '2024-11-22', 3)": 0, + "(4, '2024-11-22', 4)": 0, + "(4, '2024-11-22', 5)": 0, + "(4, '2024-11-22', 6)": 0, + "(4, '2024-11-22', 7)": 0, + "(4, '2024-11-23', 0)": 0, + "(4, '2024-11-23', 1)": 0, + "(4, '2024-11-23', 2)": 0, + "(4, '2024-11-23', 3)": 0, + "(4, '2024-11-23', 4)": 0, + "(4, '2024-11-23', 5)": 0, + "(4, '2024-11-23', 6)": 0, + "(4, '2024-11-23', 7)": 0, + "(4, '2024-11-24', 0)": 0, + "(4, '2024-11-24', 1)": 0, + "(4, '2024-11-24', 2)": 0, + "(4, '2024-11-24', 3)": 0, + "(4, '2024-11-24', 4)": 0, + "(4, '2024-11-24', 5)": 0, + "(4, '2024-11-24', 6)": 0, + "(4, '2024-11-24', 7)": 0, + "(4, '2024-11-25', 0)": 0, + "(4, '2024-11-25', 1)": 0, + "(4, '2024-11-25', 2)": 0, + "(4, '2024-11-25', 3)": 0, + "(4, '2024-11-25', 4)": 0, + "(4, '2024-11-25', 5)": 0, + "(4, '2024-11-25', 6)": 0, + "(4, '2024-11-25', 7)": 0, + "(4, '2024-11-26', 0)": 0, + "(4, '2024-11-26', 1)": 0, + "(4, '2024-11-26', 2)": 0, + "(4, '2024-11-26', 3)": 0, + "(4, '2024-11-26', 4)": 0, + "(4, '2024-11-26', 5)": 0, + "(4, '2024-11-26', 6)": 0, + "(4, '2024-11-26', 7)": 0, + "(4, '2024-11-27', 0)": 0, + "(4, '2024-11-27', 1)": 0, + "(4, '2024-11-27', 2)": 0, + "(4, '2024-11-27', 3)": 0, + "(4, '2024-11-27', 4)": 0, + "(4, '2024-11-27', 5)": 0, + "(4, '2024-11-27', 6)": 0, + "(4, '2024-11-27', 7)": 0, + "(4, '2024-11-28', 0)": 0, + "(4, '2024-11-28', 1)": 0, + "(4, '2024-11-28', 2)": 0, + "(4, '2024-11-28', 3)": 0, + "(4, '2024-11-28', 4)": 0, + "(4, '2024-11-28', 5)": 0, + "(4, '2024-11-28', 6)": 0, + "(4, '2024-11-28', 7)": 0, + "(4, '2024-11-29', 0)": 0, + "(4, '2024-11-29', 1)": 0, + "(4, '2024-11-29', 2)": 0, + "(4, '2024-11-29', 3)": 0, + "(4, '2024-11-29', 4)": 0, + "(4, '2024-11-29', 5)": 0, + "(4, '2024-11-29', 6)": 0, + "(4, '2024-11-29', 7)": 0, + "(4, '2024-11-30', 0)": 0, + "(4, '2024-11-30', 1)": 0, + "(4, '2024-11-30', 2)": 0, + "(4, '2024-11-30', 3)": 0, + "(4, '2024-11-30', 4)": 0, + "(4, '2024-11-30', 5)": 0, + "(4, '2024-11-30', 6)": 0, + "(4, '2024-11-30', 7)": 0, + "(4566, '2024-11-01', 0)": 1, + "(4566, '2024-11-01', 1)": 0, + "(4566, '2024-11-01', 2)": 0, + "(4566, '2024-11-01', 3)": 0, + "(4566, '2024-11-01', 4)": 0, + "(4566, '2024-11-01', 5)": 0, + "(4566, '2024-11-01', 6)": 0, + "(4566, '2024-11-01', 7)": 0, + "(4566, '2024-11-02', 0)": 0, + "(4566, '2024-11-02', 1)": 0, + "(4566, '2024-11-02', 2)": 0, + "(4566, '2024-11-02', 3)": 0, + "(4566, '2024-11-02', 4)": 0, + "(4566, '2024-11-02', 5)": 0, + "(4566, '2024-11-02', 6)": 0, + "(4566, '2024-11-02', 7)": 0, + "(4566, '2024-11-03', 0)": 0, + "(4566, '2024-11-03', 1)": 0, + "(4566, '2024-11-03', 2)": 1, + "(4566, '2024-11-03', 3)": 0, + "(4566, '2024-11-03', 4)": 0, + "(4566, '2024-11-03', 5)": 0, + "(4566, '2024-11-03', 6)": 0, + "(4566, '2024-11-03', 7)": 0, + "(4566, '2024-11-04', 0)": 0, + "(4566, '2024-11-04', 1)": 0, + "(4566, '2024-11-04', 2)": 1, + "(4566, '2024-11-04', 3)": 0, + "(4566, '2024-11-04', 4)": 0, + "(4566, '2024-11-04', 5)": 0, + "(4566, '2024-11-04', 6)": 0, + "(4566, '2024-11-04', 7)": 0, + "(4566, '2024-11-05', 0)": 0, + "(4566, '2024-11-05', 1)": 0, + "(4566, '2024-11-05', 2)": 1, + "(4566, '2024-11-05', 3)": 0, + "(4566, '2024-11-05', 4)": 0, + "(4566, '2024-11-05', 5)": 0, + "(4566, '2024-11-05', 6)": 0, + "(4566, '2024-11-05', 7)": 0, + "(4566, '2024-11-06', 0)": 0, + "(4566, '2024-11-06', 1)": 0, + "(4566, '2024-11-06', 2)": 0, + "(4566, '2024-11-06', 3)": 0, + "(4566, '2024-11-06', 4)": 0, + "(4566, '2024-11-06', 5)": 0, + "(4566, '2024-11-06', 6)": 0, + "(4566, '2024-11-06', 7)": 0, + "(4566, '2024-11-07', 0)": 0, + "(4566, '2024-11-07', 1)": 0, + "(4566, '2024-11-07', 2)": 0, + "(4566, '2024-11-07', 3)": 0, + "(4566, '2024-11-07', 4)": 0, + "(4566, '2024-11-07', 5)": 0, + "(4566, '2024-11-07', 6)": 0, + "(4566, '2024-11-07', 7)": 0, + "(4566, '2024-11-08', 0)": 0, + "(4566, '2024-11-08', 1)": 1, + "(4566, '2024-11-08', 2)": 0, + "(4566, '2024-11-08', 3)": 0, + "(4566, '2024-11-08', 4)": 0, + "(4566, '2024-11-08', 5)": 0, + "(4566, '2024-11-08', 6)": 0, + "(4566, '2024-11-08', 7)": 0, + "(4566, '2024-11-09', 0)": 0, + "(4566, '2024-11-09', 1)": 0, + "(4566, '2024-11-09', 2)": 0, + "(4566, '2024-11-09', 3)": 0, + "(4566, '2024-11-09', 4)": 0, + "(4566, '2024-11-09', 5)": 0, + "(4566, '2024-11-09', 6)": 0, + "(4566, '2024-11-09', 7)": 0, + "(4566, '2024-11-10', 0)": 1, + "(4566, '2024-11-10', 1)": 0, + "(4566, '2024-11-10', 2)": 0, + "(4566, '2024-11-10', 3)": 0, + "(4566, '2024-11-10', 4)": 0, + "(4566, '2024-11-10', 5)": 0, + "(4566, '2024-11-10', 6)": 0, + "(4566, '2024-11-10', 7)": 0, + "(4566, '2024-11-11', 0)": 0, + "(4566, '2024-11-11', 1)": 0, + "(4566, '2024-11-11', 2)": 0, + "(4566, '2024-11-11', 3)": 0, + "(4566, '2024-11-11', 4)": 0, + "(4566, '2024-11-11', 5)": 0, + "(4566, '2024-11-11', 6)": 0, + "(4566, '2024-11-11', 7)": 0, + "(4566, '2024-11-12', 0)": 1, + "(4566, '2024-11-12', 1)": 0, + "(4566, '2024-11-12', 2)": 0, + "(4566, '2024-11-12', 3)": 0, + "(4566, '2024-11-12', 4)": 0, + "(4566, '2024-11-12', 5)": 0, + "(4566, '2024-11-12', 6)": 0, + "(4566, '2024-11-12', 7)": 0, + "(4566, '2024-11-13', 0)": 0, + "(4566, '2024-11-13', 1)": 0, + "(4566, '2024-11-13', 2)": 1, + "(4566, '2024-11-13', 3)": 0, + "(4566, '2024-11-13', 4)": 0, + "(4566, '2024-11-13', 5)": 0, + "(4566, '2024-11-13', 6)": 0, + "(4566, '2024-11-13', 7)": 0, + "(4566, '2024-11-14', 0)": 0, + "(4566, '2024-11-14', 1)": 0, + "(4566, '2024-11-14', 2)": 1, + "(4566, '2024-11-14', 3)": 0, + "(4566, '2024-11-14', 4)": 0, + "(4566, '2024-11-14', 5)": 0, + "(4566, '2024-11-14', 6)": 0, + "(4566, '2024-11-14', 7)": 0, + "(4566, '2024-11-15', 0)": 0, + "(4566, '2024-11-15', 1)": 0, + "(4566, '2024-11-15', 2)": 1, + "(4566, '2024-11-15', 3)": 0, + "(4566, '2024-11-15', 4)": 0, + "(4566, '2024-11-15', 5)": 0, + "(4566, '2024-11-15', 6)": 0, + "(4566, '2024-11-15', 7)": 0, + "(4566, '2024-11-16', 0)": 0, + "(4566, '2024-11-16', 1)": 0, + "(4566, '2024-11-16', 2)": 0, + "(4566, '2024-11-16', 3)": 0, + "(4566, '2024-11-16', 4)": 0, + "(4566, '2024-11-16', 5)": 0, + "(4566, '2024-11-16', 6)": 0, + "(4566, '2024-11-16', 7)": 0, + "(4566, '2024-11-17', 0)": 0, + "(4566, '2024-11-17', 1)": 0, + "(4566, '2024-11-17', 2)": 0, + "(4566, '2024-11-17', 3)": 0, + "(4566, '2024-11-17', 4)": 0, + "(4566, '2024-11-17', 5)": 0, + "(4566, '2024-11-17', 6)": 0, + "(4566, '2024-11-17', 7)": 0, + "(4566, '2024-11-18', 0)": 0, + "(4566, '2024-11-18', 1)": 0, + "(4566, '2024-11-18', 2)": 0, + "(4566, '2024-11-18', 3)": 0, + "(4566, '2024-11-18', 4)": 0, + "(4566, '2024-11-18', 5)": 0, + "(4566, '2024-11-18', 6)": 0, + "(4566, '2024-11-18', 7)": 0, + "(4566, '2024-11-19', 0)": 0, + "(4566, '2024-11-19', 1)": 1, + "(4566, '2024-11-19', 2)": 0, + "(4566, '2024-11-19', 3)": 0, + "(4566, '2024-11-19', 4)": 0, + "(4566, '2024-11-19', 5)": 0, + "(4566, '2024-11-19', 6)": 0, + "(4566, '2024-11-19', 7)": 0, + "(4566, '2024-11-20', 0)": 1, + "(4566, '2024-11-20', 1)": 0, + "(4566, '2024-11-20', 2)": 0, + "(4566, '2024-11-20', 3)": 0, + "(4566, '2024-11-20', 4)": 0, + "(4566, '2024-11-20', 5)": 0, + "(4566, '2024-11-20', 6)": 0, + "(4566, '2024-11-20', 7)": 0, + "(4566, '2024-11-21', 0)": 0, + "(4566, '2024-11-21', 1)": 1, + "(4566, '2024-11-21', 2)": 0, + "(4566, '2024-11-21', 3)": 0, + "(4566, '2024-11-21', 4)": 0, + "(4566, '2024-11-21', 5)": 0, + "(4566, '2024-11-21', 6)": 0, + "(4566, '2024-11-21', 7)": 0, + "(4566, '2024-11-22', 0)": 0, + "(4566, '2024-11-22', 1)": 0, + "(4566, '2024-11-22', 2)": 0, + "(4566, '2024-11-22', 3)": 0, + "(4566, '2024-11-22', 4)": 0, + "(4566, '2024-11-22', 5)": 0, + "(4566, '2024-11-22', 6)": 0, + "(4566, '2024-11-22', 7)": 0, + "(4566, '2024-11-23', 0)": 0, + "(4566, '2024-11-23', 1)": 0, + "(4566, '2024-11-23', 2)": 1, + "(4566, '2024-11-23', 3)": 0, + "(4566, '2024-11-23', 4)": 0, + "(4566, '2024-11-23', 5)": 0, + "(4566, '2024-11-23', 6)": 0, + "(4566, '2024-11-23', 7)": 0, + "(4566, '2024-11-24', 0)": 0, + "(4566, '2024-11-24', 1)": 0, + "(4566, '2024-11-24', 2)": 0, + "(4566, '2024-11-24', 3)": 0, + "(4566, '2024-11-24', 4)": 0, + "(4566, '2024-11-24', 5)": 0, + "(4566, '2024-11-24', 6)": 0, + "(4566, '2024-11-24', 7)": 0, + "(4566, '2024-11-25', 0)": 0, + "(4566, '2024-11-25', 1)": 0, + "(4566, '2024-11-25', 2)": 0, + "(4566, '2024-11-25', 3)": 0, + "(4566, '2024-11-25', 4)": 0, + "(4566, '2024-11-25', 5)": 0, + "(4566, '2024-11-25', 6)": 0, + "(4566, '2024-11-25', 7)": 0, + "(4566, '2024-11-26', 0)": 1, + "(4566, '2024-11-26', 1)": 0, + "(4566, '2024-11-26', 2)": 0, + "(4566, '2024-11-26', 3)": 0, + "(4566, '2024-11-26', 4)": 0, + "(4566, '2024-11-26', 5)": 0, + "(4566, '2024-11-26', 6)": 0, + "(4566, '2024-11-26', 7)": 0, + "(4566, '2024-11-27', 0)": 1, + "(4566, '2024-11-27', 1)": 0, + "(4566, '2024-11-27', 2)": 0, + "(4566, '2024-11-27', 3)": 0, + "(4566, '2024-11-27', 4)": 0, + "(4566, '2024-11-27', 5)": 0, + "(4566, '2024-11-27', 6)": 0, + "(4566, '2024-11-27', 7)": 0, + "(4566, '2024-11-28', 0)": 0, + "(4566, '2024-11-28', 1)": 0, + "(4566, '2024-11-28', 2)": 0, + "(4566, '2024-11-28', 3)": 0, + "(4566, '2024-11-28', 4)": 0, + "(4566, '2024-11-28', 5)": 0, + "(4566, '2024-11-28', 6)": 0, + "(4566, '2024-11-28', 7)": 0, + "(4566, '2024-11-29', 0)": 1, + "(4566, '2024-11-29', 1)": 0, + "(4566, '2024-11-29', 2)": 0, + "(4566, '2024-11-29', 3)": 0, + "(4566, '2024-11-29', 4)": 0, + "(4566, '2024-11-29', 5)": 0, + "(4566, '2024-11-29', 6)": 0, + "(4566, '2024-11-29', 7)": 0, + "(4566, '2024-11-30', 0)": 0, + "(4566, '2024-11-30', 1)": 0, + "(4566, '2024-11-30', 2)": 1, + "(4566, '2024-11-30', 3)": 0, + "(4566, '2024-11-30', 4)": 0, + "(4566, '2024-11-30', 5)": 0, + "(4566, '2024-11-30', 6)": 0, + "(4566, '2024-11-30', 7)": 0, + "(459, '2024-11-01', 0)": 0, + "(459, '2024-11-01', 1)": 0, + "(459, '2024-11-01', 2)": 1, + "(459, '2024-11-01', 3)": 0, + "(459, '2024-11-01', 4)": 0, + "(459, '2024-11-01', 5)": 0, + "(459, '2024-11-01', 6)": 0, + "(459, '2024-11-01', 7)": 0, + "(459, '2024-11-02', 0)": 0, + "(459, '2024-11-02', 1)": 0, + "(459, '2024-11-02', 2)": 0, + "(459, '2024-11-02', 3)": 0, + "(459, '2024-11-02', 4)": 0, + "(459, '2024-11-02', 5)": 0, + "(459, '2024-11-02', 6)": 0, + "(459, '2024-11-02', 7)": 0, + "(459, '2024-11-03', 0)": 0, + "(459, '2024-11-03', 1)": 0, + "(459, '2024-11-03', 2)": 0, + "(459, '2024-11-03', 3)": 0, + "(459, '2024-11-03', 4)": 0, + "(459, '2024-11-03', 5)": 0, + "(459, '2024-11-03', 6)": 0, + "(459, '2024-11-03', 7)": 0, + "(459, '2024-11-04', 0)": 0, + "(459, '2024-11-04', 1)": 0, + "(459, '2024-11-04', 2)": 0, + "(459, '2024-11-04', 3)": 0, + "(459, '2024-11-04', 4)": 0, + "(459, '2024-11-04', 5)": 0, + "(459, '2024-11-04', 6)": 0, + "(459, '2024-11-04', 7)": 0, + "(459, '2024-11-05', 0)": 0, + "(459, '2024-11-05', 1)": 0, + "(459, '2024-11-05', 2)": 0, + "(459, '2024-11-05', 3)": 0, + "(459, '2024-11-05', 4)": 0, + "(459, '2024-11-05', 5)": 0, + "(459, '2024-11-05', 6)": 0, + "(459, '2024-11-05', 7)": 0, + "(459, '2024-11-06', 0)": 0, + "(459, '2024-11-06', 1)": 0, + "(459, '2024-11-06', 2)": 0, + "(459, '2024-11-06', 3)": 0, + "(459, '2024-11-06', 4)": 0, + "(459, '2024-11-06', 5)": 0, + "(459, '2024-11-06', 6)": 0, + "(459, '2024-11-06', 7)": 0, + "(459, '2024-11-07', 0)": 0, + "(459, '2024-11-07', 1)": 0, + "(459, '2024-11-07', 2)": 0, + "(459, '2024-11-07', 3)": 0, + "(459, '2024-11-07', 4)": 0, + "(459, '2024-11-07', 5)": 0, + "(459, '2024-11-07', 6)": 0, + "(459, '2024-11-07', 7)": 0, + "(459, '2024-11-08', 0)": 0, + "(459, '2024-11-08', 1)": 0, + "(459, '2024-11-08', 2)": 0, + "(459, '2024-11-08', 3)": 0, + "(459, '2024-11-08', 4)": 0, + "(459, '2024-11-08', 5)": 0, + "(459, '2024-11-08', 6)": 0, + "(459, '2024-11-08', 7)": 0, + "(459, '2024-11-09', 0)": 0, + "(459, '2024-11-09', 1)": 0, + "(459, '2024-11-09', 2)": 0, + "(459, '2024-11-09', 3)": 0, + "(459, '2024-11-09', 4)": 0, + "(459, '2024-11-09', 5)": 0, + "(459, '2024-11-09', 6)": 0, + "(459, '2024-11-09', 7)": 0, + "(459, '2024-11-10', 0)": 0, + "(459, '2024-11-10', 1)": 0, + "(459, '2024-11-10', 2)": 0, + "(459, '2024-11-10', 3)": 0, + "(459, '2024-11-10', 4)": 0, + "(459, '2024-11-10', 5)": 0, + "(459, '2024-11-10', 6)": 0, + "(459, '2024-11-10', 7)": 0, + "(459, '2024-11-11', 0)": 0, + "(459, '2024-11-11', 1)": 0, + "(459, '2024-11-11', 2)": 0, + "(459, '2024-11-11', 3)": 0, + "(459, '2024-11-11', 4)": 0, + "(459, '2024-11-11', 5)": 0, + "(459, '2024-11-11', 6)": 0, + "(459, '2024-11-11', 7)": 0, + "(459, '2024-11-12', 0)": 0, + "(459, '2024-11-12', 1)": 0, + "(459, '2024-11-12', 2)": 0, + "(459, '2024-11-12', 3)": 0, + "(459, '2024-11-12', 4)": 0, + "(459, '2024-11-12', 5)": 0, + "(459, '2024-11-12', 6)": 0, + "(459, '2024-11-12', 7)": 0, + "(459, '2024-11-13', 0)": 0, + "(459, '2024-11-13', 1)": 0, + "(459, '2024-11-13', 2)": 0, + "(459, '2024-11-13', 3)": 0, + "(459, '2024-11-13', 4)": 0, + "(459, '2024-11-13', 5)": 0, + "(459, '2024-11-13', 6)": 0, + "(459, '2024-11-13', 7)": 0, + "(459, '2024-11-14', 0)": 0, + "(459, '2024-11-14', 1)": 1, + "(459, '2024-11-14', 2)": 0, + "(459, '2024-11-14', 3)": 0, + "(459, '2024-11-14', 4)": 0, + "(459, '2024-11-14', 5)": 0, + "(459, '2024-11-14', 6)": 0, + "(459, '2024-11-14', 7)": 0, + "(459, '2024-11-15', 0)": 0, + "(459, '2024-11-15', 1)": 1, + "(459, '2024-11-15', 2)": 0, + "(459, '2024-11-15', 3)": 0, + "(459, '2024-11-15', 4)": 0, + "(459, '2024-11-15', 5)": 0, + "(459, '2024-11-15', 6)": 0, + "(459, '2024-11-15', 7)": 0, + "(459, '2024-11-16', 0)": 0, + "(459, '2024-11-16', 1)": 0, + "(459, '2024-11-16', 2)": 0, + "(459, '2024-11-16', 3)": 0, + "(459, '2024-11-16', 4)": 0, + "(459, '2024-11-16', 5)": 0, + "(459, '2024-11-16', 6)": 0, + "(459, '2024-11-16', 7)": 0, + "(459, '2024-11-17', 0)": 0, + "(459, '2024-11-17', 1)": 0, + "(459, '2024-11-17', 2)": 0, + "(459, '2024-11-17', 3)": 0, + "(459, '2024-11-17', 4)": 0, + "(459, '2024-11-17', 5)": 0, + "(459, '2024-11-17', 6)": 0, + "(459, '2024-11-17', 7)": 0, + "(459, '2024-11-18', 0)": 0, + "(459, '2024-11-18', 1)": 0, + "(459, '2024-11-18', 2)": 0, + "(459, '2024-11-18', 3)": 0, + "(459, '2024-11-18', 4)": 0, + "(459, '2024-11-18', 5)": 0, + "(459, '2024-11-18', 6)": 0, + "(459, '2024-11-18', 7)": 0, + "(459, '2024-11-19', 0)": 0, + "(459, '2024-11-19', 1)": 0, + "(459, '2024-11-19', 2)": 0, + "(459, '2024-11-19', 3)": 0, + "(459, '2024-11-19', 4)": 0, + "(459, '2024-11-19', 5)": 0, + "(459, '2024-11-19', 6)": 0, + "(459, '2024-11-19', 7)": 0, + "(459, '2024-11-20', 0)": 0, + "(459, '2024-11-20', 1)": 0, + "(459, '2024-11-20', 2)": 0, + "(459, '2024-11-20', 3)": 0, + "(459, '2024-11-20', 4)": 0, + "(459, '2024-11-20', 5)": 0, + "(459, '2024-11-20', 6)": 0, + "(459, '2024-11-20', 7)": 0, + "(459, '2024-11-21', 0)": 0, + "(459, '2024-11-21', 1)": 0, + "(459, '2024-11-21', 2)": 0, + "(459, '2024-11-21', 3)": 0, + "(459, '2024-11-21', 4)": 0, + "(459, '2024-11-21', 5)": 0, + "(459, '2024-11-21', 6)": 0, + "(459, '2024-11-21', 7)": 0, + "(459, '2024-11-22', 0)": 0, + "(459, '2024-11-22', 1)": 0, + "(459, '2024-11-22', 2)": 0, + "(459, '2024-11-22', 3)": 0, + "(459, '2024-11-22', 4)": 0, + "(459, '2024-11-22', 5)": 0, + "(459, '2024-11-22', 6)": 0, + "(459, '2024-11-22', 7)": 0, + "(459, '2024-11-23', 0)": 0, + "(459, '2024-11-23', 1)": 0, + "(459, '2024-11-23', 2)": 0, + "(459, '2024-11-23', 3)": 0, + "(459, '2024-11-23', 4)": 0, + "(459, '2024-11-23', 5)": 0, + "(459, '2024-11-23', 6)": 0, + "(459, '2024-11-23', 7)": 0, + "(459, '2024-11-24', 0)": 0, + "(459, '2024-11-24', 1)": 0, + "(459, '2024-11-24', 2)": 0, + "(459, '2024-11-24', 3)": 0, + "(459, '2024-11-24', 4)": 0, + "(459, '2024-11-24', 5)": 0, + "(459, '2024-11-24', 6)": 0, + "(459, '2024-11-24', 7)": 0, + "(459, '2024-11-25', 0)": 0, + "(459, '2024-11-25', 1)": 0, + "(459, '2024-11-25', 2)": 0, + "(459, '2024-11-25', 3)": 0, + "(459, '2024-11-25', 4)": 0, + "(459, '2024-11-25', 5)": 0, + "(459, '2024-11-25', 6)": 0, + "(459, '2024-11-25', 7)": 0, + "(459, '2024-11-26', 0)": 0, + "(459, '2024-11-26', 1)": 0, + "(459, '2024-11-26', 2)": 0, + "(459, '2024-11-26', 3)": 0, + "(459, '2024-11-26', 4)": 0, + "(459, '2024-11-26', 5)": 0, + "(459, '2024-11-26', 6)": 0, + "(459, '2024-11-26', 7)": 0, + "(459, '2024-11-27', 0)": 0, + "(459, '2024-11-27', 1)": 0, + "(459, '2024-11-27', 2)": 0, + "(459, '2024-11-27', 3)": 0, + "(459, '2024-11-27', 4)": 0, + "(459, '2024-11-27', 5)": 0, + "(459, '2024-11-27', 6)": 0, + "(459, '2024-11-27', 7)": 0, + "(459, '2024-11-28', 0)": 0, + "(459, '2024-11-28', 1)": 0, + "(459, '2024-11-28', 2)": 0, + "(459, '2024-11-28', 3)": 0, + "(459, '2024-11-28', 4)": 0, + "(459, '2024-11-28', 5)": 0, + "(459, '2024-11-28', 6)": 0, + "(459, '2024-11-28', 7)": 0, + "(459, '2024-11-29', 0)": 0, + "(459, '2024-11-29', 1)": 0, + "(459, '2024-11-29', 2)": 0, + "(459, '2024-11-29', 3)": 0, + "(459, '2024-11-29', 4)": 0, + "(459, '2024-11-29', 5)": 0, + "(459, '2024-11-29', 6)": 0, + "(459, '2024-11-29', 7)": 0, + "(459, '2024-11-30', 0)": 0, + "(459, '2024-11-30', 1)": 1, + "(459, '2024-11-30', 2)": 0, + "(459, '2024-11-30', 3)": 0, + "(459, '2024-11-30', 4)": 0, + "(459, '2024-11-30', 5)": 0, + "(459, '2024-11-30', 6)": 0, + "(459, '2024-11-30', 7)": 0, + "(5, '2024-11-01', 0)": 0, + "(5, '2024-11-01', 1)": 0, + "(5, '2024-11-01', 2)": 0, + "(5, '2024-11-01', 3)": 0, + "(5, '2024-11-01', 4)": 0, + "(5, '2024-11-01', 5)": 0, + "(5, '2024-11-01', 6)": 0, + "(5, '2024-11-01', 7)": 0, + "(5, '2024-11-02', 0)": 0, + "(5, '2024-11-02', 1)": 0, + "(5, '2024-11-02', 2)": 0, + "(5, '2024-11-02', 3)": 0, + "(5, '2024-11-02', 4)": 0, + "(5, '2024-11-02', 5)": 0, + "(5, '2024-11-02', 6)": 0, + "(5, '2024-11-02', 7)": 0, + "(5, '2024-11-03', 0)": 0, + "(5, '2024-11-03', 1)": 0, + "(5, '2024-11-03', 2)": 0, + "(5, '2024-11-03', 3)": 0, + "(5, '2024-11-03', 4)": 0, + "(5, '2024-11-03', 5)": 0, + "(5, '2024-11-03', 6)": 0, + "(5, '2024-11-03', 7)": 0, + "(5, '2024-11-04', 0)": 0, + "(5, '2024-11-04', 1)": 0, + "(5, '2024-11-04', 2)": 0, + "(5, '2024-11-04', 3)": 0, + "(5, '2024-11-04', 4)": 0, + "(5, '2024-11-04', 5)": 0, + "(5, '2024-11-04', 6)": 0, + "(5, '2024-11-04', 7)": 0, + "(5, '2024-11-05', 0)": 0, + "(5, '2024-11-05', 1)": 0, + "(5, '2024-11-05', 2)": 0, + "(5, '2024-11-05', 3)": 0, + "(5, '2024-11-05', 4)": 0, + "(5, '2024-11-05', 5)": 0, + "(5, '2024-11-05', 6)": 0, + "(5, '2024-11-05', 7)": 0, + "(5, '2024-11-06', 0)": 0, + "(5, '2024-11-06', 1)": 0, + "(5, '2024-11-06', 2)": 0, + "(5, '2024-11-06', 3)": 0, + "(5, '2024-11-06', 4)": 0, + "(5, '2024-11-06', 5)": 0, + "(5, '2024-11-06', 6)": 0, + "(5, '2024-11-06', 7)": 0, + "(5, '2024-11-07', 0)": 0, + "(5, '2024-11-07', 1)": 0, + "(5, '2024-11-07', 2)": 0, + "(5, '2024-11-07', 3)": 0, + "(5, '2024-11-07', 4)": 0, + "(5, '2024-11-07', 5)": 0, + "(5, '2024-11-07', 6)": 0, + "(5, '2024-11-07', 7)": 0, + "(5, '2024-11-08', 0)": 0, + "(5, '2024-11-08', 1)": 0, + "(5, '2024-11-08', 2)": 0, + "(5, '2024-11-08', 3)": 0, + "(5, '2024-11-08', 4)": 0, + "(5, '2024-11-08', 5)": 0, + "(5, '2024-11-08', 6)": 0, + "(5, '2024-11-08', 7)": 0, + "(5, '2024-11-09', 0)": 0, + "(5, '2024-11-09', 1)": 0, + "(5, '2024-11-09', 2)": 0, + "(5, '2024-11-09', 3)": 0, + "(5, '2024-11-09', 4)": 0, + "(5, '2024-11-09', 5)": 0, + "(5, '2024-11-09', 6)": 0, + "(5, '2024-11-09', 7)": 0, + "(5, '2024-11-10', 0)": 0, + "(5, '2024-11-10', 1)": 0, + "(5, '2024-11-10', 2)": 0, + "(5, '2024-11-10', 3)": 0, + "(5, '2024-11-10', 4)": 0, + "(5, '2024-11-10', 5)": 0, + "(5, '2024-11-10', 6)": 0, + "(5, '2024-11-10', 7)": 0, + "(5, '2024-11-11', 0)": 0, + "(5, '2024-11-11', 1)": 0, + "(5, '2024-11-11', 2)": 0, + "(5, '2024-11-11', 3)": 0, + "(5, '2024-11-11', 4)": 0, + "(5, '2024-11-11', 5)": 0, + "(5, '2024-11-11', 6)": 0, + "(5, '2024-11-11', 7)": 0, + "(5, '2024-11-12', 0)": 0, + "(5, '2024-11-12', 1)": 0, + "(5, '2024-11-12', 2)": 0, + "(5, '2024-11-12', 3)": 0, + "(5, '2024-11-12', 4)": 0, + "(5, '2024-11-12', 5)": 0, + "(5, '2024-11-12', 6)": 0, + "(5, '2024-11-12', 7)": 0, + "(5, '2024-11-13', 0)": 0, + "(5, '2024-11-13', 1)": 0, + "(5, '2024-11-13', 2)": 0, + "(5, '2024-11-13', 3)": 0, + "(5, '2024-11-13', 4)": 0, + "(5, '2024-11-13', 5)": 0, + "(5, '2024-11-13', 6)": 0, + "(5, '2024-11-13', 7)": 0, + "(5, '2024-11-14', 0)": 0, + "(5, '2024-11-14', 1)": 0, + "(5, '2024-11-14', 2)": 0, + "(5, '2024-11-14', 3)": 0, + "(5, '2024-11-14', 4)": 0, + "(5, '2024-11-14', 5)": 0, + "(5, '2024-11-14', 6)": 0, + "(5, '2024-11-14', 7)": 0, + "(5, '2024-11-15', 0)": 0, + "(5, '2024-11-15', 1)": 0, + "(5, '2024-11-15', 2)": 0, + "(5, '2024-11-15', 3)": 0, + "(5, '2024-11-15', 4)": 0, + "(5, '2024-11-15', 5)": 0, + "(5, '2024-11-15', 6)": 0, + "(5, '2024-11-15', 7)": 0, + "(5, '2024-11-16', 0)": 0, + "(5, '2024-11-16', 1)": 0, + "(5, '2024-11-16', 2)": 0, + "(5, '2024-11-16', 3)": 0, + "(5, '2024-11-16', 4)": 0, + "(5, '2024-11-16', 5)": 0, + "(5, '2024-11-16', 6)": 0, + "(5, '2024-11-16', 7)": 0, + "(5, '2024-11-17', 0)": 0, + "(5, '2024-11-17', 1)": 0, + "(5, '2024-11-17', 2)": 0, + "(5, '2024-11-17', 3)": 0, + "(5, '2024-11-17', 4)": 0, + "(5, '2024-11-17', 5)": 0, + "(5, '2024-11-17', 6)": 0, + "(5, '2024-11-17', 7)": 0, + "(5, '2024-11-18', 0)": 0, + "(5, '2024-11-18', 1)": 0, + "(5, '2024-11-18', 2)": 0, + "(5, '2024-11-18', 3)": 0, + "(5, '2024-11-18', 4)": 0, + "(5, '2024-11-18', 5)": 0, + "(5, '2024-11-18', 6)": 0, + "(5, '2024-11-18', 7)": 0, + "(5, '2024-11-19', 0)": 0, + "(5, '2024-11-19', 1)": 0, + "(5, '2024-11-19', 2)": 0, + "(5, '2024-11-19', 3)": 0, + "(5, '2024-11-19', 4)": 0, + "(5, '2024-11-19', 5)": 0, + "(5, '2024-11-19', 6)": 0, + "(5, '2024-11-19', 7)": 0, + "(5, '2024-11-20', 0)": 0, + "(5, '2024-11-20', 1)": 0, + "(5, '2024-11-20', 2)": 0, + "(5, '2024-11-20', 3)": 0, + "(5, '2024-11-20', 4)": 0, + "(5, '2024-11-20', 5)": 0, + "(5, '2024-11-20', 6)": 0, + "(5, '2024-11-20', 7)": 0, + "(5, '2024-11-21', 0)": 0, + "(5, '2024-11-21', 1)": 0, + "(5, '2024-11-21', 2)": 0, + "(5, '2024-11-21', 3)": 0, + "(5, '2024-11-21', 4)": 0, + "(5, '2024-11-21', 5)": 0, + "(5, '2024-11-21', 6)": 0, + "(5, '2024-11-21', 7)": 0, + "(5, '2024-11-22', 0)": 0, + "(5, '2024-11-22', 1)": 0, + "(5, '2024-11-22', 2)": 0, + "(5, '2024-11-22', 3)": 0, + "(5, '2024-11-22', 4)": 0, + "(5, '2024-11-22', 5)": 0, + "(5, '2024-11-22', 6)": 0, + "(5, '2024-11-22', 7)": 0, + "(5, '2024-11-23', 0)": 0, + "(5, '2024-11-23', 1)": 0, + "(5, '2024-11-23', 2)": 0, + "(5, '2024-11-23', 3)": 0, + "(5, '2024-11-23', 4)": 0, + "(5, '2024-11-23', 5)": 0, + "(5, '2024-11-23', 6)": 0, + "(5, '2024-11-23', 7)": 0, + "(5, '2024-11-24', 0)": 0, + "(5, '2024-11-24', 1)": 0, + "(5, '2024-11-24', 2)": 0, + "(5, '2024-11-24', 3)": 0, + "(5, '2024-11-24', 4)": 0, + "(5, '2024-11-24', 5)": 0, + "(5, '2024-11-24', 6)": 0, + "(5, '2024-11-24', 7)": 0, + "(5, '2024-11-25', 0)": 0, + "(5, '2024-11-25', 1)": 0, + "(5, '2024-11-25', 2)": 0, + "(5, '2024-11-25', 3)": 0, + "(5, '2024-11-25', 4)": 0, + "(5, '2024-11-25', 5)": 0, + "(5, '2024-11-25', 6)": 0, + "(5, '2024-11-25', 7)": 0, + "(5, '2024-11-26', 0)": 0, + "(5, '2024-11-26', 1)": 0, + "(5, '2024-11-26', 2)": 0, + "(5, '2024-11-26', 3)": 0, + "(5, '2024-11-26', 4)": 0, + "(5, '2024-11-26', 5)": 0, + "(5, '2024-11-26', 6)": 0, + "(5, '2024-11-26', 7)": 0, + "(5, '2024-11-27', 0)": 0, + "(5, '2024-11-27', 1)": 0, + "(5, '2024-11-27', 2)": 0, + "(5, '2024-11-27', 3)": 0, + "(5, '2024-11-27', 4)": 0, + "(5, '2024-11-27', 5)": 0, + "(5, '2024-11-27', 6)": 0, + "(5, '2024-11-27', 7)": 0, + "(5, '2024-11-28', 0)": 0, + "(5, '2024-11-28', 1)": 0, + "(5, '2024-11-28', 2)": 0, + "(5, '2024-11-28', 3)": 0, + "(5, '2024-11-28', 4)": 0, + "(5, '2024-11-28', 5)": 0, + "(5, '2024-11-28', 6)": 0, + "(5, '2024-11-28', 7)": 0, + "(5, '2024-11-29', 0)": 0, + "(5, '2024-11-29', 1)": 0, + "(5, '2024-11-29', 2)": 0, + "(5, '2024-11-29', 3)": 0, + "(5, '2024-11-29', 4)": 0, + "(5, '2024-11-29', 5)": 0, + "(5, '2024-11-29', 6)": 0, + "(5, '2024-11-29', 7)": 0, + "(5, '2024-11-30', 0)": 0, + "(5, '2024-11-30', 1)": 0, + "(5, '2024-11-30', 2)": 0, + "(5, '2024-11-30', 3)": 0, + "(5, '2024-11-30', 4)": 0, + "(5, '2024-11-30', 5)": 0, + "(5, '2024-11-30', 6)": 0, + "(5, '2024-11-30', 7)": 0, + "(5367, '2024-11-01', 0)": 0, + "(5367, '2024-11-01', 1)": 0, + "(5367, '2024-11-01', 2)": 1, + "(5367, '2024-11-01', 3)": 0, + "(5367, '2024-11-01', 4)": 0, + "(5367, '2024-11-01', 5)": 0, + "(5367, '2024-11-01', 6)": 0, + "(5367, '2024-11-01', 7)": 0, + "(5367, '2024-11-02', 0)": 0, + "(5367, '2024-11-02', 1)": 0, + "(5367, '2024-11-02', 2)": 1, + "(5367, '2024-11-02', 3)": 0, + "(5367, '2024-11-02', 4)": 0, + "(5367, '2024-11-02', 5)": 0, + "(5367, '2024-11-02', 6)": 0, + "(5367, '2024-11-02', 7)": 0, + "(5367, '2024-11-03', 0)": 0, + "(5367, '2024-11-03', 1)": 0, + "(5367, '2024-11-03', 2)": 1, + "(5367, '2024-11-03', 3)": 0, + "(5367, '2024-11-03', 4)": 0, + "(5367, '2024-11-03', 5)": 0, + "(5367, '2024-11-03', 6)": 0, + "(5367, '2024-11-03', 7)": 0, + "(5367, '2024-11-04', 0)": 0, + "(5367, '2024-11-04', 1)": 0, + "(5367, '2024-11-04', 2)": 0, + "(5367, '2024-11-04', 3)": 0, + "(5367, '2024-11-04', 4)": 0, + "(5367, '2024-11-04', 5)": 0, + "(5367, '2024-11-04', 6)": 0, + "(5367, '2024-11-04', 7)": 0, + "(5367, '2024-11-05', 0)": 0, + "(5367, '2024-11-05', 1)": 0, + "(5367, '2024-11-05', 2)": 0, + "(5367, '2024-11-05', 3)": 0, + "(5367, '2024-11-05', 4)": 0, + "(5367, '2024-11-05', 5)": 0, + "(5367, '2024-11-05', 6)": 0, + "(5367, '2024-11-05', 7)": 0, + "(5367, '2024-11-06', 0)": 0, + "(5367, '2024-11-06', 1)": 0, + "(5367, '2024-11-06', 2)": 1, + "(5367, '2024-11-06', 3)": 0, + "(5367, '2024-11-06', 4)": 0, + "(5367, '2024-11-06', 5)": 0, + "(5367, '2024-11-06', 6)": 0, + "(5367, '2024-11-06', 7)": 0, + "(5367, '2024-11-07', 0)": 0, + "(5367, '2024-11-07', 1)": 0, + "(5367, '2024-11-07', 2)": 0, + "(5367, '2024-11-07', 3)": 0, + "(5367, '2024-11-07', 4)": 0, + "(5367, '2024-11-07', 5)": 0, + "(5367, '2024-11-07', 6)": 0, + "(5367, '2024-11-07', 7)": 0, + "(5367, '2024-11-08', 0)": 0, + "(5367, '2024-11-08', 1)": 0, + "(5367, '2024-11-08', 2)": 0, + "(5367, '2024-11-08', 3)": 0, + "(5367, '2024-11-08', 4)": 0, + "(5367, '2024-11-08', 5)": 0, + "(5367, '2024-11-08', 6)": 0, + "(5367, '2024-11-08', 7)": 0, + "(5367, '2024-11-09', 0)": 1, + "(5367, '2024-11-09', 1)": 0, + "(5367, '2024-11-09', 2)": 0, + "(5367, '2024-11-09', 3)": 0, + "(5367, '2024-11-09', 4)": 0, + "(5367, '2024-11-09', 5)": 0, + "(5367, '2024-11-09', 6)": 0, + "(5367, '2024-11-09', 7)": 0, + "(5367, '2024-11-10', 0)": 0, + "(5367, '2024-11-10', 1)": 0, + "(5367, '2024-11-10', 2)": 0, + "(5367, '2024-11-10', 3)": 0, + "(5367, '2024-11-10', 4)": 0, + "(5367, '2024-11-10', 5)": 0, + "(5367, '2024-11-10', 6)": 0, + "(5367, '2024-11-10', 7)": 0, + "(5367, '2024-11-11', 0)": 1, + "(5367, '2024-11-11', 1)": 0, + "(5367, '2024-11-11', 2)": 0, + "(5367, '2024-11-11', 3)": 0, + "(5367, '2024-11-11', 4)": 0, + "(5367, '2024-11-11', 5)": 0, + "(5367, '2024-11-11', 6)": 0, + "(5367, '2024-11-11', 7)": 0, + "(5367, '2024-11-12', 0)": 0, + "(5367, '2024-11-12', 1)": 0, + "(5367, '2024-11-12', 2)": 1, + "(5367, '2024-11-12', 3)": 0, + "(5367, '2024-11-12', 4)": 0, + "(5367, '2024-11-12', 5)": 0, + "(5367, '2024-11-12', 6)": 0, + "(5367, '2024-11-12', 7)": 0, + "(5367, '2024-11-13', 0)": 0, + "(5367, '2024-11-13', 1)": 0, + "(5367, '2024-11-13', 2)": 0, + "(5367, '2024-11-13', 3)": 0, + "(5367, '2024-11-13', 4)": 0, + "(5367, '2024-11-13', 5)": 0, + "(5367, '2024-11-13', 6)": 0, + "(5367, '2024-11-13', 7)": 0, + "(5367, '2024-11-14', 0)": 1, + "(5367, '2024-11-14', 1)": 0, + "(5367, '2024-11-14', 2)": 0, + "(5367, '2024-11-14', 3)": 0, + "(5367, '2024-11-14', 4)": 0, + "(5367, '2024-11-14', 5)": 0, + "(5367, '2024-11-14', 6)": 0, + "(5367, '2024-11-14', 7)": 0, + "(5367, '2024-11-15', 0)": 0, + "(5367, '2024-11-15', 1)": 0, + "(5367, '2024-11-15', 2)": 1, + "(5367, '2024-11-15', 3)": 0, + "(5367, '2024-11-15', 4)": 0, + "(5367, '2024-11-15', 5)": 0, + "(5367, '2024-11-15', 6)": 0, + "(5367, '2024-11-15', 7)": 0, + "(5367, '2024-11-16', 0)": 0, + "(5367, '2024-11-16', 1)": 0, + "(5367, '2024-11-16', 2)": 0, + "(5367, '2024-11-16', 3)": 1, + "(5367, '2024-11-16', 4)": 0, + "(5367, '2024-11-16', 5)": 0, + "(5367, '2024-11-16', 6)": 0, + "(5367, '2024-11-16', 7)": 0, + "(5367, '2024-11-17', 0)": 0, + "(5367, '2024-11-17', 1)": 0, + "(5367, '2024-11-17', 2)": 0, + "(5367, '2024-11-17', 3)": 0, + "(5367, '2024-11-17', 4)": 0, + "(5367, '2024-11-17', 5)": 0, + "(5367, '2024-11-17', 6)": 0, + "(5367, '2024-11-17', 7)": 0, + "(5367, '2024-11-18', 0)": 0, + "(5367, '2024-11-18', 1)": 0, + "(5367, '2024-11-18', 2)": 1, + "(5367, '2024-11-18', 3)": 0, + "(5367, '2024-11-18', 4)": 0, + "(5367, '2024-11-18', 5)": 0, + "(5367, '2024-11-18', 6)": 0, + "(5367, '2024-11-18', 7)": 0, + "(5367, '2024-11-19', 0)": 0, + "(5367, '2024-11-19', 1)": 0, + "(5367, '2024-11-19', 2)": 0, + "(5367, '2024-11-19', 3)": 0, + "(5367, '2024-11-19', 4)": 0, + "(5367, '2024-11-19', 5)": 0, + "(5367, '2024-11-19', 6)": 0, + "(5367, '2024-11-19', 7)": 0, + "(5367, '2024-11-20', 0)": 1, + "(5367, '2024-11-20', 1)": 0, + "(5367, '2024-11-20', 2)": 0, + "(5367, '2024-11-20', 3)": 0, + "(5367, '2024-11-20', 4)": 0, + "(5367, '2024-11-20', 5)": 0, + "(5367, '2024-11-20', 6)": 0, + "(5367, '2024-11-20', 7)": 0, + "(5367, '2024-11-21', 0)": 1, + "(5367, '2024-11-21', 1)": 0, + "(5367, '2024-11-21', 2)": 0, + "(5367, '2024-11-21', 3)": 0, + "(5367, '2024-11-21', 4)": 0, + "(5367, '2024-11-21', 5)": 0, + "(5367, '2024-11-21', 6)": 0, + "(5367, '2024-11-21', 7)": 0, + "(5367, '2024-11-22', 0)": 1, + "(5367, '2024-11-22', 1)": 0, + "(5367, '2024-11-22', 2)": 0, + "(5367, '2024-11-22', 3)": 0, + "(5367, '2024-11-22', 4)": 0, + "(5367, '2024-11-22', 5)": 0, + "(5367, '2024-11-22', 6)": 0, + "(5367, '2024-11-22', 7)": 0, + "(5367, '2024-11-23', 0)": 0, + "(5367, '2024-11-23', 1)": 0, + "(5367, '2024-11-23', 2)": 0, + "(5367, '2024-11-23', 3)": 0, + "(5367, '2024-11-23', 4)": 0, + "(5367, '2024-11-23', 5)": 0, + "(5367, '2024-11-23', 6)": 0, + "(5367, '2024-11-23', 7)": 0, + "(5367, '2024-11-24', 0)": 1, + "(5367, '2024-11-24', 1)": 0, + "(5367, '2024-11-24', 2)": 0, + "(5367, '2024-11-24', 3)": 0, + "(5367, '2024-11-24', 4)": 0, + "(5367, '2024-11-24', 5)": 0, + "(5367, '2024-11-24', 6)": 0, + "(5367, '2024-11-24', 7)": 0, + "(5367, '2024-11-25', 0)": 1, + "(5367, '2024-11-25', 1)": 0, + "(5367, '2024-11-25', 2)": 0, + "(5367, '2024-11-25', 3)": 0, + "(5367, '2024-11-25', 4)": 0, + "(5367, '2024-11-25', 5)": 0, + "(5367, '2024-11-25', 6)": 0, + "(5367, '2024-11-25', 7)": 0, + "(5367, '2024-11-26', 0)": 0, + "(5367, '2024-11-26', 1)": 0, + "(5367, '2024-11-26', 2)": 0, + "(5367, '2024-11-26', 3)": 0, + "(5367, '2024-11-26', 4)": 0, + "(5367, '2024-11-26', 5)": 0, + "(5367, '2024-11-26', 6)": 0, + "(5367, '2024-11-26', 7)": 0, + "(5367, '2024-11-27', 0)": 1, + "(5367, '2024-11-27', 1)": 0, + "(5367, '2024-11-27', 2)": 0, + "(5367, '2024-11-27', 3)": 0, + "(5367, '2024-11-27', 4)": 0, + "(5367, '2024-11-27', 5)": 0, + "(5367, '2024-11-27', 6)": 0, + "(5367, '2024-11-27', 7)": 0, + "(5367, '2024-11-28', 0)": 1, + "(5367, '2024-11-28', 1)": 0, + "(5367, '2024-11-28', 2)": 0, + "(5367, '2024-11-28', 3)": 0, + "(5367, '2024-11-28', 4)": 0, + "(5367, '2024-11-28', 5)": 0, + "(5367, '2024-11-28', 6)": 0, + "(5367, '2024-11-28', 7)": 0, + "(5367, '2024-11-29', 0)": 1, + "(5367, '2024-11-29', 1)": 0, + "(5367, '2024-11-29', 2)": 0, + "(5367, '2024-11-29', 3)": 0, + "(5367, '2024-11-29', 4)": 0, + "(5367, '2024-11-29', 5)": 0, + "(5367, '2024-11-29', 6)": 0, + "(5367, '2024-11-29', 7)": 0, + "(5367, '2024-11-30', 0)": 0, + "(5367, '2024-11-30', 1)": 0, + "(5367, '2024-11-30', 2)": 0, + "(5367, '2024-11-30', 3)": 0, + "(5367, '2024-11-30', 4)": 0, + "(5367, '2024-11-30', 5)": 0, + "(5367, '2024-11-30', 6)": 0, + "(5367, '2024-11-30', 7)": 0, + "(5920, '2024-11-01', 0)": 0, + "(5920, '2024-11-01', 1)": 1, + "(5920, '2024-11-01', 2)": 0, + "(5920, '2024-11-01', 3)": 0, + "(5920, '2024-11-01', 4)": 0, + "(5920, '2024-11-01', 5)": 0, + "(5920, '2024-11-01', 6)": 0, + "(5920, '2024-11-01', 7)": 0, + "(5920, '2024-11-02', 0)": 0, + "(5920, '2024-11-02', 1)": 0, + "(5920, '2024-11-02', 2)": 0, + "(5920, '2024-11-02', 3)": 0, + "(5920, '2024-11-02', 4)": 0, + "(5920, '2024-11-02', 5)": 0, + "(5920, '2024-11-02', 6)": 0, + "(5920, '2024-11-02', 7)": 0, + "(5920, '2024-11-03', 0)": 0, + "(5920, '2024-11-03', 1)": 0, + "(5920, '2024-11-03', 2)": 0, + "(5920, '2024-11-03', 3)": 0, + "(5920, '2024-11-03', 4)": 0, + "(5920, '2024-11-03', 5)": 0, + "(5920, '2024-11-03', 6)": 0, + "(5920, '2024-11-03', 7)": 0, + "(5920, '2024-11-04', 0)": 0, + "(5920, '2024-11-04', 1)": 0, + "(5920, '2024-11-04', 2)": 0, + "(5920, '2024-11-04', 3)": 0, + "(5920, '2024-11-04', 4)": 0, + "(5920, '2024-11-04', 5)": 0, + "(5920, '2024-11-04', 6)": 0, + "(5920, '2024-11-04', 7)": 0, + "(5920, '2024-11-05', 0)": 0, + "(5920, '2024-11-05', 1)": 1, + "(5920, '2024-11-05', 2)": 0, + "(5920, '2024-11-05', 3)": 0, + "(5920, '2024-11-05', 4)": 0, + "(5920, '2024-11-05', 5)": 0, + "(5920, '2024-11-05', 6)": 0, + "(5920, '2024-11-05', 7)": 0, + "(5920, '2024-11-06', 0)": 0, + "(5920, '2024-11-06', 1)": 0, + "(5920, '2024-11-06', 2)": 0, + "(5920, '2024-11-06', 3)": 1, + "(5920, '2024-11-06', 4)": 0, + "(5920, '2024-11-06', 5)": 0, + "(5920, '2024-11-06', 6)": 0, + "(5920, '2024-11-06', 7)": 0, + "(5920, '2024-11-07', 0)": 0, + "(5920, '2024-11-07', 1)": 0, + "(5920, '2024-11-07', 2)": 0, + "(5920, '2024-11-07', 3)": 1, + "(5920, '2024-11-07', 4)": 0, + "(5920, '2024-11-07', 5)": 0, + "(5920, '2024-11-07', 6)": 0, + "(5920, '2024-11-07', 7)": 0, + "(5920, '2024-11-08', 0)": 0, + "(5920, '2024-11-08', 1)": 0, + "(5920, '2024-11-08', 2)": 0, + "(5920, '2024-11-08', 3)": 1, + "(5920, '2024-11-08', 4)": 0, + "(5920, '2024-11-08', 5)": 0, + "(5920, '2024-11-08', 6)": 0, + "(5920, '2024-11-08', 7)": 0, + "(5920, '2024-11-09', 0)": 0, + "(5920, '2024-11-09', 1)": 0, + "(5920, '2024-11-09', 2)": 0, + "(5920, '2024-11-09', 3)": 0, + "(5920, '2024-11-09', 4)": 0, + "(5920, '2024-11-09', 5)": 0, + "(5920, '2024-11-09', 6)": 0, + "(5920, '2024-11-09', 7)": 0, + "(5920, '2024-11-10', 0)": 0, + "(5920, '2024-11-10', 1)": 0, + "(5920, '2024-11-10', 2)": 1, + "(5920, '2024-11-10', 3)": 0, + "(5920, '2024-11-10', 4)": 0, + "(5920, '2024-11-10', 5)": 0, + "(5920, '2024-11-10', 6)": 0, + "(5920, '2024-11-10', 7)": 0, + "(5920, '2024-11-11', 0)": 0, + "(5920, '2024-11-11', 1)": 0, + "(5920, '2024-11-11', 2)": 1, + "(5920, '2024-11-11', 3)": 0, + "(5920, '2024-11-11', 4)": 0, + "(5920, '2024-11-11', 5)": 0, + "(5920, '2024-11-11', 6)": 0, + "(5920, '2024-11-11', 7)": 0, + "(5920, '2024-11-12', 0)": 0, + "(5920, '2024-11-12', 1)": 0, + "(5920, '2024-11-12', 2)": 0, + "(5920, '2024-11-12', 3)": 0, + "(5920, '2024-11-12', 4)": 0, + "(5920, '2024-11-12', 5)": 0, + "(5920, '2024-11-12', 6)": 0, + "(5920, '2024-11-12', 7)": 0, + "(5920, '2024-11-13', 0)": 1, + "(5920, '2024-11-13', 1)": 0, + "(5920, '2024-11-13', 2)": 0, + "(5920, '2024-11-13', 3)": 0, + "(5920, '2024-11-13', 4)": 0, + "(5920, '2024-11-13', 5)": 0, + "(5920, '2024-11-13', 6)": 0, + "(5920, '2024-11-13', 7)": 0, + "(5920, '2024-11-14', 0)": 0, + "(5920, '2024-11-14', 1)": 0, + "(5920, '2024-11-14', 2)": 0, + "(5920, '2024-11-14', 3)": 1, + "(5920, '2024-11-14', 4)": 0, + "(5920, '2024-11-14', 5)": 0, + "(5920, '2024-11-14', 6)": 0, + "(5920, '2024-11-14', 7)": 0, + "(5920, '2024-11-15', 0)": 0, + "(5920, '2024-11-15', 1)": 0, + "(5920, '2024-11-15', 2)": 0, + "(5920, '2024-11-15', 3)": 0, + "(5920, '2024-11-15', 4)": 0, + "(5920, '2024-11-15', 5)": 0, + "(5920, '2024-11-15', 6)": 0, + "(5920, '2024-11-15', 7)": 0, + "(5920, '2024-11-16', 0)": 1, + "(5920, '2024-11-16', 1)": 0, + "(5920, '2024-11-16', 2)": 0, + "(5920, '2024-11-16', 3)": 0, + "(5920, '2024-11-16', 4)": 0, + "(5920, '2024-11-16', 5)": 0, + "(5920, '2024-11-16', 6)": 0, + "(5920, '2024-11-16', 7)": 0, + "(5920, '2024-11-17', 0)": 0, + "(5920, '2024-11-17', 1)": 0, + "(5920, '2024-11-17', 2)": 1, + "(5920, '2024-11-17', 3)": 0, + "(5920, '2024-11-17', 4)": 0, + "(5920, '2024-11-17', 5)": 0, + "(5920, '2024-11-17', 6)": 0, + "(5920, '2024-11-17', 7)": 0, + "(5920, '2024-11-18', 0)": 0, + "(5920, '2024-11-18', 1)": 0, + "(5920, '2024-11-18', 2)": 0, + "(5920, '2024-11-18', 3)": 0, + "(5920, '2024-11-18', 4)": 0, + "(5920, '2024-11-18', 5)": 0, + "(5920, '2024-11-18', 6)": 0, + "(5920, '2024-11-18', 7)": 0, + "(5920, '2024-11-19', 0)": 0, + "(5920, '2024-11-19', 1)": 0, + "(5920, '2024-11-19', 2)": 1, + "(5920, '2024-11-19', 3)": 0, + "(5920, '2024-11-19', 4)": 0, + "(5920, '2024-11-19', 5)": 0, + "(5920, '2024-11-19', 6)": 0, + "(5920, '2024-11-19', 7)": 0, + "(5920, '2024-11-20', 0)": 0, + "(5920, '2024-11-20', 1)": 0, + "(5920, '2024-11-20', 2)": 0, + "(5920, '2024-11-20', 3)": 0, + "(5920, '2024-11-20', 4)": 0, + "(5920, '2024-11-20', 5)": 0, + "(5920, '2024-11-20', 6)": 0, + "(5920, '2024-11-20', 7)": 0, + "(5920, '2024-11-21', 0)": 0, + "(5920, '2024-11-21', 1)": 0, + "(5920, '2024-11-21', 2)": 0, + "(5920, '2024-11-21', 3)": 0, + "(5920, '2024-11-21', 4)": 0, + "(5920, '2024-11-21', 5)": 0, + "(5920, '2024-11-21', 6)": 0, + "(5920, '2024-11-21', 7)": 0, + "(5920, '2024-11-22', 0)": 0, + "(5920, '2024-11-22', 1)": 0, + "(5920, '2024-11-22', 2)": 1, + "(5920, '2024-11-22', 3)": 0, + "(5920, '2024-11-22', 4)": 0, + "(5920, '2024-11-22', 5)": 0, + "(5920, '2024-11-22', 6)": 0, + "(5920, '2024-11-22', 7)": 0, + "(5920, '2024-11-23', 0)": 0, + "(5920, '2024-11-23', 1)": 1, + "(5920, '2024-11-23', 2)": 0, + "(5920, '2024-11-23', 3)": 0, + "(5920, '2024-11-23', 4)": 0, + "(5920, '2024-11-23', 5)": 0, + "(5920, '2024-11-23', 6)": 0, + "(5920, '2024-11-23', 7)": 0, + "(5920, '2024-11-24', 0)": 0, + "(5920, '2024-11-24', 1)": 0, + "(5920, '2024-11-24', 2)": 0, + "(5920, '2024-11-24', 3)": 0, + "(5920, '2024-11-24', 4)": 0, + "(5920, '2024-11-24', 5)": 0, + "(5920, '2024-11-24', 6)": 0, + "(5920, '2024-11-24', 7)": 0, + "(5920, '2024-11-25', 0)": 0, + "(5920, '2024-11-25', 1)": 0, + "(5920, '2024-11-25', 2)": 0, + "(5920, '2024-11-25', 3)": 1, + "(5920, '2024-11-25', 4)": 0, + "(5920, '2024-11-25', 5)": 0, + "(5920, '2024-11-25', 6)": 0, + "(5920, '2024-11-25', 7)": 0, + "(5920, '2024-11-26', 0)": 0, + "(5920, '2024-11-26', 1)": 0, + "(5920, '2024-11-26', 2)": 0, + "(5920, '2024-11-26', 3)": 0, + "(5920, '2024-11-26', 4)": 0, + "(5920, '2024-11-26', 5)": 0, + "(5920, '2024-11-26', 6)": 0, + "(5920, '2024-11-26', 7)": 0, + "(5920, '2024-11-27', 0)": 1, + "(5920, '2024-11-27', 1)": 0, + "(5920, '2024-11-27', 2)": 0, + "(5920, '2024-11-27', 3)": 0, + "(5920, '2024-11-27', 4)": 0, + "(5920, '2024-11-27', 5)": 0, + "(5920, '2024-11-27', 6)": 0, + "(5920, '2024-11-27', 7)": 0, + "(5920, '2024-11-28', 0)": 0, + "(5920, '2024-11-28', 1)": 1, + "(5920, '2024-11-28', 2)": 0, + "(5920, '2024-11-28', 3)": 0, + "(5920, '2024-11-28', 4)": 0, + "(5920, '2024-11-28', 5)": 0, + "(5920, '2024-11-28', 6)": 0, + "(5920, '2024-11-28', 7)": 0, + "(5920, '2024-11-29', 0)": 0, + "(5920, '2024-11-29', 1)": 0, + "(5920, '2024-11-29', 2)": 0, + "(5920, '2024-11-29', 3)": 1, + "(5920, '2024-11-29', 4)": 0, + "(5920, '2024-11-29', 5)": 0, + "(5920, '2024-11-29', 6)": 0, + "(5920, '2024-11-29', 7)": 0, + "(5920, '2024-11-30', 0)": 0, + "(5920, '2024-11-30', 1)": 0, + "(5920, '2024-11-30', 2)": 0, + "(5920, '2024-11-30', 3)": 0, + "(5920, '2024-11-30', 4)": 0, + "(5920, '2024-11-30', 5)": 0, + "(5920, '2024-11-30', 6)": 0, + "(5920, '2024-11-30', 7)": 0, + "(6, '2024-11-01', 0)": 1, + "(6, '2024-11-01', 1)": 0, + "(6, '2024-11-01', 2)": 0, + "(6, '2024-11-01', 3)": 0, + "(6, '2024-11-01', 4)": 0, + "(6, '2024-11-01', 5)": 0, + "(6, '2024-11-01', 6)": 0, + "(6, '2024-11-01', 7)": 0, + "(6, '2024-11-02', 0)": 0, + "(6, '2024-11-02', 1)": 0, + "(6, '2024-11-02', 2)": 0, + "(6, '2024-11-02', 3)": 0, + "(6, '2024-11-02', 4)": 0, + "(6, '2024-11-02', 5)": 0, + "(6, '2024-11-02', 6)": 0, + "(6, '2024-11-02', 7)": 0, + "(6, '2024-11-03', 0)": 0, + "(6, '2024-11-03', 1)": 0, + "(6, '2024-11-03', 2)": 0, + "(6, '2024-11-03', 3)": 0, + "(6, '2024-11-03', 4)": 0, + "(6, '2024-11-03', 5)": 0, + "(6, '2024-11-03', 6)": 0, + "(6, '2024-11-03', 7)": 0, + "(6, '2024-11-04', 0)": 0, + "(6, '2024-11-04', 1)": 0, + "(6, '2024-11-04', 2)": 0, + "(6, '2024-11-04', 3)": 0, + "(6, '2024-11-04', 4)": 0, + "(6, '2024-11-04', 5)": 0, + "(6, '2024-11-04', 6)": 0, + "(6, '2024-11-04', 7)": 0, + "(6, '2024-11-05', 0)": 0, + "(6, '2024-11-05', 1)": 0, + "(6, '2024-11-05', 2)": 0, + "(6, '2024-11-05', 3)": 0, + "(6, '2024-11-05', 4)": 0, + "(6, '2024-11-05', 5)": 0, + "(6, '2024-11-05', 6)": 0, + "(6, '2024-11-05', 7)": 0, + "(6, '2024-11-06', 0)": 1, + "(6, '2024-11-06', 1)": 0, + "(6, '2024-11-06', 2)": 0, + "(6, '2024-11-06', 3)": 0, + "(6, '2024-11-06', 4)": 0, + "(6, '2024-11-06', 5)": 0, + "(6, '2024-11-06', 6)": 0, + "(6, '2024-11-06', 7)": 0, + "(6, '2024-11-07', 0)": 0, + "(6, '2024-11-07', 1)": 0, + "(6, '2024-11-07', 2)": 0, + "(6, '2024-11-07', 3)": 0, + "(6, '2024-11-07', 4)": 0, + "(6, '2024-11-07', 5)": 0, + "(6, '2024-11-07', 6)": 0, + "(6, '2024-11-07', 7)": 0, + "(6, '2024-11-08', 0)": 1, + "(6, '2024-11-08', 1)": 0, + "(6, '2024-11-08', 2)": 0, + "(6, '2024-11-08', 3)": 0, + "(6, '2024-11-08', 4)": 0, + "(6, '2024-11-08', 5)": 0, + "(6, '2024-11-08', 6)": 0, + "(6, '2024-11-08', 7)": 0, + "(6, '2024-11-09', 0)": 0, + "(6, '2024-11-09', 1)": 0, + "(6, '2024-11-09', 2)": 0, + "(6, '2024-11-09', 3)": 0, + "(6, '2024-11-09', 4)": 0, + "(6, '2024-11-09', 5)": 0, + "(6, '2024-11-09', 6)": 0, + "(6, '2024-11-09', 7)": 0, + "(6, '2024-11-10', 0)": 1, + "(6, '2024-11-10', 1)": 0, + "(6, '2024-11-10', 2)": 0, + "(6, '2024-11-10', 3)": 0, + "(6, '2024-11-10', 4)": 0, + "(6, '2024-11-10', 5)": 0, + "(6, '2024-11-10', 6)": 0, + "(6, '2024-11-10', 7)": 0, + "(6, '2024-11-11', 0)": 0, + "(6, '2024-11-11', 1)": 0, + "(6, '2024-11-11', 2)": 0, + "(6, '2024-11-11', 3)": 0, + "(6, '2024-11-11', 4)": 0, + "(6, '2024-11-11', 5)": 0, + "(6, '2024-11-11', 6)": 0, + "(6, '2024-11-11', 7)": 0, + "(6, '2024-11-12', 0)": 0, + "(6, '2024-11-12', 1)": 0, + "(6, '2024-11-12', 2)": 0, + "(6, '2024-11-12', 3)": 0, + "(6, '2024-11-12', 4)": 0, + "(6, '2024-11-12', 5)": 0, + "(6, '2024-11-12', 6)": 0, + "(6, '2024-11-12', 7)": 0, + "(6, '2024-11-13', 0)": 0, + "(6, '2024-11-13', 1)": 0, + "(6, '2024-11-13', 2)": 0, + "(6, '2024-11-13', 3)": 0, + "(6, '2024-11-13', 4)": 0, + "(6, '2024-11-13', 5)": 0, + "(6, '2024-11-13', 6)": 0, + "(6, '2024-11-13', 7)": 0, + "(6, '2024-11-14', 0)": 1, + "(6, '2024-11-14', 1)": 0, + "(6, '2024-11-14', 2)": 0, + "(6, '2024-11-14', 3)": 0, + "(6, '2024-11-14', 4)": 0, + "(6, '2024-11-14', 5)": 0, + "(6, '2024-11-14', 6)": 0, + "(6, '2024-11-14', 7)": 0, + "(6, '2024-11-15', 0)": 0, + "(6, '2024-11-15', 1)": 0, + "(6, '2024-11-15', 2)": 0, + "(6, '2024-11-15', 3)": 0, + "(6, '2024-11-15', 4)": 0, + "(6, '2024-11-15', 5)": 0, + "(6, '2024-11-15', 6)": 0, + "(6, '2024-11-15', 7)": 0, + "(6, '2024-11-16', 0)": 1, + "(6, '2024-11-16', 1)": 0, + "(6, '2024-11-16', 2)": 0, + "(6, '2024-11-16', 3)": 0, + "(6, '2024-11-16', 4)": 0, + "(6, '2024-11-16', 5)": 0, + "(6, '2024-11-16', 6)": 0, + "(6, '2024-11-16', 7)": 0, + "(6, '2024-11-17', 0)": 0, + "(6, '2024-11-17', 1)": 0, + "(6, '2024-11-17', 2)": 0, + "(6, '2024-11-17', 3)": 1, + "(6, '2024-11-17', 4)": 0, + "(6, '2024-11-17', 5)": 0, + "(6, '2024-11-17', 6)": 0, + "(6, '2024-11-17', 7)": 0, + "(6, '2024-11-18', 0)": 0, + "(6, '2024-11-18', 1)": 0, + "(6, '2024-11-18', 2)": 0, + "(6, '2024-11-18', 3)": 0, + "(6, '2024-11-18', 4)": 0, + "(6, '2024-11-18', 5)": 0, + "(6, '2024-11-18', 6)": 0, + "(6, '2024-11-18', 7)": 0, + "(6, '2024-11-19', 0)": 0, + "(6, '2024-11-19', 1)": 0, + "(6, '2024-11-19', 2)": 1, + "(6, '2024-11-19', 3)": 0, + "(6, '2024-11-19', 4)": 0, + "(6, '2024-11-19', 5)": 0, + "(6, '2024-11-19', 6)": 0, + "(6, '2024-11-19', 7)": 0, + "(6, '2024-11-20', 0)": 0, + "(6, '2024-11-20', 1)": 0, + "(6, '2024-11-20', 2)": 0, + "(6, '2024-11-20', 3)": 0, + "(6, '2024-11-20', 4)": 0, + "(6, '2024-11-20', 5)": 0, + "(6, '2024-11-20', 6)": 0, + "(6, '2024-11-20', 7)": 0, + "(6, '2024-11-21', 0)": 0, + "(6, '2024-11-21', 1)": 0, + "(6, '2024-11-21', 2)": 0, + "(6, '2024-11-21', 3)": 0, + "(6, '2024-11-21', 4)": 0, + "(6, '2024-11-21', 5)": 0, + "(6, '2024-11-21', 6)": 0, + "(6, '2024-11-21', 7)": 0, + "(6, '2024-11-22', 0)": 0, + "(6, '2024-11-22', 1)": 0, + "(6, '2024-11-22', 2)": 0, + "(6, '2024-11-22', 3)": 0, + "(6, '2024-11-22', 4)": 0, + "(6, '2024-11-22', 5)": 0, + "(6, '2024-11-22', 6)": 0, + "(6, '2024-11-22', 7)": 0, + "(6, '2024-11-23', 0)": 0, + "(6, '2024-11-23', 1)": 0, + "(6, '2024-11-23', 2)": 0, + "(6, '2024-11-23', 3)": 0, + "(6, '2024-11-23', 4)": 0, + "(6, '2024-11-23', 5)": 0, + "(6, '2024-11-23', 6)": 0, + "(6, '2024-11-23', 7)": 0, + "(6, '2024-11-24', 0)": 0, + "(6, '2024-11-24', 1)": 0, + "(6, '2024-11-24', 2)": 1, + "(6, '2024-11-24', 3)": 0, + "(6, '2024-11-24', 4)": 0, + "(6, '2024-11-24', 5)": 0, + "(6, '2024-11-24', 6)": 0, + "(6, '2024-11-24', 7)": 0, + "(6, '2024-11-25', 0)": 0, + "(6, '2024-11-25', 1)": 0, + "(6, '2024-11-25', 2)": 0, + "(6, '2024-11-25', 3)": 0, + "(6, '2024-11-25', 4)": 0, + "(6, '2024-11-25', 5)": 0, + "(6, '2024-11-25', 6)": 0, + "(6, '2024-11-25', 7)": 0, + "(6, '2024-11-26', 0)": 0, + "(6, '2024-11-26', 1)": 0, + "(6, '2024-11-26', 2)": 0, + "(6, '2024-11-26', 3)": 0, + "(6, '2024-11-26', 4)": 0, + "(6, '2024-11-26', 5)": 0, + "(6, '2024-11-26', 6)": 0, + "(6, '2024-11-26', 7)": 0, + "(6, '2024-11-27', 0)": 0, + "(6, '2024-11-27', 1)": 0, + "(6, '2024-11-27', 2)": 0, + "(6, '2024-11-27', 3)": 0, + "(6, '2024-11-27', 4)": 0, + "(6, '2024-11-27', 5)": 0, + "(6, '2024-11-27', 6)": 0, + "(6, '2024-11-27', 7)": 0, + "(6, '2024-11-28', 0)": 0, + "(6, '2024-11-28', 1)": 0, + "(6, '2024-11-28', 2)": 0, + "(6, '2024-11-28', 3)": 0, + "(6, '2024-11-28', 4)": 0, + "(6, '2024-11-28', 5)": 0, + "(6, '2024-11-28', 6)": 0, + "(6, '2024-11-28', 7)": 0, + "(6, '2024-11-29', 0)": 0, + "(6, '2024-11-29', 1)": 0, + "(6, '2024-11-29', 2)": 0, + "(6, '2024-11-29', 3)": 0, + "(6, '2024-11-29', 4)": 0, + "(6, '2024-11-29', 5)": 0, + "(6, '2024-11-29', 6)": 0, + "(6, '2024-11-29', 7)": 0, + "(6, '2024-11-30', 0)": 0, + "(6, '2024-11-30', 1)": 0, + "(6, '2024-11-30', 2)": 0, + "(6, '2024-11-30', 3)": 0, + "(6, '2024-11-30', 4)": 0, + "(6, '2024-11-30', 5)": 0, + "(6, '2024-11-30', 6)": 0, + "(6, '2024-11-30', 7)": 0, + "(6475, '2024-11-01', 0)": 0, + "(6475, '2024-11-01', 1)": 0, + "(6475, '2024-11-01', 2)": 0, + "(6475, '2024-11-01', 3)": 0, + "(6475, '2024-11-01', 4)": 0, + "(6475, '2024-11-01', 5)": 0, + "(6475, '2024-11-01', 6)": 0, + "(6475, '2024-11-01', 7)": 0, + "(6475, '2024-11-02', 0)": 0, + "(6475, '2024-11-02', 1)": 0, + "(6475, '2024-11-02', 2)": 0, + "(6475, '2024-11-02', 3)": 0, + "(6475, '2024-11-02', 4)": 0, + "(6475, '2024-11-02', 5)": 0, + "(6475, '2024-11-02', 6)": 0, + "(6475, '2024-11-02', 7)": 0, + "(6475, '2024-11-03', 0)": 0, + "(6475, '2024-11-03', 1)": 0, + "(6475, '2024-11-03', 2)": 0, + "(6475, '2024-11-03', 3)": 0, + "(6475, '2024-11-03', 4)": 0, + "(6475, '2024-11-03', 5)": 0, + "(6475, '2024-11-03', 6)": 0, + "(6475, '2024-11-03', 7)": 0, + "(6475, '2024-11-04', 0)": 0, + "(6475, '2024-11-04', 1)": 0, + "(6475, '2024-11-04', 2)": 0, + "(6475, '2024-11-04', 3)": 0, + "(6475, '2024-11-04', 4)": 0, + "(6475, '2024-11-04', 5)": 0, + "(6475, '2024-11-04', 6)": 0, + "(6475, '2024-11-04', 7)": 0, + "(6475, '2024-11-05', 0)": 0, + "(6475, '2024-11-05', 1)": 0, + "(6475, '2024-11-05', 2)": 0, + "(6475, '2024-11-05', 3)": 0, + "(6475, '2024-11-05', 4)": 0, + "(6475, '2024-11-05', 5)": 0, + "(6475, '2024-11-05', 6)": 0, + "(6475, '2024-11-05', 7)": 0, + "(6475, '2024-11-06', 0)": 0, + "(6475, '2024-11-06', 1)": 0, + "(6475, '2024-11-06', 2)": 0, + "(6475, '2024-11-06', 3)": 0, + "(6475, '2024-11-06', 4)": 0, + "(6475, '2024-11-06', 5)": 0, + "(6475, '2024-11-06', 6)": 0, + "(6475, '2024-11-06', 7)": 0, + "(6475, '2024-11-07', 0)": 0, + "(6475, '2024-11-07', 1)": 0, + "(6475, '2024-11-07', 2)": 0, + "(6475, '2024-11-07', 3)": 0, + "(6475, '2024-11-07', 4)": 0, + "(6475, '2024-11-07', 5)": 0, + "(6475, '2024-11-07', 6)": 0, + "(6475, '2024-11-07', 7)": 0, + "(6475, '2024-11-08', 0)": 0, + "(6475, '2024-11-08', 1)": 0, + "(6475, '2024-11-08', 2)": 0, + "(6475, '2024-11-08', 3)": 0, + "(6475, '2024-11-08', 4)": 0, + "(6475, '2024-11-08', 5)": 0, + "(6475, '2024-11-08', 6)": 0, + "(6475, '2024-11-08', 7)": 0, + "(6475, '2024-11-09', 0)": 0, + "(6475, '2024-11-09', 1)": 0, + "(6475, '2024-11-09', 2)": 0, + "(6475, '2024-11-09', 3)": 0, + "(6475, '2024-11-09', 4)": 0, + "(6475, '2024-11-09', 5)": 0, + "(6475, '2024-11-09', 6)": 0, + "(6475, '2024-11-09', 7)": 0, + "(6475, '2024-11-10', 0)": 0, + "(6475, '2024-11-10', 1)": 0, + "(6475, '2024-11-10', 2)": 0, + "(6475, '2024-11-10', 3)": 0, + "(6475, '2024-11-10', 4)": 0, + "(6475, '2024-11-10', 5)": 0, + "(6475, '2024-11-10', 6)": 0, + "(6475, '2024-11-10', 7)": 0, + "(6475, '2024-11-11', 0)": 0, + "(6475, '2024-11-11', 1)": 0, + "(6475, '2024-11-11', 2)": 0, + "(6475, '2024-11-11', 3)": 0, + "(6475, '2024-11-11', 4)": 0, + "(6475, '2024-11-11', 5)": 0, + "(6475, '2024-11-11', 6)": 0, + "(6475, '2024-11-11', 7)": 0, + "(6475, '2024-11-12', 0)": 0, + "(6475, '2024-11-12', 1)": 0, + "(6475, '2024-11-12', 2)": 0, + "(6475, '2024-11-12', 3)": 0, + "(6475, '2024-11-12', 4)": 0, + "(6475, '2024-11-12', 5)": 0, + "(6475, '2024-11-12', 6)": 0, + "(6475, '2024-11-12', 7)": 0, + "(6475, '2024-11-13', 0)": 0, + "(6475, '2024-11-13', 1)": 0, + "(6475, '2024-11-13', 2)": 0, + "(6475, '2024-11-13', 3)": 0, + "(6475, '2024-11-13', 4)": 0, + "(6475, '2024-11-13', 5)": 0, + "(6475, '2024-11-13', 6)": 0, + "(6475, '2024-11-13', 7)": 0, + "(6475, '2024-11-14', 0)": 0, + "(6475, '2024-11-14', 1)": 0, + "(6475, '2024-11-14', 2)": 0, + "(6475, '2024-11-14', 3)": 0, + "(6475, '2024-11-14', 4)": 0, + "(6475, '2024-11-14', 5)": 0, + "(6475, '2024-11-14', 6)": 0, + "(6475, '2024-11-14', 7)": 0, + "(6475, '2024-11-15', 0)": 0, + "(6475, '2024-11-15', 1)": 0, + "(6475, '2024-11-15', 2)": 0, + "(6475, '2024-11-15', 3)": 0, + "(6475, '2024-11-15', 4)": 0, + "(6475, '2024-11-15', 5)": 0, + "(6475, '2024-11-15', 6)": 0, + "(6475, '2024-11-15', 7)": 0, + "(6475, '2024-11-16', 0)": 0, + "(6475, '2024-11-16', 1)": 0, + "(6475, '2024-11-16', 2)": 0, + "(6475, '2024-11-16', 3)": 0, + "(6475, '2024-11-16', 4)": 0, + "(6475, '2024-11-16', 5)": 0, + "(6475, '2024-11-16', 6)": 0, + "(6475, '2024-11-16', 7)": 0, + "(6475, '2024-11-17', 0)": 0, + "(6475, '2024-11-17', 1)": 0, + "(6475, '2024-11-17', 2)": 0, + "(6475, '2024-11-17', 3)": 0, + "(6475, '2024-11-17', 4)": 0, + "(6475, '2024-11-17', 5)": 0, + "(6475, '2024-11-17', 6)": 0, + "(6475, '2024-11-17', 7)": 0, + "(6475, '2024-11-18', 0)": 0, + "(6475, '2024-11-18', 1)": 0, + "(6475, '2024-11-18', 2)": 0, + "(6475, '2024-11-18', 3)": 0, + "(6475, '2024-11-18', 4)": 0, + "(6475, '2024-11-18', 5)": 0, + "(6475, '2024-11-18', 6)": 0, + "(6475, '2024-11-18', 7)": 0, + "(6475, '2024-11-19', 0)": 0, + "(6475, '2024-11-19', 1)": 0, + "(6475, '2024-11-19', 2)": 0, + "(6475, '2024-11-19', 3)": 0, + "(6475, '2024-11-19', 4)": 0, + "(6475, '2024-11-19', 5)": 0, + "(6475, '2024-11-19', 6)": 0, + "(6475, '2024-11-19', 7)": 0, + "(6475, '2024-11-20', 0)": 0, + "(6475, '2024-11-20', 1)": 0, + "(6475, '2024-11-20', 2)": 0, + "(6475, '2024-11-20', 3)": 0, + "(6475, '2024-11-20', 4)": 0, + "(6475, '2024-11-20', 5)": 0, + "(6475, '2024-11-20', 6)": 0, + "(6475, '2024-11-20', 7)": 0, + "(6475, '2024-11-21', 0)": 0, + "(6475, '2024-11-21', 1)": 0, + "(6475, '2024-11-21', 2)": 0, + "(6475, '2024-11-21', 3)": 0, + "(6475, '2024-11-21', 4)": 0, + "(6475, '2024-11-21', 5)": 0, + "(6475, '2024-11-21', 6)": 0, + "(6475, '2024-11-21', 7)": 0, + "(6475, '2024-11-22', 0)": 0, + "(6475, '2024-11-22', 1)": 0, + "(6475, '2024-11-22', 2)": 0, + "(6475, '2024-11-22', 3)": 0, + "(6475, '2024-11-22', 4)": 0, + "(6475, '2024-11-22', 5)": 0, + "(6475, '2024-11-22', 6)": 0, + "(6475, '2024-11-22', 7)": 0, + "(6475, '2024-11-23', 0)": 0, + "(6475, '2024-11-23', 1)": 0, + "(6475, '2024-11-23', 2)": 0, + "(6475, '2024-11-23', 3)": 0, + "(6475, '2024-11-23', 4)": 0, + "(6475, '2024-11-23', 5)": 0, + "(6475, '2024-11-23', 6)": 0, + "(6475, '2024-11-23', 7)": 0, + "(6475, '2024-11-24', 0)": 0, + "(6475, '2024-11-24', 1)": 0, + "(6475, '2024-11-24', 2)": 0, + "(6475, '2024-11-24', 3)": 0, + "(6475, '2024-11-24', 4)": 0, + "(6475, '2024-11-24', 5)": 0, + "(6475, '2024-11-24', 6)": 0, + "(6475, '2024-11-24', 7)": 0, + "(6475, '2024-11-25', 0)": 0, + "(6475, '2024-11-25', 1)": 0, + "(6475, '2024-11-25', 2)": 0, + "(6475, '2024-11-25', 3)": 0, + "(6475, '2024-11-25', 4)": 0, + "(6475, '2024-11-25', 5)": 0, + "(6475, '2024-11-25', 6)": 0, + "(6475, '2024-11-25', 7)": 0, + "(6475, '2024-11-26', 0)": 0, + "(6475, '2024-11-26', 1)": 0, + "(6475, '2024-11-26', 2)": 0, + "(6475, '2024-11-26', 3)": 0, + "(6475, '2024-11-26', 4)": 0, + "(6475, '2024-11-26', 5)": 0, + "(6475, '2024-11-26', 6)": 0, + "(6475, '2024-11-26', 7)": 0, + "(6475, '2024-11-27', 0)": 0, + "(6475, '2024-11-27', 1)": 0, + "(6475, '2024-11-27', 2)": 0, + "(6475, '2024-11-27', 3)": 0, + "(6475, '2024-11-27', 4)": 0, + "(6475, '2024-11-27', 5)": 0, + "(6475, '2024-11-27', 6)": 0, + "(6475, '2024-11-27', 7)": 0, + "(6475, '2024-11-28', 0)": 0, + "(6475, '2024-11-28', 1)": 0, + "(6475, '2024-11-28', 2)": 0, + "(6475, '2024-11-28', 3)": 0, + "(6475, '2024-11-28', 4)": 0, + "(6475, '2024-11-28', 5)": 0, + "(6475, '2024-11-28', 6)": 0, + "(6475, '2024-11-28', 7)": 0, + "(6475, '2024-11-29', 0)": 0, + "(6475, '2024-11-29', 1)": 0, + "(6475, '2024-11-29', 2)": 0, + "(6475, '2024-11-29', 3)": 0, + "(6475, '2024-11-29', 4)": 0, + "(6475, '2024-11-29', 5)": 0, + "(6475, '2024-11-29', 6)": 0, + "(6475, '2024-11-29', 7)": 0, + "(6475, '2024-11-30', 0)": 1, + "(6475, '2024-11-30', 1)": 0, + "(6475, '2024-11-30', 2)": 0, + "(6475, '2024-11-30', 3)": 0, + "(6475, '2024-11-30', 4)": 0, + "(6475, '2024-11-30', 5)": 0, + "(6475, '2024-11-30', 6)": 0, + "(6475, '2024-11-30', 7)": 0, + "(6507, '2024-11-01', 0)": 0, + "(6507, '2024-11-01', 1)": 0, + "(6507, '2024-11-01', 2)": 0, + "(6507, '2024-11-01', 3)": 0, + "(6507, '2024-11-01', 4)": 0, + "(6507, '2024-11-01', 5)": 0, + "(6507, '2024-11-01', 6)": 0, + "(6507, '2024-11-01', 7)": 0, + "(6507, '2024-11-02', 0)": 1, + "(6507, '2024-11-02', 1)": 0, + "(6507, '2024-11-02', 2)": 0, + "(6507, '2024-11-02', 3)": 0, + "(6507, '2024-11-02', 4)": 0, + "(6507, '2024-11-02', 5)": 0, + "(6507, '2024-11-02', 6)": 0, + "(6507, '2024-11-02', 7)": 0, + "(6507, '2024-11-03', 0)": 0, + "(6507, '2024-11-03', 1)": 0, + "(6507, '2024-11-03', 2)": 0, + "(6507, '2024-11-03', 3)": 0, + "(6507, '2024-11-03', 4)": 0, + "(6507, '2024-11-03', 5)": 0, + "(6507, '2024-11-03', 6)": 0, + "(6507, '2024-11-03', 7)": 0, + "(6507, '2024-11-04', 0)": 1, + "(6507, '2024-11-04', 1)": 0, + "(6507, '2024-11-04', 2)": 0, + "(6507, '2024-11-04', 3)": 0, + "(6507, '2024-11-04', 4)": 0, + "(6507, '2024-11-04', 5)": 0, + "(6507, '2024-11-04', 6)": 0, + "(6507, '2024-11-04', 7)": 0, + "(6507, '2024-11-05', 0)": 1, + "(6507, '2024-11-05', 1)": 0, + "(6507, '2024-11-05', 2)": 0, + "(6507, '2024-11-05', 3)": 0, + "(6507, '2024-11-05', 4)": 0, + "(6507, '2024-11-05', 5)": 0, + "(6507, '2024-11-05', 6)": 0, + "(6507, '2024-11-05', 7)": 0, + "(6507, '2024-11-06', 0)": 1, + "(6507, '2024-11-06', 1)": 0, + "(6507, '2024-11-06', 2)": 0, + "(6507, '2024-11-06', 3)": 0, + "(6507, '2024-11-06', 4)": 0, + "(6507, '2024-11-06', 5)": 0, + "(6507, '2024-11-06', 6)": 0, + "(6507, '2024-11-06', 7)": 0, + "(6507, '2024-11-07', 0)": 1, + "(6507, '2024-11-07', 1)": 0, + "(6507, '2024-11-07', 2)": 0, + "(6507, '2024-11-07', 3)": 0, + "(6507, '2024-11-07', 4)": 0, + "(6507, '2024-11-07', 5)": 0, + "(6507, '2024-11-07', 6)": 0, + "(6507, '2024-11-07', 7)": 0, + "(6507, '2024-11-08', 0)": 1, + "(6507, '2024-11-08', 1)": 0, + "(6507, '2024-11-08', 2)": 0, + "(6507, '2024-11-08', 3)": 0, + "(6507, '2024-11-08', 4)": 0, + "(6507, '2024-11-08', 5)": 0, + "(6507, '2024-11-08', 6)": 0, + "(6507, '2024-11-08', 7)": 0, + "(6507, '2024-11-09', 0)": 0, + "(6507, '2024-11-09', 1)": 0, + "(6507, '2024-11-09', 2)": 1, + "(6507, '2024-11-09', 3)": 0, + "(6507, '2024-11-09', 4)": 0, + "(6507, '2024-11-09', 5)": 0, + "(6507, '2024-11-09', 6)": 0, + "(6507, '2024-11-09', 7)": 0, + "(6507, '2024-11-10', 0)": 0, + "(6507, '2024-11-10', 1)": 1, + "(6507, '2024-11-10', 2)": 0, + "(6507, '2024-11-10', 3)": 0, + "(6507, '2024-11-10', 4)": 0, + "(6507, '2024-11-10', 5)": 0, + "(6507, '2024-11-10', 6)": 0, + "(6507, '2024-11-10', 7)": 0, + "(6507, '2024-11-11', 0)": 1, + "(6507, '2024-11-11', 1)": 0, + "(6507, '2024-11-11', 2)": 0, + "(6507, '2024-11-11', 3)": 0, + "(6507, '2024-11-11', 4)": 0, + "(6507, '2024-11-11', 5)": 0, + "(6507, '2024-11-11', 6)": 0, + "(6507, '2024-11-11', 7)": 0, + "(6507, '2024-11-12', 0)": 0, + "(6507, '2024-11-12', 1)": 0, + "(6507, '2024-11-12', 2)": 0, + "(6507, '2024-11-12', 3)": 0, + "(6507, '2024-11-12', 4)": 0, + "(6507, '2024-11-12', 5)": 0, + "(6507, '2024-11-12', 6)": 0, + "(6507, '2024-11-12', 7)": 0, + "(6507, '2024-11-13', 0)": 1, + "(6507, '2024-11-13', 1)": 0, + "(6507, '2024-11-13', 2)": 0, + "(6507, '2024-11-13', 3)": 0, + "(6507, '2024-11-13', 4)": 0, + "(6507, '2024-11-13', 5)": 0, + "(6507, '2024-11-13', 6)": 0, + "(6507, '2024-11-13', 7)": 0, + "(6507, '2024-11-14', 0)": 0, + "(6507, '2024-11-14', 1)": 0, + "(6507, '2024-11-14', 2)": 0, + "(6507, '2024-11-14', 3)": 0, + "(6507, '2024-11-14', 4)": 0, + "(6507, '2024-11-14', 5)": 0, + "(6507, '2024-11-14', 6)": 0, + "(6507, '2024-11-14', 7)": 0, + "(6507, '2024-11-15', 0)": 0, + "(6507, '2024-11-15', 1)": 0, + "(6507, '2024-11-15', 2)": 0, + "(6507, '2024-11-15', 3)": 0, + "(6507, '2024-11-15', 4)": 0, + "(6507, '2024-11-15', 5)": 0, + "(6507, '2024-11-15', 6)": 0, + "(6507, '2024-11-15', 7)": 0, + "(6507, '2024-11-16', 0)": 0, + "(6507, '2024-11-16', 1)": 0, + "(6507, '2024-11-16', 2)": 1, + "(6507, '2024-11-16', 3)": 0, + "(6507, '2024-11-16', 4)": 0, + "(6507, '2024-11-16', 5)": 0, + "(6507, '2024-11-16', 6)": 0, + "(6507, '2024-11-16', 7)": 0, + "(6507, '2024-11-17', 0)": 0, + "(6507, '2024-11-17', 1)": 0, + "(6507, '2024-11-17', 2)": 0, + "(6507, '2024-11-17', 3)": 0, + "(6507, '2024-11-17', 4)": 0, + "(6507, '2024-11-17', 5)": 0, + "(6507, '2024-11-17', 6)": 0, + "(6507, '2024-11-17', 7)": 0, + "(6507, '2024-11-18', 0)": 0, + "(6507, '2024-11-18', 1)": 0, + "(6507, '2024-11-18', 2)": 0, + "(6507, '2024-11-18', 3)": 0, + "(6507, '2024-11-18', 4)": 0, + "(6507, '2024-11-18', 5)": 0, + "(6507, '2024-11-18', 6)": 0, + "(6507, '2024-11-18', 7)": 0, + "(6507, '2024-11-19', 0)": 1, + "(6507, '2024-11-19', 1)": 0, + "(6507, '2024-11-19', 2)": 0, + "(6507, '2024-11-19', 3)": 0, + "(6507, '2024-11-19', 4)": 0, + "(6507, '2024-11-19', 5)": 0, + "(6507, '2024-11-19', 6)": 0, + "(6507, '2024-11-19', 7)": 0, + "(6507, '2024-11-20', 0)": 1, + "(6507, '2024-11-20', 1)": 0, + "(6507, '2024-11-20', 2)": 0, + "(6507, '2024-11-20', 3)": 0, + "(6507, '2024-11-20', 4)": 0, + "(6507, '2024-11-20', 5)": 0, + "(6507, '2024-11-20', 6)": 0, + "(6507, '2024-11-20', 7)": 0, + "(6507, '2024-11-21', 0)": 1, + "(6507, '2024-11-21', 1)": 0, + "(6507, '2024-11-21', 2)": 0, + "(6507, '2024-11-21', 3)": 0, + "(6507, '2024-11-21', 4)": 0, + "(6507, '2024-11-21', 5)": 0, + "(6507, '2024-11-21', 6)": 0, + "(6507, '2024-11-21', 7)": 0, + "(6507, '2024-11-22', 0)": 1, + "(6507, '2024-11-22', 1)": 0, + "(6507, '2024-11-22', 2)": 0, + "(6507, '2024-11-22', 3)": 0, + "(6507, '2024-11-22', 4)": 0, + "(6507, '2024-11-22', 5)": 0, + "(6507, '2024-11-22', 6)": 0, + "(6507, '2024-11-22', 7)": 0, + "(6507, '2024-11-23', 0)": 0, + "(6507, '2024-11-23', 1)": 0, + "(6507, '2024-11-23', 2)": 0, + "(6507, '2024-11-23', 3)": 0, + "(6507, '2024-11-23', 4)": 0, + "(6507, '2024-11-23', 5)": 0, + "(6507, '2024-11-23', 6)": 0, + "(6507, '2024-11-23', 7)": 0, + "(6507, '2024-11-24', 0)": 0, + "(6507, '2024-11-24', 1)": 0, + "(6507, '2024-11-24', 2)": 0, + "(6507, '2024-11-24', 3)": 0, + "(6507, '2024-11-24', 4)": 0, + "(6507, '2024-11-24', 5)": 0, + "(6507, '2024-11-24', 6)": 0, + "(6507, '2024-11-24', 7)": 0, + "(6507, '2024-11-25', 0)": 1, + "(6507, '2024-11-25', 1)": 0, + "(6507, '2024-11-25', 2)": 0, + "(6507, '2024-11-25', 3)": 0, + "(6507, '2024-11-25', 4)": 0, + "(6507, '2024-11-25', 5)": 0, + "(6507, '2024-11-25', 6)": 0, + "(6507, '2024-11-25', 7)": 0, + "(6507, '2024-11-26', 0)": 1, + "(6507, '2024-11-26', 1)": 0, + "(6507, '2024-11-26', 2)": 0, + "(6507, '2024-11-26', 3)": 0, + "(6507, '2024-11-26', 4)": 0, + "(6507, '2024-11-26', 5)": 0, + "(6507, '2024-11-26', 6)": 0, + "(6507, '2024-11-26', 7)": 0, + "(6507, '2024-11-27', 0)": 1, + "(6507, '2024-11-27', 1)": 0, + "(6507, '2024-11-27', 2)": 0, + "(6507, '2024-11-27', 3)": 0, + "(6507, '2024-11-27', 4)": 0, + "(6507, '2024-11-27', 5)": 0, + "(6507, '2024-11-27', 6)": 0, + "(6507, '2024-11-27', 7)": 0, + "(6507, '2024-11-28', 0)": 1, + "(6507, '2024-11-28', 1)": 0, + "(6507, '2024-11-28', 2)": 0, + "(6507, '2024-11-28', 3)": 0, + "(6507, '2024-11-28', 4)": 0, + "(6507, '2024-11-28', 5)": 0, + "(6507, '2024-11-28', 6)": 0, + "(6507, '2024-11-28', 7)": 0, + "(6507, '2024-11-29', 0)": 0, + "(6507, '2024-11-29', 1)": 0, + "(6507, '2024-11-29', 2)": 0, + "(6507, '2024-11-29', 3)": 0, + "(6507, '2024-11-29', 4)": 0, + "(6507, '2024-11-29', 5)": 0, + "(6507, '2024-11-29', 6)": 0, + "(6507, '2024-11-29', 7)": 0, + "(6507, '2024-11-30', 0)": 1, + "(6507, '2024-11-30', 1)": 0, + "(6507, '2024-11-30', 2)": 0, + "(6507, '2024-11-30', 3)": 0, + "(6507, '2024-11-30', 4)": 0, + "(6507, '2024-11-30', 5)": 0, + "(6507, '2024-11-30', 6)": 0, + "(6507, '2024-11-30', 7)": 0, + "(6677, '2024-11-01', 0)": 0, + "(6677, '2024-11-01', 1)": 0, + "(6677, '2024-11-01', 2)": 0, + "(6677, '2024-11-01', 3)": 0, + "(6677, '2024-11-01', 4)": 0, + "(6677, '2024-11-01', 5)": 0, + "(6677, '2024-11-01', 6)": 0, + "(6677, '2024-11-01', 7)": 0, + "(6677, '2024-11-02', 0)": 0, + "(6677, '2024-11-02', 1)": 0, + "(6677, '2024-11-02', 2)": 0, + "(6677, '2024-11-02', 3)": 0, + "(6677, '2024-11-02', 4)": 0, + "(6677, '2024-11-02', 5)": 0, + "(6677, '2024-11-02', 6)": 0, + "(6677, '2024-11-02', 7)": 0, + "(6677, '2024-11-03', 0)": 1, + "(6677, '2024-11-03', 1)": 0, + "(6677, '2024-11-03', 2)": 0, + "(6677, '2024-11-03', 3)": 0, + "(6677, '2024-11-03', 4)": 0, + "(6677, '2024-11-03', 5)": 0, + "(6677, '2024-11-03', 6)": 0, + "(6677, '2024-11-03', 7)": 0, + "(6677, '2024-11-04', 0)": 1, + "(6677, '2024-11-04', 1)": 0, + "(6677, '2024-11-04', 2)": 0, + "(6677, '2024-11-04', 3)": 0, + "(6677, '2024-11-04', 4)": 0, + "(6677, '2024-11-04', 5)": 0, + "(6677, '2024-11-04', 6)": 0, + "(6677, '2024-11-04', 7)": 0, + "(6677, '2024-11-05', 0)": 1, + "(6677, '2024-11-05', 1)": 0, + "(6677, '2024-11-05', 2)": 0, + "(6677, '2024-11-05', 3)": 0, + "(6677, '2024-11-05', 4)": 0, + "(6677, '2024-11-05', 5)": 0, + "(6677, '2024-11-05', 6)": 0, + "(6677, '2024-11-05', 7)": 0, + "(6677, '2024-11-06', 0)": 1, + "(6677, '2024-11-06', 1)": 0, + "(6677, '2024-11-06', 2)": 0, + "(6677, '2024-11-06', 3)": 0, + "(6677, '2024-11-06', 4)": 0, + "(6677, '2024-11-06', 5)": 0, + "(6677, '2024-11-06', 6)": 0, + "(6677, '2024-11-06', 7)": 0, + "(6677, '2024-11-07', 0)": 1, + "(6677, '2024-11-07', 1)": 0, + "(6677, '2024-11-07', 2)": 0, + "(6677, '2024-11-07', 3)": 0, + "(6677, '2024-11-07', 4)": 0, + "(6677, '2024-11-07', 5)": 0, + "(6677, '2024-11-07', 6)": 0, + "(6677, '2024-11-07', 7)": 0, + "(6677, '2024-11-08', 0)": 0, + "(6677, '2024-11-08', 1)": 0, + "(6677, '2024-11-08', 2)": 0, + "(6677, '2024-11-08', 3)": 0, + "(6677, '2024-11-08', 4)": 0, + "(6677, '2024-11-08', 5)": 0, + "(6677, '2024-11-08', 6)": 0, + "(6677, '2024-11-08', 7)": 0, + "(6677, '2024-11-09', 0)": 0, + "(6677, '2024-11-09', 1)": 0, + "(6677, '2024-11-09', 2)": 1, + "(6677, '2024-11-09', 3)": 0, + "(6677, '2024-11-09', 4)": 0, + "(6677, '2024-11-09', 5)": 0, + "(6677, '2024-11-09', 6)": 0, + "(6677, '2024-11-09', 7)": 0, + "(6677, '2024-11-10', 0)": 0, + "(6677, '2024-11-10', 1)": 0, + "(6677, '2024-11-10', 2)": 1, + "(6677, '2024-11-10', 3)": 0, + "(6677, '2024-11-10', 4)": 0, + "(6677, '2024-11-10', 5)": 0, + "(6677, '2024-11-10', 6)": 0, + "(6677, '2024-11-10', 7)": 0, + "(6677, '2024-11-11', 0)": 0, + "(6677, '2024-11-11', 1)": 0, + "(6677, '2024-11-11', 2)": 0, + "(6677, '2024-11-11', 3)": 0, + "(6677, '2024-11-11', 4)": 0, + "(6677, '2024-11-11', 5)": 0, + "(6677, '2024-11-11', 6)": 0, + "(6677, '2024-11-11', 7)": 0, + "(6677, '2024-11-12', 0)": 1, + "(6677, '2024-11-12', 1)": 0, + "(6677, '2024-11-12', 2)": 0, + "(6677, '2024-11-12', 3)": 0, + "(6677, '2024-11-12', 4)": 0, + "(6677, '2024-11-12', 5)": 0, + "(6677, '2024-11-12', 6)": 0, + "(6677, '2024-11-12', 7)": 0, + "(6677, '2024-11-13', 0)": 1, + "(6677, '2024-11-13', 1)": 0, + "(6677, '2024-11-13', 2)": 0, + "(6677, '2024-11-13', 3)": 0, + "(6677, '2024-11-13', 4)": 0, + "(6677, '2024-11-13', 5)": 0, + "(6677, '2024-11-13', 6)": 0, + "(6677, '2024-11-13', 7)": 0, + "(6677, '2024-11-14', 0)": 0, + "(6677, '2024-11-14', 1)": 0, + "(6677, '2024-11-14', 2)": 1, + "(6677, '2024-11-14', 3)": 0, + "(6677, '2024-11-14', 4)": 0, + "(6677, '2024-11-14', 5)": 0, + "(6677, '2024-11-14', 6)": 0, + "(6677, '2024-11-14', 7)": 0, + "(6677, '2024-11-15', 0)": 0, + "(6677, '2024-11-15', 1)": 0, + "(6677, '2024-11-15', 2)": 0, + "(6677, '2024-11-15', 3)": 0, + "(6677, '2024-11-15', 4)": 0, + "(6677, '2024-11-15', 5)": 0, + "(6677, '2024-11-15', 6)": 0, + "(6677, '2024-11-15', 7)": 0, + "(6677, '2024-11-16', 0)": 0, + "(6677, '2024-11-16', 1)": 0, + "(6677, '2024-11-16', 2)": 0, + "(6677, '2024-11-16', 3)": 0, + "(6677, '2024-11-16', 4)": 0, + "(6677, '2024-11-16', 5)": 0, + "(6677, '2024-11-16', 6)": 0, + "(6677, '2024-11-16', 7)": 0, + "(6677, '2024-11-17', 0)": 1, + "(6677, '2024-11-17', 1)": 0, + "(6677, '2024-11-17', 2)": 0, + "(6677, '2024-11-17', 3)": 0, + "(6677, '2024-11-17', 4)": 0, + "(6677, '2024-11-17', 5)": 0, + "(6677, '2024-11-17', 6)": 0, + "(6677, '2024-11-17', 7)": 0, + "(6677, '2024-11-18', 0)": 1, + "(6677, '2024-11-18', 1)": 0, + "(6677, '2024-11-18', 2)": 0, + "(6677, '2024-11-18', 3)": 0, + "(6677, '2024-11-18', 4)": 0, + "(6677, '2024-11-18', 5)": 0, + "(6677, '2024-11-18', 6)": 0, + "(6677, '2024-11-18', 7)": 0, + "(6677, '2024-11-19', 0)": 0, + "(6677, '2024-11-19', 1)": 0, + "(6677, '2024-11-19', 2)": 0, + "(6677, '2024-11-19', 3)": 0, + "(6677, '2024-11-19', 4)": 0, + "(6677, '2024-11-19', 5)": 0, + "(6677, '2024-11-19', 6)": 0, + "(6677, '2024-11-19', 7)": 0, + "(6677, '2024-11-20', 0)": 0, + "(6677, '2024-11-20', 1)": 0, + "(6677, '2024-11-20', 2)": 0, + "(6677, '2024-11-20', 3)": 0, + "(6677, '2024-11-20', 4)": 0, + "(6677, '2024-11-20', 5)": 0, + "(6677, '2024-11-20', 6)": 0, + "(6677, '2024-11-20', 7)": 0, + "(6677, '2024-11-21', 0)": 0, + "(6677, '2024-11-21', 1)": 0, + "(6677, '2024-11-21', 2)": 0, + "(6677, '2024-11-21', 3)": 0, + "(6677, '2024-11-21', 4)": 0, + "(6677, '2024-11-21', 5)": 0, + "(6677, '2024-11-21', 6)": 0, + "(6677, '2024-11-21', 7)": 0, + "(6677, '2024-11-22', 0)": 0, + "(6677, '2024-11-22', 1)": 0, + "(6677, '2024-11-22', 2)": 0, + "(6677, '2024-11-22', 3)": 0, + "(6677, '2024-11-22', 4)": 0, + "(6677, '2024-11-22', 5)": 0, + "(6677, '2024-11-22', 6)": 0, + "(6677, '2024-11-22', 7)": 0, + "(6677, '2024-11-23', 0)": 1, + "(6677, '2024-11-23', 1)": 0, + "(6677, '2024-11-23', 2)": 0, + "(6677, '2024-11-23', 3)": 0, + "(6677, '2024-11-23', 4)": 0, + "(6677, '2024-11-23', 5)": 0, + "(6677, '2024-11-23', 6)": 0, + "(6677, '2024-11-23', 7)": 0, + "(6677, '2024-11-24', 0)": 1, + "(6677, '2024-11-24', 1)": 0, + "(6677, '2024-11-24', 2)": 0, + "(6677, '2024-11-24', 3)": 0, + "(6677, '2024-11-24', 4)": 0, + "(6677, '2024-11-24', 5)": 0, + "(6677, '2024-11-24', 6)": 0, + "(6677, '2024-11-24', 7)": 0, + "(6677, '2024-11-25', 0)": 0, + "(6677, '2024-11-25', 1)": 0, + "(6677, '2024-11-25', 2)": 1, + "(6677, '2024-11-25', 3)": 0, + "(6677, '2024-11-25', 4)": 0, + "(6677, '2024-11-25', 5)": 0, + "(6677, '2024-11-25', 6)": 0, + "(6677, '2024-11-25', 7)": 0, + "(6677, '2024-11-26', 0)": 0, + "(6677, '2024-11-26', 1)": 0, + "(6677, '2024-11-26', 2)": 1, + "(6677, '2024-11-26', 3)": 0, + "(6677, '2024-11-26', 4)": 0, + "(6677, '2024-11-26', 5)": 0, + "(6677, '2024-11-26', 6)": 0, + "(6677, '2024-11-26', 7)": 0, + "(6677, '2024-11-27', 0)": 0, + "(6677, '2024-11-27', 1)": 0, + "(6677, '2024-11-27', 2)": 1, + "(6677, '2024-11-27', 3)": 0, + "(6677, '2024-11-27', 4)": 0, + "(6677, '2024-11-27', 5)": 0, + "(6677, '2024-11-27', 6)": 0, + "(6677, '2024-11-27', 7)": 0, + "(6677, '2024-11-28', 0)": 0, + "(6677, '2024-11-28', 1)": 0, + "(6677, '2024-11-28', 2)": 1, + "(6677, '2024-11-28', 3)": 0, + "(6677, '2024-11-28', 4)": 0, + "(6677, '2024-11-28', 5)": 0, + "(6677, '2024-11-28', 6)": 0, + "(6677, '2024-11-28', 7)": 0, + "(6677, '2024-11-29', 0)": 0, + "(6677, '2024-11-29', 1)": 0, + "(6677, '2024-11-29', 2)": 1, + "(6677, '2024-11-29', 3)": 0, + "(6677, '2024-11-29', 4)": 0, + "(6677, '2024-11-29', 5)": 0, + "(6677, '2024-11-29', 6)": 0, + "(6677, '2024-11-29', 7)": 0, + "(6677, '2024-11-30', 0)": 0, + "(6677, '2024-11-30', 1)": 0, + "(6677, '2024-11-30', 2)": 1, + "(6677, '2024-11-30', 3)": 0, + "(6677, '2024-11-30', 4)": 0, + "(6677, '2024-11-30', 5)": 0, + "(6677, '2024-11-30', 6)": 0, + "(6677, '2024-11-30', 7)": 0, + "(6681, '2024-11-01', 0)": 0, + "(6681, '2024-11-01', 1)": 0, + "(6681, '2024-11-01', 2)": 0, + "(6681, '2024-11-01', 3)": 0, + "(6681, '2024-11-01', 4)": 0, + "(6681, '2024-11-01', 5)": 0, + "(6681, '2024-11-01', 6)": 0, + "(6681, '2024-11-01', 7)": 0, + "(6681, '2024-11-02', 0)": 0, + "(6681, '2024-11-02', 1)": 0, + "(6681, '2024-11-02', 2)": 0, + "(6681, '2024-11-02', 3)": 1, + "(6681, '2024-11-02', 4)": 0, + "(6681, '2024-11-02', 5)": 0, + "(6681, '2024-11-02', 6)": 0, + "(6681, '2024-11-02', 7)": 0, + "(6681, '2024-11-03', 0)": 0, + "(6681, '2024-11-03', 1)": 0, + "(6681, '2024-11-03', 2)": 0, + "(6681, '2024-11-03', 3)": 0, + "(6681, '2024-11-03', 4)": 0, + "(6681, '2024-11-03', 5)": 0, + "(6681, '2024-11-03', 6)": 0, + "(6681, '2024-11-03', 7)": 0, + "(6681, '2024-11-04', 0)": 0, + "(6681, '2024-11-04', 1)": 0, + "(6681, '2024-11-04', 2)": 0, + "(6681, '2024-11-04', 3)": 0, + "(6681, '2024-11-04', 4)": 0, + "(6681, '2024-11-04', 5)": 0, + "(6681, '2024-11-04', 6)": 0, + "(6681, '2024-11-04', 7)": 0, + "(6681, '2024-11-05', 0)": 0, + "(6681, '2024-11-05', 1)": 0, + "(6681, '2024-11-05', 2)": 0, + "(6681, '2024-11-05', 3)": 1, + "(6681, '2024-11-05', 4)": 0, + "(6681, '2024-11-05', 5)": 0, + "(6681, '2024-11-05', 6)": 0, + "(6681, '2024-11-05', 7)": 0, + "(6681, '2024-11-06', 0)": 0, + "(6681, '2024-11-06', 1)": 0, + "(6681, '2024-11-06', 2)": 0, + "(6681, '2024-11-06', 3)": 0, + "(6681, '2024-11-06', 4)": 0, + "(6681, '2024-11-06', 5)": 0, + "(6681, '2024-11-06', 6)": 0, + "(6681, '2024-11-06', 7)": 0, + "(6681, '2024-11-07', 0)": 0, + "(6681, '2024-11-07', 1)": 0, + "(6681, '2024-11-07', 2)": 0, + "(6681, '2024-11-07', 3)": 1, + "(6681, '2024-11-07', 4)": 0, + "(6681, '2024-11-07', 5)": 0, + "(6681, '2024-11-07', 6)": 0, + "(6681, '2024-11-07', 7)": 0, + "(6681, '2024-11-08', 0)": 0, + "(6681, '2024-11-08', 1)": 0, + "(6681, '2024-11-08', 2)": 0, + "(6681, '2024-11-08', 3)": 0, + "(6681, '2024-11-08', 4)": 0, + "(6681, '2024-11-08', 5)": 0, + "(6681, '2024-11-08', 6)": 0, + "(6681, '2024-11-08', 7)": 0, + "(6681, '2024-11-09', 0)": 0, + "(6681, '2024-11-09', 1)": 0, + "(6681, '2024-11-09', 2)": 0, + "(6681, '2024-11-09', 3)": 0, + "(6681, '2024-11-09', 4)": 0, + "(6681, '2024-11-09', 5)": 0, + "(6681, '2024-11-09', 6)": 0, + "(6681, '2024-11-09', 7)": 0, + "(6681, '2024-11-10', 0)": 0, + "(6681, '2024-11-10', 1)": 0, + "(6681, '2024-11-10', 2)": 0, + "(6681, '2024-11-10', 3)": 0, + "(6681, '2024-11-10', 4)": 0, + "(6681, '2024-11-10', 5)": 0, + "(6681, '2024-11-10', 6)": 0, + "(6681, '2024-11-10', 7)": 0, + "(6681, '2024-11-11', 0)": 0, + "(6681, '2024-11-11', 1)": 0, + "(6681, '2024-11-11', 2)": 0, + "(6681, '2024-11-11', 3)": 0, + "(6681, '2024-11-11', 4)": 0, + "(6681, '2024-11-11', 5)": 0, + "(6681, '2024-11-11', 6)": 0, + "(6681, '2024-11-11', 7)": 0, + "(6681, '2024-11-12', 0)": 0, + "(6681, '2024-11-12', 1)": 0, + "(6681, '2024-11-12', 2)": 0, + "(6681, '2024-11-12', 3)": 1, + "(6681, '2024-11-12', 4)": 0, + "(6681, '2024-11-12', 5)": 0, + "(6681, '2024-11-12', 6)": 0, + "(6681, '2024-11-12', 7)": 0, + "(6681, '2024-11-13', 0)": 0, + "(6681, '2024-11-13', 1)": 0, + "(6681, '2024-11-13', 2)": 0, + "(6681, '2024-11-13', 3)": 1, + "(6681, '2024-11-13', 4)": 0, + "(6681, '2024-11-13', 5)": 0, + "(6681, '2024-11-13', 6)": 0, + "(6681, '2024-11-13', 7)": 0, + "(6681, '2024-11-14', 0)": 0, + "(6681, '2024-11-14', 1)": 0, + "(6681, '2024-11-14', 2)": 0, + "(6681, '2024-11-14', 3)": 0, + "(6681, '2024-11-14', 4)": 0, + "(6681, '2024-11-14', 5)": 0, + "(6681, '2024-11-14', 6)": 0, + "(6681, '2024-11-14', 7)": 0, + "(6681, '2024-11-15', 0)": 0, + "(6681, '2024-11-15', 1)": 0, + "(6681, '2024-11-15', 2)": 0, + "(6681, '2024-11-15', 3)": 1, + "(6681, '2024-11-15', 4)": 0, + "(6681, '2024-11-15', 5)": 0, + "(6681, '2024-11-15', 6)": 0, + "(6681, '2024-11-15', 7)": 0, + "(6681, '2024-11-16', 0)": 0, + "(6681, '2024-11-16', 1)": 0, + "(6681, '2024-11-16', 2)": 0, + "(6681, '2024-11-16', 3)": 0, + "(6681, '2024-11-16', 4)": 0, + "(6681, '2024-11-16', 5)": 0, + "(6681, '2024-11-16', 6)": 0, + "(6681, '2024-11-16', 7)": 0, + "(6681, '2024-11-17', 0)": 0, + "(6681, '2024-11-17', 1)": 0, + "(6681, '2024-11-17', 2)": 0, + "(6681, '2024-11-17', 3)": 0, + "(6681, '2024-11-17', 4)": 0, + "(6681, '2024-11-17', 5)": 0, + "(6681, '2024-11-17', 6)": 0, + "(6681, '2024-11-17', 7)": 0, + "(6681, '2024-11-18', 0)": 0, + "(6681, '2024-11-18', 1)": 0, + "(6681, '2024-11-18', 2)": 0, + "(6681, '2024-11-18', 3)": 0, + "(6681, '2024-11-18', 4)": 0, + "(6681, '2024-11-18', 5)": 0, + "(6681, '2024-11-18', 6)": 0, + "(6681, '2024-11-18', 7)": 0, + "(6681, '2024-11-19', 0)": 0, + "(6681, '2024-11-19', 1)": 0, + "(6681, '2024-11-19', 2)": 0, + "(6681, '2024-11-19', 3)": 0, + "(6681, '2024-11-19', 4)": 0, + "(6681, '2024-11-19', 5)": 0, + "(6681, '2024-11-19', 6)": 0, + "(6681, '2024-11-19', 7)": 0, + "(6681, '2024-11-20', 0)": 0, + "(6681, '2024-11-20', 1)": 0, + "(6681, '2024-11-20', 2)": 0, + "(6681, '2024-11-20', 3)": 0, + "(6681, '2024-11-20', 4)": 0, + "(6681, '2024-11-20', 5)": 0, + "(6681, '2024-11-20', 6)": 0, + "(6681, '2024-11-20', 7)": 0, + "(6681, '2024-11-21', 0)": 0, + "(6681, '2024-11-21', 1)": 0, + "(6681, '2024-11-21', 2)": 0, + "(6681, '2024-11-21', 3)": 0, + "(6681, '2024-11-21', 4)": 0, + "(6681, '2024-11-21', 5)": 0, + "(6681, '2024-11-21', 6)": 0, + "(6681, '2024-11-21', 7)": 0, + "(6681, '2024-11-22', 0)": 0, + "(6681, '2024-11-22', 1)": 0, + "(6681, '2024-11-22', 2)": 0, + "(6681, '2024-11-22', 3)": 0, + "(6681, '2024-11-22', 4)": 0, + "(6681, '2024-11-22', 5)": 0, + "(6681, '2024-11-22', 6)": 0, + "(6681, '2024-11-22', 7)": 0, + "(6681, '2024-11-23', 0)": 0, + "(6681, '2024-11-23', 1)": 0, + "(6681, '2024-11-23', 2)": 0, + "(6681, '2024-11-23', 3)": 0, + "(6681, '2024-11-23', 4)": 0, + "(6681, '2024-11-23', 5)": 0, + "(6681, '2024-11-23', 6)": 0, + "(6681, '2024-11-23', 7)": 0, + "(6681, '2024-11-24', 0)": 0, + "(6681, '2024-11-24', 1)": 0, + "(6681, '2024-11-24', 2)": 0, + "(6681, '2024-11-24', 3)": 0, + "(6681, '2024-11-24', 4)": 0, + "(6681, '2024-11-24', 5)": 0, + "(6681, '2024-11-24', 6)": 0, + "(6681, '2024-11-24', 7)": 0, + "(6681, '2024-11-25', 0)": 0, + "(6681, '2024-11-25', 1)": 0, + "(6681, '2024-11-25', 2)": 0, + "(6681, '2024-11-25', 3)": 0, + "(6681, '2024-11-25', 4)": 0, + "(6681, '2024-11-25', 5)": 0, + "(6681, '2024-11-25', 6)": 0, + "(6681, '2024-11-25', 7)": 0, + "(6681, '2024-11-26', 0)": 0, + "(6681, '2024-11-26', 1)": 0, + "(6681, '2024-11-26', 2)": 0, + "(6681, '2024-11-26', 3)": 0, + "(6681, '2024-11-26', 4)": 0, + "(6681, '2024-11-26', 5)": 0, + "(6681, '2024-11-26', 6)": 0, + "(6681, '2024-11-26', 7)": 0, + "(6681, '2024-11-27', 0)": 0, + "(6681, '2024-11-27', 1)": 0, + "(6681, '2024-11-27', 2)": 0, + "(6681, '2024-11-27', 3)": 0, + "(6681, '2024-11-27', 4)": 0, + "(6681, '2024-11-27', 5)": 0, + "(6681, '2024-11-27', 6)": 0, + "(6681, '2024-11-27', 7)": 0, + "(6681, '2024-11-28', 0)": 0, + "(6681, '2024-11-28', 1)": 0, + "(6681, '2024-11-28', 2)": 0, + "(6681, '2024-11-28', 3)": 0, + "(6681, '2024-11-28', 4)": 0, + "(6681, '2024-11-28', 5)": 0, + "(6681, '2024-11-28', 6)": 0, + "(6681, '2024-11-28', 7)": 0, + "(6681, '2024-11-29', 0)": 0, + "(6681, '2024-11-29', 1)": 0, + "(6681, '2024-11-29', 2)": 0, + "(6681, '2024-11-29', 3)": 0, + "(6681, '2024-11-29', 4)": 0, + "(6681, '2024-11-29', 5)": 0, + "(6681, '2024-11-29', 6)": 0, + "(6681, '2024-11-29', 7)": 0, + "(6681, '2024-11-30', 0)": 0, + "(6681, '2024-11-30', 1)": 0, + "(6681, '2024-11-30', 2)": 0, + "(6681, '2024-11-30', 3)": 0, + "(6681, '2024-11-30', 4)": 0, + "(6681, '2024-11-30', 5)": 0, + "(6681, '2024-11-30', 6)": 0, + "(6681, '2024-11-30', 7)": 0, + "(6715, '2024-11-01', 0)": 0, + "(6715, '2024-11-01', 1)": 0, + "(6715, '2024-11-01', 2)": 0, + "(6715, '2024-11-01', 3)": 0, + "(6715, '2024-11-01', 4)": 0, + "(6715, '2024-11-01', 5)": 0, + "(6715, '2024-11-01', 6)": 0, + "(6715, '2024-11-01', 7)": 0, + "(6715, '2024-11-02', 0)": 0, + "(6715, '2024-11-02', 1)": 0, + "(6715, '2024-11-02', 2)": 0, + "(6715, '2024-11-02', 3)": 0, + "(6715, '2024-11-02', 4)": 0, + "(6715, '2024-11-02', 5)": 0, + "(6715, '2024-11-02', 6)": 0, + "(6715, '2024-11-02', 7)": 0, + "(6715, '2024-11-03', 0)": 0, + "(6715, '2024-11-03', 1)": 0, + "(6715, '2024-11-03', 2)": 0, + "(6715, '2024-11-03', 3)": 0, + "(6715, '2024-11-03', 4)": 0, + "(6715, '2024-11-03', 5)": 0, + "(6715, '2024-11-03', 6)": 0, + "(6715, '2024-11-03', 7)": 0, + "(6715, '2024-11-04', 0)": 0, + "(6715, '2024-11-04', 1)": 0, + "(6715, '2024-11-04', 2)": 0, + "(6715, '2024-11-04', 3)": 0, + "(6715, '2024-11-04', 4)": 0, + "(6715, '2024-11-04', 5)": 0, + "(6715, '2024-11-04', 6)": 0, + "(6715, '2024-11-04', 7)": 0, + "(6715, '2024-11-05', 0)": 0, + "(6715, '2024-11-05', 1)": 0, + "(6715, '2024-11-05', 2)": 0, + "(6715, '2024-11-05', 3)": 0, + "(6715, '2024-11-05', 4)": 0, + "(6715, '2024-11-05', 5)": 0, + "(6715, '2024-11-05', 6)": 0, + "(6715, '2024-11-05', 7)": 0, + "(6715, '2024-11-06', 0)": 0, + "(6715, '2024-11-06', 1)": 1, + "(6715, '2024-11-06', 2)": 0, + "(6715, '2024-11-06', 3)": 0, + "(6715, '2024-11-06', 4)": 0, + "(6715, '2024-11-06', 5)": 0, + "(6715, '2024-11-06', 6)": 0, + "(6715, '2024-11-06', 7)": 0, + "(6715, '2024-11-07', 0)": 0, + "(6715, '2024-11-07', 1)": 0, + "(6715, '2024-11-07', 2)": 0, + "(6715, '2024-11-07', 3)": 0, + "(6715, '2024-11-07', 4)": 0, + "(6715, '2024-11-07', 5)": 0, + "(6715, '2024-11-07', 6)": 0, + "(6715, '2024-11-07', 7)": 0, + "(6715, '2024-11-08', 0)": 0, + "(6715, '2024-11-08', 1)": 0, + "(6715, '2024-11-08', 2)": 0, + "(6715, '2024-11-08', 3)": 0, + "(6715, '2024-11-08', 4)": 0, + "(6715, '2024-11-08', 5)": 0, + "(6715, '2024-11-08', 6)": 0, + "(6715, '2024-11-08', 7)": 0, + "(6715, '2024-11-09', 0)": 0, + "(6715, '2024-11-09', 1)": 0, + "(6715, '2024-11-09', 2)": 0, + "(6715, '2024-11-09', 3)": 0, + "(6715, '2024-11-09', 4)": 0, + "(6715, '2024-11-09', 5)": 0, + "(6715, '2024-11-09', 6)": 0, + "(6715, '2024-11-09', 7)": 0, + "(6715, '2024-11-10', 0)": 0, + "(6715, '2024-11-10', 1)": 0, + "(6715, '2024-11-10', 2)": 0, + "(6715, '2024-11-10', 3)": 0, + "(6715, '2024-11-10', 4)": 0, + "(6715, '2024-11-10', 5)": 0, + "(6715, '2024-11-10', 6)": 0, + "(6715, '2024-11-10', 7)": 0, + "(6715, '2024-11-11', 0)": 0, + "(6715, '2024-11-11', 1)": 0, + "(6715, '2024-11-11', 2)": 0, + "(6715, '2024-11-11', 3)": 0, + "(6715, '2024-11-11', 4)": 0, + "(6715, '2024-11-11', 5)": 0, + "(6715, '2024-11-11', 6)": 0, + "(6715, '2024-11-11', 7)": 0, + "(6715, '2024-11-12', 0)": 0, + "(6715, '2024-11-12', 1)": 0, + "(6715, '2024-11-12', 2)": 0, + "(6715, '2024-11-12', 3)": 0, + "(6715, '2024-11-12', 4)": 0, + "(6715, '2024-11-12', 5)": 0, + "(6715, '2024-11-12', 6)": 0, + "(6715, '2024-11-12', 7)": 0, + "(6715, '2024-11-13', 0)": 0, + "(6715, '2024-11-13', 1)": 0, + "(6715, '2024-11-13', 2)": 0, + "(6715, '2024-11-13', 3)": 0, + "(6715, '2024-11-13', 4)": 0, + "(6715, '2024-11-13', 5)": 0, + "(6715, '2024-11-13', 6)": 0, + "(6715, '2024-11-13', 7)": 0, + "(6715, '2024-11-14', 0)": 0, + "(6715, '2024-11-14', 1)": 0, + "(6715, '2024-11-14', 2)": 0, + "(6715, '2024-11-14', 3)": 0, + "(6715, '2024-11-14', 4)": 0, + "(6715, '2024-11-14', 5)": 0, + "(6715, '2024-11-14', 6)": 0, + "(6715, '2024-11-14', 7)": 0, + "(6715, '2024-11-15', 0)": 0, + "(6715, '2024-11-15', 1)": 0, + "(6715, '2024-11-15', 2)": 0, + "(6715, '2024-11-15', 3)": 0, + "(6715, '2024-11-15', 4)": 0, + "(6715, '2024-11-15', 5)": 0, + "(6715, '2024-11-15', 6)": 0, + "(6715, '2024-11-15', 7)": 0, + "(6715, '2024-11-16', 0)": 0, + "(6715, '2024-11-16', 1)": 0, + "(6715, '2024-11-16', 2)": 0, + "(6715, '2024-11-16', 3)": 0, + "(6715, '2024-11-16', 4)": 0, + "(6715, '2024-11-16', 5)": 0, + "(6715, '2024-11-16', 6)": 0, + "(6715, '2024-11-16', 7)": 0, + "(6715, '2024-11-17', 0)": 0, + "(6715, '2024-11-17', 1)": 0, + "(6715, '2024-11-17', 2)": 0, + "(6715, '2024-11-17', 3)": 0, + "(6715, '2024-11-17', 4)": 0, + "(6715, '2024-11-17', 5)": 0, + "(6715, '2024-11-17', 6)": 0, + "(6715, '2024-11-17', 7)": 0, + "(6715, '2024-11-18', 0)": 0, + "(6715, '2024-11-18', 1)": 0, + "(6715, '2024-11-18', 2)": 0, + "(6715, '2024-11-18', 3)": 0, + "(6715, '2024-11-18', 4)": 0, + "(6715, '2024-11-18', 5)": 0, + "(6715, '2024-11-18', 6)": 0, + "(6715, '2024-11-18', 7)": 0, + "(6715, '2024-11-19', 0)": 0, + "(6715, '2024-11-19', 1)": 0, + "(6715, '2024-11-19', 2)": 0, + "(6715, '2024-11-19', 3)": 0, + "(6715, '2024-11-19', 4)": 0, + "(6715, '2024-11-19', 5)": 0, + "(6715, '2024-11-19', 6)": 0, + "(6715, '2024-11-19', 7)": 0, + "(6715, '2024-11-20', 0)": 0, + "(6715, '2024-11-20', 1)": 0, + "(6715, '2024-11-20', 2)": 0, + "(6715, '2024-11-20', 3)": 0, + "(6715, '2024-11-20', 4)": 0, + "(6715, '2024-11-20', 5)": 0, + "(6715, '2024-11-20', 6)": 0, + "(6715, '2024-11-20', 7)": 0, + "(6715, '2024-11-21', 0)": 0, + "(6715, '2024-11-21', 1)": 0, + "(6715, '2024-11-21', 2)": 0, + "(6715, '2024-11-21', 3)": 0, + "(6715, '2024-11-21', 4)": 0, + "(6715, '2024-11-21', 5)": 0, + "(6715, '2024-11-21', 6)": 0, + "(6715, '2024-11-21', 7)": 0, + "(6715, '2024-11-22', 0)": 0, + "(6715, '2024-11-22', 1)": 0, + "(6715, '2024-11-22', 2)": 0, + "(6715, '2024-11-22', 3)": 0, + "(6715, '2024-11-22', 4)": 0, + "(6715, '2024-11-22', 5)": 0, + "(6715, '2024-11-22', 6)": 0, + "(6715, '2024-11-22', 7)": 0, + "(6715, '2024-11-23', 0)": 0, + "(6715, '2024-11-23', 1)": 0, + "(6715, '2024-11-23', 2)": 0, + "(6715, '2024-11-23', 3)": 0, + "(6715, '2024-11-23', 4)": 0, + "(6715, '2024-11-23', 5)": 0, + "(6715, '2024-11-23', 6)": 0, + "(6715, '2024-11-23', 7)": 0, + "(6715, '2024-11-24', 0)": 0, + "(6715, '2024-11-24', 1)": 0, + "(6715, '2024-11-24', 2)": 0, + "(6715, '2024-11-24', 3)": 0, + "(6715, '2024-11-24', 4)": 0, + "(6715, '2024-11-24', 5)": 0, + "(6715, '2024-11-24', 6)": 0, + "(6715, '2024-11-24', 7)": 0, + "(6715, '2024-11-25', 0)": 0, + "(6715, '2024-11-25', 1)": 0, + "(6715, '2024-11-25', 2)": 0, + "(6715, '2024-11-25', 3)": 0, + "(6715, '2024-11-25', 4)": 0, + "(6715, '2024-11-25', 5)": 0, + "(6715, '2024-11-25', 6)": 0, + "(6715, '2024-11-25', 7)": 0, + "(6715, '2024-11-26', 0)": 0, + "(6715, '2024-11-26', 1)": 0, + "(6715, '2024-11-26', 2)": 0, + "(6715, '2024-11-26', 3)": 0, + "(6715, '2024-11-26', 4)": 0, + "(6715, '2024-11-26', 5)": 0, + "(6715, '2024-11-26', 6)": 0, + "(6715, '2024-11-26', 7)": 0, + "(6715, '2024-11-27', 0)": 0, + "(6715, '2024-11-27', 1)": 0, + "(6715, '2024-11-27', 2)": 0, + "(6715, '2024-11-27', 3)": 0, + "(6715, '2024-11-27', 4)": 0, + "(6715, '2024-11-27', 5)": 0, + "(6715, '2024-11-27', 6)": 0, + "(6715, '2024-11-27', 7)": 0, + "(6715, '2024-11-28', 0)": 0, + "(6715, '2024-11-28', 1)": 0, + "(6715, '2024-11-28', 2)": 0, + "(6715, '2024-11-28', 3)": 0, + "(6715, '2024-11-28', 4)": 0, + "(6715, '2024-11-28', 5)": 0, + "(6715, '2024-11-28', 6)": 0, + "(6715, '2024-11-28', 7)": 0, + "(6715, '2024-11-29', 0)": 0, + "(6715, '2024-11-29', 1)": 0, + "(6715, '2024-11-29', 2)": 0, + "(6715, '2024-11-29', 3)": 0, + "(6715, '2024-11-29', 4)": 0, + "(6715, '2024-11-29', 5)": 0, + "(6715, '2024-11-29', 6)": 0, + "(6715, '2024-11-29', 7)": 0, + "(6715, '2024-11-30', 0)": 0, + "(6715, '2024-11-30', 1)": 0, + "(6715, '2024-11-30', 2)": 0, + "(6715, '2024-11-30', 3)": 0, + "(6715, '2024-11-30', 4)": 0, + "(6715, '2024-11-30', 5)": 0, + "(6715, '2024-11-30', 6)": 0, + "(6715, '2024-11-30', 7)": 0, + "(6836, '2024-11-01', 0)": 0, + "(6836, '2024-11-01', 1)": 0, + "(6836, '2024-11-01', 2)": 0, + "(6836, '2024-11-01', 3)": 1, + "(6836, '2024-11-01', 4)": 0, + "(6836, '2024-11-01', 5)": 0, + "(6836, '2024-11-01', 6)": 0, + "(6836, '2024-11-01', 7)": 0, + "(6836, '2024-11-02', 0)": 0, + "(6836, '2024-11-02', 1)": 0, + "(6836, '2024-11-02', 2)": 0, + "(6836, '2024-11-02', 3)": 0, + "(6836, '2024-11-02', 4)": 0, + "(6836, '2024-11-02', 5)": 0, + "(6836, '2024-11-02', 6)": 0, + "(6836, '2024-11-02', 7)": 0, + "(6836, '2024-11-03', 0)": 1, + "(6836, '2024-11-03', 1)": 0, + "(6836, '2024-11-03', 2)": 0, + "(6836, '2024-11-03', 3)": 0, + "(6836, '2024-11-03', 4)": 0, + "(6836, '2024-11-03', 5)": 0, + "(6836, '2024-11-03', 6)": 0, + "(6836, '2024-11-03', 7)": 0, + "(6836, '2024-11-04', 0)": 1, + "(6836, '2024-11-04', 1)": 0, + "(6836, '2024-11-04', 2)": 0, + "(6836, '2024-11-04', 3)": 0, + "(6836, '2024-11-04', 4)": 0, + "(6836, '2024-11-04', 5)": 0, + "(6836, '2024-11-04', 6)": 0, + "(6836, '2024-11-04', 7)": 0, + "(6836, '2024-11-05', 0)": 0, + "(6836, '2024-11-05', 1)": 0, + "(6836, '2024-11-05', 2)": 0, + "(6836, '2024-11-05', 3)": 1, + "(6836, '2024-11-05', 4)": 0, + "(6836, '2024-11-05', 5)": 0, + "(6836, '2024-11-05', 6)": 0, + "(6836, '2024-11-05', 7)": 0, + "(6836, '2024-11-06', 0)": 0, + "(6836, '2024-11-06', 1)": 0, + "(6836, '2024-11-06', 2)": 0, + "(6836, '2024-11-06', 3)": 0, + "(6836, '2024-11-06', 4)": 0, + "(6836, '2024-11-06', 5)": 0, + "(6836, '2024-11-06', 6)": 0, + "(6836, '2024-11-06', 7)": 0, + "(6836, '2024-11-07', 0)": 0, + "(6836, '2024-11-07', 1)": 0, + "(6836, '2024-11-07', 2)": 0, + "(6836, '2024-11-07', 3)": 0, + "(6836, '2024-11-07', 4)": 0, + "(6836, '2024-11-07', 5)": 0, + "(6836, '2024-11-07', 6)": 0, + "(6836, '2024-11-07', 7)": 0, + "(6836, '2024-11-08', 0)": 0, + "(6836, '2024-11-08', 1)": 0, + "(6836, '2024-11-08', 2)": 0, + "(6836, '2024-11-08', 3)": 0, + "(6836, '2024-11-08', 4)": 0, + "(6836, '2024-11-08', 5)": 0, + "(6836, '2024-11-08', 6)": 0, + "(6836, '2024-11-08', 7)": 0, + "(6836, '2024-11-09', 0)": 0, + "(6836, '2024-11-09', 1)": 0, + "(6836, '2024-11-09', 2)": 1, + "(6836, '2024-11-09', 3)": 0, + "(6836, '2024-11-09', 4)": 0, + "(6836, '2024-11-09', 5)": 0, + "(6836, '2024-11-09', 6)": 0, + "(6836, '2024-11-09', 7)": 0, + "(6836, '2024-11-10', 0)": 0, + "(6836, '2024-11-10', 1)": 0, + "(6836, '2024-11-10', 2)": 0, + "(6836, '2024-11-10', 3)": 1, + "(6836, '2024-11-10', 4)": 0, + "(6836, '2024-11-10', 5)": 0, + "(6836, '2024-11-10', 6)": 0, + "(6836, '2024-11-10', 7)": 0, + "(6836, '2024-11-11', 0)": 0, + "(6836, '2024-11-11', 1)": 0, + "(6836, '2024-11-11', 2)": 0, + "(6836, '2024-11-11', 3)": 0, + "(6836, '2024-11-11', 4)": 0, + "(6836, '2024-11-11', 5)": 0, + "(6836, '2024-11-11', 6)": 0, + "(6836, '2024-11-11', 7)": 0, + "(6836, '2024-11-12', 0)": 0, + "(6836, '2024-11-12', 1)": 0, + "(6836, '2024-11-12', 2)": 0, + "(6836, '2024-11-12', 3)": 0, + "(6836, '2024-11-12', 4)": 0, + "(6836, '2024-11-12', 5)": 0, + "(6836, '2024-11-12', 6)": 0, + "(6836, '2024-11-12', 7)": 0, + "(6836, '2024-11-13', 0)": 1, + "(6836, '2024-11-13', 1)": 0, + "(6836, '2024-11-13', 2)": 0, + "(6836, '2024-11-13', 3)": 0, + "(6836, '2024-11-13', 4)": 0, + "(6836, '2024-11-13', 5)": 0, + "(6836, '2024-11-13', 6)": 0, + "(6836, '2024-11-13', 7)": 0, + "(6836, '2024-11-14', 0)": 1, + "(6836, '2024-11-14', 1)": 0, + "(6836, '2024-11-14', 2)": 0, + "(6836, '2024-11-14', 3)": 0, + "(6836, '2024-11-14', 4)": 0, + "(6836, '2024-11-14', 5)": 0, + "(6836, '2024-11-14', 6)": 0, + "(6836, '2024-11-14', 7)": 0, + "(6836, '2024-11-15', 0)": 1, + "(6836, '2024-11-15', 1)": 0, + "(6836, '2024-11-15', 2)": 0, + "(6836, '2024-11-15', 3)": 0, + "(6836, '2024-11-15', 4)": 0, + "(6836, '2024-11-15', 5)": 0, + "(6836, '2024-11-15', 6)": 0, + "(6836, '2024-11-15', 7)": 0, + "(6836, '2024-11-16', 0)": 1, + "(6836, '2024-11-16', 1)": 0, + "(6836, '2024-11-16', 2)": 0, + "(6836, '2024-11-16', 3)": 0, + "(6836, '2024-11-16', 4)": 0, + "(6836, '2024-11-16', 5)": 0, + "(6836, '2024-11-16', 6)": 0, + "(6836, '2024-11-16', 7)": 0, + "(6836, '2024-11-17', 0)": 0, + "(6836, '2024-11-17', 1)": 0, + "(6836, '2024-11-17', 2)": 0, + "(6836, '2024-11-17', 3)": 0, + "(6836, '2024-11-17', 4)": 0, + "(6836, '2024-11-17', 5)": 1, + "(6836, '2024-11-17', 6)": 0, + "(6836, '2024-11-17', 7)": 0, + "(6836, '2024-11-18', 0)": 0, + "(6836, '2024-11-18', 1)": 0, + "(6836, '2024-11-18', 2)": 1, + "(6836, '2024-11-18', 3)": 0, + "(6836, '2024-11-18', 4)": 0, + "(6836, '2024-11-18', 5)": 0, + "(6836, '2024-11-18', 6)": 0, + "(6836, '2024-11-18', 7)": 0, + "(6836, '2024-11-19', 0)": 0, + "(6836, '2024-11-19', 1)": 0, + "(6836, '2024-11-19', 2)": 0, + "(6836, '2024-11-19', 3)": 1, + "(6836, '2024-11-19', 4)": 0, + "(6836, '2024-11-19', 5)": 0, + "(6836, '2024-11-19', 6)": 0, + "(6836, '2024-11-19', 7)": 0, + "(6836, '2024-11-20', 0)": 0, + "(6836, '2024-11-20', 1)": 0, + "(6836, '2024-11-20', 2)": 0, + "(6836, '2024-11-20', 3)": 0, + "(6836, '2024-11-20', 4)": 0, + "(6836, '2024-11-20', 5)": 0, + "(6836, '2024-11-20', 6)": 0, + "(6836, '2024-11-20', 7)": 0, + "(6836, '2024-11-21', 0)": 0, + "(6836, '2024-11-21', 1)": 0, + "(6836, '2024-11-21', 2)": 0, + "(6836, '2024-11-21', 3)": 0, + "(6836, '2024-11-21', 4)": 0, + "(6836, '2024-11-21', 5)": 0, + "(6836, '2024-11-21', 6)": 0, + "(6836, '2024-11-21', 7)": 0, + "(6836, '2024-11-22', 0)": 1, + "(6836, '2024-11-22', 1)": 0, + "(6836, '2024-11-22', 2)": 0, + "(6836, '2024-11-22', 3)": 0, + "(6836, '2024-11-22', 4)": 0, + "(6836, '2024-11-22', 5)": 0, + "(6836, '2024-11-22', 6)": 0, + "(6836, '2024-11-22', 7)": 0, + "(6836, '2024-11-23', 0)": 0, + "(6836, '2024-11-23', 1)": 1, + "(6836, '2024-11-23', 2)": 0, + "(6836, '2024-11-23', 3)": 0, + "(6836, '2024-11-23', 4)": 0, + "(6836, '2024-11-23', 5)": 0, + "(6836, '2024-11-23', 6)": 0, + "(6836, '2024-11-23', 7)": 0, + "(6836, '2024-11-24', 0)": 0, + "(6836, '2024-11-24', 1)": 0, + "(6836, '2024-11-24', 2)": 1, + "(6836, '2024-11-24', 3)": 0, + "(6836, '2024-11-24', 4)": 0, + "(6836, '2024-11-24', 5)": 0, + "(6836, '2024-11-24', 6)": 0, + "(6836, '2024-11-24', 7)": 0, + "(6836, '2024-11-25', 0)": 0, + "(6836, '2024-11-25', 1)": 0, + "(6836, '2024-11-25', 2)": 0, + "(6836, '2024-11-25', 3)": 0, + "(6836, '2024-11-25', 4)": 0, + "(6836, '2024-11-25', 5)": 0, + "(6836, '2024-11-25', 6)": 0, + "(6836, '2024-11-25', 7)": 0, + "(6836, '2024-11-26', 0)": 0, + "(6836, '2024-11-26', 1)": 0, + "(6836, '2024-11-26', 2)": 0, + "(6836, '2024-11-26', 3)": 0, + "(6836, '2024-11-26', 4)": 0, + "(6836, '2024-11-26', 5)": 0, + "(6836, '2024-11-26', 6)": 0, + "(6836, '2024-11-26', 7)": 0, + "(6836, '2024-11-27', 0)": 0, + "(6836, '2024-11-27', 1)": 0, + "(6836, '2024-11-27', 2)": 1, + "(6836, '2024-11-27', 3)": 0, + "(6836, '2024-11-27', 4)": 0, + "(6836, '2024-11-27', 5)": 0, + "(6836, '2024-11-27', 6)": 0, + "(6836, '2024-11-27', 7)": 0, + "(6836, '2024-11-28', 0)": 0, + "(6836, '2024-11-28', 1)": 0, + "(6836, '2024-11-28', 2)": 0, + "(6836, '2024-11-28', 3)": 0, + "(6836, '2024-11-28', 4)": 0, + "(6836, '2024-11-28', 5)": 0, + "(6836, '2024-11-28', 6)": 0, + "(6836, '2024-11-28', 7)": 0, + "(6836, '2024-11-29', 0)": 0, + "(6836, '2024-11-29', 1)": 0, + "(6836, '2024-11-29', 2)": 1, + "(6836, '2024-11-29', 3)": 0, + "(6836, '2024-11-29', 4)": 0, + "(6836, '2024-11-29', 5)": 0, + "(6836, '2024-11-29', 6)": 0, + "(6836, '2024-11-29', 7)": 0, + "(6836, '2024-11-30', 0)": 0, + "(6836, '2024-11-30', 1)": 0, + "(6836, '2024-11-30', 2)": 1, + "(6836, '2024-11-30', 3)": 0, + "(6836, '2024-11-30', 4)": 0, + "(6836, '2024-11-30', 5)": 0, + "(6836, '2024-11-30', 6)": 0, + "(6836, '2024-11-30', 7)": 0, + "(6928, '2024-11-01', 0)": 1, + "(6928, '2024-11-01', 1)": 0, + "(6928, '2024-11-01', 2)": 0, + "(6928, '2024-11-01', 3)": 0, + "(6928, '2024-11-01', 4)": 0, + "(6928, '2024-11-01', 5)": 0, + "(6928, '2024-11-01', 6)": 0, + "(6928, '2024-11-01', 7)": 0, + "(6928, '2024-11-02', 0)": 0, + "(6928, '2024-11-02', 1)": 0, + "(6928, '2024-11-02', 2)": 1, + "(6928, '2024-11-02', 3)": 0, + "(6928, '2024-11-02', 4)": 0, + "(6928, '2024-11-02', 5)": 0, + "(6928, '2024-11-02', 6)": 0, + "(6928, '2024-11-02', 7)": 0, + "(6928, '2024-11-03', 0)": 0, + "(6928, '2024-11-03', 1)": 0, + "(6928, '2024-11-03', 2)": 1, + "(6928, '2024-11-03', 3)": 0, + "(6928, '2024-11-03', 4)": 0, + "(6928, '2024-11-03', 5)": 0, + "(6928, '2024-11-03', 6)": 0, + "(6928, '2024-11-03', 7)": 0, + "(6928, '2024-11-04', 0)": 0, + "(6928, '2024-11-04', 1)": 0, + "(6928, '2024-11-04', 2)": 1, + "(6928, '2024-11-04', 3)": 0, + "(6928, '2024-11-04', 4)": 0, + "(6928, '2024-11-04', 5)": 0, + "(6928, '2024-11-04', 6)": 0, + "(6928, '2024-11-04', 7)": 0, + "(6928, '2024-11-05', 0)": 0, + "(6928, '2024-11-05', 1)": 0, + "(6928, '2024-11-05', 2)": 0, + "(6928, '2024-11-05', 3)": 0, + "(6928, '2024-11-05', 4)": 0, + "(6928, '2024-11-05', 5)": 0, + "(6928, '2024-11-05', 6)": 0, + "(6928, '2024-11-05', 7)": 0, + "(6928, '2024-11-06', 0)": 0, + "(6928, '2024-11-06', 1)": 0, + "(6928, '2024-11-06', 2)": 0, + "(6928, '2024-11-06', 3)": 0, + "(6928, '2024-11-06', 4)": 0, + "(6928, '2024-11-06', 5)": 0, + "(6928, '2024-11-06', 6)": 0, + "(6928, '2024-11-06', 7)": 0, + "(6928, '2024-11-07', 0)": 0, + "(6928, '2024-11-07', 1)": 0, + "(6928, '2024-11-07', 2)": 1, + "(6928, '2024-11-07', 3)": 0, + "(6928, '2024-11-07', 4)": 0, + "(6928, '2024-11-07', 5)": 0, + "(6928, '2024-11-07', 6)": 0, + "(6928, '2024-11-07', 7)": 0, + "(6928, '2024-11-08', 0)": 0, + "(6928, '2024-11-08', 1)": 0, + "(6928, '2024-11-08', 2)": 1, + "(6928, '2024-11-08', 3)": 0, + "(6928, '2024-11-08', 4)": 0, + "(6928, '2024-11-08', 5)": 0, + "(6928, '2024-11-08', 6)": 0, + "(6928, '2024-11-08', 7)": 0, + "(6928, '2024-11-09', 0)": 0, + "(6928, '2024-11-09', 1)": 0, + "(6928, '2024-11-09', 2)": 0, + "(6928, '2024-11-09', 3)": 0, + "(6928, '2024-11-09', 4)": 0, + "(6928, '2024-11-09', 5)": 0, + "(6928, '2024-11-09', 6)": 0, + "(6928, '2024-11-09', 7)": 0, + "(6928, '2024-11-10', 0)": 0, + "(6928, '2024-11-10', 1)": 0, + "(6928, '2024-11-10', 2)": 1, + "(6928, '2024-11-10', 3)": 0, + "(6928, '2024-11-10', 4)": 0, + "(6928, '2024-11-10', 5)": 0, + "(6928, '2024-11-10', 6)": 0, + "(6928, '2024-11-10', 7)": 0, + "(6928, '2024-11-11', 0)": 0, + "(6928, '2024-11-11', 1)": 0, + "(6928, '2024-11-11', 2)": 0, + "(6928, '2024-11-11', 3)": 0, + "(6928, '2024-11-11', 4)": 0, + "(6928, '2024-11-11', 5)": 0, + "(6928, '2024-11-11', 6)": 0, + "(6928, '2024-11-11', 7)": 0, + "(6928, '2024-11-12', 0)": 1, + "(6928, '2024-11-12', 1)": 0, + "(6928, '2024-11-12', 2)": 0, + "(6928, '2024-11-12', 3)": 0, + "(6928, '2024-11-12', 4)": 0, + "(6928, '2024-11-12', 5)": 0, + "(6928, '2024-11-12', 6)": 0, + "(6928, '2024-11-12', 7)": 0, + "(6928, '2024-11-13', 0)": 1, + "(6928, '2024-11-13', 1)": 0, + "(6928, '2024-11-13', 2)": 0, + "(6928, '2024-11-13', 3)": 0, + "(6928, '2024-11-13', 4)": 0, + "(6928, '2024-11-13', 5)": 0, + "(6928, '2024-11-13', 6)": 0, + "(6928, '2024-11-13', 7)": 0, + "(6928, '2024-11-14', 0)": 0, + "(6928, '2024-11-14', 1)": 0, + "(6928, '2024-11-14', 2)": 0, + "(6928, '2024-11-14', 3)": 0, + "(6928, '2024-11-14', 4)": 0, + "(6928, '2024-11-14', 5)": 0, + "(6928, '2024-11-14', 6)": 0, + "(6928, '2024-11-14', 7)": 0, + "(6928, '2024-11-15', 0)": 1, + "(6928, '2024-11-15', 1)": 0, + "(6928, '2024-11-15', 2)": 0, + "(6928, '2024-11-15', 3)": 0, + "(6928, '2024-11-15', 4)": 0, + "(6928, '2024-11-15', 5)": 0, + "(6928, '2024-11-15', 6)": 0, + "(6928, '2024-11-15', 7)": 0, + "(6928, '2024-11-16', 0)": 1, + "(6928, '2024-11-16', 1)": 0, + "(6928, '2024-11-16', 2)": 0, + "(6928, '2024-11-16', 3)": 0, + "(6928, '2024-11-16', 4)": 0, + "(6928, '2024-11-16', 5)": 0, + "(6928, '2024-11-16', 6)": 0, + "(6928, '2024-11-16', 7)": 0, + "(6928, '2024-11-17', 0)": 0, + "(6928, '2024-11-17', 1)": 0, + "(6928, '2024-11-17', 2)": 1, + "(6928, '2024-11-17', 3)": 0, + "(6928, '2024-11-17', 4)": 0, + "(6928, '2024-11-17', 5)": 0, + "(6928, '2024-11-17', 6)": 0, + "(6928, '2024-11-17', 7)": 0, + "(6928, '2024-11-18', 0)": 0, + "(6928, '2024-11-18', 1)": 0, + "(6928, '2024-11-18', 2)": 1, + "(6928, '2024-11-18', 3)": 0, + "(6928, '2024-11-18', 4)": 0, + "(6928, '2024-11-18', 5)": 0, + "(6928, '2024-11-18', 6)": 0, + "(6928, '2024-11-18', 7)": 0, + "(6928, '2024-11-19', 0)": 0, + "(6928, '2024-11-19', 1)": 0, + "(6928, '2024-11-19', 2)": 1, + "(6928, '2024-11-19', 3)": 0, + "(6928, '2024-11-19', 4)": 0, + "(6928, '2024-11-19', 5)": 0, + "(6928, '2024-11-19', 6)": 0, + "(6928, '2024-11-19', 7)": 0, + "(6928, '2024-11-20', 0)": 0, + "(6928, '2024-11-20', 1)": 0, + "(6928, '2024-11-20', 2)": 0, + "(6928, '2024-11-20', 3)": 0, + "(6928, '2024-11-20', 4)": 0, + "(6928, '2024-11-20', 5)": 0, + "(6928, '2024-11-20', 6)": 0, + "(6928, '2024-11-20', 7)": 0, + "(6928, '2024-11-21', 0)": 0, + "(6928, '2024-11-21', 1)": 0, + "(6928, '2024-11-21', 2)": 0, + "(6928, '2024-11-21', 3)": 0, + "(6928, '2024-11-21', 4)": 0, + "(6928, '2024-11-21', 5)": 0, + "(6928, '2024-11-21', 6)": 0, + "(6928, '2024-11-21', 7)": 0, + "(6928, '2024-11-22', 0)": 1, + "(6928, '2024-11-22', 1)": 0, + "(6928, '2024-11-22', 2)": 0, + "(6928, '2024-11-22', 3)": 0, + "(6928, '2024-11-22', 4)": 0, + "(6928, '2024-11-22', 5)": 0, + "(6928, '2024-11-22', 6)": 0, + "(6928, '2024-11-22', 7)": 0, + "(6928, '2024-11-23', 0)": 0, + "(6928, '2024-11-23', 1)": 0, + "(6928, '2024-11-23', 2)": 0, + "(6928, '2024-11-23', 3)": 1, + "(6928, '2024-11-23', 4)": 0, + "(6928, '2024-11-23', 5)": 0, + "(6928, '2024-11-23', 6)": 0, + "(6928, '2024-11-23', 7)": 0, + "(6928, '2024-11-24', 0)": 0, + "(6928, '2024-11-24', 1)": 0, + "(6928, '2024-11-24', 2)": 0, + "(6928, '2024-11-24', 3)": 0, + "(6928, '2024-11-24', 4)": 0, + "(6928, '2024-11-24', 5)": 0, + "(6928, '2024-11-24', 6)": 0, + "(6928, '2024-11-24', 7)": 0, + "(6928, '2024-11-25', 0)": 0, + "(6928, '2024-11-25', 1)": 0, + "(6928, '2024-11-25', 2)": 0, + "(6928, '2024-11-25', 3)": 0, + "(6928, '2024-11-25', 4)": 0, + "(6928, '2024-11-25', 5)": 0, + "(6928, '2024-11-25', 6)": 0, + "(6928, '2024-11-25', 7)": 0, + "(6928, '2024-11-26', 0)": 1, + "(6928, '2024-11-26', 1)": 0, + "(6928, '2024-11-26', 2)": 0, + "(6928, '2024-11-26', 3)": 0, + "(6928, '2024-11-26', 4)": 0, + "(6928, '2024-11-26', 5)": 0, + "(6928, '2024-11-26', 6)": 0, + "(6928, '2024-11-26', 7)": 0, + "(6928, '2024-11-27', 0)": 0, + "(6928, '2024-11-27', 1)": 0, + "(6928, '2024-11-27', 2)": 0, + "(6928, '2024-11-27', 3)": 0, + "(6928, '2024-11-27', 4)": 0, + "(6928, '2024-11-27', 5)": 0, + "(6928, '2024-11-27', 6)": 0, + "(6928, '2024-11-27', 7)": 0, + "(6928, '2024-11-28', 0)": 1, + "(6928, '2024-11-28', 1)": 0, + "(6928, '2024-11-28', 2)": 0, + "(6928, '2024-11-28', 3)": 0, + "(6928, '2024-11-28', 4)": 0, + "(6928, '2024-11-28', 5)": 0, + "(6928, '2024-11-28', 6)": 0, + "(6928, '2024-11-28', 7)": 0, + "(6928, '2024-11-29', 0)": 0, + "(6928, '2024-11-29', 1)": 0, + "(6928, '2024-11-29', 2)": 1, + "(6928, '2024-11-29', 3)": 0, + "(6928, '2024-11-29', 4)": 0, + "(6928, '2024-11-29', 5)": 0, + "(6928, '2024-11-29', 6)": 0, + "(6928, '2024-11-29', 7)": 0, + "(6928, '2024-11-30', 0)": 0, + "(6928, '2024-11-30', 1)": 0, + "(6928, '2024-11-30', 2)": 0, + "(6928, '2024-11-30', 3)": 1, + "(6928, '2024-11-30', 4)": 0, + "(6928, '2024-11-30', 5)": 0, + "(6928, '2024-11-30', 6)": 0, + "(6928, '2024-11-30', 7)": 0, + "(7, '2024-11-01', 0)": 0, + "(7, '2024-11-01', 1)": 0, + "(7, '2024-11-01', 2)": 0, + "(7, '2024-11-01', 3)": 0, + "(7, '2024-11-01', 4)": 0, + "(7, '2024-11-01', 5)": 0, + "(7, '2024-11-01', 6)": 0, + "(7, '2024-11-01', 7)": 0, + "(7, '2024-11-02', 0)": 1, + "(7, '2024-11-02', 1)": 0, + "(7, '2024-11-02', 2)": 0, + "(7, '2024-11-02', 3)": 0, + "(7, '2024-11-02', 4)": 0, + "(7, '2024-11-02', 5)": 0, + "(7, '2024-11-02', 6)": 0, + "(7, '2024-11-02', 7)": 0, + "(7, '2024-11-03', 0)": 1, + "(7, '2024-11-03', 1)": 0, + "(7, '2024-11-03', 2)": 0, + "(7, '2024-11-03', 3)": 0, + "(7, '2024-11-03', 4)": 0, + "(7, '2024-11-03', 5)": 0, + "(7, '2024-11-03', 6)": 0, + "(7, '2024-11-03', 7)": 0, + "(7, '2024-11-04', 0)": 0, + "(7, '2024-11-04', 1)": 0, + "(7, '2024-11-04', 2)": 1, + "(7, '2024-11-04', 3)": 0, + "(7, '2024-11-04', 4)": 0, + "(7, '2024-11-04', 5)": 0, + "(7, '2024-11-04', 6)": 0, + "(7, '2024-11-04', 7)": 0, + "(7, '2024-11-05', 0)": 0, + "(7, '2024-11-05', 1)": 0, + "(7, '2024-11-05', 2)": 0, + "(7, '2024-11-05', 3)": 0, + "(7, '2024-11-05', 4)": 0, + "(7, '2024-11-05', 5)": 0, + "(7, '2024-11-05', 6)": 0, + "(7, '2024-11-05', 7)": 0, + "(7, '2024-11-06', 0)": 0, + "(7, '2024-11-06', 1)": 0, + "(7, '2024-11-06', 2)": 0, + "(7, '2024-11-06', 3)": 0, + "(7, '2024-11-06', 4)": 0, + "(7, '2024-11-06', 5)": 0, + "(7, '2024-11-06', 6)": 0, + "(7, '2024-11-06', 7)": 0, + "(7, '2024-11-07', 0)": 0, + "(7, '2024-11-07', 1)": 0, + "(7, '2024-11-07', 2)": 0, + "(7, '2024-11-07', 3)": 0, + "(7, '2024-11-07', 4)": 0, + "(7, '2024-11-07', 5)": 0, + "(7, '2024-11-07', 6)": 0, + "(7, '2024-11-07', 7)": 0, + "(7, '2024-11-08', 0)": 0, + "(7, '2024-11-08', 1)": 0, + "(7, '2024-11-08', 2)": 0, + "(7, '2024-11-08', 3)": 0, + "(7, '2024-11-08', 4)": 0, + "(7, '2024-11-08', 5)": 0, + "(7, '2024-11-08', 6)": 0, + "(7, '2024-11-08', 7)": 0, + "(7, '2024-11-09', 0)": 0, + "(7, '2024-11-09', 1)": 0, + "(7, '2024-11-09', 2)": 1, + "(7, '2024-11-09', 3)": 0, + "(7, '2024-11-09', 4)": 0, + "(7, '2024-11-09', 5)": 0, + "(7, '2024-11-09', 6)": 0, + "(7, '2024-11-09', 7)": 0, + "(7, '2024-11-10', 0)": 0, + "(7, '2024-11-10', 1)": 0, + "(7, '2024-11-10', 2)": 0, + "(7, '2024-11-10', 3)": 0, + "(7, '2024-11-10', 4)": 0, + "(7, '2024-11-10', 5)": 0, + "(7, '2024-11-10', 6)": 0, + "(7, '2024-11-10', 7)": 0, + "(7, '2024-11-11', 0)": 0, + "(7, '2024-11-11', 1)": 0, + "(7, '2024-11-11', 2)": 1, + "(7, '2024-11-11', 3)": 0, + "(7, '2024-11-11', 4)": 0, + "(7, '2024-11-11', 5)": 0, + "(7, '2024-11-11', 6)": 0, + "(7, '2024-11-11', 7)": 0, + "(7, '2024-11-12', 0)": 0, + "(7, '2024-11-12', 1)": 0, + "(7, '2024-11-12', 2)": 0, + "(7, '2024-11-12', 3)": 0, + "(7, '2024-11-12', 4)": 0, + "(7, '2024-11-12', 5)": 0, + "(7, '2024-11-12', 6)": 0, + "(7, '2024-11-12', 7)": 0, + "(7, '2024-11-13', 0)": 0, + "(7, '2024-11-13', 1)": 0, + "(7, '2024-11-13', 2)": 1, + "(7, '2024-11-13', 3)": 0, + "(7, '2024-11-13', 4)": 0, + "(7, '2024-11-13', 5)": 0, + "(7, '2024-11-13', 6)": 0, + "(7, '2024-11-13', 7)": 0, + "(7, '2024-11-14', 0)": 0, + "(7, '2024-11-14', 1)": 0, + "(7, '2024-11-14', 2)": 0, + "(7, '2024-11-14', 3)": 0, + "(7, '2024-11-14', 4)": 0, + "(7, '2024-11-14', 5)": 0, + "(7, '2024-11-14', 6)": 0, + "(7, '2024-11-14', 7)": 0, + "(7, '2024-11-15', 0)": 0, + "(7, '2024-11-15', 1)": 0, + "(7, '2024-11-15', 2)": 0, + "(7, '2024-11-15', 3)": 0, + "(7, '2024-11-15', 4)": 0, + "(7, '2024-11-15', 5)": 0, + "(7, '2024-11-15', 6)": 0, + "(7, '2024-11-15', 7)": 0, + "(7, '2024-11-16', 0)": 0, + "(7, '2024-11-16', 1)": 0, + "(7, '2024-11-16', 2)": 0, + "(7, '2024-11-16', 3)": 0, + "(7, '2024-11-16', 4)": 0, + "(7, '2024-11-16', 5)": 0, + "(7, '2024-11-16', 6)": 0, + "(7, '2024-11-16', 7)": 0, + "(7, '2024-11-17', 0)": 1, + "(7, '2024-11-17', 1)": 0, + "(7, '2024-11-17', 2)": 0, + "(7, '2024-11-17', 3)": 0, + "(7, '2024-11-17', 4)": 0, + "(7, '2024-11-17', 5)": 0, + "(7, '2024-11-17', 6)": 0, + "(7, '2024-11-17', 7)": 0, + "(7, '2024-11-18', 0)": 0, + "(7, '2024-11-18', 1)": 0, + "(7, '2024-11-18', 2)": 0, + "(7, '2024-11-18', 3)": 0, + "(7, '2024-11-18', 4)": 0, + "(7, '2024-11-18', 5)": 0, + "(7, '2024-11-18', 6)": 0, + "(7, '2024-11-18', 7)": 0, + "(7, '2024-11-19', 0)": 0, + "(7, '2024-11-19', 1)": 0, + "(7, '2024-11-19', 2)": 0, + "(7, '2024-11-19', 3)": 0, + "(7, '2024-11-19', 4)": 0, + "(7, '2024-11-19', 5)": 0, + "(7, '2024-11-19', 6)": 0, + "(7, '2024-11-19', 7)": 0, + "(7, '2024-11-20', 0)": 0, + "(7, '2024-11-20', 1)": 0, + "(7, '2024-11-20', 2)": 1, + "(7, '2024-11-20', 3)": 0, + "(7, '2024-11-20', 4)": 0, + "(7, '2024-11-20', 5)": 0, + "(7, '2024-11-20', 6)": 0, + "(7, '2024-11-20', 7)": 0, + "(7, '2024-11-21', 0)": 0, + "(7, '2024-11-21', 1)": 0, + "(7, '2024-11-21', 2)": 0, + "(7, '2024-11-21', 3)": 0, + "(7, '2024-11-21', 4)": 0, + "(7, '2024-11-21', 5)": 0, + "(7, '2024-11-21', 6)": 0, + "(7, '2024-11-21', 7)": 0, + "(7, '2024-11-22', 0)": 0, + "(7, '2024-11-22', 1)": 0, + "(7, '2024-11-22', 2)": 0, + "(7, '2024-11-22', 3)": 0, + "(7, '2024-11-22', 4)": 0, + "(7, '2024-11-22', 5)": 0, + "(7, '2024-11-22', 6)": 0, + "(7, '2024-11-22', 7)": 0, + "(7, '2024-11-23', 0)": 1, + "(7, '2024-11-23', 1)": 0, + "(7, '2024-11-23', 2)": 0, + "(7, '2024-11-23', 3)": 0, + "(7, '2024-11-23', 4)": 0, + "(7, '2024-11-23', 5)": 0, + "(7, '2024-11-23', 6)": 0, + "(7, '2024-11-23', 7)": 0, + "(7, '2024-11-24', 0)": 0, + "(7, '2024-11-24', 1)": 0, + "(7, '2024-11-24', 2)": 0, + "(7, '2024-11-24', 3)": 0, + "(7, '2024-11-24', 4)": 0, + "(7, '2024-11-24', 5)": 0, + "(7, '2024-11-24', 6)": 0, + "(7, '2024-11-24', 7)": 0, + "(7, '2024-11-25', 0)": 1, + "(7, '2024-11-25', 1)": 0, + "(7, '2024-11-25', 2)": 0, + "(7, '2024-11-25', 3)": 0, + "(7, '2024-11-25', 4)": 0, + "(7, '2024-11-25', 5)": 0, + "(7, '2024-11-25', 6)": 0, + "(7, '2024-11-25', 7)": 0, + "(7, '2024-11-26', 0)": 0, + "(7, '2024-11-26', 1)": 0, + "(7, '2024-11-26', 2)": 0, + "(7, '2024-11-26', 3)": 0, + "(7, '2024-11-26', 4)": 0, + "(7, '2024-11-26', 5)": 0, + "(7, '2024-11-26', 6)": 0, + "(7, '2024-11-26', 7)": 0, + "(7, '2024-11-27', 0)": 0, + "(7, '2024-11-27', 1)": 0, + "(7, '2024-11-27', 2)": 0, + "(7, '2024-11-27', 3)": 0, + "(7, '2024-11-27', 4)": 0, + "(7, '2024-11-27', 5)": 0, + "(7, '2024-11-27', 6)": 0, + "(7, '2024-11-27', 7)": 0, + "(7, '2024-11-28', 0)": 0, + "(7, '2024-11-28', 1)": 0, + "(7, '2024-11-28', 2)": 0, + "(7, '2024-11-28', 3)": 0, + "(7, '2024-11-28', 4)": 0, + "(7, '2024-11-28', 5)": 0, + "(7, '2024-11-28', 6)": 0, + "(7, '2024-11-28', 7)": 0, + "(7, '2024-11-29', 0)": 0, + "(7, '2024-11-29', 1)": 0, + "(7, '2024-11-29', 2)": 0, + "(7, '2024-11-29', 3)": 0, + "(7, '2024-11-29', 4)": 0, + "(7, '2024-11-29', 5)": 0, + "(7, '2024-11-29', 6)": 0, + "(7, '2024-11-29', 7)": 0, + "(7, '2024-11-30', 0)": 0, + "(7, '2024-11-30', 1)": 0, + "(7, '2024-11-30', 2)": 0, + "(7, '2024-11-30', 3)": 0, + "(7, '2024-11-30', 4)": 0, + "(7, '2024-11-30', 5)": 0, + "(7, '2024-11-30', 6)": 0, + "(7, '2024-11-30', 7)": 0, + "(7496, '2024-11-01', 0)": 0, + "(7496, '2024-11-01', 1)": 0, + "(7496, '2024-11-01', 2)": 0, + "(7496, '2024-11-01', 3)": 0, + "(7496, '2024-11-01', 4)": 0, + "(7496, '2024-11-01', 5)": 0, + "(7496, '2024-11-01', 6)": 0, + "(7496, '2024-11-01', 7)": 0, + "(7496, '2024-11-02', 0)": 0, + "(7496, '2024-11-02', 1)": 0, + "(7496, '2024-11-02', 2)": 0, + "(7496, '2024-11-02', 3)": 0, + "(7496, '2024-11-02', 4)": 0, + "(7496, '2024-11-02', 5)": 0, + "(7496, '2024-11-02', 6)": 0, + "(7496, '2024-11-02', 7)": 0, + "(7496, '2024-11-03', 0)": 0, + "(7496, '2024-11-03', 1)": 0, + "(7496, '2024-11-03', 2)": 0, + "(7496, '2024-11-03', 3)": 0, + "(7496, '2024-11-03', 4)": 0, + "(7496, '2024-11-03', 5)": 0, + "(7496, '2024-11-03', 6)": 0, + "(7496, '2024-11-03', 7)": 0, + "(7496, '2024-11-04', 0)": 0, + "(7496, '2024-11-04', 1)": 0, + "(7496, '2024-11-04', 2)": 0, + "(7496, '2024-11-04', 3)": 0, + "(7496, '2024-11-04', 4)": 0, + "(7496, '2024-11-04', 5)": 0, + "(7496, '2024-11-04', 6)": 0, + "(7496, '2024-11-04', 7)": 0, + "(7496, '2024-11-05', 0)": 0, + "(7496, '2024-11-05', 1)": 0, + "(7496, '2024-11-05', 2)": 0, + "(7496, '2024-11-05', 3)": 0, + "(7496, '2024-11-05', 4)": 0, + "(7496, '2024-11-05', 5)": 0, + "(7496, '2024-11-05', 6)": 0, + "(7496, '2024-11-05', 7)": 0, + "(7496, '2024-11-06', 0)": 0, + "(7496, '2024-11-06', 1)": 0, + "(7496, '2024-11-06', 2)": 0, + "(7496, '2024-11-06', 3)": 0, + "(7496, '2024-11-06', 4)": 0, + "(7496, '2024-11-06', 5)": 0, + "(7496, '2024-11-06', 6)": 0, + "(7496, '2024-11-06', 7)": 0, + "(7496, '2024-11-07', 0)": 0, + "(7496, '2024-11-07', 1)": 0, + "(7496, '2024-11-07', 2)": 0, + "(7496, '2024-11-07', 3)": 0, + "(7496, '2024-11-07', 4)": 0, + "(7496, '2024-11-07', 5)": 0, + "(7496, '2024-11-07', 6)": 0, + "(7496, '2024-11-07', 7)": 0, + "(7496, '2024-11-08', 0)": 0, + "(7496, '2024-11-08', 1)": 0, + "(7496, '2024-11-08', 2)": 0, + "(7496, '2024-11-08', 3)": 0, + "(7496, '2024-11-08', 4)": 0, + "(7496, '2024-11-08', 5)": 0, + "(7496, '2024-11-08', 6)": 0, + "(7496, '2024-11-08', 7)": 0, + "(7496, '2024-11-09', 0)": 0, + "(7496, '2024-11-09', 1)": 0, + "(7496, '2024-11-09', 2)": 0, + "(7496, '2024-11-09', 3)": 0, + "(7496, '2024-11-09', 4)": 0, + "(7496, '2024-11-09', 5)": 0, + "(7496, '2024-11-09', 6)": 0, + "(7496, '2024-11-09', 7)": 0, + "(7496, '2024-11-10', 0)": 0, + "(7496, '2024-11-10', 1)": 0, + "(7496, '2024-11-10', 2)": 0, + "(7496, '2024-11-10', 3)": 0, + "(7496, '2024-11-10', 4)": 0, + "(7496, '2024-11-10', 5)": 0, + "(7496, '2024-11-10', 6)": 0, + "(7496, '2024-11-10', 7)": 0, + "(7496, '2024-11-11', 0)": 0, + "(7496, '2024-11-11', 1)": 0, + "(7496, '2024-11-11', 2)": 0, + "(7496, '2024-11-11', 3)": 0, + "(7496, '2024-11-11', 4)": 0, + "(7496, '2024-11-11', 5)": 0, + "(7496, '2024-11-11', 6)": 0, + "(7496, '2024-11-11', 7)": 0, + "(7496, '2024-11-12', 0)": 0, + "(7496, '2024-11-12', 1)": 0, + "(7496, '2024-11-12', 2)": 0, + "(7496, '2024-11-12', 3)": 0, + "(7496, '2024-11-12', 4)": 0, + "(7496, '2024-11-12', 5)": 0, + "(7496, '2024-11-12', 6)": 0, + "(7496, '2024-11-12', 7)": 0, + "(7496, '2024-11-13', 0)": 0, + "(7496, '2024-11-13', 1)": 0, + "(7496, '2024-11-13', 2)": 0, + "(7496, '2024-11-13', 3)": 0, + "(7496, '2024-11-13', 4)": 0, + "(7496, '2024-11-13', 5)": 0, + "(7496, '2024-11-13', 6)": 0, + "(7496, '2024-11-13', 7)": 0, + "(7496, '2024-11-14', 0)": 0, + "(7496, '2024-11-14', 1)": 0, + "(7496, '2024-11-14', 2)": 0, + "(7496, '2024-11-14', 3)": 0, + "(7496, '2024-11-14', 4)": 0, + "(7496, '2024-11-14', 5)": 0, + "(7496, '2024-11-14', 6)": 0, + "(7496, '2024-11-14', 7)": 0, + "(7496, '2024-11-15', 0)": 0, + "(7496, '2024-11-15', 1)": 0, + "(7496, '2024-11-15', 2)": 0, + "(7496, '2024-11-15', 3)": 0, + "(7496, '2024-11-15', 4)": 0, + "(7496, '2024-11-15', 5)": 0, + "(7496, '2024-11-15', 6)": 1, + "(7496, '2024-11-15', 7)": 0, + "(7496, '2024-11-16', 0)": 0, + "(7496, '2024-11-16', 1)": 0, + "(7496, '2024-11-16', 2)": 0, + "(7496, '2024-11-16', 3)": 0, + "(7496, '2024-11-16', 4)": 0, + "(7496, '2024-11-16', 5)": 0, + "(7496, '2024-11-16', 6)": 0, + "(7496, '2024-11-16', 7)": 0, + "(7496, '2024-11-17', 0)": 0, + "(7496, '2024-11-17', 1)": 0, + "(7496, '2024-11-17', 2)": 0, + "(7496, '2024-11-17', 3)": 0, + "(7496, '2024-11-17', 4)": 0, + "(7496, '2024-11-17', 5)": 1, + "(7496, '2024-11-17', 6)": 0, + "(7496, '2024-11-17', 7)": 0, + "(7496, '2024-11-18', 0)": 0, + "(7496, '2024-11-18', 1)": 0, + "(7496, '2024-11-18', 2)": 0, + "(7496, '2024-11-18', 3)": 0, + "(7496, '2024-11-18', 4)": 0, + "(7496, '2024-11-18', 5)": 1, + "(7496, '2024-11-18', 6)": 0, + "(7496, '2024-11-18', 7)": 0, + "(7496, '2024-11-19', 0)": 0, + "(7496, '2024-11-19', 1)": 0, + "(7496, '2024-11-19', 2)": 0, + "(7496, '2024-11-19', 3)": 0, + "(7496, '2024-11-19', 4)": 0, + "(7496, '2024-11-19', 5)": 0, + "(7496, '2024-11-19', 6)": 0, + "(7496, '2024-11-19', 7)": 1, + "(7496, '2024-11-20', 0)": 0, + "(7496, '2024-11-20', 1)": 0, + "(7496, '2024-11-20', 2)": 0, + "(7496, '2024-11-20', 3)": 0, + "(7496, '2024-11-20', 4)": 0, + "(7496, '2024-11-20', 5)": 0, + "(7496, '2024-11-20', 6)": 0, + "(7496, '2024-11-20', 7)": 1, + "(7496, '2024-11-21', 0)": 0, + "(7496, '2024-11-21', 1)": 0, + "(7496, '2024-11-21', 2)": 0, + "(7496, '2024-11-21', 3)": 0, + "(7496, '2024-11-21', 4)": 0, + "(7496, '2024-11-21', 5)": 0, + "(7496, '2024-11-21', 6)": 0, + "(7496, '2024-11-21', 7)": 0, + "(7496, '2024-11-22', 0)": 0, + "(7496, '2024-11-22', 1)": 0, + "(7496, '2024-11-22', 2)": 0, + "(7496, '2024-11-22', 3)": 0, + "(7496, '2024-11-22', 4)": 0, + "(7496, '2024-11-22', 5)": 0, + "(7496, '2024-11-22', 6)": 0, + "(7496, '2024-11-22', 7)": 0, + "(7496, '2024-11-23', 0)": 0, + "(7496, '2024-11-23', 1)": 0, + "(7496, '2024-11-23', 2)": 0, + "(7496, '2024-11-23', 3)": 0, + "(7496, '2024-11-23', 4)": 0, + "(7496, '2024-11-23', 5)": 0, + "(7496, '2024-11-23', 6)": 0, + "(7496, '2024-11-23', 7)": 0, + "(7496, '2024-11-24', 0)": 0, + "(7496, '2024-11-24', 1)": 0, + "(7496, '2024-11-24', 2)": 0, + "(7496, '2024-11-24', 3)": 0, + "(7496, '2024-11-24', 4)": 0, + "(7496, '2024-11-24', 5)": 0, + "(7496, '2024-11-24', 6)": 0, + "(7496, '2024-11-24', 7)": 0, + "(7496, '2024-11-25', 0)": 0, + "(7496, '2024-11-25', 1)": 0, + "(7496, '2024-11-25', 2)": 0, + "(7496, '2024-11-25', 3)": 0, + "(7496, '2024-11-25', 4)": 0, + "(7496, '2024-11-25', 5)": 1, + "(7496, '2024-11-25', 6)": 0, + "(7496, '2024-11-25', 7)": 0, + "(7496, '2024-11-26', 0)": 0, + "(7496, '2024-11-26', 1)": 0, + "(7496, '2024-11-26', 2)": 0, + "(7496, '2024-11-26', 3)": 0, + "(7496, '2024-11-26', 4)": 0, + "(7496, '2024-11-26', 5)": 1, + "(7496, '2024-11-26', 6)": 0, + "(7496, '2024-11-26', 7)": 0, + "(7496, '2024-11-27', 0)": 0, + "(7496, '2024-11-27', 1)": 0, + "(7496, '2024-11-27', 2)": 0, + "(7496, '2024-11-27', 3)": 0, + "(7496, '2024-11-27', 4)": 0, + "(7496, '2024-11-27', 5)": 1, + "(7496, '2024-11-27', 6)": 0, + "(7496, '2024-11-27', 7)": 0, + "(7496, '2024-11-28', 0)": 0, + "(7496, '2024-11-28', 1)": 0, + "(7496, '2024-11-28', 2)": 0, + "(7496, '2024-11-28', 3)": 0, + "(7496, '2024-11-28', 4)": 0, + "(7496, '2024-11-28', 5)": 1, + "(7496, '2024-11-28', 6)": 0, + "(7496, '2024-11-28', 7)": 0, + "(7496, '2024-11-29', 0)": 0, + "(7496, '2024-11-29', 1)": 0, + "(7496, '2024-11-29', 2)": 0, + "(7496, '2024-11-29', 3)": 0, + "(7496, '2024-11-29', 4)": 0, + "(7496, '2024-11-29', 5)": 1, + "(7496, '2024-11-29', 6)": 0, + "(7496, '2024-11-29', 7)": 0, + "(7496, '2024-11-30', 0)": 0, + "(7496, '2024-11-30', 1)": 0, + "(7496, '2024-11-30', 2)": 0, + "(7496, '2024-11-30', 3)": 0, + "(7496, '2024-11-30', 4)": 0, + "(7496, '2024-11-30', 5)": 0, + "(7496, '2024-11-30', 6)": 0, + "(7496, '2024-11-30', 7)": 0, + "(7603, '2024-11-01', 0)": 1, + "(7603, '2024-11-01', 1)": 0, + "(7603, '2024-11-01', 2)": 0, + "(7603, '2024-11-01', 3)": 0, + "(7603, '2024-11-01', 4)": 0, + "(7603, '2024-11-01', 5)": 0, + "(7603, '2024-11-01', 6)": 0, + "(7603, '2024-11-01', 7)": 0, + "(7603, '2024-11-02', 0)": 0, + "(7603, '2024-11-02', 1)": 1, + "(7603, '2024-11-02', 2)": 0, + "(7603, '2024-11-02', 3)": 0, + "(7603, '2024-11-02', 4)": 0, + "(7603, '2024-11-02', 5)": 0, + "(7603, '2024-11-02', 6)": 0, + "(7603, '2024-11-02', 7)": 0, + "(7603, '2024-11-03', 0)": 0, + "(7603, '2024-11-03', 1)": 1, + "(7603, '2024-11-03', 2)": 0, + "(7603, '2024-11-03', 3)": 0, + "(7603, '2024-11-03', 4)": 0, + "(7603, '2024-11-03', 5)": 0, + "(7603, '2024-11-03', 6)": 0, + "(7603, '2024-11-03', 7)": 0, + "(7603, '2024-11-04', 0)": 0, + "(7603, '2024-11-04', 1)": 1, + "(7603, '2024-11-04', 2)": 0, + "(7603, '2024-11-04', 3)": 0, + "(7603, '2024-11-04', 4)": 0, + "(7603, '2024-11-04', 5)": 0, + "(7603, '2024-11-04', 6)": 0, + "(7603, '2024-11-04', 7)": 0, + "(7603, '2024-11-05', 0)": 0, + "(7603, '2024-11-05', 1)": 0, + "(7603, '2024-11-05', 2)": 0, + "(7603, '2024-11-05', 3)": 0, + "(7603, '2024-11-05', 4)": 0, + "(7603, '2024-11-05', 5)": 0, + "(7603, '2024-11-05', 6)": 0, + "(7603, '2024-11-05', 7)": 0, + "(7603, '2024-11-06', 0)": 0, + "(7603, '2024-11-06', 1)": 0, + "(7603, '2024-11-06', 2)": 1, + "(7603, '2024-11-06', 3)": 0, + "(7603, '2024-11-06', 4)": 0, + "(7603, '2024-11-06', 5)": 0, + "(7603, '2024-11-06', 6)": 0, + "(7603, '2024-11-06', 7)": 0, + "(7603, '2024-11-07', 0)": 0, + "(7603, '2024-11-07', 1)": 0, + "(7603, '2024-11-07', 2)": 1, + "(7603, '2024-11-07', 3)": 0, + "(7603, '2024-11-07', 4)": 0, + "(7603, '2024-11-07', 5)": 0, + "(7603, '2024-11-07', 6)": 0, + "(7603, '2024-11-07', 7)": 0, + "(7603, '2024-11-08', 0)": 0, + "(7603, '2024-11-08', 1)": 0, + "(7603, '2024-11-08', 2)": 1, + "(7603, '2024-11-08', 3)": 0, + "(7603, '2024-11-08', 4)": 0, + "(7603, '2024-11-08', 5)": 0, + "(7603, '2024-11-08', 6)": 0, + "(7603, '2024-11-08', 7)": 0, + "(7603, '2024-11-09', 0)": 0, + "(7603, '2024-11-09', 1)": 1, + "(7603, '2024-11-09', 2)": 0, + "(7603, '2024-11-09', 3)": 0, + "(7603, '2024-11-09', 4)": 0, + "(7603, '2024-11-09', 5)": 0, + "(7603, '2024-11-09', 6)": 0, + "(7603, '2024-11-09', 7)": 0, + "(7603, '2024-11-10', 0)": 0, + "(7603, '2024-11-10', 1)": 0, + "(7603, '2024-11-10', 2)": 0, + "(7603, '2024-11-10', 3)": 0, + "(7603, '2024-11-10', 4)": 0, + "(7603, '2024-11-10', 5)": 0, + "(7603, '2024-11-10', 6)": 0, + "(7603, '2024-11-10', 7)": 0, + "(7603, '2024-11-11', 0)": 0, + "(7603, '2024-11-11', 1)": 0, + "(7603, '2024-11-11', 2)": 0, + "(7603, '2024-11-11', 3)": 0, + "(7603, '2024-11-11', 4)": 0, + "(7603, '2024-11-11', 5)": 0, + "(7603, '2024-11-11', 6)": 0, + "(7603, '2024-11-11', 7)": 0, + "(7603, '2024-11-12', 0)": 1, + "(7603, '2024-11-12', 1)": 0, + "(7603, '2024-11-12', 2)": 0, + "(7603, '2024-11-12', 3)": 0, + "(7603, '2024-11-12', 4)": 0, + "(7603, '2024-11-12', 5)": 0, + "(7603, '2024-11-12', 6)": 0, + "(7603, '2024-11-12', 7)": 0, + "(7603, '2024-11-13', 0)": 0, + "(7603, '2024-11-13', 1)": 0, + "(7603, '2024-11-13', 2)": 0, + "(7603, '2024-11-13', 3)": 0, + "(7603, '2024-11-13', 4)": 0, + "(7603, '2024-11-13', 5)": 0, + "(7603, '2024-11-13', 6)": 0, + "(7603, '2024-11-13', 7)": 0, + "(7603, '2024-11-14', 0)": 0, + "(7603, '2024-11-14', 1)": 0, + "(7603, '2024-11-14', 2)": 0, + "(7603, '2024-11-14', 3)": 0, + "(7603, '2024-11-14', 4)": 0, + "(7603, '2024-11-14', 5)": 0, + "(7603, '2024-11-14', 6)": 0, + "(7603, '2024-11-14', 7)": 0, + "(7603, '2024-11-15', 0)": 0, + "(7603, '2024-11-15', 1)": 0, + "(7603, '2024-11-15', 2)": 1, + "(7603, '2024-11-15', 3)": 0, + "(7603, '2024-11-15', 4)": 0, + "(7603, '2024-11-15', 5)": 0, + "(7603, '2024-11-15', 6)": 0, + "(7603, '2024-11-15', 7)": 0, + "(7603, '2024-11-16', 0)": 0, + "(7603, '2024-11-16', 1)": 0, + "(7603, '2024-11-16', 2)": 0, + "(7603, '2024-11-16', 3)": 0, + "(7603, '2024-11-16', 4)": 0, + "(7603, '2024-11-16', 5)": 0, + "(7603, '2024-11-16', 6)": 0, + "(7603, '2024-11-16', 7)": 0, + "(7603, '2024-11-17', 0)": 0, + "(7603, '2024-11-17', 1)": 1, + "(7603, '2024-11-17', 2)": 0, + "(7603, '2024-11-17', 3)": 0, + "(7603, '2024-11-17', 4)": 0, + "(7603, '2024-11-17', 5)": 0, + "(7603, '2024-11-17', 6)": 0, + "(7603, '2024-11-17', 7)": 0, + "(7603, '2024-11-18', 0)": 1, + "(7603, '2024-11-18', 1)": 0, + "(7603, '2024-11-18', 2)": 0, + "(7603, '2024-11-18', 3)": 0, + "(7603, '2024-11-18', 4)": 0, + "(7603, '2024-11-18', 5)": 0, + "(7603, '2024-11-18', 6)": 0, + "(7603, '2024-11-18', 7)": 0, + "(7603, '2024-11-19', 0)": 0, + "(7603, '2024-11-19', 1)": 1, + "(7603, '2024-11-19', 2)": 0, + "(7603, '2024-11-19', 3)": 0, + "(7603, '2024-11-19', 4)": 0, + "(7603, '2024-11-19', 5)": 0, + "(7603, '2024-11-19', 6)": 0, + "(7603, '2024-11-19', 7)": 0, + "(7603, '2024-11-20', 0)": 0, + "(7603, '2024-11-20', 1)": 0, + "(7603, '2024-11-20', 2)": 1, + "(7603, '2024-11-20', 3)": 0, + "(7603, '2024-11-20', 4)": 0, + "(7603, '2024-11-20', 5)": 0, + "(7603, '2024-11-20', 6)": 0, + "(7603, '2024-11-20', 7)": 0, + "(7603, '2024-11-21', 0)": 0, + "(7603, '2024-11-21', 1)": 0, + "(7603, '2024-11-21', 2)": 1, + "(7603, '2024-11-21', 3)": 0, + "(7603, '2024-11-21', 4)": 0, + "(7603, '2024-11-21', 5)": 0, + "(7603, '2024-11-21', 6)": 0, + "(7603, '2024-11-21', 7)": 0, + "(7603, '2024-11-22', 0)": 0, + "(7603, '2024-11-22', 1)": 0, + "(7603, '2024-11-22', 2)": 1, + "(7603, '2024-11-22', 3)": 0, + "(7603, '2024-11-22', 4)": 0, + "(7603, '2024-11-22', 5)": 0, + "(7603, '2024-11-22', 6)": 0, + "(7603, '2024-11-22', 7)": 0, + "(7603, '2024-11-23', 0)": 0, + "(7603, '2024-11-23', 1)": 0, + "(7603, '2024-11-23', 2)": 1, + "(7603, '2024-11-23', 3)": 0, + "(7603, '2024-11-23', 4)": 0, + "(7603, '2024-11-23', 5)": 0, + "(7603, '2024-11-23', 6)": 0, + "(7603, '2024-11-23', 7)": 0, + "(7603, '2024-11-24', 0)": 0, + "(7603, '2024-11-24', 1)": 0, + "(7603, '2024-11-24', 2)": 0, + "(7603, '2024-11-24', 3)": 0, + "(7603, '2024-11-24', 4)": 0, + "(7603, '2024-11-24', 5)": 0, + "(7603, '2024-11-24', 6)": 0, + "(7603, '2024-11-24', 7)": 0, + "(7603, '2024-11-25', 0)": 1, + "(7603, '2024-11-25', 1)": 0, + "(7603, '2024-11-25', 2)": 0, + "(7603, '2024-11-25', 3)": 0, + "(7603, '2024-11-25', 4)": 0, + "(7603, '2024-11-25', 5)": 0, + "(7603, '2024-11-25', 6)": 0, + "(7603, '2024-11-25', 7)": 0, + "(7603, '2024-11-26', 0)": 0, + "(7603, '2024-11-26', 1)": 0, + "(7603, '2024-11-26', 2)": 0, + "(7603, '2024-11-26', 3)": 0, + "(7603, '2024-11-26', 4)": 0, + "(7603, '2024-11-26', 5)": 0, + "(7603, '2024-11-26', 6)": 0, + "(7603, '2024-11-26', 7)": 0, + "(7603, '2024-11-27', 0)": 0, + "(7603, '2024-11-27', 1)": 1, + "(7603, '2024-11-27', 2)": 0, + "(7603, '2024-11-27', 3)": 0, + "(7603, '2024-11-27', 4)": 0, + "(7603, '2024-11-27', 5)": 0, + "(7603, '2024-11-27', 6)": 0, + "(7603, '2024-11-27', 7)": 0, + "(7603, '2024-11-28', 0)": 0, + "(7603, '2024-11-28', 1)": 0, + "(7603, '2024-11-28', 2)": 0, + "(7603, '2024-11-28', 3)": 0, + "(7603, '2024-11-28', 4)": 0, + "(7603, '2024-11-28', 5)": 0, + "(7603, '2024-11-28', 6)": 0, + "(7603, '2024-11-28', 7)": 0, + "(7603, '2024-11-29', 0)": 1, + "(7603, '2024-11-29', 1)": 0, + "(7603, '2024-11-29', 2)": 0, + "(7603, '2024-11-29', 3)": 0, + "(7603, '2024-11-29', 4)": 0, + "(7603, '2024-11-29', 5)": 0, + "(7603, '2024-11-29', 6)": 0, + "(7603, '2024-11-29', 7)": 0, + "(7603, '2024-11-30', 0)": 0, + "(7603, '2024-11-30', 1)": 0, + "(7603, '2024-11-30', 2)": 0, + "(7603, '2024-11-30', 3)": 0, + "(7603, '2024-11-30', 4)": 0, + "(7603, '2024-11-30', 5)": 0, + "(7603, '2024-11-30', 6)": 0, + "(7603, '2024-11-30', 7)": 0, + "(7741, '2024-11-01', 0)": 0, + "(7741, '2024-11-01', 1)": 0, + "(7741, '2024-11-01', 2)": 0, + "(7741, '2024-11-01', 3)": 0, + "(7741, '2024-11-01', 4)": 0, + "(7741, '2024-11-01', 5)": 0, + "(7741, '2024-11-01', 6)": 0, + "(7741, '2024-11-01', 7)": 0, + "(7741, '2024-11-02', 0)": 0, + "(7741, '2024-11-02', 1)": 0, + "(7741, '2024-11-02', 2)": 0, + "(7741, '2024-11-02', 3)": 0, + "(7741, '2024-11-02', 4)": 0, + "(7741, '2024-11-02', 5)": 0, + "(7741, '2024-11-02', 6)": 0, + "(7741, '2024-11-02', 7)": 0, + "(7741, '2024-11-03', 0)": 0, + "(7741, '2024-11-03', 1)": 0, + "(7741, '2024-11-03', 2)": 0, + "(7741, '2024-11-03', 3)": 0, + "(7741, '2024-11-03', 4)": 0, + "(7741, '2024-11-03', 5)": 0, + "(7741, '2024-11-03', 6)": 0, + "(7741, '2024-11-03', 7)": 0, + "(7741, '2024-11-04', 0)": 0, + "(7741, '2024-11-04', 1)": 0, + "(7741, '2024-11-04', 2)": 0, + "(7741, '2024-11-04', 3)": 0, + "(7741, '2024-11-04', 4)": 0, + "(7741, '2024-11-04', 5)": 0, + "(7741, '2024-11-04', 6)": 0, + "(7741, '2024-11-04', 7)": 0, + "(7741, '2024-11-05', 0)": 0, + "(7741, '2024-11-05', 1)": 0, + "(7741, '2024-11-05', 2)": 0, + "(7741, '2024-11-05', 3)": 0, + "(7741, '2024-11-05', 4)": 0, + "(7741, '2024-11-05', 5)": 0, + "(7741, '2024-11-05', 6)": 0, + "(7741, '2024-11-05', 7)": 0, + "(7741, '2024-11-06', 0)": 0, + "(7741, '2024-11-06', 1)": 0, + "(7741, '2024-11-06', 2)": 0, + "(7741, '2024-11-06', 3)": 0, + "(7741, '2024-11-06', 4)": 0, + "(7741, '2024-11-06', 5)": 0, + "(7741, '2024-11-06', 6)": 0, + "(7741, '2024-11-06', 7)": 0, + "(7741, '2024-11-07', 0)": 0, + "(7741, '2024-11-07', 1)": 0, + "(7741, '2024-11-07', 2)": 0, + "(7741, '2024-11-07', 3)": 0, + "(7741, '2024-11-07', 4)": 0, + "(7741, '2024-11-07', 5)": 0, + "(7741, '2024-11-07', 6)": 0, + "(7741, '2024-11-07', 7)": 0, + "(7741, '2024-11-08', 0)": 0, + "(7741, '2024-11-08', 1)": 0, + "(7741, '2024-11-08', 2)": 0, + "(7741, '2024-11-08', 3)": 0, + "(7741, '2024-11-08', 4)": 0, + "(7741, '2024-11-08', 5)": 0, + "(7741, '2024-11-08', 6)": 0, + "(7741, '2024-11-08', 7)": 0, + "(7741, '2024-11-09', 0)": 0, + "(7741, '2024-11-09', 1)": 0, + "(7741, '2024-11-09', 2)": 0, + "(7741, '2024-11-09', 3)": 0, + "(7741, '2024-11-09', 4)": 0, + "(7741, '2024-11-09', 5)": 0, + "(7741, '2024-11-09', 6)": 0, + "(7741, '2024-11-09', 7)": 0, + "(7741, '2024-11-10', 0)": 0, + "(7741, '2024-11-10', 1)": 0, + "(7741, '2024-11-10', 2)": 0, + "(7741, '2024-11-10', 3)": 0, + "(7741, '2024-11-10', 4)": 0, + "(7741, '2024-11-10', 5)": 0, + "(7741, '2024-11-10', 6)": 0, + "(7741, '2024-11-10', 7)": 0, + "(7741, '2024-11-11', 0)": 0, + "(7741, '2024-11-11', 1)": 0, + "(7741, '2024-11-11', 2)": 0, + "(7741, '2024-11-11', 3)": 0, + "(7741, '2024-11-11', 4)": 0, + "(7741, '2024-11-11', 5)": 0, + "(7741, '2024-11-11', 6)": 0, + "(7741, '2024-11-11', 7)": 0, + "(7741, '2024-11-12', 0)": 0, + "(7741, '2024-11-12', 1)": 0, + "(7741, '2024-11-12', 2)": 0, + "(7741, '2024-11-12', 3)": 0, + "(7741, '2024-11-12', 4)": 0, + "(7741, '2024-11-12', 5)": 0, + "(7741, '2024-11-12', 6)": 0, + "(7741, '2024-11-12', 7)": 0, + "(7741, '2024-11-13', 0)": 0, + "(7741, '2024-11-13', 1)": 0, + "(7741, '2024-11-13', 2)": 0, + "(7741, '2024-11-13', 3)": 0, + "(7741, '2024-11-13', 4)": 0, + "(7741, '2024-11-13', 5)": 0, + "(7741, '2024-11-13', 6)": 0, + "(7741, '2024-11-13', 7)": 0, + "(7741, '2024-11-14', 0)": 0, + "(7741, '2024-11-14', 1)": 0, + "(7741, '2024-11-14', 2)": 0, + "(7741, '2024-11-14', 3)": 0, + "(7741, '2024-11-14', 4)": 0, + "(7741, '2024-11-14', 5)": 0, + "(7741, '2024-11-14', 6)": 0, + "(7741, '2024-11-14', 7)": 0, + "(7741, '2024-11-15', 0)": 0, + "(7741, '2024-11-15', 1)": 0, + "(7741, '2024-11-15', 2)": 0, + "(7741, '2024-11-15', 3)": 0, + "(7741, '2024-11-15', 4)": 0, + "(7741, '2024-11-15', 5)": 0, + "(7741, '2024-11-15', 6)": 0, + "(7741, '2024-11-15', 7)": 0, + "(7741, '2024-11-16', 0)": 0, + "(7741, '2024-11-16', 1)": 0, + "(7741, '2024-11-16', 2)": 0, + "(7741, '2024-11-16', 3)": 0, + "(7741, '2024-11-16', 4)": 0, + "(7741, '2024-11-16', 5)": 0, + "(7741, '2024-11-16', 6)": 0, + "(7741, '2024-11-16', 7)": 0, + "(7741, '2024-11-17', 0)": 0, + "(7741, '2024-11-17', 1)": 0, + "(7741, '2024-11-17', 2)": 0, + "(7741, '2024-11-17', 3)": 0, + "(7741, '2024-11-17', 4)": 0, + "(7741, '2024-11-17', 5)": 0, + "(7741, '2024-11-17', 6)": 0, + "(7741, '2024-11-17', 7)": 0, + "(7741, '2024-11-18', 0)": 0, + "(7741, '2024-11-18', 1)": 0, + "(7741, '2024-11-18', 2)": 0, + "(7741, '2024-11-18', 3)": 0, + "(7741, '2024-11-18', 4)": 0, + "(7741, '2024-11-18', 5)": 0, + "(7741, '2024-11-18', 6)": 0, + "(7741, '2024-11-18', 7)": 0, + "(7741, '2024-11-19', 0)": 0, + "(7741, '2024-11-19', 1)": 0, + "(7741, '2024-11-19', 2)": 0, + "(7741, '2024-11-19', 3)": 0, + "(7741, '2024-11-19', 4)": 0, + "(7741, '2024-11-19', 5)": 0, + "(7741, '2024-11-19', 6)": 0, + "(7741, '2024-11-19', 7)": 0, + "(7741, '2024-11-20', 0)": 0, + "(7741, '2024-11-20', 1)": 0, + "(7741, '2024-11-20', 2)": 0, + "(7741, '2024-11-20', 3)": 0, + "(7741, '2024-11-20', 4)": 0, + "(7741, '2024-11-20', 5)": 0, + "(7741, '2024-11-20', 6)": 0, + "(7741, '2024-11-20', 7)": 0, + "(7741, '2024-11-21', 0)": 0, + "(7741, '2024-11-21', 1)": 0, + "(7741, '2024-11-21', 2)": 0, + "(7741, '2024-11-21', 3)": 0, + "(7741, '2024-11-21', 4)": 0, + "(7741, '2024-11-21', 5)": 0, + "(7741, '2024-11-21', 6)": 0, + "(7741, '2024-11-21', 7)": 0, + "(7741, '2024-11-22', 0)": 0, + "(7741, '2024-11-22', 1)": 0, + "(7741, '2024-11-22', 2)": 0, + "(7741, '2024-11-22', 3)": 0, + "(7741, '2024-11-22', 4)": 0, + "(7741, '2024-11-22', 5)": 0, + "(7741, '2024-11-22', 6)": 0, + "(7741, '2024-11-22', 7)": 0, + "(7741, '2024-11-23', 0)": 0, + "(7741, '2024-11-23', 1)": 0, + "(7741, '2024-11-23', 2)": 0, + "(7741, '2024-11-23', 3)": 0, + "(7741, '2024-11-23', 4)": 0, + "(7741, '2024-11-23', 5)": 0, + "(7741, '2024-11-23', 6)": 0, + "(7741, '2024-11-23', 7)": 0, + "(7741, '2024-11-24', 0)": 0, + "(7741, '2024-11-24', 1)": 0, + "(7741, '2024-11-24', 2)": 0, + "(7741, '2024-11-24', 3)": 0, + "(7741, '2024-11-24', 4)": 0, + "(7741, '2024-11-24', 5)": 0, + "(7741, '2024-11-24', 6)": 0, + "(7741, '2024-11-24', 7)": 0, + "(7741, '2024-11-25', 0)": 0, + "(7741, '2024-11-25', 1)": 0, + "(7741, '2024-11-25', 2)": 0, + "(7741, '2024-11-25', 3)": 0, + "(7741, '2024-11-25', 4)": 0, + "(7741, '2024-11-25', 5)": 0, + "(7741, '2024-11-25', 6)": 0, + "(7741, '2024-11-25', 7)": 0, + "(7741, '2024-11-26', 0)": 0, + "(7741, '2024-11-26', 1)": 0, + "(7741, '2024-11-26', 2)": 0, + "(7741, '2024-11-26', 3)": 0, + "(7741, '2024-11-26', 4)": 0, + "(7741, '2024-11-26', 5)": 0, + "(7741, '2024-11-26', 6)": 0, + "(7741, '2024-11-26', 7)": 0, + "(7741, '2024-11-27', 0)": 0, + "(7741, '2024-11-27', 1)": 0, + "(7741, '2024-11-27', 2)": 0, + "(7741, '2024-11-27', 3)": 0, + "(7741, '2024-11-27', 4)": 0, + "(7741, '2024-11-27', 5)": 0, + "(7741, '2024-11-27', 6)": 1, + "(7741, '2024-11-27', 7)": 0, + "(7741, '2024-11-28', 0)": 0, + "(7741, '2024-11-28', 1)": 0, + "(7741, '2024-11-28', 2)": 0, + "(7741, '2024-11-28', 3)": 0, + "(7741, '2024-11-28', 4)": 0, + "(7741, '2024-11-28', 5)": 0, + "(7741, '2024-11-28', 6)": 1, + "(7741, '2024-11-28', 7)": 0, + "(7741, '2024-11-29', 0)": 0, + "(7741, '2024-11-29', 1)": 0, + "(7741, '2024-11-29', 2)": 0, + "(7741, '2024-11-29', 3)": 0, + "(7741, '2024-11-29', 4)": 0, + "(7741, '2024-11-29', 5)": 0, + "(7741, '2024-11-29', 6)": 0, + "(7741, '2024-11-29', 7)": 0, + "(7741, '2024-11-30', 0)": 0, + "(7741, '2024-11-30', 1)": 0, + "(7741, '2024-11-30', 2)": 0, + "(7741, '2024-11-30', 3)": 0, + "(7741, '2024-11-30', 4)": 0, + "(7741, '2024-11-30', 5)": 0, + "(7741, '2024-11-30', 6)": 0, + "(7741, '2024-11-30', 7)": 0, + "(7752, '2024-11-01', 0)": 0, + "(7752, '2024-11-01', 1)": 0, + "(7752, '2024-11-01', 2)": 1, + "(7752, '2024-11-01', 3)": 0, + "(7752, '2024-11-01', 4)": 0, + "(7752, '2024-11-01', 5)": 0, + "(7752, '2024-11-01', 6)": 0, + "(7752, '2024-11-01', 7)": 0, + "(7752, '2024-11-02', 0)": 0, + "(7752, '2024-11-02', 1)": 0, + "(7752, '2024-11-02', 2)": 0, + "(7752, '2024-11-02', 3)": 1, + "(7752, '2024-11-02', 4)": 0, + "(7752, '2024-11-02', 5)": 0, + "(7752, '2024-11-02', 6)": 0, + "(7752, '2024-11-02', 7)": 0, + "(7752, '2024-11-03', 0)": 0, + "(7752, '2024-11-03', 1)": 0, + "(7752, '2024-11-03', 2)": 0, + "(7752, '2024-11-03', 3)": 0, + "(7752, '2024-11-03', 4)": 0, + "(7752, '2024-11-03', 5)": 0, + "(7752, '2024-11-03', 6)": 0, + "(7752, '2024-11-03', 7)": 0, + "(7752, '2024-11-04', 0)": 0, + "(7752, '2024-11-04', 1)": 0, + "(7752, '2024-11-04', 2)": 0, + "(7752, '2024-11-04', 3)": 0, + "(7752, '2024-11-04', 4)": 0, + "(7752, '2024-11-04', 5)": 0, + "(7752, '2024-11-04', 6)": 0, + "(7752, '2024-11-04', 7)": 0, + "(7752, '2024-11-05', 0)": 0, + "(7752, '2024-11-05', 1)": 0, + "(7752, '2024-11-05', 2)": 0, + "(7752, '2024-11-05', 3)": 0, + "(7752, '2024-11-05', 4)": 0, + "(7752, '2024-11-05', 5)": 0, + "(7752, '2024-11-05', 6)": 1, + "(7752, '2024-11-05', 7)": 0, + "(7752, '2024-11-06', 0)": 0, + "(7752, '2024-11-06', 1)": 0, + "(7752, '2024-11-06', 2)": 0, + "(7752, '2024-11-06', 3)": 0, + "(7752, '2024-11-06', 4)": 0, + "(7752, '2024-11-06', 5)": 0, + "(7752, '2024-11-06', 6)": 1, + "(7752, '2024-11-06', 7)": 0, + "(7752, '2024-11-07', 0)": 0, + "(7752, '2024-11-07', 1)": 0, + "(7752, '2024-11-07', 2)": 0, + "(7752, '2024-11-07', 3)": 0, + "(7752, '2024-11-07', 4)": 0, + "(7752, '2024-11-07', 5)": 0, + "(7752, '2024-11-07', 6)": 1, + "(7752, '2024-11-07', 7)": 0, + "(7752, '2024-11-08', 0)": 0, + "(7752, '2024-11-08', 1)": 0, + "(7752, '2024-11-08', 2)": 0, + "(7752, '2024-11-08', 3)": 0, + "(7752, '2024-11-08', 4)": 0, + "(7752, '2024-11-08', 5)": 0, + "(7752, '2024-11-08', 6)": 0, + "(7752, '2024-11-08', 7)": 0, + "(7752, '2024-11-09', 0)": 0, + "(7752, '2024-11-09', 1)": 0, + "(7752, '2024-11-09', 2)": 0, + "(7752, '2024-11-09', 3)": 0, + "(7752, '2024-11-09', 4)": 0, + "(7752, '2024-11-09', 5)": 0, + "(7752, '2024-11-09', 6)": 0, + "(7752, '2024-11-09', 7)": 0, + "(7752, '2024-11-10', 0)": 0, + "(7752, '2024-11-10', 1)": 0, + "(7752, '2024-11-10', 2)": 0, + "(7752, '2024-11-10', 3)": 0, + "(7752, '2024-11-10', 4)": 0, + "(7752, '2024-11-10', 5)": 0, + "(7752, '2024-11-10', 6)": 0, + "(7752, '2024-11-10', 7)": 0, + "(7752, '2024-11-11', 0)": 1, + "(7752, '2024-11-11', 1)": 0, + "(7752, '2024-11-11', 2)": 0, + "(7752, '2024-11-11', 3)": 0, + "(7752, '2024-11-11', 4)": 0, + "(7752, '2024-11-11', 5)": 0, + "(7752, '2024-11-11', 6)": 0, + "(7752, '2024-11-11', 7)": 0, + "(7752, '2024-11-12', 0)": 0, + "(7752, '2024-11-12', 1)": 0, + "(7752, '2024-11-12', 2)": 0, + "(7752, '2024-11-12', 3)": 0, + "(7752, '2024-11-12', 4)": 0, + "(7752, '2024-11-12', 5)": 0, + "(7752, '2024-11-12', 6)": 0, + "(7752, '2024-11-12', 7)": 0, + "(7752, '2024-11-13', 0)": 0, + "(7752, '2024-11-13', 1)": 0, + "(7752, '2024-11-13', 2)": 0, + "(7752, '2024-11-13', 3)": 0, + "(7752, '2024-11-13', 4)": 0, + "(7752, '2024-11-13', 5)": 0, + "(7752, '2024-11-13', 6)": 0, + "(7752, '2024-11-13', 7)": 0, + "(7752, '2024-11-14', 0)": 0, + "(7752, '2024-11-14', 1)": 0, + "(7752, '2024-11-14', 2)": 0, + "(7752, '2024-11-14', 3)": 0, + "(7752, '2024-11-14', 4)": 0, + "(7752, '2024-11-14', 5)": 0, + "(7752, '2024-11-14', 6)": 0, + "(7752, '2024-11-14', 7)": 0, + "(7752, '2024-11-15', 0)": 1, + "(7752, '2024-11-15', 1)": 0, + "(7752, '2024-11-15', 2)": 0, + "(7752, '2024-11-15', 3)": 0, + "(7752, '2024-11-15', 4)": 0, + "(7752, '2024-11-15', 5)": 0, + "(7752, '2024-11-15', 6)": 0, + "(7752, '2024-11-15', 7)": 0, + "(7752, '2024-11-16', 0)": 0, + "(7752, '2024-11-16', 1)": 0, + "(7752, '2024-11-16', 2)": 0, + "(7752, '2024-11-16', 3)": 0, + "(7752, '2024-11-16', 4)": 0, + "(7752, '2024-11-16', 5)": 0, + "(7752, '2024-11-16', 6)": 0, + "(7752, '2024-11-16', 7)": 0, + "(7752, '2024-11-17', 0)": 0, + "(7752, '2024-11-17', 1)": 0, + "(7752, '2024-11-17', 2)": 0, + "(7752, '2024-11-17', 3)": 0, + "(7752, '2024-11-17', 4)": 0, + "(7752, '2024-11-17', 5)": 0, + "(7752, '2024-11-17', 6)": 0, + "(7752, '2024-11-17', 7)": 0, + "(7752, '2024-11-18', 0)": 1, + "(7752, '2024-11-18', 1)": 0, + "(7752, '2024-11-18', 2)": 0, + "(7752, '2024-11-18', 3)": 0, + "(7752, '2024-11-18', 4)": 0, + "(7752, '2024-11-18', 5)": 0, + "(7752, '2024-11-18', 6)": 0, + "(7752, '2024-11-18', 7)": 0, + "(7752, '2024-11-19', 0)": 0, + "(7752, '2024-11-19', 1)": 0, + "(7752, '2024-11-19', 2)": 0, + "(7752, '2024-11-19', 3)": 0, + "(7752, '2024-11-19', 4)": 0, + "(7752, '2024-11-19', 5)": 0, + "(7752, '2024-11-19', 6)": 0, + "(7752, '2024-11-19', 7)": 0, + "(7752, '2024-11-20', 0)": 0, + "(7752, '2024-11-20', 1)": 0, + "(7752, '2024-11-20', 2)": 1, + "(7752, '2024-11-20', 3)": 0, + "(7752, '2024-11-20', 4)": 0, + "(7752, '2024-11-20', 5)": 0, + "(7752, '2024-11-20', 6)": 0, + "(7752, '2024-11-20', 7)": 0, + "(7752, '2024-11-21', 0)": 0, + "(7752, '2024-11-21', 1)": 0, + "(7752, '2024-11-21', 2)": 1, + "(7752, '2024-11-21', 3)": 0, + "(7752, '2024-11-21', 4)": 0, + "(7752, '2024-11-21', 5)": 0, + "(7752, '2024-11-21', 6)": 0, + "(7752, '2024-11-21', 7)": 0, + "(7752, '2024-11-22', 0)": 0, + "(7752, '2024-11-22', 1)": 0, + "(7752, '2024-11-22', 2)": 1, + "(7752, '2024-11-22', 3)": 0, + "(7752, '2024-11-22', 4)": 0, + "(7752, '2024-11-22', 5)": 0, + "(7752, '2024-11-22', 6)": 0, + "(7752, '2024-11-22', 7)": 0, + "(7752, '2024-11-23', 0)": 0, + "(7752, '2024-11-23', 1)": 0, + "(7752, '2024-11-23', 2)": 0, + "(7752, '2024-11-23', 3)": 0, + "(7752, '2024-11-23', 4)": 0, + "(7752, '2024-11-23', 5)": 0, + "(7752, '2024-11-23', 6)": 0, + "(7752, '2024-11-23', 7)": 0, + "(7752, '2024-11-24', 0)": 0, + "(7752, '2024-11-24', 1)": 0, + "(7752, '2024-11-24', 2)": 0, + "(7752, '2024-11-24', 3)": 1, + "(7752, '2024-11-24', 4)": 0, + "(7752, '2024-11-24', 5)": 0, + "(7752, '2024-11-24', 6)": 0, + "(7752, '2024-11-24', 7)": 0, + "(7752, '2024-11-25', 0)": 0, + "(7752, '2024-11-25', 1)": 0, + "(7752, '2024-11-25', 2)": 0, + "(7752, '2024-11-25', 3)": 0, + "(7752, '2024-11-25', 4)": 0, + "(7752, '2024-11-25', 5)": 0, + "(7752, '2024-11-25', 6)": 0, + "(7752, '2024-11-25', 7)": 0, + "(7752, '2024-11-26', 0)": 1, + "(7752, '2024-11-26', 1)": 0, + "(7752, '2024-11-26', 2)": 0, + "(7752, '2024-11-26', 3)": 0, + "(7752, '2024-11-26', 4)": 0, + "(7752, '2024-11-26', 5)": 0, + "(7752, '2024-11-26', 6)": 0, + "(7752, '2024-11-26', 7)": 0, + "(7752, '2024-11-27', 0)": 0, + "(7752, '2024-11-27', 1)": 0, + "(7752, '2024-11-27', 2)": 1, + "(7752, '2024-11-27', 3)": 0, + "(7752, '2024-11-27', 4)": 0, + "(7752, '2024-11-27', 5)": 0, + "(7752, '2024-11-27', 6)": 0, + "(7752, '2024-11-27', 7)": 0, + "(7752, '2024-11-28', 0)": 0, + "(7752, '2024-11-28', 1)": 0, + "(7752, '2024-11-28', 2)": 0, + "(7752, '2024-11-28', 3)": 0, + "(7752, '2024-11-28', 4)": 0, + "(7752, '2024-11-28', 5)": 0, + "(7752, '2024-11-28', 6)": 0, + "(7752, '2024-11-28', 7)": 0, + "(7752, '2024-11-29', 0)": 0, + "(7752, '2024-11-29', 1)": 0, + "(7752, '2024-11-29', 2)": 0, + "(7752, '2024-11-29', 3)": 0, + "(7752, '2024-11-29', 4)": 0, + "(7752, '2024-11-29', 5)": 0, + "(7752, '2024-11-29', 6)": 0, + "(7752, '2024-11-29', 7)": 0, + "(7752, '2024-11-30', 0)": 0, + "(7752, '2024-11-30', 1)": 0, + "(7752, '2024-11-30', 2)": 1, + "(7752, '2024-11-30', 3)": 0, + "(7752, '2024-11-30', 4)": 0, + "(7752, '2024-11-30', 5)": 0, + "(7752, '2024-11-30', 6)": 0, + "(7752, '2024-11-30', 7)": 0, + "(7770, '2024-11-01', 0)": 0, + "(7770, '2024-11-01', 1)": 0, + "(7770, '2024-11-01', 2)": 0, + "(7770, '2024-11-01', 3)": 0, + "(7770, '2024-11-01', 4)": 0, + "(7770, '2024-11-01', 5)": 0, + "(7770, '2024-11-01', 6)": 0, + "(7770, '2024-11-01', 7)": 0, + "(7770, '2024-11-02', 0)": 0, + "(7770, '2024-11-02', 1)": 0, + "(7770, '2024-11-02', 2)": 0, + "(7770, '2024-11-02', 3)": 0, + "(7770, '2024-11-02', 4)": 0, + "(7770, '2024-11-02', 5)": 0, + "(7770, '2024-11-02', 6)": 0, + "(7770, '2024-11-02', 7)": 0, + "(7770, '2024-11-03', 0)": 0, + "(7770, '2024-11-03', 1)": 0, + "(7770, '2024-11-03', 2)": 0, + "(7770, '2024-11-03', 3)": 0, + "(7770, '2024-11-03', 4)": 0, + "(7770, '2024-11-03', 5)": 0, + "(7770, '2024-11-03', 6)": 0, + "(7770, '2024-11-03', 7)": 0, + "(7770, '2024-11-04', 0)": 0, + "(7770, '2024-11-04', 1)": 0, + "(7770, '2024-11-04', 2)": 0, + "(7770, '2024-11-04', 3)": 0, + "(7770, '2024-11-04', 4)": 0, + "(7770, '2024-11-04', 5)": 0, + "(7770, '2024-11-04', 6)": 0, + "(7770, '2024-11-04', 7)": 0, + "(7770, '2024-11-05', 0)": 0, + "(7770, '2024-11-05', 1)": 0, + "(7770, '2024-11-05', 2)": 0, + "(7770, '2024-11-05', 3)": 0, + "(7770, '2024-11-05', 4)": 0, + "(7770, '2024-11-05', 5)": 0, + "(7770, '2024-11-05', 6)": 0, + "(7770, '2024-11-05', 7)": 0, + "(7770, '2024-11-06', 0)": 0, + "(7770, '2024-11-06', 1)": 0, + "(7770, '2024-11-06', 2)": 0, + "(7770, '2024-11-06', 3)": 0, + "(7770, '2024-11-06', 4)": 0, + "(7770, '2024-11-06', 5)": 0, + "(7770, '2024-11-06', 6)": 0, + "(7770, '2024-11-06', 7)": 0, + "(7770, '2024-11-07', 0)": 0, + "(7770, '2024-11-07', 1)": 1, + "(7770, '2024-11-07', 2)": 0, + "(7770, '2024-11-07', 3)": 0, + "(7770, '2024-11-07', 4)": 0, + "(7770, '2024-11-07', 5)": 0, + "(7770, '2024-11-07', 6)": 0, + "(7770, '2024-11-07', 7)": 0, + "(7770, '2024-11-08', 0)": 0, + "(7770, '2024-11-08', 1)": 0, + "(7770, '2024-11-08', 2)": 0, + "(7770, '2024-11-08', 3)": 0, + "(7770, '2024-11-08', 4)": 0, + "(7770, '2024-11-08', 5)": 0, + "(7770, '2024-11-08', 6)": 0, + "(7770, '2024-11-08', 7)": 0, + "(7770, '2024-11-09', 0)": 0, + "(7770, '2024-11-09', 1)": 0, + "(7770, '2024-11-09', 2)": 0, + "(7770, '2024-11-09', 3)": 0, + "(7770, '2024-11-09', 4)": 0, + "(7770, '2024-11-09', 5)": 0, + "(7770, '2024-11-09', 6)": 0, + "(7770, '2024-11-09', 7)": 0, + "(7770, '2024-11-10', 0)": 0, + "(7770, '2024-11-10', 1)": 0, + "(7770, '2024-11-10', 2)": 0, + "(7770, '2024-11-10', 3)": 0, + "(7770, '2024-11-10', 4)": 0, + "(7770, '2024-11-10', 5)": 0, + "(7770, '2024-11-10', 6)": 0, + "(7770, '2024-11-10', 7)": 0, + "(7770, '2024-11-11', 0)": 0, + "(7770, '2024-11-11', 1)": 1, + "(7770, '2024-11-11', 2)": 0, + "(7770, '2024-11-11', 3)": 0, + "(7770, '2024-11-11', 4)": 0, + "(7770, '2024-11-11', 5)": 0, + "(7770, '2024-11-11', 6)": 0, + "(7770, '2024-11-11', 7)": 0, + "(7770, '2024-11-12', 0)": 0, + "(7770, '2024-11-12', 1)": 0, + "(7770, '2024-11-12', 2)": 0, + "(7770, '2024-11-12', 3)": 0, + "(7770, '2024-11-12', 4)": 0, + "(7770, '2024-11-12', 5)": 0, + "(7770, '2024-11-12', 6)": 0, + "(7770, '2024-11-12', 7)": 0, + "(7770, '2024-11-13', 0)": 0, + "(7770, '2024-11-13', 1)": 0, + "(7770, '2024-11-13', 2)": 0, + "(7770, '2024-11-13', 3)": 0, + "(7770, '2024-11-13', 4)": 0, + "(7770, '2024-11-13', 5)": 0, + "(7770, '2024-11-13', 6)": 0, + "(7770, '2024-11-13', 7)": 0, + "(7770, '2024-11-14', 0)": 0, + "(7770, '2024-11-14', 1)": 0, + "(7770, '2024-11-14', 2)": 0, + "(7770, '2024-11-14', 3)": 0, + "(7770, '2024-11-14', 4)": 0, + "(7770, '2024-11-14', 5)": 0, + "(7770, '2024-11-14', 6)": 0, + "(7770, '2024-11-14', 7)": 0, + "(7770, '2024-11-15', 0)": 0, + "(7770, '2024-11-15', 1)": 0, + "(7770, '2024-11-15', 2)": 0, + "(7770, '2024-11-15', 3)": 0, + "(7770, '2024-11-15', 4)": 0, + "(7770, '2024-11-15', 5)": 0, + "(7770, '2024-11-15', 6)": 0, + "(7770, '2024-11-15', 7)": 0, + "(7770, '2024-11-16', 0)": 0, + "(7770, '2024-11-16', 1)": 0, + "(7770, '2024-11-16', 2)": 0, + "(7770, '2024-11-16', 3)": 0, + "(7770, '2024-11-16', 4)": 0, + "(7770, '2024-11-16', 5)": 0, + "(7770, '2024-11-16', 6)": 0, + "(7770, '2024-11-16', 7)": 0, + "(7770, '2024-11-17', 0)": 0, + "(7770, '2024-11-17', 1)": 0, + "(7770, '2024-11-17', 2)": 0, + "(7770, '2024-11-17', 3)": 0, + "(7770, '2024-11-17', 4)": 0, + "(7770, '2024-11-17', 5)": 0, + "(7770, '2024-11-17', 6)": 0, + "(7770, '2024-11-17', 7)": 0, + "(7770, '2024-11-18', 0)": 1, + "(7770, '2024-11-18', 1)": 0, + "(7770, '2024-11-18', 2)": 0, + "(7770, '2024-11-18', 3)": 0, + "(7770, '2024-11-18', 4)": 0, + "(7770, '2024-11-18', 5)": 0, + "(7770, '2024-11-18', 6)": 0, + "(7770, '2024-11-18', 7)": 0, + "(7770, '2024-11-19', 0)": 0, + "(7770, '2024-11-19', 1)": 0, + "(7770, '2024-11-19', 2)": 0, + "(7770, '2024-11-19', 3)": 0, + "(7770, '2024-11-19', 4)": 0, + "(7770, '2024-11-19', 5)": 0, + "(7770, '2024-11-19', 6)": 0, + "(7770, '2024-11-19', 7)": 0, + "(7770, '2024-11-20', 0)": 0, + "(7770, '2024-11-20', 1)": 0, + "(7770, '2024-11-20', 2)": 0, + "(7770, '2024-11-20', 3)": 0, + "(7770, '2024-11-20', 4)": 0, + "(7770, '2024-11-20', 5)": 0, + "(7770, '2024-11-20', 6)": 0, + "(7770, '2024-11-20', 7)": 0, + "(7770, '2024-11-21', 0)": 0, + "(7770, '2024-11-21', 1)": 1, + "(7770, '2024-11-21', 2)": 0, + "(7770, '2024-11-21', 3)": 0, + "(7770, '2024-11-21', 4)": 0, + "(7770, '2024-11-21', 5)": 0, + "(7770, '2024-11-21', 6)": 0, + "(7770, '2024-11-21', 7)": 0, + "(7770, '2024-11-22', 0)": 0, + "(7770, '2024-11-22', 1)": 0, + "(7770, '2024-11-22', 2)": 0, + "(7770, '2024-11-22', 3)": 0, + "(7770, '2024-11-22', 4)": 0, + "(7770, '2024-11-22', 5)": 0, + "(7770, '2024-11-22', 6)": 0, + "(7770, '2024-11-22', 7)": 0, + "(7770, '2024-11-23', 0)": 1, + "(7770, '2024-11-23', 1)": 0, + "(7770, '2024-11-23', 2)": 0, + "(7770, '2024-11-23', 3)": 0, + "(7770, '2024-11-23', 4)": 0, + "(7770, '2024-11-23', 5)": 0, + "(7770, '2024-11-23', 6)": 0, + "(7770, '2024-11-23', 7)": 0, + "(7770, '2024-11-24', 0)": 0, + "(7770, '2024-11-24', 1)": 1, + "(7770, '2024-11-24', 2)": 0, + "(7770, '2024-11-24', 3)": 0, + "(7770, '2024-11-24', 4)": 0, + "(7770, '2024-11-24', 5)": 0, + "(7770, '2024-11-24', 6)": 0, + "(7770, '2024-11-24', 7)": 0, + "(7770, '2024-11-25', 0)": 0, + "(7770, '2024-11-25', 1)": 0, + "(7770, '2024-11-25', 2)": 0, + "(7770, '2024-11-25', 3)": 0, + "(7770, '2024-11-25', 4)": 0, + "(7770, '2024-11-25', 5)": 0, + "(7770, '2024-11-25', 6)": 0, + "(7770, '2024-11-25', 7)": 0, + "(7770, '2024-11-26', 0)": 0, + "(7770, '2024-11-26', 1)": 0, + "(7770, '2024-11-26', 2)": 1, + "(7770, '2024-11-26', 3)": 0, + "(7770, '2024-11-26', 4)": 0, + "(7770, '2024-11-26', 5)": 0, + "(7770, '2024-11-26', 6)": 0, + "(7770, '2024-11-26', 7)": 0, + "(7770, '2024-11-27', 0)": 0, + "(7770, '2024-11-27', 1)": 0, + "(7770, '2024-11-27', 2)": 1, + "(7770, '2024-11-27', 3)": 0, + "(7770, '2024-11-27', 4)": 0, + "(7770, '2024-11-27', 5)": 0, + "(7770, '2024-11-27', 6)": 0, + "(7770, '2024-11-27', 7)": 0, + "(7770, '2024-11-28', 0)": 0, + "(7770, '2024-11-28', 1)": 0, + "(7770, '2024-11-28', 2)": 0, + "(7770, '2024-11-28', 3)": 0, + "(7770, '2024-11-28', 4)": 0, + "(7770, '2024-11-28', 5)": 0, + "(7770, '2024-11-28', 6)": 0, + "(7770, '2024-11-28', 7)": 0, + "(7770, '2024-11-29', 0)": 0, + "(7770, '2024-11-29', 1)": 1, + "(7770, '2024-11-29', 2)": 0, + "(7770, '2024-11-29', 3)": 0, + "(7770, '2024-11-29', 4)": 0, + "(7770, '2024-11-29', 5)": 0, + "(7770, '2024-11-29', 6)": 0, + "(7770, '2024-11-29', 7)": 0, + "(7770, '2024-11-30', 0)": 0, + "(7770, '2024-11-30', 1)": 0, + "(7770, '2024-11-30', 2)": 0, + "(7770, '2024-11-30', 3)": 0, + "(7770, '2024-11-30', 4)": 0, + "(7770, '2024-11-30', 5)": 0, + "(7770, '2024-11-30', 6)": 0, + "(7770, '2024-11-30', 7)": 0, + "(7796, '2024-11-01', 0)": 0, + "(7796, '2024-11-01', 1)": 0, + "(7796, '2024-11-01', 2)": 0, + "(7796, '2024-11-01', 3)": 0, + "(7796, '2024-11-01', 4)": 0, + "(7796, '2024-11-01', 5)": 0, + "(7796, '2024-11-01', 6)": 0, + "(7796, '2024-11-01', 7)": 0, + "(7796, '2024-11-02', 0)": 0, + "(7796, '2024-11-02', 1)": 0, + "(7796, '2024-11-02', 2)": 0, + "(7796, '2024-11-02', 3)": 0, + "(7796, '2024-11-02', 4)": 0, + "(7796, '2024-11-02', 5)": 0, + "(7796, '2024-11-02', 6)": 0, + "(7796, '2024-11-02', 7)": 0, + "(7796, '2024-11-03', 0)": 0, + "(7796, '2024-11-03', 1)": 0, + "(7796, '2024-11-03', 2)": 0, + "(7796, '2024-11-03', 3)": 0, + "(7796, '2024-11-03', 4)": 0, + "(7796, '2024-11-03', 5)": 0, + "(7796, '2024-11-03', 6)": 0, + "(7796, '2024-11-03', 7)": 0, + "(7796, '2024-11-04', 0)": 0, + "(7796, '2024-11-04', 1)": 0, + "(7796, '2024-11-04', 2)": 0, + "(7796, '2024-11-04', 3)": 0, + "(7796, '2024-11-04', 4)": 0, + "(7796, '2024-11-04', 5)": 0, + "(7796, '2024-11-04', 6)": 0, + "(7796, '2024-11-04', 7)": 0, + "(7796, '2024-11-05', 0)": 0, + "(7796, '2024-11-05', 1)": 0, + "(7796, '2024-11-05', 2)": 0, + "(7796, '2024-11-05', 3)": 0, + "(7796, '2024-11-05', 4)": 0, + "(7796, '2024-11-05', 5)": 0, + "(7796, '2024-11-05', 6)": 0, + "(7796, '2024-11-05', 7)": 0, + "(7796, '2024-11-06', 0)": 0, + "(7796, '2024-11-06', 1)": 0, + "(7796, '2024-11-06', 2)": 0, + "(7796, '2024-11-06', 3)": 0, + "(7796, '2024-11-06', 4)": 0, + "(7796, '2024-11-06', 5)": 0, + "(7796, '2024-11-06', 6)": 0, + "(7796, '2024-11-06', 7)": 0, + "(7796, '2024-11-07', 0)": 0, + "(7796, '2024-11-07', 1)": 1, + "(7796, '2024-11-07', 2)": 0, + "(7796, '2024-11-07', 3)": 0, + "(7796, '2024-11-07', 4)": 0, + "(7796, '2024-11-07', 5)": 0, + "(7796, '2024-11-07', 6)": 0, + "(7796, '2024-11-07', 7)": 0, + "(7796, '2024-11-08', 0)": 1, + "(7796, '2024-11-08', 1)": 0, + "(7796, '2024-11-08', 2)": 0, + "(7796, '2024-11-08', 3)": 0, + "(7796, '2024-11-08', 4)": 0, + "(7796, '2024-11-08', 5)": 0, + "(7796, '2024-11-08', 6)": 0, + "(7796, '2024-11-08', 7)": 0, + "(7796, '2024-11-09', 0)": 0, + "(7796, '2024-11-09', 1)": 0, + "(7796, '2024-11-09', 2)": 0, + "(7796, '2024-11-09', 3)": 0, + "(7796, '2024-11-09', 4)": 0, + "(7796, '2024-11-09', 5)": 0, + "(7796, '2024-11-09', 6)": 0, + "(7796, '2024-11-09', 7)": 0, + "(7796, '2024-11-10', 0)": 0, + "(7796, '2024-11-10', 1)": 0, + "(7796, '2024-11-10', 2)": 0, + "(7796, '2024-11-10', 3)": 0, + "(7796, '2024-11-10', 4)": 0, + "(7796, '2024-11-10', 5)": 0, + "(7796, '2024-11-10', 6)": 0, + "(7796, '2024-11-10', 7)": 0, + "(7796, '2024-11-11', 0)": 1, + "(7796, '2024-11-11', 1)": 0, + "(7796, '2024-11-11', 2)": 0, + "(7796, '2024-11-11', 3)": 0, + "(7796, '2024-11-11', 4)": 0, + "(7796, '2024-11-11', 5)": 0, + "(7796, '2024-11-11', 6)": 0, + "(7796, '2024-11-11', 7)": 0, + "(7796, '2024-11-12', 0)": 0, + "(7796, '2024-11-12', 1)": 0, + "(7796, '2024-11-12', 2)": 0, + "(7796, '2024-11-12', 3)": 0, + "(7796, '2024-11-12', 4)": 0, + "(7796, '2024-11-12', 5)": 0, + "(7796, '2024-11-12', 6)": 0, + "(7796, '2024-11-12', 7)": 0, + "(7796, '2024-11-13', 0)": 0, + "(7796, '2024-11-13', 1)": 0, + "(7796, '2024-11-13', 2)": 0, + "(7796, '2024-11-13', 3)": 0, + "(7796, '2024-11-13', 4)": 0, + "(7796, '2024-11-13', 5)": 0, + "(7796, '2024-11-13', 6)": 0, + "(7796, '2024-11-13', 7)": 0, + "(7796, '2024-11-14', 0)": 0, + "(7796, '2024-11-14', 1)": 1, + "(7796, '2024-11-14', 2)": 0, + "(7796, '2024-11-14', 3)": 0, + "(7796, '2024-11-14', 4)": 0, + "(7796, '2024-11-14', 5)": 0, + "(7796, '2024-11-14', 6)": 0, + "(7796, '2024-11-14', 7)": 0, + "(7796, '2024-11-15', 0)": 0, + "(7796, '2024-11-15', 1)": 0, + "(7796, '2024-11-15', 2)": 0, + "(7796, '2024-11-15', 3)": 0, + "(7796, '2024-11-15', 4)": 0, + "(7796, '2024-11-15', 5)": 0, + "(7796, '2024-11-15', 6)": 0, + "(7796, '2024-11-15', 7)": 0, + "(7796, '2024-11-16', 0)": 0, + "(7796, '2024-11-16', 1)": 0, + "(7796, '2024-11-16', 2)": 0, + "(7796, '2024-11-16', 3)": 0, + "(7796, '2024-11-16', 4)": 0, + "(7796, '2024-11-16', 5)": 0, + "(7796, '2024-11-16', 6)": 0, + "(7796, '2024-11-16', 7)": 0, + "(7796, '2024-11-17', 0)": 0, + "(7796, '2024-11-17', 1)": 1, + "(7796, '2024-11-17', 2)": 0, + "(7796, '2024-11-17', 3)": 0, + "(7796, '2024-11-17', 4)": 0, + "(7796, '2024-11-17', 5)": 0, + "(7796, '2024-11-17', 6)": 0, + "(7796, '2024-11-17', 7)": 0, + "(7796, '2024-11-18', 0)": 0, + "(7796, '2024-11-18', 1)": 0, + "(7796, '2024-11-18', 2)": 0, + "(7796, '2024-11-18', 3)": 0, + "(7796, '2024-11-18', 4)": 0, + "(7796, '2024-11-18', 5)": 0, + "(7796, '2024-11-18', 6)": 0, + "(7796, '2024-11-18', 7)": 0, + "(7796, '2024-11-19', 0)": 0, + "(7796, '2024-11-19', 1)": 0, + "(7796, '2024-11-19', 2)": 0, + "(7796, '2024-11-19', 3)": 0, + "(7796, '2024-11-19', 4)": 0, + "(7796, '2024-11-19', 5)": 0, + "(7796, '2024-11-19', 6)": 0, + "(7796, '2024-11-19', 7)": 0, + "(7796, '2024-11-20', 0)": 0, + "(7796, '2024-11-20', 1)": 0, + "(7796, '2024-11-20', 2)": 0, + "(7796, '2024-11-20', 3)": 0, + "(7796, '2024-11-20', 4)": 0, + "(7796, '2024-11-20', 5)": 0, + "(7796, '2024-11-20', 6)": 0, + "(7796, '2024-11-20', 7)": 0, + "(7796, '2024-11-21', 0)": 0, + "(7796, '2024-11-21', 1)": 0, + "(7796, '2024-11-21', 2)": 0, + "(7796, '2024-11-21', 3)": 0, + "(7796, '2024-11-21', 4)": 0, + "(7796, '2024-11-21', 5)": 0, + "(7796, '2024-11-21', 6)": 0, + "(7796, '2024-11-21', 7)": 0, + "(7796, '2024-11-22', 0)": 1, + "(7796, '2024-11-22', 1)": 0, + "(7796, '2024-11-22', 2)": 0, + "(7796, '2024-11-22', 3)": 0, + "(7796, '2024-11-22', 4)": 0, + "(7796, '2024-11-22', 5)": 0, + "(7796, '2024-11-22', 6)": 0, + "(7796, '2024-11-22', 7)": 0, + "(7796, '2024-11-23', 0)": 0, + "(7796, '2024-11-23', 1)": 0, + "(7796, '2024-11-23', 2)": 0, + "(7796, '2024-11-23', 3)": 0, + "(7796, '2024-11-23', 4)": 0, + "(7796, '2024-11-23', 5)": 0, + "(7796, '2024-11-23', 6)": 0, + "(7796, '2024-11-23', 7)": 0, + "(7796, '2024-11-24', 0)": 0, + "(7796, '2024-11-24', 1)": 0, + "(7796, '2024-11-24', 2)": 0, + "(7796, '2024-11-24', 3)": 0, + "(7796, '2024-11-24', 4)": 0, + "(7796, '2024-11-24', 5)": 0, + "(7796, '2024-11-24', 6)": 0, + "(7796, '2024-11-24', 7)": 0, + "(7796, '2024-11-25', 0)": 0, + "(7796, '2024-11-25', 1)": 1, + "(7796, '2024-11-25', 2)": 0, + "(7796, '2024-11-25', 3)": 0, + "(7796, '2024-11-25', 4)": 0, + "(7796, '2024-11-25', 5)": 0, + "(7796, '2024-11-25', 6)": 0, + "(7796, '2024-11-25', 7)": 0, + "(7796, '2024-11-26', 0)": 0, + "(7796, '2024-11-26', 1)": 1, + "(7796, '2024-11-26', 2)": 0, + "(7796, '2024-11-26', 3)": 0, + "(7796, '2024-11-26', 4)": 0, + "(7796, '2024-11-26', 5)": 0, + "(7796, '2024-11-26', 6)": 0, + "(7796, '2024-11-26', 7)": 0, + "(7796, '2024-11-27', 0)": 0, + "(7796, '2024-11-27', 1)": 0, + "(7796, '2024-11-27', 2)": 0, + "(7796, '2024-11-27', 3)": 0, + "(7796, '2024-11-27', 4)": 0, + "(7796, '2024-11-27', 5)": 0, + "(7796, '2024-11-27', 6)": 0, + "(7796, '2024-11-27', 7)": 0, + "(7796, '2024-11-28', 0)": 0, + "(7796, '2024-11-28', 1)": 0, + "(7796, '2024-11-28', 2)": 0, + "(7796, '2024-11-28', 3)": 0, + "(7796, '2024-11-28', 4)": 0, + "(7796, '2024-11-28', 5)": 0, + "(7796, '2024-11-28', 6)": 0, + "(7796, '2024-11-28', 7)": 0, + "(7796, '2024-11-29', 0)": 0, + "(7796, '2024-11-29', 1)": 0, + "(7796, '2024-11-29', 2)": 0, + "(7796, '2024-11-29', 3)": 0, + "(7796, '2024-11-29', 4)": 0, + "(7796, '2024-11-29', 5)": 0, + "(7796, '2024-11-29', 6)": 0, + "(7796, '2024-11-29', 7)": 0, + "(7796, '2024-11-30', 0)": 1, + "(7796, '2024-11-30', 1)": 0, + "(7796, '2024-11-30', 2)": 0, + "(7796, '2024-11-30', 3)": 0, + "(7796, '2024-11-30', 4)": 0, + "(7796, '2024-11-30', 5)": 0, + "(7796, '2024-11-30', 6)": 0, + "(7796, '2024-11-30', 7)": 0, + "(7835, '2024-11-01', 0)": 0, + "(7835, '2024-11-01', 1)": 0, + "(7835, '2024-11-01', 2)": 0, + "(7835, '2024-11-01', 3)": 0, + "(7835, '2024-11-01', 4)": 0, + "(7835, '2024-11-01', 5)": 0, + "(7835, '2024-11-01', 6)": 0, + "(7835, '2024-11-01', 7)": 0, + "(7835, '2024-11-02', 0)": 0, + "(7835, '2024-11-02', 1)": 0, + "(7835, '2024-11-02', 2)": 0, + "(7835, '2024-11-02', 3)": 0, + "(7835, '2024-11-02', 4)": 0, + "(7835, '2024-11-02', 5)": 0, + "(7835, '2024-11-02', 6)": 0, + "(7835, '2024-11-02', 7)": 0, + "(7835, '2024-11-03', 0)": 0, + "(7835, '2024-11-03', 1)": 0, + "(7835, '2024-11-03', 2)": 0, + "(7835, '2024-11-03', 3)": 0, + "(7835, '2024-11-03', 4)": 0, + "(7835, '2024-11-03', 5)": 0, + "(7835, '2024-11-03', 6)": 0, + "(7835, '2024-11-03', 7)": 0, + "(7835, '2024-11-04', 0)": 0, + "(7835, '2024-11-04', 1)": 0, + "(7835, '2024-11-04', 2)": 0, + "(7835, '2024-11-04', 3)": 0, + "(7835, '2024-11-04', 4)": 0, + "(7835, '2024-11-04', 5)": 0, + "(7835, '2024-11-04', 6)": 0, + "(7835, '2024-11-04', 7)": 0, + "(7835, '2024-11-05', 0)": 0, + "(7835, '2024-11-05', 1)": 0, + "(7835, '2024-11-05', 2)": 0, + "(7835, '2024-11-05', 3)": 0, + "(7835, '2024-11-05', 4)": 0, + "(7835, '2024-11-05', 5)": 0, + "(7835, '2024-11-05', 6)": 0, + "(7835, '2024-11-05', 7)": 0, + "(7835, '2024-11-06', 0)": 0, + "(7835, '2024-11-06', 1)": 0, + "(7835, '2024-11-06', 2)": 0, + "(7835, '2024-11-06', 3)": 0, + "(7835, '2024-11-06', 4)": 0, + "(7835, '2024-11-06', 5)": 0, + "(7835, '2024-11-06', 6)": 0, + "(7835, '2024-11-06', 7)": 0, + "(7835, '2024-11-07', 0)": 0, + "(7835, '2024-11-07', 1)": 0, + "(7835, '2024-11-07', 2)": 0, + "(7835, '2024-11-07', 3)": 0, + "(7835, '2024-11-07', 4)": 0, + "(7835, '2024-11-07', 5)": 0, + "(7835, '2024-11-07', 6)": 0, + "(7835, '2024-11-07', 7)": 0, + "(7835, '2024-11-08', 0)": 0, + "(7835, '2024-11-08', 1)": 1, + "(7835, '2024-11-08', 2)": 0, + "(7835, '2024-11-08', 3)": 0, + "(7835, '2024-11-08', 4)": 0, + "(7835, '2024-11-08', 5)": 0, + "(7835, '2024-11-08', 6)": 0, + "(7835, '2024-11-08', 7)": 0, + "(7835, '2024-11-09', 0)": 1, + "(7835, '2024-11-09', 1)": 0, + "(7835, '2024-11-09', 2)": 0, + "(7835, '2024-11-09', 3)": 0, + "(7835, '2024-11-09', 4)": 0, + "(7835, '2024-11-09', 5)": 0, + "(7835, '2024-11-09', 6)": 0, + "(7835, '2024-11-09', 7)": 0, + "(7835, '2024-11-10', 0)": 0, + "(7835, '2024-11-10', 1)": 0, + "(7835, '2024-11-10', 2)": 0, + "(7835, '2024-11-10', 3)": 0, + "(7835, '2024-11-10', 4)": 0, + "(7835, '2024-11-10', 5)": 0, + "(7835, '2024-11-10', 6)": 0, + "(7835, '2024-11-10', 7)": 0, + "(7835, '2024-11-11', 0)": 0, + "(7835, '2024-11-11', 1)": 0, + "(7835, '2024-11-11', 2)": 1, + "(7835, '2024-11-11', 3)": 0, + "(7835, '2024-11-11', 4)": 0, + "(7835, '2024-11-11', 5)": 0, + "(7835, '2024-11-11', 6)": 0, + "(7835, '2024-11-11', 7)": 0, + "(7835, '2024-11-12', 0)": 0, + "(7835, '2024-11-12', 1)": 1, + "(7835, '2024-11-12', 2)": 0, + "(7835, '2024-11-12', 3)": 0, + "(7835, '2024-11-12', 4)": 0, + "(7835, '2024-11-12', 5)": 0, + "(7835, '2024-11-12', 6)": 0, + "(7835, '2024-11-12', 7)": 0, + "(7835, '2024-11-13', 0)": 0, + "(7835, '2024-11-13', 1)": 0, + "(7835, '2024-11-13', 2)": 0, + "(7835, '2024-11-13', 3)": 0, + "(7835, '2024-11-13', 4)": 0, + "(7835, '2024-11-13', 5)": 0, + "(7835, '2024-11-13', 6)": 0, + "(7835, '2024-11-13', 7)": 0, + "(7835, '2024-11-14', 0)": 0, + "(7835, '2024-11-14', 1)": 0, + "(7835, '2024-11-14', 2)": 0, + "(7835, '2024-11-14', 3)": 0, + "(7835, '2024-11-14', 4)": 0, + "(7835, '2024-11-14', 5)": 0, + "(7835, '2024-11-14', 6)": 0, + "(7835, '2024-11-14', 7)": 0, + "(7835, '2024-11-15', 0)": 0, + "(7835, '2024-11-15', 1)": 0, + "(7835, '2024-11-15', 2)": 0, + "(7835, '2024-11-15', 3)": 0, + "(7835, '2024-11-15', 4)": 0, + "(7835, '2024-11-15', 5)": 0, + "(7835, '2024-11-15', 6)": 0, + "(7835, '2024-11-15', 7)": 0, + "(7835, '2024-11-16', 0)": 0, + "(7835, '2024-11-16', 1)": 0, + "(7835, '2024-11-16', 2)": 0, + "(7835, '2024-11-16', 3)": 0, + "(7835, '2024-11-16', 4)": 0, + "(7835, '2024-11-16', 5)": 0, + "(7835, '2024-11-16', 6)": 0, + "(7835, '2024-11-16', 7)": 0, + "(7835, '2024-11-17', 0)": 0, + "(7835, '2024-11-17', 1)": 0, + "(7835, '2024-11-17', 2)": 1, + "(7835, '2024-11-17', 3)": 0, + "(7835, '2024-11-17', 4)": 0, + "(7835, '2024-11-17', 5)": 0, + "(7835, '2024-11-17', 6)": 0, + "(7835, '2024-11-17', 7)": 0, + "(7835, '2024-11-18', 0)": 0, + "(7835, '2024-11-18', 1)": 1, + "(7835, '2024-11-18', 2)": 0, + "(7835, '2024-11-18', 3)": 0, + "(7835, '2024-11-18', 4)": 0, + "(7835, '2024-11-18', 5)": 0, + "(7835, '2024-11-18', 6)": 0, + "(7835, '2024-11-18', 7)": 0, + "(7835, '2024-11-19', 0)": 1, + "(7835, '2024-11-19', 1)": 0, + "(7835, '2024-11-19', 2)": 0, + "(7835, '2024-11-19', 3)": 0, + "(7835, '2024-11-19', 4)": 0, + "(7835, '2024-11-19', 5)": 0, + "(7835, '2024-11-19', 6)": 0, + "(7835, '2024-11-19', 7)": 0, + "(7835, '2024-11-20', 0)": 0, + "(7835, '2024-11-20', 1)": 0, + "(7835, '2024-11-20', 2)": 0, + "(7835, '2024-11-20', 3)": 0, + "(7835, '2024-11-20', 4)": 0, + "(7835, '2024-11-20', 5)": 0, + "(7835, '2024-11-20', 6)": 0, + "(7835, '2024-11-20', 7)": 0, + "(7835, '2024-11-21', 0)": 0, + "(7835, '2024-11-21', 1)": 0, + "(7835, '2024-11-21', 2)": 0, + "(7835, '2024-11-21', 3)": 0, + "(7835, '2024-11-21', 4)": 0, + "(7835, '2024-11-21', 5)": 0, + "(7835, '2024-11-21', 6)": 0, + "(7835, '2024-11-21', 7)": 0, + "(7835, '2024-11-22', 0)": 0, + "(7835, '2024-11-22', 1)": 0, + "(7835, '2024-11-22', 2)": 0, + "(7835, '2024-11-22', 3)": 0, + "(7835, '2024-11-22', 4)": 0, + "(7835, '2024-11-22', 5)": 0, + "(7835, '2024-11-22', 6)": 0, + "(7835, '2024-11-22', 7)": 0, + "(7835, '2024-11-23', 0)": 0, + "(7835, '2024-11-23', 1)": 0, + "(7835, '2024-11-23', 2)": 0, + "(7835, '2024-11-23', 3)": 0, + "(7835, '2024-11-23', 4)": 0, + "(7835, '2024-11-23', 5)": 0, + "(7835, '2024-11-23', 6)": 0, + "(7835, '2024-11-23', 7)": 0, + "(7835, '2024-11-24', 0)": 0, + "(7835, '2024-11-24', 1)": 0, + "(7835, '2024-11-24', 2)": 0, + "(7835, '2024-11-24', 3)": 0, + "(7835, '2024-11-24', 4)": 0, + "(7835, '2024-11-24', 5)": 0, + "(7835, '2024-11-24', 6)": 0, + "(7835, '2024-11-24', 7)": 0, + "(7835, '2024-11-25', 0)": 0, + "(7835, '2024-11-25', 1)": 0, + "(7835, '2024-11-25', 2)": 0, + "(7835, '2024-11-25', 3)": 0, + "(7835, '2024-11-25', 4)": 0, + "(7835, '2024-11-25', 5)": 0, + "(7835, '2024-11-25', 6)": 0, + "(7835, '2024-11-25', 7)": 0, + "(7835, '2024-11-26', 0)": 0, + "(7835, '2024-11-26', 1)": 0, + "(7835, '2024-11-26', 2)": 0, + "(7835, '2024-11-26', 3)": 0, + "(7835, '2024-11-26', 4)": 0, + "(7835, '2024-11-26', 5)": 0, + "(7835, '2024-11-26', 6)": 0, + "(7835, '2024-11-26', 7)": 0, + "(7835, '2024-11-27', 0)": 0, + "(7835, '2024-11-27', 1)": 0, + "(7835, '2024-11-27', 2)": 0, + "(7835, '2024-11-27', 3)": 0, + "(7835, '2024-11-27', 4)": 0, + "(7835, '2024-11-27', 5)": 0, + "(7835, '2024-11-27', 6)": 0, + "(7835, '2024-11-27', 7)": 0, + "(7835, '2024-11-28', 0)": 0, + "(7835, '2024-11-28', 1)": 0, + "(7835, '2024-11-28', 2)": 1, + "(7835, '2024-11-28', 3)": 0, + "(7835, '2024-11-28', 4)": 0, + "(7835, '2024-11-28', 5)": 0, + "(7835, '2024-11-28', 6)": 0, + "(7835, '2024-11-28', 7)": 0, + "(7835, '2024-11-29', 0)": 0, + "(7835, '2024-11-29', 1)": 0, + "(7835, '2024-11-29', 2)": 1, + "(7835, '2024-11-29', 3)": 0, + "(7835, '2024-11-29', 4)": 0, + "(7835, '2024-11-29', 5)": 0, + "(7835, '2024-11-29', 6)": 0, + "(7835, '2024-11-29', 7)": 0, + "(7835, '2024-11-30', 0)": 0, + "(7835, '2024-11-30', 1)": 0, + "(7835, '2024-11-30', 2)": 0, + "(7835, '2024-11-30', 3)": 0, + "(7835, '2024-11-30', 4)": 0, + "(7835, '2024-11-30', 5)": 0, + "(7835, '2024-11-30', 6)": 0, + "(7835, '2024-11-30', 7)": 0, + "(7848, '2024-11-01', 0)": 0, + "(7848, '2024-11-01', 1)": 0, + "(7848, '2024-11-01', 2)": 1, + "(7848, '2024-11-01', 3)": 0, + "(7848, '2024-11-01', 4)": 0, + "(7848, '2024-11-01', 5)": 0, + "(7848, '2024-11-01', 6)": 0, + "(7848, '2024-11-01', 7)": 0, + "(7848, '2024-11-02', 0)": 0, + "(7848, '2024-11-02', 1)": 0, + "(7848, '2024-11-02', 2)": 0, + "(7848, '2024-11-02', 3)": 0, + "(7848, '2024-11-02', 4)": 0, + "(7848, '2024-11-02', 5)": 0, + "(7848, '2024-11-02', 6)": 0, + "(7848, '2024-11-02', 7)": 0, + "(7848, '2024-11-03', 0)": 1, + "(7848, '2024-11-03', 1)": 0, + "(7848, '2024-11-03', 2)": 0, + "(7848, '2024-11-03', 3)": 0, + "(7848, '2024-11-03', 4)": 0, + "(7848, '2024-11-03', 5)": 0, + "(7848, '2024-11-03', 6)": 0, + "(7848, '2024-11-03', 7)": 0, + "(7848, '2024-11-04', 0)": 1, + "(7848, '2024-11-04', 1)": 0, + "(7848, '2024-11-04', 2)": 0, + "(7848, '2024-11-04', 3)": 0, + "(7848, '2024-11-04', 4)": 0, + "(7848, '2024-11-04', 5)": 0, + "(7848, '2024-11-04', 6)": 0, + "(7848, '2024-11-04', 7)": 0, + "(7848, '2024-11-05', 0)": 1, + "(7848, '2024-11-05', 1)": 0, + "(7848, '2024-11-05', 2)": 0, + "(7848, '2024-11-05', 3)": 0, + "(7848, '2024-11-05', 4)": 0, + "(7848, '2024-11-05', 5)": 0, + "(7848, '2024-11-05', 6)": 0, + "(7848, '2024-11-05', 7)": 0, + "(7848, '2024-11-06', 0)": 0, + "(7848, '2024-11-06', 1)": 1, + "(7848, '2024-11-06', 2)": 0, + "(7848, '2024-11-06', 3)": 0, + "(7848, '2024-11-06', 4)": 0, + "(7848, '2024-11-06', 5)": 0, + "(7848, '2024-11-06', 6)": 0, + "(7848, '2024-11-06', 7)": 0, + "(7848, '2024-11-07', 0)": 1, + "(7848, '2024-11-07', 1)": 0, + "(7848, '2024-11-07', 2)": 0, + "(7848, '2024-11-07', 3)": 0, + "(7848, '2024-11-07', 4)": 0, + "(7848, '2024-11-07', 5)": 0, + "(7848, '2024-11-07', 6)": 0, + "(7848, '2024-11-07', 7)": 0, + "(7848, '2024-11-08', 0)": 0, + "(7848, '2024-11-08', 1)": 0, + "(7848, '2024-11-08', 2)": 0, + "(7848, '2024-11-08', 3)": 0, + "(7848, '2024-11-08', 4)": 0, + "(7848, '2024-11-08', 5)": 0, + "(7848, '2024-11-08', 6)": 0, + "(7848, '2024-11-08', 7)": 0, + "(7848, '2024-11-09', 0)": 0, + "(7848, '2024-11-09', 1)": 0, + "(7848, '2024-11-09', 2)": 0, + "(7848, '2024-11-09', 3)": 0, + "(7848, '2024-11-09', 4)": 0, + "(7848, '2024-11-09', 5)": 0, + "(7848, '2024-11-09', 6)": 0, + "(7848, '2024-11-09', 7)": 0, + "(7848, '2024-11-10', 0)": 0, + "(7848, '2024-11-10', 1)": 0, + "(7848, '2024-11-10', 2)": 1, + "(7848, '2024-11-10', 3)": 0, + "(7848, '2024-11-10', 4)": 0, + "(7848, '2024-11-10', 5)": 0, + "(7848, '2024-11-10', 6)": 0, + "(7848, '2024-11-10', 7)": 0, + "(7848, '2024-11-11', 0)": 0, + "(7848, '2024-11-11', 1)": 0, + "(7848, '2024-11-11', 2)": 0, + "(7848, '2024-11-11', 3)": 0, + "(7848, '2024-11-11', 4)": 0, + "(7848, '2024-11-11', 5)": 0, + "(7848, '2024-11-11', 6)": 0, + "(7848, '2024-11-11', 7)": 0, + "(7848, '2024-11-12', 0)": 0, + "(7848, '2024-11-12', 1)": 0, + "(7848, '2024-11-12', 2)": 1, + "(7848, '2024-11-12', 3)": 0, + "(7848, '2024-11-12', 4)": 0, + "(7848, '2024-11-12', 5)": 0, + "(7848, '2024-11-12', 6)": 0, + "(7848, '2024-11-12', 7)": 0, + "(7848, '2024-11-13', 0)": 0, + "(7848, '2024-11-13', 1)": 0, + "(7848, '2024-11-13', 2)": 0, + "(7848, '2024-11-13', 3)": 0, + "(7848, '2024-11-13', 4)": 0, + "(7848, '2024-11-13', 5)": 0, + "(7848, '2024-11-13', 6)": 0, + "(7848, '2024-11-13', 7)": 0, + "(7848, '2024-11-14', 0)": 0, + "(7848, '2024-11-14', 1)": 0, + "(7848, '2024-11-14', 2)": 0, + "(7848, '2024-11-14', 3)": 0, + "(7848, '2024-11-14', 4)": 0, + "(7848, '2024-11-14', 5)": 0, + "(7848, '2024-11-14', 6)": 0, + "(7848, '2024-11-14', 7)": 0, + "(7848, '2024-11-15', 0)": 0, + "(7848, '2024-11-15', 1)": 1, + "(7848, '2024-11-15', 2)": 0, + "(7848, '2024-11-15', 3)": 0, + "(7848, '2024-11-15', 4)": 0, + "(7848, '2024-11-15', 5)": 0, + "(7848, '2024-11-15', 6)": 0, + "(7848, '2024-11-15', 7)": 0, + "(7848, '2024-11-16', 0)": 1, + "(7848, '2024-11-16', 1)": 0, + "(7848, '2024-11-16', 2)": 0, + "(7848, '2024-11-16', 3)": 0, + "(7848, '2024-11-16', 4)": 0, + "(7848, '2024-11-16', 5)": 0, + "(7848, '2024-11-16', 6)": 0, + "(7848, '2024-11-16', 7)": 0, + "(7848, '2024-11-17', 0)": 1, + "(7848, '2024-11-17', 1)": 0, + "(7848, '2024-11-17', 2)": 0, + "(7848, '2024-11-17', 3)": 0, + "(7848, '2024-11-17', 4)": 0, + "(7848, '2024-11-17', 5)": 0, + "(7848, '2024-11-17', 6)": 0, + "(7848, '2024-11-17', 7)": 0, + "(7848, '2024-11-18', 0)": 0, + "(7848, '2024-11-18', 1)": 0, + "(7848, '2024-11-18', 2)": 0, + "(7848, '2024-11-18', 3)": 0, + "(7848, '2024-11-18', 4)": 0, + "(7848, '2024-11-18', 5)": 0, + "(7848, '2024-11-18', 6)": 0, + "(7848, '2024-11-18', 7)": 0, + "(7848, '2024-11-19', 0)": 0, + "(7848, '2024-11-19', 1)": 0, + "(7848, '2024-11-19', 2)": 1, + "(7848, '2024-11-19', 3)": 0, + "(7848, '2024-11-19', 4)": 0, + "(7848, '2024-11-19', 5)": 0, + "(7848, '2024-11-19', 6)": 0, + "(7848, '2024-11-19', 7)": 0, + "(7848, '2024-11-20', 0)": 0, + "(7848, '2024-11-20', 1)": 1, + "(7848, '2024-11-20', 2)": 0, + "(7848, '2024-11-20', 3)": 0, + "(7848, '2024-11-20', 4)": 0, + "(7848, '2024-11-20', 5)": 0, + "(7848, '2024-11-20', 6)": 0, + "(7848, '2024-11-20', 7)": 0, + "(7848, '2024-11-21', 0)": 1, + "(7848, '2024-11-21', 1)": 0, + "(7848, '2024-11-21', 2)": 0, + "(7848, '2024-11-21', 3)": 0, + "(7848, '2024-11-21', 4)": 0, + "(7848, '2024-11-21', 5)": 0, + "(7848, '2024-11-21', 6)": 0, + "(7848, '2024-11-21', 7)": 0, + "(7848, '2024-11-22', 0)": 0, + "(7848, '2024-11-22', 1)": 1, + "(7848, '2024-11-22', 2)": 0, + "(7848, '2024-11-22', 3)": 0, + "(7848, '2024-11-22', 4)": 0, + "(7848, '2024-11-22', 5)": 0, + "(7848, '2024-11-22', 6)": 0, + "(7848, '2024-11-22', 7)": 0, + "(7848, '2024-11-23', 0)": 0, + "(7848, '2024-11-23', 1)": 0, + "(7848, '2024-11-23', 2)": 0, + "(7848, '2024-11-23', 3)": 0, + "(7848, '2024-11-23', 4)": 0, + "(7848, '2024-11-23', 5)": 0, + "(7848, '2024-11-23', 6)": 0, + "(7848, '2024-11-23', 7)": 0, + "(7848, '2024-11-24', 0)": 1, + "(7848, '2024-11-24', 1)": 0, + "(7848, '2024-11-24', 2)": 0, + "(7848, '2024-11-24', 3)": 0, + "(7848, '2024-11-24', 4)": 0, + "(7848, '2024-11-24', 5)": 0, + "(7848, '2024-11-24', 6)": 0, + "(7848, '2024-11-24', 7)": 0, + "(7848, '2024-11-25', 0)": 0, + "(7848, '2024-11-25', 1)": 1, + "(7848, '2024-11-25', 2)": 0, + "(7848, '2024-11-25', 3)": 0, + "(7848, '2024-11-25', 4)": 0, + "(7848, '2024-11-25', 5)": 0, + "(7848, '2024-11-25', 6)": 0, + "(7848, '2024-11-25', 7)": 0, + "(7848, '2024-11-26', 0)": 0, + "(7848, '2024-11-26', 1)": 0, + "(7848, '2024-11-26', 2)": 0, + "(7848, '2024-11-26', 3)": 0, + "(7848, '2024-11-26', 4)": 0, + "(7848, '2024-11-26', 5)": 0, + "(7848, '2024-11-26', 6)": 0, + "(7848, '2024-11-26', 7)": 0, + "(7848, '2024-11-27', 0)": 0, + "(7848, '2024-11-27', 1)": 0, + "(7848, '2024-11-27', 2)": 0, + "(7848, '2024-11-27', 3)": 0, + "(7848, '2024-11-27', 4)": 0, + "(7848, '2024-11-27', 5)": 0, + "(7848, '2024-11-27', 6)": 0, + "(7848, '2024-11-27', 7)": 0, + "(7848, '2024-11-28', 0)": 0, + "(7848, '2024-11-28', 1)": 1, + "(7848, '2024-11-28', 2)": 0, + "(7848, '2024-11-28', 3)": 0, + "(7848, '2024-11-28', 4)": 0, + "(7848, '2024-11-28', 5)": 0, + "(7848, '2024-11-28', 6)": 0, + "(7848, '2024-11-28', 7)": 0, + "(7848, '2024-11-29', 0)": 0, + "(7848, '2024-11-29', 1)": 1, + "(7848, '2024-11-29', 2)": 0, + "(7848, '2024-11-29', 3)": 0, + "(7848, '2024-11-29', 4)": 0, + "(7848, '2024-11-29', 5)": 0, + "(7848, '2024-11-29', 6)": 0, + "(7848, '2024-11-29', 7)": 0, + "(7848, '2024-11-30', 0)": 0, + "(7848, '2024-11-30', 1)": 0, + "(7848, '2024-11-30', 2)": 0, + "(7848, '2024-11-30', 3)": 0, + "(7848, '2024-11-30', 4)": 0, + "(7848, '2024-11-30', 5)": 0, + "(7848, '2024-11-30', 6)": 0, + "(7848, '2024-11-30', 7)": 0, + "(7877, '2024-11-01', 0)": 0, + "(7877, '2024-11-01', 1)": 0, + "(7877, '2024-11-01', 2)": 0, + "(7877, '2024-11-01', 3)": 0, + "(7877, '2024-11-01', 4)": 0, + "(7877, '2024-11-01', 5)": 0, + "(7877, '2024-11-01', 6)": 0, + "(7877, '2024-11-01', 7)": 0, + "(7877, '2024-11-02', 0)": 0, + "(7877, '2024-11-02', 1)": 0, + "(7877, '2024-11-02', 2)": 0, + "(7877, '2024-11-02', 3)": 0, + "(7877, '2024-11-02', 4)": 0, + "(7877, '2024-11-02', 5)": 0, + "(7877, '2024-11-02', 6)": 0, + "(7877, '2024-11-02', 7)": 0, + "(7877, '2024-11-03', 0)": 0, + "(7877, '2024-11-03', 1)": 0, + "(7877, '2024-11-03', 2)": 0, + "(7877, '2024-11-03', 3)": 0, + "(7877, '2024-11-03', 4)": 0, + "(7877, '2024-11-03', 5)": 0, + "(7877, '2024-11-03', 6)": 0, + "(7877, '2024-11-03', 7)": 0, + "(7877, '2024-11-04', 0)": 0, + "(7877, '2024-11-04', 1)": 0, + "(7877, '2024-11-04', 2)": 0, + "(7877, '2024-11-04', 3)": 0, + "(7877, '2024-11-04', 4)": 0, + "(7877, '2024-11-04', 5)": 0, + "(7877, '2024-11-04', 6)": 0, + "(7877, '2024-11-04', 7)": 0, + "(7877, '2024-11-05', 0)": 0, + "(7877, '2024-11-05', 1)": 0, + "(7877, '2024-11-05', 2)": 0, + "(7877, '2024-11-05', 3)": 0, + "(7877, '2024-11-05', 4)": 0, + "(7877, '2024-11-05', 5)": 0, + "(7877, '2024-11-05', 6)": 0, + "(7877, '2024-11-05', 7)": 0, + "(7877, '2024-11-06', 0)": 0, + "(7877, '2024-11-06', 1)": 0, + "(7877, '2024-11-06', 2)": 0, + "(7877, '2024-11-06', 3)": 0, + "(7877, '2024-11-06', 4)": 0, + "(7877, '2024-11-06', 5)": 0, + "(7877, '2024-11-06', 6)": 0, + "(7877, '2024-11-06', 7)": 0, + "(7877, '2024-11-07', 0)": 0, + "(7877, '2024-11-07', 1)": 0, + "(7877, '2024-11-07', 2)": 1, + "(7877, '2024-11-07', 3)": 0, + "(7877, '2024-11-07', 4)": 0, + "(7877, '2024-11-07', 5)": 0, + "(7877, '2024-11-07', 6)": 0, + "(7877, '2024-11-07', 7)": 0, + "(7877, '2024-11-08', 0)": 0, + "(7877, '2024-11-08', 1)": 0, + "(7877, '2024-11-08', 2)": 0, + "(7877, '2024-11-08', 3)": 0, + "(7877, '2024-11-08', 4)": 0, + "(7877, '2024-11-08', 5)": 0, + "(7877, '2024-11-08', 6)": 0, + "(7877, '2024-11-08', 7)": 0, + "(7877, '2024-11-09', 0)": 0, + "(7877, '2024-11-09', 1)": 0, + "(7877, '2024-11-09', 2)": 1, + "(7877, '2024-11-09', 3)": 0, + "(7877, '2024-11-09', 4)": 0, + "(7877, '2024-11-09', 5)": 0, + "(7877, '2024-11-09', 6)": 0, + "(7877, '2024-11-09', 7)": 0, + "(7877, '2024-11-10', 0)": 0, + "(7877, '2024-11-10', 1)": 0, + "(7877, '2024-11-10', 2)": 0, + "(7877, '2024-11-10', 3)": 0, + "(7877, '2024-11-10', 4)": 0, + "(7877, '2024-11-10', 5)": 0, + "(7877, '2024-11-10', 6)": 0, + "(7877, '2024-11-10', 7)": 0, + "(7877, '2024-11-11', 0)": 0, + "(7877, '2024-11-11', 1)": 1, + "(7877, '2024-11-11', 2)": 0, + "(7877, '2024-11-11', 3)": 0, + "(7877, '2024-11-11', 4)": 0, + "(7877, '2024-11-11', 5)": 0, + "(7877, '2024-11-11', 6)": 0, + "(7877, '2024-11-11', 7)": 0, + "(7877, '2024-11-12', 0)": 0, + "(7877, '2024-11-12', 1)": 1, + "(7877, '2024-11-12', 2)": 0, + "(7877, '2024-11-12', 3)": 0, + "(7877, '2024-11-12', 4)": 0, + "(7877, '2024-11-12', 5)": 0, + "(7877, '2024-11-12', 6)": 0, + "(7877, '2024-11-12', 7)": 0, + "(7877, '2024-11-13', 0)": 0, + "(7877, '2024-11-13', 1)": 1, + "(7877, '2024-11-13', 2)": 0, + "(7877, '2024-11-13', 3)": 0, + "(7877, '2024-11-13', 4)": 0, + "(7877, '2024-11-13', 5)": 0, + "(7877, '2024-11-13', 6)": 0, + "(7877, '2024-11-13', 7)": 0, + "(7877, '2024-11-14', 0)": 1, + "(7877, '2024-11-14', 1)": 0, + "(7877, '2024-11-14', 2)": 0, + "(7877, '2024-11-14', 3)": 0, + "(7877, '2024-11-14', 4)": 0, + "(7877, '2024-11-14', 5)": 0, + "(7877, '2024-11-14', 6)": 0, + "(7877, '2024-11-14', 7)": 0, + "(7877, '2024-11-15', 0)": 1, + "(7877, '2024-11-15', 1)": 0, + "(7877, '2024-11-15', 2)": 0, + "(7877, '2024-11-15', 3)": 0, + "(7877, '2024-11-15', 4)": 0, + "(7877, '2024-11-15', 5)": 0, + "(7877, '2024-11-15', 6)": 0, + "(7877, '2024-11-15', 7)": 0, + "(7877, '2024-11-16', 0)": 0, + "(7877, '2024-11-16', 1)": 1, + "(7877, '2024-11-16', 2)": 0, + "(7877, '2024-11-16', 3)": 0, + "(7877, '2024-11-16', 4)": 0, + "(7877, '2024-11-16', 5)": 0, + "(7877, '2024-11-16', 6)": 0, + "(7877, '2024-11-16', 7)": 0, + "(7877, '2024-11-17', 0)": 0, + "(7877, '2024-11-17', 1)": 0, + "(7877, '2024-11-17', 2)": 0, + "(7877, '2024-11-17', 3)": 0, + "(7877, '2024-11-17', 4)": 0, + "(7877, '2024-11-17', 5)": 0, + "(7877, '2024-11-17', 6)": 0, + "(7877, '2024-11-17', 7)": 0, + "(7877, '2024-11-18', 0)": 0, + "(7877, '2024-11-18', 1)": 0, + "(7877, '2024-11-18', 2)": 1, + "(7877, '2024-11-18', 3)": 0, + "(7877, '2024-11-18', 4)": 0, + "(7877, '2024-11-18', 5)": 0, + "(7877, '2024-11-18', 6)": 0, + "(7877, '2024-11-18', 7)": 0, + "(7877, '2024-11-19', 0)": 0, + "(7877, '2024-11-19', 1)": 0, + "(7877, '2024-11-19', 2)": 0, + "(7877, '2024-11-19', 3)": 0, + "(7877, '2024-11-19', 4)": 0, + "(7877, '2024-11-19', 5)": 0, + "(7877, '2024-11-19', 6)": 0, + "(7877, '2024-11-19', 7)": 0, + "(7877, '2024-11-20', 0)": 0, + "(7877, '2024-11-20', 1)": 1, + "(7877, '2024-11-20', 2)": 0, + "(7877, '2024-11-20', 3)": 0, + "(7877, '2024-11-20', 4)": 0, + "(7877, '2024-11-20', 5)": 0, + "(7877, '2024-11-20', 6)": 0, + "(7877, '2024-11-20', 7)": 0, + "(7877, '2024-11-21', 0)": 0, + "(7877, '2024-11-21', 1)": 0, + "(7877, '2024-11-21', 2)": 0, + "(7877, '2024-11-21', 3)": 0, + "(7877, '2024-11-21', 4)": 0, + "(7877, '2024-11-21', 5)": 0, + "(7877, '2024-11-21', 6)": 0, + "(7877, '2024-11-21', 7)": 0, + "(7877, '2024-11-22', 0)": 0, + "(7877, '2024-11-22', 1)": 0, + "(7877, '2024-11-22', 2)": 0, + "(7877, '2024-11-22', 3)": 0, + "(7877, '2024-11-22', 4)": 0, + "(7877, '2024-11-22', 5)": 0, + "(7877, '2024-11-22', 6)": 0, + "(7877, '2024-11-22', 7)": 0, + "(7877, '2024-11-23', 0)": 0, + "(7877, '2024-11-23', 1)": 0, + "(7877, '2024-11-23', 2)": 0, + "(7877, '2024-11-23', 3)": 0, + "(7877, '2024-11-23', 4)": 0, + "(7877, '2024-11-23', 5)": 0, + "(7877, '2024-11-23', 6)": 0, + "(7877, '2024-11-23', 7)": 0, + "(7877, '2024-11-24', 0)": 0, + "(7877, '2024-11-24', 1)": 0, + "(7877, '2024-11-24', 2)": 1, + "(7877, '2024-11-24', 3)": 0, + "(7877, '2024-11-24', 4)": 0, + "(7877, '2024-11-24', 5)": 0, + "(7877, '2024-11-24', 6)": 0, + "(7877, '2024-11-24', 7)": 0, + "(7877, '2024-11-25', 0)": 0, + "(7877, '2024-11-25', 1)": 0, + "(7877, '2024-11-25', 2)": 1, + "(7877, '2024-11-25', 3)": 0, + "(7877, '2024-11-25', 4)": 0, + "(7877, '2024-11-25', 5)": 0, + "(7877, '2024-11-25', 6)": 0, + "(7877, '2024-11-25', 7)": 0, + "(7877, '2024-11-26', 0)": 0, + "(7877, '2024-11-26', 1)": 0, + "(7877, '2024-11-26', 2)": 0, + "(7877, '2024-11-26', 3)": 0, + "(7877, '2024-11-26', 4)": 0, + "(7877, '2024-11-26', 5)": 0, + "(7877, '2024-11-26', 6)": 0, + "(7877, '2024-11-26', 7)": 0, + "(7877, '2024-11-27', 0)": 0, + "(7877, '2024-11-27', 1)": 0, + "(7877, '2024-11-27', 2)": 0, + "(7877, '2024-11-27', 3)": 0, + "(7877, '2024-11-27', 4)": 0, + "(7877, '2024-11-27', 5)": 0, + "(7877, '2024-11-27', 6)": 0, + "(7877, '2024-11-27', 7)": 0, + "(7877, '2024-11-28', 0)": 1, + "(7877, '2024-11-28', 1)": 0, + "(7877, '2024-11-28', 2)": 0, + "(7877, '2024-11-28', 3)": 0, + "(7877, '2024-11-28', 4)": 0, + "(7877, '2024-11-28', 5)": 0, + "(7877, '2024-11-28', 6)": 0, + "(7877, '2024-11-28', 7)": 0, + "(7877, '2024-11-29', 0)": 0, + "(7877, '2024-11-29', 1)": 0, + "(7877, '2024-11-29', 2)": 0, + "(7877, '2024-11-29', 3)": 0, + "(7877, '2024-11-29', 4)": 0, + "(7877, '2024-11-29', 5)": 0, + "(7877, '2024-11-29', 6)": 0, + "(7877, '2024-11-29', 7)": 0, + "(7877, '2024-11-30', 0)": 0, + "(7877, '2024-11-30', 1)": 0, + "(7877, '2024-11-30', 2)": 0, + "(7877, '2024-11-30', 3)": 0, + "(7877, '2024-11-30', 4)": 0, + "(7877, '2024-11-30', 5)": 0, + "(7877, '2024-11-30', 6)": 0, + "(7877, '2024-11-30', 7)": 0, + "(790, '2024-11-01', 0)": 0, + "(790, '2024-11-01', 1)": 0, + "(790, '2024-11-01', 2)": 0, + "(790, '2024-11-01', 3)": 0, + "(790, '2024-11-01', 4)": 0, + "(790, '2024-11-01', 5)": 0, + "(790, '2024-11-01', 6)": 0, + "(790, '2024-11-01', 7)": 0, + "(790, '2024-11-02', 0)": 0, + "(790, '2024-11-02', 1)": 0, + "(790, '2024-11-02', 2)": 0, + "(790, '2024-11-02', 3)": 0, + "(790, '2024-11-02', 4)": 0, + "(790, '2024-11-02', 5)": 0, + "(790, '2024-11-02', 6)": 0, + "(790, '2024-11-02', 7)": 0, + "(790, '2024-11-03', 0)": 0, + "(790, '2024-11-03', 1)": 0, + "(790, '2024-11-03', 2)": 0, + "(790, '2024-11-03', 3)": 0, + "(790, '2024-11-03', 4)": 0, + "(790, '2024-11-03', 5)": 0, + "(790, '2024-11-03', 6)": 0, + "(790, '2024-11-03', 7)": 0, + "(790, '2024-11-04', 0)": 0, + "(790, '2024-11-04', 1)": 0, + "(790, '2024-11-04', 2)": 0, + "(790, '2024-11-04', 3)": 0, + "(790, '2024-11-04', 4)": 0, + "(790, '2024-11-04', 5)": 0, + "(790, '2024-11-04', 6)": 0, + "(790, '2024-11-04', 7)": 0, + "(790, '2024-11-05', 0)": 0, + "(790, '2024-11-05', 1)": 0, + "(790, '2024-11-05', 2)": 0, + "(790, '2024-11-05', 3)": 0, + "(790, '2024-11-05', 4)": 0, + "(790, '2024-11-05', 5)": 0, + "(790, '2024-11-05', 6)": 0, + "(790, '2024-11-05', 7)": 0, + "(790, '2024-11-06', 0)": 0, + "(790, '2024-11-06', 1)": 0, + "(790, '2024-11-06', 2)": 0, + "(790, '2024-11-06', 3)": 0, + "(790, '2024-11-06', 4)": 0, + "(790, '2024-11-06', 5)": 0, + "(790, '2024-11-06', 6)": 0, + "(790, '2024-11-06', 7)": 0, + "(790, '2024-11-07', 0)": 0, + "(790, '2024-11-07', 1)": 0, + "(790, '2024-11-07', 2)": 0, + "(790, '2024-11-07', 3)": 0, + "(790, '2024-11-07', 4)": 0, + "(790, '2024-11-07', 5)": 0, + "(790, '2024-11-07', 6)": 0, + "(790, '2024-11-07', 7)": 0, + "(790, '2024-11-08', 0)": 0, + "(790, '2024-11-08', 1)": 0, + "(790, '2024-11-08', 2)": 0, + "(790, '2024-11-08', 3)": 0, + "(790, '2024-11-08', 4)": 0, + "(790, '2024-11-08', 5)": 0, + "(790, '2024-11-08', 6)": 0, + "(790, '2024-11-08', 7)": 0, + "(790, '2024-11-09', 0)": 0, + "(790, '2024-11-09', 1)": 0, + "(790, '2024-11-09', 2)": 0, + "(790, '2024-11-09', 3)": 0, + "(790, '2024-11-09', 4)": 0, + "(790, '2024-11-09', 5)": 0, + "(790, '2024-11-09', 6)": 0, + "(790, '2024-11-09', 7)": 0, + "(790, '2024-11-10', 0)": 0, + "(790, '2024-11-10', 1)": 0, + "(790, '2024-11-10', 2)": 0, + "(790, '2024-11-10', 3)": 0, + "(790, '2024-11-10', 4)": 0, + "(790, '2024-11-10', 5)": 0, + "(790, '2024-11-10', 6)": 0, + "(790, '2024-11-10', 7)": 0, + "(790, '2024-11-11', 0)": 0, + "(790, '2024-11-11', 1)": 0, + "(790, '2024-11-11', 2)": 0, + "(790, '2024-11-11', 3)": 0, + "(790, '2024-11-11', 4)": 0, + "(790, '2024-11-11', 5)": 0, + "(790, '2024-11-11', 6)": 0, + "(790, '2024-11-11', 7)": 0, + "(790, '2024-11-12', 0)": 0, + "(790, '2024-11-12', 1)": 0, + "(790, '2024-11-12', 2)": 0, + "(790, '2024-11-12', 3)": 0, + "(790, '2024-11-12', 4)": 0, + "(790, '2024-11-12', 5)": 0, + "(790, '2024-11-12', 6)": 0, + "(790, '2024-11-12', 7)": 0, + "(790, '2024-11-13', 0)": 0, + "(790, '2024-11-13', 1)": 0, + "(790, '2024-11-13', 2)": 0, + "(790, '2024-11-13', 3)": 0, + "(790, '2024-11-13', 4)": 0, + "(790, '2024-11-13', 5)": 0, + "(790, '2024-11-13', 6)": 0, + "(790, '2024-11-13', 7)": 0, + "(790, '2024-11-14', 0)": 0, + "(790, '2024-11-14', 1)": 0, + "(790, '2024-11-14', 2)": 0, + "(790, '2024-11-14', 3)": 0, + "(790, '2024-11-14', 4)": 0, + "(790, '2024-11-14', 5)": 0, + "(790, '2024-11-14', 6)": 0, + "(790, '2024-11-14', 7)": 0, + "(790, '2024-11-15', 0)": 0, + "(790, '2024-11-15', 1)": 0, + "(790, '2024-11-15', 2)": 0, + "(790, '2024-11-15', 3)": 0, + "(790, '2024-11-15', 4)": 0, + "(790, '2024-11-15', 5)": 0, + "(790, '2024-11-15', 6)": 0, + "(790, '2024-11-15', 7)": 0, + "(790, '2024-11-16', 0)": 0, + "(790, '2024-11-16', 1)": 0, + "(790, '2024-11-16', 2)": 0, + "(790, '2024-11-16', 3)": 0, + "(790, '2024-11-16', 4)": 0, + "(790, '2024-11-16', 5)": 0, + "(790, '2024-11-16', 6)": 0, + "(790, '2024-11-16', 7)": 0, + "(790, '2024-11-17', 0)": 0, + "(790, '2024-11-17', 1)": 0, + "(790, '2024-11-17', 2)": 0, + "(790, '2024-11-17', 3)": 0, + "(790, '2024-11-17', 4)": 0, + "(790, '2024-11-17', 5)": 0, + "(790, '2024-11-17', 6)": 0, + "(790, '2024-11-17', 7)": 0, + "(790, '2024-11-18', 0)": 0, + "(790, '2024-11-18', 1)": 0, + "(790, '2024-11-18', 2)": 0, + "(790, '2024-11-18', 3)": 0, + "(790, '2024-11-18', 4)": 0, + "(790, '2024-11-18', 5)": 0, + "(790, '2024-11-18', 6)": 0, + "(790, '2024-11-18', 7)": 0, + "(790, '2024-11-19', 0)": 0, + "(790, '2024-11-19', 1)": 0, + "(790, '2024-11-19', 2)": 0, + "(790, '2024-11-19', 3)": 0, + "(790, '2024-11-19', 4)": 0, + "(790, '2024-11-19', 5)": 0, + "(790, '2024-11-19', 6)": 0, + "(790, '2024-11-19', 7)": 0, + "(790, '2024-11-20', 0)": 0, + "(790, '2024-11-20', 1)": 0, + "(790, '2024-11-20', 2)": 0, + "(790, '2024-11-20', 3)": 0, + "(790, '2024-11-20', 4)": 0, + "(790, '2024-11-20', 5)": 0, + "(790, '2024-11-20', 6)": 0, + "(790, '2024-11-20', 7)": 0, + "(790, '2024-11-21', 0)": 0, + "(790, '2024-11-21', 1)": 0, + "(790, '2024-11-21', 2)": 0, + "(790, '2024-11-21', 3)": 0, + "(790, '2024-11-21', 4)": 0, + "(790, '2024-11-21', 5)": 0, + "(790, '2024-11-21', 6)": 0, + "(790, '2024-11-21', 7)": 0, + "(790, '2024-11-22', 0)": 0, + "(790, '2024-11-22', 1)": 0, + "(790, '2024-11-22', 2)": 0, + "(790, '2024-11-22', 3)": 0, + "(790, '2024-11-22', 4)": 0, + "(790, '2024-11-22', 5)": 0, + "(790, '2024-11-22', 6)": 0, + "(790, '2024-11-22', 7)": 0, + "(790, '2024-11-23', 0)": 0, + "(790, '2024-11-23', 1)": 0, + "(790, '2024-11-23', 2)": 0, + "(790, '2024-11-23', 3)": 0, + "(790, '2024-11-23', 4)": 0, + "(790, '2024-11-23', 5)": 0, + "(790, '2024-11-23', 6)": 0, + "(790, '2024-11-23', 7)": 0, + "(790, '2024-11-24', 0)": 0, + "(790, '2024-11-24', 1)": 0, + "(790, '2024-11-24', 2)": 0, + "(790, '2024-11-24', 3)": 0, + "(790, '2024-11-24', 4)": 0, + "(790, '2024-11-24', 5)": 0, + "(790, '2024-11-24', 6)": 0, + "(790, '2024-11-24', 7)": 0, + "(790, '2024-11-25', 0)": 0, + "(790, '2024-11-25', 1)": 0, + "(790, '2024-11-25', 2)": 0, + "(790, '2024-11-25', 3)": 0, + "(790, '2024-11-25', 4)": 0, + "(790, '2024-11-25', 5)": 0, + "(790, '2024-11-25', 6)": 0, + "(790, '2024-11-25', 7)": 0, + "(790, '2024-11-26', 0)": 0, + "(790, '2024-11-26', 1)": 0, + "(790, '2024-11-26', 2)": 0, + "(790, '2024-11-26', 3)": 0, + "(790, '2024-11-26', 4)": 0, + "(790, '2024-11-26', 5)": 0, + "(790, '2024-11-26', 6)": 0, + "(790, '2024-11-26', 7)": 0, + "(790, '2024-11-27', 0)": 0, + "(790, '2024-11-27', 1)": 0, + "(790, '2024-11-27', 2)": 0, + "(790, '2024-11-27', 3)": 0, + "(790, '2024-11-27', 4)": 0, + "(790, '2024-11-27', 5)": 0, + "(790, '2024-11-27', 6)": 0, + "(790, '2024-11-27', 7)": 0, + "(790, '2024-11-28', 0)": 0, + "(790, '2024-11-28', 1)": 0, + "(790, '2024-11-28', 2)": 0, + "(790, '2024-11-28', 3)": 0, + "(790, '2024-11-28', 4)": 0, + "(790, '2024-11-28', 5)": 0, + "(790, '2024-11-28', 6)": 0, + "(790, '2024-11-28', 7)": 0, + "(790, '2024-11-29', 0)": 0, + "(790, '2024-11-29', 1)": 0, + "(790, '2024-11-29', 2)": 0, + "(790, '2024-11-29', 3)": 0, + "(790, '2024-11-29', 4)": 0, + "(790, '2024-11-29', 5)": 0, + "(790, '2024-11-29', 6)": 0, + "(790, '2024-11-29', 7)": 0, + "(790, '2024-11-30', 0)": 0, + "(790, '2024-11-30', 1)": 0, + "(790, '2024-11-30', 2)": 0, + "(790, '2024-11-30', 3)": 0, + "(790, '2024-11-30', 4)": 0, + "(790, '2024-11-30', 5)": 0, + "(790, '2024-11-30', 6)": 0, + "(790, '2024-11-30', 7)": 0, + "(791, '2024-11-01', 0)": 0, + "(791, '2024-11-01', 1)": 0, + "(791, '2024-11-01', 2)": 0, + "(791, '2024-11-01', 3)": 0, + "(791, '2024-11-01', 4)": 0, + "(791, '2024-11-01', 5)": 0, + "(791, '2024-11-01', 6)": 0, + "(791, '2024-11-01', 7)": 0, + "(791, '2024-11-02', 0)": 0, + "(791, '2024-11-02', 1)": 0, + "(791, '2024-11-02', 2)": 1, + "(791, '2024-11-02', 3)": 0, + "(791, '2024-11-02', 4)": 0, + "(791, '2024-11-02', 5)": 0, + "(791, '2024-11-02', 6)": 0, + "(791, '2024-11-02', 7)": 0, + "(791, '2024-11-03', 0)": 0, + "(791, '2024-11-03', 1)": 0, + "(791, '2024-11-03', 2)": 0, + "(791, '2024-11-03', 3)": 0, + "(791, '2024-11-03', 4)": 0, + "(791, '2024-11-03', 5)": 0, + "(791, '2024-11-03', 6)": 0, + "(791, '2024-11-03', 7)": 0, + "(791, '2024-11-04', 0)": 0, + "(791, '2024-11-04', 1)": 0, + "(791, '2024-11-04', 2)": 0, + "(791, '2024-11-04', 3)": 0, + "(791, '2024-11-04', 4)": 0, + "(791, '2024-11-04', 5)": 0, + "(791, '2024-11-04', 6)": 0, + "(791, '2024-11-04', 7)": 0, + "(791, '2024-11-05', 0)": 0, + "(791, '2024-11-05', 1)": 0, + "(791, '2024-11-05', 2)": 0, + "(791, '2024-11-05', 3)": 0, + "(791, '2024-11-05', 4)": 0, + "(791, '2024-11-05', 5)": 0, + "(791, '2024-11-05', 6)": 0, + "(791, '2024-11-05', 7)": 0, + "(791, '2024-11-06', 0)": 1, + "(791, '2024-11-06', 1)": 0, + "(791, '2024-11-06', 2)": 0, + "(791, '2024-11-06', 3)": 0, + "(791, '2024-11-06', 4)": 0, + "(791, '2024-11-06', 5)": 0, + "(791, '2024-11-06', 6)": 0, + "(791, '2024-11-06', 7)": 0, + "(791, '2024-11-07', 0)": 1, + "(791, '2024-11-07', 1)": 0, + "(791, '2024-11-07', 2)": 0, + "(791, '2024-11-07', 3)": 0, + "(791, '2024-11-07', 4)": 0, + "(791, '2024-11-07', 5)": 0, + "(791, '2024-11-07', 6)": 0, + "(791, '2024-11-07', 7)": 0, + "(791, '2024-11-08', 0)": 0, + "(791, '2024-11-08', 1)": 0, + "(791, '2024-11-08', 2)": 0, + "(791, '2024-11-08', 3)": 0, + "(791, '2024-11-08', 4)": 0, + "(791, '2024-11-08', 5)": 0, + "(791, '2024-11-08', 6)": 0, + "(791, '2024-11-08', 7)": 0, + "(791, '2024-11-09', 0)": 1, + "(791, '2024-11-09', 1)": 0, + "(791, '2024-11-09', 2)": 0, + "(791, '2024-11-09', 3)": 0, + "(791, '2024-11-09', 4)": 0, + "(791, '2024-11-09', 5)": 0, + "(791, '2024-11-09', 6)": 0, + "(791, '2024-11-09', 7)": 0, + "(791, '2024-11-10', 0)": 0, + "(791, '2024-11-10', 1)": 0, + "(791, '2024-11-10', 2)": 0, + "(791, '2024-11-10', 3)": 0, + "(791, '2024-11-10', 4)": 0, + "(791, '2024-11-10', 5)": 0, + "(791, '2024-11-10', 6)": 0, + "(791, '2024-11-10', 7)": 0, + "(791, '2024-11-11', 0)": 1, + "(791, '2024-11-11', 1)": 0, + "(791, '2024-11-11', 2)": 0, + "(791, '2024-11-11', 3)": 0, + "(791, '2024-11-11', 4)": 0, + "(791, '2024-11-11', 5)": 0, + "(791, '2024-11-11', 6)": 0, + "(791, '2024-11-11', 7)": 0, + "(791, '2024-11-12', 0)": 1, + "(791, '2024-11-12', 1)": 0, + "(791, '2024-11-12', 2)": 0, + "(791, '2024-11-12', 3)": 0, + "(791, '2024-11-12', 4)": 0, + "(791, '2024-11-12', 5)": 0, + "(791, '2024-11-12', 6)": 0, + "(791, '2024-11-12', 7)": 0, + "(791, '2024-11-13', 0)": 1, + "(791, '2024-11-13', 1)": 0, + "(791, '2024-11-13', 2)": 0, + "(791, '2024-11-13', 3)": 0, + "(791, '2024-11-13', 4)": 0, + "(791, '2024-11-13', 5)": 0, + "(791, '2024-11-13', 6)": 0, + "(791, '2024-11-13', 7)": 0, + "(791, '2024-11-14', 0)": 1, + "(791, '2024-11-14', 1)": 0, + "(791, '2024-11-14', 2)": 0, + "(791, '2024-11-14', 3)": 0, + "(791, '2024-11-14', 4)": 0, + "(791, '2024-11-14', 5)": 0, + "(791, '2024-11-14', 6)": 0, + "(791, '2024-11-14', 7)": 0, + "(791, '2024-11-15', 0)": 1, + "(791, '2024-11-15', 1)": 0, + "(791, '2024-11-15', 2)": 0, + "(791, '2024-11-15', 3)": 0, + "(791, '2024-11-15', 4)": 0, + "(791, '2024-11-15', 5)": 0, + "(791, '2024-11-15', 6)": 0, + "(791, '2024-11-15', 7)": 0, + "(791, '2024-11-16', 0)": 0, + "(791, '2024-11-16', 1)": 0, + "(791, '2024-11-16', 2)": 0, + "(791, '2024-11-16', 3)": 0, + "(791, '2024-11-16', 4)": 0, + "(791, '2024-11-16', 5)": 0, + "(791, '2024-11-16', 6)": 0, + "(791, '2024-11-16', 7)": 0, + "(791, '2024-11-17', 0)": 1, + "(791, '2024-11-17', 1)": 0, + "(791, '2024-11-17', 2)": 0, + "(791, '2024-11-17', 3)": 0, + "(791, '2024-11-17', 4)": 0, + "(791, '2024-11-17', 5)": 0, + "(791, '2024-11-17', 6)": 0, + "(791, '2024-11-17', 7)": 0, + "(791, '2024-11-18', 0)": 0, + "(791, '2024-11-18', 1)": 0, + "(791, '2024-11-18', 2)": 0, + "(791, '2024-11-18', 3)": 0, + "(791, '2024-11-18', 4)": 0, + "(791, '2024-11-18', 5)": 0, + "(791, '2024-11-18', 6)": 0, + "(791, '2024-11-18', 7)": 0, + "(791, '2024-11-19', 0)": 1, + "(791, '2024-11-19', 1)": 0, + "(791, '2024-11-19', 2)": 0, + "(791, '2024-11-19', 3)": 0, + "(791, '2024-11-19', 4)": 0, + "(791, '2024-11-19', 5)": 0, + "(791, '2024-11-19', 6)": 0, + "(791, '2024-11-19', 7)": 0, + "(791, '2024-11-20', 0)": 1, + "(791, '2024-11-20', 1)": 0, + "(791, '2024-11-20', 2)": 0, + "(791, '2024-11-20', 3)": 0, + "(791, '2024-11-20', 4)": 0, + "(791, '2024-11-20', 5)": 0, + "(791, '2024-11-20', 6)": 0, + "(791, '2024-11-20', 7)": 0, + "(791, '2024-11-21', 0)": 0, + "(791, '2024-11-21', 1)": 0, + "(791, '2024-11-21', 2)": 0, + "(791, '2024-11-21', 3)": 0, + "(791, '2024-11-21', 4)": 0, + "(791, '2024-11-21', 5)": 0, + "(791, '2024-11-21', 6)": 0, + "(791, '2024-11-21', 7)": 0, + "(791, '2024-11-22', 0)": 0, + "(791, '2024-11-22', 1)": 0, + "(791, '2024-11-22', 2)": 0, + "(791, '2024-11-22', 3)": 0, + "(791, '2024-11-22', 4)": 0, + "(791, '2024-11-22', 5)": 0, + "(791, '2024-11-22', 6)": 0, + "(791, '2024-11-22', 7)": 0, + "(791, '2024-11-23', 0)": 0, + "(791, '2024-11-23', 1)": 0, + "(791, '2024-11-23', 2)": 1, + "(791, '2024-11-23', 3)": 0, + "(791, '2024-11-23', 4)": 0, + "(791, '2024-11-23', 5)": 0, + "(791, '2024-11-23', 6)": 0, + "(791, '2024-11-23', 7)": 0, + "(791, '2024-11-24', 0)": 0, + "(791, '2024-11-24', 1)": 0, + "(791, '2024-11-24', 2)": 0, + "(791, '2024-11-24', 3)": 0, + "(791, '2024-11-24', 4)": 0, + "(791, '2024-11-24', 5)": 0, + "(791, '2024-11-24', 6)": 0, + "(791, '2024-11-24', 7)": 0, + "(791, '2024-11-25', 0)": 0, + "(791, '2024-11-25', 1)": 0, + "(791, '2024-11-25', 2)": 0, + "(791, '2024-11-25', 3)": 0, + "(791, '2024-11-25', 4)": 0, + "(791, '2024-11-25', 5)": 0, + "(791, '2024-11-25', 6)": 0, + "(791, '2024-11-25', 7)": 0, + "(791, '2024-11-26', 0)": 1, + "(791, '2024-11-26', 1)": 0, + "(791, '2024-11-26', 2)": 0, + "(791, '2024-11-26', 3)": 0, + "(791, '2024-11-26', 4)": 0, + "(791, '2024-11-26', 5)": 0, + "(791, '2024-11-26', 6)": 0, + "(791, '2024-11-26', 7)": 0, + "(791, '2024-11-27', 0)": 1, + "(791, '2024-11-27', 1)": 0, + "(791, '2024-11-27', 2)": 0, + "(791, '2024-11-27', 3)": 0, + "(791, '2024-11-27', 4)": 0, + "(791, '2024-11-27', 5)": 0, + "(791, '2024-11-27', 6)": 0, + "(791, '2024-11-27', 7)": 0, + "(791, '2024-11-28', 0)": 0, + "(791, '2024-11-28', 1)": 0, + "(791, '2024-11-28', 2)": 1, + "(791, '2024-11-28', 3)": 0, + "(791, '2024-11-28', 4)": 0, + "(791, '2024-11-28', 5)": 0, + "(791, '2024-11-28', 6)": 0, + "(791, '2024-11-28', 7)": 0, + "(791, '2024-11-29', 0)": 0, + "(791, '2024-11-29', 1)": 0, + "(791, '2024-11-29', 2)": 0, + "(791, '2024-11-29', 3)": 0, + "(791, '2024-11-29', 4)": 0, + "(791, '2024-11-29', 5)": 0, + "(791, '2024-11-29', 6)": 0, + "(791, '2024-11-29', 7)": 0, + "(791, '2024-11-30', 0)": 0, + "(791, '2024-11-30', 1)": 0, + "(791, '2024-11-30', 2)": 0, + "(791, '2024-11-30', 3)": 0, + "(791, '2024-11-30', 4)": 0, + "(791, '2024-11-30', 5)": 0, + "(791, '2024-11-30', 6)": 0, + "(791, '2024-11-30', 7)": 0, + "(7919, '2024-11-01', 0)": 0, + "(7919, '2024-11-01', 1)": 0, + "(7919, '2024-11-01', 2)": 0, + "(7919, '2024-11-01', 3)": 0, + "(7919, '2024-11-01', 4)": 0, + "(7919, '2024-11-01', 5)": 0, + "(7919, '2024-11-01', 6)": 0, + "(7919, '2024-11-01', 7)": 0, + "(7919, '2024-11-02', 0)": 1, + "(7919, '2024-11-02', 1)": 0, + "(7919, '2024-11-02', 2)": 0, + "(7919, '2024-11-02', 3)": 0, + "(7919, '2024-11-02', 4)": 0, + "(7919, '2024-11-02', 5)": 0, + "(7919, '2024-11-02', 6)": 0, + "(7919, '2024-11-02', 7)": 0, + "(7919, '2024-11-03', 0)": 0, + "(7919, '2024-11-03', 1)": 0, + "(7919, '2024-11-03', 2)": 1, + "(7919, '2024-11-03', 3)": 0, + "(7919, '2024-11-03', 4)": 0, + "(7919, '2024-11-03', 5)": 0, + "(7919, '2024-11-03', 6)": 0, + "(7919, '2024-11-03', 7)": 0, + "(7919, '2024-11-04', 0)": 0, + "(7919, '2024-11-04', 1)": 1, + "(7919, '2024-11-04', 2)": 0, + "(7919, '2024-11-04', 3)": 0, + "(7919, '2024-11-04', 4)": 0, + "(7919, '2024-11-04', 5)": 0, + "(7919, '2024-11-04', 6)": 0, + "(7919, '2024-11-04', 7)": 0, + "(7919, '2024-11-05', 0)": 1, + "(7919, '2024-11-05', 1)": 0, + "(7919, '2024-11-05', 2)": 0, + "(7919, '2024-11-05', 3)": 0, + "(7919, '2024-11-05', 4)": 0, + "(7919, '2024-11-05', 5)": 0, + "(7919, '2024-11-05', 6)": 0, + "(7919, '2024-11-05', 7)": 0, + "(7919, '2024-11-06', 0)": 0, + "(7919, '2024-11-06', 1)": 0, + "(7919, '2024-11-06', 2)": 0, + "(7919, '2024-11-06', 3)": 0, + "(7919, '2024-11-06', 4)": 0, + "(7919, '2024-11-06', 5)": 0, + "(7919, '2024-11-06', 6)": 0, + "(7919, '2024-11-06', 7)": 0, + "(7919, '2024-11-07', 0)": 0, + "(7919, '2024-11-07', 1)": 0, + "(7919, '2024-11-07', 2)": 1, + "(7919, '2024-11-07', 3)": 0, + "(7919, '2024-11-07', 4)": 0, + "(7919, '2024-11-07', 5)": 0, + "(7919, '2024-11-07', 6)": 0, + "(7919, '2024-11-07', 7)": 0, + "(7919, '2024-11-08', 0)": 0, + "(7919, '2024-11-08', 1)": 0, + "(7919, '2024-11-08', 2)": 1, + "(7919, '2024-11-08', 3)": 0, + "(7919, '2024-11-08', 4)": 0, + "(7919, '2024-11-08', 5)": 0, + "(7919, '2024-11-08', 6)": 0, + "(7919, '2024-11-08', 7)": 0, + "(7919, '2024-11-09', 0)": 0, + "(7919, '2024-11-09', 1)": 0, + "(7919, '2024-11-09', 2)": 0, + "(7919, '2024-11-09', 3)": 0, + "(7919, '2024-11-09', 4)": 0, + "(7919, '2024-11-09', 5)": 0, + "(7919, '2024-11-09', 6)": 0, + "(7919, '2024-11-09', 7)": 0, + "(7919, '2024-11-10', 0)": 0, + "(7919, '2024-11-10', 1)": 0, + "(7919, '2024-11-10', 2)": 1, + "(7919, '2024-11-10', 3)": 0, + "(7919, '2024-11-10', 4)": 0, + "(7919, '2024-11-10', 5)": 0, + "(7919, '2024-11-10', 6)": 0, + "(7919, '2024-11-10', 7)": 0, + "(7919, '2024-11-11', 0)": 0, + "(7919, '2024-11-11', 1)": 0, + "(7919, '2024-11-11', 2)": 1, + "(7919, '2024-11-11', 3)": 0, + "(7919, '2024-11-11', 4)": 0, + "(7919, '2024-11-11', 5)": 0, + "(7919, '2024-11-11', 6)": 0, + "(7919, '2024-11-11', 7)": 0, + "(7919, '2024-11-12', 0)": 0, + "(7919, '2024-11-12', 1)": 0, + "(7919, '2024-11-12', 2)": 0, + "(7919, '2024-11-12', 3)": 0, + "(7919, '2024-11-12', 4)": 0, + "(7919, '2024-11-12', 5)": 0, + "(7919, '2024-11-12', 6)": 0, + "(7919, '2024-11-12', 7)": 0, + "(7919, '2024-11-13', 0)": 0, + "(7919, '2024-11-13', 1)": 0, + "(7919, '2024-11-13', 2)": 0, + "(7919, '2024-11-13', 3)": 0, + "(7919, '2024-11-13', 4)": 0, + "(7919, '2024-11-13', 5)": 0, + "(7919, '2024-11-13', 6)": 0, + "(7919, '2024-11-13', 7)": 0, + "(7919, '2024-11-14', 0)": 0, + "(7919, '2024-11-14', 1)": 0, + "(7919, '2024-11-14', 2)": 0, + "(7919, '2024-11-14', 3)": 0, + "(7919, '2024-11-14', 4)": 0, + "(7919, '2024-11-14', 5)": 0, + "(7919, '2024-11-14', 6)": 0, + "(7919, '2024-11-14', 7)": 0, + "(7919, '2024-11-15', 0)": 1, + "(7919, '2024-11-15', 1)": 0, + "(7919, '2024-11-15', 2)": 0, + "(7919, '2024-11-15', 3)": 0, + "(7919, '2024-11-15', 4)": 0, + "(7919, '2024-11-15', 5)": 0, + "(7919, '2024-11-15', 6)": 0, + "(7919, '2024-11-15', 7)": 0, + "(7919, '2024-11-16', 0)": 0, + "(7919, '2024-11-16', 1)": 0, + "(7919, '2024-11-16', 2)": 1, + "(7919, '2024-11-16', 3)": 0, + "(7919, '2024-11-16', 4)": 0, + "(7919, '2024-11-16', 5)": 0, + "(7919, '2024-11-16', 6)": 0, + "(7919, '2024-11-16', 7)": 0, + "(7919, '2024-11-17', 0)": 0, + "(7919, '2024-11-17', 1)": 0, + "(7919, '2024-11-17', 2)": 1, + "(7919, '2024-11-17', 3)": 0, + "(7919, '2024-11-17', 4)": 0, + "(7919, '2024-11-17', 5)": 0, + "(7919, '2024-11-17', 6)": 0, + "(7919, '2024-11-17', 7)": 0, + "(7919, '2024-11-18', 0)": 0, + "(7919, '2024-11-18', 1)": 0, + "(7919, '2024-11-18', 2)": 0, + "(7919, '2024-11-18', 3)": 1, + "(7919, '2024-11-18', 4)": 0, + "(7919, '2024-11-18', 5)": 0, + "(7919, '2024-11-18', 6)": 0, + "(7919, '2024-11-18', 7)": 0, + "(7919, '2024-11-19', 0)": 0, + "(7919, '2024-11-19', 1)": 0, + "(7919, '2024-11-19', 2)": 0, + "(7919, '2024-11-19', 3)": 0, + "(7919, '2024-11-19', 4)": 0, + "(7919, '2024-11-19', 5)": 0, + "(7919, '2024-11-19', 6)": 0, + "(7919, '2024-11-19', 7)": 0, + "(7919, '2024-11-20', 0)": 1, + "(7919, '2024-11-20', 1)": 0, + "(7919, '2024-11-20', 2)": 0, + "(7919, '2024-11-20', 3)": 0, + "(7919, '2024-11-20', 4)": 0, + "(7919, '2024-11-20', 5)": 0, + "(7919, '2024-11-20', 6)": 0, + "(7919, '2024-11-20', 7)": 0, + "(7919, '2024-11-21', 0)": 0, + "(7919, '2024-11-21', 1)": 0, + "(7919, '2024-11-21', 2)": 0, + "(7919, '2024-11-21', 3)": 1, + "(7919, '2024-11-21', 4)": 0, + "(7919, '2024-11-21', 5)": 0, + "(7919, '2024-11-21', 6)": 0, + "(7919, '2024-11-21', 7)": 0, + "(7919, '2024-11-22', 0)": 0, + "(7919, '2024-11-22', 1)": 0, + "(7919, '2024-11-22', 2)": 0, + "(7919, '2024-11-22', 3)": 0, + "(7919, '2024-11-22', 4)": 0, + "(7919, '2024-11-22', 5)": 0, + "(7919, '2024-11-22', 6)": 0, + "(7919, '2024-11-22', 7)": 0, + "(7919, '2024-11-23', 0)": 0, + "(7919, '2024-11-23', 1)": 0, + "(7919, '2024-11-23', 2)": 0, + "(7919, '2024-11-23', 3)": 1, + "(7919, '2024-11-23', 4)": 0, + "(7919, '2024-11-23', 5)": 0, + "(7919, '2024-11-23', 6)": 0, + "(7919, '2024-11-23', 7)": 0, + "(7919, '2024-11-24', 0)": 0, + "(7919, '2024-11-24', 1)": 0, + "(7919, '2024-11-24', 2)": 0, + "(7919, '2024-11-24', 3)": 0, + "(7919, '2024-11-24', 4)": 0, + "(7919, '2024-11-24', 5)": 0, + "(7919, '2024-11-24', 6)": 0, + "(7919, '2024-11-24', 7)": 0, + "(7919, '2024-11-25', 0)": 0, + "(7919, '2024-11-25', 1)": 0, + "(7919, '2024-11-25', 2)": 1, + "(7919, '2024-11-25', 3)": 0, + "(7919, '2024-11-25', 4)": 0, + "(7919, '2024-11-25', 5)": 0, + "(7919, '2024-11-25', 6)": 0, + "(7919, '2024-11-25', 7)": 0, + "(7919, '2024-11-26', 0)": 0, + "(7919, '2024-11-26', 1)": 0, + "(7919, '2024-11-26', 2)": 1, + "(7919, '2024-11-26', 3)": 0, + "(7919, '2024-11-26', 4)": 0, + "(7919, '2024-11-26', 5)": 0, + "(7919, '2024-11-26', 6)": 0, + "(7919, '2024-11-26', 7)": 0, + "(7919, '2024-11-27', 0)": 0, + "(7919, '2024-11-27', 1)": 0, + "(7919, '2024-11-27', 2)": 0, + "(7919, '2024-11-27', 3)": 1, + "(7919, '2024-11-27', 4)": 0, + "(7919, '2024-11-27', 5)": 0, + "(7919, '2024-11-27', 6)": 0, + "(7919, '2024-11-27', 7)": 0, + "(7919, '2024-11-28', 0)": 0, + "(7919, '2024-11-28', 1)": 0, + "(7919, '2024-11-28', 2)": 0, + "(7919, '2024-11-28', 3)": 0, + "(7919, '2024-11-28', 4)": 0, + "(7919, '2024-11-28', 5)": 0, + "(7919, '2024-11-28', 6)": 0, + "(7919, '2024-11-28', 7)": 0, + "(7919, '2024-11-29', 0)": 0, + "(7919, '2024-11-29', 1)": 0, + "(7919, '2024-11-29', 2)": 1, + "(7919, '2024-11-29', 3)": 0, + "(7919, '2024-11-29', 4)": 0, + "(7919, '2024-11-29', 5)": 0, + "(7919, '2024-11-29', 6)": 0, + "(7919, '2024-11-29', 7)": 0, + "(7919, '2024-11-30', 0)": 0, + "(7919, '2024-11-30', 1)": 0, + "(7919, '2024-11-30', 2)": 0, + "(7919, '2024-11-30', 3)": 0, + "(7919, '2024-11-30', 4)": 0, + "(7919, '2024-11-30', 5)": 0, + "(7919, '2024-11-30', 6)": 0, + "(7919, '2024-11-30', 7)": 0, + "(7990, '2024-11-01', 0)": 0, + "(7990, '2024-11-01', 1)": 0, + "(7990, '2024-11-01', 2)": 0, + "(7990, '2024-11-01', 3)": 0, + "(7990, '2024-11-01', 4)": 0, + "(7990, '2024-11-01', 5)": 0, + "(7990, '2024-11-01', 6)": 0, + "(7990, '2024-11-01', 7)": 0, + "(7990, '2024-11-02', 0)": 0, + "(7990, '2024-11-02', 1)": 0, + "(7990, '2024-11-02', 2)": 0, + "(7990, '2024-11-02', 3)": 0, + "(7990, '2024-11-02', 4)": 0, + "(7990, '2024-11-02', 5)": 0, + "(7990, '2024-11-02', 6)": 0, + "(7990, '2024-11-02', 7)": 0, + "(7990, '2024-11-03', 0)": 0, + "(7990, '2024-11-03', 1)": 0, + "(7990, '2024-11-03', 2)": 0, + "(7990, '2024-11-03', 3)": 0, + "(7990, '2024-11-03', 4)": 0, + "(7990, '2024-11-03', 5)": 0, + "(7990, '2024-11-03', 6)": 0, + "(7990, '2024-11-03', 7)": 0, + "(7990, '2024-11-04', 0)": 0, + "(7990, '2024-11-04', 1)": 0, + "(7990, '2024-11-04', 2)": 0, + "(7990, '2024-11-04', 3)": 0, + "(7990, '2024-11-04', 4)": 0, + "(7990, '2024-11-04', 5)": 0, + "(7990, '2024-11-04', 6)": 0, + "(7990, '2024-11-04', 7)": 0, + "(7990, '2024-11-05', 0)": 0, + "(7990, '2024-11-05', 1)": 0, + "(7990, '2024-11-05', 2)": 0, + "(7990, '2024-11-05', 3)": 0, + "(7990, '2024-11-05', 4)": 0, + "(7990, '2024-11-05', 5)": 0, + "(7990, '2024-11-05', 6)": 0, + "(7990, '2024-11-05', 7)": 0, + "(7990, '2024-11-06', 0)": 0, + "(7990, '2024-11-06', 1)": 0, + "(7990, '2024-11-06', 2)": 0, + "(7990, '2024-11-06', 3)": 0, + "(7990, '2024-11-06', 4)": 0, + "(7990, '2024-11-06', 5)": 0, + "(7990, '2024-11-06', 6)": 0, + "(7990, '2024-11-06', 7)": 0, + "(7990, '2024-11-07', 0)": 0, + "(7990, '2024-11-07', 1)": 0, + "(7990, '2024-11-07', 2)": 0, + "(7990, '2024-11-07', 3)": 0, + "(7990, '2024-11-07', 4)": 0, + "(7990, '2024-11-07', 5)": 0, + "(7990, '2024-11-07', 6)": 0, + "(7990, '2024-11-07', 7)": 0, + "(7990, '2024-11-08', 0)": 0, + "(7990, '2024-11-08', 1)": 0, + "(7990, '2024-11-08', 2)": 0, + "(7990, '2024-11-08', 3)": 0, + "(7990, '2024-11-08', 4)": 0, + "(7990, '2024-11-08', 5)": 0, + "(7990, '2024-11-08', 6)": 0, + "(7990, '2024-11-08', 7)": 0, + "(7990, '2024-11-09', 0)": 0, + "(7990, '2024-11-09', 1)": 0, + "(7990, '2024-11-09', 2)": 0, + "(7990, '2024-11-09', 3)": 0, + "(7990, '2024-11-09', 4)": 0, + "(7990, '2024-11-09', 5)": 0, + "(7990, '2024-11-09', 6)": 0, + "(7990, '2024-11-09', 7)": 0, + "(7990, '2024-11-10', 0)": 0, + "(7990, '2024-11-10', 1)": 0, + "(7990, '2024-11-10', 2)": 0, + "(7990, '2024-11-10', 3)": 0, + "(7990, '2024-11-10', 4)": 0, + "(7990, '2024-11-10', 5)": 0, + "(7990, '2024-11-10', 6)": 0, + "(7990, '2024-11-10', 7)": 0, + "(7990, '2024-11-11', 0)": 0, + "(7990, '2024-11-11', 1)": 0, + "(7990, '2024-11-11', 2)": 0, + "(7990, '2024-11-11', 3)": 0, + "(7990, '2024-11-11', 4)": 0, + "(7990, '2024-11-11', 5)": 0, + "(7990, '2024-11-11', 6)": 0, + "(7990, '2024-11-11', 7)": 0, + "(7990, '2024-11-12', 0)": 0, + "(7990, '2024-11-12', 1)": 0, + "(7990, '2024-11-12', 2)": 0, + "(7990, '2024-11-12', 3)": 0, + "(7990, '2024-11-12', 4)": 0, + "(7990, '2024-11-12', 5)": 0, + "(7990, '2024-11-12', 6)": 0, + "(7990, '2024-11-12', 7)": 0, + "(7990, '2024-11-13', 0)": 0, + "(7990, '2024-11-13', 1)": 0, + "(7990, '2024-11-13', 2)": 0, + "(7990, '2024-11-13', 3)": 0, + "(7990, '2024-11-13', 4)": 0, + "(7990, '2024-11-13', 5)": 0, + "(7990, '2024-11-13', 6)": 0, + "(7990, '2024-11-13', 7)": 0, + "(7990, '2024-11-14', 0)": 0, + "(7990, '2024-11-14', 1)": 0, + "(7990, '2024-11-14', 2)": 0, + "(7990, '2024-11-14', 3)": 0, + "(7990, '2024-11-14', 4)": 0, + "(7990, '2024-11-14', 5)": 0, + "(7990, '2024-11-14', 6)": 0, + "(7990, '2024-11-14', 7)": 0, + "(7990, '2024-11-15', 0)": 0, + "(7990, '2024-11-15', 1)": 0, + "(7990, '2024-11-15', 2)": 0, + "(7990, '2024-11-15', 3)": 0, + "(7990, '2024-11-15', 4)": 0, + "(7990, '2024-11-15', 5)": 0, + "(7990, '2024-11-15', 6)": 0, + "(7990, '2024-11-15', 7)": 0, + "(7990, '2024-11-16', 0)": 0, + "(7990, '2024-11-16', 1)": 0, + "(7990, '2024-11-16', 2)": 0, + "(7990, '2024-11-16', 3)": 0, + "(7990, '2024-11-16', 4)": 0, + "(7990, '2024-11-16', 5)": 0, + "(7990, '2024-11-16', 6)": 0, + "(7990, '2024-11-16', 7)": 0, + "(7990, '2024-11-17', 0)": 0, + "(7990, '2024-11-17', 1)": 0, + "(7990, '2024-11-17', 2)": 0, + "(7990, '2024-11-17', 3)": 0, + "(7990, '2024-11-17', 4)": 0, + "(7990, '2024-11-17', 5)": 0, + "(7990, '2024-11-17', 6)": 0, + "(7990, '2024-11-17', 7)": 0, + "(7990, '2024-11-18', 0)": 0, + "(7990, '2024-11-18', 1)": 0, + "(7990, '2024-11-18', 2)": 0, + "(7990, '2024-11-18', 3)": 0, + "(7990, '2024-11-18', 4)": 0, + "(7990, '2024-11-18', 5)": 0, + "(7990, '2024-11-18', 6)": 0, + "(7990, '2024-11-18', 7)": 0, + "(7990, '2024-11-19', 0)": 0, + "(7990, '2024-11-19', 1)": 0, + "(7990, '2024-11-19', 2)": 0, + "(7990, '2024-11-19', 3)": 0, + "(7990, '2024-11-19', 4)": 0, + "(7990, '2024-11-19', 5)": 0, + "(7990, '2024-11-19', 6)": 0, + "(7990, '2024-11-19', 7)": 0, + "(7990, '2024-11-20', 0)": 0, + "(7990, '2024-11-20', 1)": 0, + "(7990, '2024-11-20', 2)": 0, + "(7990, '2024-11-20', 3)": 0, + "(7990, '2024-11-20', 4)": 0, + "(7990, '2024-11-20', 5)": 0, + "(7990, '2024-11-20', 6)": 0, + "(7990, '2024-11-20', 7)": 0, + "(7990, '2024-11-21', 0)": 0, + "(7990, '2024-11-21', 1)": 0, + "(7990, '2024-11-21', 2)": 0, + "(7990, '2024-11-21', 3)": 0, + "(7990, '2024-11-21', 4)": 0, + "(7990, '2024-11-21', 5)": 0, + "(7990, '2024-11-21', 6)": 0, + "(7990, '2024-11-21', 7)": 0, + "(7990, '2024-11-22', 0)": 0, + "(7990, '2024-11-22', 1)": 0, + "(7990, '2024-11-22', 2)": 0, + "(7990, '2024-11-22', 3)": 0, + "(7990, '2024-11-22', 4)": 0, + "(7990, '2024-11-22', 5)": 0, + "(7990, '2024-11-22', 6)": 0, + "(7990, '2024-11-22', 7)": 0, + "(7990, '2024-11-23', 0)": 0, + "(7990, '2024-11-23', 1)": 0, + "(7990, '2024-11-23', 2)": 0, + "(7990, '2024-11-23', 3)": 0, + "(7990, '2024-11-23', 4)": 0, + "(7990, '2024-11-23', 5)": 0, + "(7990, '2024-11-23', 6)": 0, + "(7990, '2024-11-23', 7)": 0, + "(7990, '2024-11-24', 0)": 0, + "(7990, '2024-11-24', 1)": 0, + "(7990, '2024-11-24', 2)": 0, + "(7990, '2024-11-24', 3)": 0, + "(7990, '2024-11-24', 4)": 0, + "(7990, '2024-11-24', 5)": 0, + "(7990, '2024-11-24', 6)": 0, + "(7990, '2024-11-24', 7)": 0, + "(7990, '2024-11-25', 0)": 0, + "(7990, '2024-11-25', 1)": 0, + "(7990, '2024-11-25', 2)": 0, + "(7990, '2024-11-25', 3)": 0, + "(7990, '2024-11-25', 4)": 0, + "(7990, '2024-11-25', 5)": 0, + "(7990, '2024-11-25', 6)": 0, + "(7990, '2024-11-25', 7)": 0, + "(7990, '2024-11-26', 0)": 0, + "(7990, '2024-11-26', 1)": 0, + "(7990, '2024-11-26', 2)": 0, + "(7990, '2024-11-26', 3)": 0, + "(7990, '2024-11-26', 4)": 0, + "(7990, '2024-11-26', 5)": 0, + "(7990, '2024-11-26', 6)": 0, + "(7990, '2024-11-26', 7)": 0, + "(7990, '2024-11-27', 0)": 0, + "(7990, '2024-11-27', 1)": 0, + "(7990, '2024-11-27', 2)": 0, + "(7990, '2024-11-27', 3)": 0, + "(7990, '2024-11-27', 4)": 0, + "(7990, '2024-11-27', 5)": 0, + "(7990, '2024-11-27', 6)": 0, + "(7990, '2024-11-27', 7)": 0, + "(7990, '2024-11-28', 0)": 0, + "(7990, '2024-11-28', 1)": 0, + "(7990, '2024-11-28', 2)": 0, + "(7990, '2024-11-28', 3)": 0, + "(7990, '2024-11-28', 4)": 0, + "(7990, '2024-11-28', 5)": 0, + "(7990, '2024-11-28', 6)": 0, + "(7990, '2024-11-28', 7)": 0, + "(7990, '2024-11-29', 0)": 0, + "(7990, '2024-11-29', 1)": 0, + "(7990, '2024-11-29', 2)": 0, + "(7990, '2024-11-29', 3)": 0, + "(7990, '2024-11-29', 4)": 0, + "(7990, '2024-11-29', 5)": 0, + "(7990, '2024-11-29', 6)": 0, + "(7990, '2024-11-29', 7)": 0, + "(7990, '2024-11-30', 0)": 0, + "(7990, '2024-11-30', 1)": 0, + "(7990, '2024-11-30', 2)": 0, + "(7990, '2024-11-30', 3)": 0, + "(7990, '2024-11-30', 4)": 0, + "(7990, '2024-11-30', 5)": 0, + "(7990, '2024-11-30', 6)": 0, + "(7990, '2024-11-30', 7)": 0, + "(8, '2024-11-01', 0)": 0, + "(8, '2024-11-01', 1)": 0, + "(8, '2024-11-01', 2)": 0, + "(8, '2024-11-01', 3)": 0, + "(8, '2024-11-01', 4)": 0, + "(8, '2024-11-01', 5)": 0, + "(8, '2024-11-01', 6)": 0, + "(8, '2024-11-01', 7)": 0, + "(8, '2024-11-02', 0)": 0, + "(8, '2024-11-02', 1)": 0, + "(8, '2024-11-02', 2)": 0, + "(8, '2024-11-02', 3)": 0, + "(8, '2024-11-02', 4)": 0, + "(8, '2024-11-02', 5)": 0, + "(8, '2024-11-02', 6)": 0, + "(8, '2024-11-02', 7)": 0, + "(8, '2024-11-03', 0)": 0, + "(8, '2024-11-03', 1)": 0, + "(8, '2024-11-03', 2)": 0, + "(8, '2024-11-03', 3)": 0, + "(8, '2024-11-03', 4)": 0, + "(8, '2024-11-03', 5)": 0, + "(8, '2024-11-03', 6)": 0, + "(8, '2024-11-03', 7)": 0, + "(8, '2024-11-04', 0)": 1, + "(8, '2024-11-04', 1)": 0, + "(8, '2024-11-04', 2)": 0, + "(8, '2024-11-04', 3)": 0, + "(8, '2024-11-04', 4)": 0, + "(8, '2024-11-04', 5)": 0, + "(8, '2024-11-04', 6)": 0, + "(8, '2024-11-04', 7)": 0, + "(8, '2024-11-05', 0)": 1, + "(8, '2024-11-05', 1)": 0, + "(8, '2024-11-05', 2)": 0, + "(8, '2024-11-05', 3)": 0, + "(8, '2024-11-05', 4)": 0, + "(8, '2024-11-05', 5)": 0, + "(8, '2024-11-05', 6)": 0, + "(8, '2024-11-05', 7)": 0, + "(8, '2024-11-06', 0)": 0, + "(8, '2024-11-06', 1)": 0, + "(8, '2024-11-06', 2)": 0, + "(8, '2024-11-06', 3)": 0, + "(8, '2024-11-06', 4)": 0, + "(8, '2024-11-06', 5)": 0, + "(8, '2024-11-06', 6)": 0, + "(8, '2024-11-06', 7)": 0, + "(8, '2024-11-07', 0)": 0, + "(8, '2024-11-07', 1)": 0, + "(8, '2024-11-07', 2)": 0, + "(8, '2024-11-07', 3)": 0, + "(8, '2024-11-07', 4)": 0, + "(8, '2024-11-07', 5)": 0, + "(8, '2024-11-07', 6)": 0, + "(8, '2024-11-07', 7)": 0, + "(8, '2024-11-08', 0)": 0, + "(8, '2024-11-08', 1)": 0, + "(8, '2024-11-08', 2)": 0, + "(8, '2024-11-08', 3)": 0, + "(8, '2024-11-08', 4)": 0, + "(8, '2024-11-08', 5)": 0, + "(8, '2024-11-08', 6)": 0, + "(8, '2024-11-08', 7)": 0, + "(8, '2024-11-09', 0)": 0, + "(8, '2024-11-09', 1)": 0, + "(8, '2024-11-09', 2)": 0, + "(8, '2024-11-09', 3)": 1, + "(8, '2024-11-09', 4)": 0, + "(8, '2024-11-09', 5)": 0, + "(8, '2024-11-09', 6)": 0, + "(8, '2024-11-09', 7)": 0, + "(8, '2024-11-10', 0)": 0, + "(8, '2024-11-10', 1)": 0, + "(8, '2024-11-10', 2)": 0, + "(8, '2024-11-10', 3)": 1, + "(8, '2024-11-10', 4)": 0, + "(8, '2024-11-10', 5)": 0, + "(8, '2024-11-10', 6)": 0, + "(8, '2024-11-10', 7)": 0, + "(8, '2024-11-11', 0)": 0, + "(8, '2024-11-11', 1)": 0, + "(8, '2024-11-11', 2)": 0, + "(8, '2024-11-11', 3)": 0, + "(8, '2024-11-11', 4)": 0, + "(8, '2024-11-11', 5)": 0, + "(8, '2024-11-11', 6)": 0, + "(8, '2024-11-11', 7)": 0, + "(8, '2024-11-12', 0)": 0, + "(8, '2024-11-12', 1)": 0, + "(8, '2024-11-12', 2)": 0, + "(8, '2024-11-12', 3)": 0, + "(8, '2024-11-12', 4)": 0, + "(8, '2024-11-12', 5)": 0, + "(8, '2024-11-12', 6)": 0, + "(8, '2024-11-12', 7)": 0, + "(8, '2024-11-13', 0)": 0, + "(8, '2024-11-13', 1)": 0, + "(8, '2024-11-13', 2)": 0, + "(8, '2024-11-13', 3)": 0, + "(8, '2024-11-13', 4)": 0, + "(8, '2024-11-13', 5)": 0, + "(8, '2024-11-13', 6)": 0, + "(8, '2024-11-13', 7)": 0, + "(8, '2024-11-14', 0)": 0, + "(8, '2024-11-14', 1)": 0, + "(8, '2024-11-14', 2)": 1, + "(8, '2024-11-14', 3)": 0, + "(8, '2024-11-14', 4)": 0, + "(8, '2024-11-14', 5)": 0, + "(8, '2024-11-14', 6)": 0, + "(8, '2024-11-14', 7)": 0, + "(8, '2024-11-15', 0)": 0, + "(8, '2024-11-15', 1)": 0, + "(8, '2024-11-15', 2)": 0, + "(8, '2024-11-15', 3)": 0, + "(8, '2024-11-15', 4)": 0, + "(8, '2024-11-15', 5)": 0, + "(8, '2024-11-15', 6)": 0, + "(8, '2024-11-15', 7)": 0, + "(8, '2024-11-16', 0)": 0, + "(8, '2024-11-16', 1)": 0, + "(8, '2024-11-16', 2)": 1, + "(8, '2024-11-16', 3)": 0, + "(8, '2024-11-16', 4)": 0, + "(8, '2024-11-16', 5)": 0, + "(8, '2024-11-16', 6)": 0, + "(8, '2024-11-16', 7)": 0, + "(8, '2024-11-17', 0)": 0, + "(8, '2024-11-17', 1)": 0, + "(8, '2024-11-17', 2)": 0, + "(8, '2024-11-17', 3)": 0, + "(8, '2024-11-17', 4)": 0, + "(8, '2024-11-17', 5)": 0, + "(8, '2024-11-17', 6)": 0, + "(8, '2024-11-17', 7)": 0, + "(8, '2024-11-18', 0)": 0, + "(8, '2024-11-18', 1)": 0, + "(8, '2024-11-18', 2)": 0, + "(8, '2024-11-18', 3)": 0, + "(8, '2024-11-18', 4)": 0, + "(8, '2024-11-18', 5)": 0, + "(8, '2024-11-18', 6)": 0, + "(8, '2024-11-18', 7)": 0, + "(8, '2024-11-19', 0)": 0, + "(8, '2024-11-19', 1)": 0, + "(8, '2024-11-19', 2)": 0, + "(8, '2024-11-19', 3)": 0, + "(8, '2024-11-19', 4)": 0, + "(8, '2024-11-19', 5)": 0, + "(8, '2024-11-19', 6)": 0, + "(8, '2024-11-19', 7)": 0, + "(8, '2024-11-20', 0)": 1, + "(8, '2024-11-20', 1)": 0, + "(8, '2024-11-20', 2)": 0, + "(8, '2024-11-20', 3)": 0, + "(8, '2024-11-20', 4)": 0, + "(8, '2024-11-20', 5)": 0, + "(8, '2024-11-20', 6)": 0, + "(8, '2024-11-20', 7)": 0, + "(8, '2024-11-21', 0)": 0, + "(8, '2024-11-21', 1)": 0, + "(8, '2024-11-21', 2)": 1, + "(8, '2024-11-21', 3)": 0, + "(8, '2024-11-21', 4)": 0, + "(8, '2024-11-21', 5)": 0, + "(8, '2024-11-21', 6)": 0, + "(8, '2024-11-21', 7)": 0, + "(8, '2024-11-22', 0)": 0, + "(8, '2024-11-22', 1)": 0, + "(8, '2024-11-22', 2)": 0, + "(8, '2024-11-22', 3)": 0, + "(8, '2024-11-22', 4)": 0, + "(8, '2024-11-22', 5)": 0, + "(8, '2024-11-22', 6)": 0, + "(8, '2024-11-22', 7)": 0, + "(8, '2024-11-23', 0)": 0, + "(8, '2024-11-23', 1)": 0, + "(8, '2024-11-23', 2)": 1, + "(8, '2024-11-23', 3)": 0, + "(8, '2024-11-23', 4)": 0, + "(8, '2024-11-23', 5)": 0, + "(8, '2024-11-23', 6)": 0, + "(8, '2024-11-23', 7)": 0, + "(8, '2024-11-24', 0)": 0, + "(8, '2024-11-24', 1)": 0, + "(8, '2024-11-24', 2)": 0, + "(8, '2024-11-24', 3)": 0, + "(8, '2024-11-24', 4)": 0, + "(8, '2024-11-24', 5)": 0, + "(8, '2024-11-24', 6)": 0, + "(8, '2024-11-24', 7)": 0, + "(8, '2024-11-25', 0)": 0, + "(8, '2024-11-25', 1)": 0, + "(8, '2024-11-25', 2)": 0, + "(8, '2024-11-25', 3)": 0, + "(8, '2024-11-25', 4)": 0, + "(8, '2024-11-25', 5)": 0, + "(8, '2024-11-25', 6)": 0, + "(8, '2024-11-25', 7)": 0, + "(8, '2024-11-26', 0)": 0, + "(8, '2024-11-26', 1)": 0, + "(8, '2024-11-26', 2)": 0, + "(8, '2024-11-26', 3)": 0, + "(8, '2024-11-26', 4)": 0, + "(8, '2024-11-26', 5)": 0, + "(8, '2024-11-26', 6)": 0, + "(8, '2024-11-26', 7)": 0, + "(8, '2024-11-27', 0)": 1, + "(8, '2024-11-27', 1)": 0, + "(8, '2024-11-27', 2)": 0, + "(8, '2024-11-27', 3)": 0, + "(8, '2024-11-27', 4)": 0, + "(8, '2024-11-27', 5)": 0, + "(8, '2024-11-27', 6)": 0, + "(8, '2024-11-27', 7)": 0, + "(8, '2024-11-28', 0)": 0, + "(8, '2024-11-28', 1)": 0, + "(8, '2024-11-28', 2)": 0, + "(8, '2024-11-28', 3)": 0, + "(8, '2024-11-28', 4)": 0, + "(8, '2024-11-28', 5)": 0, + "(8, '2024-11-28', 6)": 0, + "(8, '2024-11-28', 7)": 0, + "(8, '2024-11-29', 0)": 0, + "(8, '2024-11-29', 1)": 0, + "(8, '2024-11-29', 2)": 0, + "(8, '2024-11-29', 3)": 0, + "(8, '2024-11-29', 4)": 0, + "(8, '2024-11-29', 5)": 0, + "(8, '2024-11-29', 6)": 0, + "(8, '2024-11-29', 7)": 0, + "(8, '2024-11-30', 0)": 1, + "(8, '2024-11-30', 1)": 0, + "(8, '2024-11-30', 2)": 0, + "(8, '2024-11-30', 3)": 0, + "(8, '2024-11-30', 4)": 0, + "(8, '2024-11-30', 5)": 0, + "(8, '2024-11-30', 6)": 0, + "(8, '2024-11-30', 7)": 0, + "(822, '2024-11-01', 0)": 0, + "(822, '2024-11-01', 1)": 0, + "(822, '2024-11-01', 2)": 0, + "(822, '2024-11-01', 3)": 0, + "(822, '2024-11-01', 4)": 0, + "(822, '2024-11-01', 5)": 0, + "(822, '2024-11-01', 6)": 0, + "(822, '2024-11-01', 7)": 1, + "(822, '2024-11-02', 0)": 0, + "(822, '2024-11-02', 1)": 0, + "(822, '2024-11-02', 2)": 0, + "(822, '2024-11-02', 3)": 0, + "(822, '2024-11-02', 4)": 0, + "(822, '2024-11-02', 5)": 0, + "(822, '2024-11-02', 6)": 0, + "(822, '2024-11-02', 7)": 1, + "(822, '2024-11-03', 0)": 0, + "(822, '2024-11-03', 1)": 0, + "(822, '2024-11-03', 2)": 0, + "(822, '2024-11-03', 3)": 0, + "(822, '2024-11-03', 4)": 0, + "(822, '2024-11-03', 5)": 0, + "(822, '2024-11-03', 6)": 0, + "(822, '2024-11-03', 7)": 1, + "(822, '2024-11-04', 0)": 0, + "(822, '2024-11-04', 1)": 0, + "(822, '2024-11-04', 2)": 0, + "(822, '2024-11-04', 3)": 0, + "(822, '2024-11-04', 4)": 0, + "(822, '2024-11-04', 5)": 0, + "(822, '2024-11-04', 6)": 0, + "(822, '2024-11-04', 7)": 0, + "(822, '2024-11-05', 0)": 0, + "(822, '2024-11-05', 1)": 0, + "(822, '2024-11-05', 2)": 0, + "(822, '2024-11-05', 3)": 0, + "(822, '2024-11-05', 4)": 0, + "(822, '2024-11-05', 5)": 0, + "(822, '2024-11-05', 6)": 0, + "(822, '2024-11-05', 7)": 0, + "(822, '2024-11-06', 0)": 0, + "(822, '2024-11-06', 1)": 0, + "(822, '2024-11-06', 2)": 0, + "(822, '2024-11-06', 3)": 0, + "(822, '2024-11-06', 4)": 0, + "(822, '2024-11-06', 5)": 0, + "(822, '2024-11-06', 6)": 1, + "(822, '2024-11-06', 7)": 0, + "(822, '2024-11-07', 0)": 0, + "(822, '2024-11-07', 1)": 0, + "(822, '2024-11-07', 2)": 0, + "(822, '2024-11-07', 3)": 0, + "(822, '2024-11-07', 4)": 0, + "(822, '2024-11-07', 5)": 0, + "(822, '2024-11-07', 6)": 1, + "(822, '2024-11-07', 7)": 0, + "(822, '2024-11-08', 0)": 0, + "(822, '2024-11-08', 1)": 0, + "(822, '2024-11-08', 2)": 0, + "(822, '2024-11-08', 3)": 0, + "(822, '2024-11-08', 4)": 0, + "(822, '2024-11-08', 5)": 0, + "(822, '2024-11-08', 6)": 1, + "(822, '2024-11-08', 7)": 0, + "(822, '2024-11-09', 0)": 0, + "(822, '2024-11-09', 1)": 0, + "(822, '2024-11-09', 2)": 0, + "(822, '2024-11-09', 3)": 0, + "(822, '2024-11-09', 4)": 0, + "(822, '2024-11-09', 5)": 0, + "(822, '2024-11-09', 6)": 0, + "(822, '2024-11-09', 7)": 0, + "(822, '2024-11-10', 0)": 0, + "(822, '2024-11-10', 1)": 0, + "(822, '2024-11-10', 2)": 0, + "(822, '2024-11-10', 3)": 0, + "(822, '2024-11-10', 4)": 0, + "(822, '2024-11-10', 5)": 0, + "(822, '2024-11-10', 6)": 0, + "(822, '2024-11-10', 7)": 0, + "(822, '2024-11-11', 0)": 0, + "(822, '2024-11-11', 1)": 0, + "(822, '2024-11-11', 2)": 0, + "(822, '2024-11-11', 3)": 0, + "(822, '2024-11-11', 4)": 0, + "(822, '2024-11-11', 5)": 1, + "(822, '2024-11-11', 6)": 0, + "(822, '2024-11-11', 7)": 0, + "(822, '2024-11-12', 0)": 0, + "(822, '2024-11-12', 1)": 0, + "(822, '2024-11-12', 2)": 0, + "(822, '2024-11-12', 3)": 0, + "(822, '2024-11-12', 4)": 0, + "(822, '2024-11-12', 5)": 1, + "(822, '2024-11-12', 6)": 0, + "(822, '2024-11-12', 7)": 0, + "(822, '2024-11-13', 0)": 0, + "(822, '2024-11-13', 1)": 0, + "(822, '2024-11-13', 2)": 0, + "(822, '2024-11-13', 3)": 0, + "(822, '2024-11-13', 4)": 0, + "(822, '2024-11-13', 5)": 1, + "(822, '2024-11-13', 6)": 0, + "(822, '2024-11-13', 7)": 0, + "(822, '2024-11-14', 0)": 0, + "(822, '2024-11-14', 1)": 0, + "(822, '2024-11-14', 2)": 0, + "(822, '2024-11-14', 3)": 0, + "(822, '2024-11-14', 4)": 0, + "(822, '2024-11-14', 5)": 1, + "(822, '2024-11-14', 6)": 0, + "(822, '2024-11-14', 7)": 0, + "(822, '2024-11-15', 0)": 0, + "(822, '2024-11-15', 1)": 0, + "(822, '2024-11-15', 2)": 0, + "(822, '2024-11-15', 3)": 0, + "(822, '2024-11-15', 4)": 0, + "(822, '2024-11-15', 5)": 0, + "(822, '2024-11-15', 6)": 0, + "(822, '2024-11-15', 7)": 0, + "(822, '2024-11-16', 0)": 0, + "(822, '2024-11-16', 1)": 0, + "(822, '2024-11-16', 2)": 0, + "(822, '2024-11-16', 3)": 0, + "(822, '2024-11-16', 4)": 0, + "(822, '2024-11-16', 5)": 0, + "(822, '2024-11-16', 6)": 0, + "(822, '2024-11-16', 7)": 0, + "(822, '2024-11-17', 0)": 0, + "(822, '2024-11-17', 1)": 0, + "(822, '2024-11-17', 2)": 0, + "(822, '2024-11-17', 3)": 0, + "(822, '2024-11-17', 4)": 0, + "(822, '2024-11-17', 5)": 0, + "(822, '2024-11-17', 6)": 0, + "(822, '2024-11-17', 7)": 0, + "(822, '2024-11-18', 0)": 0, + "(822, '2024-11-18', 1)": 0, + "(822, '2024-11-18', 2)": 0, + "(822, '2024-11-18', 3)": 0, + "(822, '2024-11-18', 4)": 0, + "(822, '2024-11-18', 5)": 0, + "(822, '2024-11-18', 6)": 0, + "(822, '2024-11-18', 7)": 0, + "(822, '2024-11-19', 0)": 0, + "(822, '2024-11-19', 1)": 0, + "(822, '2024-11-19', 2)": 0, + "(822, '2024-11-19', 3)": 0, + "(822, '2024-11-19', 4)": 0, + "(822, '2024-11-19', 5)": 0, + "(822, '2024-11-19', 6)": 0, + "(822, '2024-11-19', 7)": 0, + "(822, '2024-11-20', 0)": 0, + "(822, '2024-11-20', 1)": 0, + "(822, '2024-11-20', 2)": 0, + "(822, '2024-11-20', 3)": 0, + "(822, '2024-11-20', 4)": 0, + "(822, '2024-11-20', 5)": 0, + "(822, '2024-11-20', 6)": 1, + "(822, '2024-11-20', 7)": 0, + "(822, '2024-11-21', 0)": 0, + "(822, '2024-11-21', 1)": 0, + "(822, '2024-11-21', 2)": 0, + "(822, '2024-11-21', 3)": 0, + "(822, '2024-11-21', 4)": 0, + "(822, '2024-11-21', 5)": 0, + "(822, '2024-11-21', 6)": 0, + "(822, '2024-11-21', 7)": 1, + "(822, '2024-11-22', 0)": 0, + "(822, '2024-11-22', 1)": 0, + "(822, '2024-11-22', 2)": 0, + "(822, '2024-11-22', 3)": 0, + "(822, '2024-11-22', 4)": 0, + "(822, '2024-11-22', 5)": 0, + "(822, '2024-11-22', 6)": 0, + "(822, '2024-11-22', 7)": 1, + "(822, '2024-11-23', 0)": 0, + "(822, '2024-11-23', 1)": 0, + "(822, '2024-11-23', 2)": 0, + "(822, '2024-11-23', 3)": 0, + "(822, '2024-11-23', 4)": 0, + "(822, '2024-11-23', 5)": 0, + "(822, '2024-11-23', 6)": 0, + "(822, '2024-11-23', 7)": 1, + "(822, '2024-11-24', 0)": 0, + "(822, '2024-11-24', 1)": 0, + "(822, '2024-11-24', 2)": 0, + "(822, '2024-11-24', 3)": 0, + "(822, '2024-11-24', 4)": 0, + "(822, '2024-11-24', 5)": 0, + "(822, '2024-11-24', 6)": 0, + "(822, '2024-11-24', 7)": 1, + "(822, '2024-11-25', 0)": 0, + "(822, '2024-11-25', 1)": 0, + "(822, '2024-11-25', 2)": 0, + "(822, '2024-11-25', 3)": 0, + "(822, '2024-11-25', 4)": 0, + "(822, '2024-11-25', 5)": 0, + "(822, '2024-11-25', 6)": 0, + "(822, '2024-11-25', 7)": 0, + "(822, '2024-11-26', 0)": 0, + "(822, '2024-11-26', 1)": 0, + "(822, '2024-11-26', 2)": 0, + "(822, '2024-11-26', 3)": 0, + "(822, '2024-11-26', 4)": 0, + "(822, '2024-11-26', 5)": 0, + "(822, '2024-11-26', 6)": 0, + "(822, '2024-11-26', 7)": 0, + "(822, '2024-11-27', 0)": 0, + "(822, '2024-11-27', 1)": 0, + "(822, '2024-11-27', 2)": 0, + "(822, '2024-11-27', 3)": 0, + "(822, '2024-11-27', 4)": 0, + "(822, '2024-11-27', 5)": 0, + "(822, '2024-11-27', 6)": 0, + "(822, '2024-11-27', 7)": 0, + "(822, '2024-11-28', 0)": 0, + "(822, '2024-11-28', 1)": 0, + "(822, '2024-11-28', 2)": 1, + "(822, '2024-11-28', 3)": 0, + "(822, '2024-11-28', 4)": 0, + "(822, '2024-11-28', 5)": 0, + "(822, '2024-11-28', 6)": 0, + "(822, '2024-11-28', 7)": 0, + "(822, '2024-11-29', 0)": 0, + "(822, '2024-11-29', 1)": 0, + "(822, '2024-11-29', 2)": 0, + "(822, '2024-11-29', 3)": 0, + "(822, '2024-11-29', 4)": 0, + "(822, '2024-11-29', 5)": 0, + "(822, '2024-11-29', 6)": 0, + "(822, '2024-11-29', 7)": 1, + "(822, '2024-11-30', 0)": 0, + "(822, '2024-11-30', 1)": 0, + "(822, '2024-11-30', 2)": 0, + "(822, '2024-11-30', 3)": 0, + "(822, '2024-11-30', 4)": 0, + "(822, '2024-11-30', 5)": 0, + "(822, '2024-11-30', 6)": 0, + "(822, '2024-11-30', 7)": 1, + "(839, '2024-11-01', 0)": 0, + "(839, '2024-11-01', 1)": 0, + "(839, '2024-11-01', 2)": 0, + "(839, '2024-11-01', 3)": 0, + "(839, '2024-11-01', 4)": 0, + "(839, '2024-11-01', 5)": 0, + "(839, '2024-11-01', 6)": 0, + "(839, '2024-11-01', 7)": 0, + "(839, '2024-11-02', 0)": 0, + "(839, '2024-11-02', 1)": 0, + "(839, '2024-11-02', 2)": 0, + "(839, '2024-11-02', 3)": 0, + "(839, '2024-11-02', 4)": 0, + "(839, '2024-11-02', 5)": 0, + "(839, '2024-11-02', 6)": 0, + "(839, '2024-11-02', 7)": 0, + "(839, '2024-11-03', 0)": 0, + "(839, '2024-11-03', 1)": 0, + "(839, '2024-11-03', 2)": 0, + "(839, '2024-11-03', 3)": 0, + "(839, '2024-11-03', 4)": 0, + "(839, '2024-11-03', 5)": 0, + "(839, '2024-11-03', 6)": 0, + "(839, '2024-11-03', 7)": 0, + "(839, '2024-11-04', 0)": 0, + "(839, '2024-11-04', 1)": 0, + "(839, '2024-11-04', 2)": 0, + "(839, '2024-11-04', 3)": 0, + "(839, '2024-11-04', 4)": 0, + "(839, '2024-11-04', 5)": 0, + "(839, '2024-11-04', 6)": 0, + "(839, '2024-11-04', 7)": 0, + "(839, '2024-11-05', 0)": 0, + "(839, '2024-11-05', 1)": 0, + "(839, '2024-11-05', 2)": 1, + "(839, '2024-11-05', 3)": 0, + "(839, '2024-11-05', 4)": 0, + "(839, '2024-11-05', 5)": 0, + "(839, '2024-11-05', 6)": 0, + "(839, '2024-11-05', 7)": 0, + "(839, '2024-11-06', 0)": 0, + "(839, '2024-11-06', 1)": 0, + "(839, '2024-11-06', 2)": 0, + "(839, '2024-11-06', 3)": 0, + "(839, '2024-11-06', 4)": 0, + "(839, '2024-11-06', 5)": 0, + "(839, '2024-11-06', 6)": 0, + "(839, '2024-11-06', 7)": 0, + "(839, '2024-11-07', 0)": 0, + "(839, '2024-11-07', 1)": 0, + "(839, '2024-11-07', 2)": 0, + "(839, '2024-11-07', 3)": 0, + "(839, '2024-11-07', 4)": 0, + "(839, '2024-11-07', 5)": 0, + "(839, '2024-11-07', 6)": 0, + "(839, '2024-11-07', 7)": 0, + "(839, '2024-11-08', 0)": 0, + "(839, '2024-11-08', 1)": 0, + "(839, '2024-11-08', 2)": 0, + "(839, '2024-11-08', 3)": 0, + "(839, '2024-11-08', 4)": 0, + "(839, '2024-11-08', 5)": 0, + "(839, '2024-11-08', 6)": 0, + "(839, '2024-11-08', 7)": 0, + "(839, '2024-11-09', 0)": 0, + "(839, '2024-11-09', 1)": 0, + "(839, '2024-11-09', 2)": 0, + "(839, '2024-11-09', 3)": 0, + "(839, '2024-11-09', 4)": 0, + "(839, '2024-11-09', 5)": 0, + "(839, '2024-11-09', 6)": 0, + "(839, '2024-11-09', 7)": 0, + "(839, '2024-11-10', 0)": 0, + "(839, '2024-11-10', 1)": 0, + "(839, '2024-11-10', 2)": 0, + "(839, '2024-11-10', 3)": 0, + "(839, '2024-11-10', 4)": 0, + "(839, '2024-11-10', 5)": 0, + "(839, '2024-11-10', 6)": 0, + "(839, '2024-11-10', 7)": 1, + "(839, '2024-11-11', 0)": 0, + "(839, '2024-11-11', 1)": 0, + "(839, '2024-11-11', 2)": 0, + "(839, '2024-11-11', 3)": 0, + "(839, '2024-11-11', 4)": 0, + "(839, '2024-11-11', 5)": 0, + "(839, '2024-11-11', 6)": 0, + "(839, '2024-11-11', 7)": 1, + "(839, '2024-11-12', 0)": 0, + "(839, '2024-11-12', 1)": 0, + "(839, '2024-11-12', 2)": 0, + "(839, '2024-11-12', 3)": 0, + "(839, '2024-11-12', 4)": 0, + "(839, '2024-11-12', 5)": 0, + "(839, '2024-11-12', 6)": 0, + "(839, '2024-11-12', 7)": 0, + "(839, '2024-11-13', 0)": 0, + "(839, '2024-11-13', 1)": 0, + "(839, '2024-11-13', 2)": 0, + "(839, '2024-11-13', 3)": 0, + "(839, '2024-11-13', 4)": 0, + "(839, '2024-11-13', 5)": 0, + "(839, '2024-11-13', 6)": 0, + "(839, '2024-11-13', 7)": 0, + "(839, '2024-11-14', 0)": 0, + "(839, '2024-11-14', 1)": 0, + "(839, '2024-11-14', 2)": 0, + "(839, '2024-11-14', 3)": 0, + "(839, '2024-11-14', 4)": 0, + "(839, '2024-11-14', 5)": 0, + "(839, '2024-11-14', 6)": 0, + "(839, '2024-11-14', 7)": 0, + "(839, '2024-11-15', 0)": 0, + "(839, '2024-11-15', 1)": 0, + "(839, '2024-11-15', 2)": 0, + "(839, '2024-11-15', 3)": 0, + "(839, '2024-11-15', 4)": 0, + "(839, '2024-11-15', 5)": 0, + "(839, '2024-11-15', 6)": 0, + "(839, '2024-11-15', 7)": 1, + "(839, '2024-11-16', 0)": 0, + "(839, '2024-11-16', 1)": 0, + "(839, '2024-11-16', 2)": 0, + "(839, '2024-11-16', 3)": 0, + "(839, '2024-11-16', 4)": 0, + "(839, '2024-11-16', 5)": 0, + "(839, '2024-11-16', 6)": 0, + "(839, '2024-11-16', 7)": 0, + "(839, '2024-11-17', 0)": 0, + "(839, '2024-11-17', 1)": 0, + "(839, '2024-11-17', 2)": 0, + "(839, '2024-11-17', 3)": 0, + "(839, '2024-11-17', 4)": 0, + "(839, '2024-11-17', 5)": 0, + "(839, '2024-11-17', 6)": 0, + "(839, '2024-11-17', 7)": 0, + "(839, '2024-11-18', 0)": 0, + "(839, '2024-11-18', 1)": 0, + "(839, '2024-11-18', 2)": 0, + "(839, '2024-11-18', 3)": 0, + "(839, '2024-11-18', 4)": 0, + "(839, '2024-11-18', 5)": 0, + "(839, '2024-11-18', 6)": 0, + "(839, '2024-11-18', 7)": 0, + "(839, '2024-11-19', 0)": 0, + "(839, '2024-11-19', 1)": 0, + "(839, '2024-11-19', 2)": 0, + "(839, '2024-11-19', 3)": 0, + "(839, '2024-11-19', 4)": 0, + "(839, '2024-11-19', 5)": 0, + "(839, '2024-11-19', 6)": 0, + "(839, '2024-11-19', 7)": 1, + "(839, '2024-11-20', 0)": 0, + "(839, '2024-11-20', 1)": 0, + "(839, '2024-11-20', 2)": 0, + "(839, '2024-11-20', 3)": 0, + "(839, '2024-11-20', 4)": 0, + "(839, '2024-11-20', 5)": 0, + "(839, '2024-11-20', 6)": 0, + "(839, '2024-11-20', 7)": 1, + "(839, '2024-11-21', 0)": 0, + "(839, '2024-11-21', 1)": 0, + "(839, '2024-11-21', 2)": 0, + "(839, '2024-11-21', 3)": 0, + "(839, '2024-11-21', 4)": 0, + "(839, '2024-11-21', 5)": 0, + "(839, '2024-11-21', 6)": 0, + "(839, '2024-11-21', 7)": 0, + "(839, '2024-11-22', 0)": 0, + "(839, '2024-11-22', 1)": 0, + "(839, '2024-11-22', 2)": 0, + "(839, '2024-11-22', 3)": 0, + "(839, '2024-11-22', 4)": 0, + "(839, '2024-11-22', 5)": 0, + "(839, '2024-11-22', 6)": 0, + "(839, '2024-11-22', 7)": 0, + "(839, '2024-11-23', 0)": 0, + "(839, '2024-11-23', 1)": 0, + "(839, '2024-11-23', 2)": 0, + "(839, '2024-11-23', 3)": 0, + "(839, '2024-11-23', 4)": 0, + "(839, '2024-11-23', 5)": 0, + "(839, '2024-11-23', 6)": 0, + "(839, '2024-11-23', 7)": 0, + "(839, '2024-11-24', 0)": 0, + "(839, '2024-11-24', 1)": 0, + "(839, '2024-11-24', 2)": 0, + "(839, '2024-11-24', 3)": 0, + "(839, '2024-11-24', 4)": 0, + "(839, '2024-11-24', 5)": 0, + "(839, '2024-11-24', 6)": 0, + "(839, '2024-11-24', 7)": 0, + "(839, '2024-11-25', 0)": 0, + "(839, '2024-11-25', 1)": 0, + "(839, '2024-11-25', 2)": 0, + "(839, '2024-11-25', 3)": 0, + "(839, '2024-11-25', 4)": 0, + "(839, '2024-11-25', 5)": 0, + "(839, '2024-11-25', 6)": 0, + "(839, '2024-11-25', 7)": 0, + "(839, '2024-11-26', 0)": 0, + "(839, '2024-11-26', 1)": 0, + "(839, '2024-11-26', 2)": 0, + "(839, '2024-11-26', 3)": 0, + "(839, '2024-11-26', 4)": 0, + "(839, '2024-11-26', 5)": 0, + "(839, '2024-11-26', 6)": 0, + "(839, '2024-11-26', 7)": 1, + "(839, '2024-11-27', 0)": 0, + "(839, '2024-11-27', 1)": 0, + "(839, '2024-11-27', 2)": 0, + "(839, '2024-11-27', 3)": 0, + "(839, '2024-11-27', 4)": 0, + "(839, '2024-11-27', 5)": 0, + "(839, '2024-11-27', 6)": 0, + "(839, '2024-11-27', 7)": 1, + "(839, '2024-11-28', 0)": 0, + "(839, '2024-11-28', 1)": 0, + "(839, '2024-11-28', 2)": 0, + "(839, '2024-11-28', 3)": 0, + "(839, '2024-11-28', 4)": 0, + "(839, '2024-11-28', 5)": 0, + "(839, '2024-11-28', 6)": 0, + "(839, '2024-11-28', 7)": 0, + "(839, '2024-11-29', 0)": 0, + "(839, '2024-11-29', 1)": 0, + "(839, '2024-11-29', 2)": 0, + "(839, '2024-11-29', 3)": 0, + "(839, '2024-11-29', 4)": 0, + "(839, '2024-11-29', 5)": 0, + "(839, '2024-11-29', 6)": 0, + "(839, '2024-11-29', 7)": 0, + "(839, '2024-11-30', 0)": 0, + "(839, '2024-11-30', 1)": 0, + "(839, '2024-11-30', 2)": 0, + "(839, '2024-11-30', 3)": 0, + "(839, '2024-11-30', 4)": 0, + "(839, '2024-11-30', 5)": 0, + "(839, '2024-11-30', 6)": 0, + "(839, '2024-11-30', 7)": 0, + "(914, '2024-11-01', 0)": 0, + "(914, '2024-11-01', 1)": 0, + "(914, '2024-11-01', 2)": 0, + "(914, '2024-11-01', 3)": 0, + "(914, '2024-11-01', 4)": 0, + "(914, '2024-11-01', 5)": 0, + "(914, '2024-11-01', 6)": 0, + "(914, '2024-11-01', 7)": 0, + "(914, '2024-11-02', 0)": 1, + "(914, '2024-11-02', 1)": 0, + "(914, '2024-11-02', 2)": 0, + "(914, '2024-11-02', 3)": 0, + "(914, '2024-11-02', 4)": 0, + "(914, '2024-11-02', 5)": 0, + "(914, '2024-11-02', 6)": 0, + "(914, '2024-11-02', 7)": 0, + "(914, '2024-11-03', 0)": 0, + "(914, '2024-11-03', 1)": 0, + "(914, '2024-11-03', 2)": 0, + "(914, '2024-11-03', 3)": 0, + "(914, '2024-11-03', 4)": 0, + "(914, '2024-11-03', 5)": 0, + "(914, '2024-11-03', 6)": 0, + "(914, '2024-11-03', 7)": 0, + "(914, '2024-11-04', 0)": 0, + "(914, '2024-11-04', 1)": 0, + "(914, '2024-11-04', 2)": 0, + "(914, '2024-11-04', 3)": 0, + "(914, '2024-11-04', 4)": 0, + "(914, '2024-11-04', 5)": 0, + "(914, '2024-11-04', 6)": 0, + "(914, '2024-11-04', 7)": 0, + "(914, '2024-11-05', 0)": 0, + "(914, '2024-11-05', 1)": 0, + "(914, '2024-11-05', 2)": 1, + "(914, '2024-11-05', 3)": 0, + "(914, '2024-11-05', 4)": 0, + "(914, '2024-11-05', 5)": 0, + "(914, '2024-11-05', 6)": 0, + "(914, '2024-11-05', 7)": 0, + "(914, '2024-11-06', 0)": 0, + "(914, '2024-11-06', 1)": 0, + "(914, '2024-11-06', 2)": 1, + "(914, '2024-11-06', 3)": 0, + "(914, '2024-11-06', 4)": 0, + "(914, '2024-11-06', 5)": 0, + "(914, '2024-11-06', 6)": 0, + "(914, '2024-11-06', 7)": 0, + "(914, '2024-11-07', 0)": 0, + "(914, '2024-11-07', 1)": 0, + "(914, '2024-11-07', 2)": 1, + "(914, '2024-11-07', 3)": 0, + "(914, '2024-11-07', 4)": 0, + "(914, '2024-11-07', 5)": 0, + "(914, '2024-11-07', 6)": 0, + "(914, '2024-11-07', 7)": 0, + "(914, '2024-11-08', 0)": 0, + "(914, '2024-11-08', 1)": 0, + "(914, '2024-11-08', 2)": 1, + "(914, '2024-11-08', 3)": 0, + "(914, '2024-11-08', 4)": 0, + "(914, '2024-11-08', 5)": 0, + "(914, '2024-11-08', 6)": 0, + "(914, '2024-11-08', 7)": 0, + "(914, '2024-11-09', 0)": 0, + "(914, '2024-11-09', 1)": 0, + "(914, '2024-11-09', 2)": 0, + "(914, '2024-11-09', 3)": 0, + "(914, '2024-11-09', 4)": 0, + "(914, '2024-11-09', 5)": 0, + "(914, '2024-11-09', 6)": 0, + "(914, '2024-11-09', 7)": 0, + "(914, '2024-11-10', 0)": 0, + "(914, '2024-11-10', 1)": 0, + "(914, '2024-11-10', 2)": 0, + "(914, '2024-11-10', 3)": 0, + "(914, '2024-11-10', 4)": 0, + "(914, '2024-11-10', 5)": 0, + "(914, '2024-11-10', 6)": 0, + "(914, '2024-11-10', 7)": 0, + "(914, '2024-11-11', 0)": 0, + "(914, '2024-11-11', 1)": 0, + "(914, '2024-11-11', 2)": 0, + "(914, '2024-11-11', 3)": 0, + "(914, '2024-11-11', 4)": 0, + "(914, '2024-11-11', 5)": 0, + "(914, '2024-11-11', 6)": 0, + "(914, '2024-11-11', 7)": 0, + "(914, '2024-11-12', 0)": 0, + "(914, '2024-11-12', 1)": 0, + "(914, '2024-11-12', 2)": 0, + "(914, '2024-11-12', 3)": 0, + "(914, '2024-11-12', 4)": 0, + "(914, '2024-11-12', 5)": 0, + "(914, '2024-11-12', 6)": 0, + "(914, '2024-11-12', 7)": 0, + "(914, '2024-11-13', 0)": 0, + "(914, '2024-11-13', 1)": 0, + "(914, '2024-11-13', 2)": 0, + "(914, '2024-11-13', 3)": 0, + "(914, '2024-11-13', 4)": 0, + "(914, '2024-11-13', 5)": 0, + "(914, '2024-11-13', 6)": 0, + "(914, '2024-11-13', 7)": 0, + "(914, '2024-11-14', 0)": 0, + "(914, '2024-11-14', 1)": 0, + "(914, '2024-11-14', 2)": 0, + "(914, '2024-11-14', 3)": 0, + "(914, '2024-11-14', 4)": 0, + "(914, '2024-11-14', 5)": 0, + "(914, '2024-11-14', 6)": 0, + "(914, '2024-11-14', 7)": 0, + "(914, '2024-11-15', 0)": 0, + "(914, '2024-11-15', 1)": 0, + "(914, '2024-11-15', 2)": 1, + "(914, '2024-11-15', 3)": 0, + "(914, '2024-11-15', 4)": 0, + "(914, '2024-11-15', 5)": 0, + "(914, '2024-11-15', 6)": 0, + "(914, '2024-11-15', 7)": 0, + "(914, '2024-11-16', 0)": 0, + "(914, '2024-11-16', 1)": 0, + "(914, '2024-11-16', 2)": 0, + "(914, '2024-11-16', 3)": 0, + "(914, '2024-11-16', 4)": 0, + "(914, '2024-11-16', 5)": 0, + "(914, '2024-11-16', 6)": 0, + "(914, '2024-11-16', 7)": 0, + "(914, '2024-11-17', 0)": 0, + "(914, '2024-11-17', 1)": 0, + "(914, '2024-11-17', 2)": 0, + "(914, '2024-11-17', 3)": 0, + "(914, '2024-11-17', 4)": 0, + "(914, '2024-11-17', 5)": 0, + "(914, '2024-11-17', 6)": 0, + "(914, '2024-11-17', 7)": 0, + "(914, '2024-11-18', 0)": 0, + "(914, '2024-11-18', 1)": 0, + "(914, '2024-11-18', 2)": 0, + "(914, '2024-11-18', 3)": 0, + "(914, '2024-11-18', 4)": 0, + "(914, '2024-11-18', 5)": 0, + "(914, '2024-11-18', 6)": 0, + "(914, '2024-11-18', 7)": 0, + "(914, '2024-11-19', 0)": 1, + "(914, '2024-11-19', 1)": 0, + "(914, '2024-11-19', 2)": 0, + "(914, '2024-11-19', 3)": 0, + "(914, '2024-11-19', 4)": 0, + "(914, '2024-11-19', 5)": 0, + "(914, '2024-11-19', 6)": 0, + "(914, '2024-11-19', 7)": 0, + "(914, '2024-11-20', 0)": 0, + "(914, '2024-11-20', 1)": 0, + "(914, '2024-11-20', 2)": 0, + "(914, '2024-11-20', 3)": 0, + "(914, '2024-11-20', 4)": 0, + "(914, '2024-11-20', 5)": 0, + "(914, '2024-11-20', 6)": 0, + "(914, '2024-11-20', 7)": 0, + "(914, '2024-11-21', 0)": 0, + "(914, '2024-11-21', 1)": 0, + "(914, '2024-11-21', 2)": 0, + "(914, '2024-11-21', 3)": 0, + "(914, '2024-11-21', 4)": 0, + "(914, '2024-11-21', 5)": 0, + "(914, '2024-11-21', 6)": 0, + "(914, '2024-11-21', 7)": 0, + "(914, '2024-11-22', 0)": 0, + "(914, '2024-11-22', 1)": 0, + "(914, '2024-11-22', 2)": 0, + "(914, '2024-11-22', 3)": 0, + "(914, '2024-11-22', 4)": 0, + "(914, '2024-11-22', 5)": 0, + "(914, '2024-11-22', 6)": 0, + "(914, '2024-11-22', 7)": 0, + "(914, '2024-11-23', 0)": 0, + "(914, '2024-11-23', 1)": 0, + "(914, '2024-11-23', 2)": 0, + "(914, '2024-11-23', 3)": 0, + "(914, '2024-11-23', 4)": 0, + "(914, '2024-11-23', 5)": 0, + "(914, '2024-11-23', 6)": 0, + "(914, '2024-11-23', 7)": 0, + "(914, '2024-11-24', 0)": 0, + "(914, '2024-11-24', 1)": 0, + "(914, '2024-11-24', 2)": 0, + "(914, '2024-11-24', 3)": 0, + "(914, '2024-11-24', 4)": 0, + "(914, '2024-11-24', 5)": 0, + "(914, '2024-11-24', 6)": 0, + "(914, '2024-11-24', 7)": 0, + "(914, '2024-11-25', 0)": 0, + "(914, '2024-11-25', 1)": 0, + "(914, '2024-11-25', 2)": 0, + "(914, '2024-11-25', 3)": 0, + "(914, '2024-11-25', 4)": 0, + "(914, '2024-11-25', 5)": 0, + "(914, '2024-11-25', 6)": 0, + "(914, '2024-11-25', 7)": 0, + "(914, '2024-11-26', 0)": 0, + "(914, '2024-11-26', 1)": 0, + "(914, '2024-11-26', 2)": 0, + "(914, '2024-11-26', 3)": 0, + "(914, '2024-11-26', 4)": 0, + "(914, '2024-11-26', 5)": 0, + "(914, '2024-11-26', 6)": 0, + "(914, '2024-11-26', 7)": 0, + "(914, '2024-11-27', 0)": 0, + "(914, '2024-11-27', 1)": 0, + "(914, '2024-11-27', 2)": 0, + "(914, '2024-11-27', 3)": 0, + "(914, '2024-11-27', 4)": 0, + "(914, '2024-11-27', 5)": 0, + "(914, '2024-11-27', 6)": 0, + "(914, '2024-11-27', 7)": 0, + "(914, '2024-11-28', 0)": 0, + "(914, '2024-11-28', 1)": 0, + "(914, '2024-11-28', 2)": 0, + "(914, '2024-11-28', 3)": 0, + "(914, '2024-11-28', 4)": 0, + "(914, '2024-11-28', 5)": 0, + "(914, '2024-11-28', 6)": 0, + "(914, '2024-11-28', 7)": 0, + "(914, '2024-11-29', 0)": 0, + "(914, '2024-11-29', 1)": 0, + "(914, '2024-11-29', 2)": 0, + "(914, '2024-11-29', 3)": 0, + "(914, '2024-11-29', 4)": 0, + "(914, '2024-11-29', 5)": 0, + "(914, '2024-11-29', 6)": 0, + "(914, '2024-11-29', 7)": 0, + "(914, '2024-11-30', 0)": 0, + "(914, '2024-11-30', 1)": 0, + "(914, '2024-11-30', 2)": 0, + "(914, '2024-11-30', 3)": 0, + "(914, '2024-11-30', 4)": 0, + "(914, '2024-11-30', 5)": 0, + "(914, '2024-11-30', 6)": 0, + "(914, '2024-11-30', 7)": 0, + "(917, '2024-11-01', 0)": 1, + "(917, '2024-11-01', 1)": 0, + "(917, '2024-11-01', 2)": 0, + "(917, '2024-11-01', 3)": 0, + "(917, '2024-11-01', 4)": 0, + "(917, '2024-11-01', 5)": 0, + "(917, '2024-11-01', 6)": 0, + "(917, '2024-11-01', 7)": 0, + "(917, '2024-11-02', 0)": 0, + "(917, '2024-11-02', 1)": 0, + "(917, '2024-11-02', 2)": 0, + "(917, '2024-11-02', 3)": 0, + "(917, '2024-11-02', 4)": 0, + "(917, '2024-11-02', 5)": 0, + "(917, '2024-11-02', 6)": 0, + "(917, '2024-11-02', 7)": 0, + "(917, '2024-11-03', 0)": 0, + "(917, '2024-11-03', 1)": 0, + "(917, '2024-11-03', 2)": 0, + "(917, '2024-11-03', 3)": 1, + "(917, '2024-11-03', 4)": 0, + "(917, '2024-11-03', 5)": 0, + "(917, '2024-11-03', 6)": 0, + "(917, '2024-11-03', 7)": 0, + "(917, '2024-11-04', 0)": 0, + "(917, '2024-11-04', 1)": 0, + "(917, '2024-11-04', 2)": 0, + "(917, '2024-11-04', 3)": 0, + "(917, '2024-11-04', 4)": 0, + "(917, '2024-11-04', 5)": 0, + "(917, '2024-11-04', 6)": 0, + "(917, '2024-11-04', 7)": 0, + "(917, '2024-11-05', 0)": 0, + "(917, '2024-11-05', 1)": 0, + "(917, '2024-11-05', 2)": 1, + "(917, '2024-11-05', 3)": 0, + "(917, '2024-11-05', 4)": 0, + "(917, '2024-11-05', 5)": 0, + "(917, '2024-11-05', 6)": 0, + "(917, '2024-11-05', 7)": 0, + "(917, '2024-11-06', 0)": 0, + "(917, '2024-11-06', 1)": 0, + "(917, '2024-11-06', 2)": 1, + "(917, '2024-11-06', 3)": 0, + "(917, '2024-11-06', 4)": 0, + "(917, '2024-11-06', 5)": 0, + "(917, '2024-11-06', 6)": 0, + "(917, '2024-11-06', 7)": 0, + "(917, '2024-11-07', 0)": 0, + "(917, '2024-11-07', 1)": 0, + "(917, '2024-11-07', 2)": 0, + "(917, '2024-11-07', 3)": 0, + "(917, '2024-11-07', 4)": 0, + "(917, '2024-11-07', 5)": 0, + "(917, '2024-11-07', 6)": 0, + "(917, '2024-11-07', 7)": 0, + "(917, '2024-11-08', 0)": 1, + "(917, '2024-11-08', 1)": 0, + "(917, '2024-11-08', 2)": 0, + "(917, '2024-11-08', 3)": 0, + "(917, '2024-11-08', 4)": 0, + "(917, '2024-11-08', 5)": 0, + "(917, '2024-11-08', 6)": 0, + "(917, '2024-11-08', 7)": 0, + "(917, '2024-11-09', 0)": 0, + "(917, '2024-11-09', 1)": 0, + "(917, '2024-11-09', 2)": 0, + "(917, '2024-11-09', 3)": 0, + "(917, '2024-11-09', 4)": 0, + "(917, '2024-11-09', 5)": 0, + "(917, '2024-11-09', 6)": 0, + "(917, '2024-11-09', 7)": 0, + "(917, '2024-11-10', 0)": 0, + "(917, '2024-11-10', 1)": 0, + "(917, '2024-11-10', 2)": 0, + "(917, '2024-11-10', 3)": 0, + "(917, '2024-11-10', 4)": 0, + "(917, '2024-11-10', 5)": 0, + "(917, '2024-11-10', 6)": 0, + "(917, '2024-11-10', 7)": 0, + "(917, '2024-11-11', 0)": 0, + "(917, '2024-11-11', 1)": 0, + "(917, '2024-11-11', 2)": 0, + "(917, '2024-11-11', 3)": 0, + "(917, '2024-11-11', 4)": 0, + "(917, '2024-11-11', 5)": 0, + "(917, '2024-11-11', 6)": 0, + "(917, '2024-11-11', 7)": 0, + "(917, '2024-11-12', 0)": 0, + "(917, '2024-11-12', 1)": 0, + "(917, '2024-11-12', 2)": 1, + "(917, '2024-11-12', 3)": 0, + "(917, '2024-11-12', 4)": 0, + "(917, '2024-11-12', 5)": 0, + "(917, '2024-11-12', 6)": 0, + "(917, '2024-11-12', 7)": 0, + "(917, '2024-11-13', 0)": 0, + "(917, '2024-11-13', 1)": 0, + "(917, '2024-11-13', 2)": 1, + "(917, '2024-11-13', 3)": 0, + "(917, '2024-11-13', 4)": 0, + "(917, '2024-11-13', 5)": 0, + "(917, '2024-11-13', 6)": 0, + "(917, '2024-11-13', 7)": 0, + "(917, '2024-11-14', 0)": 0, + "(917, '2024-11-14', 1)": 0, + "(917, '2024-11-14', 2)": 1, + "(917, '2024-11-14', 3)": 0, + "(917, '2024-11-14', 4)": 0, + "(917, '2024-11-14', 5)": 0, + "(917, '2024-11-14', 6)": 0, + "(917, '2024-11-14', 7)": 0, + "(917, '2024-11-15', 0)": 0, + "(917, '2024-11-15', 1)": 0, + "(917, '2024-11-15', 2)": 0, + "(917, '2024-11-15', 3)": 0, + "(917, '2024-11-15', 4)": 0, + "(917, '2024-11-15', 5)": 0, + "(917, '2024-11-15', 6)": 1, + "(917, '2024-11-15', 7)": 0, + "(917, '2024-11-16', 0)": 0, + "(917, '2024-11-16', 1)": 0, + "(917, '2024-11-16', 2)": 0, + "(917, '2024-11-16', 3)": 0, + "(917, '2024-11-16', 4)": 0, + "(917, '2024-11-16', 5)": 0, + "(917, '2024-11-16', 6)": 0, + "(917, '2024-11-16', 7)": 0, + "(917, '2024-11-17', 0)": 0, + "(917, '2024-11-17', 1)": 0, + "(917, '2024-11-17', 2)": 0, + "(917, '2024-11-17', 3)": 0, + "(917, '2024-11-17', 4)": 0, + "(917, '2024-11-17', 5)": 0, + "(917, '2024-11-17', 6)": 0, + "(917, '2024-11-17', 7)": 0, + "(917, '2024-11-18', 0)": 0, + "(917, '2024-11-18', 1)": 0, + "(917, '2024-11-18', 2)": 0, + "(917, '2024-11-18', 3)": 0, + "(917, '2024-11-18', 4)": 0, + "(917, '2024-11-18', 5)": 0, + "(917, '2024-11-18', 6)": 0, + "(917, '2024-11-18', 7)": 0, + "(917, '2024-11-19', 0)": 0, + "(917, '2024-11-19', 1)": 0, + "(917, '2024-11-19', 2)": 0, + "(917, '2024-11-19', 3)": 1, + "(917, '2024-11-19', 4)": 0, + "(917, '2024-11-19', 5)": 0, + "(917, '2024-11-19', 6)": 0, + "(917, '2024-11-19', 7)": 0, + "(917, '2024-11-20', 0)": 0, + "(917, '2024-11-20', 1)": 0, + "(917, '2024-11-20', 2)": 0, + "(917, '2024-11-20', 3)": 1, + "(917, '2024-11-20', 4)": 0, + "(917, '2024-11-20', 5)": 0, + "(917, '2024-11-20', 6)": 0, + "(917, '2024-11-20', 7)": 0, + "(917, '2024-11-21', 0)": 0, + "(917, '2024-11-21', 1)": 0, + "(917, '2024-11-21', 2)": 0, + "(917, '2024-11-21', 3)": 0, + "(917, '2024-11-21', 4)": 0, + "(917, '2024-11-21', 5)": 0, + "(917, '2024-11-21', 6)": 0, + "(917, '2024-11-21', 7)": 0, + "(917, '2024-11-22', 0)": 0, + "(917, '2024-11-22', 1)": 0, + "(917, '2024-11-22', 2)": 0, + "(917, '2024-11-22', 3)": 0, + "(917, '2024-11-22', 4)": 0, + "(917, '2024-11-22', 5)": 0, + "(917, '2024-11-22', 6)": 0, + "(917, '2024-11-22', 7)": 0, + "(917, '2024-11-23', 0)": 0, + "(917, '2024-11-23', 1)": 0, + "(917, '2024-11-23', 2)": 0, + "(917, '2024-11-23', 3)": 0, + "(917, '2024-11-23', 4)": 0, + "(917, '2024-11-23', 5)": 0, + "(917, '2024-11-23', 6)": 0, + "(917, '2024-11-23', 7)": 0, + "(917, '2024-11-24', 0)": 0, + "(917, '2024-11-24', 1)": 0, + "(917, '2024-11-24', 2)": 0, + "(917, '2024-11-24', 3)": 1, + "(917, '2024-11-24', 4)": 0, + "(917, '2024-11-24', 5)": 0, + "(917, '2024-11-24', 6)": 0, + "(917, '2024-11-24', 7)": 0, + "(917, '2024-11-25', 0)": 0, + "(917, '2024-11-25', 1)": 0, + "(917, '2024-11-25', 2)": 0, + "(917, '2024-11-25', 3)": 0, + "(917, '2024-11-25', 4)": 0, + "(917, '2024-11-25', 5)": 0, + "(917, '2024-11-25', 6)": 0, + "(917, '2024-11-25', 7)": 0, + "(917, '2024-11-26', 0)": 1, + "(917, '2024-11-26', 1)": 0, + "(917, '2024-11-26', 2)": 0, + "(917, '2024-11-26', 3)": 0, + "(917, '2024-11-26', 4)": 0, + "(917, '2024-11-26', 5)": 0, + "(917, '2024-11-26', 6)": 0, + "(917, '2024-11-26', 7)": 0, + "(917, '2024-11-27', 0)": 0, + "(917, '2024-11-27', 1)": 0, + "(917, '2024-11-27', 2)": 0, + "(917, '2024-11-27', 3)": 0, + "(917, '2024-11-27', 4)": 0, + "(917, '2024-11-27', 5)": 0, + "(917, '2024-11-27', 6)": 0, + "(917, '2024-11-27', 7)": 0, + "(917, '2024-11-28', 0)": 0, + "(917, '2024-11-28', 1)": 0, + "(917, '2024-11-28', 2)": 1, + "(917, '2024-11-28', 3)": 0, + "(917, '2024-11-28', 4)": 0, + "(917, '2024-11-28', 5)": 0, + "(917, '2024-11-28', 6)": 0, + "(917, '2024-11-28', 7)": 0, + "(917, '2024-11-29', 0)": 0, + "(917, '2024-11-29', 1)": 0, + "(917, '2024-11-29', 2)": 0, + "(917, '2024-11-29', 3)": 0, + "(917, '2024-11-29', 4)": 0, + "(917, '2024-11-29', 5)": 0, + "(917, '2024-11-29', 6)": 0, + "(917, '2024-11-29', 7)": 0, + "(917, '2024-11-30', 0)": 1, + "(917, '2024-11-30', 1)": 0, + "(917, '2024-11-30', 2)": 0, + "(917, '2024-11-30', 3)": 0, + "(917, '2024-11-30', 4)": 0, + "(917, '2024-11-30', 5)": 0, + "(917, '2024-11-30', 6)": 0, + "(917, '2024-11-30', 7)": 0, + "(921, '2024-11-01', 0)": 0, + "(921, '2024-11-01', 1)": 0, + "(921, '2024-11-01', 2)": 0, + "(921, '2024-11-01', 3)": 0, + "(921, '2024-11-01', 4)": 0, + "(921, '2024-11-01', 5)": 0, + "(921, '2024-11-01', 6)": 0, + "(921, '2024-11-01', 7)": 0, + "(921, '2024-11-02', 0)": 0, + "(921, '2024-11-02', 1)": 0, + "(921, '2024-11-02', 2)": 0, + "(921, '2024-11-02', 3)": 0, + "(921, '2024-11-02', 4)": 0, + "(921, '2024-11-02', 5)": 0, + "(921, '2024-11-02', 6)": 0, + "(921, '2024-11-02', 7)": 0, + "(921, '2024-11-03', 0)": 0, + "(921, '2024-11-03', 1)": 0, + "(921, '2024-11-03', 2)": 0, + "(921, '2024-11-03', 3)": 0, + "(921, '2024-11-03', 4)": 0, + "(921, '2024-11-03', 5)": 0, + "(921, '2024-11-03', 6)": 0, + "(921, '2024-11-03', 7)": 0, + "(921, '2024-11-04', 0)": 0, + "(921, '2024-11-04', 1)": 0, + "(921, '2024-11-04', 2)": 0, + "(921, '2024-11-04', 3)": 0, + "(921, '2024-11-04', 4)": 0, + "(921, '2024-11-04', 5)": 0, + "(921, '2024-11-04', 6)": 0, + "(921, '2024-11-04', 7)": 0, + "(921, '2024-11-05', 0)": 0, + "(921, '2024-11-05', 1)": 0, + "(921, '2024-11-05', 2)": 0, + "(921, '2024-11-05', 3)": 0, + "(921, '2024-11-05', 4)": 0, + "(921, '2024-11-05', 5)": 0, + "(921, '2024-11-05', 6)": 0, + "(921, '2024-11-05', 7)": 0, + "(921, '2024-11-06', 0)": 0, + "(921, '2024-11-06', 1)": 0, + "(921, '2024-11-06', 2)": 0, + "(921, '2024-11-06', 3)": 0, + "(921, '2024-11-06', 4)": 0, + "(921, '2024-11-06', 5)": 0, + "(921, '2024-11-06', 6)": 0, + "(921, '2024-11-06', 7)": 0, + "(921, '2024-11-07', 0)": 0, + "(921, '2024-11-07', 1)": 0, + "(921, '2024-11-07', 2)": 0, + "(921, '2024-11-07', 3)": 0, + "(921, '2024-11-07', 4)": 0, + "(921, '2024-11-07', 5)": 0, + "(921, '2024-11-07', 6)": 0, + "(921, '2024-11-07', 7)": 0, + "(921, '2024-11-08', 0)": 0, + "(921, '2024-11-08', 1)": 0, + "(921, '2024-11-08', 2)": 0, + "(921, '2024-11-08', 3)": 0, + "(921, '2024-11-08', 4)": 0, + "(921, '2024-11-08', 5)": 0, + "(921, '2024-11-08', 6)": 0, + "(921, '2024-11-08', 7)": 0, + "(921, '2024-11-09', 0)": 0, + "(921, '2024-11-09', 1)": 0, + "(921, '2024-11-09', 2)": 0, + "(921, '2024-11-09', 3)": 0, + "(921, '2024-11-09', 4)": 0, + "(921, '2024-11-09', 5)": 0, + "(921, '2024-11-09', 6)": 0, + "(921, '2024-11-09', 7)": 0, + "(921, '2024-11-10', 0)": 0, + "(921, '2024-11-10', 1)": 0, + "(921, '2024-11-10', 2)": 0, + "(921, '2024-11-10', 3)": 0, + "(921, '2024-11-10', 4)": 0, + "(921, '2024-11-10', 5)": 0, + "(921, '2024-11-10', 6)": 0, + "(921, '2024-11-10', 7)": 0, + "(921, '2024-11-11', 0)": 0, + "(921, '2024-11-11', 1)": 0, + "(921, '2024-11-11', 2)": 0, + "(921, '2024-11-11', 3)": 0, + "(921, '2024-11-11', 4)": 0, + "(921, '2024-11-11', 5)": 0, + "(921, '2024-11-11', 6)": 0, + "(921, '2024-11-11', 7)": 0, + "(921, '2024-11-12', 0)": 0, + "(921, '2024-11-12', 1)": 0, + "(921, '2024-11-12', 2)": 0, + "(921, '2024-11-12', 3)": 0, + "(921, '2024-11-12', 4)": 0, + "(921, '2024-11-12', 5)": 0, + "(921, '2024-11-12', 6)": 0, + "(921, '2024-11-12', 7)": 0, + "(921, '2024-11-13', 0)": 0, + "(921, '2024-11-13', 1)": 0, + "(921, '2024-11-13', 2)": 0, + "(921, '2024-11-13', 3)": 0, + "(921, '2024-11-13', 4)": 0, + "(921, '2024-11-13', 5)": 0, + "(921, '2024-11-13', 6)": 0, + "(921, '2024-11-13', 7)": 0, + "(921, '2024-11-14', 0)": 0, + "(921, '2024-11-14', 1)": 0, + "(921, '2024-11-14', 2)": 0, + "(921, '2024-11-14', 3)": 0, + "(921, '2024-11-14', 4)": 0, + "(921, '2024-11-14', 5)": 0, + "(921, '2024-11-14', 6)": 0, + "(921, '2024-11-14', 7)": 0, + "(921, '2024-11-15', 0)": 0, + "(921, '2024-11-15', 1)": 0, + "(921, '2024-11-15', 2)": 0, + "(921, '2024-11-15', 3)": 0, + "(921, '2024-11-15', 4)": 0, + "(921, '2024-11-15', 5)": 0, + "(921, '2024-11-15', 6)": 0, + "(921, '2024-11-15', 7)": 0, + "(921, '2024-11-16', 0)": 0, + "(921, '2024-11-16', 1)": 0, + "(921, '2024-11-16', 2)": 0, + "(921, '2024-11-16', 3)": 0, + "(921, '2024-11-16', 4)": 0, + "(921, '2024-11-16', 5)": 0, + "(921, '2024-11-16', 6)": 0, + "(921, '2024-11-16', 7)": 0, + "(921, '2024-11-17', 0)": 0, + "(921, '2024-11-17', 1)": 0, + "(921, '2024-11-17', 2)": 0, + "(921, '2024-11-17', 3)": 0, + "(921, '2024-11-17', 4)": 0, + "(921, '2024-11-17', 5)": 0, + "(921, '2024-11-17', 6)": 0, + "(921, '2024-11-17', 7)": 0, + "(921, '2024-11-18', 0)": 0, + "(921, '2024-11-18', 1)": 0, + "(921, '2024-11-18', 2)": 0, + "(921, '2024-11-18', 3)": 0, + "(921, '2024-11-18', 4)": 0, + "(921, '2024-11-18', 5)": 0, + "(921, '2024-11-18', 6)": 0, + "(921, '2024-11-18', 7)": 0, + "(921, '2024-11-19', 0)": 0, + "(921, '2024-11-19', 1)": 0, + "(921, '2024-11-19', 2)": 0, + "(921, '2024-11-19', 3)": 0, + "(921, '2024-11-19', 4)": 0, + "(921, '2024-11-19', 5)": 0, + "(921, '2024-11-19', 6)": 0, + "(921, '2024-11-19', 7)": 0, + "(921, '2024-11-20', 0)": 0, + "(921, '2024-11-20', 1)": 0, + "(921, '2024-11-20', 2)": 0, + "(921, '2024-11-20', 3)": 0, + "(921, '2024-11-20', 4)": 0, + "(921, '2024-11-20', 5)": 0, + "(921, '2024-11-20', 6)": 0, + "(921, '2024-11-20', 7)": 0, + "(921, '2024-11-21', 0)": 0, + "(921, '2024-11-21', 1)": 0, + "(921, '2024-11-21', 2)": 0, + "(921, '2024-11-21', 3)": 0, + "(921, '2024-11-21', 4)": 0, + "(921, '2024-11-21', 5)": 0, + "(921, '2024-11-21', 6)": 0, + "(921, '2024-11-21', 7)": 0, + "(921, '2024-11-22', 0)": 0, + "(921, '2024-11-22', 1)": 0, + "(921, '2024-11-22', 2)": 0, + "(921, '2024-11-22', 3)": 0, + "(921, '2024-11-22', 4)": 0, + "(921, '2024-11-22', 5)": 0, + "(921, '2024-11-22', 6)": 0, + "(921, '2024-11-22', 7)": 0, + "(921, '2024-11-23', 0)": 0, + "(921, '2024-11-23', 1)": 0, + "(921, '2024-11-23', 2)": 0, + "(921, '2024-11-23', 3)": 0, + "(921, '2024-11-23', 4)": 0, + "(921, '2024-11-23', 5)": 0, + "(921, '2024-11-23', 6)": 0, + "(921, '2024-11-23', 7)": 0, + "(921, '2024-11-24', 0)": 0, + "(921, '2024-11-24', 1)": 0, + "(921, '2024-11-24', 2)": 0, + "(921, '2024-11-24', 3)": 0, + "(921, '2024-11-24', 4)": 0, + "(921, '2024-11-24', 5)": 0, + "(921, '2024-11-24', 6)": 0, + "(921, '2024-11-24', 7)": 0, + "(921, '2024-11-25', 0)": 0, + "(921, '2024-11-25', 1)": 0, + "(921, '2024-11-25', 2)": 0, + "(921, '2024-11-25', 3)": 0, + "(921, '2024-11-25', 4)": 0, + "(921, '2024-11-25', 5)": 0, + "(921, '2024-11-25', 6)": 0, + "(921, '2024-11-25', 7)": 0, + "(921, '2024-11-26', 0)": 0, + "(921, '2024-11-26', 1)": 0, + "(921, '2024-11-26', 2)": 0, + "(921, '2024-11-26', 3)": 0, + "(921, '2024-11-26', 4)": 0, + "(921, '2024-11-26', 5)": 0, + "(921, '2024-11-26', 6)": 0, + "(921, '2024-11-26', 7)": 0, + "(921, '2024-11-27', 0)": 0, + "(921, '2024-11-27', 1)": 0, + "(921, '2024-11-27', 2)": 0, + "(921, '2024-11-27', 3)": 0, + "(921, '2024-11-27', 4)": 0, + "(921, '2024-11-27', 5)": 0, + "(921, '2024-11-27', 6)": 0, + "(921, '2024-11-27', 7)": 0, + "(921, '2024-11-28', 0)": 0, + "(921, '2024-11-28', 1)": 0, + "(921, '2024-11-28', 2)": 0, + "(921, '2024-11-28', 3)": 0, + "(921, '2024-11-28', 4)": 0, + "(921, '2024-11-28', 5)": 0, + "(921, '2024-11-28', 6)": 0, + "(921, '2024-11-28', 7)": 0, + "(921, '2024-11-29', 0)": 0, + "(921, '2024-11-29', 1)": 0, + "(921, '2024-11-29', 2)": 0, + "(921, '2024-11-29', 3)": 0, + "(921, '2024-11-29', 4)": 0, + "(921, '2024-11-29', 5)": 0, + "(921, '2024-11-29', 6)": 0, + "(921, '2024-11-29', 7)": 0, + "(921, '2024-11-30', 0)": 0, + "(921, '2024-11-30', 1)": 0, + "(921, '2024-11-30', 2)": 0, + "(921, '2024-11-30', 3)": 0, + "(921, '2024-11-30', 4)": 0, + "(921, '2024-11-30', 5)": 0, + "(921, '2024-11-30', 6)": 0, + "(921, '2024-11-30', 7)": 0, + "(924, '2024-11-01', 0)": 0, + "(924, '2024-11-01', 1)": 0, + "(924, '2024-11-01', 2)": 0, + "(924, '2024-11-01', 3)": 0, + "(924, '2024-11-01', 4)": 0, + "(924, '2024-11-01', 5)": 0, + "(924, '2024-11-01', 6)": 0, + "(924, '2024-11-01', 7)": 0, + "(924, '2024-11-02', 0)": 0, + "(924, '2024-11-02', 1)": 0, + "(924, '2024-11-02', 2)": 0, + "(924, '2024-11-02', 3)": 0, + "(924, '2024-11-02', 4)": 0, + "(924, '2024-11-02', 5)": 0, + "(924, '2024-11-02', 6)": 0, + "(924, '2024-11-02', 7)": 0, + "(924, '2024-11-03', 0)": 0, + "(924, '2024-11-03', 1)": 0, + "(924, '2024-11-03', 2)": 0, + "(924, '2024-11-03', 3)": 0, + "(924, '2024-11-03', 4)": 0, + "(924, '2024-11-03', 5)": 0, + "(924, '2024-11-03', 6)": 0, + "(924, '2024-11-03', 7)": 0, + "(924, '2024-11-04', 0)": 0, + "(924, '2024-11-04', 1)": 0, + "(924, '2024-11-04', 2)": 0, + "(924, '2024-11-04', 3)": 0, + "(924, '2024-11-04', 4)": 0, + "(924, '2024-11-04', 5)": 0, + "(924, '2024-11-04', 6)": 0, + "(924, '2024-11-04', 7)": 0, + "(924, '2024-11-05', 0)": 0, + "(924, '2024-11-05', 1)": 0, + "(924, '2024-11-05', 2)": 0, + "(924, '2024-11-05', 3)": 0, + "(924, '2024-11-05', 4)": 0, + "(924, '2024-11-05', 5)": 0, + "(924, '2024-11-05', 6)": 0, + "(924, '2024-11-05', 7)": 0, + "(924, '2024-11-06', 0)": 0, + "(924, '2024-11-06', 1)": 0, + "(924, '2024-11-06', 2)": 0, + "(924, '2024-11-06', 3)": 0, + "(924, '2024-11-06', 4)": 0, + "(924, '2024-11-06', 5)": 0, + "(924, '2024-11-06', 6)": 0, + "(924, '2024-11-06', 7)": 0, + "(924, '2024-11-07', 0)": 0, + "(924, '2024-11-07', 1)": 0, + "(924, '2024-11-07', 2)": 0, + "(924, '2024-11-07', 3)": 0, + "(924, '2024-11-07', 4)": 0, + "(924, '2024-11-07', 5)": 0, + "(924, '2024-11-07', 6)": 0, + "(924, '2024-11-07', 7)": 0, + "(924, '2024-11-08', 0)": 0, + "(924, '2024-11-08', 1)": 0, + "(924, '2024-11-08', 2)": 0, + "(924, '2024-11-08', 3)": 0, + "(924, '2024-11-08', 4)": 0, + "(924, '2024-11-08', 5)": 0, + "(924, '2024-11-08', 6)": 0, + "(924, '2024-11-08', 7)": 0, + "(924, '2024-11-09', 0)": 0, + "(924, '2024-11-09', 1)": 1, + "(924, '2024-11-09', 2)": 0, + "(924, '2024-11-09', 3)": 0, + "(924, '2024-11-09', 4)": 0, + "(924, '2024-11-09', 5)": 0, + "(924, '2024-11-09', 6)": 0, + "(924, '2024-11-09', 7)": 0, + "(924, '2024-11-10', 0)": 0, + "(924, '2024-11-10', 1)": 0, + "(924, '2024-11-10', 2)": 0, + "(924, '2024-11-10', 3)": 0, + "(924, '2024-11-10', 4)": 0, + "(924, '2024-11-10', 5)": 0, + "(924, '2024-11-10', 6)": 0, + "(924, '2024-11-10', 7)": 0, + "(924, '2024-11-11', 0)": 0, + "(924, '2024-11-11', 1)": 0, + "(924, '2024-11-11', 2)": 0, + "(924, '2024-11-11', 3)": 0, + "(924, '2024-11-11', 4)": 0, + "(924, '2024-11-11', 5)": 0, + "(924, '2024-11-11', 6)": 0, + "(924, '2024-11-11', 7)": 0, + "(924, '2024-11-12', 0)": 0, + "(924, '2024-11-12', 1)": 0, + "(924, '2024-11-12', 2)": 0, + "(924, '2024-11-12', 3)": 0, + "(924, '2024-11-12', 4)": 0, + "(924, '2024-11-12', 5)": 0, + "(924, '2024-11-12', 6)": 0, + "(924, '2024-11-12', 7)": 0, + "(924, '2024-11-13', 0)": 0, + "(924, '2024-11-13', 1)": 1, + "(924, '2024-11-13', 2)": 0, + "(924, '2024-11-13', 3)": 0, + "(924, '2024-11-13', 4)": 0, + "(924, '2024-11-13', 5)": 0, + "(924, '2024-11-13', 6)": 0, + "(924, '2024-11-13', 7)": 0, + "(924, '2024-11-14', 0)": 0, + "(924, '2024-11-14', 1)": 0, + "(924, '2024-11-14', 2)": 0, + "(924, '2024-11-14', 3)": 0, + "(924, '2024-11-14', 4)": 0, + "(924, '2024-11-14', 5)": 0, + "(924, '2024-11-14', 6)": 0, + "(924, '2024-11-14', 7)": 0, + "(924, '2024-11-15', 0)": 0, + "(924, '2024-11-15', 1)": 0, + "(924, '2024-11-15', 2)": 0, + "(924, '2024-11-15', 3)": 0, + "(924, '2024-11-15', 4)": 0, + "(924, '2024-11-15', 5)": 0, + "(924, '2024-11-15', 6)": 0, + "(924, '2024-11-15', 7)": 0, + "(924, '2024-11-16', 0)": 0, + "(924, '2024-11-16', 1)": 0, + "(924, '2024-11-16', 2)": 0, + "(924, '2024-11-16', 3)": 0, + "(924, '2024-11-16', 4)": 0, + "(924, '2024-11-16', 5)": 0, + "(924, '2024-11-16', 6)": 0, + "(924, '2024-11-16', 7)": 0, + "(924, '2024-11-17', 0)": 0, + "(924, '2024-11-17', 1)": 0, + "(924, '2024-11-17', 2)": 0, + "(924, '2024-11-17', 3)": 0, + "(924, '2024-11-17', 4)": 0, + "(924, '2024-11-17', 5)": 0, + "(924, '2024-11-17', 6)": 0, + "(924, '2024-11-17', 7)": 0, + "(924, '2024-11-18', 0)": 1, + "(924, '2024-11-18', 1)": 0, + "(924, '2024-11-18', 2)": 0, + "(924, '2024-11-18', 3)": 0, + "(924, '2024-11-18', 4)": 0, + "(924, '2024-11-18', 5)": 0, + "(924, '2024-11-18', 6)": 0, + "(924, '2024-11-18', 7)": 0, + "(924, '2024-11-19', 0)": 0, + "(924, '2024-11-19', 1)": 0, + "(924, '2024-11-19', 2)": 0, + "(924, '2024-11-19', 3)": 0, + "(924, '2024-11-19', 4)": 0, + "(924, '2024-11-19', 5)": 0, + "(924, '2024-11-19', 6)": 0, + "(924, '2024-11-19', 7)": 0, + "(924, '2024-11-20', 0)": 0, + "(924, '2024-11-20', 1)": 0, + "(924, '2024-11-20', 2)": 0, + "(924, '2024-11-20', 3)": 0, + "(924, '2024-11-20', 4)": 0, + "(924, '2024-11-20', 5)": 0, + "(924, '2024-11-20', 6)": 0, + "(924, '2024-11-20', 7)": 0, + "(924, '2024-11-21', 0)": 0, + "(924, '2024-11-21', 1)": 0, + "(924, '2024-11-21', 2)": 0, + "(924, '2024-11-21', 3)": 0, + "(924, '2024-11-21', 4)": 0, + "(924, '2024-11-21', 5)": 0, + "(924, '2024-11-21', 6)": 0, + "(924, '2024-11-21', 7)": 0, + "(924, '2024-11-22', 0)": 0, + "(924, '2024-11-22', 1)": 0, + "(924, '2024-11-22', 2)": 0, + "(924, '2024-11-22', 3)": 0, + "(924, '2024-11-22', 4)": 0, + "(924, '2024-11-22', 5)": 0, + "(924, '2024-11-22', 6)": 0, + "(924, '2024-11-22', 7)": 0, + "(924, '2024-11-23', 0)": 0, + "(924, '2024-11-23', 1)": 0, + "(924, '2024-11-23', 2)": 0, + "(924, '2024-11-23', 3)": 0, + "(924, '2024-11-23', 4)": 0, + "(924, '2024-11-23', 5)": 0, + "(924, '2024-11-23', 6)": 0, + "(924, '2024-11-23', 7)": 0, + "(924, '2024-11-24', 0)": 0, + "(924, '2024-11-24', 1)": 0, + "(924, '2024-11-24', 2)": 0, + "(924, '2024-11-24', 3)": 0, + "(924, '2024-11-24', 4)": 0, + "(924, '2024-11-24', 5)": 0, + "(924, '2024-11-24', 6)": 0, + "(924, '2024-11-24', 7)": 0, + "(924, '2024-11-25', 0)": 0, + "(924, '2024-11-25', 1)": 0, + "(924, '2024-11-25', 2)": 0, + "(924, '2024-11-25', 3)": 0, + "(924, '2024-11-25', 4)": 0, + "(924, '2024-11-25', 5)": 0, + "(924, '2024-11-25', 6)": 0, + "(924, '2024-11-25', 7)": 0, + "(924, '2024-11-26', 0)": 0, + "(924, '2024-11-26', 1)": 1, + "(924, '2024-11-26', 2)": 0, + "(924, '2024-11-26', 3)": 0, + "(924, '2024-11-26', 4)": 0, + "(924, '2024-11-26', 5)": 0, + "(924, '2024-11-26', 6)": 0, + "(924, '2024-11-26', 7)": 0, + "(924, '2024-11-27', 0)": 0, + "(924, '2024-11-27', 1)": 1, + "(924, '2024-11-27', 2)": 0, + "(924, '2024-11-27', 3)": 0, + "(924, '2024-11-27', 4)": 0, + "(924, '2024-11-27', 5)": 0, + "(924, '2024-11-27', 6)": 0, + "(924, '2024-11-27', 7)": 0, + "(924, '2024-11-28', 0)": 0, + "(924, '2024-11-28', 1)": 0, + "(924, '2024-11-28', 2)": 0, + "(924, '2024-11-28', 3)": 0, + "(924, '2024-11-28', 4)": 0, + "(924, '2024-11-28', 5)": 0, + "(924, '2024-11-28', 6)": 0, + "(924, '2024-11-28', 7)": 0, + "(924, '2024-11-29', 0)": 0, + "(924, '2024-11-29', 1)": 0, + "(924, '2024-11-29', 2)": 0, + "(924, '2024-11-29', 3)": 0, + "(924, '2024-11-29', 4)": 0, + "(924, '2024-11-29', 5)": 0, + "(924, '2024-11-29', 6)": 0, + "(924, '2024-11-29', 7)": 0, + "(924, '2024-11-30', 0)": 0, + "(924, '2024-11-30', 1)": 0, + "(924, '2024-11-30', 2)": 0, + "(924, '2024-11-30', 3)": 0, + "(924, '2024-11-30', 4)": 0, + "(924, '2024-11-30', 5)": 0, + "(924, '2024-11-30', 6)": 0, + "(924, '2024-11-30', 7)": 0, + "(925, '2024-11-01', 0)": 0, + "(925, '2024-11-01', 1)": 0, + "(925, '2024-11-01', 2)": 0, + "(925, '2024-11-01', 3)": 0, + "(925, '2024-11-01', 4)": 0, + "(925, '2024-11-01', 5)": 0, + "(925, '2024-11-01', 6)": 0, + "(925, '2024-11-01', 7)": 0, + "(925, '2024-11-02', 0)": 0, + "(925, '2024-11-02', 1)": 0, + "(925, '2024-11-02', 2)": 0, + "(925, '2024-11-02', 3)": 0, + "(925, '2024-11-02', 4)": 0, + "(925, '2024-11-02', 5)": 0, + "(925, '2024-11-02', 6)": 0, + "(925, '2024-11-02', 7)": 0, + "(925, '2024-11-03', 0)": 0, + "(925, '2024-11-03', 1)": 0, + "(925, '2024-11-03', 2)": 0, + "(925, '2024-11-03', 3)": 0, + "(925, '2024-11-03', 4)": 0, + "(925, '2024-11-03', 5)": 0, + "(925, '2024-11-03', 6)": 0, + "(925, '2024-11-03', 7)": 0, + "(925, '2024-11-04', 0)": 0, + "(925, '2024-11-04', 1)": 0, + "(925, '2024-11-04', 2)": 0, + "(925, '2024-11-04', 3)": 1, + "(925, '2024-11-04', 4)": 0, + "(925, '2024-11-04', 5)": 0, + "(925, '2024-11-04', 6)": 0, + "(925, '2024-11-04', 7)": 0, + "(925, '2024-11-05', 0)": 0, + "(925, '2024-11-05', 1)": 0, + "(925, '2024-11-05', 2)": 0, + "(925, '2024-11-05', 3)": 0, + "(925, '2024-11-05', 4)": 0, + "(925, '2024-11-05', 5)": 0, + "(925, '2024-11-05', 6)": 0, + "(925, '2024-11-05', 7)": 0, + "(925, '2024-11-06', 0)": 0, + "(925, '2024-11-06', 1)": 0, + "(925, '2024-11-06', 2)": 0, + "(925, '2024-11-06', 3)": 0, + "(925, '2024-11-06', 4)": 0, + "(925, '2024-11-06', 5)": 0, + "(925, '2024-11-06', 6)": 0, + "(925, '2024-11-06', 7)": 0, + "(925, '2024-11-07', 0)": 0, + "(925, '2024-11-07', 1)": 0, + "(925, '2024-11-07', 2)": 0, + "(925, '2024-11-07', 3)": 0, + "(925, '2024-11-07', 4)": 0, + "(925, '2024-11-07', 5)": 0, + "(925, '2024-11-07', 6)": 0, + "(925, '2024-11-07', 7)": 0, + "(925, '2024-11-08', 0)": 0, + "(925, '2024-11-08', 1)": 0, + "(925, '2024-11-08', 2)": 0, + "(925, '2024-11-08', 3)": 0, + "(925, '2024-11-08', 4)": 0, + "(925, '2024-11-08', 5)": 0, + "(925, '2024-11-08', 6)": 0, + "(925, '2024-11-08', 7)": 0, + "(925, '2024-11-09', 0)": 0, + "(925, '2024-11-09', 1)": 0, + "(925, '2024-11-09', 2)": 0, + "(925, '2024-11-09', 3)": 0, + "(925, '2024-11-09', 4)": 0, + "(925, '2024-11-09', 5)": 0, + "(925, '2024-11-09', 6)": 0, + "(925, '2024-11-09', 7)": 0, + "(925, '2024-11-10', 0)": 0, + "(925, '2024-11-10', 1)": 0, + "(925, '2024-11-10', 2)": 0, + "(925, '2024-11-10', 3)": 0, + "(925, '2024-11-10', 4)": 0, + "(925, '2024-11-10', 5)": 0, + "(925, '2024-11-10', 6)": 0, + "(925, '2024-11-10', 7)": 0, + "(925, '2024-11-11', 0)": 0, + "(925, '2024-11-11', 1)": 0, + "(925, '2024-11-11', 2)": 0, + "(925, '2024-11-11', 3)": 1, + "(925, '2024-11-11', 4)": 0, + "(925, '2024-11-11', 5)": 0, + "(925, '2024-11-11', 6)": 0, + "(925, '2024-11-11', 7)": 0, + "(925, '2024-11-12', 0)": 0, + "(925, '2024-11-12', 1)": 0, + "(925, '2024-11-12', 2)": 0, + "(925, '2024-11-12', 3)": 1, + "(925, '2024-11-12', 4)": 0, + "(925, '2024-11-12', 5)": 0, + "(925, '2024-11-12', 6)": 0, + "(925, '2024-11-12', 7)": 0, + "(925, '2024-11-13', 0)": 0, + "(925, '2024-11-13', 1)": 0, + "(925, '2024-11-13', 2)": 0, + "(925, '2024-11-13', 3)": 0, + "(925, '2024-11-13', 4)": 0, + "(925, '2024-11-13', 5)": 0, + "(925, '2024-11-13', 6)": 0, + "(925, '2024-11-13', 7)": 0, + "(925, '2024-11-14', 0)": 0, + "(925, '2024-11-14', 1)": 0, + "(925, '2024-11-14', 2)": 0, + "(925, '2024-11-14', 3)": 1, + "(925, '2024-11-14', 4)": 0, + "(925, '2024-11-14', 5)": 0, + "(925, '2024-11-14', 6)": 0, + "(925, '2024-11-14', 7)": 0, + "(925, '2024-11-15', 0)": 0, + "(925, '2024-11-15', 1)": 0, + "(925, '2024-11-15', 2)": 0, + "(925, '2024-11-15', 3)": 0, + "(925, '2024-11-15', 4)": 0, + "(925, '2024-11-15', 5)": 0, + "(925, '2024-11-15', 6)": 0, + "(925, '2024-11-15', 7)": 0, + "(925, '2024-11-16', 0)": 0, + "(925, '2024-11-16', 1)": 0, + "(925, '2024-11-16', 2)": 0, + "(925, '2024-11-16', 3)": 0, + "(925, '2024-11-16', 4)": 0, + "(925, '2024-11-16', 5)": 0, + "(925, '2024-11-16', 6)": 0, + "(925, '2024-11-16', 7)": 0, + "(925, '2024-11-17', 0)": 0, + "(925, '2024-11-17', 1)": 0, + "(925, '2024-11-17', 2)": 0, + "(925, '2024-11-17', 3)": 0, + "(925, '2024-11-17', 4)": 0, + "(925, '2024-11-17', 5)": 0, + "(925, '2024-11-17', 6)": 0, + "(925, '2024-11-17', 7)": 0, + "(925, '2024-11-18', 0)": 0, + "(925, '2024-11-18', 1)": 0, + "(925, '2024-11-18', 2)": 0, + "(925, '2024-11-18', 3)": 0, + "(925, '2024-11-18', 4)": 0, + "(925, '2024-11-18', 5)": 0, + "(925, '2024-11-18', 6)": 0, + "(925, '2024-11-18', 7)": 0, + "(925, '2024-11-19', 0)": 0, + "(925, '2024-11-19', 1)": 0, + "(925, '2024-11-19', 2)": 0, + "(925, '2024-11-19', 3)": 0, + "(925, '2024-11-19', 4)": 0, + "(925, '2024-11-19', 5)": 0, + "(925, '2024-11-19', 6)": 0, + "(925, '2024-11-19', 7)": 0, + "(925, '2024-11-20', 0)": 0, + "(925, '2024-11-20', 1)": 0, + "(925, '2024-11-20', 2)": 0, + "(925, '2024-11-20', 3)": 1, + "(925, '2024-11-20', 4)": 0, + "(925, '2024-11-20', 5)": 0, + "(925, '2024-11-20', 6)": 0, + "(925, '2024-11-20', 7)": 0, + "(925, '2024-11-21', 0)": 0, + "(925, '2024-11-21', 1)": 0, + "(925, '2024-11-21', 2)": 0, + "(925, '2024-11-21', 3)": 0, + "(925, '2024-11-21', 4)": 0, + "(925, '2024-11-21', 5)": 0, + "(925, '2024-11-21', 6)": 0, + "(925, '2024-11-21', 7)": 0, + "(925, '2024-11-22', 0)": 0, + "(925, '2024-11-22', 1)": 0, + "(925, '2024-11-22', 2)": 0, + "(925, '2024-11-22', 3)": 1, + "(925, '2024-11-22', 4)": 0, + "(925, '2024-11-22', 5)": 0, + "(925, '2024-11-22', 6)": 0, + "(925, '2024-11-22', 7)": 0, + "(925, '2024-11-23', 0)": 0, + "(925, '2024-11-23', 1)": 0, + "(925, '2024-11-23', 2)": 0, + "(925, '2024-11-23', 3)": 0, + "(925, '2024-11-23', 4)": 0, + "(925, '2024-11-23', 5)": 0, + "(925, '2024-11-23', 6)": 0, + "(925, '2024-11-23', 7)": 0, + "(925, '2024-11-24', 0)": 0, + "(925, '2024-11-24', 1)": 0, + "(925, '2024-11-24', 2)": 0, + "(925, '2024-11-24', 3)": 0, + "(925, '2024-11-24', 4)": 0, + "(925, '2024-11-24', 5)": 0, + "(925, '2024-11-24', 6)": 0, + "(925, '2024-11-24', 7)": 0, + "(925, '2024-11-25', 0)": 0, + "(925, '2024-11-25', 1)": 0, + "(925, '2024-11-25', 2)": 0, + "(925, '2024-11-25', 3)": 0, + "(925, '2024-11-25', 4)": 0, + "(925, '2024-11-25', 5)": 0, + "(925, '2024-11-25', 6)": 0, + "(925, '2024-11-25', 7)": 0, + "(925, '2024-11-26', 0)": 0, + "(925, '2024-11-26', 1)": 0, + "(925, '2024-11-26', 2)": 0, + "(925, '2024-11-26', 3)": 1, + "(925, '2024-11-26', 4)": 0, + "(925, '2024-11-26', 5)": 0, + "(925, '2024-11-26', 6)": 0, + "(925, '2024-11-26', 7)": 0, + "(925, '2024-11-27', 0)": 0, + "(925, '2024-11-27', 1)": 0, + "(925, '2024-11-27', 2)": 0, + "(925, '2024-11-27', 3)": 0, + "(925, '2024-11-27', 4)": 0, + "(925, '2024-11-27', 5)": 0, + "(925, '2024-11-27', 6)": 0, + "(925, '2024-11-27', 7)": 0, + "(925, '2024-11-28', 0)": 0, + "(925, '2024-11-28', 1)": 0, + "(925, '2024-11-28', 2)": 0, + "(925, '2024-11-28', 3)": 1, + "(925, '2024-11-28', 4)": 0, + "(925, '2024-11-28', 5)": 0, + "(925, '2024-11-28', 6)": 0, + "(925, '2024-11-28', 7)": 0, + "(925, '2024-11-29', 0)": 0, + "(925, '2024-11-29', 1)": 0, + "(925, '2024-11-29', 2)": 0, + "(925, '2024-11-29', 3)": 0, + "(925, '2024-11-29', 4)": 0, + "(925, '2024-11-29', 5)": 0, + "(925, '2024-11-29', 6)": 0, + "(925, '2024-11-29', 7)": 0, + "(925, '2024-11-30', 0)": 0, + "(925, '2024-11-30', 1)": 0, + "(925, '2024-11-30', 2)": 0, + "(925, '2024-11-30', 3)": 1, + "(925, '2024-11-30', 4)": 0, + "(925, '2024-11-30', 5)": 0, + "(925, '2024-11-30', 6)": 0, + "(925, '2024-11-30', 7)": 0, + "(927, '2024-11-01', 0)": 0, + "(927, '2024-11-01', 1)": 0, + "(927, '2024-11-01', 2)": 0, + "(927, '2024-11-01', 3)": 0, + "(927, '2024-11-01', 4)": 0, + "(927, '2024-11-01', 5)": 0, + "(927, '2024-11-01', 6)": 0, + "(927, '2024-11-01', 7)": 0, + "(927, '2024-11-02', 0)": 0, + "(927, '2024-11-02', 1)": 0, + "(927, '2024-11-02', 2)": 0, + "(927, '2024-11-02', 3)": 0, + "(927, '2024-11-02', 4)": 0, + "(927, '2024-11-02', 5)": 0, + "(927, '2024-11-02', 6)": 0, + "(927, '2024-11-02', 7)": 0, + "(927, '2024-11-03', 0)": 0, + "(927, '2024-11-03', 1)": 0, + "(927, '2024-11-03', 2)": 0, + "(927, '2024-11-03', 3)": 1, + "(927, '2024-11-03', 4)": 0, + "(927, '2024-11-03', 5)": 0, + "(927, '2024-11-03', 6)": 0, + "(927, '2024-11-03', 7)": 0, + "(927, '2024-11-04', 0)": 0, + "(927, '2024-11-04', 1)": 0, + "(927, '2024-11-04', 2)": 0, + "(927, '2024-11-04', 3)": 0, + "(927, '2024-11-04', 4)": 0, + "(927, '2024-11-04', 5)": 0, + "(927, '2024-11-04', 6)": 0, + "(927, '2024-11-04', 7)": 0, + "(927, '2024-11-05', 0)": 0, + "(927, '2024-11-05', 1)": 0, + "(927, '2024-11-05', 2)": 0, + "(927, '2024-11-05', 3)": 0, + "(927, '2024-11-05', 4)": 0, + "(927, '2024-11-05', 5)": 0, + "(927, '2024-11-05', 6)": 0, + "(927, '2024-11-05', 7)": 0, + "(927, '2024-11-06', 0)": 0, + "(927, '2024-11-06', 1)": 0, + "(927, '2024-11-06', 2)": 0, + "(927, '2024-11-06', 3)": 0, + "(927, '2024-11-06', 4)": 0, + "(927, '2024-11-06', 5)": 0, + "(927, '2024-11-06', 6)": 0, + "(927, '2024-11-06', 7)": 0, + "(927, '2024-11-07', 0)": 1, + "(927, '2024-11-07', 1)": 0, + "(927, '2024-11-07', 2)": 0, + "(927, '2024-11-07', 3)": 0, + "(927, '2024-11-07', 4)": 0, + "(927, '2024-11-07', 5)": 0, + "(927, '2024-11-07', 6)": 0, + "(927, '2024-11-07', 7)": 0, + "(927, '2024-11-08', 0)": 1, + "(927, '2024-11-08', 1)": 0, + "(927, '2024-11-08', 2)": 0, + "(927, '2024-11-08', 3)": 0, + "(927, '2024-11-08', 4)": 0, + "(927, '2024-11-08', 5)": 0, + "(927, '2024-11-08', 6)": 0, + "(927, '2024-11-08', 7)": 0, + "(927, '2024-11-09', 0)": 1, + "(927, '2024-11-09', 1)": 0, + "(927, '2024-11-09', 2)": 0, + "(927, '2024-11-09', 3)": 0, + "(927, '2024-11-09', 4)": 0, + "(927, '2024-11-09', 5)": 0, + "(927, '2024-11-09', 6)": 0, + "(927, '2024-11-09', 7)": 0, + "(927, '2024-11-10', 0)": 1, + "(927, '2024-11-10', 1)": 0, + "(927, '2024-11-10', 2)": 0, + "(927, '2024-11-10', 3)": 0, + "(927, '2024-11-10', 4)": 0, + "(927, '2024-11-10', 5)": 0, + "(927, '2024-11-10', 6)": 0, + "(927, '2024-11-10', 7)": 0, + "(927, '2024-11-11', 0)": 0, + "(927, '2024-11-11', 1)": 0, + "(927, '2024-11-11', 2)": 1, + "(927, '2024-11-11', 3)": 0, + "(927, '2024-11-11', 4)": 0, + "(927, '2024-11-11', 5)": 0, + "(927, '2024-11-11', 6)": 0, + "(927, '2024-11-11', 7)": 0, + "(927, '2024-11-12', 0)": 0, + "(927, '2024-11-12', 1)": 0, + "(927, '2024-11-12', 2)": 1, + "(927, '2024-11-12', 3)": 0, + "(927, '2024-11-12', 4)": 0, + "(927, '2024-11-12', 5)": 0, + "(927, '2024-11-12', 6)": 0, + "(927, '2024-11-12', 7)": 0, + "(927, '2024-11-13', 0)": 0, + "(927, '2024-11-13', 1)": 0, + "(927, '2024-11-13', 2)": 1, + "(927, '2024-11-13', 3)": 0, + "(927, '2024-11-13', 4)": 0, + "(927, '2024-11-13', 5)": 0, + "(927, '2024-11-13', 6)": 0, + "(927, '2024-11-13', 7)": 0, + "(927, '2024-11-14', 0)": 0, + "(927, '2024-11-14', 1)": 0, + "(927, '2024-11-14', 2)": 0, + "(927, '2024-11-14', 3)": 0, + "(927, '2024-11-14', 4)": 0, + "(927, '2024-11-14', 5)": 0, + "(927, '2024-11-14', 6)": 0, + "(927, '2024-11-14', 7)": 0, + "(927, '2024-11-15', 0)": 0, + "(927, '2024-11-15', 1)": 0, + "(927, '2024-11-15', 2)": 0, + "(927, '2024-11-15', 3)": 0, + "(927, '2024-11-15', 4)": 0, + "(927, '2024-11-15', 5)": 0, + "(927, '2024-11-15', 6)": 0, + "(927, '2024-11-15', 7)": 0, + "(927, '2024-11-16', 0)": 0, + "(927, '2024-11-16', 1)": 0, + "(927, '2024-11-16', 2)": 1, + "(927, '2024-11-16', 3)": 0, + "(927, '2024-11-16', 4)": 0, + "(927, '2024-11-16', 5)": 0, + "(927, '2024-11-16', 6)": 0, + "(927, '2024-11-16', 7)": 0, + "(927, '2024-11-17', 0)": 0, + "(927, '2024-11-17', 1)": 0, + "(927, '2024-11-17', 2)": 1, + "(927, '2024-11-17', 3)": 0, + "(927, '2024-11-17', 4)": 0, + "(927, '2024-11-17', 5)": 0, + "(927, '2024-11-17', 6)": 0, + "(927, '2024-11-17', 7)": 0, + "(927, '2024-11-18', 0)": 0, + "(927, '2024-11-18', 1)": 0, + "(927, '2024-11-18', 2)": 0, + "(927, '2024-11-18', 3)": 0, + "(927, '2024-11-18', 4)": 0, + "(927, '2024-11-18', 5)": 0, + "(927, '2024-11-18', 6)": 0, + "(927, '2024-11-18', 7)": 0, + "(927, '2024-11-19', 0)": 1, + "(927, '2024-11-19', 1)": 0, + "(927, '2024-11-19', 2)": 0, + "(927, '2024-11-19', 3)": 0, + "(927, '2024-11-19', 4)": 0, + "(927, '2024-11-19', 5)": 0, + "(927, '2024-11-19', 6)": 0, + "(927, '2024-11-19', 7)": 0, + "(927, '2024-11-20', 0)": 0, + "(927, '2024-11-20', 1)": 0, + "(927, '2024-11-20', 2)": 0, + "(927, '2024-11-20', 3)": 0, + "(927, '2024-11-20', 4)": 0, + "(927, '2024-11-20', 5)": 0, + "(927, '2024-11-20', 6)": 0, + "(927, '2024-11-20', 7)": 0, + "(927, '2024-11-21', 0)": 1, + "(927, '2024-11-21', 1)": 0, + "(927, '2024-11-21', 2)": 0, + "(927, '2024-11-21', 3)": 0, + "(927, '2024-11-21', 4)": 0, + "(927, '2024-11-21', 5)": 0, + "(927, '2024-11-21', 6)": 0, + "(927, '2024-11-21', 7)": 0, + "(927, '2024-11-22', 0)": 0, + "(927, '2024-11-22', 1)": 0, + "(927, '2024-11-22', 2)": 1, + "(927, '2024-11-22', 3)": 0, + "(927, '2024-11-22', 4)": 0, + "(927, '2024-11-22', 5)": 0, + "(927, '2024-11-22', 6)": 0, + "(927, '2024-11-22', 7)": 0, + "(927, '2024-11-23', 0)": 0, + "(927, '2024-11-23', 1)": 0, + "(927, '2024-11-23', 2)": 1, + "(927, '2024-11-23', 3)": 0, + "(927, '2024-11-23', 4)": 0, + "(927, '2024-11-23', 5)": 0, + "(927, '2024-11-23', 6)": 0, + "(927, '2024-11-23', 7)": 0, + "(927, '2024-11-24', 0)": 0, + "(927, '2024-11-24', 1)": 0, + "(927, '2024-11-24', 2)": 1, + "(927, '2024-11-24', 3)": 0, + "(927, '2024-11-24', 4)": 0, + "(927, '2024-11-24', 5)": 0, + "(927, '2024-11-24', 6)": 0, + "(927, '2024-11-24', 7)": 0, + "(927, '2024-11-25', 0)": 0, + "(927, '2024-11-25', 1)": 0, + "(927, '2024-11-25', 2)": 1, + "(927, '2024-11-25', 3)": 0, + "(927, '2024-11-25', 4)": 0, + "(927, '2024-11-25', 5)": 0, + "(927, '2024-11-25', 6)": 0, + "(927, '2024-11-25', 7)": 0, + "(927, '2024-11-26', 0)": 0, + "(927, '2024-11-26', 1)": 0, + "(927, '2024-11-26', 2)": 1, + "(927, '2024-11-26', 3)": 0, + "(927, '2024-11-26', 4)": 0, + "(927, '2024-11-26', 5)": 0, + "(927, '2024-11-26', 6)": 0, + "(927, '2024-11-26', 7)": 0, + "(927, '2024-11-27', 0)": 0, + "(927, '2024-11-27', 1)": 0, + "(927, '2024-11-27', 2)": 0, + "(927, '2024-11-27', 3)": 0, + "(927, '2024-11-27', 4)": 0, + "(927, '2024-11-27', 5)": 0, + "(927, '2024-11-27', 6)": 0, + "(927, '2024-11-27', 7)": 0, + "(927, '2024-11-28', 0)": 0, + "(927, '2024-11-28', 1)": 0, + "(927, '2024-11-28', 2)": 0, + "(927, '2024-11-28', 3)": 0, + "(927, '2024-11-28', 4)": 0, + "(927, '2024-11-28', 5)": 0, + "(927, '2024-11-28', 6)": 0, + "(927, '2024-11-28', 7)": 0, + "(927, '2024-11-29', 0)": 1, + "(927, '2024-11-29', 1)": 0, + "(927, '2024-11-29', 2)": 0, + "(927, '2024-11-29', 3)": 0, + "(927, '2024-11-29', 4)": 0, + "(927, '2024-11-29', 5)": 0, + "(927, '2024-11-29', 6)": 0, + "(927, '2024-11-29', 7)": 0, + "(927, '2024-11-30', 0)": 0, + "(927, '2024-11-30', 1)": 0, + "(927, '2024-11-30', 2)": 0, + "(927, '2024-11-30', 3)": 0, + "(927, '2024-11-30', 4)": 0, + "(927, '2024-11-30', 5)": 0, + "(927, '2024-11-30', 6)": 0, + "(927, '2024-11-30', 7)": 0, + "(928, '2024-11-01', 0)": 0, + "(928, '2024-11-01', 1)": 0, + "(928, '2024-11-01', 2)": 0, + "(928, '2024-11-01', 3)": 0, + "(928, '2024-11-01', 4)": 0, + "(928, '2024-11-01', 5)": 0, + "(928, '2024-11-01', 6)": 0, + "(928, '2024-11-01', 7)": 0, + "(928, '2024-11-02', 0)": 0, + "(928, '2024-11-02', 1)": 0, + "(928, '2024-11-02', 2)": 0, + "(928, '2024-11-02', 3)": 0, + "(928, '2024-11-02', 4)": 0, + "(928, '2024-11-02', 5)": 0, + "(928, '2024-11-02', 6)": 0, + "(928, '2024-11-02', 7)": 0, + "(928, '2024-11-03', 0)": 0, + "(928, '2024-11-03', 1)": 0, + "(928, '2024-11-03', 2)": 0, + "(928, '2024-11-03', 3)": 0, + "(928, '2024-11-03', 4)": 0, + "(928, '2024-11-03', 5)": 0, + "(928, '2024-11-03', 6)": 0, + "(928, '2024-11-03', 7)": 0, + "(928, '2024-11-04', 0)": 0, + "(928, '2024-11-04', 1)": 0, + "(928, '2024-11-04', 2)": 0, + "(928, '2024-11-04', 3)": 1, + "(928, '2024-11-04', 4)": 0, + "(928, '2024-11-04', 5)": 0, + "(928, '2024-11-04', 6)": 0, + "(928, '2024-11-04', 7)": 0, + "(928, '2024-11-05', 0)": 0, + "(928, '2024-11-05', 1)": 0, + "(928, '2024-11-05', 2)": 0, + "(928, '2024-11-05', 3)": 0, + "(928, '2024-11-05', 4)": 0, + "(928, '2024-11-05', 5)": 0, + "(928, '2024-11-05', 6)": 0, + "(928, '2024-11-05', 7)": 0, + "(928, '2024-11-06', 0)": 0, + "(928, '2024-11-06', 1)": 0, + "(928, '2024-11-06', 2)": 0, + "(928, '2024-11-06', 3)": 0, + "(928, '2024-11-06', 4)": 0, + "(928, '2024-11-06', 5)": 0, + "(928, '2024-11-06', 6)": 0, + "(928, '2024-11-06', 7)": 0, + "(928, '2024-11-07', 0)": 0, + "(928, '2024-11-07', 1)": 0, + "(928, '2024-11-07', 2)": 0, + "(928, '2024-11-07', 3)": 0, + "(928, '2024-11-07', 4)": 0, + "(928, '2024-11-07', 5)": 0, + "(928, '2024-11-07', 6)": 0, + "(928, '2024-11-07', 7)": 0, + "(928, '2024-11-08', 0)": 0, + "(928, '2024-11-08', 1)": 0, + "(928, '2024-11-08', 2)": 0, + "(928, '2024-11-08', 3)": 1, + "(928, '2024-11-08', 4)": 0, + "(928, '2024-11-08', 5)": 0, + "(928, '2024-11-08', 6)": 0, + "(928, '2024-11-08', 7)": 0, + "(928, '2024-11-09', 0)": 0, + "(928, '2024-11-09', 1)": 0, + "(928, '2024-11-09', 2)": 0, + "(928, '2024-11-09', 3)": 0, + "(928, '2024-11-09', 4)": 0, + "(928, '2024-11-09', 5)": 0, + "(928, '2024-11-09', 6)": 0, + "(928, '2024-11-09', 7)": 0, + "(928, '2024-11-10', 0)": 0, + "(928, '2024-11-10', 1)": 0, + "(928, '2024-11-10', 2)": 0, + "(928, '2024-11-10', 3)": 0, + "(928, '2024-11-10', 4)": 0, + "(928, '2024-11-10', 5)": 0, + "(928, '2024-11-10', 6)": 0, + "(928, '2024-11-10', 7)": 0, + "(928, '2024-11-11', 0)": 0, + "(928, '2024-11-11', 1)": 0, + "(928, '2024-11-11', 2)": 0, + "(928, '2024-11-11', 3)": 0, + "(928, '2024-11-11', 4)": 0, + "(928, '2024-11-11', 5)": 0, + "(928, '2024-11-11', 6)": 0, + "(928, '2024-11-11', 7)": 0, + "(928, '2024-11-12', 0)": 0, + "(928, '2024-11-12', 1)": 0, + "(928, '2024-11-12', 2)": 0, + "(928, '2024-11-12', 3)": 0, + "(928, '2024-11-12', 4)": 0, + "(928, '2024-11-12', 5)": 0, + "(928, '2024-11-12', 6)": 0, + "(928, '2024-11-12', 7)": 0, + "(928, '2024-11-13', 0)": 0, + "(928, '2024-11-13', 1)": 0, + "(928, '2024-11-13', 2)": 0, + "(928, '2024-11-13', 3)": 0, + "(928, '2024-11-13', 4)": 0, + "(928, '2024-11-13', 5)": 0, + "(928, '2024-11-13', 6)": 0, + "(928, '2024-11-13', 7)": 0, + "(928, '2024-11-14', 0)": 0, + "(928, '2024-11-14', 1)": 0, + "(928, '2024-11-14', 2)": 0, + "(928, '2024-11-14', 3)": 0, + "(928, '2024-11-14', 4)": 0, + "(928, '2024-11-14', 5)": 0, + "(928, '2024-11-14', 6)": 0, + "(928, '2024-11-14', 7)": 0, + "(928, '2024-11-15', 0)": 0, + "(928, '2024-11-15', 1)": 0, + "(928, '2024-11-15', 2)": 0, + "(928, '2024-11-15', 3)": 0, + "(928, '2024-11-15', 4)": 0, + "(928, '2024-11-15', 5)": 0, + "(928, '2024-11-15', 6)": 0, + "(928, '2024-11-15', 7)": 0, + "(928, '2024-11-16', 0)": 0, + "(928, '2024-11-16', 1)": 0, + "(928, '2024-11-16', 2)": 0, + "(928, '2024-11-16', 3)": 0, + "(928, '2024-11-16', 4)": 0, + "(928, '2024-11-16', 5)": 0, + "(928, '2024-11-16', 6)": 0, + "(928, '2024-11-16', 7)": 0, + "(928, '2024-11-17', 0)": 0, + "(928, '2024-11-17', 1)": 0, + "(928, '2024-11-17', 2)": 0, + "(928, '2024-11-17', 3)": 0, + "(928, '2024-11-17', 4)": 0, + "(928, '2024-11-17', 5)": 0, + "(928, '2024-11-17', 6)": 0, + "(928, '2024-11-17', 7)": 0, + "(928, '2024-11-18', 0)": 0, + "(928, '2024-11-18', 1)": 0, + "(928, '2024-11-18', 2)": 0, + "(928, '2024-11-18', 3)": 0, + "(928, '2024-11-18', 4)": 0, + "(928, '2024-11-18', 5)": 0, + "(928, '2024-11-18', 6)": 0, + "(928, '2024-11-18', 7)": 0, + "(928, '2024-11-19', 0)": 0, + "(928, '2024-11-19', 1)": 0, + "(928, '2024-11-19', 2)": 0, + "(928, '2024-11-19', 3)": 0, + "(928, '2024-11-19', 4)": 0, + "(928, '2024-11-19', 5)": 0, + "(928, '2024-11-19', 6)": 0, + "(928, '2024-11-19', 7)": 0, + "(928, '2024-11-20', 0)": 0, + "(928, '2024-11-20', 1)": 0, + "(928, '2024-11-20', 2)": 0, + "(928, '2024-11-20', 3)": 0, + "(928, '2024-11-20', 4)": 0, + "(928, '2024-11-20', 5)": 0, + "(928, '2024-11-20', 6)": 0, + "(928, '2024-11-20', 7)": 0, + "(928, '2024-11-21', 0)": 0, + "(928, '2024-11-21', 1)": 0, + "(928, '2024-11-21', 2)": 0, + "(928, '2024-11-21', 3)": 1, + "(928, '2024-11-21', 4)": 0, + "(928, '2024-11-21', 5)": 0, + "(928, '2024-11-21', 6)": 0, + "(928, '2024-11-21', 7)": 0, + "(928, '2024-11-22', 0)": 0, + "(928, '2024-11-22', 1)": 0, + "(928, '2024-11-22', 2)": 0, + "(928, '2024-11-22', 3)": 0, + "(928, '2024-11-22', 4)": 0, + "(928, '2024-11-22', 5)": 0, + "(928, '2024-11-22', 6)": 0, + "(928, '2024-11-22', 7)": 0, + "(928, '2024-11-23', 0)": 0, + "(928, '2024-11-23', 1)": 0, + "(928, '2024-11-23', 2)": 0, + "(928, '2024-11-23', 3)": 0, + "(928, '2024-11-23', 4)": 0, + "(928, '2024-11-23', 5)": 0, + "(928, '2024-11-23', 6)": 0, + "(928, '2024-11-23', 7)": 0, + "(928, '2024-11-24', 0)": 0, + "(928, '2024-11-24', 1)": 0, + "(928, '2024-11-24', 2)": 0, + "(928, '2024-11-24', 3)": 0, + "(928, '2024-11-24', 4)": 0, + "(928, '2024-11-24', 5)": 0, + "(928, '2024-11-24', 6)": 0, + "(928, '2024-11-24', 7)": 0, + "(928, '2024-11-25', 0)": 0, + "(928, '2024-11-25', 1)": 0, + "(928, '2024-11-25', 2)": 0, + "(928, '2024-11-25', 3)": 0, + "(928, '2024-11-25', 4)": 0, + "(928, '2024-11-25', 5)": 0, + "(928, '2024-11-25', 6)": 0, + "(928, '2024-11-25', 7)": 0, + "(928, '2024-11-26', 0)": 0, + "(928, '2024-11-26', 1)": 0, + "(928, '2024-11-26', 2)": 0, + "(928, '2024-11-26', 3)": 0, + "(928, '2024-11-26', 4)": 0, + "(928, '2024-11-26', 5)": 0, + "(928, '2024-11-26', 6)": 0, + "(928, '2024-11-26', 7)": 0, + "(928, '2024-11-27', 0)": 0, + "(928, '2024-11-27', 1)": 0, + "(928, '2024-11-27', 2)": 0, + "(928, '2024-11-27', 3)": 0, + "(928, '2024-11-27', 4)": 0, + "(928, '2024-11-27', 5)": 0, + "(928, '2024-11-27', 6)": 0, + "(928, '2024-11-27', 7)": 0, + "(928, '2024-11-28', 0)": 0, + "(928, '2024-11-28', 1)": 0, + "(928, '2024-11-28', 2)": 0, + "(928, '2024-11-28', 3)": 0, + "(928, '2024-11-28', 4)": 0, + "(928, '2024-11-28', 5)": 0, + "(928, '2024-11-28', 6)": 0, + "(928, '2024-11-28', 7)": 0, + "(928, '2024-11-29', 0)": 0, + "(928, '2024-11-29', 1)": 0, + "(928, '2024-11-29', 2)": 0, + "(928, '2024-11-29', 3)": 0, + "(928, '2024-11-29', 4)": 0, + "(928, '2024-11-29', 5)": 0, + "(928, '2024-11-29', 6)": 0, + "(928, '2024-11-29', 7)": 0, + "(928, '2024-11-30', 0)": 0, + "(928, '2024-11-30', 1)": 0, + "(928, '2024-11-30', 2)": 0, + "(928, '2024-11-30', 3)": 0, + "(928, '2024-11-30', 4)": 0, + "(928, '2024-11-30', 5)": 0, + "(928, '2024-11-30', 6)": 0, + "(928, '2024-11-30', 7)": 0, + "e:0_d:2024-11-01": 0, + "e:0_d:2024-11-02": 0, + "e:0_d:2024-11-03": 0, + "e:0_d:2024-11-04": 0, + "e:0_d:2024-11-05": 0, + "e:0_d:2024-11-06": 1, + "e:0_d:2024-11-07": 0, + "e:0_d:2024-11-08": 0, + "e:0_d:2024-11-09": 0, + "e:0_d:2024-11-10": 0, + "e:0_d:2024-11-11": 0, + "e:0_d:2024-11-12": 0, + "e:0_d:2024-11-13": 1, + "e:0_d:2024-11-14": 0, + "e:0_d:2024-11-15": 0, + "e:0_d:2024-11-16": 0, + "e:0_d:2024-11-17": 0, + "e:0_d:2024-11-18": 0, + "e:0_d:2024-11-19": 0, + "e:0_d:2024-11-20": 0, + "e:0_d:2024-11-21": 1, + "e:0_d:2024-11-22": 0, + "e:0_d:2024-11-23": 0, + "e:0_d:2024-11-24": 0, + "e:0_d:2024-11-25": 0, + "e:0_d:2024-11-26": 0, + "e:0_d:2024-11-27": 0, + "e:0_d:2024-11-28": 0, + "e:0_d:2024-11-29": 0, + "e:0_d:2024-11-30": 0, + "e:1230_d:2024-11-01": 1, + "e:1230_d:2024-11-02": 0, + "e:1230_d:2024-11-03": 0, + "e:1230_d:2024-11-04": 1, + "e:1230_d:2024-11-05": 0, + "e:1230_d:2024-11-06": 1, + "e:1230_d:2024-11-07": 1, + "e:1230_d:2024-11-08": 0, + "e:1230_d:2024-11-09": 1, + "e:1230_d:2024-11-10": 1, + "e:1230_d:2024-11-11": 0, + "e:1230_d:2024-11-12": 1, + "e:1230_d:2024-11-13": 1, + "e:1230_d:2024-11-14": 0, + "e:1230_d:2024-11-15": 0, + "e:1230_d:2024-11-16": 0, + "e:1230_d:2024-11-17": 0, + "e:1230_d:2024-11-18": 1, + "e:1230_d:2024-11-19": 0, + "e:1230_d:2024-11-20": 1, + "e:1230_d:2024-11-21": 0, + "e:1230_d:2024-11-22": 1, + "e:1230_d:2024-11-23": 0, + "e:1230_d:2024-11-24": 1, + "e:1230_d:2024-11-25": 1, + "e:1230_d:2024-11-26": 1, + "e:1230_d:2024-11-27": 0, + "e:1230_d:2024-11-28": 1, + "e:1230_d:2024-11-29": 1, + "e:1230_d:2024-11-30": 1, + "e:1_d:2024-11-01": 0, + "e:1_d:2024-11-02": 1, + "e:1_d:2024-11-03": 0, + "e:1_d:2024-11-04": 0, + "e:1_d:2024-11-05": 0, + "e:1_d:2024-11-06": 0, + "e:1_d:2024-11-07": 0, + "e:1_d:2024-11-08": 0, + "e:1_d:2024-11-09": 0, + "e:1_d:2024-11-10": 0, + "e:1_d:2024-11-11": 0, + "e:1_d:2024-11-12": 0, + "e:1_d:2024-11-13": 0, + "e:1_d:2024-11-14": 0, + "e:1_d:2024-11-15": 0, + "e:1_d:2024-11-16": 0, + "e:1_d:2024-11-17": 0, + "e:1_d:2024-11-18": 0, + "e:1_d:2024-11-19": 0, + "e:1_d:2024-11-20": 0, + "e:1_d:2024-11-21": 0, + "e:1_d:2024-11-22": 1, + "e:1_d:2024-11-23": 0, + "e:1_d:2024-11-24": 0, + "e:1_d:2024-11-25": 1, + "e:1_d:2024-11-26": 0, + "e:1_d:2024-11-27": 0, + "e:1_d:2024-11-28": 0, + "e:1_d:2024-11-29": 0, + "e:1_d:2024-11-30": 0, + "e:2932_d:2024-11-01": 1, + "e:2932_d:2024-11-02": 0, + "e:2932_d:2024-11-03": 1, + "e:2932_d:2024-11-04": 1, + "e:2932_d:2024-11-05": 1, + "e:2932_d:2024-11-06": 1, + "e:2932_d:2024-11-07": 0, + "e:2932_d:2024-11-08": 0, + "e:2932_d:2024-11-09": 0, + "e:2932_d:2024-11-10": 1, + "e:2932_d:2024-11-11": 1, + "e:2932_d:2024-11-12": 0, + "e:2932_d:2024-11-13": 0, + "e:2932_d:2024-11-14": 1, + "e:2932_d:2024-11-15": 1, + "e:2932_d:2024-11-16": 1, + "e:2932_d:2024-11-17": 1, + "e:2932_d:2024-11-18": 1, + "e:2932_d:2024-11-19": 1, + "e:2932_d:2024-11-20": 0, + "e:2932_d:2024-11-21": 1, + "e:2932_d:2024-11-22": 0, + "e:2932_d:2024-11-23": 0, + "e:2932_d:2024-11-24": 1, + "e:2932_d:2024-11-25": 1, + "e:2932_d:2024-11-26": 1, + "e:2932_d:2024-11-27": 1, + "e:2932_d:2024-11-28": 0, + "e:2932_d:2024-11-29": 0, + "e:2932_d:2024-11-30": 0, + "e:2963_d:2024-11-01": 1, + "e:2963_d:2024-11-02": 1, + "e:2963_d:2024-11-03": 1, + "e:2963_d:2024-11-04": 0, + "e:2963_d:2024-11-05": 1, + "e:2963_d:2024-11-06": 1, + "e:2963_d:2024-11-07": 0, + "e:2963_d:2024-11-08": 0, + "e:2963_d:2024-11-09": 1, + "e:2963_d:2024-11-10": 0, + "e:2963_d:2024-11-11": 0, + "e:2963_d:2024-11-12": 0, + "e:2963_d:2024-11-13": 0, + "e:2963_d:2024-11-14": 1, + "e:2963_d:2024-11-15": 0, + "e:2963_d:2024-11-16": 1, + "e:2963_d:2024-11-17": 0, + "e:2963_d:2024-11-18": 1, + "e:2963_d:2024-11-19": 1, + "e:2963_d:2024-11-20": 1, + "e:2963_d:2024-11-21": 1, + "e:2963_d:2024-11-22": 1, + "e:2963_d:2024-11-23": 1, + "e:2963_d:2024-11-24": 1, + "e:2963_d:2024-11-25": 1, + "e:2963_d:2024-11-26": 0, + "e:2963_d:2024-11-27": 1, + "e:2963_d:2024-11-28": 1, + "e:2963_d:2024-11-29": 1, + "e:2963_d:2024-11-30": 0, + "e:2_d:2024-11-01": 0, + "e:2_d:2024-11-02": 1, + "e:2_d:2024-11-03": 0, + "e:2_d:2024-11-04": 0, + "e:2_d:2024-11-05": 0, + "e:2_d:2024-11-06": 1, + "e:2_d:2024-11-07": 0, + "e:2_d:2024-11-08": 1, + "e:2_d:2024-11-09": 0, + "e:2_d:2024-11-10": 0, + "e:2_d:2024-11-11": 0, + "e:2_d:2024-11-12": 0, + "e:2_d:2024-11-13": 0, + "e:2_d:2024-11-14": 0, + "e:2_d:2024-11-15": 0, + "e:2_d:2024-11-16": 1, + "e:2_d:2024-11-17": 0, + "e:2_d:2024-11-18": 0, + "e:2_d:2024-11-19": 0, + "e:2_d:2024-11-20": 1, + "e:2_d:2024-11-21": 0, + "e:2_d:2024-11-22": 0, + "e:2_d:2024-11-23": 0, + "e:2_d:2024-11-24": 0, + "e:2_d:2024-11-25": 0, + "e:2_d:2024-11-26": 0, + "e:2_d:2024-11-27": 0, + "e:2_d:2024-11-28": 0, + "e:2_d:2024-11-29": 0, + "e:2_d:2024-11-30": 0, + "e:3566_d:2024-11-01": 0, + "e:3566_d:2024-11-02": 0, + "e:3566_d:2024-11-03": 0, + "e:3566_d:2024-11-04": 0, + "e:3566_d:2024-11-05": 0, + "e:3566_d:2024-11-06": 0, + "e:3566_d:2024-11-07": 0, + "e:3566_d:2024-11-08": 0, + "e:3566_d:2024-11-09": 0, + "e:3566_d:2024-11-10": 0, + "e:3566_d:2024-11-11": 0, + "e:3566_d:2024-11-12": 0, + "e:3566_d:2024-11-13": 0, + "e:3566_d:2024-11-14": 0, + "e:3566_d:2024-11-15": 0, + "e:3566_d:2024-11-16": 0, + "e:3566_d:2024-11-17": 0, + "e:3566_d:2024-11-18": 0, + "e:3566_d:2024-11-19": 0, + "e:3566_d:2024-11-20": 0, + "e:3566_d:2024-11-21": 0, + "e:3566_d:2024-11-22": 0, + "e:3566_d:2024-11-23": 0, + "e:3566_d:2024-11-24": 0, + "e:3566_d:2024-11-25": 0, + "e:3566_d:2024-11-26": 0, + "e:3566_d:2024-11-27": 0, + "e:3566_d:2024-11-28": 0, + "e:3566_d:2024-11-29": 0, + "e:3566_d:2024-11-30": 0, + "e:3868_d:2024-11-01": 0, + "e:3868_d:2024-11-02": 0, + "e:3868_d:2024-11-03": 0, + "e:3868_d:2024-11-04": 1, + "e:3868_d:2024-11-05": 1, + "e:3868_d:2024-11-06": 0, + "e:3868_d:2024-11-07": 0, + "e:3868_d:2024-11-08": 1, + "e:3868_d:2024-11-09": 0, + "e:3868_d:2024-11-10": 1, + "e:3868_d:2024-11-11": 1, + "e:3868_d:2024-11-12": 1, + "e:3868_d:2024-11-13": 1, + "e:3868_d:2024-11-14": 0, + "e:3868_d:2024-11-15": 1, + "e:3868_d:2024-11-16": 0, + "e:3868_d:2024-11-17": 1, + "e:3868_d:2024-11-18": 1, + "e:3868_d:2024-11-19": 0, + "e:3868_d:2024-11-20": 0, + "e:3868_d:2024-11-21": 1, + "e:3868_d:2024-11-22": 1, + "e:3868_d:2024-11-23": 1, + "e:3868_d:2024-11-24": 1, + "e:3868_d:2024-11-25": 1, + "e:3868_d:2024-11-26": 0, + "e:3868_d:2024-11-27": 1, + "e:3868_d:2024-11-28": 1, + "e:3868_d:2024-11-29": 1, + "e:3868_d:2024-11-30": 0, + "e:3_d:2024-11-01": 0, + "e:3_d:2024-11-02": 0, + "e:3_d:2024-11-03": 0, + "e:3_d:2024-11-04": 0, + "e:3_d:2024-11-05": 0, + "e:3_d:2024-11-06": 0, + "e:3_d:2024-11-07": 0, + "e:3_d:2024-11-08": 0, + "e:3_d:2024-11-09": 0, + "e:3_d:2024-11-10": 0, + "e:3_d:2024-11-11": 0, + "e:3_d:2024-11-12": 0, + "e:3_d:2024-11-13": 0, + "e:3_d:2024-11-14": 0, + "e:3_d:2024-11-15": 0, + "e:3_d:2024-11-16": 0, + "e:3_d:2024-11-17": 0, + "e:3_d:2024-11-18": 0, + "e:3_d:2024-11-19": 0, + "e:3_d:2024-11-20": 0, + "e:3_d:2024-11-21": 0, + "e:3_d:2024-11-22": 0, + "e:3_d:2024-11-23": 0, + "e:3_d:2024-11-24": 0, + "e:3_d:2024-11-25": 0, + "e:3_d:2024-11-26": 0, + "e:3_d:2024-11-27": 0, + "e:3_d:2024-11-28": 0, + "e:3_d:2024-11-29": 0, + "e:3_d:2024-11-30": 0, + "e:4566_d:2024-11-01": 1, + "e:4566_d:2024-11-02": 0, + "e:4566_d:2024-11-03": 1, + "e:4566_d:2024-11-04": 1, + "e:4566_d:2024-11-05": 1, + "e:4566_d:2024-11-06": 0, + "e:4566_d:2024-11-07": 0, + "e:4566_d:2024-11-08": 1, + "e:4566_d:2024-11-09": 0, + "e:4566_d:2024-11-10": 1, + "e:4566_d:2024-11-11": 0, + "e:4566_d:2024-11-12": 1, + "e:4566_d:2024-11-13": 1, + "e:4566_d:2024-11-14": 1, + "e:4566_d:2024-11-15": 1, + "e:4566_d:2024-11-16": 0, + "e:4566_d:2024-11-17": 0, + "e:4566_d:2024-11-18": 0, + "e:4566_d:2024-11-19": 1, + "e:4566_d:2024-11-20": 1, + "e:4566_d:2024-11-21": 1, + "e:4566_d:2024-11-22": 0, + "e:4566_d:2024-11-23": 1, + "e:4566_d:2024-11-24": 0, + "e:4566_d:2024-11-25": 0, + "e:4566_d:2024-11-26": 1, + "e:4566_d:2024-11-27": 1, + "e:4566_d:2024-11-28": 0, + "e:4566_d:2024-11-29": 1, + "e:4566_d:2024-11-30": 1, + "e:459_d:2024-11-01": 1, + "e:459_d:2024-11-02": 0, + "e:459_d:2024-11-03": 0, + "e:459_d:2024-11-04": 0, + "e:459_d:2024-11-05": 0, + "e:459_d:2024-11-06": 0, + "e:459_d:2024-11-07": 0, + "e:459_d:2024-11-08": 0, + "e:459_d:2024-11-09": 0, + "e:459_d:2024-11-10": 0, + "e:459_d:2024-11-11": 0, + "e:459_d:2024-11-12": 0, + "e:459_d:2024-11-13": 0, + "e:459_d:2024-11-14": 1, + "e:459_d:2024-11-15": 1, + "e:459_d:2024-11-16": 0, + "e:459_d:2024-11-17": 0, + "e:459_d:2024-11-18": 0, + "e:459_d:2024-11-19": 0, + "e:459_d:2024-11-20": 0, + "e:459_d:2024-11-21": 0, + "e:459_d:2024-11-22": 0, + "e:459_d:2024-11-23": 0, + "e:459_d:2024-11-24": 0, + "e:459_d:2024-11-25": 0, + "e:459_d:2024-11-26": 0, + "e:459_d:2024-11-27": 0, + "e:459_d:2024-11-28": 0, + "e:459_d:2024-11-29": 0, + "e:459_d:2024-11-30": 1, + "e:4_d:2024-11-01": 0, + "e:4_d:2024-11-02": 0, + "e:4_d:2024-11-03": 0, + "e:4_d:2024-11-04": 0, + "e:4_d:2024-11-05": 0, + "e:4_d:2024-11-06": 0, + "e:4_d:2024-11-07": 0, + "e:4_d:2024-11-08": 0, + "e:4_d:2024-11-09": 0, + "e:4_d:2024-11-10": 0, + "e:4_d:2024-11-11": 0, + "e:4_d:2024-11-12": 0, + "e:4_d:2024-11-13": 0, + "e:4_d:2024-11-14": 0, + "e:4_d:2024-11-15": 0, + "e:4_d:2024-11-16": 0, + "e:4_d:2024-11-17": 0, + "e:4_d:2024-11-18": 0, + "e:4_d:2024-11-19": 0, + "e:4_d:2024-11-20": 0, + "e:4_d:2024-11-21": 0, + "e:4_d:2024-11-22": 0, + "e:4_d:2024-11-23": 0, + "e:4_d:2024-11-24": 0, + "e:4_d:2024-11-25": 0, + "e:4_d:2024-11-26": 0, + "e:4_d:2024-11-27": 0, + "e:4_d:2024-11-28": 0, + "e:4_d:2024-11-29": 0, + "e:4_d:2024-11-30": 0, + "e:5367_d:2024-11-01": 1, + "e:5367_d:2024-11-02": 1, + "e:5367_d:2024-11-03": 1, + "e:5367_d:2024-11-04": 0, + "e:5367_d:2024-11-05": 0, + "e:5367_d:2024-11-06": 1, + "e:5367_d:2024-11-07": 0, + "e:5367_d:2024-11-08": 0, + "e:5367_d:2024-11-09": 1, + "e:5367_d:2024-11-10": 0, + "e:5367_d:2024-11-11": 1, + "e:5367_d:2024-11-12": 1, + "e:5367_d:2024-11-13": 0, + "e:5367_d:2024-11-14": 1, + "e:5367_d:2024-11-15": 1, + "e:5367_d:2024-11-16": 1, + "e:5367_d:2024-11-17": 0, + "e:5367_d:2024-11-18": 1, + "e:5367_d:2024-11-19": 0, + "e:5367_d:2024-11-20": 1, + "e:5367_d:2024-11-21": 1, + "e:5367_d:2024-11-22": 1, + "e:5367_d:2024-11-23": 0, + "e:5367_d:2024-11-24": 1, + "e:5367_d:2024-11-25": 1, + "e:5367_d:2024-11-26": 0, + "e:5367_d:2024-11-27": 1, + "e:5367_d:2024-11-28": 1, + "e:5367_d:2024-11-29": 1, + "e:5367_d:2024-11-30": 0, + "e:5920_d:2024-11-01": 1, + "e:5920_d:2024-11-02": 0, + "e:5920_d:2024-11-03": 0, + "e:5920_d:2024-11-04": 0, + "e:5920_d:2024-11-05": 1, + "e:5920_d:2024-11-06": 1, + "e:5920_d:2024-11-07": 1, + "e:5920_d:2024-11-08": 1, + "e:5920_d:2024-11-09": 0, + "e:5920_d:2024-11-10": 1, + "e:5920_d:2024-11-11": 1, + "e:5920_d:2024-11-12": 0, + "e:5920_d:2024-11-13": 1, + "e:5920_d:2024-11-14": 1, + "e:5920_d:2024-11-15": 0, + "e:5920_d:2024-11-16": 1, + "e:5920_d:2024-11-17": 1, + "e:5920_d:2024-11-18": 0, + "e:5920_d:2024-11-19": 1, + "e:5920_d:2024-11-20": 0, + "e:5920_d:2024-11-21": 0, + "e:5920_d:2024-11-22": 1, + "e:5920_d:2024-11-23": 1, + "e:5920_d:2024-11-24": 0, + "e:5920_d:2024-11-25": 1, + "e:5920_d:2024-11-26": 0, + "e:5920_d:2024-11-27": 1, + "e:5920_d:2024-11-28": 1, + "e:5920_d:2024-11-29": 1, + "e:5920_d:2024-11-30": 0, + "e:5_d:2024-11-01": 0, + "e:5_d:2024-11-02": 0, + "e:5_d:2024-11-03": 0, + "e:5_d:2024-11-04": 0, + "e:5_d:2024-11-05": 0, + "e:5_d:2024-11-06": 0, + "e:5_d:2024-11-07": 0, + "e:5_d:2024-11-08": 0, + "e:5_d:2024-11-09": 0, + "e:5_d:2024-11-10": 0, + "e:5_d:2024-11-11": 0, + "e:5_d:2024-11-12": 0, + "e:5_d:2024-11-13": 0, + "e:5_d:2024-11-14": 0, + "e:5_d:2024-11-15": 0, + "e:5_d:2024-11-16": 0, + "e:5_d:2024-11-17": 0, + "e:5_d:2024-11-18": 0, + "e:5_d:2024-11-19": 0, + "e:5_d:2024-11-20": 0, + "e:5_d:2024-11-21": 0, + "e:5_d:2024-11-22": 0, + "e:5_d:2024-11-23": 0, + "e:5_d:2024-11-24": 0, + "e:5_d:2024-11-25": 0, + "e:5_d:2024-11-26": 0, + "e:5_d:2024-11-27": 0, + "e:5_d:2024-11-28": 0, + "e:5_d:2024-11-29": 0, + "e:5_d:2024-11-30": 0, + "e:6475_d:2024-11-01": 0, + "e:6475_d:2024-11-02": 0, + "e:6475_d:2024-11-03": 0, + "e:6475_d:2024-11-04": 0, + "e:6475_d:2024-11-05": 0, + "e:6475_d:2024-11-06": 0, + "e:6475_d:2024-11-07": 0, + "e:6475_d:2024-11-08": 0, + "e:6475_d:2024-11-09": 0, + "e:6475_d:2024-11-10": 0, + "e:6475_d:2024-11-11": 0, + "e:6475_d:2024-11-12": 0, + "e:6475_d:2024-11-13": 0, + "e:6475_d:2024-11-14": 0, + "e:6475_d:2024-11-15": 0, + "e:6475_d:2024-11-16": 0, + "e:6475_d:2024-11-17": 0, + "e:6475_d:2024-11-18": 0, + "e:6475_d:2024-11-19": 0, + "e:6475_d:2024-11-20": 0, + "e:6475_d:2024-11-21": 0, + "e:6475_d:2024-11-22": 0, + "e:6475_d:2024-11-23": 0, + "e:6475_d:2024-11-24": 0, + "e:6475_d:2024-11-25": 0, + "e:6475_d:2024-11-26": 0, + "e:6475_d:2024-11-27": 0, + "e:6475_d:2024-11-28": 0, + "e:6475_d:2024-11-29": 0, + "e:6475_d:2024-11-30": 1, + "e:6507_d:2024-11-01": 0, + "e:6507_d:2024-11-02": 1, + "e:6507_d:2024-11-03": 0, + "e:6507_d:2024-11-04": 1, + "e:6507_d:2024-11-05": 1, + "e:6507_d:2024-11-06": 1, + "e:6507_d:2024-11-07": 1, + "e:6507_d:2024-11-08": 1, + "e:6507_d:2024-11-09": 1, + "e:6507_d:2024-11-10": 1, + "e:6507_d:2024-11-11": 1, + "e:6507_d:2024-11-12": 0, + "e:6507_d:2024-11-13": 1, + "e:6507_d:2024-11-14": 0, + "e:6507_d:2024-11-15": 0, + "e:6507_d:2024-11-16": 1, + "e:6507_d:2024-11-17": 0, + "e:6507_d:2024-11-18": 0, + "e:6507_d:2024-11-19": 1, + "e:6507_d:2024-11-20": 1, + "e:6507_d:2024-11-21": 1, + "e:6507_d:2024-11-22": 1, + "e:6507_d:2024-11-23": 0, + "e:6507_d:2024-11-24": 0, + "e:6507_d:2024-11-25": 1, + "e:6507_d:2024-11-26": 1, + "e:6507_d:2024-11-27": 1, + "e:6507_d:2024-11-28": 1, + "e:6507_d:2024-11-29": 0, + "e:6507_d:2024-11-30": 1, + "e:6677_d:2024-11-01": 0, + "e:6677_d:2024-11-02": 0, + "e:6677_d:2024-11-03": 1, + "e:6677_d:2024-11-04": 1, + "e:6677_d:2024-11-05": 1, + "e:6677_d:2024-11-06": 1, + "e:6677_d:2024-11-07": 1, + "e:6677_d:2024-11-08": 0, + "e:6677_d:2024-11-09": 1, + "e:6677_d:2024-11-10": 1, + "e:6677_d:2024-11-11": 0, + "e:6677_d:2024-11-12": 1, + "e:6677_d:2024-11-13": 1, + "e:6677_d:2024-11-14": 1, + "e:6677_d:2024-11-15": 0, + "e:6677_d:2024-11-16": 0, + "e:6677_d:2024-11-17": 1, + "e:6677_d:2024-11-18": 1, + "e:6677_d:2024-11-19": 0, + "e:6677_d:2024-11-20": 0, + "e:6677_d:2024-11-21": 0, + "e:6677_d:2024-11-22": 0, + "e:6677_d:2024-11-23": 1, + "e:6677_d:2024-11-24": 1, + "e:6677_d:2024-11-25": 1, + "e:6677_d:2024-11-26": 1, + "e:6677_d:2024-11-27": 1, + "e:6677_d:2024-11-28": 1, + "e:6677_d:2024-11-29": 1, + "e:6677_d:2024-11-30": 1, + "e:6681_d:2024-11-01": 0, + "e:6681_d:2024-11-02": 1, + "e:6681_d:2024-11-03": 0, + "e:6681_d:2024-11-04": 0, + "e:6681_d:2024-11-05": 1, + "e:6681_d:2024-11-06": 0, + "e:6681_d:2024-11-07": 1, + "e:6681_d:2024-11-08": 0, + "e:6681_d:2024-11-09": 0, + "e:6681_d:2024-11-10": 0, + "e:6681_d:2024-11-11": 0, + "e:6681_d:2024-11-12": 1, + "e:6681_d:2024-11-13": 1, + "e:6681_d:2024-11-14": 0, + "e:6681_d:2024-11-15": 1, + "e:6681_d:2024-11-16": 0, + "e:6681_d:2024-11-17": 0, + "e:6681_d:2024-11-18": 0, + "e:6681_d:2024-11-19": 0, + "e:6681_d:2024-11-20": 0, + "e:6681_d:2024-11-21": 0, + "e:6681_d:2024-11-22": 0, + "e:6681_d:2024-11-23": 0, + "e:6681_d:2024-11-24": 0, + "e:6681_d:2024-11-25": 0, + "e:6681_d:2024-11-26": 0, + "e:6681_d:2024-11-27": 0, + "e:6681_d:2024-11-28": 0, + "e:6681_d:2024-11-29": 0, + "e:6681_d:2024-11-30": 0, + "e:6715_d:2024-11-01": 0, + "e:6715_d:2024-11-02": 0, + "e:6715_d:2024-11-03": 0, + "e:6715_d:2024-11-04": 0, + "e:6715_d:2024-11-05": 0, + "e:6715_d:2024-11-06": 1, + "e:6715_d:2024-11-07": 0, + "e:6715_d:2024-11-08": 0, + "e:6715_d:2024-11-09": 0, + "e:6715_d:2024-11-10": 0, + "e:6715_d:2024-11-11": 0, + "e:6715_d:2024-11-12": 0, + "e:6715_d:2024-11-13": 0, + "e:6715_d:2024-11-14": 0, + "e:6715_d:2024-11-15": 0, + "e:6715_d:2024-11-16": 0, + "e:6715_d:2024-11-17": 0, + "e:6715_d:2024-11-18": 0, + "e:6715_d:2024-11-19": 0, + "e:6715_d:2024-11-20": 0, + "e:6715_d:2024-11-21": 0, + "e:6715_d:2024-11-22": 0, + "e:6715_d:2024-11-23": 0, + "e:6715_d:2024-11-24": 0, + "e:6715_d:2024-11-25": 0, + "e:6715_d:2024-11-26": 0, + "e:6715_d:2024-11-27": 0, + "e:6715_d:2024-11-28": 0, + "e:6715_d:2024-11-29": 0, + "e:6715_d:2024-11-30": 0, + "e:6836_d:2024-11-01": 1, + "e:6836_d:2024-11-02": 0, + "e:6836_d:2024-11-03": 1, + "e:6836_d:2024-11-04": 1, + "e:6836_d:2024-11-05": 1, + "e:6836_d:2024-11-06": 0, + "e:6836_d:2024-11-07": 0, + "e:6836_d:2024-11-08": 0, + "e:6836_d:2024-11-09": 1, + "e:6836_d:2024-11-10": 1, + "e:6836_d:2024-11-11": 0, + "e:6836_d:2024-11-12": 0, + "e:6836_d:2024-11-13": 1, + "e:6836_d:2024-11-14": 1, + "e:6836_d:2024-11-15": 1, + "e:6836_d:2024-11-16": 1, + "e:6836_d:2024-11-17": 1, + "e:6836_d:2024-11-18": 1, + "e:6836_d:2024-11-19": 1, + "e:6836_d:2024-11-20": 0, + "e:6836_d:2024-11-21": 0, + "e:6836_d:2024-11-22": 1, + "e:6836_d:2024-11-23": 1, + "e:6836_d:2024-11-24": 1, + "e:6836_d:2024-11-25": 0, + "e:6836_d:2024-11-26": 0, + "e:6836_d:2024-11-27": 1, + "e:6836_d:2024-11-28": 0, + "e:6836_d:2024-11-29": 1, + "e:6836_d:2024-11-30": 1, + "e:6928_d:2024-11-01": 1, + "e:6928_d:2024-11-02": 1, + "e:6928_d:2024-11-03": 1, + "e:6928_d:2024-11-04": 1, + "e:6928_d:2024-11-05": 0, + "e:6928_d:2024-11-06": 0, + "e:6928_d:2024-11-07": 1, + "e:6928_d:2024-11-08": 1, + "e:6928_d:2024-11-09": 0, + "e:6928_d:2024-11-10": 1, + "e:6928_d:2024-11-11": 0, + "e:6928_d:2024-11-12": 1, + "e:6928_d:2024-11-13": 1, + "e:6928_d:2024-11-14": 0, + "e:6928_d:2024-11-15": 1, + "e:6928_d:2024-11-16": 1, + "e:6928_d:2024-11-17": 1, + "e:6928_d:2024-11-18": 1, + "e:6928_d:2024-11-19": 1, + "e:6928_d:2024-11-20": 0, + "e:6928_d:2024-11-21": 0, + "e:6928_d:2024-11-22": 1, + "e:6928_d:2024-11-23": 1, + "e:6928_d:2024-11-24": 0, + "e:6928_d:2024-11-25": 0, + "e:6928_d:2024-11-26": 1, + "e:6928_d:2024-11-27": 0, + "e:6928_d:2024-11-28": 1, + "e:6928_d:2024-11-29": 1, + "e:6928_d:2024-11-30": 1, + "e:6_d:2024-11-01": 1, + "e:6_d:2024-11-02": 0, + "e:6_d:2024-11-03": 0, + "e:6_d:2024-11-04": 0, + "e:6_d:2024-11-05": 0, + "e:6_d:2024-11-06": 1, + "e:6_d:2024-11-07": 0, + "e:6_d:2024-11-08": 1, + "e:6_d:2024-11-09": 0, + "e:6_d:2024-11-10": 1, + "e:6_d:2024-11-11": 0, + "e:6_d:2024-11-12": 0, + "e:6_d:2024-11-13": 0, + "e:6_d:2024-11-14": 1, + "e:6_d:2024-11-15": 0, + "e:6_d:2024-11-16": 1, + "e:6_d:2024-11-17": 1, + "e:6_d:2024-11-18": 0, + "e:6_d:2024-11-19": 1, + "e:6_d:2024-11-20": 0, + "e:6_d:2024-11-21": 0, + "e:6_d:2024-11-22": 0, + "e:6_d:2024-11-23": 0, + "e:6_d:2024-11-24": 1, + "e:6_d:2024-11-25": 0, + "e:6_d:2024-11-26": 0, + "e:6_d:2024-11-27": 0, + "e:6_d:2024-11-28": 0, + "e:6_d:2024-11-29": 0, + "e:6_d:2024-11-30": 0, + "e:7496_d:2024-11-01": 0, + "e:7496_d:2024-11-02": 0, + "e:7496_d:2024-11-03": 0, + "e:7496_d:2024-11-04": 0, + "e:7496_d:2024-11-05": 0, + "e:7496_d:2024-11-06": 0, + "e:7496_d:2024-11-07": 0, + "e:7496_d:2024-11-08": 0, + "e:7496_d:2024-11-09": 0, + "e:7496_d:2024-11-10": 0, + "e:7496_d:2024-11-11": 0, + "e:7496_d:2024-11-12": 0, + "e:7496_d:2024-11-13": 0, + "e:7496_d:2024-11-14": 0, + "e:7496_d:2024-11-15": 1, + "e:7496_d:2024-11-16": 0, + "e:7496_d:2024-11-17": 1, + "e:7496_d:2024-11-18": 1, + "e:7496_d:2024-11-19": 1, + "e:7496_d:2024-11-20": 1, + "e:7496_d:2024-11-21": 0, + "e:7496_d:2024-11-22": 0, + "e:7496_d:2024-11-23": 0, + "e:7496_d:2024-11-24": 0, + "e:7496_d:2024-11-25": 1, + "e:7496_d:2024-11-26": 1, + "e:7496_d:2024-11-27": 1, + "e:7496_d:2024-11-28": 1, + "e:7496_d:2024-11-29": 1, + "e:7496_d:2024-11-30": 0, + "e:7603_d:2024-11-01": 1, + "e:7603_d:2024-11-02": 1, + "e:7603_d:2024-11-03": 1, + "e:7603_d:2024-11-04": 1, + "e:7603_d:2024-11-05": 0, + "e:7603_d:2024-11-06": 1, + "e:7603_d:2024-11-07": 1, + "e:7603_d:2024-11-08": 1, + "e:7603_d:2024-11-09": 1, + "e:7603_d:2024-11-10": 0, + "e:7603_d:2024-11-11": 0, + "e:7603_d:2024-11-12": 1, + "e:7603_d:2024-11-13": 0, + "e:7603_d:2024-11-14": 0, + "e:7603_d:2024-11-15": 1, + "e:7603_d:2024-11-16": 0, + "e:7603_d:2024-11-17": 1, + "e:7603_d:2024-11-18": 1, + "e:7603_d:2024-11-19": 1, + "e:7603_d:2024-11-20": 1, + "e:7603_d:2024-11-21": 1, + "e:7603_d:2024-11-22": 1, + "e:7603_d:2024-11-23": 1, + "e:7603_d:2024-11-24": 0, + "e:7603_d:2024-11-25": 1, + "e:7603_d:2024-11-26": 0, + "e:7603_d:2024-11-27": 1, + "e:7603_d:2024-11-28": 0, + "e:7603_d:2024-11-29": 1, + "e:7603_d:2024-11-30": 0, + "e:7741_d:2024-11-01": 0, + "e:7741_d:2024-11-02": 0, + "e:7741_d:2024-11-03": 0, + "e:7741_d:2024-11-04": 0, + "e:7741_d:2024-11-05": 0, + "e:7741_d:2024-11-06": 0, + "e:7741_d:2024-11-07": 0, + "e:7741_d:2024-11-08": 0, + "e:7741_d:2024-11-09": 0, + "e:7741_d:2024-11-10": 0, + "e:7741_d:2024-11-11": 0, + "e:7741_d:2024-11-12": 0, + "e:7741_d:2024-11-13": 0, + "e:7741_d:2024-11-14": 0, + "e:7741_d:2024-11-15": 0, + "e:7741_d:2024-11-16": 0, + "e:7741_d:2024-11-17": 0, + "e:7741_d:2024-11-18": 0, + "e:7741_d:2024-11-19": 0, + "e:7741_d:2024-11-20": 0, + "e:7741_d:2024-11-21": 0, + "e:7741_d:2024-11-22": 0, + "e:7741_d:2024-11-23": 0, + "e:7741_d:2024-11-24": 0, + "e:7741_d:2024-11-25": 0, + "e:7741_d:2024-11-26": 0, + "e:7741_d:2024-11-27": 1, + "e:7741_d:2024-11-28": 1, + "e:7741_d:2024-11-29": 0, + "e:7741_d:2024-11-30": 0, + "e:7752_d:2024-11-01": 1, + "e:7752_d:2024-11-02": 1, + "e:7752_d:2024-11-03": 0, + "e:7752_d:2024-11-04": 0, + "e:7752_d:2024-11-05": 1, + "e:7752_d:2024-11-06": 1, + "e:7752_d:2024-11-07": 1, + "e:7752_d:2024-11-08": 0, + "e:7752_d:2024-11-09": 0, + "e:7752_d:2024-11-10": 0, + "e:7752_d:2024-11-11": 1, + "e:7752_d:2024-11-12": 0, + "e:7752_d:2024-11-13": 0, + "e:7752_d:2024-11-14": 0, + "e:7752_d:2024-11-15": 1, + "e:7752_d:2024-11-16": 0, + "e:7752_d:2024-11-17": 0, + "e:7752_d:2024-11-18": 1, + "e:7752_d:2024-11-19": 0, + "e:7752_d:2024-11-20": 1, + "e:7752_d:2024-11-21": 1, + "e:7752_d:2024-11-22": 1, + "e:7752_d:2024-11-23": 0, + "e:7752_d:2024-11-24": 1, + "e:7752_d:2024-11-25": 0, + "e:7752_d:2024-11-26": 1, + "e:7752_d:2024-11-27": 1, + "e:7752_d:2024-11-28": 0, + "e:7752_d:2024-11-29": 0, + "e:7752_d:2024-11-30": 1, + "e:7770_d:2024-11-01": 0, + "e:7770_d:2024-11-02": 0, + "e:7770_d:2024-11-03": 0, + "e:7770_d:2024-11-04": 0, + "e:7770_d:2024-11-05": 0, + "e:7770_d:2024-11-06": 0, + "e:7770_d:2024-11-07": 1, + "e:7770_d:2024-11-08": 0, + "e:7770_d:2024-11-09": 0, + "e:7770_d:2024-11-10": 0, + "e:7770_d:2024-11-11": 1, + "e:7770_d:2024-11-12": 0, + "e:7770_d:2024-11-13": 0, + "e:7770_d:2024-11-14": 0, + "e:7770_d:2024-11-15": 0, + "e:7770_d:2024-11-16": 0, + "e:7770_d:2024-11-17": 0, + "e:7770_d:2024-11-18": 1, + "e:7770_d:2024-11-19": 0, + "e:7770_d:2024-11-20": 0, + "e:7770_d:2024-11-21": 1, + "e:7770_d:2024-11-22": 0, + "e:7770_d:2024-11-23": 1, + "e:7770_d:2024-11-24": 1, + "e:7770_d:2024-11-25": 0, + "e:7770_d:2024-11-26": 1, + "e:7770_d:2024-11-27": 1, + "e:7770_d:2024-11-28": 0, + "e:7770_d:2024-11-29": 1, + "e:7770_d:2024-11-30": 0, + "e:7796_d:2024-11-01": 0, + "e:7796_d:2024-11-02": 0, + "e:7796_d:2024-11-03": 0, + "e:7796_d:2024-11-04": 0, + "e:7796_d:2024-11-05": 0, + "e:7796_d:2024-11-06": 0, + "e:7796_d:2024-11-07": 1, + "e:7796_d:2024-11-08": 1, + "e:7796_d:2024-11-09": 0, + "e:7796_d:2024-11-10": 0, + "e:7796_d:2024-11-11": 1, + "e:7796_d:2024-11-12": 0, + "e:7796_d:2024-11-13": 0, + "e:7796_d:2024-11-14": 1, + "e:7796_d:2024-11-15": 0, + "e:7796_d:2024-11-16": 0, + "e:7796_d:2024-11-17": 1, + "e:7796_d:2024-11-18": 0, + "e:7796_d:2024-11-19": 0, + "e:7796_d:2024-11-20": 0, + "e:7796_d:2024-11-21": 0, + "e:7796_d:2024-11-22": 1, + "e:7796_d:2024-11-23": 0, + "e:7796_d:2024-11-24": 0, + "e:7796_d:2024-11-25": 1, + "e:7796_d:2024-11-26": 1, + "e:7796_d:2024-11-27": 0, + "e:7796_d:2024-11-28": 0, + "e:7796_d:2024-11-29": 0, + "e:7796_d:2024-11-30": 1, + "e:7835_d:2024-11-01": 0, + "e:7835_d:2024-11-02": 0, + "e:7835_d:2024-11-03": 0, + "e:7835_d:2024-11-04": 0, + "e:7835_d:2024-11-05": 0, + "e:7835_d:2024-11-06": 0, + "e:7835_d:2024-11-07": 0, + "e:7835_d:2024-11-08": 1, + "e:7835_d:2024-11-09": 1, + "e:7835_d:2024-11-10": 0, + "e:7835_d:2024-11-11": 1, + "e:7835_d:2024-11-12": 1, + "e:7835_d:2024-11-13": 0, + "e:7835_d:2024-11-14": 0, + "e:7835_d:2024-11-15": 0, + "e:7835_d:2024-11-16": 0, + "e:7835_d:2024-11-17": 1, + "e:7835_d:2024-11-18": 1, + "e:7835_d:2024-11-19": 1, + "e:7835_d:2024-11-20": 0, + "e:7835_d:2024-11-21": 0, + "e:7835_d:2024-11-22": 0, + "e:7835_d:2024-11-23": 0, + "e:7835_d:2024-11-24": 0, + "e:7835_d:2024-11-25": 0, + "e:7835_d:2024-11-26": 0, + "e:7835_d:2024-11-27": 0, + "e:7835_d:2024-11-28": 1, + "e:7835_d:2024-11-29": 1, + "e:7835_d:2024-11-30": 0, + "e:7848_d:2024-11-01": 1, + "e:7848_d:2024-11-02": 0, + "e:7848_d:2024-11-03": 1, + "e:7848_d:2024-11-04": 1, + "e:7848_d:2024-11-05": 1, + "e:7848_d:2024-11-06": 1, + "e:7848_d:2024-11-07": 1, + "e:7848_d:2024-11-08": 0, + "e:7848_d:2024-11-09": 0, + "e:7848_d:2024-11-10": 1, + "e:7848_d:2024-11-11": 0, + "e:7848_d:2024-11-12": 1, + "e:7848_d:2024-11-13": 0, + "e:7848_d:2024-11-14": 0, + "e:7848_d:2024-11-15": 1, + "e:7848_d:2024-11-16": 1, + "e:7848_d:2024-11-17": 1, + "e:7848_d:2024-11-18": 0, + "e:7848_d:2024-11-19": 1, + "e:7848_d:2024-11-20": 1, + "e:7848_d:2024-11-21": 1, + "e:7848_d:2024-11-22": 1, + "e:7848_d:2024-11-23": 0, + "e:7848_d:2024-11-24": 1, + "e:7848_d:2024-11-25": 1, + "e:7848_d:2024-11-26": 0, + "e:7848_d:2024-11-27": 0, + "e:7848_d:2024-11-28": 1, + "e:7848_d:2024-11-29": 1, + "e:7848_d:2024-11-30": 0, + "e:7877_d:2024-11-01": 0, + "e:7877_d:2024-11-02": 0, + "e:7877_d:2024-11-03": 0, + "e:7877_d:2024-11-04": 0, + "e:7877_d:2024-11-05": 0, + "e:7877_d:2024-11-06": 0, + "e:7877_d:2024-11-07": 1, + "e:7877_d:2024-11-08": 0, + "e:7877_d:2024-11-09": 1, + "e:7877_d:2024-11-10": 0, + "e:7877_d:2024-11-11": 1, + "e:7877_d:2024-11-12": 1, + "e:7877_d:2024-11-13": 1, + "e:7877_d:2024-11-14": 1, + "e:7877_d:2024-11-15": 1, + "e:7877_d:2024-11-16": 1, + "e:7877_d:2024-11-17": 0, + "e:7877_d:2024-11-18": 1, + "e:7877_d:2024-11-19": 0, + "e:7877_d:2024-11-20": 1, + "e:7877_d:2024-11-21": 0, + "e:7877_d:2024-11-22": 0, + "e:7877_d:2024-11-23": 0, + "e:7877_d:2024-11-24": 1, + "e:7877_d:2024-11-25": 1, + "e:7877_d:2024-11-26": 0, + "e:7877_d:2024-11-27": 0, + "e:7877_d:2024-11-28": 1, + "e:7877_d:2024-11-29": 0, + "e:7877_d:2024-11-30": 0, + "e:790_d:2024-11-01": 0, + "e:790_d:2024-11-02": 0, + "e:790_d:2024-11-03": 0, + "e:790_d:2024-11-04": 0, + "e:790_d:2024-11-05": 0, + "e:790_d:2024-11-06": 0, + "e:790_d:2024-11-07": 0, + "e:790_d:2024-11-08": 0, + "e:790_d:2024-11-09": 0, + "e:790_d:2024-11-10": 0, + "e:790_d:2024-11-11": 0, + "e:790_d:2024-11-12": 0, + "e:790_d:2024-11-13": 0, + "e:790_d:2024-11-14": 0, + "e:790_d:2024-11-15": 0, + "e:790_d:2024-11-16": 0, + "e:790_d:2024-11-17": 0, + "e:790_d:2024-11-18": 0, + "e:790_d:2024-11-19": 0, + "e:790_d:2024-11-20": 0, + "e:790_d:2024-11-21": 0, + "e:790_d:2024-11-22": 0, + "e:790_d:2024-11-23": 0, + "e:790_d:2024-11-24": 0, + "e:790_d:2024-11-25": 0, + "e:790_d:2024-11-26": 0, + "e:790_d:2024-11-27": 0, + "e:790_d:2024-11-28": 0, + "e:790_d:2024-11-29": 0, + "e:790_d:2024-11-30": 0, + "e:7919_d:2024-11-01": 0, + "e:7919_d:2024-11-02": 1, + "e:7919_d:2024-11-03": 1, + "e:7919_d:2024-11-04": 1, + "e:7919_d:2024-11-05": 1, + "e:7919_d:2024-11-06": 0, + "e:7919_d:2024-11-07": 1, + "e:7919_d:2024-11-08": 1, + "e:7919_d:2024-11-09": 0, + "e:7919_d:2024-11-10": 1, + "e:7919_d:2024-11-11": 1, + "e:7919_d:2024-11-12": 0, + "e:7919_d:2024-11-13": 0, + "e:7919_d:2024-11-14": 0, + "e:7919_d:2024-11-15": 1, + "e:7919_d:2024-11-16": 1, + "e:7919_d:2024-11-17": 1, + "e:7919_d:2024-11-18": 1, + "e:7919_d:2024-11-19": 0, + "e:7919_d:2024-11-20": 1, + "e:7919_d:2024-11-21": 1, + "e:7919_d:2024-11-22": 0, + "e:7919_d:2024-11-23": 1, + "e:7919_d:2024-11-24": 0, + "e:7919_d:2024-11-25": 1, + "e:7919_d:2024-11-26": 1, + "e:7919_d:2024-11-27": 1, + "e:7919_d:2024-11-28": 0, + "e:7919_d:2024-11-29": 1, + "e:7919_d:2024-11-30": 0, + "e:791_d:2024-11-01": 0, + "e:791_d:2024-11-02": 1, + "e:791_d:2024-11-03": 0, + "e:791_d:2024-11-04": 0, + "e:791_d:2024-11-05": 0, + "e:791_d:2024-11-06": 1, + "e:791_d:2024-11-07": 1, + "e:791_d:2024-11-08": 0, + "e:791_d:2024-11-09": 1, + "e:791_d:2024-11-10": 0, + "e:791_d:2024-11-11": 1, + "e:791_d:2024-11-12": 1, + "e:791_d:2024-11-13": 1, + "e:791_d:2024-11-14": 1, + "e:791_d:2024-11-15": 1, + "e:791_d:2024-11-16": 0, + "e:791_d:2024-11-17": 1, + "e:791_d:2024-11-18": 0, + "e:791_d:2024-11-19": 1, + "e:791_d:2024-11-20": 1, + "e:791_d:2024-11-21": 0, + "e:791_d:2024-11-22": 0, + "e:791_d:2024-11-23": 1, + "e:791_d:2024-11-24": 0, + "e:791_d:2024-11-25": 0, + "e:791_d:2024-11-26": 1, + "e:791_d:2024-11-27": 1, + "e:791_d:2024-11-28": 1, + "e:791_d:2024-11-29": 0, + "e:791_d:2024-11-30": 0, + "e:7990_d:2024-11-01": 0, + "e:7990_d:2024-11-02": 0, + "e:7990_d:2024-11-03": 0, + "e:7990_d:2024-11-04": 0, + "e:7990_d:2024-11-05": 0, + "e:7990_d:2024-11-06": 0, + "e:7990_d:2024-11-07": 0, + "e:7990_d:2024-11-08": 0, + "e:7990_d:2024-11-09": 0, + "e:7990_d:2024-11-10": 0, + "e:7990_d:2024-11-11": 0, + "e:7990_d:2024-11-12": 0, + "e:7990_d:2024-11-13": 0, + "e:7990_d:2024-11-14": 0, + "e:7990_d:2024-11-15": 0, + "e:7990_d:2024-11-16": 0, + "e:7990_d:2024-11-17": 0, + "e:7990_d:2024-11-18": 0, + "e:7990_d:2024-11-19": 0, + "e:7990_d:2024-11-20": 0, + "e:7990_d:2024-11-21": 0, + "e:7990_d:2024-11-22": 0, + "e:7990_d:2024-11-23": 0, + "e:7990_d:2024-11-24": 0, + "e:7990_d:2024-11-25": 0, + "e:7990_d:2024-11-26": 0, + "e:7990_d:2024-11-27": 0, + "e:7990_d:2024-11-28": 0, + "e:7990_d:2024-11-29": 0, + "e:7990_d:2024-11-30": 0, + "e:7_d:2024-11-01": 0, + "e:7_d:2024-11-02": 1, + "e:7_d:2024-11-03": 1, + "e:7_d:2024-11-04": 1, + "e:7_d:2024-11-05": 0, + "e:7_d:2024-11-06": 0, + "e:7_d:2024-11-07": 0, + "e:7_d:2024-11-08": 0, + "e:7_d:2024-11-09": 1, + "e:7_d:2024-11-10": 0, + "e:7_d:2024-11-11": 1, + "e:7_d:2024-11-12": 0, + "e:7_d:2024-11-13": 1, + "e:7_d:2024-11-14": 0, + "e:7_d:2024-11-15": 0, + "e:7_d:2024-11-16": 0, + "e:7_d:2024-11-17": 1, + "e:7_d:2024-11-18": 0, + "e:7_d:2024-11-19": 0, + "e:7_d:2024-11-20": 1, + "e:7_d:2024-11-21": 0, + "e:7_d:2024-11-22": 0, + "e:7_d:2024-11-23": 1, + "e:7_d:2024-11-24": 0, + "e:7_d:2024-11-25": 1, + "e:7_d:2024-11-26": 0, + "e:7_d:2024-11-27": 0, + "e:7_d:2024-11-28": 0, + "e:7_d:2024-11-29": 0, + "e:7_d:2024-11-30": 0, + "e:822_d:2024-11-01": 1, + "e:822_d:2024-11-02": 1, + "e:822_d:2024-11-03": 1, + "e:822_d:2024-11-04": 0, + "e:822_d:2024-11-05": 0, + "e:822_d:2024-11-06": 1, + "e:822_d:2024-11-07": 1, + "e:822_d:2024-11-08": 1, + "e:822_d:2024-11-09": 0, + "e:822_d:2024-11-10": 0, + "e:822_d:2024-11-11": 1, + "e:822_d:2024-11-12": 1, + "e:822_d:2024-11-13": 1, + "e:822_d:2024-11-14": 1, + "e:822_d:2024-11-15": 0, + "e:822_d:2024-11-16": 0, + "e:822_d:2024-11-17": 0, + "e:822_d:2024-11-18": 0, + "e:822_d:2024-11-19": 0, + "e:822_d:2024-11-20": 1, + "e:822_d:2024-11-21": 1, + "e:822_d:2024-11-22": 1, + "e:822_d:2024-11-23": 1, + "e:822_d:2024-11-24": 1, + "e:822_d:2024-11-25": 0, + "e:822_d:2024-11-26": 0, + "e:822_d:2024-11-27": 0, + "e:822_d:2024-11-28": 1, + "e:822_d:2024-11-29": 1, + "e:822_d:2024-11-30": 1, + "e:839_d:2024-11-01": 0, + "e:839_d:2024-11-02": 0, + "e:839_d:2024-11-03": 0, + "e:839_d:2024-11-04": 0, + "e:839_d:2024-11-05": 1, + "e:839_d:2024-11-06": 0, + "e:839_d:2024-11-07": 0, + "e:839_d:2024-11-08": 0, + "e:839_d:2024-11-09": 0, + "e:839_d:2024-11-10": 1, + "e:839_d:2024-11-11": 1, + "e:839_d:2024-11-12": 0, + "e:839_d:2024-11-13": 0, + "e:839_d:2024-11-14": 0, + "e:839_d:2024-11-15": 1, + "e:839_d:2024-11-16": 0, + "e:839_d:2024-11-17": 0, + "e:839_d:2024-11-18": 0, + "e:839_d:2024-11-19": 1, + "e:839_d:2024-11-20": 1, + "e:839_d:2024-11-21": 0, + "e:839_d:2024-11-22": 0, + "e:839_d:2024-11-23": 0, + "e:839_d:2024-11-24": 0, + "e:839_d:2024-11-25": 0, + "e:839_d:2024-11-26": 1, + "e:839_d:2024-11-27": 1, + "e:839_d:2024-11-28": 0, + "e:839_d:2024-11-29": 0, + "e:839_d:2024-11-30": 0, + "e:8_d:2024-11-01": 0, + "e:8_d:2024-11-02": 0, + "e:8_d:2024-11-03": 0, + "e:8_d:2024-11-04": 1, + "e:8_d:2024-11-05": 1, + "e:8_d:2024-11-06": 0, + "e:8_d:2024-11-07": 0, + "e:8_d:2024-11-08": 0, + "e:8_d:2024-11-09": 1, + "e:8_d:2024-11-10": 1, + "e:8_d:2024-11-11": 0, + "e:8_d:2024-11-12": 0, + "e:8_d:2024-11-13": 0, + "e:8_d:2024-11-14": 1, + "e:8_d:2024-11-15": 0, + "e:8_d:2024-11-16": 1, + "e:8_d:2024-11-17": 0, + "e:8_d:2024-11-18": 0, + "e:8_d:2024-11-19": 0, + "e:8_d:2024-11-20": 1, + "e:8_d:2024-11-21": 1, + "e:8_d:2024-11-22": 0, + "e:8_d:2024-11-23": 1, + "e:8_d:2024-11-24": 0, + "e:8_d:2024-11-25": 0, + "e:8_d:2024-11-26": 0, + "e:8_d:2024-11-27": 1, + "e:8_d:2024-11-28": 0, + "e:8_d:2024-11-29": 0, + "e:8_d:2024-11-30": 1, + "e:914_d:2024-11-01": 0, + "e:914_d:2024-11-02": 1, + "e:914_d:2024-11-03": 0, + "e:914_d:2024-11-04": 0, + "e:914_d:2024-11-05": 1, + "e:914_d:2024-11-06": 1, + "e:914_d:2024-11-07": 1, + "e:914_d:2024-11-08": 1, + "e:914_d:2024-11-09": 0, + "e:914_d:2024-11-10": 0, + "e:914_d:2024-11-11": 0, + "e:914_d:2024-11-12": 0, + "e:914_d:2024-11-13": 0, + "e:914_d:2024-11-14": 0, + "e:914_d:2024-11-15": 1, + "e:914_d:2024-11-16": 0, + "e:914_d:2024-11-17": 0, + "e:914_d:2024-11-18": 0, + "e:914_d:2024-11-19": 1, + "e:914_d:2024-11-20": 0, + "e:914_d:2024-11-21": 0, + "e:914_d:2024-11-22": 0, + "e:914_d:2024-11-23": 0, + "e:914_d:2024-11-24": 0, + "e:914_d:2024-11-25": 0, + "e:914_d:2024-11-26": 0, + "e:914_d:2024-11-27": 0, + "e:914_d:2024-11-28": 0, + "e:914_d:2024-11-29": 0, + "e:914_d:2024-11-30": 0, + "e:917_d:2024-11-01": 1, + "e:917_d:2024-11-02": 0, + "e:917_d:2024-11-03": 1, + "e:917_d:2024-11-04": 0, + "e:917_d:2024-11-05": 1, + "e:917_d:2024-11-06": 1, + "e:917_d:2024-11-07": 0, + "e:917_d:2024-11-08": 1, + "e:917_d:2024-11-09": 0, + "e:917_d:2024-11-10": 0, + "e:917_d:2024-11-11": 0, + "e:917_d:2024-11-12": 1, + "e:917_d:2024-11-13": 1, + "e:917_d:2024-11-14": 1, + "e:917_d:2024-11-15": 1, + "e:917_d:2024-11-16": 0, + "e:917_d:2024-11-17": 0, + "e:917_d:2024-11-18": 0, + "e:917_d:2024-11-19": 1, + "e:917_d:2024-11-20": 1, + "e:917_d:2024-11-21": 0, + "e:917_d:2024-11-22": 0, + "e:917_d:2024-11-23": 0, + "e:917_d:2024-11-24": 1, + "e:917_d:2024-11-25": 0, + "e:917_d:2024-11-26": 1, + "e:917_d:2024-11-27": 0, + "e:917_d:2024-11-28": 1, + "e:917_d:2024-11-29": 0, + "e:917_d:2024-11-30": 1, + "e:921_d:2024-11-01": 0, + "e:921_d:2024-11-02": 0, + "e:921_d:2024-11-03": 0, + "e:921_d:2024-11-04": 0, + "e:921_d:2024-11-05": 0, + "e:921_d:2024-11-06": 0, + "e:921_d:2024-11-07": 0, + "e:921_d:2024-11-08": 0, + "e:921_d:2024-11-09": 0, + "e:921_d:2024-11-10": 0, + "e:921_d:2024-11-11": 0, + "e:921_d:2024-11-12": 0, + "e:921_d:2024-11-13": 0, + "e:921_d:2024-11-14": 0, + "e:921_d:2024-11-15": 0, + "e:921_d:2024-11-16": 0, + "e:921_d:2024-11-17": 0, + "e:921_d:2024-11-18": 0, + "e:921_d:2024-11-19": 0, + "e:921_d:2024-11-20": 0, + "e:921_d:2024-11-21": 0, + "e:921_d:2024-11-22": 0, + "e:921_d:2024-11-23": 0, + "e:921_d:2024-11-24": 0, + "e:921_d:2024-11-25": 0, + "e:921_d:2024-11-26": 0, + "e:921_d:2024-11-27": 0, + "e:921_d:2024-11-28": 0, + "e:921_d:2024-11-29": 0, + "e:921_d:2024-11-30": 0, + "e:924_d:2024-11-01": 0, + "e:924_d:2024-11-02": 0, + "e:924_d:2024-11-03": 0, + "e:924_d:2024-11-04": 0, + "e:924_d:2024-11-05": 0, + "e:924_d:2024-11-06": 0, + "e:924_d:2024-11-07": 0, + "e:924_d:2024-11-08": 0, + "e:924_d:2024-11-09": 1, + "e:924_d:2024-11-10": 0, + "e:924_d:2024-11-11": 0, + "e:924_d:2024-11-12": 0, + "e:924_d:2024-11-13": 1, + "e:924_d:2024-11-14": 0, + "e:924_d:2024-11-15": 0, + "e:924_d:2024-11-16": 0, + "e:924_d:2024-11-17": 0, + "e:924_d:2024-11-18": 1, + "e:924_d:2024-11-19": 0, + "e:924_d:2024-11-20": 0, + "e:924_d:2024-11-21": 0, + "e:924_d:2024-11-22": 0, + "e:924_d:2024-11-23": 0, + "e:924_d:2024-11-24": 0, + "e:924_d:2024-11-25": 0, + "e:924_d:2024-11-26": 1, + "e:924_d:2024-11-27": 1, + "e:924_d:2024-11-28": 0, + "e:924_d:2024-11-29": 0, + "e:924_d:2024-11-30": 0, + "e:925_d:2024-11-01": 0, + "e:925_d:2024-11-02": 0, + "e:925_d:2024-11-03": 0, + "e:925_d:2024-11-04": 1, + "e:925_d:2024-11-05": 0, + "e:925_d:2024-11-06": 0, + "e:925_d:2024-11-07": 0, + "e:925_d:2024-11-08": 0, + "e:925_d:2024-11-09": 0, + "e:925_d:2024-11-10": 0, + "e:925_d:2024-11-11": 1, + "e:925_d:2024-11-12": 1, + "e:925_d:2024-11-13": 0, + "e:925_d:2024-11-14": 1, + "e:925_d:2024-11-15": 0, + "e:925_d:2024-11-16": 0, + "e:925_d:2024-11-17": 0, + "e:925_d:2024-11-18": 0, + "e:925_d:2024-11-19": 0, + "e:925_d:2024-11-20": 1, + "e:925_d:2024-11-21": 0, + "e:925_d:2024-11-22": 1, + "e:925_d:2024-11-23": 0, + "e:925_d:2024-11-24": 0, + "e:925_d:2024-11-25": 0, + "e:925_d:2024-11-26": 1, + "e:925_d:2024-11-27": 0, + "e:925_d:2024-11-28": 1, + "e:925_d:2024-11-29": 0, + "e:925_d:2024-11-30": 1, + "e:927_d:2024-11-01": 0, + "e:927_d:2024-11-02": 0, + "e:927_d:2024-11-03": 1, + "e:927_d:2024-11-04": 0, + "e:927_d:2024-11-05": 0, + "e:927_d:2024-11-06": 0, + "e:927_d:2024-11-07": 1, + "e:927_d:2024-11-08": 1, + "e:927_d:2024-11-09": 1, + "e:927_d:2024-11-10": 1, + "e:927_d:2024-11-11": 1, + "e:927_d:2024-11-12": 1, + "e:927_d:2024-11-13": 1, + "e:927_d:2024-11-14": 0, + "e:927_d:2024-11-15": 0, + "e:927_d:2024-11-16": 1, + "e:927_d:2024-11-17": 1, + "e:927_d:2024-11-18": 0, + "e:927_d:2024-11-19": 1, + "e:927_d:2024-11-20": 0, + "e:927_d:2024-11-21": 1, + "e:927_d:2024-11-22": 1, + "e:927_d:2024-11-23": 1, + "e:927_d:2024-11-24": 1, + "e:927_d:2024-11-25": 1, + "e:927_d:2024-11-26": 1, + "e:927_d:2024-11-27": 0, + "e:927_d:2024-11-28": 0, + "e:927_d:2024-11-29": 1, + "e:927_d:2024-11-30": 0, + "e:928_d:2024-11-01": 0, + "e:928_d:2024-11-02": 0, + "e:928_d:2024-11-03": 0, + "e:928_d:2024-11-04": 1, + "e:928_d:2024-11-05": 0, + "e:928_d:2024-11-06": 0, + "e:928_d:2024-11-07": 0, + "e:928_d:2024-11-08": 1, + "e:928_d:2024-11-09": 0, + "e:928_d:2024-11-10": 0, + "e:928_d:2024-11-11": 0, + "e:928_d:2024-11-12": 0, + "e:928_d:2024-11-13": 0, + "e:928_d:2024-11-14": 0, + "e:928_d:2024-11-15": 0, + "e:928_d:2024-11-16": 0, + "e:928_d:2024-11-17": 0, + "e:928_d:2024-11-18": 0, + "e:928_d:2024-11-19": 0, + "e:928_d:2024-11-20": 0, + "e:928_d:2024-11-21": 1, + "e:928_d:2024-11-22": 0, + "e:928_d:2024-11-23": 0, + "e:928_d:2024-11-24": 0, + "e:928_d:2024-11-25": 0, + "e:928_d:2024-11-26": 0, + "e:928_d:2024-11-27": 0, + "e:928_d:2024-11-28": 0, + "e:928_d:2024-11-29": 0, + "e:928_d:2024-11-30": 0 + } +} diff --git a/legacy/found_solutions/solution_77_2024-11-01-2024-11-30_wdefault.json b/legacy/found_solutions/solution_77_2024-11-01-2024-11-30_wdefault.json new file mode 100644 index 00000000..8dca696f --- /dev/null +++ b/legacy/found_solutions/solution_77_2024-11-01-2024-11-30_wdefault.json @@ -0,0 +1,4 @@ +{ + "objective": 0.0, + "variables": {} +} diff --git a/legacy/found_solutions/solution_77_2024-12-01-2024-12-31_wdefault.json b/legacy/found_solutions/solution_77_2024-12-01-2024-12-31_wdefault.json new file mode 100644 index 00000000..8dca696f --- /dev/null +++ b/legacy/found_solutions/solution_77_2024-12-01-2024-12-31_wdefault.json @@ -0,0 +1,4 @@ +{ + "objective": 0.0, + "variables": {} +} diff --git a/legacy/found_solutions/solution_77_2025-02-01-2025-02-28_wdefault.json b/legacy/found_solutions/solution_77_2025-02-01-2025-02-28_wdefault.json new file mode 100644 index 00000000..8dca696f --- /dev/null +++ b/legacy/found_solutions/solution_77_2025-02-01-2025-02-28_wdefault.json @@ -0,0 +1,4 @@ +{ + "objective": 0.0, + "variables": {} +} diff --git a/legacy/processed_solutions/soluting_of_test_all_costraints_single_case_processed.json b/legacy/processed_solutions/soluting_of_test_all_costraints_single_case_processed.json new file mode 100644 index 00000000..03175f6c --- /dev/null +++ b/legacy/processed_solutions/soluting_of_test_all_costraints_single_case_processed.json @@ -0,0 +1,16416 @@ +{ + "all_day_off_wish_cells": [ + [ + 2963, + "2024-11-26" + ], + [ + 6677, + "2024-11-28" + ], + [ + 3868, + "2024-11-11" + ], + [ + 925, + "2024-11-21" + ], + [ + 6677, + "2024-11-29" + ], + [ + 925, + "2024-11-20" + ], + [ + 917, + "2024-11-08" + ] + ], + "all_shift_wish_colors": { + "3868-2024-11-01": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-02": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-03": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-04": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-05": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-06": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-07": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-08": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-09": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-10": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-11": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-12": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-13": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-14": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-15": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-16": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-17": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-18": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-19": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-20": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-21": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-22": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-23": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-24": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-25": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-26": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-27": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-28": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-29": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-30": [ + "#225e62", + "#dadada" + ], + "5367-2024-11-06": [ + "#f69e17", + "#dadada", + "#225e62", + "#dadada", + "#3a9ea1", + "oklch(82.1% 0.087 285.6)" + ], + "6836-2024-11-15": [ + "#a8d51f", + "#dadada", + "#f69e17", + "#dadada", + "#3a9ea1", + "oklch(82.1% 0.087 285.6)" + ], + "6836-2024-11-16": [ + "#a8d51f", + "#dadada", + "#f69e17", + "#dadada", + "#3a9ea1", + "oklch(82.1% 0.087 285.6)" + ], + "7603-2024-11-11": [ + "#a8d51f", + "#dadada", + "#3a9ea1", + "oklch(82.1% 0.087 285.6)" + ], + "791-2024-11-25": [ + "#a8d51f", + "#dadada" + ], + "917-2024-11-30": [ + "#f69e17", + "#dadada", + "#225e62", + "#dadada" + ] + }, + "days": [ + "2024-11-01", + "2024-11-02", + "2024-11-03", + "2024-11-04", + "2024-11-05", + "2024-11-06", + "2024-11-07", + "2024-11-08", + "2024-11-09", + "2024-11-10", + "2024-11-11", + "2024-11-12", + "2024-11-13", + "2024-11-14", + "2024-11-15", + "2024-11-16", + "2024-11-17", + "2024-11-18", + "2024-11-19", + "2024-11-20", + "2024-11-21", + "2024-11-22", + "2024-11-23", + "2024-11-24", + "2024-11-25", + "2024-11-26", + "2024-11-27", + "2024-11-28", + "2024-11-29", + "2024-11-30" + ], + "employees": [ + { + "actual_working_time": 1920, + "forbidden_days": [ + 18, + 22, + 23, + 24, + 25 + ], + "forbidden_shifts": [ + [ + 1, + "N" + ], + [ + 2, + "N" + ], + [ + 3, + "N" + ], + [ + 4, + "N" + ], + [ + 5, + "N" + ], + [ + 6, + "N" + ], + [ + 7, + "N" + ], + [ + 8, + "N" + ], + [ + 9, + "N" + ], + [ + 10, + "N" + ], + [ + 11, + "N" + ], + [ + 12, + "N" + ], + [ + 13, + "N" + ], + [ + 14, + "N" + ], + [ + 15, + "N" + ], + [ + 16, + "N" + ], + [ + 17, + "N" + ], + [ + 18, + "N" + ], + [ + 19, + "N" + ], + [ + 20, + "N" + ], + [ + 21, + "N" + ], + [ + 22, + "N" + ], + [ + 23, + "N" + ], + [ + 24, + "N" + ], + [ + 25, + "N" + ], + [ + 26, + "N" + ], + [ + 27, + "N" + ], + [ + 28, + "N" + ], + [ + 29, + "N" + ], + [ + 30, + "N" + ] + ], + "hidden_actual_working_time": 1920, + "id": 459, + "level": "Fachkraft", + "name": "Shoemake Sandra", + "target_working_time": 7680, + "vacation_days": [ + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 12 + ], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 9360, + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "forbidden_shifts": [], + "hidden_actual_working_time": 9360, + "id": 790, + "level": "Hilfskraft", + "name": "Mccomas Adriane", + "target_working_time": 9360, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 0, + "forbidden_days": [], + "forbidden_shifts": [], + "hidden_actual_working_time": 0, + "id": 791, + "level": "Fachkraft", + "name": "Branz Janett", + "target_working_time": 6930, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [ + [ + 25, + "F" + ] + ] + } + }, + { + "actual_working_time": 8765, + "forbidden_days": [ + 4, + 5, + 9, + 10, + 15, + 16, + 17, + 18, + 19, + 25, + 26, + 27 + ], + "forbidden_shifts": [], + "hidden_actual_working_time": 0, + "id": 822, + "level": "Hilfskraft", + "name": "Sewell Nele", + "target_working_time": 9240, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 3955, + "forbidden_days": [ + 1, + 2, + 3, + 6, + 7, + 8, + 9, + 12, + 13, + 14, + 16, + 17, + 18, + 21, + 22, + 23, + 24, + 25, + 28, + 29, + 30 + ], + "forbidden_shifts": [], + "hidden_actual_working_time": 0, + "id": 839, + "level": "Hilfskraft", + "name": "S\u00e4uffert Liselotte", + "target_working_time": 4620, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 2079, + "forbidden_days": [ + 23, + 24, + 30 + ], + "forbidden_shifts": [], + "hidden_actual_working_time": 2079, + "id": 914, + "level": "Hilfskraft", + "name": "Harkins Silvia", + "target_working_time": 8316, + "vacation_days": [ + 21, + 22, + 25, + 26, + 27, + 28, + 29 + ], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 460, + "forbidden_days": [], + "forbidden_shifts": [], + "hidden_actual_working_time": 0, + "id": 917, + "level": "Fachkraft", + "name": "Catoe Ingbert", + "target_working_time": 7680, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [ + 8 + ], + "shift_wishes": [ + [ + 30, + "S" + ], + [ + 30, + "N" + ] + ] + } + }, + { + "actual_working_time": 4620, + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "forbidden_shifts": [], + "hidden_actual_working_time": 4620, + "id": 921, + "level": "Fachkraft", + "name": "Keese Jenny", + "target_working_time": 4620, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 0, + "forbidden_days": [ + 1, + 2, + 3, + 8, + 14, + 15, + 21, + 22, + 28, + 29 + ], + "forbidden_shifts": [ + [ + 1, + "N" + ], + [ + 2, + "N" + ], + [ + 3, + "N" + ], + [ + 4, + "N" + ], + [ + 5, + "N" + ], + [ + 6, + "N" + ], + [ + 7, + "N" + ], + [ + 8, + "N" + ], + [ + 9, + "N" + ], + [ + 10, + "N" + ], + [ + 11, + "N" + ], + [ + 12, + "N" + ], + [ + 13, + "N" + ], + [ + 14, + "N" + ], + [ + 15, + "N" + ], + [ + 16, + "N" + ], + [ + 17, + "N" + ], + [ + 18, + "N" + ], + [ + 19, + "N" + ], + [ + 20, + "N" + ], + [ + 21, + "N" + ], + [ + 22, + "N" + ], + [ + 23, + "N" + ], + [ + 24, + "N" + ], + [ + 25, + "N" + ], + [ + 26, + "N" + ], + [ + 27, + "N" + ], + [ + 28, + "N" + ], + [ + 29, + "N" + ], + [ + 30, + "N" + ] + ], + "hidden_actual_working_time": 0, + "id": 924, + "level": "Fachkraft", + "name": "Merriweather B\u00e4rbl", + "target_working_time": 4561, + "vacation_days": [ + 4, + 5, + 6, + 7 + ], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 0, + "forbidden_days": [], + "forbidden_shifts": [ + [ + 1, + "F" + ], + [ + 2, + "F" + ], + [ + 3, + "F" + ], + [ + 4, + "F" + ], + [ + 5, + "F" + ], + [ + 6, + "F" + ], + [ + 7, + "F" + ], + [ + 8, + "F" + ], + [ + 9, + "F" + ], + [ + 10, + "F" + ], + [ + 11, + "F" + ], + [ + 12, + "F" + ], + [ + 13, + "F" + ], + [ + 14, + "F" + ], + [ + 15, + "F" + ], + [ + 16, + "F" + ], + [ + 17, + "F" + ], + [ + 18, + "F" + ], + [ + 19, + "F" + ], + [ + 20, + "F" + ], + [ + 21, + "F" + ], + [ + 22, + "F" + ], + [ + 23, + "F" + ], + [ + 24, + "F" + ], + [ + 25, + "F" + ], + [ + 26, + "F" + ], + [ + 27, + "F" + ], + [ + 28, + "F" + ], + [ + 29, + "F" + ], + [ + 30, + "F" + ], + [ + 1, + "S" + ], + [ + 2, + "S" + ], + [ + 3, + "S" + ], + [ + 4, + "S" + ], + [ + 5, + "S" + ], + [ + 6, + "S" + ], + [ + 7, + "S" + ], + [ + 8, + "S" + ], + [ + 9, + "S" + ], + [ + 10, + "S" + ], + [ + 11, + "S" + ], + [ + 12, + "S" + ], + [ + 13, + "S" + ], + [ + 14, + "S" + ], + [ + 15, + "S" + ], + [ + 16, + "S" + ], + [ + 17, + "S" + ], + [ + 18, + "S" + ], + [ + 19, + "S" + ], + [ + 20, + "S" + ], + [ + 21, + "S" + ], + [ + 22, + "S" + ], + [ + 23, + "S" + ], + [ + 24, + "S" + ], + [ + 25, + "S" + ], + [ + 26, + "S" + ], + [ + 27, + "S" + ], + [ + 28, + "S" + ], + [ + 29, + "S" + ], + [ + 30, + "S" + ], + [ + 1, + "Z" + ], + [ + 2, + "Z" + ], + [ + 3, + "Z" + ], + [ + 4, + "Z" + ], + [ + 5, + "Z" + ], + [ + 6, + "Z" + ], + [ + 7, + "Z" + ], + [ + 8, + "Z" + ], + [ + 9, + "Z" + ], + [ + 10, + "Z" + ], + [ + 11, + "Z" + ], + [ + 12, + "Z" + ], + [ + 13, + "Z" + ], + [ + 14, + "Z" + ], + [ + 15, + "Z" + ], + [ + 16, + "Z" + ], + [ + 17, + "Z" + ], + [ + 18, + "Z" + ], + [ + 19, + "Z" + ], + [ + 20, + "Z" + ], + [ + 21, + "Z" + ], + [ + 22, + "Z" + ], + [ + 23, + "Z" + ], + [ + 24, + "Z" + ], + [ + 25, + "Z" + ], + [ + 26, + "Z" + ], + [ + 27, + "Z" + ], + [ + 28, + "Z" + ], + [ + 29, + "Z" + ], + [ + 30, + "Z" + ] + ], + "hidden_actual_working_time": 0, + "id": 925, + "level": "Fachkraft", + "name": "Farniok Lina", + "target_working_time": 5544, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [ + 20, + 21 + ], + "shift_wishes": [] + } + }, + { + "actual_working_time": 468, + "forbidden_days": [], + "forbidden_shifts": [], + "hidden_actual_working_time": 468, + "id": 927, + "level": "Hilfskraft", + "name": "Mittrach Margaritt", + "target_working_time": 9360, + "vacation_days": [ + 18 + ], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 832, + "forbidden_days": [ + 1, + 6, + 7, + 16, + 17, + 18, + 19, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "forbidden_shifts": [ + [ + 1, + "F" + ], + [ + 2, + "F" + ], + [ + 3, + "F" + ], + [ + 4, + "F" + ], + [ + 5, + "F" + ], + [ + 6, + "F" + ], + [ + 7, + "F" + ], + [ + 8, + "F" + ], + [ + 9, + "F" + ], + [ + 10, + "F" + ], + [ + 11, + "F" + ], + [ + 12, + "F" + ], + [ + 13, + "F" + ], + [ + 14, + "F" + ], + [ + 15, + "F" + ], + [ + 16, + "F" + ], + [ + 17, + "F" + ], + [ + 18, + "F" + ], + [ + 19, + "F" + ], + [ + 20, + "F" + ], + [ + 21, + "F" + ], + [ + 22, + "F" + ], + [ + 23, + "F" + ], + [ + 24, + "F" + ], + [ + 25, + "F" + ], + [ + 26, + "F" + ], + [ + 27, + "F" + ], + [ + 28, + "F" + ], + [ + 29, + "F" + ], + [ + 30, + "F" + ], + [ + 1, + "S" + ], + [ + 2, + "S" + ], + [ + 3, + "S" + ], + [ + 4, + "S" + ], + [ + 5, + "S" + ], + [ + 6, + "S" + ], + [ + 7, + "S" + ], + [ + 8, + "S" + ], + [ + 9, + "S" + ], + [ + 10, + "S" + ], + [ + 11, + "S" + ], + [ + 12, + "S" + ], + [ + 13, + "S" + ], + [ + 14, + "S" + ], + [ + 15, + "S" + ], + [ + 16, + "S" + ], + [ + 17, + "S" + ], + [ + 18, + "S" + ], + [ + 19, + "S" + ], + [ + 20, + "S" + ], + [ + 21, + "S" + ], + [ + 22, + "S" + ], + [ + 23, + "S" + ], + [ + 24, + "S" + ], + [ + 25, + "S" + ], + [ + 26, + "S" + ], + [ + 27, + "S" + ], + [ + 28, + "S" + ], + [ + 29, + "S" + ], + [ + 30, + "S" + ], + [ + 1, + "Z" + ], + [ + 2, + "Z" + ], + [ + 3, + "Z" + ], + [ + 4, + "Z" + ], + [ + 5, + "Z" + ], + [ + 6, + "Z" + ], + [ + 7, + "Z" + ], + [ + 8, + "Z" + ], + [ + 9, + "Z" + ], + [ + 10, + "Z" + ], + [ + 11, + "Z" + ], + [ + 12, + "Z" + ], + [ + 13, + "Z" + ], + [ + 14, + "Z" + ], + [ + 15, + "Z" + ], + [ + 16, + "Z" + ], + [ + 17, + "Z" + ], + [ + 18, + "Z" + ], + [ + 19, + "Z" + ], + [ + 20, + "Z" + ], + [ + 21, + "Z" + ], + [ + 22, + "Z" + ], + [ + 23, + "Z" + ], + [ + 24, + "Z" + ], + [ + 25, + "Z" + ], + [ + 26, + "Z" + ], + [ + 27, + "Z" + ], + [ + 28, + "Z" + ], + [ + 29, + "Z" + ], + [ + 30, + "Z" + ] + ], + "hidden_actual_working_time": 832, + "id": 928, + "level": "Fachkraft", + "name": "Wunderlich Daniele", + "target_working_time": 5544, + "vacation_days": [ + 25, + 26, + 27, + 28, + 29 + ], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 462, + "forbidden_days": [], + "forbidden_shifts": [], + "hidden_actual_working_time": 462, + "id": 1230, + "level": "Fachkraft", + "name": "Palacio Constance", + "target_working_time": 9240, + "vacation_days": [ + 11 + ], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 0, + "forbidden_days": [], + "forbidden_shifts": [], + "hidden_actual_working_time": 0, + "id": 2932, + "level": "Fachkraft", + "name": "Devers Kersten", + "target_working_time": 9240, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 0, + "forbidden_days": [], + "forbidden_shifts": [ + [ + 1, + "N" + ], + [ + 1, + "S" + ], + [ + 1, + "Z" + ], + [ + 4, + "N" + ], + [ + 4, + "S" + ], + [ + 4, + "Z" + ], + [ + 5, + "N" + ], + [ + 5, + "S" + ], + [ + 5, + "Z" + ], + [ + 6, + "N" + ], + [ + 6, + "S" + ], + [ + 6, + "Z" + ], + [ + 7, + "N" + ], + [ + 7, + "S" + ], + [ + 7, + "Z" + ], + [ + 8, + "N" + ], + [ + 8, + "S" + ], + [ + 8, + "Z" + ], + [ + 11, + "N" + ], + [ + 11, + "S" + ], + [ + 11, + "Z" + ], + [ + 12, + "N" + ], + [ + 12, + "S" + ], + [ + 12, + "Z" + ], + [ + 13, + "N" + ], + [ + 13, + "S" + ], + [ + 13, + "Z" + ], + [ + 14, + "N" + ], + [ + 14, + "S" + ], + [ + 14, + "Z" + ], + [ + 15, + "N" + ], + [ + 15, + "S" + ], + [ + 15, + "Z" + ], + [ + 18, + "N" + ], + [ + 18, + "S" + ], + [ + 18, + "Z" + ], + [ + 19, + "N" + ], + [ + 19, + "S" + ], + [ + 19, + "Z" + ], + [ + 20, + "N" + ], + [ + 20, + "S" + ], + [ + 20, + "Z" + ], + [ + 21, + "N" + ], + [ + 21, + "S" + ], + [ + 21, + "Z" + ], + [ + 22, + "N" + ], + [ + 22, + "S" + ], + [ + 22, + "Z" + ], + [ + 25, + "N" + ], + [ + 25, + "S" + ], + [ + 25, + "Z" + ], + [ + 26, + "N" + ], + [ + 26, + "S" + ], + [ + 26, + "Z" + ], + [ + 27, + "N" + ], + [ + 27, + "S" + ], + [ + 27, + "Z" + ], + [ + 28, + "N" + ], + [ + 28, + "S" + ], + [ + 28, + "Z" + ], + [ + 29, + "N" + ], + [ + 29, + "S" + ], + [ + 29, + "Z" + ] + ], + "hidden_actual_working_time": 0, + "id": 2963, + "level": "Fachkraft", + "name": "Hoots Renilde", + "target_working_time": 9240, + "vacation_days": [ + 11 + ], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [ + 26 + ], + "shift_wishes": [] + } + }, + { + "actual_working_time": 9240, + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "forbidden_shifts": [], + "hidden_actual_working_time": 9240, + "id": 3566, + "level": "Fachkraft", + "name": "Seligman Elgine", + "target_working_time": 9240, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 460, + "forbidden_days": [], + "forbidden_shifts": [], + "hidden_actual_working_time": 0, + "id": 3868, + "level": "Fachkraft", + "name": "Vanfleet Eike", + "target_working_time": 9240, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [ + 11 + ], + "shift_wishes": [ + [ + 1, + "N" + ], + [ + 2, + "N" + ], + [ + 3, + "N" + ], + [ + 4, + "N" + ], + [ + 5, + "N" + ], + [ + 6, + "N" + ], + [ + 7, + "N" + ], + [ + 8, + "N" + ], + [ + 9, + "N" + ], + [ + 10, + "N" + ], + [ + 11, + "N" + ], + [ + 12, + "N" + ], + [ + 13, + "N" + ], + [ + 14, + "N" + ], + [ + 15, + "N" + ], + [ + 16, + "N" + ], + [ + 17, + "N" + ], + [ + 18, + "N" + ], + [ + 19, + "N" + ], + [ + 20, + "N" + ], + [ + 21, + "N" + ], + [ + 22, + "N" + ], + [ + 23, + "N" + ], + [ + 24, + "N" + ], + [ + 25, + "N" + ], + [ + 26, + "N" + ], + [ + 27, + "N" + ], + [ + 28, + "N" + ], + [ + 29, + "N" + ], + [ + 30, + "N" + ] + ] + } + }, + { + "actual_working_time": 462, + "forbidden_days": [ + 18 + ], + "forbidden_shifts": [], + "hidden_actual_working_time": 462, + "id": 4566, + "level": "Azubi", + "name": "Woodcock Hannah", + "target_working_time": 9240, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 0, + "forbidden_days": [], + "forbidden_shifts": [], + "hidden_actual_working_time": 0, + "id": 5367, + "level": "Hilfskraft", + "name": "Donis Henni", + "target_working_time": 9240, + "vacation_days": [ + 7, + 8 + ], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [ + [ + 6, + "S" + ], + [ + 6, + "N" + ], + [ + 6, + "Z" + ] + ] + } + }, + { + "actual_working_time": 0, + "forbidden_days": [], + "forbidden_shifts": [], + "hidden_actual_working_time": 0, + "id": 5920, + "level": "Fachkraft", + "name": "Carreras Augustin", + "target_working_time": 9240, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 1, + "forbidden_days": [ + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29 + ], + "forbidden_shifts": [], + "hidden_actual_working_time": 1, + "id": 6475, + "level": "Hilfskraft", + "name": "Binford Heinz", + "target_working_time": 2, + "vacation_days": [ + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21 + ], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 0, + "forbidden_days": [], + "forbidden_shifts": [ + [ + 1, + "N" + ], + [ + 1, + "S" + ], + [ + 1, + "Z" + ], + [ + 4, + "N" + ], + [ + 4, + "S" + ], + [ + 4, + "Z" + ], + [ + 5, + "N" + ], + [ + 5, + "S" + ], + [ + 5, + "Z" + ], + [ + 6, + "N" + ], + [ + 6, + "S" + ], + [ + 6, + "Z" + ], + [ + 7, + "N" + ], + [ + 7, + "S" + ], + [ + 7, + "Z" + ], + [ + 8, + "N" + ], + [ + 8, + "S" + ], + [ + 8, + "Z" + ], + [ + 11, + "N" + ], + [ + 11, + "S" + ], + [ + 11, + "Z" + ], + [ + 12, + "N" + ], + [ + 12, + "S" + ], + [ + 12, + "Z" + ], + [ + 13, + "N" + ], + [ + 13, + "S" + ], + [ + 13, + "Z" + ], + [ + 14, + "N" + ], + [ + 14, + "S" + ], + [ + 14, + "Z" + ], + [ + 15, + "N" + ], + [ + 15, + "S" + ], + [ + 15, + "Z" + ], + [ + 18, + "N" + ], + [ + 18, + "S" + ], + [ + 18, + "Z" + ], + [ + 19, + "N" + ], + [ + 19, + "S" + ], + [ + 19, + "Z" + ], + [ + 20, + "N" + ], + [ + 20, + "S" + ], + [ + 20, + "Z" + ], + [ + 21, + "N" + ], + [ + 21, + "S" + ], + [ + 21, + "Z" + ], + [ + 22, + "N" + ], + [ + 22, + "S" + ], + [ + 22, + "Z" + ], + [ + 25, + "N" + ], + [ + 25, + "S" + ], + [ + 25, + "Z" + ], + [ + 26, + "N" + ], + [ + 26, + "S" + ], + [ + 26, + "Z" + ], + [ + 27, + "N" + ], + [ + 27, + "S" + ], + [ + 27, + "Z" + ], + [ + 28, + "N" + ], + [ + 28, + "S" + ], + [ + 28, + "Z" + ], + [ + 29, + "N" + ], + [ + 29, + "S" + ], + [ + 29, + "Z" + ] + ], + "hidden_actual_working_time": 0, + "id": 6507, + "level": "Fachkraft", + "name": "Rashid Roseliese", + "target_working_time": 9240, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 0, + "forbidden_days": [], + "forbidden_shifts": [], + "hidden_actual_working_time": 0, + "id": 6677, + "level": "Hilfskraft", + "name": "Fullerton Christfri", + "target_working_time": 9240, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [ + 28, + 29 + ], + "shift_wishes": [] + } + }, + { + "actual_working_time": 1848, + "forbidden_days": [ + 23, + 24 + ], + "forbidden_shifts": [ + [ + 1, + "F" + ], + [ + 2, + "F" + ], + [ + 3, + "F" + ], + [ + 4, + "F" + ], + [ + 5, + "F" + ], + [ + 6, + "F" + ], + [ + 7, + "F" + ], + [ + 8, + "F" + ], + [ + 9, + "F" + ], + [ + 10, + "F" + ], + [ + 11, + "F" + ], + [ + 12, + "F" + ], + [ + 13, + "F" + ], + [ + 14, + "F" + ], + [ + 15, + "F" + ], + [ + 16, + "F" + ], + [ + 17, + "F" + ], + [ + 18, + "F" + ], + [ + 19, + "F" + ], + [ + 20, + "F" + ], + [ + 21, + "F" + ], + [ + 22, + "F" + ], + [ + 23, + "F" + ], + [ + 24, + "F" + ], + [ + 25, + "F" + ], + [ + 26, + "F" + ], + [ + 27, + "F" + ], + [ + 28, + "F" + ], + [ + 29, + "F" + ], + [ + 30, + "F" + ], + [ + 1, + "S" + ], + [ + 2, + "S" + ], + [ + 3, + "S" + ], + [ + 4, + "S" + ], + [ + 5, + "S" + ], + [ + 6, + "S" + ], + [ + 7, + "S" + ], + [ + 8, + "S" + ], + [ + 9, + "S" + ], + [ + 10, + "S" + ], + [ + 11, + "S" + ], + [ + 12, + "S" + ], + [ + 13, + "S" + ], + [ + 14, + "S" + ], + [ + 15, + "S" + ], + [ + 16, + "S" + ], + [ + 17, + "S" + ], + [ + 18, + "S" + ], + [ + 19, + "S" + ], + [ + 20, + "S" + ], + [ + 21, + "S" + ], + [ + 22, + "S" + ], + [ + 23, + "S" + ], + [ + 24, + "S" + ], + [ + 25, + "S" + ], + [ + 26, + "S" + ], + [ + 27, + "S" + ], + [ + 28, + "S" + ], + [ + 29, + "S" + ], + [ + 30, + "S" + ], + [ + 1, + "Z" + ], + [ + 2, + "Z" + ], + [ + 3, + "Z" + ], + [ + 4, + "Z" + ], + [ + 5, + "Z" + ], + [ + 6, + "Z" + ], + [ + 7, + "Z" + ], + [ + 8, + "Z" + ], + [ + 9, + "Z" + ], + [ + 10, + "Z" + ], + [ + 11, + "Z" + ], + [ + 12, + "Z" + ], + [ + 13, + "Z" + ], + [ + 14, + "Z" + ], + [ + 15, + "Z" + ], + [ + 16, + "Z" + ], + [ + 17, + "Z" + ], + [ + 18, + "Z" + ], + [ + 19, + "Z" + ], + [ + 20, + "Z" + ], + [ + 21, + "Z" + ], + [ + 22, + "Z" + ], + [ + 23, + "Z" + ], + [ + 24, + "Z" + ], + [ + 25, + "Z" + ], + [ + 26, + "Z" + ], + [ + 27, + "Z" + ], + [ + 28, + "Z" + ], + [ + 29, + "Z" + ], + [ + 30, + "Z" + ] + ], + "hidden_actual_working_time": 1848, + "id": 6681, + "level": "Fachkraft", + "name": "Labelle Saskia", + "target_working_time": 7392, + "vacation_days": [ + 18, + 19, + 20, + 21, + 22, + 25, + 26 + ], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 6930, + "forbidden_days": [ + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "forbidden_shifts": [], + "hidden_actual_working_time": 6930, + "id": 6715, + "level": "Azubi", + "name": "Burris Lioba", + "target_working_time": 9240, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 460, + "forbidden_days": [], + "forbidden_shifts": [], + "hidden_actual_working_time": 0, + "id": 6836, + "level": "Fachkraft", + "name": "Valentino Trude", + "target_working_time": 9240, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [ + [ + 15, + "F" + ], + [ + 16, + "F" + ], + [ + 15, + "S" + ], + [ + 16, + "S" + ], + [ + 15, + "Z" + ], + [ + 16, + "Z" + ] + ] + } + }, + { + "actual_working_time": 0, + "forbidden_days": [], + "forbidden_shifts": [], + "hidden_actual_working_time": 0, + "id": 6928, + "level": "Hilfskraft", + "name": "Izzo Annelene", + "target_working_time": 9360, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 8950, + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 16, + 21, + 22, + 23, + 24, + 30 + ], + "forbidden_shifts": [], + "hidden_actual_working_time": 4140, + "id": 7496, + "level": "Fachkraft", + "name": "Demarco Marcus", + "target_working_time": 8400, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 0, + "forbidden_days": [], + "forbidden_shifts": [ + [ + 1, + "N" + ], + [ + 2, + "N" + ], + [ + 3, + "N" + ], + [ + 4, + "N" + ], + [ + 5, + "N" + ], + [ + 6, + "N" + ], + [ + 7, + "N" + ], + [ + 8, + "N" + ], + [ + 9, + "N" + ], + [ + 10, + "N" + ], + [ + 11, + "N" + ], + [ + 12, + "N" + ], + [ + 13, + "N" + ], + [ + 14, + "N" + ], + [ + 15, + "N" + ], + [ + 16, + "N" + ], + [ + 17, + "N" + ], + [ + 18, + "N" + ], + [ + 19, + "N" + ], + [ + 20, + "N" + ], + [ + 21, + "N" + ], + [ + 22, + "N" + ], + [ + 23, + "N" + ], + [ + 24, + "N" + ], + [ + 25, + "N" + ], + [ + 26, + "N" + ], + [ + 27, + "N" + ], + [ + 28, + "N" + ], + [ + 29, + "N" + ], + [ + 30, + "N" + ] + ], + "hidden_actual_working_time": 0, + "id": 7603, + "level": "Fachkraft", + "name": "Roberson Ludger", + "target_working_time": 9240, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [ + [ + 11, + "F" + ], + [ + 11, + "Z" + ] + ] + } + }, + { + "actual_working_time": 920, + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 26, + 29, + 30 + ], + "forbidden_shifts": [], + "hidden_actual_working_time": 0, + "id": 7741, + "level": "Fachkraft", + "name": "Tharp Sieghardt", + "target_working_time": 960, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 2300, + "forbidden_days": [ + 4, + 8, + 9, + 10 + ], + "forbidden_shifts": [], + "hidden_actual_working_time": 920, + "id": 7752, + "level": "Hilfskraft", + "name": "Rodriques Kilian", + "target_working_time": 9240, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 2310, + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 22, + 25 + ], + "forbidden_shifts": [], + "hidden_actual_working_time": 2310, + "id": 7770, + "level": "Azubi", + "name": "Yeh Julia", + "target_working_time": 9240, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 2310, + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 28, + 29 + ], + "forbidden_shifts": [], + "hidden_actual_working_time": 2310, + "id": 7796, + "level": "Azubi", + "name": "Hertzler Burkhild", + "target_working_time": 9240, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 2310, + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 22, + 25 + ], + "forbidden_shifts": [], + "hidden_actual_working_time": 2310, + "id": 7835, + "level": "Azubi", + "name": "Driggers Karena", + "target_working_time": 9240, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 460, + "forbidden_days": [], + "forbidden_shifts": [], + "hidden_actual_working_time": 460, + "id": 7848, + "level": "Azubi", + "name": "Winters Gertraute", + "target_working_time": 9240, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 1386, + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6 + ], + "forbidden_shifts": [], + "hidden_actual_working_time": 1386, + "id": 7877, + "level": "Azubi", + "name": "Staggs Janett", + "target_working_time": 9240, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 0, + "forbidden_days": [ + 1 + ], + "forbidden_shifts": [], + "hidden_actual_working_time": 0, + "id": 7919, + "level": "Fachkraft", + "name": "Weathers Irma", + "target_working_time": 9240, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 0, + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27 + ], + "forbidden_shifts": [], + "hidden_actual_working_time": 0, + "id": 7990, + "level": "Azubi", + "name": "Milburn Loremarie", + "target_working_time": 2310, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 0, + "forbidden_days": [], + "forbidden_shifts": [], + "hidden_actual_working_time": 0, + "id": 0, + "level": "Azubi", + "name": "Azubi0 Hidden", + "target_working_time": 0, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 0, + "forbidden_days": [], + "forbidden_shifts": [], + "hidden_actual_working_time": 0, + "id": 1, + "level": "Azubi", + "name": "Azubi1 Hidden", + "target_working_time": 0, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 0, + "forbidden_days": [], + "forbidden_shifts": [], + "hidden_actual_working_time": 0, + "id": 2, + "level": "Azubi", + "name": "Azubi2 Hidden", + "target_working_time": 0, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 0, + "forbidden_days": [], + "forbidden_shifts": [], + "hidden_actual_working_time": 0, + "id": 3, + "level": "Fachkraft", + "name": "Fachkraft3 Hidden", + "target_working_time": 0, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 0, + "forbidden_days": [], + "forbidden_shifts": [], + "hidden_actual_working_time": 0, + "id": 4, + "level": "Fachkraft", + "name": "Fachkraft4 Hidden", + "target_working_time": 0, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 0, + "forbidden_days": [], + "forbidden_shifts": [], + "hidden_actual_working_time": 0, + "id": 5, + "level": "Fachkraft", + "name": "Fachkraft5 Hidden", + "target_working_time": 0, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 0, + "forbidden_days": [], + "forbidden_shifts": [], + "hidden_actual_working_time": 0, + "id": 6, + "level": "Hilfskraft", + "name": "Hilfskraft6 Hidden", + "target_working_time": 0, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 0, + "forbidden_days": [], + "forbidden_shifts": [], + "hidden_actual_working_time": 0, + "id": 7, + "level": "Hilfskraft", + "name": "Hilfskraft7 Hidden", + "target_working_time": 0, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 0, + "forbidden_days": [], + "forbidden_shifts": [], + "hidden_actual_working_time": 0, + "id": 8, + "level": "Hilfskraft", + "name": "Hilfskraft8 Hidden", + "target_working_time": 0, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + } + ], + "fulfilled_day_off_cells": [ + [ + 925, + "2024-11-21" + ], + [ + 2963, + "2024-11-26" + ] + ], + "fulfilled_shift_wish_cells": [ + [ + 3868, + "2024-11-12" + ], + [ + 3868, + "2024-11-14" + ], + [ + 3868, + "2024-11-16" + ], + [ + 3868, + "2024-11-23" + ], + [ + 917, + "2024-11-30" + ], + [ + 3868, + "2024-11-22" + ], + [ + 3868, + "2024-11-25" + ], + [ + 791, + "2024-11-25" + ], + [ + 3868, + "2024-11-30" + ], + [ + 3868, + "2024-11-01" + ], + [ + 3868, + "2024-11-27" + ], + [ + 7603, + "2024-11-11" + ], + [ + 3868, + "2024-11-24" + ], + [ + 3868, + "2024-11-26" + ], + [ + 3868, + "2024-11-06" + ], + [ + 3868, + "2024-11-20" + ], + [ + 3868, + "2024-11-09" + ], + [ + 3868, + "2024-11-10" + ], + [ + 3868, + "2024-11-04" + ], + [ + 3868, + "2024-11-08" + ], + [ + 3868, + "2024-11-03" + ], + [ + 3868, + "2024-11-21" + ], + [ + 3868, + "2024-11-02" + ], + [ + 3868, + "2024-11-05" + ], + [ + 3868, + "2024-11-07" + ], + [ + 3868, + "2024-11-19" + ] + ], + "selected_solution_file_name": "soluting_of_test_all_costraints_single_case", + "shifts": [ + { + "abbreviation": "F", + "color": "#a8d51f", + "duration": 460, + "id": 0, + "is_exclusive": false, + "name": "Fr\u00fch" + }, + { + "abbreviation": "Z", + "color": "#3a9ea1", + "duration": 460, + "id": 1, + "is_exclusive": false, + "name": "Zwischen" + }, + { + "abbreviation": "S", + "color": "#f69e17", + "duration": 460, + "id": 2, + "is_exclusive": false, + "name": "Sp\u00e4t" + }, + { + "abbreviation": "N", + "color": "#225e62", + "duration": 565, + "id": 3, + "is_exclusive": false, + "name": "Nacht" + }, + { + "abbreviation": "Z", + "color": "oklch(82.1% 0.087 285.6)", + "duration": 360, + "id": 4, + "is_exclusive": true, + "name": "Z60" + }, + { + "abbreviation": "F", + "color": "#dadada", + "duration": 460, + "id": 5, + "is_exclusive": true, + "name": "F2_" + }, + { + "abbreviation": "S", + "color": "#dadada", + "duration": 460, + "id": 6, + "is_exclusive": true, + "name": "S2_" + }, + { + "abbreviation": "N", + "color": "#dadada", + "duration": 565, + "id": 7, + "is_exclusive": true, + "name": "N5" + } + ], + "solution_file_names": [ + "solution_77_2024-11-01-2024-11-30_wdefault", + "solution_77_2024-12-01-2024-12-31_wdefault", + "solution_77_2025-02-01-2025-02-28_wdefault" + ], + "stats": { + "consecutive_night_shifts_gt_3": 10, + "consecutive_working_days_gt_5": 9, + "forward_rotation_violations": 68, + "no_free_days_around_weekend": 59, + "no_free_weekend": 36, + "not_free_after_night_shift": 33, + "total_overtime_hours": 335.22, + "violated_wish_total": 14 + }, + "variables": { + "(0, '2024-11-01', 0)": 0, + "(0, '2024-11-01', 1)": 0, + "(0, '2024-11-01', 2)": 0, + "(0, '2024-11-01', 3)": 0, + "(0, '2024-11-01', 4)": 0, + "(0, '2024-11-01', 5)": 0, + "(0, '2024-11-01', 6)": 0, + "(0, '2024-11-01', 7)": 0, + "(0, '2024-11-02', 0)": 0, + "(0, '2024-11-02', 1)": 0, + "(0, '2024-11-02', 2)": 0, + "(0, '2024-11-02', 3)": 0, + "(0, '2024-11-02', 4)": 0, + "(0, '2024-11-02', 5)": 0, + "(0, '2024-11-02', 6)": 0, + "(0, '2024-11-02', 7)": 0, + "(0, '2024-11-03', 0)": 0, + "(0, '2024-11-03', 1)": 0, + "(0, '2024-11-03', 2)": 0, + "(0, '2024-11-03', 3)": 0, + "(0, '2024-11-03', 4)": 0, + "(0, '2024-11-03', 5)": 0, + "(0, '2024-11-03', 6)": 0, + "(0, '2024-11-03', 7)": 0, + "(0, '2024-11-04', 0)": 0, + "(0, '2024-11-04', 1)": 0, + "(0, '2024-11-04', 2)": 0, + "(0, '2024-11-04', 3)": 0, + "(0, '2024-11-04', 4)": 0, + "(0, '2024-11-04', 5)": 0, + "(0, '2024-11-04', 6)": 0, + "(0, '2024-11-04', 7)": 0, + "(0, '2024-11-05', 0)": 0, + "(0, '2024-11-05', 1)": 0, + "(0, '2024-11-05', 2)": 0, + "(0, '2024-11-05', 3)": 0, + "(0, '2024-11-05', 4)": 0, + "(0, '2024-11-05', 5)": 0, + "(0, '2024-11-05', 6)": 0, + "(0, '2024-11-05', 7)": 0, + "(0, '2024-11-06', 0)": 0, + "(0, '2024-11-06', 1)": 0, + "(0, '2024-11-06', 2)": 1, + "(0, '2024-11-06', 3)": 0, + "(0, '2024-11-06', 4)": 0, + "(0, '2024-11-06', 5)": 0, + "(0, '2024-11-06', 6)": 0, + "(0, '2024-11-06', 7)": 0, + "(0, '2024-11-07', 0)": 0, + "(0, '2024-11-07', 1)": 0, + "(0, '2024-11-07', 2)": 0, + "(0, '2024-11-07', 3)": 0, + "(0, '2024-11-07', 4)": 0, + "(0, '2024-11-07', 5)": 0, + "(0, '2024-11-07', 6)": 0, + "(0, '2024-11-07', 7)": 0, + "(0, '2024-11-08', 0)": 0, + "(0, '2024-11-08', 1)": 0, + "(0, '2024-11-08', 2)": 0, + "(0, '2024-11-08', 3)": 0, + "(0, '2024-11-08', 4)": 0, + "(0, '2024-11-08', 5)": 0, + "(0, '2024-11-08', 6)": 0, + "(0, '2024-11-08', 7)": 0, + "(0, '2024-11-09', 0)": 0, + "(0, '2024-11-09', 1)": 0, + "(0, '2024-11-09', 2)": 0, + "(0, '2024-11-09', 3)": 0, + "(0, '2024-11-09', 4)": 0, + "(0, '2024-11-09', 5)": 0, + "(0, '2024-11-09', 6)": 0, + "(0, '2024-11-09', 7)": 0, + "(0, '2024-11-10', 0)": 0, + "(0, '2024-11-10', 1)": 0, + "(0, '2024-11-10', 2)": 0, + "(0, '2024-11-10', 3)": 0, + "(0, '2024-11-10', 4)": 0, + "(0, '2024-11-10', 5)": 0, + "(0, '2024-11-10', 6)": 0, + "(0, '2024-11-10', 7)": 0, + "(0, '2024-11-11', 0)": 0, + "(0, '2024-11-11', 1)": 0, + "(0, '2024-11-11', 2)": 0, + "(0, '2024-11-11', 3)": 0, + "(0, '2024-11-11', 4)": 0, + "(0, '2024-11-11', 5)": 0, + "(0, '2024-11-11', 6)": 0, + "(0, '2024-11-11', 7)": 0, + "(0, '2024-11-12', 0)": 0, + "(0, '2024-11-12', 1)": 0, + "(0, '2024-11-12', 2)": 0, + "(0, '2024-11-12', 3)": 0, + "(0, '2024-11-12', 4)": 0, + "(0, '2024-11-12', 5)": 0, + "(0, '2024-11-12', 6)": 0, + "(0, '2024-11-12', 7)": 0, + "(0, '2024-11-13', 0)": 1, + "(0, '2024-11-13', 1)": 0, + "(0, '2024-11-13', 2)": 0, + "(0, '2024-11-13', 3)": 0, + "(0, '2024-11-13', 4)": 0, + "(0, '2024-11-13', 5)": 0, + "(0, '2024-11-13', 6)": 0, + "(0, '2024-11-13', 7)": 0, + "(0, '2024-11-14', 0)": 0, + "(0, '2024-11-14', 1)": 0, + "(0, '2024-11-14', 2)": 0, + "(0, '2024-11-14', 3)": 0, + "(0, '2024-11-14', 4)": 0, + "(0, '2024-11-14', 5)": 0, + "(0, '2024-11-14', 6)": 0, + "(0, '2024-11-14', 7)": 0, + "(0, '2024-11-15', 0)": 0, + "(0, '2024-11-15', 1)": 0, + "(0, '2024-11-15', 2)": 0, + "(0, '2024-11-15', 3)": 0, + "(0, '2024-11-15', 4)": 0, + "(0, '2024-11-15', 5)": 0, + "(0, '2024-11-15', 6)": 0, + "(0, '2024-11-15', 7)": 0, + "(0, '2024-11-16', 0)": 0, + "(0, '2024-11-16', 1)": 0, + "(0, '2024-11-16', 2)": 0, + "(0, '2024-11-16', 3)": 0, + "(0, '2024-11-16', 4)": 0, + "(0, '2024-11-16', 5)": 0, + "(0, '2024-11-16', 6)": 0, + "(0, '2024-11-16', 7)": 0, + "(0, '2024-11-17', 0)": 0, + "(0, '2024-11-17', 1)": 0, + "(0, '2024-11-17', 2)": 0, + "(0, '2024-11-17', 3)": 0, + "(0, '2024-11-17', 4)": 0, + "(0, '2024-11-17', 5)": 0, + "(0, '2024-11-17', 6)": 0, + "(0, '2024-11-17', 7)": 0, + "(0, '2024-11-18', 0)": 0, + "(0, '2024-11-18', 1)": 0, + "(0, '2024-11-18', 2)": 0, + "(0, '2024-11-18', 3)": 0, + "(0, '2024-11-18', 4)": 0, + "(0, '2024-11-18', 5)": 0, + "(0, '2024-11-18', 6)": 0, + "(0, '2024-11-18', 7)": 0, + "(0, '2024-11-19', 0)": 0, + "(0, '2024-11-19', 1)": 0, + "(0, '2024-11-19', 2)": 0, + "(0, '2024-11-19', 3)": 0, + "(0, '2024-11-19', 4)": 0, + "(0, '2024-11-19', 5)": 0, + "(0, '2024-11-19', 6)": 0, + "(0, '2024-11-19', 7)": 0, + "(0, '2024-11-20', 0)": 0, + "(0, '2024-11-20', 1)": 0, + "(0, '2024-11-20', 2)": 0, + "(0, '2024-11-20', 3)": 0, + "(0, '2024-11-20', 4)": 0, + "(0, '2024-11-20', 5)": 0, + "(0, '2024-11-20', 6)": 0, + "(0, '2024-11-20', 7)": 0, + "(0, '2024-11-21', 0)": 0, + "(0, '2024-11-21', 1)": 0, + "(0, '2024-11-21', 2)": 1, + "(0, '2024-11-21', 3)": 0, + "(0, '2024-11-21', 4)": 0, + "(0, '2024-11-21', 5)": 0, + "(0, '2024-11-21', 6)": 0, + "(0, '2024-11-21', 7)": 0, + "(0, '2024-11-22', 0)": 0, + "(0, '2024-11-22', 1)": 0, + "(0, '2024-11-22', 2)": 0, + "(0, '2024-11-22', 3)": 0, + "(0, '2024-11-22', 4)": 0, + "(0, '2024-11-22', 5)": 0, + "(0, '2024-11-22', 6)": 0, + "(0, '2024-11-22', 7)": 0, + "(0, '2024-11-23', 0)": 0, + "(0, '2024-11-23', 1)": 0, + "(0, '2024-11-23', 2)": 0, + "(0, '2024-11-23', 3)": 0, + "(0, '2024-11-23', 4)": 0, + "(0, '2024-11-23', 5)": 0, + "(0, '2024-11-23', 6)": 0, + "(0, '2024-11-23', 7)": 0, + "(0, '2024-11-24', 0)": 0, + "(0, '2024-11-24', 1)": 0, + "(0, '2024-11-24', 2)": 0, + "(0, '2024-11-24', 3)": 0, + "(0, '2024-11-24', 4)": 0, + "(0, '2024-11-24', 5)": 0, + "(0, '2024-11-24', 6)": 0, + "(0, '2024-11-24', 7)": 0, + "(0, '2024-11-25', 0)": 0, + "(0, '2024-11-25', 1)": 0, + "(0, '2024-11-25', 2)": 0, + "(0, '2024-11-25', 3)": 0, + "(0, '2024-11-25', 4)": 0, + "(0, '2024-11-25', 5)": 0, + "(0, '2024-11-25', 6)": 0, + "(0, '2024-11-25', 7)": 0, + "(0, '2024-11-26', 0)": 0, + "(0, '2024-11-26', 1)": 0, + "(0, '2024-11-26', 2)": 0, + "(0, '2024-11-26', 3)": 0, + "(0, '2024-11-26', 4)": 0, + "(0, '2024-11-26', 5)": 0, + "(0, '2024-11-26', 6)": 0, + "(0, '2024-11-26', 7)": 0, + "(0, '2024-11-27', 0)": 0, + "(0, '2024-11-27', 1)": 0, + "(0, '2024-11-27', 2)": 0, + "(0, '2024-11-27', 3)": 0, + "(0, '2024-11-27', 4)": 0, + "(0, '2024-11-27', 5)": 0, + "(0, '2024-11-27', 6)": 0, + "(0, '2024-11-27', 7)": 0, + "(0, '2024-11-28', 0)": 0, + "(0, '2024-11-28', 1)": 0, + "(0, '2024-11-28', 2)": 0, + "(0, '2024-11-28', 3)": 0, + "(0, '2024-11-28', 4)": 0, + "(0, '2024-11-28', 5)": 0, + "(0, '2024-11-28', 6)": 0, + "(0, '2024-11-28', 7)": 0, + "(0, '2024-11-29', 0)": 0, + "(0, '2024-11-29', 1)": 0, + "(0, '2024-11-29', 2)": 0, + "(0, '2024-11-29', 3)": 0, + "(0, '2024-11-29', 4)": 0, + "(0, '2024-11-29', 5)": 0, + "(0, '2024-11-29', 6)": 0, + "(0, '2024-11-29', 7)": 0, + "(0, '2024-11-30', 0)": 0, + "(0, '2024-11-30', 1)": 0, + "(0, '2024-11-30', 2)": 0, + "(0, '2024-11-30', 3)": 0, + "(0, '2024-11-30', 4)": 0, + "(0, '2024-11-30', 5)": 0, + "(0, '2024-11-30', 6)": 0, + "(0, '2024-11-30', 7)": 0, + "(1, '2024-11-01', 0)": 0, + "(1, '2024-11-01', 1)": 0, + "(1, '2024-11-01', 2)": 0, + "(1, '2024-11-01', 3)": 0, + "(1, '2024-11-01', 4)": 0, + "(1, '2024-11-01', 5)": 0, + "(1, '2024-11-01', 6)": 0, + "(1, '2024-11-01', 7)": 0, + "(1, '2024-11-02', 0)": 0, + "(1, '2024-11-02', 1)": 0, + "(1, '2024-11-02', 2)": 1, + "(1, '2024-11-02', 3)": 0, + "(1, '2024-11-02', 4)": 0, + "(1, '2024-11-02', 5)": 0, + "(1, '2024-11-02', 6)": 0, + "(1, '2024-11-02', 7)": 0, + "(1, '2024-11-03', 0)": 0, + "(1, '2024-11-03', 1)": 0, + "(1, '2024-11-03', 2)": 0, + "(1, '2024-11-03', 3)": 0, + "(1, '2024-11-03', 4)": 0, + "(1, '2024-11-03', 5)": 0, + "(1, '2024-11-03', 6)": 0, + "(1, '2024-11-03', 7)": 0, + "(1, '2024-11-04', 0)": 0, + "(1, '2024-11-04', 1)": 0, + "(1, '2024-11-04', 2)": 0, + "(1, '2024-11-04', 3)": 0, + "(1, '2024-11-04', 4)": 0, + "(1, '2024-11-04', 5)": 0, + "(1, '2024-11-04', 6)": 0, + "(1, '2024-11-04', 7)": 0, + "(1, '2024-11-05', 0)": 0, + "(1, '2024-11-05', 1)": 0, + "(1, '2024-11-05', 2)": 0, + "(1, '2024-11-05', 3)": 0, + "(1, '2024-11-05', 4)": 0, + "(1, '2024-11-05', 5)": 0, + "(1, '2024-11-05', 6)": 0, + "(1, '2024-11-05', 7)": 0, + "(1, '2024-11-06', 0)": 0, + "(1, '2024-11-06', 1)": 0, + "(1, '2024-11-06', 2)": 0, + "(1, '2024-11-06', 3)": 0, + "(1, '2024-11-06', 4)": 0, + "(1, '2024-11-06', 5)": 0, + "(1, '2024-11-06', 6)": 0, + "(1, '2024-11-06', 7)": 0, + "(1, '2024-11-07', 0)": 0, + "(1, '2024-11-07', 1)": 0, + "(1, '2024-11-07', 2)": 0, + "(1, '2024-11-07', 3)": 0, + "(1, '2024-11-07', 4)": 0, + "(1, '2024-11-07', 5)": 0, + "(1, '2024-11-07', 6)": 0, + "(1, '2024-11-07', 7)": 0, + "(1, '2024-11-08', 0)": 0, + "(1, '2024-11-08', 1)": 0, + "(1, '2024-11-08', 2)": 0, + "(1, '2024-11-08', 3)": 0, + "(1, '2024-11-08', 4)": 0, + "(1, '2024-11-08', 5)": 0, + "(1, '2024-11-08', 6)": 0, + "(1, '2024-11-08', 7)": 0, + "(1, '2024-11-09', 0)": 0, + "(1, '2024-11-09', 1)": 0, + "(1, '2024-11-09', 2)": 0, + "(1, '2024-11-09', 3)": 0, + "(1, '2024-11-09', 4)": 0, + "(1, '2024-11-09', 5)": 0, + "(1, '2024-11-09', 6)": 0, + "(1, '2024-11-09', 7)": 0, + "(1, '2024-11-10', 0)": 0, + "(1, '2024-11-10', 1)": 0, + "(1, '2024-11-10', 2)": 0, + "(1, '2024-11-10', 3)": 0, + "(1, '2024-11-10', 4)": 0, + "(1, '2024-11-10', 5)": 0, + "(1, '2024-11-10', 6)": 0, + "(1, '2024-11-10', 7)": 0, + "(1, '2024-11-11', 0)": 0, + "(1, '2024-11-11', 1)": 0, + "(1, '2024-11-11', 2)": 0, + "(1, '2024-11-11', 3)": 0, + "(1, '2024-11-11', 4)": 0, + "(1, '2024-11-11', 5)": 0, + "(1, '2024-11-11', 6)": 0, + "(1, '2024-11-11', 7)": 0, + "(1, '2024-11-12', 0)": 0, + "(1, '2024-11-12', 1)": 0, + "(1, '2024-11-12', 2)": 0, + "(1, '2024-11-12', 3)": 0, + "(1, '2024-11-12', 4)": 0, + "(1, '2024-11-12', 5)": 0, + "(1, '2024-11-12', 6)": 0, + "(1, '2024-11-12', 7)": 0, + "(1, '2024-11-13', 0)": 0, + "(1, '2024-11-13', 1)": 0, + "(1, '2024-11-13', 2)": 0, + "(1, '2024-11-13', 3)": 0, + "(1, '2024-11-13', 4)": 0, + "(1, '2024-11-13', 5)": 0, + "(1, '2024-11-13', 6)": 0, + "(1, '2024-11-13', 7)": 0, + "(1, '2024-11-14', 0)": 0, + "(1, '2024-11-14', 1)": 0, + "(1, '2024-11-14', 2)": 0, + "(1, '2024-11-14', 3)": 0, + "(1, '2024-11-14', 4)": 0, + "(1, '2024-11-14', 5)": 0, + "(1, '2024-11-14', 6)": 0, + "(1, '2024-11-14', 7)": 0, + "(1, '2024-11-15', 0)": 0, + "(1, '2024-11-15', 1)": 0, + "(1, '2024-11-15', 2)": 0, + "(1, '2024-11-15', 3)": 0, + "(1, '2024-11-15', 4)": 0, + "(1, '2024-11-15', 5)": 0, + "(1, '2024-11-15', 6)": 0, + "(1, '2024-11-15', 7)": 0, + "(1, '2024-11-16', 0)": 0, + "(1, '2024-11-16', 1)": 0, + "(1, '2024-11-16', 2)": 0, + "(1, '2024-11-16', 3)": 0, + "(1, '2024-11-16', 4)": 0, + "(1, '2024-11-16', 5)": 0, + "(1, '2024-11-16', 6)": 0, + "(1, '2024-11-16', 7)": 0, + "(1, '2024-11-17', 0)": 0, + "(1, '2024-11-17', 1)": 0, + "(1, '2024-11-17', 2)": 0, + "(1, '2024-11-17', 3)": 0, + "(1, '2024-11-17', 4)": 0, + "(1, '2024-11-17', 5)": 0, + "(1, '2024-11-17', 6)": 0, + "(1, '2024-11-17', 7)": 0, + "(1, '2024-11-18', 0)": 0, + "(1, '2024-11-18', 1)": 0, + "(1, '2024-11-18', 2)": 0, + "(1, '2024-11-18', 3)": 0, + "(1, '2024-11-18', 4)": 0, + "(1, '2024-11-18', 5)": 0, + "(1, '2024-11-18', 6)": 0, + "(1, '2024-11-18', 7)": 0, + "(1, '2024-11-19', 0)": 0, + "(1, '2024-11-19', 1)": 0, + "(1, '2024-11-19', 2)": 0, + "(1, '2024-11-19', 3)": 0, + "(1, '2024-11-19', 4)": 0, + "(1, '2024-11-19', 5)": 0, + "(1, '2024-11-19', 6)": 0, + "(1, '2024-11-19', 7)": 0, + "(1, '2024-11-20', 0)": 0, + "(1, '2024-11-20', 1)": 0, + "(1, '2024-11-20', 2)": 0, + "(1, '2024-11-20', 3)": 0, + "(1, '2024-11-20', 4)": 0, + "(1, '2024-11-20', 5)": 0, + "(1, '2024-11-20', 6)": 0, + "(1, '2024-11-20', 7)": 0, + "(1, '2024-11-21', 0)": 0, + "(1, '2024-11-21', 1)": 0, + "(1, '2024-11-21', 2)": 0, + "(1, '2024-11-21', 3)": 0, + "(1, '2024-11-21', 4)": 0, + "(1, '2024-11-21', 5)": 0, + "(1, '2024-11-21', 6)": 0, + "(1, '2024-11-21', 7)": 0, + "(1, '2024-11-22', 0)": 0, + "(1, '2024-11-22', 1)": 0, + "(1, '2024-11-22', 2)": 1, + "(1, '2024-11-22', 3)": 0, + "(1, '2024-11-22', 4)": 0, + "(1, '2024-11-22', 5)": 0, + "(1, '2024-11-22', 6)": 0, + "(1, '2024-11-22', 7)": 0, + "(1, '2024-11-23', 0)": 0, + "(1, '2024-11-23', 1)": 0, + "(1, '2024-11-23', 2)": 0, + "(1, '2024-11-23', 3)": 0, + "(1, '2024-11-23', 4)": 0, + "(1, '2024-11-23', 5)": 0, + "(1, '2024-11-23', 6)": 0, + "(1, '2024-11-23', 7)": 0, + "(1, '2024-11-24', 0)": 0, + "(1, '2024-11-24', 1)": 0, + "(1, '2024-11-24', 2)": 0, + "(1, '2024-11-24', 3)": 0, + "(1, '2024-11-24', 4)": 0, + "(1, '2024-11-24', 5)": 0, + "(1, '2024-11-24', 6)": 0, + "(1, '2024-11-24', 7)": 0, + "(1, '2024-11-25', 0)": 1, + "(1, '2024-11-25', 1)": 0, + "(1, '2024-11-25', 2)": 0, + "(1, '2024-11-25', 3)": 0, + "(1, '2024-11-25', 4)": 0, + "(1, '2024-11-25', 5)": 0, + "(1, '2024-11-25', 6)": 0, + "(1, '2024-11-25', 7)": 0, + "(1, '2024-11-26', 0)": 0, + "(1, '2024-11-26', 1)": 0, + "(1, '2024-11-26', 2)": 0, + "(1, '2024-11-26', 3)": 0, + "(1, '2024-11-26', 4)": 0, + "(1, '2024-11-26', 5)": 0, + "(1, '2024-11-26', 6)": 0, + "(1, '2024-11-26', 7)": 0, + "(1, '2024-11-27', 0)": 0, + "(1, '2024-11-27', 1)": 0, + "(1, '2024-11-27', 2)": 0, + "(1, '2024-11-27', 3)": 0, + "(1, '2024-11-27', 4)": 0, + "(1, '2024-11-27', 5)": 0, + "(1, '2024-11-27', 6)": 0, + "(1, '2024-11-27', 7)": 0, + "(1, '2024-11-28', 0)": 0, + "(1, '2024-11-28', 1)": 0, + "(1, '2024-11-28', 2)": 0, + "(1, '2024-11-28', 3)": 0, + "(1, '2024-11-28', 4)": 0, + "(1, '2024-11-28', 5)": 0, + "(1, '2024-11-28', 6)": 0, + "(1, '2024-11-28', 7)": 0, + "(1, '2024-11-29', 0)": 0, + "(1, '2024-11-29', 1)": 0, + "(1, '2024-11-29', 2)": 0, + "(1, '2024-11-29', 3)": 0, + "(1, '2024-11-29', 4)": 0, + "(1, '2024-11-29', 5)": 0, + "(1, '2024-11-29', 6)": 0, + "(1, '2024-11-29', 7)": 0, + "(1, '2024-11-30', 0)": 0, + "(1, '2024-11-30', 1)": 0, + "(1, '2024-11-30', 2)": 0, + "(1, '2024-11-30', 3)": 0, + "(1, '2024-11-30', 4)": 0, + "(1, '2024-11-30', 5)": 0, + "(1, '2024-11-30', 6)": 0, + "(1, '2024-11-30', 7)": 0, + "(1230, '2024-11-01', 0)": 0, + "(1230, '2024-11-01', 1)": 0, + "(1230, '2024-11-01', 2)": 0, + "(1230, '2024-11-01', 3)": 1, + "(1230, '2024-11-01', 4)": 0, + "(1230, '2024-11-01', 5)": 0, + "(1230, '2024-11-01', 6)": 0, + "(1230, '2024-11-01', 7)": 0, + "(1230, '2024-11-02', 0)": 0, + "(1230, '2024-11-02', 1)": 0, + "(1230, '2024-11-02', 2)": 0, + "(1230, '2024-11-02', 3)": 0, + "(1230, '2024-11-02', 4)": 0, + "(1230, '2024-11-02', 5)": 0, + "(1230, '2024-11-02', 6)": 0, + "(1230, '2024-11-02', 7)": 0, + "(1230, '2024-11-03', 0)": 0, + "(1230, '2024-11-03', 1)": 0, + "(1230, '2024-11-03', 2)": 0, + "(1230, '2024-11-03', 3)": 0, + "(1230, '2024-11-03', 4)": 0, + "(1230, '2024-11-03', 5)": 0, + "(1230, '2024-11-03', 6)": 0, + "(1230, '2024-11-03', 7)": 0, + "(1230, '2024-11-04', 0)": 0, + "(1230, '2024-11-04', 1)": 0, + "(1230, '2024-11-04', 2)": 1, + "(1230, '2024-11-04', 3)": 0, + "(1230, '2024-11-04', 4)": 0, + "(1230, '2024-11-04', 5)": 0, + "(1230, '2024-11-04', 6)": 0, + "(1230, '2024-11-04', 7)": 0, + "(1230, '2024-11-05', 0)": 0, + "(1230, '2024-11-05', 1)": 0, + "(1230, '2024-11-05', 2)": 0, + "(1230, '2024-11-05', 3)": 0, + "(1230, '2024-11-05', 4)": 0, + "(1230, '2024-11-05', 5)": 0, + "(1230, '2024-11-05', 6)": 0, + "(1230, '2024-11-05', 7)": 0, + "(1230, '2024-11-06', 0)": 1, + "(1230, '2024-11-06', 1)": 0, + "(1230, '2024-11-06', 2)": 0, + "(1230, '2024-11-06', 3)": 0, + "(1230, '2024-11-06', 4)": 0, + "(1230, '2024-11-06', 5)": 0, + "(1230, '2024-11-06', 6)": 0, + "(1230, '2024-11-06', 7)": 0, + "(1230, '2024-11-07', 0)": 1, + "(1230, '2024-11-07', 1)": 0, + "(1230, '2024-11-07', 2)": 0, + "(1230, '2024-11-07', 3)": 0, + "(1230, '2024-11-07', 4)": 0, + "(1230, '2024-11-07', 5)": 0, + "(1230, '2024-11-07', 6)": 0, + "(1230, '2024-11-07', 7)": 0, + "(1230, '2024-11-08', 0)": 0, + "(1230, '2024-11-08', 1)": 0, + "(1230, '2024-11-08', 2)": 0, + "(1230, '2024-11-08', 3)": 0, + "(1230, '2024-11-08', 4)": 0, + "(1230, '2024-11-08', 5)": 0, + "(1230, '2024-11-08', 6)": 0, + "(1230, '2024-11-08', 7)": 0, + "(1230, '2024-11-09', 0)": 1, + "(1230, '2024-11-09', 1)": 0, + "(1230, '2024-11-09', 2)": 0, + "(1230, '2024-11-09', 3)": 0, + "(1230, '2024-11-09', 4)": 0, + "(1230, '2024-11-09', 5)": 0, + "(1230, '2024-11-09', 6)": 0, + "(1230, '2024-11-09', 7)": 0, + "(1230, '2024-11-10', 0)": 1, + "(1230, '2024-11-10', 1)": 0, + "(1230, '2024-11-10', 2)": 0, + "(1230, '2024-11-10', 3)": 0, + "(1230, '2024-11-10', 4)": 0, + "(1230, '2024-11-10', 5)": 0, + "(1230, '2024-11-10', 6)": 0, + "(1230, '2024-11-10', 7)": 0, + "(1230, '2024-11-11', 0)": 0, + "(1230, '2024-11-11', 1)": 0, + "(1230, '2024-11-11', 2)": 0, + "(1230, '2024-11-11', 3)": 0, + "(1230, '2024-11-11', 4)": 0, + "(1230, '2024-11-11', 5)": 0, + "(1230, '2024-11-11', 6)": 0, + "(1230, '2024-11-11', 7)": 0, + "(1230, '2024-11-12', 0)": 1, + "(1230, '2024-11-12', 1)": 0, + "(1230, '2024-11-12', 2)": 0, + "(1230, '2024-11-12', 3)": 0, + "(1230, '2024-11-12', 4)": 0, + "(1230, '2024-11-12', 5)": 0, + "(1230, '2024-11-12', 6)": 0, + "(1230, '2024-11-12', 7)": 0, + "(1230, '2024-11-13', 0)": 0, + "(1230, '2024-11-13', 1)": 0, + "(1230, '2024-11-13', 2)": 1, + "(1230, '2024-11-13', 3)": 0, + "(1230, '2024-11-13', 4)": 0, + "(1230, '2024-11-13', 5)": 0, + "(1230, '2024-11-13', 6)": 0, + "(1230, '2024-11-13', 7)": 0, + "(1230, '2024-11-14', 0)": 0, + "(1230, '2024-11-14', 1)": 0, + "(1230, '2024-11-14', 2)": 0, + "(1230, '2024-11-14', 3)": 0, + "(1230, '2024-11-14', 4)": 0, + "(1230, '2024-11-14', 5)": 0, + "(1230, '2024-11-14', 6)": 0, + "(1230, '2024-11-14', 7)": 0, + "(1230, '2024-11-15', 0)": 0, + "(1230, '2024-11-15', 1)": 0, + "(1230, '2024-11-15', 2)": 0, + "(1230, '2024-11-15', 3)": 0, + "(1230, '2024-11-15', 4)": 0, + "(1230, '2024-11-15', 5)": 0, + "(1230, '2024-11-15', 6)": 0, + "(1230, '2024-11-15', 7)": 0, + "(1230, '2024-11-16', 0)": 0, + "(1230, '2024-11-16', 1)": 0, + "(1230, '2024-11-16', 2)": 0, + "(1230, '2024-11-16', 3)": 0, + "(1230, '2024-11-16', 4)": 0, + "(1230, '2024-11-16', 5)": 0, + "(1230, '2024-11-16', 6)": 0, + "(1230, '2024-11-16', 7)": 0, + "(1230, '2024-11-17', 0)": 0, + "(1230, '2024-11-17', 1)": 0, + "(1230, '2024-11-17', 2)": 0, + "(1230, '2024-11-17', 3)": 0, + "(1230, '2024-11-17', 4)": 0, + "(1230, '2024-11-17', 5)": 0, + "(1230, '2024-11-17', 6)": 0, + "(1230, '2024-11-17', 7)": 0, + "(1230, '2024-11-18', 0)": 0, + "(1230, '2024-11-18', 1)": 0, + "(1230, '2024-11-18', 2)": 1, + "(1230, '2024-11-18', 3)": 0, + "(1230, '2024-11-18', 4)": 0, + "(1230, '2024-11-18', 5)": 0, + "(1230, '2024-11-18', 6)": 0, + "(1230, '2024-11-18', 7)": 0, + "(1230, '2024-11-19', 0)": 0, + "(1230, '2024-11-19', 1)": 0, + "(1230, '2024-11-19', 2)": 0, + "(1230, '2024-11-19', 3)": 0, + "(1230, '2024-11-19', 4)": 0, + "(1230, '2024-11-19', 5)": 0, + "(1230, '2024-11-19', 6)": 0, + "(1230, '2024-11-19', 7)": 0, + "(1230, '2024-11-20', 0)": 0, + "(1230, '2024-11-20', 1)": 0, + "(1230, '2024-11-20', 2)": 1, + "(1230, '2024-11-20', 3)": 0, + "(1230, '2024-11-20', 4)": 0, + "(1230, '2024-11-20', 5)": 0, + "(1230, '2024-11-20', 6)": 0, + "(1230, '2024-11-20', 7)": 0, + "(1230, '2024-11-21', 0)": 0, + "(1230, '2024-11-21', 1)": 0, + "(1230, '2024-11-21', 2)": 0, + "(1230, '2024-11-21', 3)": 0, + "(1230, '2024-11-21', 4)": 0, + "(1230, '2024-11-21', 5)": 0, + "(1230, '2024-11-21', 6)": 0, + "(1230, '2024-11-21', 7)": 0, + "(1230, '2024-11-22', 0)": 0, + "(1230, '2024-11-22', 1)": 0, + "(1230, '2024-11-22', 2)": 0, + "(1230, '2024-11-22', 3)": 1, + "(1230, '2024-11-22', 4)": 0, + "(1230, '2024-11-22', 5)": 0, + "(1230, '2024-11-22', 6)": 0, + "(1230, '2024-11-22', 7)": 0, + "(1230, '2024-11-23', 0)": 0, + "(1230, '2024-11-23', 1)": 0, + "(1230, '2024-11-23', 2)": 0, + "(1230, '2024-11-23', 3)": 0, + "(1230, '2024-11-23', 4)": 0, + "(1230, '2024-11-23', 5)": 0, + "(1230, '2024-11-23', 6)": 0, + "(1230, '2024-11-23', 7)": 0, + "(1230, '2024-11-24', 0)": 1, + "(1230, '2024-11-24', 1)": 0, + "(1230, '2024-11-24', 2)": 0, + "(1230, '2024-11-24', 3)": 0, + "(1230, '2024-11-24', 4)": 0, + "(1230, '2024-11-24', 5)": 0, + "(1230, '2024-11-24', 6)": 0, + "(1230, '2024-11-24', 7)": 0, + "(1230, '2024-11-25', 0)": 0, + "(1230, '2024-11-25', 1)": 0, + "(1230, '2024-11-25', 2)": 1, + "(1230, '2024-11-25', 3)": 0, + "(1230, '2024-11-25', 4)": 0, + "(1230, '2024-11-25', 5)": 0, + "(1230, '2024-11-25', 6)": 0, + "(1230, '2024-11-25', 7)": 0, + "(1230, '2024-11-26', 0)": 0, + "(1230, '2024-11-26', 1)": 0, + "(1230, '2024-11-26', 2)": 1, + "(1230, '2024-11-26', 3)": 0, + "(1230, '2024-11-26', 4)": 0, + "(1230, '2024-11-26', 5)": 0, + "(1230, '2024-11-26', 6)": 0, + "(1230, '2024-11-26', 7)": 0, + "(1230, '2024-11-27', 0)": 0, + "(1230, '2024-11-27', 1)": 0, + "(1230, '2024-11-27', 2)": 0, + "(1230, '2024-11-27', 3)": 0, + "(1230, '2024-11-27', 4)": 0, + "(1230, '2024-11-27', 5)": 0, + "(1230, '2024-11-27', 6)": 0, + "(1230, '2024-11-27', 7)": 0, + "(1230, '2024-11-28', 0)": 1, + "(1230, '2024-11-28', 1)": 0, + "(1230, '2024-11-28', 2)": 0, + "(1230, '2024-11-28', 3)": 0, + "(1230, '2024-11-28', 4)": 0, + "(1230, '2024-11-28', 5)": 0, + "(1230, '2024-11-28', 6)": 0, + "(1230, '2024-11-28', 7)": 0, + "(1230, '2024-11-29', 0)": 1, + "(1230, '2024-11-29', 1)": 0, + "(1230, '2024-11-29', 2)": 0, + "(1230, '2024-11-29', 3)": 0, + "(1230, '2024-11-29', 4)": 0, + "(1230, '2024-11-29', 5)": 0, + "(1230, '2024-11-29', 6)": 0, + "(1230, '2024-11-29', 7)": 0, + "(1230, '2024-11-30', 0)": 0, + "(1230, '2024-11-30', 1)": 0, + "(1230, '2024-11-30', 2)": 1, + "(1230, '2024-11-30', 3)": 0, + "(1230, '2024-11-30', 4)": 0, + "(1230, '2024-11-30', 5)": 0, + "(1230, '2024-11-30', 6)": 0, + "(1230, '2024-11-30', 7)": 0, + "(2, '2024-11-01', 0)": 0, + "(2, '2024-11-01', 1)": 0, + "(2, '2024-11-01', 2)": 0, + "(2, '2024-11-01', 3)": 0, + "(2, '2024-11-01', 4)": 0, + "(2, '2024-11-01', 5)": 0, + "(2, '2024-11-01', 6)": 0, + "(2, '2024-11-01', 7)": 0, + "(2, '2024-11-02', 0)": 1, + "(2, '2024-11-02', 1)": 0, + "(2, '2024-11-02', 2)": 0, + "(2, '2024-11-02', 3)": 0, + "(2, '2024-11-02', 4)": 0, + "(2, '2024-11-02', 5)": 0, + "(2, '2024-11-02', 6)": 0, + "(2, '2024-11-02', 7)": 0, + "(2, '2024-11-03', 0)": 0, + "(2, '2024-11-03', 1)": 0, + "(2, '2024-11-03', 2)": 0, + "(2, '2024-11-03', 3)": 0, + "(2, '2024-11-03', 4)": 0, + "(2, '2024-11-03', 5)": 0, + "(2, '2024-11-03', 6)": 0, + "(2, '2024-11-03', 7)": 0, + "(2, '2024-11-04', 0)": 0, + "(2, '2024-11-04', 1)": 0, + "(2, '2024-11-04', 2)": 0, + "(2, '2024-11-04', 3)": 0, + "(2, '2024-11-04', 4)": 0, + "(2, '2024-11-04', 5)": 0, + "(2, '2024-11-04', 6)": 0, + "(2, '2024-11-04', 7)": 0, + "(2, '2024-11-05', 0)": 0, + "(2, '2024-11-05', 1)": 0, + "(2, '2024-11-05', 2)": 0, + "(2, '2024-11-05', 3)": 0, + "(2, '2024-11-05', 4)": 0, + "(2, '2024-11-05', 5)": 0, + "(2, '2024-11-05', 6)": 0, + "(2, '2024-11-05', 7)": 0, + "(2, '2024-11-06', 0)": 1, + "(2, '2024-11-06', 1)": 0, + "(2, '2024-11-06', 2)": 0, + "(2, '2024-11-06', 3)": 0, + "(2, '2024-11-06', 4)": 0, + "(2, '2024-11-06', 5)": 0, + "(2, '2024-11-06', 6)": 0, + "(2, '2024-11-06', 7)": 0, + "(2, '2024-11-07', 0)": 0, + "(2, '2024-11-07', 1)": 0, + "(2, '2024-11-07', 2)": 0, + "(2, '2024-11-07', 3)": 0, + "(2, '2024-11-07', 4)": 0, + "(2, '2024-11-07', 5)": 0, + "(2, '2024-11-07', 6)": 0, + "(2, '2024-11-07', 7)": 0, + "(2, '2024-11-08', 0)": 0, + "(2, '2024-11-08', 1)": 0, + "(2, '2024-11-08', 2)": 1, + "(2, '2024-11-08', 3)": 0, + "(2, '2024-11-08', 4)": 0, + "(2, '2024-11-08', 5)": 0, + "(2, '2024-11-08', 6)": 0, + "(2, '2024-11-08', 7)": 0, + "(2, '2024-11-09', 0)": 0, + "(2, '2024-11-09', 1)": 0, + "(2, '2024-11-09', 2)": 0, + "(2, '2024-11-09', 3)": 0, + "(2, '2024-11-09', 4)": 0, + "(2, '2024-11-09', 5)": 0, + "(2, '2024-11-09', 6)": 0, + "(2, '2024-11-09', 7)": 0, + "(2, '2024-11-10', 0)": 0, + "(2, '2024-11-10', 1)": 0, + "(2, '2024-11-10', 2)": 0, + "(2, '2024-11-10', 3)": 0, + "(2, '2024-11-10', 4)": 0, + "(2, '2024-11-10', 5)": 0, + "(2, '2024-11-10', 6)": 0, + "(2, '2024-11-10', 7)": 0, + "(2, '2024-11-11', 0)": 0, + "(2, '2024-11-11', 1)": 0, + "(2, '2024-11-11', 2)": 0, + "(2, '2024-11-11', 3)": 0, + "(2, '2024-11-11', 4)": 0, + "(2, '2024-11-11', 5)": 0, + "(2, '2024-11-11', 6)": 0, + "(2, '2024-11-11', 7)": 0, + "(2, '2024-11-12', 0)": 0, + "(2, '2024-11-12', 1)": 0, + "(2, '2024-11-12', 2)": 0, + "(2, '2024-11-12', 3)": 0, + "(2, '2024-11-12', 4)": 0, + "(2, '2024-11-12', 5)": 0, + "(2, '2024-11-12', 6)": 0, + "(2, '2024-11-12', 7)": 0, + "(2, '2024-11-13', 0)": 0, + "(2, '2024-11-13', 1)": 0, + "(2, '2024-11-13', 2)": 0, + "(2, '2024-11-13', 3)": 0, + "(2, '2024-11-13', 4)": 0, + "(2, '2024-11-13', 5)": 0, + "(2, '2024-11-13', 6)": 0, + "(2, '2024-11-13', 7)": 0, + "(2, '2024-11-14', 0)": 0, + "(2, '2024-11-14', 1)": 0, + "(2, '2024-11-14', 2)": 0, + "(2, '2024-11-14', 3)": 0, + "(2, '2024-11-14', 4)": 0, + "(2, '2024-11-14', 5)": 0, + "(2, '2024-11-14', 6)": 0, + "(2, '2024-11-14', 7)": 0, + "(2, '2024-11-15', 0)": 0, + "(2, '2024-11-15', 1)": 0, + "(2, '2024-11-15', 2)": 0, + "(2, '2024-11-15', 3)": 0, + "(2, '2024-11-15', 4)": 0, + "(2, '2024-11-15', 5)": 0, + "(2, '2024-11-15', 6)": 0, + "(2, '2024-11-15', 7)": 0, + "(2, '2024-11-16', 0)": 0, + "(2, '2024-11-16', 1)": 0, + "(2, '2024-11-16', 2)": 1, + "(2, '2024-11-16', 3)": 0, + "(2, '2024-11-16', 4)": 0, + "(2, '2024-11-16', 5)": 0, + "(2, '2024-11-16', 6)": 0, + "(2, '2024-11-16', 7)": 0, + "(2, '2024-11-17', 0)": 0, + "(2, '2024-11-17', 1)": 0, + "(2, '2024-11-17', 2)": 0, + "(2, '2024-11-17', 3)": 0, + "(2, '2024-11-17', 4)": 0, + "(2, '2024-11-17', 5)": 0, + "(2, '2024-11-17', 6)": 0, + "(2, '2024-11-17', 7)": 0, + "(2, '2024-11-18', 0)": 0, + "(2, '2024-11-18', 1)": 0, + "(2, '2024-11-18', 2)": 0, + "(2, '2024-11-18', 3)": 0, + "(2, '2024-11-18', 4)": 0, + "(2, '2024-11-18', 5)": 0, + "(2, '2024-11-18', 6)": 0, + "(2, '2024-11-18', 7)": 0, + "(2, '2024-11-19', 0)": 0, + "(2, '2024-11-19', 1)": 0, + "(2, '2024-11-19', 2)": 0, + "(2, '2024-11-19', 3)": 0, + "(2, '2024-11-19', 4)": 0, + "(2, '2024-11-19', 5)": 0, + "(2, '2024-11-19', 6)": 0, + "(2, '2024-11-19', 7)": 0, + "(2, '2024-11-20', 0)": 0, + "(2, '2024-11-20', 1)": 0, + "(2, '2024-11-20', 2)": 1, + "(2, '2024-11-20', 3)": 0, + "(2, '2024-11-20', 4)": 0, + "(2, '2024-11-20', 5)": 0, + "(2, '2024-11-20', 6)": 0, + "(2, '2024-11-20', 7)": 0, + "(2, '2024-11-21', 0)": 0, + "(2, '2024-11-21', 1)": 0, + "(2, '2024-11-21', 2)": 0, + "(2, '2024-11-21', 3)": 0, + "(2, '2024-11-21', 4)": 0, + "(2, '2024-11-21', 5)": 0, + "(2, '2024-11-21', 6)": 0, + "(2, '2024-11-21', 7)": 0, + "(2, '2024-11-22', 0)": 0, + "(2, '2024-11-22', 1)": 0, + "(2, '2024-11-22', 2)": 0, + "(2, '2024-11-22', 3)": 0, + "(2, '2024-11-22', 4)": 0, + "(2, '2024-11-22', 5)": 0, + "(2, '2024-11-22', 6)": 0, + "(2, '2024-11-22', 7)": 0, + "(2, '2024-11-23', 0)": 0, + "(2, '2024-11-23', 1)": 0, + "(2, '2024-11-23', 2)": 0, + "(2, '2024-11-23', 3)": 0, + "(2, '2024-11-23', 4)": 0, + "(2, '2024-11-23', 5)": 0, + "(2, '2024-11-23', 6)": 0, + "(2, '2024-11-23', 7)": 0, + "(2, '2024-11-24', 0)": 0, + "(2, '2024-11-24', 1)": 0, + "(2, '2024-11-24', 2)": 0, + "(2, '2024-11-24', 3)": 0, + "(2, '2024-11-24', 4)": 0, + "(2, '2024-11-24', 5)": 0, + "(2, '2024-11-24', 6)": 0, + "(2, '2024-11-24', 7)": 0, + "(2, '2024-11-25', 0)": 0, + "(2, '2024-11-25', 1)": 0, + "(2, '2024-11-25', 2)": 0, + "(2, '2024-11-25', 3)": 0, + "(2, '2024-11-25', 4)": 0, + "(2, '2024-11-25', 5)": 0, + "(2, '2024-11-25', 6)": 0, + "(2, '2024-11-25', 7)": 0, + "(2, '2024-11-26', 0)": 0, + "(2, '2024-11-26', 1)": 0, + "(2, '2024-11-26', 2)": 0, + "(2, '2024-11-26', 3)": 0, + "(2, '2024-11-26', 4)": 0, + "(2, '2024-11-26', 5)": 0, + "(2, '2024-11-26', 6)": 0, + "(2, '2024-11-26', 7)": 0, + "(2, '2024-11-27', 0)": 0, + "(2, '2024-11-27', 1)": 0, + "(2, '2024-11-27', 2)": 0, + "(2, '2024-11-27', 3)": 0, + "(2, '2024-11-27', 4)": 0, + "(2, '2024-11-27', 5)": 0, + "(2, '2024-11-27', 6)": 0, + "(2, '2024-11-27', 7)": 0, + "(2, '2024-11-28', 0)": 0, + "(2, '2024-11-28', 1)": 0, + "(2, '2024-11-28', 2)": 0, + "(2, '2024-11-28', 3)": 0, + "(2, '2024-11-28', 4)": 0, + "(2, '2024-11-28', 5)": 0, + "(2, '2024-11-28', 6)": 0, + "(2, '2024-11-28', 7)": 0, + "(2, '2024-11-29', 0)": 0, + "(2, '2024-11-29', 1)": 0, + "(2, '2024-11-29', 2)": 0, + "(2, '2024-11-29', 3)": 0, + "(2, '2024-11-29', 4)": 0, + "(2, '2024-11-29', 5)": 0, + "(2, '2024-11-29', 6)": 0, + "(2, '2024-11-29', 7)": 0, + "(2, '2024-11-30', 0)": 0, + "(2, '2024-11-30', 1)": 0, + "(2, '2024-11-30', 2)": 0, + "(2, '2024-11-30', 3)": 0, + "(2, '2024-11-30', 4)": 0, + "(2, '2024-11-30', 5)": 0, + "(2, '2024-11-30', 6)": 0, + "(2, '2024-11-30', 7)": 0, + "(2932, '2024-11-01', 0)": 0, + "(2932, '2024-11-01', 1)": 0, + "(2932, '2024-11-01', 2)": 1, + "(2932, '2024-11-01', 3)": 0, + "(2932, '2024-11-01', 4)": 0, + "(2932, '2024-11-01', 5)": 0, + "(2932, '2024-11-01', 6)": 0, + "(2932, '2024-11-01', 7)": 0, + "(2932, '2024-11-02', 0)": 0, + "(2932, '2024-11-02', 1)": 0, + "(2932, '2024-11-02', 2)": 0, + "(2932, '2024-11-02', 3)": 0, + "(2932, '2024-11-02', 4)": 0, + "(2932, '2024-11-02', 5)": 0, + "(2932, '2024-11-02', 6)": 0, + "(2932, '2024-11-02', 7)": 0, + "(2932, '2024-11-03', 0)": 1, + "(2932, '2024-11-03', 1)": 0, + "(2932, '2024-11-03', 2)": 0, + "(2932, '2024-11-03', 3)": 0, + "(2932, '2024-11-03', 4)": 0, + "(2932, '2024-11-03', 5)": 0, + "(2932, '2024-11-03', 6)": 0, + "(2932, '2024-11-03', 7)": 0, + "(2932, '2024-11-04', 0)": 0, + "(2932, '2024-11-04', 1)": 0, + "(2932, '2024-11-04', 2)": 1, + "(2932, '2024-11-04', 3)": 0, + "(2932, '2024-11-04', 4)": 0, + "(2932, '2024-11-04', 5)": 0, + "(2932, '2024-11-04', 6)": 0, + "(2932, '2024-11-04', 7)": 0, + "(2932, '2024-11-05', 0)": 0, + "(2932, '2024-11-05', 1)": 1, + "(2932, '2024-11-05', 2)": 0, + "(2932, '2024-11-05', 3)": 0, + "(2932, '2024-11-05', 4)": 0, + "(2932, '2024-11-05', 5)": 0, + "(2932, '2024-11-05', 6)": 0, + "(2932, '2024-11-05', 7)": 0, + "(2932, '2024-11-06', 0)": 0, + "(2932, '2024-11-06', 1)": 0, + "(2932, '2024-11-06', 2)": 0, + "(2932, '2024-11-06', 3)": 1, + "(2932, '2024-11-06', 4)": 0, + "(2932, '2024-11-06', 5)": 0, + "(2932, '2024-11-06', 6)": 0, + "(2932, '2024-11-06', 7)": 0, + "(2932, '2024-11-07', 0)": 0, + "(2932, '2024-11-07', 1)": 0, + "(2932, '2024-11-07', 2)": 0, + "(2932, '2024-11-07', 3)": 0, + "(2932, '2024-11-07', 4)": 0, + "(2932, '2024-11-07', 5)": 0, + "(2932, '2024-11-07', 6)": 0, + "(2932, '2024-11-07', 7)": 0, + "(2932, '2024-11-08', 0)": 0, + "(2932, '2024-11-08', 1)": 0, + "(2932, '2024-11-08', 2)": 0, + "(2932, '2024-11-08', 3)": 0, + "(2932, '2024-11-08', 4)": 0, + "(2932, '2024-11-08', 5)": 0, + "(2932, '2024-11-08', 6)": 0, + "(2932, '2024-11-08', 7)": 0, + "(2932, '2024-11-09', 0)": 0, + "(2932, '2024-11-09', 1)": 0, + "(2932, '2024-11-09', 2)": 0, + "(2932, '2024-11-09', 3)": 0, + "(2932, '2024-11-09', 4)": 0, + "(2932, '2024-11-09', 5)": 0, + "(2932, '2024-11-09', 6)": 0, + "(2932, '2024-11-09', 7)": 0, + "(2932, '2024-11-10', 0)": 0, + "(2932, '2024-11-10', 1)": 1, + "(2932, '2024-11-10', 2)": 0, + "(2932, '2024-11-10', 3)": 0, + "(2932, '2024-11-10', 4)": 0, + "(2932, '2024-11-10', 5)": 0, + "(2932, '2024-11-10', 6)": 0, + "(2932, '2024-11-10', 7)": 0, + "(2932, '2024-11-11', 0)": 0, + "(2932, '2024-11-11', 1)": 0, + "(2932, '2024-11-11', 2)": 0, + "(2932, '2024-11-11', 3)": 1, + "(2932, '2024-11-11', 4)": 0, + "(2932, '2024-11-11', 5)": 0, + "(2932, '2024-11-11', 6)": 0, + "(2932, '2024-11-11', 7)": 0, + "(2932, '2024-11-12', 0)": 0, + "(2932, '2024-11-12', 1)": 0, + "(2932, '2024-11-12', 2)": 0, + "(2932, '2024-11-12', 3)": 0, + "(2932, '2024-11-12', 4)": 0, + "(2932, '2024-11-12', 5)": 0, + "(2932, '2024-11-12', 6)": 0, + "(2932, '2024-11-12', 7)": 0, + "(2932, '2024-11-13', 0)": 0, + "(2932, '2024-11-13', 1)": 0, + "(2932, '2024-11-13', 2)": 0, + "(2932, '2024-11-13', 3)": 0, + "(2932, '2024-11-13', 4)": 0, + "(2932, '2024-11-13', 5)": 0, + "(2932, '2024-11-13', 6)": 0, + "(2932, '2024-11-13', 7)": 0, + "(2932, '2024-11-14', 0)": 0, + "(2932, '2024-11-14', 1)": 0, + "(2932, '2024-11-14', 2)": 1, + "(2932, '2024-11-14', 3)": 0, + "(2932, '2024-11-14', 4)": 0, + "(2932, '2024-11-14', 5)": 0, + "(2932, '2024-11-14', 6)": 0, + "(2932, '2024-11-14', 7)": 0, + "(2932, '2024-11-15', 0)": 0, + "(2932, '2024-11-15', 1)": 0, + "(2932, '2024-11-15', 2)": 1, + "(2932, '2024-11-15', 3)": 0, + "(2932, '2024-11-15', 4)": 0, + "(2932, '2024-11-15', 5)": 0, + "(2932, '2024-11-15', 6)": 0, + "(2932, '2024-11-15', 7)": 0, + "(2932, '2024-11-16', 0)": 0, + "(2932, '2024-11-16', 1)": 1, + "(2932, '2024-11-16', 2)": 0, + "(2932, '2024-11-16', 3)": 0, + "(2932, '2024-11-16', 4)": 0, + "(2932, '2024-11-16', 5)": 0, + "(2932, '2024-11-16', 6)": 0, + "(2932, '2024-11-16', 7)": 0, + "(2932, '2024-11-17', 0)": 1, + "(2932, '2024-11-17', 1)": 0, + "(2932, '2024-11-17', 2)": 0, + "(2932, '2024-11-17', 3)": 0, + "(2932, '2024-11-17', 4)": 0, + "(2932, '2024-11-17', 5)": 0, + "(2932, '2024-11-17', 6)": 0, + "(2932, '2024-11-17', 7)": 0, + "(2932, '2024-11-18', 0)": 0, + "(2932, '2024-11-18', 1)": 1, + "(2932, '2024-11-18', 2)": 0, + "(2932, '2024-11-18', 3)": 0, + "(2932, '2024-11-18', 4)": 0, + "(2932, '2024-11-18', 5)": 0, + "(2932, '2024-11-18', 6)": 0, + "(2932, '2024-11-18', 7)": 0, + "(2932, '2024-11-19', 0)": 0, + "(2932, '2024-11-19', 1)": 0, + "(2932, '2024-11-19', 2)": 1, + "(2932, '2024-11-19', 3)": 0, + "(2932, '2024-11-19', 4)": 0, + "(2932, '2024-11-19', 5)": 0, + "(2932, '2024-11-19', 6)": 0, + "(2932, '2024-11-19', 7)": 0, + "(2932, '2024-11-20', 0)": 0, + "(2932, '2024-11-20', 1)": 0, + "(2932, '2024-11-20', 2)": 0, + "(2932, '2024-11-20', 3)": 0, + "(2932, '2024-11-20', 4)": 0, + "(2932, '2024-11-20', 5)": 0, + "(2932, '2024-11-20', 6)": 0, + "(2932, '2024-11-20', 7)": 0, + "(2932, '2024-11-21', 0)": 0, + "(2932, '2024-11-21', 1)": 0, + "(2932, '2024-11-21', 2)": 1, + "(2932, '2024-11-21', 3)": 0, + "(2932, '2024-11-21', 4)": 0, + "(2932, '2024-11-21', 5)": 0, + "(2932, '2024-11-21', 6)": 0, + "(2932, '2024-11-21', 7)": 0, + "(2932, '2024-11-22', 0)": 0, + "(2932, '2024-11-22', 1)": 0, + "(2932, '2024-11-22', 2)": 0, + "(2932, '2024-11-22', 3)": 0, + "(2932, '2024-11-22', 4)": 0, + "(2932, '2024-11-22', 5)": 0, + "(2932, '2024-11-22', 6)": 0, + "(2932, '2024-11-22', 7)": 0, + "(2932, '2024-11-23', 0)": 0, + "(2932, '2024-11-23', 1)": 0, + "(2932, '2024-11-23', 2)": 0, + "(2932, '2024-11-23', 3)": 0, + "(2932, '2024-11-23', 4)": 0, + "(2932, '2024-11-23', 5)": 0, + "(2932, '2024-11-23', 6)": 0, + "(2932, '2024-11-23', 7)": 0, + "(2932, '2024-11-24', 0)": 0, + "(2932, '2024-11-24', 1)": 0, + "(2932, '2024-11-24', 2)": 1, + "(2932, '2024-11-24', 3)": 0, + "(2932, '2024-11-24', 4)": 0, + "(2932, '2024-11-24', 5)": 0, + "(2932, '2024-11-24', 6)": 0, + "(2932, '2024-11-24', 7)": 0, + "(2932, '2024-11-25', 0)": 0, + "(2932, '2024-11-25', 1)": 0, + "(2932, '2024-11-25', 2)": 0, + "(2932, '2024-11-25', 3)": 1, + "(2932, '2024-11-25', 4)": 0, + "(2932, '2024-11-25', 5)": 0, + "(2932, '2024-11-25', 6)": 0, + "(2932, '2024-11-25', 7)": 0, + "(2932, '2024-11-26', 0)": 0, + "(2932, '2024-11-26', 1)": 0, + "(2932, '2024-11-26', 2)": 0, + "(2932, '2024-11-26', 3)": 1, + "(2932, '2024-11-26', 4)": 0, + "(2932, '2024-11-26', 5)": 0, + "(2932, '2024-11-26', 6)": 0, + "(2932, '2024-11-26', 7)": 0, + "(2932, '2024-11-27', 0)": 0, + "(2932, '2024-11-27', 1)": 0, + "(2932, '2024-11-27', 2)": 0, + "(2932, '2024-11-27', 3)": 1, + "(2932, '2024-11-27', 4)": 0, + "(2932, '2024-11-27', 5)": 0, + "(2932, '2024-11-27', 6)": 0, + "(2932, '2024-11-27', 7)": 0, + "(2932, '2024-11-28', 0)": 0, + "(2932, '2024-11-28', 1)": 0, + "(2932, '2024-11-28', 2)": 0, + "(2932, '2024-11-28', 3)": 0, + "(2932, '2024-11-28', 4)": 0, + "(2932, '2024-11-28', 5)": 0, + "(2932, '2024-11-28', 6)": 0, + "(2932, '2024-11-28', 7)": 0, + "(2932, '2024-11-29', 0)": 0, + "(2932, '2024-11-29', 1)": 0, + "(2932, '2024-11-29', 2)": 0, + "(2932, '2024-11-29', 3)": 0, + "(2932, '2024-11-29', 4)": 0, + "(2932, '2024-11-29', 5)": 0, + "(2932, '2024-11-29', 6)": 0, + "(2932, '2024-11-29', 7)": 0, + "(2932, '2024-11-30', 0)": 0, + "(2932, '2024-11-30', 1)": 0, + "(2932, '2024-11-30', 2)": 0, + "(2932, '2024-11-30', 3)": 0, + "(2932, '2024-11-30', 4)": 0, + "(2932, '2024-11-30', 5)": 0, + "(2932, '2024-11-30', 6)": 0, + "(2932, '2024-11-30', 7)": 0, + "(2963, '2024-11-01', 0)": 1, + "(2963, '2024-11-01', 1)": 0, + "(2963, '2024-11-01', 2)": 0, + "(2963, '2024-11-01', 3)": 0, + "(2963, '2024-11-01', 4)": 0, + "(2963, '2024-11-01', 5)": 0, + "(2963, '2024-11-01', 6)": 0, + "(2963, '2024-11-01', 7)": 0, + "(2963, '2024-11-02', 0)": 0, + "(2963, '2024-11-02', 1)": 0, + "(2963, '2024-11-02', 2)": 1, + "(2963, '2024-11-02', 3)": 0, + "(2963, '2024-11-02', 4)": 0, + "(2963, '2024-11-02', 5)": 0, + "(2963, '2024-11-02', 6)": 0, + "(2963, '2024-11-02', 7)": 0, + "(2963, '2024-11-03', 0)": 0, + "(2963, '2024-11-03', 1)": 0, + "(2963, '2024-11-03', 2)": 1, + "(2963, '2024-11-03', 3)": 0, + "(2963, '2024-11-03', 4)": 0, + "(2963, '2024-11-03', 5)": 0, + "(2963, '2024-11-03', 6)": 0, + "(2963, '2024-11-03', 7)": 0, + "(2963, '2024-11-04', 0)": 0, + "(2963, '2024-11-04', 1)": 0, + "(2963, '2024-11-04', 2)": 0, + "(2963, '2024-11-04', 3)": 0, + "(2963, '2024-11-04', 4)": 0, + "(2963, '2024-11-04', 5)": 0, + "(2963, '2024-11-04', 6)": 0, + "(2963, '2024-11-04', 7)": 0, + "(2963, '2024-11-05', 0)": 1, + "(2963, '2024-11-05', 1)": 0, + "(2963, '2024-11-05', 2)": 0, + "(2963, '2024-11-05', 3)": 0, + "(2963, '2024-11-05', 4)": 0, + "(2963, '2024-11-05', 5)": 0, + "(2963, '2024-11-05', 6)": 0, + "(2963, '2024-11-05', 7)": 0, + "(2963, '2024-11-06', 0)": 1, + "(2963, '2024-11-06', 1)": 0, + "(2963, '2024-11-06', 2)": 0, + "(2963, '2024-11-06', 3)": 0, + "(2963, '2024-11-06', 4)": 0, + "(2963, '2024-11-06', 5)": 0, + "(2963, '2024-11-06', 6)": 0, + "(2963, '2024-11-06', 7)": 0, + "(2963, '2024-11-07', 0)": 0, + "(2963, '2024-11-07', 1)": 0, + "(2963, '2024-11-07', 2)": 0, + "(2963, '2024-11-07', 3)": 0, + "(2963, '2024-11-07', 4)": 0, + "(2963, '2024-11-07', 5)": 0, + "(2963, '2024-11-07', 6)": 0, + "(2963, '2024-11-07', 7)": 0, + "(2963, '2024-11-08', 0)": 0, + "(2963, '2024-11-08', 1)": 0, + "(2963, '2024-11-08', 2)": 0, + "(2963, '2024-11-08', 3)": 0, + "(2963, '2024-11-08', 4)": 0, + "(2963, '2024-11-08', 5)": 0, + "(2963, '2024-11-08', 6)": 0, + "(2963, '2024-11-08', 7)": 0, + "(2963, '2024-11-09', 0)": 0, + "(2963, '2024-11-09', 1)": 0, + "(2963, '2024-11-09', 2)": 0, + "(2963, '2024-11-09', 3)": 1, + "(2963, '2024-11-09', 4)": 0, + "(2963, '2024-11-09', 5)": 0, + "(2963, '2024-11-09', 6)": 0, + "(2963, '2024-11-09', 7)": 0, + "(2963, '2024-11-10', 0)": 0, + "(2963, '2024-11-10', 1)": 0, + "(2963, '2024-11-10', 2)": 0, + "(2963, '2024-11-10', 3)": 0, + "(2963, '2024-11-10', 4)": 0, + "(2963, '2024-11-10', 5)": 0, + "(2963, '2024-11-10', 6)": 0, + "(2963, '2024-11-10', 7)": 0, + "(2963, '2024-11-11', 0)": 0, + "(2963, '2024-11-11', 1)": 0, + "(2963, '2024-11-11', 2)": 0, + "(2963, '2024-11-11', 3)": 0, + "(2963, '2024-11-11', 4)": 0, + "(2963, '2024-11-11', 5)": 0, + "(2963, '2024-11-11', 6)": 0, + "(2963, '2024-11-11', 7)": 0, + "(2963, '2024-11-12', 0)": 0, + "(2963, '2024-11-12', 1)": 0, + "(2963, '2024-11-12', 2)": 0, + "(2963, '2024-11-12', 3)": 0, + "(2963, '2024-11-12', 4)": 0, + "(2963, '2024-11-12', 5)": 0, + "(2963, '2024-11-12', 6)": 0, + "(2963, '2024-11-12', 7)": 0, + "(2963, '2024-11-13', 0)": 0, + "(2963, '2024-11-13', 1)": 0, + "(2963, '2024-11-13', 2)": 0, + "(2963, '2024-11-13', 3)": 0, + "(2963, '2024-11-13', 4)": 0, + "(2963, '2024-11-13', 5)": 0, + "(2963, '2024-11-13', 6)": 0, + "(2963, '2024-11-13', 7)": 0, + "(2963, '2024-11-14', 0)": 1, + "(2963, '2024-11-14', 1)": 0, + "(2963, '2024-11-14', 2)": 0, + "(2963, '2024-11-14', 3)": 0, + "(2963, '2024-11-14', 4)": 0, + "(2963, '2024-11-14', 5)": 0, + "(2963, '2024-11-14', 6)": 0, + "(2963, '2024-11-14', 7)": 0, + "(2963, '2024-11-15', 0)": 0, + "(2963, '2024-11-15', 1)": 0, + "(2963, '2024-11-15', 2)": 0, + "(2963, '2024-11-15', 3)": 0, + "(2963, '2024-11-15', 4)": 0, + "(2963, '2024-11-15', 5)": 0, + "(2963, '2024-11-15', 6)": 0, + "(2963, '2024-11-15', 7)": 0, + "(2963, '2024-11-16', 0)": 0, + "(2963, '2024-11-16', 1)": 0, + "(2963, '2024-11-16', 2)": 0, + "(2963, '2024-11-16', 3)": 1, + "(2963, '2024-11-16', 4)": 0, + "(2963, '2024-11-16', 5)": 0, + "(2963, '2024-11-16', 6)": 0, + "(2963, '2024-11-16', 7)": 0, + "(2963, '2024-11-17', 0)": 0, + "(2963, '2024-11-17', 1)": 0, + "(2963, '2024-11-17', 2)": 0, + "(2963, '2024-11-17', 3)": 0, + "(2963, '2024-11-17', 4)": 0, + "(2963, '2024-11-17', 5)": 0, + "(2963, '2024-11-17', 6)": 0, + "(2963, '2024-11-17', 7)": 0, + "(2963, '2024-11-18', 0)": 1, + "(2963, '2024-11-18', 1)": 0, + "(2963, '2024-11-18', 2)": 0, + "(2963, '2024-11-18', 3)": 0, + "(2963, '2024-11-18', 4)": 0, + "(2963, '2024-11-18', 5)": 0, + "(2963, '2024-11-18', 6)": 0, + "(2963, '2024-11-18', 7)": 0, + "(2963, '2024-11-19', 0)": 1, + "(2963, '2024-11-19', 1)": 0, + "(2963, '2024-11-19', 2)": 0, + "(2963, '2024-11-19', 3)": 0, + "(2963, '2024-11-19', 4)": 0, + "(2963, '2024-11-19', 5)": 0, + "(2963, '2024-11-19', 6)": 0, + "(2963, '2024-11-19', 7)": 0, + "(2963, '2024-11-20', 0)": 1, + "(2963, '2024-11-20', 1)": 0, + "(2963, '2024-11-20', 2)": 0, + "(2963, '2024-11-20', 3)": 0, + "(2963, '2024-11-20', 4)": 0, + "(2963, '2024-11-20', 5)": 0, + "(2963, '2024-11-20', 6)": 0, + "(2963, '2024-11-20', 7)": 0, + "(2963, '2024-11-21', 0)": 1, + "(2963, '2024-11-21', 1)": 0, + "(2963, '2024-11-21', 2)": 0, + "(2963, '2024-11-21', 3)": 0, + "(2963, '2024-11-21', 4)": 0, + "(2963, '2024-11-21', 5)": 0, + "(2963, '2024-11-21', 6)": 0, + "(2963, '2024-11-21', 7)": 0, + "(2963, '2024-11-22', 0)": 1, + "(2963, '2024-11-22', 1)": 0, + "(2963, '2024-11-22', 2)": 0, + "(2963, '2024-11-22', 3)": 0, + "(2963, '2024-11-22', 4)": 0, + "(2963, '2024-11-22', 5)": 0, + "(2963, '2024-11-22', 6)": 0, + "(2963, '2024-11-22', 7)": 0, + "(2963, '2024-11-23', 0)": 1, + "(2963, '2024-11-23', 1)": 0, + "(2963, '2024-11-23', 2)": 0, + "(2963, '2024-11-23', 3)": 0, + "(2963, '2024-11-23', 4)": 0, + "(2963, '2024-11-23', 5)": 0, + "(2963, '2024-11-23', 6)": 0, + "(2963, '2024-11-23', 7)": 0, + "(2963, '2024-11-24', 0)": 1, + "(2963, '2024-11-24', 1)": 0, + "(2963, '2024-11-24', 2)": 0, + "(2963, '2024-11-24', 3)": 0, + "(2963, '2024-11-24', 4)": 0, + "(2963, '2024-11-24', 5)": 0, + "(2963, '2024-11-24', 6)": 0, + "(2963, '2024-11-24', 7)": 0, + "(2963, '2024-11-25', 0)": 1, + "(2963, '2024-11-25', 1)": 0, + "(2963, '2024-11-25', 2)": 0, + "(2963, '2024-11-25', 3)": 0, + "(2963, '2024-11-25', 4)": 0, + "(2963, '2024-11-25', 5)": 0, + "(2963, '2024-11-25', 6)": 0, + "(2963, '2024-11-25', 7)": 0, + "(2963, '2024-11-26', 0)": 0, + "(2963, '2024-11-26', 1)": 0, + "(2963, '2024-11-26', 2)": 0, + "(2963, '2024-11-26', 3)": 0, + "(2963, '2024-11-26', 4)": 0, + "(2963, '2024-11-26', 5)": 0, + "(2963, '2024-11-26', 6)": 0, + "(2963, '2024-11-26', 7)": 0, + "(2963, '2024-11-27', 0)": 1, + "(2963, '2024-11-27', 1)": 0, + "(2963, '2024-11-27', 2)": 0, + "(2963, '2024-11-27', 3)": 0, + "(2963, '2024-11-27', 4)": 0, + "(2963, '2024-11-27', 5)": 0, + "(2963, '2024-11-27', 6)": 0, + "(2963, '2024-11-27', 7)": 0, + "(2963, '2024-11-28', 0)": 1, + "(2963, '2024-11-28', 1)": 0, + "(2963, '2024-11-28', 2)": 0, + "(2963, '2024-11-28', 3)": 0, + "(2963, '2024-11-28', 4)": 0, + "(2963, '2024-11-28', 5)": 0, + "(2963, '2024-11-28', 6)": 0, + "(2963, '2024-11-28', 7)": 0, + "(2963, '2024-11-29', 0)": 1, + "(2963, '2024-11-29', 1)": 0, + "(2963, '2024-11-29', 2)": 0, + "(2963, '2024-11-29', 3)": 0, + "(2963, '2024-11-29', 4)": 0, + "(2963, '2024-11-29', 5)": 0, + "(2963, '2024-11-29', 6)": 0, + "(2963, '2024-11-29', 7)": 0, + "(2963, '2024-11-30', 0)": 0, + "(2963, '2024-11-30', 1)": 0, + "(2963, '2024-11-30', 2)": 0, + "(2963, '2024-11-30', 3)": 0, + "(2963, '2024-11-30', 4)": 0, + "(2963, '2024-11-30', 5)": 0, + "(2963, '2024-11-30', 6)": 0, + "(2963, '2024-11-30', 7)": 0, + "(3, '2024-11-01', 0)": 0, + "(3, '2024-11-01', 1)": 0, + "(3, '2024-11-01', 2)": 0, + "(3, '2024-11-01', 3)": 0, + "(3, '2024-11-01', 4)": 0, + "(3, '2024-11-01', 5)": 0, + "(3, '2024-11-01', 6)": 0, + "(3, '2024-11-01', 7)": 0, + "(3, '2024-11-02', 0)": 0, + "(3, '2024-11-02', 1)": 0, + "(3, '2024-11-02', 2)": 0, + "(3, '2024-11-02', 3)": 0, + "(3, '2024-11-02', 4)": 0, + "(3, '2024-11-02', 5)": 0, + "(3, '2024-11-02', 6)": 0, + "(3, '2024-11-02', 7)": 0, + "(3, '2024-11-03', 0)": 0, + "(3, '2024-11-03', 1)": 0, + "(3, '2024-11-03', 2)": 0, + "(3, '2024-11-03', 3)": 0, + "(3, '2024-11-03', 4)": 0, + "(3, '2024-11-03', 5)": 0, + "(3, '2024-11-03', 6)": 0, + "(3, '2024-11-03', 7)": 0, + "(3, '2024-11-04', 0)": 0, + "(3, '2024-11-04', 1)": 0, + "(3, '2024-11-04', 2)": 0, + "(3, '2024-11-04', 3)": 0, + "(3, '2024-11-04', 4)": 0, + "(3, '2024-11-04', 5)": 0, + "(3, '2024-11-04', 6)": 0, + "(3, '2024-11-04', 7)": 0, + "(3, '2024-11-05', 0)": 0, + "(3, '2024-11-05', 1)": 0, + "(3, '2024-11-05', 2)": 0, + "(3, '2024-11-05', 3)": 0, + "(3, '2024-11-05', 4)": 0, + "(3, '2024-11-05', 5)": 0, + "(3, '2024-11-05', 6)": 0, + "(3, '2024-11-05', 7)": 0, + "(3, '2024-11-06', 0)": 0, + "(3, '2024-11-06', 1)": 0, + "(3, '2024-11-06', 2)": 0, + "(3, '2024-11-06', 3)": 0, + "(3, '2024-11-06', 4)": 0, + "(3, '2024-11-06', 5)": 0, + "(3, '2024-11-06', 6)": 0, + "(3, '2024-11-06', 7)": 0, + "(3, '2024-11-07', 0)": 0, + "(3, '2024-11-07', 1)": 0, + "(3, '2024-11-07', 2)": 0, + "(3, '2024-11-07', 3)": 0, + "(3, '2024-11-07', 4)": 0, + "(3, '2024-11-07', 5)": 0, + "(3, '2024-11-07', 6)": 0, + "(3, '2024-11-07', 7)": 0, + "(3, '2024-11-08', 0)": 0, + "(3, '2024-11-08', 1)": 0, + "(3, '2024-11-08', 2)": 0, + "(3, '2024-11-08', 3)": 0, + "(3, '2024-11-08', 4)": 0, + "(3, '2024-11-08', 5)": 0, + "(3, '2024-11-08', 6)": 0, + "(3, '2024-11-08', 7)": 0, + "(3, '2024-11-09', 0)": 0, + "(3, '2024-11-09', 1)": 0, + "(3, '2024-11-09', 2)": 0, + "(3, '2024-11-09', 3)": 0, + "(3, '2024-11-09', 4)": 0, + "(3, '2024-11-09', 5)": 0, + "(3, '2024-11-09', 6)": 0, + "(3, '2024-11-09', 7)": 0, + "(3, '2024-11-10', 0)": 0, + "(3, '2024-11-10', 1)": 0, + "(3, '2024-11-10', 2)": 0, + "(3, '2024-11-10', 3)": 0, + "(3, '2024-11-10', 4)": 0, + "(3, '2024-11-10', 5)": 0, + "(3, '2024-11-10', 6)": 0, + "(3, '2024-11-10', 7)": 0, + "(3, '2024-11-11', 0)": 0, + "(3, '2024-11-11', 1)": 0, + "(3, '2024-11-11', 2)": 0, + "(3, '2024-11-11', 3)": 0, + "(3, '2024-11-11', 4)": 0, + "(3, '2024-11-11', 5)": 0, + "(3, '2024-11-11', 6)": 0, + "(3, '2024-11-11', 7)": 0, + "(3, '2024-11-12', 0)": 0, + "(3, '2024-11-12', 1)": 0, + "(3, '2024-11-12', 2)": 0, + "(3, '2024-11-12', 3)": 0, + "(3, '2024-11-12', 4)": 0, + "(3, '2024-11-12', 5)": 0, + "(3, '2024-11-12', 6)": 0, + "(3, '2024-11-12', 7)": 0, + "(3, '2024-11-13', 0)": 0, + "(3, '2024-11-13', 1)": 0, + "(3, '2024-11-13', 2)": 0, + "(3, '2024-11-13', 3)": 0, + "(3, '2024-11-13', 4)": 0, + "(3, '2024-11-13', 5)": 0, + "(3, '2024-11-13', 6)": 0, + "(3, '2024-11-13', 7)": 0, + "(3, '2024-11-14', 0)": 0, + "(3, '2024-11-14', 1)": 0, + "(3, '2024-11-14', 2)": 0, + "(3, '2024-11-14', 3)": 0, + "(3, '2024-11-14', 4)": 0, + "(3, '2024-11-14', 5)": 0, + "(3, '2024-11-14', 6)": 0, + "(3, '2024-11-14', 7)": 0, + "(3, '2024-11-15', 0)": 0, + "(3, '2024-11-15', 1)": 0, + "(3, '2024-11-15', 2)": 0, + "(3, '2024-11-15', 3)": 0, + "(3, '2024-11-15', 4)": 0, + "(3, '2024-11-15', 5)": 0, + "(3, '2024-11-15', 6)": 0, + "(3, '2024-11-15', 7)": 0, + "(3, '2024-11-16', 0)": 0, + "(3, '2024-11-16', 1)": 0, + "(3, '2024-11-16', 2)": 0, + "(3, '2024-11-16', 3)": 0, + "(3, '2024-11-16', 4)": 0, + "(3, '2024-11-16', 5)": 0, + "(3, '2024-11-16', 6)": 0, + "(3, '2024-11-16', 7)": 0, + "(3, '2024-11-17', 0)": 0, + "(3, '2024-11-17', 1)": 0, + "(3, '2024-11-17', 2)": 0, + "(3, '2024-11-17', 3)": 0, + "(3, '2024-11-17', 4)": 0, + "(3, '2024-11-17', 5)": 0, + "(3, '2024-11-17', 6)": 0, + "(3, '2024-11-17', 7)": 0, + "(3, '2024-11-18', 0)": 0, + "(3, '2024-11-18', 1)": 0, + "(3, '2024-11-18', 2)": 0, + "(3, '2024-11-18', 3)": 0, + "(3, '2024-11-18', 4)": 0, + "(3, '2024-11-18', 5)": 0, + "(3, '2024-11-18', 6)": 0, + "(3, '2024-11-18', 7)": 0, + "(3, '2024-11-19', 0)": 0, + "(3, '2024-11-19', 1)": 0, + "(3, '2024-11-19', 2)": 0, + "(3, '2024-11-19', 3)": 0, + "(3, '2024-11-19', 4)": 0, + "(3, '2024-11-19', 5)": 0, + "(3, '2024-11-19', 6)": 0, + "(3, '2024-11-19', 7)": 0, + "(3, '2024-11-20', 0)": 0, + "(3, '2024-11-20', 1)": 0, + "(3, '2024-11-20', 2)": 0, + "(3, '2024-11-20', 3)": 0, + "(3, '2024-11-20', 4)": 0, + "(3, '2024-11-20', 5)": 0, + "(3, '2024-11-20', 6)": 0, + "(3, '2024-11-20', 7)": 0, + "(3, '2024-11-21', 0)": 0, + "(3, '2024-11-21', 1)": 0, + "(3, '2024-11-21', 2)": 0, + "(3, '2024-11-21', 3)": 0, + "(3, '2024-11-21', 4)": 0, + "(3, '2024-11-21', 5)": 0, + "(3, '2024-11-21', 6)": 0, + "(3, '2024-11-21', 7)": 0, + "(3, '2024-11-22', 0)": 0, + "(3, '2024-11-22', 1)": 0, + "(3, '2024-11-22', 2)": 0, + "(3, '2024-11-22', 3)": 0, + "(3, '2024-11-22', 4)": 0, + "(3, '2024-11-22', 5)": 0, + "(3, '2024-11-22', 6)": 0, + "(3, '2024-11-22', 7)": 0, + "(3, '2024-11-23', 0)": 0, + "(3, '2024-11-23', 1)": 0, + "(3, '2024-11-23', 2)": 0, + "(3, '2024-11-23', 3)": 0, + "(3, '2024-11-23', 4)": 0, + "(3, '2024-11-23', 5)": 0, + "(3, '2024-11-23', 6)": 0, + "(3, '2024-11-23', 7)": 0, + "(3, '2024-11-24', 0)": 0, + "(3, '2024-11-24', 1)": 0, + "(3, '2024-11-24', 2)": 0, + "(3, '2024-11-24', 3)": 0, + "(3, '2024-11-24', 4)": 0, + "(3, '2024-11-24', 5)": 0, + "(3, '2024-11-24', 6)": 0, + "(3, '2024-11-24', 7)": 0, + "(3, '2024-11-25', 0)": 0, + "(3, '2024-11-25', 1)": 0, + "(3, '2024-11-25', 2)": 0, + "(3, '2024-11-25', 3)": 0, + "(3, '2024-11-25', 4)": 0, + "(3, '2024-11-25', 5)": 0, + "(3, '2024-11-25', 6)": 0, + "(3, '2024-11-25', 7)": 0, + "(3, '2024-11-26', 0)": 0, + "(3, '2024-11-26', 1)": 0, + "(3, '2024-11-26', 2)": 0, + "(3, '2024-11-26', 3)": 0, + "(3, '2024-11-26', 4)": 0, + "(3, '2024-11-26', 5)": 0, + "(3, '2024-11-26', 6)": 0, + "(3, '2024-11-26', 7)": 0, + "(3, '2024-11-27', 0)": 0, + "(3, '2024-11-27', 1)": 0, + "(3, '2024-11-27', 2)": 0, + "(3, '2024-11-27', 3)": 0, + "(3, '2024-11-27', 4)": 0, + "(3, '2024-11-27', 5)": 0, + "(3, '2024-11-27', 6)": 0, + "(3, '2024-11-27', 7)": 0, + "(3, '2024-11-28', 0)": 0, + "(3, '2024-11-28', 1)": 0, + "(3, '2024-11-28', 2)": 0, + "(3, '2024-11-28', 3)": 0, + "(3, '2024-11-28', 4)": 0, + "(3, '2024-11-28', 5)": 0, + "(3, '2024-11-28', 6)": 0, + "(3, '2024-11-28', 7)": 0, + "(3, '2024-11-29', 0)": 0, + "(3, '2024-11-29', 1)": 0, + "(3, '2024-11-29', 2)": 0, + "(3, '2024-11-29', 3)": 0, + "(3, '2024-11-29', 4)": 0, + "(3, '2024-11-29', 5)": 0, + "(3, '2024-11-29', 6)": 0, + "(3, '2024-11-29', 7)": 0, + "(3, '2024-11-30', 0)": 0, + "(3, '2024-11-30', 1)": 0, + "(3, '2024-11-30', 2)": 0, + "(3, '2024-11-30', 3)": 0, + "(3, '2024-11-30', 4)": 0, + "(3, '2024-11-30', 5)": 0, + "(3, '2024-11-30', 6)": 0, + "(3, '2024-11-30', 7)": 0, + "(3566, '2024-11-01', 0)": 0, + "(3566, '2024-11-01', 1)": 0, + "(3566, '2024-11-01', 2)": 0, + "(3566, '2024-11-01', 3)": 0, + "(3566, '2024-11-01', 4)": 0, + "(3566, '2024-11-01', 5)": 0, + "(3566, '2024-11-01', 6)": 0, + "(3566, '2024-11-01', 7)": 0, + "(3566, '2024-11-02', 0)": 0, + "(3566, '2024-11-02', 1)": 0, + "(3566, '2024-11-02', 2)": 0, + "(3566, '2024-11-02', 3)": 0, + "(3566, '2024-11-02', 4)": 0, + "(3566, '2024-11-02', 5)": 0, + "(3566, '2024-11-02', 6)": 0, + "(3566, '2024-11-02', 7)": 0, + "(3566, '2024-11-03', 0)": 0, + "(3566, '2024-11-03', 1)": 0, + "(3566, '2024-11-03', 2)": 0, + "(3566, '2024-11-03', 3)": 0, + "(3566, '2024-11-03', 4)": 0, + "(3566, '2024-11-03', 5)": 0, + "(3566, '2024-11-03', 6)": 0, + "(3566, '2024-11-03', 7)": 0, + "(3566, '2024-11-04', 0)": 0, + "(3566, '2024-11-04', 1)": 0, + "(3566, '2024-11-04', 2)": 0, + "(3566, '2024-11-04', 3)": 0, + "(3566, '2024-11-04', 4)": 0, + "(3566, '2024-11-04', 5)": 0, + "(3566, '2024-11-04', 6)": 0, + "(3566, '2024-11-04', 7)": 0, + "(3566, '2024-11-05', 0)": 0, + "(3566, '2024-11-05', 1)": 0, + "(3566, '2024-11-05', 2)": 0, + "(3566, '2024-11-05', 3)": 0, + "(3566, '2024-11-05', 4)": 0, + "(3566, '2024-11-05', 5)": 0, + "(3566, '2024-11-05', 6)": 0, + "(3566, '2024-11-05', 7)": 0, + "(3566, '2024-11-06', 0)": 0, + "(3566, '2024-11-06', 1)": 0, + "(3566, '2024-11-06', 2)": 0, + "(3566, '2024-11-06', 3)": 0, + "(3566, '2024-11-06', 4)": 0, + "(3566, '2024-11-06', 5)": 0, + "(3566, '2024-11-06', 6)": 0, + "(3566, '2024-11-06', 7)": 0, + "(3566, '2024-11-07', 0)": 0, + "(3566, '2024-11-07', 1)": 0, + "(3566, '2024-11-07', 2)": 0, + "(3566, '2024-11-07', 3)": 0, + "(3566, '2024-11-07', 4)": 0, + "(3566, '2024-11-07', 5)": 0, + "(3566, '2024-11-07', 6)": 0, + "(3566, '2024-11-07', 7)": 0, + "(3566, '2024-11-08', 0)": 0, + "(3566, '2024-11-08', 1)": 0, + "(3566, '2024-11-08', 2)": 0, + "(3566, '2024-11-08', 3)": 0, + "(3566, '2024-11-08', 4)": 0, + "(3566, '2024-11-08', 5)": 0, + "(3566, '2024-11-08', 6)": 0, + "(3566, '2024-11-08', 7)": 0, + "(3566, '2024-11-09', 0)": 0, + "(3566, '2024-11-09', 1)": 0, + "(3566, '2024-11-09', 2)": 0, + "(3566, '2024-11-09', 3)": 0, + "(3566, '2024-11-09', 4)": 0, + "(3566, '2024-11-09', 5)": 0, + "(3566, '2024-11-09', 6)": 0, + "(3566, '2024-11-09', 7)": 0, + "(3566, '2024-11-10', 0)": 0, + "(3566, '2024-11-10', 1)": 0, + "(3566, '2024-11-10', 2)": 0, + "(3566, '2024-11-10', 3)": 0, + "(3566, '2024-11-10', 4)": 0, + "(3566, '2024-11-10', 5)": 0, + "(3566, '2024-11-10', 6)": 0, + "(3566, '2024-11-10', 7)": 0, + "(3566, '2024-11-11', 0)": 0, + "(3566, '2024-11-11', 1)": 0, + "(3566, '2024-11-11', 2)": 0, + "(3566, '2024-11-11', 3)": 0, + "(3566, '2024-11-11', 4)": 0, + "(3566, '2024-11-11', 5)": 0, + "(3566, '2024-11-11', 6)": 0, + "(3566, '2024-11-11', 7)": 0, + "(3566, '2024-11-12', 0)": 0, + "(3566, '2024-11-12', 1)": 0, + "(3566, '2024-11-12', 2)": 0, + "(3566, '2024-11-12', 3)": 0, + "(3566, '2024-11-12', 4)": 0, + "(3566, '2024-11-12', 5)": 0, + "(3566, '2024-11-12', 6)": 0, + "(3566, '2024-11-12', 7)": 0, + "(3566, '2024-11-13', 0)": 0, + "(3566, '2024-11-13', 1)": 0, + "(3566, '2024-11-13', 2)": 0, + "(3566, '2024-11-13', 3)": 0, + "(3566, '2024-11-13', 4)": 0, + "(3566, '2024-11-13', 5)": 0, + "(3566, '2024-11-13', 6)": 0, + "(3566, '2024-11-13', 7)": 0, + "(3566, '2024-11-14', 0)": 0, + "(3566, '2024-11-14', 1)": 0, + "(3566, '2024-11-14', 2)": 0, + "(3566, '2024-11-14', 3)": 0, + "(3566, '2024-11-14', 4)": 0, + "(3566, '2024-11-14', 5)": 0, + "(3566, '2024-11-14', 6)": 0, + "(3566, '2024-11-14', 7)": 0, + "(3566, '2024-11-15', 0)": 0, + "(3566, '2024-11-15', 1)": 0, + "(3566, '2024-11-15', 2)": 0, + "(3566, '2024-11-15', 3)": 0, + "(3566, '2024-11-15', 4)": 0, + "(3566, '2024-11-15', 5)": 0, + "(3566, '2024-11-15', 6)": 0, + "(3566, '2024-11-15', 7)": 0, + "(3566, '2024-11-16', 0)": 0, + "(3566, '2024-11-16', 1)": 0, + "(3566, '2024-11-16', 2)": 0, + "(3566, '2024-11-16', 3)": 0, + "(3566, '2024-11-16', 4)": 0, + "(3566, '2024-11-16', 5)": 0, + "(3566, '2024-11-16', 6)": 0, + "(3566, '2024-11-16', 7)": 0, + "(3566, '2024-11-17', 0)": 0, + "(3566, '2024-11-17', 1)": 0, + "(3566, '2024-11-17', 2)": 0, + "(3566, '2024-11-17', 3)": 0, + "(3566, '2024-11-17', 4)": 0, + "(3566, '2024-11-17', 5)": 0, + "(3566, '2024-11-17', 6)": 0, + "(3566, '2024-11-17', 7)": 0, + "(3566, '2024-11-18', 0)": 0, + "(3566, '2024-11-18', 1)": 0, + "(3566, '2024-11-18', 2)": 0, + "(3566, '2024-11-18', 3)": 0, + "(3566, '2024-11-18', 4)": 0, + "(3566, '2024-11-18', 5)": 0, + "(3566, '2024-11-18', 6)": 0, + "(3566, '2024-11-18', 7)": 0, + "(3566, '2024-11-19', 0)": 0, + "(3566, '2024-11-19', 1)": 0, + "(3566, '2024-11-19', 2)": 0, + "(3566, '2024-11-19', 3)": 0, + "(3566, '2024-11-19', 4)": 0, + "(3566, '2024-11-19', 5)": 0, + "(3566, '2024-11-19', 6)": 0, + "(3566, '2024-11-19', 7)": 0, + "(3566, '2024-11-20', 0)": 0, + "(3566, '2024-11-20', 1)": 0, + "(3566, '2024-11-20', 2)": 0, + "(3566, '2024-11-20', 3)": 0, + "(3566, '2024-11-20', 4)": 0, + "(3566, '2024-11-20', 5)": 0, + "(3566, '2024-11-20', 6)": 0, + "(3566, '2024-11-20', 7)": 0, + "(3566, '2024-11-21', 0)": 0, + "(3566, '2024-11-21', 1)": 0, + "(3566, '2024-11-21', 2)": 0, + "(3566, '2024-11-21', 3)": 0, + "(3566, '2024-11-21', 4)": 0, + "(3566, '2024-11-21', 5)": 0, + "(3566, '2024-11-21', 6)": 0, + "(3566, '2024-11-21', 7)": 0, + "(3566, '2024-11-22', 0)": 0, + "(3566, '2024-11-22', 1)": 0, + "(3566, '2024-11-22', 2)": 0, + "(3566, '2024-11-22', 3)": 0, + "(3566, '2024-11-22', 4)": 0, + "(3566, '2024-11-22', 5)": 0, + "(3566, '2024-11-22', 6)": 0, + "(3566, '2024-11-22', 7)": 0, + "(3566, '2024-11-23', 0)": 0, + "(3566, '2024-11-23', 1)": 0, + "(3566, '2024-11-23', 2)": 0, + "(3566, '2024-11-23', 3)": 0, + "(3566, '2024-11-23', 4)": 0, + "(3566, '2024-11-23', 5)": 0, + "(3566, '2024-11-23', 6)": 0, + "(3566, '2024-11-23', 7)": 0, + "(3566, '2024-11-24', 0)": 0, + "(3566, '2024-11-24', 1)": 0, + "(3566, '2024-11-24', 2)": 0, + "(3566, '2024-11-24', 3)": 0, + "(3566, '2024-11-24', 4)": 0, + "(3566, '2024-11-24', 5)": 0, + "(3566, '2024-11-24', 6)": 0, + "(3566, '2024-11-24', 7)": 0, + "(3566, '2024-11-25', 0)": 0, + "(3566, '2024-11-25', 1)": 0, + "(3566, '2024-11-25', 2)": 0, + "(3566, '2024-11-25', 3)": 0, + "(3566, '2024-11-25', 4)": 0, + "(3566, '2024-11-25', 5)": 0, + "(3566, '2024-11-25', 6)": 0, + "(3566, '2024-11-25', 7)": 0, + "(3566, '2024-11-26', 0)": 0, + "(3566, '2024-11-26', 1)": 0, + "(3566, '2024-11-26', 2)": 0, + "(3566, '2024-11-26', 3)": 0, + "(3566, '2024-11-26', 4)": 0, + "(3566, '2024-11-26', 5)": 0, + "(3566, '2024-11-26', 6)": 0, + "(3566, '2024-11-26', 7)": 0, + "(3566, '2024-11-27', 0)": 0, + "(3566, '2024-11-27', 1)": 0, + "(3566, '2024-11-27', 2)": 0, + "(3566, '2024-11-27', 3)": 0, + "(3566, '2024-11-27', 4)": 0, + "(3566, '2024-11-27', 5)": 0, + "(3566, '2024-11-27', 6)": 0, + "(3566, '2024-11-27', 7)": 0, + "(3566, '2024-11-28', 0)": 0, + "(3566, '2024-11-28', 1)": 0, + "(3566, '2024-11-28', 2)": 0, + "(3566, '2024-11-28', 3)": 0, + "(3566, '2024-11-28', 4)": 0, + "(3566, '2024-11-28', 5)": 0, + "(3566, '2024-11-28', 6)": 0, + "(3566, '2024-11-28', 7)": 0, + "(3566, '2024-11-29', 0)": 0, + "(3566, '2024-11-29', 1)": 0, + "(3566, '2024-11-29', 2)": 0, + "(3566, '2024-11-29', 3)": 0, + "(3566, '2024-11-29', 4)": 0, + "(3566, '2024-11-29', 5)": 0, + "(3566, '2024-11-29', 6)": 0, + "(3566, '2024-11-29', 7)": 0, + "(3566, '2024-11-30', 0)": 0, + "(3566, '2024-11-30', 1)": 0, + "(3566, '2024-11-30', 2)": 0, + "(3566, '2024-11-30', 3)": 0, + "(3566, '2024-11-30', 4)": 0, + "(3566, '2024-11-30', 5)": 0, + "(3566, '2024-11-30', 6)": 0, + "(3566, '2024-11-30', 7)": 0, + "(3868, '2024-11-01', 0)": 0, + "(3868, '2024-11-01', 1)": 0, + "(3868, '2024-11-01', 2)": 0, + "(3868, '2024-11-01', 3)": 0, + "(3868, '2024-11-01', 4)": 0, + "(3868, '2024-11-01', 5)": 0, + "(3868, '2024-11-01', 6)": 0, + "(3868, '2024-11-01', 7)": 0, + "(3868, '2024-11-02', 0)": 0, + "(3868, '2024-11-02', 1)": 0, + "(3868, '2024-11-02', 2)": 0, + "(3868, '2024-11-02', 3)": 0, + "(3868, '2024-11-02', 4)": 0, + "(3868, '2024-11-02', 5)": 0, + "(3868, '2024-11-02', 6)": 0, + "(3868, '2024-11-02', 7)": 0, + "(3868, '2024-11-03', 0)": 0, + "(3868, '2024-11-03', 1)": 0, + "(3868, '2024-11-03', 2)": 0, + "(3868, '2024-11-03', 3)": 0, + "(3868, '2024-11-03', 4)": 0, + "(3868, '2024-11-03', 5)": 0, + "(3868, '2024-11-03', 6)": 0, + "(3868, '2024-11-03', 7)": 0, + "(3868, '2024-11-04', 0)": 1, + "(3868, '2024-11-04', 1)": 0, + "(3868, '2024-11-04', 2)": 0, + "(3868, '2024-11-04', 3)": 0, + "(3868, '2024-11-04', 4)": 0, + "(3868, '2024-11-04', 5)": 0, + "(3868, '2024-11-04', 6)": 0, + "(3868, '2024-11-04', 7)": 0, + "(3868, '2024-11-05', 0)": 0, + "(3868, '2024-11-05', 1)": 0, + "(3868, '2024-11-05', 2)": 1, + "(3868, '2024-11-05', 3)": 0, + "(3868, '2024-11-05', 4)": 0, + "(3868, '2024-11-05', 5)": 0, + "(3868, '2024-11-05', 6)": 0, + "(3868, '2024-11-05', 7)": 0, + "(3868, '2024-11-06', 0)": 0, + "(3868, '2024-11-06', 1)": 0, + "(3868, '2024-11-06', 2)": 0, + "(3868, '2024-11-06', 3)": 0, + "(3868, '2024-11-06', 4)": 0, + "(3868, '2024-11-06', 5)": 0, + "(3868, '2024-11-06', 6)": 0, + "(3868, '2024-11-06', 7)": 0, + "(3868, '2024-11-07', 0)": 0, + "(3868, '2024-11-07', 1)": 0, + "(3868, '2024-11-07', 2)": 0, + "(3868, '2024-11-07', 3)": 0, + "(3868, '2024-11-07', 4)": 0, + "(3868, '2024-11-07', 5)": 0, + "(3868, '2024-11-07', 6)": 0, + "(3868, '2024-11-07', 7)": 0, + "(3868, '2024-11-08', 0)": 1, + "(3868, '2024-11-08', 1)": 0, + "(3868, '2024-11-08', 2)": 0, + "(3868, '2024-11-08', 3)": 0, + "(3868, '2024-11-08', 4)": 0, + "(3868, '2024-11-08', 5)": 0, + "(3868, '2024-11-08', 6)": 0, + "(3868, '2024-11-08', 7)": 0, + "(3868, '2024-11-09', 0)": 0, + "(3868, '2024-11-09', 1)": 0, + "(3868, '2024-11-09', 2)": 0, + "(3868, '2024-11-09', 3)": 0, + "(3868, '2024-11-09', 4)": 0, + "(3868, '2024-11-09', 5)": 0, + "(3868, '2024-11-09', 6)": 0, + "(3868, '2024-11-09', 7)": 0, + "(3868, '2024-11-10', 0)": 1, + "(3868, '2024-11-10', 1)": 0, + "(3868, '2024-11-10', 2)": 0, + "(3868, '2024-11-10', 3)": 0, + "(3868, '2024-11-10', 4)": 0, + "(3868, '2024-11-10', 5)": 0, + "(3868, '2024-11-10', 6)": 0, + "(3868, '2024-11-10', 7)": 0, + "(3868, '2024-11-11', 0)": 1, + "(3868, '2024-11-11', 1)": 0, + "(3868, '2024-11-11', 2)": 0, + "(3868, '2024-11-11', 3)": 0, + "(3868, '2024-11-11', 4)": 0, + "(3868, '2024-11-11', 5)": 0, + "(3868, '2024-11-11', 6)": 0, + "(3868, '2024-11-11', 7)": 0, + "(3868, '2024-11-12', 0)": 0, + "(3868, '2024-11-12', 1)": 0, + "(3868, '2024-11-12', 2)": 1, + "(3868, '2024-11-12', 3)": 0, + "(3868, '2024-11-12', 4)": 0, + "(3868, '2024-11-12', 5)": 0, + "(3868, '2024-11-12', 6)": 0, + "(3868, '2024-11-12', 7)": 0, + "(3868, '2024-11-13', 0)": 0, + "(3868, '2024-11-13', 1)": 0, + "(3868, '2024-11-13', 2)": 0, + "(3868, '2024-11-13', 3)": 1, + "(3868, '2024-11-13', 4)": 0, + "(3868, '2024-11-13', 5)": 0, + "(3868, '2024-11-13', 6)": 0, + "(3868, '2024-11-13', 7)": 0, + "(3868, '2024-11-14', 0)": 0, + "(3868, '2024-11-14', 1)": 0, + "(3868, '2024-11-14', 2)": 0, + "(3868, '2024-11-14', 3)": 0, + "(3868, '2024-11-14', 4)": 0, + "(3868, '2024-11-14', 5)": 0, + "(3868, '2024-11-14', 6)": 0, + "(3868, '2024-11-14', 7)": 0, + "(3868, '2024-11-15', 0)": 0, + "(3868, '2024-11-15', 1)": 0, + "(3868, '2024-11-15', 2)": 0, + "(3868, '2024-11-15', 3)": 1, + "(3868, '2024-11-15', 4)": 0, + "(3868, '2024-11-15', 5)": 0, + "(3868, '2024-11-15', 6)": 0, + "(3868, '2024-11-15', 7)": 0, + "(3868, '2024-11-16', 0)": 0, + "(3868, '2024-11-16', 1)": 0, + "(3868, '2024-11-16', 2)": 0, + "(3868, '2024-11-16', 3)": 0, + "(3868, '2024-11-16', 4)": 0, + "(3868, '2024-11-16', 5)": 0, + "(3868, '2024-11-16', 6)": 0, + "(3868, '2024-11-16', 7)": 0, + "(3868, '2024-11-17', 0)": 0, + "(3868, '2024-11-17', 1)": 0, + "(3868, '2024-11-17', 2)": 0, + "(3868, '2024-11-17', 3)": 1, + "(3868, '2024-11-17', 4)": 0, + "(3868, '2024-11-17', 5)": 0, + "(3868, '2024-11-17', 6)": 0, + "(3868, '2024-11-17', 7)": 0, + "(3868, '2024-11-18', 0)": 0, + "(3868, '2024-11-18', 1)": 0, + "(3868, '2024-11-18', 2)": 0, + "(3868, '2024-11-18', 3)": 1, + "(3868, '2024-11-18', 4)": 0, + "(3868, '2024-11-18', 5)": 0, + "(3868, '2024-11-18', 6)": 0, + "(3868, '2024-11-18', 7)": 0, + "(3868, '2024-11-19', 0)": 0, + "(3868, '2024-11-19', 1)": 0, + "(3868, '2024-11-19', 2)": 0, + "(3868, '2024-11-19', 3)": 0, + "(3868, '2024-11-19', 4)": 0, + "(3868, '2024-11-19', 5)": 0, + "(3868, '2024-11-19', 6)": 0, + "(3868, '2024-11-19', 7)": 0, + "(3868, '2024-11-20', 0)": 0, + "(3868, '2024-11-20', 1)": 0, + "(3868, '2024-11-20', 2)": 0, + "(3868, '2024-11-20', 3)": 0, + "(3868, '2024-11-20', 4)": 0, + "(3868, '2024-11-20', 5)": 0, + "(3868, '2024-11-20', 6)": 0, + "(3868, '2024-11-20', 7)": 0, + "(3868, '2024-11-21', 0)": 1, + "(3868, '2024-11-21', 1)": 0, + "(3868, '2024-11-21', 2)": 0, + "(3868, '2024-11-21', 3)": 0, + "(3868, '2024-11-21', 4)": 0, + "(3868, '2024-11-21', 5)": 0, + "(3868, '2024-11-21', 6)": 0, + "(3868, '2024-11-21', 7)": 0, + "(3868, '2024-11-22', 0)": 0, + "(3868, '2024-11-22', 1)": 1, + "(3868, '2024-11-22', 2)": 0, + "(3868, '2024-11-22', 3)": 0, + "(3868, '2024-11-22', 4)": 0, + "(3868, '2024-11-22', 5)": 0, + "(3868, '2024-11-22', 6)": 0, + "(3868, '2024-11-22', 7)": 0, + "(3868, '2024-11-23', 0)": 1, + "(3868, '2024-11-23', 1)": 0, + "(3868, '2024-11-23', 2)": 0, + "(3868, '2024-11-23', 3)": 0, + "(3868, '2024-11-23', 4)": 0, + "(3868, '2024-11-23', 5)": 0, + "(3868, '2024-11-23', 6)": 0, + "(3868, '2024-11-23', 7)": 0, + "(3868, '2024-11-24', 0)": 0, + "(3868, '2024-11-24', 1)": 1, + "(3868, '2024-11-24', 2)": 0, + "(3868, '2024-11-24', 3)": 0, + "(3868, '2024-11-24', 4)": 0, + "(3868, '2024-11-24', 5)": 0, + "(3868, '2024-11-24', 6)": 0, + "(3868, '2024-11-24', 7)": 0, + "(3868, '2024-11-25', 0)": 0, + "(3868, '2024-11-25', 1)": 0, + "(3868, '2024-11-25', 2)": 0, + "(3868, '2024-11-25', 3)": 0, + "(3868, '2024-11-25', 4)": 0, + "(3868, '2024-11-25', 5)": 0, + "(3868, '2024-11-25', 6)": 1, + "(3868, '2024-11-25', 7)": 0, + "(3868, '2024-11-26', 0)": 0, + "(3868, '2024-11-26', 1)": 0, + "(3868, '2024-11-26', 2)": 0, + "(3868, '2024-11-26', 3)": 0, + "(3868, '2024-11-26', 4)": 0, + "(3868, '2024-11-26', 5)": 0, + "(3868, '2024-11-26', 6)": 0, + "(3868, '2024-11-26', 7)": 0, + "(3868, '2024-11-27', 0)": 0, + "(3868, '2024-11-27', 1)": 0, + "(3868, '2024-11-27', 2)": 1, + "(3868, '2024-11-27', 3)": 0, + "(3868, '2024-11-27', 4)": 0, + "(3868, '2024-11-27', 5)": 0, + "(3868, '2024-11-27', 6)": 0, + "(3868, '2024-11-27', 7)": 0, + "(3868, '2024-11-28', 0)": 0, + "(3868, '2024-11-28', 1)": 0, + "(3868, '2024-11-28', 2)": 0, + "(3868, '2024-11-28', 3)": 1, + "(3868, '2024-11-28', 4)": 0, + "(3868, '2024-11-28', 5)": 0, + "(3868, '2024-11-28', 6)": 0, + "(3868, '2024-11-28', 7)": 0, + "(3868, '2024-11-29', 0)": 0, + "(3868, '2024-11-29', 1)": 0, + "(3868, '2024-11-29', 2)": 0, + "(3868, '2024-11-29', 3)": 1, + "(3868, '2024-11-29', 4)": 0, + "(3868, '2024-11-29', 5)": 0, + "(3868, '2024-11-29', 6)": 0, + "(3868, '2024-11-29', 7)": 0, + "(3868, '2024-11-30', 0)": 0, + "(3868, '2024-11-30', 1)": 0, + "(3868, '2024-11-30', 2)": 0, + "(3868, '2024-11-30', 3)": 0, + "(3868, '2024-11-30', 4)": 0, + "(3868, '2024-11-30', 5)": 0, + "(3868, '2024-11-30', 6)": 0, + "(3868, '2024-11-30', 7)": 0, + "(4, '2024-11-01', 0)": 0, + "(4, '2024-11-01', 1)": 0, + "(4, '2024-11-01', 2)": 0, + "(4, '2024-11-01', 3)": 0, + "(4, '2024-11-01', 4)": 0, + "(4, '2024-11-01', 5)": 0, + "(4, '2024-11-01', 6)": 0, + "(4, '2024-11-01', 7)": 0, + "(4, '2024-11-02', 0)": 0, + "(4, '2024-11-02', 1)": 0, + "(4, '2024-11-02', 2)": 0, + "(4, '2024-11-02', 3)": 0, + "(4, '2024-11-02', 4)": 0, + "(4, '2024-11-02', 5)": 0, + "(4, '2024-11-02', 6)": 0, + "(4, '2024-11-02', 7)": 0, + "(4, '2024-11-03', 0)": 0, + "(4, '2024-11-03', 1)": 0, + "(4, '2024-11-03', 2)": 0, + "(4, '2024-11-03', 3)": 0, + "(4, '2024-11-03', 4)": 0, + "(4, '2024-11-03', 5)": 0, + "(4, '2024-11-03', 6)": 0, + "(4, '2024-11-03', 7)": 0, + "(4, '2024-11-04', 0)": 0, + "(4, '2024-11-04', 1)": 0, + "(4, '2024-11-04', 2)": 0, + "(4, '2024-11-04', 3)": 0, + "(4, '2024-11-04', 4)": 0, + "(4, '2024-11-04', 5)": 0, + "(4, '2024-11-04', 6)": 0, + "(4, '2024-11-04', 7)": 0, + "(4, '2024-11-05', 0)": 0, + "(4, '2024-11-05', 1)": 0, + "(4, '2024-11-05', 2)": 0, + "(4, '2024-11-05', 3)": 0, + "(4, '2024-11-05', 4)": 0, + "(4, '2024-11-05', 5)": 0, + "(4, '2024-11-05', 6)": 0, + "(4, '2024-11-05', 7)": 0, + "(4, '2024-11-06', 0)": 0, + "(4, '2024-11-06', 1)": 0, + "(4, '2024-11-06', 2)": 0, + "(4, '2024-11-06', 3)": 0, + "(4, '2024-11-06', 4)": 0, + "(4, '2024-11-06', 5)": 0, + "(4, '2024-11-06', 6)": 0, + "(4, '2024-11-06', 7)": 0, + "(4, '2024-11-07', 0)": 0, + "(4, '2024-11-07', 1)": 0, + "(4, '2024-11-07', 2)": 0, + "(4, '2024-11-07', 3)": 0, + "(4, '2024-11-07', 4)": 0, + "(4, '2024-11-07', 5)": 0, + "(4, '2024-11-07', 6)": 0, + "(4, '2024-11-07', 7)": 0, + "(4, '2024-11-08', 0)": 0, + "(4, '2024-11-08', 1)": 0, + "(4, '2024-11-08', 2)": 0, + "(4, '2024-11-08', 3)": 0, + "(4, '2024-11-08', 4)": 0, + "(4, '2024-11-08', 5)": 0, + "(4, '2024-11-08', 6)": 0, + "(4, '2024-11-08', 7)": 0, + "(4, '2024-11-09', 0)": 0, + "(4, '2024-11-09', 1)": 0, + "(4, '2024-11-09', 2)": 0, + "(4, '2024-11-09', 3)": 0, + "(4, '2024-11-09', 4)": 0, + "(4, '2024-11-09', 5)": 0, + "(4, '2024-11-09', 6)": 0, + "(4, '2024-11-09', 7)": 0, + "(4, '2024-11-10', 0)": 0, + "(4, '2024-11-10', 1)": 0, + "(4, '2024-11-10', 2)": 0, + "(4, '2024-11-10', 3)": 0, + "(4, '2024-11-10', 4)": 0, + "(4, '2024-11-10', 5)": 0, + "(4, '2024-11-10', 6)": 0, + "(4, '2024-11-10', 7)": 0, + "(4, '2024-11-11', 0)": 0, + "(4, '2024-11-11', 1)": 0, + "(4, '2024-11-11', 2)": 0, + "(4, '2024-11-11', 3)": 0, + "(4, '2024-11-11', 4)": 0, + "(4, '2024-11-11', 5)": 0, + "(4, '2024-11-11', 6)": 0, + "(4, '2024-11-11', 7)": 0, + "(4, '2024-11-12', 0)": 0, + "(4, '2024-11-12', 1)": 0, + "(4, '2024-11-12', 2)": 0, + "(4, '2024-11-12', 3)": 0, + "(4, '2024-11-12', 4)": 0, + "(4, '2024-11-12', 5)": 0, + "(4, '2024-11-12', 6)": 0, + "(4, '2024-11-12', 7)": 0, + "(4, '2024-11-13', 0)": 0, + "(4, '2024-11-13', 1)": 0, + "(4, '2024-11-13', 2)": 0, + "(4, '2024-11-13', 3)": 0, + "(4, '2024-11-13', 4)": 0, + "(4, '2024-11-13', 5)": 0, + "(4, '2024-11-13', 6)": 0, + "(4, '2024-11-13', 7)": 0, + "(4, '2024-11-14', 0)": 0, + "(4, '2024-11-14', 1)": 0, + "(4, '2024-11-14', 2)": 0, + "(4, '2024-11-14', 3)": 0, + "(4, '2024-11-14', 4)": 0, + "(4, '2024-11-14', 5)": 0, + "(4, '2024-11-14', 6)": 0, + "(4, '2024-11-14', 7)": 0, + "(4, '2024-11-15', 0)": 0, + "(4, '2024-11-15', 1)": 0, + "(4, '2024-11-15', 2)": 0, + "(4, '2024-11-15', 3)": 0, + "(4, '2024-11-15', 4)": 0, + "(4, '2024-11-15', 5)": 0, + "(4, '2024-11-15', 6)": 0, + "(4, '2024-11-15', 7)": 0, + "(4, '2024-11-16', 0)": 0, + "(4, '2024-11-16', 1)": 0, + "(4, '2024-11-16', 2)": 0, + "(4, '2024-11-16', 3)": 0, + "(4, '2024-11-16', 4)": 0, + "(4, '2024-11-16', 5)": 0, + "(4, '2024-11-16', 6)": 0, + "(4, '2024-11-16', 7)": 0, + "(4, '2024-11-17', 0)": 0, + "(4, '2024-11-17', 1)": 0, + "(4, '2024-11-17', 2)": 0, + "(4, '2024-11-17', 3)": 0, + "(4, '2024-11-17', 4)": 0, + "(4, '2024-11-17', 5)": 0, + "(4, '2024-11-17', 6)": 0, + "(4, '2024-11-17', 7)": 0, + "(4, '2024-11-18', 0)": 0, + "(4, '2024-11-18', 1)": 0, + "(4, '2024-11-18', 2)": 0, + "(4, '2024-11-18', 3)": 0, + "(4, '2024-11-18', 4)": 0, + "(4, '2024-11-18', 5)": 0, + "(4, '2024-11-18', 6)": 0, + "(4, '2024-11-18', 7)": 0, + "(4, '2024-11-19', 0)": 0, + "(4, '2024-11-19', 1)": 0, + "(4, '2024-11-19', 2)": 0, + "(4, '2024-11-19', 3)": 0, + "(4, '2024-11-19', 4)": 0, + "(4, '2024-11-19', 5)": 0, + "(4, '2024-11-19', 6)": 0, + "(4, '2024-11-19', 7)": 0, + "(4, '2024-11-20', 0)": 0, + "(4, '2024-11-20', 1)": 0, + "(4, '2024-11-20', 2)": 0, + "(4, '2024-11-20', 3)": 0, + "(4, '2024-11-20', 4)": 0, + "(4, '2024-11-20', 5)": 0, + "(4, '2024-11-20', 6)": 0, + "(4, '2024-11-20', 7)": 0, + "(4, '2024-11-21', 0)": 0, + "(4, '2024-11-21', 1)": 0, + "(4, '2024-11-21', 2)": 0, + "(4, '2024-11-21', 3)": 0, + "(4, '2024-11-21', 4)": 0, + "(4, '2024-11-21', 5)": 0, + "(4, '2024-11-21', 6)": 0, + "(4, '2024-11-21', 7)": 0, + "(4, '2024-11-22', 0)": 0, + "(4, '2024-11-22', 1)": 0, + "(4, '2024-11-22', 2)": 0, + "(4, '2024-11-22', 3)": 0, + "(4, '2024-11-22', 4)": 0, + "(4, '2024-11-22', 5)": 0, + "(4, '2024-11-22', 6)": 0, + "(4, '2024-11-22', 7)": 0, + "(4, '2024-11-23', 0)": 0, + "(4, '2024-11-23', 1)": 0, + "(4, '2024-11-23', 2)": 0, + "(4, '2024-11-23', 3)": 0, + "(4, '2024-11-23', 4)": 0, + "(4, '2024-11-23', 5)": 0, + "(4, '2024-11-23', 6)": 0, + "(4, '2024-11-23', 7)": 0, + "(4, '2024-11-24', 0)": 0, + "(4, '2024-11-24', 1)": 0, + "(4, '2024-11-24', 2)": 0, + "(4, '2024-11-24', 3)": 0, + "(4, '2024-11-24', 4)": 0, + "(4, '2024-11-24', 5)": 0, + "(4, '2024-11-24', 6)": 0, + "(4, '2024-11-24', 7)": 0, + "(4, '2024-11-25', 0)": 0, + "(4, '2024-11-25', 1)": 0, + "(4, '2024-11-25', 2)": 0, + "(4, '2024-11-25', 3)": 0, + "(4, '2024-11-25', 4)": 0, + "(4, '2024-11-25', 5)": 0, + "(4, '2024-11-25', 6)": 0, + "(4, '2024-11-25', 7)": 0, + "(4, '2024-11-26', 0)": 0, + "(4, '2024-11-26', 1)": 0, + "(4, '2024-11-26', 2)": 0, + "(4, '2024-11-26', 3)": 0, + "(4, '2024-11-26', 4)": 0, + "(4, '2024-11-26', 5)": 0, + "(4, '2024-11-26', 6)": 0, + "(4, '2024-11-26', 7)": 0, + "(4, '2024-11-27', 0)": 0, + "(4, '2024-11-27', 1)": 0, + "(4, '2024-11-27', 2)": 0, + "(4, '2024-11-27', 3)": 0, + "(4, '2024-11-27', 4)": 0, + "(4, '2024-11-27', 5)": 0, + "(4, '2024-11-27', 6)": 0, + "(4, '2024-11-27', 7)": 0, + "(4, '2024-11-28', 0)": 0, + "(4, '2024-11-28', 1)": 0, + "(4, '2024-11-28', 2)": 0, + "(4, '2024-11-28', 3)": 0, + "(4, '2024-11-28', 4)": 0, + "(4, '2024-11-28', 5)": 0, + "(4, '2024-11-28', 6)": 0, + "(4, '2024-11-28', 7)": 0, + "(4, '2024-11-29', 0)": 0, + "(4, '2024-11-29', 1)": 0, + "(4, '2024-11-29', 2)": 0, + "(4, '2024-11-29', 3)": 0, + "(4, '2024-11-29', 4)": 0, + "(4, '2024-11-29', 5)": 0, + "(4, '2024-11-29', 6)": 0, + "(4, '2024-11-29', 7)": 0, + "(4, '2024-11-30', 0)": 0, + "(4, '2024-11-30', 1)": 0, + "(4, '2024-11-30', 2)": 0, + "(4, '2024-11-30', 3)": 0, + "(4, '2024-11-30', 4)": 0, + "(4, '2024-11-30', 5)": 0, + "(4, '2024-11-30', 6)": 0, + "(4, '2024-11-30', 7)": 0, + "(4566, '2024-11-01', 0)": 1, + "(4566, '2024-11-01', 1)": 0, + "(4566, '2024-11-01', 2)": 0, + "(4566, '2024-11-01', 3)": 0, + "(4566, '2024-11-01', 4)": 0, + "(4566, '2024-11-01', 5)": 0, + "(4566, '2024-11-01', 6)": 0, + "(4566, '2024-11-01', 7)": 0, + "(4566, '2024-11-02', 0)": 0, + "(4566, '2024-11-02', 1)": 0, + "(4566, '2024-11-02', 2)": 0, + "(4566, '2024-11-02', 3)": 0, + "(4566, '2024-11-02', 4)": 0, + "(4566, '2024-11-02', 5)": 0, + "(4566, '2024-11-02', 6)": 0, + "(4566, '2024-11-02', 7)": 0, + "(4566, '2024-11-03', 0)": 0, + "(4566, '2024-11-03', 1)": 0, + "(4566, '2024-11-03', 2)": 1, + "(4566, '2024-11-03', 3)": 0, + "(4566, '2024-11-03', 4)": 0, + "(4566, '2024-11-03', 5)": 0, + "(4566, '2024-11-03', 6)": 0, + "(4566, '2024-11-03', 7)": 0, + "(4566, '2024-11-04', 0)": 0, + "(4566, '2024-11-04', 1)": 0, + "(4566, '2024-11-04', 2)": 1, + "(4566, '2024-11-04', 3)": 0, + "(4566, '2024-11-04', 4)": 0, + "(4566, '2024-11-04', 5)": 0, + "(4566, '2024-11-04', 6)": 0, + "(4566, '2024-11-04', 7)": 0, + "(4566, '2024-11-05', 0)": 0, + "(4566, '2024-11-05', 1)": 0, + "(4566, '2024-11-05', 2)": 1, + "(4566, '2024-11-05', 3)": 0, + "(4566, '2024-11-05', 4)": 0, + "(4566, '2024-11-05', 5)": 0, + "(4566, '2024-11-05', 6)": 0, + "(4566, '2024-11-05', 7)": 0, + "(4566, '2024-11-06', 0)": 0, + "(4566, '2024-11-06', 1)": 0, + "(4566, '2024-11-06', 2)": 0, + "(4566, '2024-11-06', 3)": 0, + "(4566, '2024-11-06', 4)": 0, + "(4566, '2024-11-06', 5)": 0, + "(4566, '2024-11-06', 6)": 0, + "(4566, '2024-11-06', 7)": 0, + "(4566, '2024-11-07', 0)": 0, + "(4566, '2024-11-07', 1)": 0, + "(4566, '2024-11-07', 2)": 0, + "(4566, '2024-11-07', 3)": 0, + "(4566, '2024-11-07', 4)": 0, + "(4566, '2024-11-07', 5)": 0, + "(4566, '2024-11-07', 6)": 0, + "(4566, '2024-11-07', 7)": 0, + "(4566, '2024-11-08', 0)": 0, + "(4566, '2024-11-08', 1)": 1, + "(4566, '2024-11-08', 2)": 0, + "(4566, '2024-11-08', 3)": 0, + "(4566, '2024-11-08', 4)": 0, + "(4566, '2024-11-08', 5)": 0, + "(4566, '2024-11-08', 6)": 0, + "(4566, '2024-11-08', 7)": 0, + "(4566, '2024-11-09', 0)": 0, + "(4566, '2024-11-09', 1)": 0, + "(4566, '2024-11-09', 2)": 0, + "(4566, '2024-11-09', 3)": 0, + "(4566, '2024-11-09', 4)": 0, + "(4566, '2024-11-09', 5)": 0, + "(4566, '2024-11-09', 6)": 0, + "(4566, '2024-11-09', 7)": 0, + "(4566, '2024-11-10', 0)": 1, + "(4566, '2024-11-10', 1)": 0, + "(4566, '2024-11-10', 2)": 0, + "(4566, '2024-11-10', 3)": 0, + "(4566, '2024-11-10', 4)": 0, + "(4566, '2024-11-10', 5)": 0, + "(4566, '2024-11-10', 6)": 0, + "(4566, '2024-11-10', 7)": 0, + "(4566, '2024-11-11', 0)": 0, + "(4566, '2024-11-11', 1)": 0, + "(4566, '2024-11-11', 2)": 0, + "(4566, '2024-11-11', 3)": 0, + "(4566, '2024-11-11', 4)": 0, + "(4566, '2024-11-11', 5)": 0, + "(4566, '2024-11-11', 6)": 0, + "(4566, '2024-11-11', 7)": 0, + "(4566, '2024-11-12', 0)": 1, + "(4566, '2024-11-12', 1)": 0, + "(4566, '2024-11-12', 2)": 0, + "(4566, '2024-11-12', 3)": 0, + "(4566, '2024-11-12', 4)": 0, + "(4566, '2024-11-12', 5)": 0, + "(4566, '2024-11-12', 6)": 0, + "(4566, '2024-11-12', 7)": 0, + "(4566, '2024-11-13', 0)": 0, + "(4566, '2024-11-13', 1)": 0, + "(4566, '2024-11-13', 2)": 1, + "(4566, '2024-11-13', 3)": 0, + "(4566, '2024-11-13', 4)": 0, + "(4566, '2024-11-13', 5)": 0, + "(4566, '2024-11-13', 6)": 0, + "(4566, '2024-11-13', 7)": 0, + "(4566, '2024-11-14', 0)": 0, + "(4566, '2024-11-14', 1)": 0, + "(4566, '2024-11-14', 2)": 1, + "(4566, '2024-11-14', 3)": 0, + "(4566, '2024-11-14', 4)": 0, + "(4566, '2024-11-14', 5)": 0, + "(4566, '2024-11-14', 6)": 0, + "(4566, '2024-11-14', 7)": 0, + "(4566, '2024-11-15', 0)": 0, + "(4566, '2024-11-15', 1)": 0, + "(4566, '2024-11-15', 2)": 1, + "(4566, '2024-11-15', 3)": 0, + "(4566, '2024-11-15', 4)": 0, + "(4566, '2024-11-15', 5)": 0, + "(4566, '2024-11-15', 6)": 0, + "(4566, '2024-11-15', 7)": 0, + "(4566, '2024-11-16', 0)": 0, + "(4566, '2024-11-16', 1)": 0, + "(4566, '2024-11-16', 2)": 0, + "(4566, '2024-11-16', 3)": 0, + "(4566, '2024-11-16', 4)": 0, + "(4566, '2024-11-16', 5)": 0, + "(4566, '2024-11-16', 6)": 0, + "(4566, '2024-11-16', 7)": 0, + "(4566, '2024-11-17', 0)": 0, + "(4566, '2024-11-17', 1)": 0, + "(4566, '2024-11-17', 2)": 0, + "(4566, '2024-11-17', 3)": 0, + "(4566, '2024-11-17', 4)": 0, + "(4566, '2024-11-17', 5)": 0, + "(4566, '2024-11-17', 6)": 0, + "(4566, '2024-11-17', 7)": 0, + "(4566, '2024-11-18', 0)": 0, + "(4566, '2024-11-18', 1)": 0, + "(4566, '2024-11-18', 2)": 0, + "(4566, '2024-11-18', 3)": 0, + "(4566, '2024-11-18', 4)": 0, + "(4566, '2024-11-18', 5)": 0, + "(4566, '2024-11-18', 6)": 0, + "(4566, '2024-11-18', 7)": 0, + "(4566, '2024-11-19', 0)": 0, + "(4566, '2024-11-19', 1)": 1, + "(4566, '2024-11-19', 2)": 0, + "(4566, '2024-11-19', 3)": 0, + "(4566, '2024-11-19', 4)": 0, + "(4566, '2024-11-19', 5)": 0, + "(4566, '2024-11-19', 6)": 0, + "(4566, '2024-11-19', 7)": 0, + "(4566, '2024-11-20', 0)": 1, + "(4566, '2024-11-20', 1)": 0, + "(4566, '2024-11-20', 2)": 0, + "(4566, '2024-11-20', 3)": 0, + "(4566, '2024-11-20', 4)": 0, + "(4566, '2024-11-20', 5)": 0, + "(4566, '2024-11-20', 6)": 0, + "(4566, '2024-11-20', 7)": 0, + "(4566, '2024-11-21', 0)": 0, + "(4566, '2024-11-21', 1)": 1, + "(4566, '2024-11-21', 2)": 0, + "(4566, '2024-11-21', 3)": 0, + "(4566, '2024-11-21', 4)": 0, + "(4566, '2024-11-21', 5)": 0, + "(4566, '2024-11-21', 6)": 0, + "(4566, '2024-11-21', 7)": 0, + "(4566, '2024-11-22', 0)": 0, + "(4566, '2024-11-22', 1)": 0, + "(4566, '2024-11-22', 2)": 0, + "(4566, '2024-11-22', 3)": 0, + "(4566, '2024-11-22', 4)": 0, + "(4566, '2024-11-22', 5)": 0, + "(4566, '2024-11-22', 6)": 0, + "(4566, '2024-11-22', 7)": 0, + "(4566, '2024-11-23', 0)": 0, + "(4566, '2024-11-23', 1)": 0, + "(4566, '2024-11-23', 2)": 1, + "(4566, '2024-11-23', 3)": 0, + "(4566, '2024-11-23', 4)": 0, + "(4566, '2024-11-23', 5)": 0, + "(4566, '2024-11-23', 6)": 0, + "(4566, '2024-11-23', 7)": 0, + "(4566, '2024-11-24', 0)": 0, + "(4566, '2024-11-24', 1)": 0, + "(4566, '2024-11-24', 2)": 0, + "(4566, '2024-11-24', 3)": 0, + "(4566, '2024-11-24', 4)": 0, + "(4566, '2024-11-24', 5)": 0, + "(4566, '2024-11-24', 6)": 0, + "(4566, '2024-11-24', 7)": 0, + "(4566, '2024-11-25', 0)": 0, + "(4566, '2024-11-25', 1)": 0, + "(4566, '2024-11-25', 2)": 0, + "(4566, '2024-11-25', 3)": 0, + "(4566, '2024-11-25', 4)": 0, + "(4566, '2024-11-25', 5)": 0, + "(4566, '2024-11-25', 6)": 0, + "(4566, '2024-11-25', 7)": 0, + "(4566, '2024-11-26', 0)": 1, + "(4566, '2024-11-26', 1)": 0, + "(4566, '2024-11-26', 2)": 0, + "(4566, '2024-11-26', 3)": 0, + "(4566, '2024-11-26', 4)": 0, + "(4566, '2024-11-26', 5)": 0, + "(4566, '2024-11-26', 6)": 0, + "(4566, '2024-11-26', 7)": 0, + "(4566, '2024-11-27', 0)": 1, + "(4566, '2024-11-27', 1)": 0, + "(4566, '2024-11-27', 2)": 0, + "(4566, '2024-11-27', 3)": 0, + "(4566, '2024-11-27', 4)": 0, + "(4566, '2024-11-27', 5)": 0, + "(4566, '2024-11-27', 6)": 0, + "(4566, '2024-11-27', 7)": 0, + "(4566, '2024-11-28', 0)": 0, + "(4566, '2024-11-28', 1)": 0, + "(4566, '2024-11-28', 2)": 0, + "(4566, '2024-11-28', 3)": 0, + "(4566, '2024-11-28', 4)": 0, + "(4566, '2024-11-28', 5)": 0, + "(4566, '2024-11-28', 6)": 0, + "(4566, '2024-11-28', 7)": 0, + "(4566, '2024-11-29', 0)": 1, + "(4566, '2024-11-29', 1)": 0, + "(4566, '2024-11-29', 2)": 0, + "(4566, '2024-11-29', 3)": 0, + "(4566, '2024-11-29', 4)": 0, + "(4566, '2024-11-29', 5)": 0, + "(4566, '2024-11-29', 6)": 0, + "(4566, '2024-11-29', 7)": 0, + "(4566, '2024-11-30', 0)": 0, + "(4566, '2024-11-30', 1)": 0, + "(4566, '2024-11-30', 2)": 1, + "(4566, '2024-11-30', 3)": 0, + "(4566, '2024-11-30', 4)": 0, + "(4566, '2024-11-30', 5)": 0, + "(4566, '2024-11-30', 6)": 0, + "(4566, '2024-11-30', 7)": 0, + "(459, '2024-11-01', 0)": 0, + "(459, '2024-11-01', 1)": 0, + "(459, '2024-11-01', 2)": 1, + "(459, '2024-11-01', 3)": 0, + "(459, '2024-11-01', 4)": 0, + "(459, '2024-11-01', 5)": 0, + "(459, '2024-11-01', 6)": 0, + "(459, '2024-11-01', 7)": 0, + "(459, '2024-11-02', 0)": 0, + "(459, '2024-11-02', 1)": 0, + "(459, '2024-11-02', 2)": 0, + "(459, '2024-11-02', 3)": 0, + "(459, '2024-11-02', 4)": 0, + "(459, '2024-11-02', 5)": 0, + "(459, '2024-11-02', 6)": 0, + "(459, '2024-11-02', 7)": 0, + "(459, '2024-11-03', 0)": 0, + "(459, '2024-11-03', 1)": 0, + "(459, '2024-11-03', 2)": 0, + "(459, '2024-11-03', 3)": 0, + "(459, '2024-11-03', 4)": 0, + "(459, '2024-11-03', 5)": 0, + "(459, '2024-11-03', 6)": 0, + "(459, '2024-11-03', 7)": 0, + "(459, '2024-11-04', 0)": 0, + "(459, '2024-11-04', 1)": 0, + "(459, '2024-11-04', 2)": 0, + "(459, '2024-11-04', 3)": 0, + "(459, '2024-11-04', 4)": 0, + "(459, '2024-11-04', 5)": 0, + "(459, '2024-11-04', 6)": 0, + "(459, '2024-11-04', 7)": 0, + "(459, '2024-11-05', 0)": 0, + "(459, '2024-11-05', 1)": 0, + "(459, '2024-11-05', 2)": 0, + "(459, '2024-11-05', 3)": 0, + "(459, '2024-11-05', 4)": 0, + "(459, '2024-11-05', 5)": 0, + "(459, '2024-11-05', 6)": 0, + "(459, '2024-11-05', 7)": 0, + "(459, '2024-11-06', 0)": 0, + "(459, '2024-11-06', 1)": 0, + "(459, '2024-11-06', 2)": 0, + "(459, '2024-11-06', 3)": 0, + "(459, '2024-11-06', 4)": 0, + "(459, '2024-11-06', 5)": 0, + "(459, '2024-11-06', 6)": 0, + "(459, '2024-11-06', 7)": 0, + "(459, '2024-11-07', 0)": 0, + "(459, '2024-11-07', 1)": 0, + "(459, '2024-11-07', 2)": 0, + "(459, '2024-11-07', 3)": 0, + "(459, '2024-11-07', 4)": 0, + "(459, '2024-11-07', 5)": 0, + "(459, '2024-11-07', 6)": 0, + "(459, '2024-11-07', 7)": 0, + "(459, '2024-11-08', 0)": 0, + "(459, '2024-11-08', 1)": 0, + "(459, '2024-11-08', 2)": 0, + "(459, '2024-11-08', 3)": 0, + "(459, '2024-11-08', 4)": 0, + "(459, '2024-11-08', 5)": 0, + "(459, '2024-11-08', 6)": 0, + "(459, '2024-11-08', 7)": 0, + "(459, '2024-11-09', 0)": 0, + "(459, '2024-11-09', 1)": 0, + "(459, '2024-11-09', 2)": 0, + "(459, '2024-11-09', 3)": 0, + "(459, '2024-11-09', 4)": 0, + "(459, '2024-11-09', 5)": 0, + "(459, '2024-11-09', 6)": 0, + "(459, '2024-11-09', 7)": 0, + "(459, '2024-11-10', 0)": 0, + "(459, '2024-11-10', 1)": 0, + "(459, '2024-11-10', 2)": 0, + "(459, '2024-11-10', 3)": 0, + "(459, '2024-11-10', 4)": 0, + "(459, '2024-11-10', 5)": 0, + "(459, '2024-11-10', 6)": 0, + "(459, '2024-11-10', 7)": 0, + "(459, '2024-11-11', 0)": 0, + "(459, '2024-11-11', 1)": 0, + "(459, '2024-11-11', 2)": 0, + "(459, '2024-11-11', 3)": 0, + "(459, '2024-11-11', 4)": 0, + "(459, '2024-11-11', 5)": 0, + "(459, '2024-11-11', 6)": 0, + "(459, '2024-11-11', 7)": 0, + "(459, '2024-11-12', 0)": 0, + "(459, '2024-11-12', 1)": 0, + "(459, '2024-11-12', 2)": 0, + "(459, '2024-11-12', 3)": 0, + "(459, '2024-11-12', 4)": 0, + "(459, '2024-11-12', 5)": 0, + "(459, '2024-11-12', 6)": 0, + "(459, '2024-11-12', 7)": 0, + "(459, '2024-11-13', 0)": 0, + "(459, '2024-11-13', 1)": 0, + "(459, '2024-11-13', 2)": 0, + "(459, '2024-11-13', 3)": 0, + "(459, '2024-11-13', 4)": 0, + "(459, '2024-11-13', 5)": 0, + "(459, '2024-11-13', 6)": 0, + "(459, '2024-11-13', 7)": 0, + "(459, '2024-11-14', 0)": 0, + "(459, '2024-11-14', 1)": 1, + "(459, '2024-11-14', 2)": 0, + "(459, '2024-11-14', 3)": 0, + "(459, '2024-11-14', 4)": 0, + "(459, '2024-11-14', 5)": 0, + "(459, '2024-11-14', 6)": 0, + "(459, '2024-11-14', 7)": 0, + "(459, '2024-11-15', 0)": 0, + "(459, '2024-11-15', 1)": 1, + "(459, '2024-11-15', 2)": 0, + "(459, '2024-11-15', 3)": 0, + "(459, '2024-11-15', 4)": 0, + "(459, '2024-11-15', 5)": 0, + "(459, '2024-11-15', 6)": 0, + "(459, '2024-11-15', 7)": 0, + "(459, '2024-11-16', 0)": 0, + "(459, '2024-11-16', 1)": 0, + "(459, '2024-11-16', 2)": 0, + "(459, '2024-11-16', 3)": 0, + "(459, '2024-11-16', 4)": 0, + "(459, '2024-11-16', 5)": 0, + "(459, '2024-11-16', 6)": 0, + "(459, '2024-11-16', 7)": 0, + "(459, '2024-11-17', 0)": 0, + "(459, '2024-11-17', 1)": 0, + "(459, '2024-11-17', 2)": 0, + "(459, '2024-11-17', 3)": 0, + "(459, '2024-11-17', 4)": 0, + "(459, '2024-11-17', 5)": 0, + "(459, '2024-11-17', 6)": 0, + "(459, '2024-11-17', 7)": 0, + "(459, '2024-11-18', 0)": 0, + "(459, '2024-11-18', 1)": 0, + "(459, '2024-11-18', 2)": 0, + "(459, '2024-11-18', 3)": 0, + "(459, '2024-11-18', 4)": 0, + "(459, '2024-11-18', 5)": 0, + "(459, '2024-11-18', 6)": 0, + "(459, '2024-11-18', 7)": 0, + "(459, '2024-11-19', 0)": 0, + "(459, '2024-11-19', 1)": 0, + "(459, '2024-11-19', 2)": 0, + "(459, '2024-11-19', 3)": 0, + "(459, '2024-11-19', 4)": 0, + "(459, '2024-11-19', 5)": 0, + "(459, '2024-11-19', 6)": 0, + "(459, '2024-11-19', 7)": 0, + "(459, '2024-11-20', 0)": 0, + "(459, '2024-11-20', 1)": 0, + "(459, '2024-11-20', 2)": 0, + "(459, '2024-11-20', 3)": 0, + "(459, '2024-11-20', 4)": 0, + "(459, '2024-11-20', 5)": 0, + "(459, '2024-11-20', 6)": 0, + "(459, '2024-11-20', 7)": 0, + "(459, '2024-11-21', 0)": 0, + "(459, '2024-11-21', 1)": 0, + "(459, '2024-11-21', 2)": 0, + "(459, '2024-11-21', 3)": 0, + "(459, '2024-11-21', 4)": 0, + "(459, '2024-11-21', 5)": 0, + "(459, '2024-11-21', 6)": 0, + "(459, '2024-11-21', 7)": 0, + "(459, '2024-11-22', 0)": 0, + "(459, '2024-11-22', 1)": 0, + "(459, '2024-11-22', 2)": 0, + "(459, '2024-11-22', 3)": 0, + "(459, '2024-11-22', 4)": 0, + "(459, '2024-11-22', 5)": 0, + "(459, '2024-11-22', 6)": 0, + "(459, '2024-11-22', 7)": 0, + "(459, '2024-11-23', 0)": 0, + "(459, '2024-11-23', 1)": 0, + "(459, '2024-11-23', 2)": 0, + "(459, '2024-11-23', 3)": 0, + "(459, '2024-11-23', 4)": 0, + "(459, '2024-11-23', 5)": 0, + "(459, '2024-11-23', 6)": 0, + "(459, '2024-11-23', 7)": 0, + "(459, '2024-11-24', 0)": 0, + "(459, '2024-11-24', 1)": 0, + "(459, '2024-11-24', 2)": 0, + "(459, '2024-11-24', 3)": 0, + "(459, '2024-11-24', 4)": 0, + "(459, '2024-11-24', 5)": 0, + "(459, '2024-11-24', 6)": 0, + "(459, '2024-11-24', 7)": 0, + "(459, '2024-11-25', 0)": 0, + "(459, '2024-11-25', 1)": 0, + "(459, '2024-11-25', 2)": 0, + "(459, '2024-11-25', 3)": 0, + "(459, '2024-11-25', 4)": 0, + "(459, '2024-11-25', 5)": 0, + "(459, '2024-11-25', 6)": 0, + "(459, '2024-11-25', 7)": 0, + "(459, '2024-11-26', 0)": 0, + "(459, '2024-11-26', 1)": 0, + "(459, '2024-11-26', 2)": 0, + "(459, '2024-11-26', 3)": 0, + "(459, '2024-11-26', 4)": 0, + "(459, '2024-11-26', 5)": 0, + "(459, '2024-11-26', 6)": 0, + "(459, '2024-11-26', 7)": 0, + "(459, '2024-11-27', 0)": 0, + "(459, '2024-11-27', 1)": 0, + "(459, '2024-11-27', 2)": 0, + "(459, '2024-11-27', 3)": 0, + "(459, '2024-11-27', 4)": 0, + "(459, '2024-11-27', 5)": 0, + "(459, '2024-11-27', 6)": 0, + "(459, '2024-11-27', 7)": 0, + "(459, '2024-11-28', 0)": 0, + "(459, '2024-11-28', 1)": 0, + "(459, '2024-11-28', 2)": 0, + "(459, '2024-11-28', 3)": 0, + "(459, '2024-11-28', 4)": 0, + "(459, '2024-11-28', 5)": 0, + "(459, '2024-11-28', 6)": 0, + "(459, '2024-11-28', 7)": 0, + "(459, '2024-11-29', 0)": 0, + "(459, '2024-11-29', 1)": 0, + "(459, '2024-11-29', 2)": 0, + "(459, '2024-11-29', 3)": 0, + "(459, '2024-11-29', 4)": 0, + "(459, '2024-11-29', 5)": 0, + "(459, '2024-11-29', 6)": 0, + "(459, '2024-11-29', 7)": 0, + "(459, '2024-11-30', 0)": 0, + "(459, '2024-11-30', 1)": 1, + "(459, '2024-11-30', 2)": 0, + "(459, '2024-11-30', 3)": 0, + "(459, '2024-11-30', 4)": 0, + "(459, '2024-11-30', 5)": 0, + "(459, '2024-11-30', 6)": 0, + "(459, '2024-11-30', 7)": 0, + "(5, '2024-11-01', 0)": 0, + "(5, '2024-11-01', 1)": 0, + "(5, '2024-11-01', 2)": 0, + "(5, '2024-11-01', 3)": 0, + "(5, '2024-11-01', 4)": 0, + "(5, '2024-11-01', 5)": 0, + "(5, '2024-11-01', 6)": 0, + "(5, '2024-11-01', 7)": 0, + "(5, '2024-11-02', 0)": 0, + "(5, '2024-11-02', 1)": 0, + "(5, '2024-11-02', 2)": 0, + "(5, '2024-11-02', 3)": 0, + "(5, '2024-11-02', 4)": 0, + "(5, '2024-11-02', 5)": 0, + "(5, '2024-11-02', 6)": 0, + "(5, '2024-11-02', 7)": 0, + "(5, '2024-11-03', 0)": 0, + "(5, '2024-11-03', 1)": 0, + "(5, '2024-11-03', 2)": 0, + "(5, '2024-11-03', 3)": 0, + "(5, '2024-11-03', 4)": 0, + "(5, '2024-11-03', 5)": 0, + "(5, '2024-11-03', 6)": 0, + "(5, '2024-11-03', 7)": 0, + "(5, '2024-11-04', 0)": 0, + "(5, '2024-11-04', 1)": 0, + "(5, '2024-11-04', 2)": 0, + "(5, '2024-11-04', 3)": 0, + "(5, '2024-11-04', 4)": 0, + "(5, '2024-11-04', 5)": 0, + "(5, '2024-11-04', 6)": 0, + "(5, '2024-11-04', 7)": 0, + "(5, '2024-11-05', 0)": 0, + "(5, '2024-11-05', 1)": 0, + "(5, '2024-11-05', 2)": 0, + "(5, '2024-11-05', 3)": 0, + "(5, '2024-11-05', 4)": 0, + "(5, '2024-11-05', 5)": 0, + "(5, '2024-11-05', 6)": 0, + "(5, '2024-11-05', 7)": 0, + "(5, '2024-11-06', 0)": 0, + "(5, '2024-11-06', 1)": 0, + "(5, '2024-11-06', 2)": 0, + "(5, '2024-11-06', 3)": 0, + "(5, '2024-11-06', 4)": 0, + "(5, '2024-11-06', 5)": 0, + "(5, '2024-11-06', 6)": 0, + "(5, '2024-11-06', 7)": 0, + "(5, '2024-11-07', 0)": 0, + "(5, '2024-11-07', 1)": 0, + "(5, '2024-11-07', 2)": 0, + "(5, '2024-11-07', 3)": 0, + "(5, '2024-11-07', 4)": 0, + "(5, '2024-11-07', 5)": 0, + "(5, '2024-11-07', 6)": 0, + "(5, '2024-11-07', 7)": 0, + "(5, '2024-11-08', 0)": 0, + "(5, '2024-11-08', 1)": 0, + "(5, '2024-11-08', 2)": 0, + "(5, '2024-11-08', 3)": 0, + "(5, '2024-11-08', 4)": 0, + "(5, '2024-11-08', 5)": 0, + "(5, '2024-11-08', 6)": 0, + "(5, '2024-11-08', 7)": 0, + "(5, '2024-11-09', 0)": 0, + "(5, '2024-11-09', 1)": 0, + "(5, '2024-11-09', 2)": 0, + "(5, '2024-11-09', 3)": 0, + "(5, '2024-11-09', 4)": 0, + "(5, '2024-11-09', 5)": 0, + "(5, '2024-11-09', 6)": 0, + "(5, '2024-11-09', 7)": 0, + "(5, '2024-11-10', 0)": 0, + "(5, '2024-11-10', 1)": 0, + "(5, '2024-11-10', 2)": 0, + "(5, '2024-11-10', 3)": 0, + "(5, '2024-11-10', 4)": 0, + "(5, '2024-11-10', 5)": 0, + "(5, '2024-11-10', 6)": 0, + "(5, '2024-11-10', 7)": 0, + "(5, '2024-11-11', 0)": 0, + "(5, '2024-11-11', 1)": 0, + "(5, '2024-11-11', 2)": 0, + "(5, '2024-11-11', 3)": 0, + "(5, '2024-11-11', 4)": 0, + "(5, '2024-11-11', 5)": 0, + "(5, '2024-11-11', 6)": 0, + "(5, '2024-11-11', 7)": 0, + "(5, '2024-11-12', 0)": 0, + "(5, '2024-11-12', 1)": 0, + "(5, '2024-11-12', 2)": 0, + "(5, '2024-11-12', 3)": 0, + "(5, '2024-11-12', 4)": 0, + "(5, '2024-11-12', 5)": 0, + "(5, '2024-11-12', 6)": 0, + "(5, '2024-11-12', 7)": 0, + "(5, '2024-11-13', 0)": 0, + "(5, '2024-11-13', 1)": 0, + "(5, '2024-11-13', 2)": 0, + "(5, '2024-11-13', 3)": 0, + "(5, '2024-11-13', 4)": 0, + "(5, '2024-11-13', 5)": 0, + "(5, '2024-11-13', 6)": 0, + "(5, '2024-11-13', 7)": 0, + "(5, '2024-11-14', 0)": 0, + "(5, '2024-11-14', 1)": 0, + "(5, '2024-11-14', 2)": 0, + "(5, '2024-11-14', 3)": 0, + "(5, '2024-11-14', 4)": 0, + "(5, '2024-11-14', 5)": 0, + "(5, '2024-11-14', 6)": 0, + "(5, '2024-11-14', 7)": 0, + "(5, '2024-11-15', 0)": 0, + "(5, '2024-11-15', 1)": 0, + "(5, '2024-11-15', 2)": 0, + "(5, '2024-11-15', 3)": 0, + "(5, '2024-11-15', 4)": 0, + "(5, '2024-11-15', 5)": 0, + "(5, '2024-11-15', 6)": 0, + "(5, '2024-11-15', 7)": 0, + "(5, '2024-11-16', 0)": 0, + "(5, '2024-11-16', 1)": 0, + "(5, '2024-11-16', 2)": 0, + "(5, '2024-11-16', 3)": 0, + "(5, '2024-11-16', 4)": 0, + "(5, '2024-11-16', 5)": 0, + "(5, '2024-11-16', 6)": 0, + "(5, '2024-11-16', 7)": 0, + "(5, '2024-11-17', 0)": 0, + "(5, '2024-11-17', 1)": 0, + "(5, '2024-11-17', 2)": 0, + "(5, '2024-11-17', 3)": 0, + "(5, '2024-11-17', 4)": 0, + "(5, '2024-11-17', 5)": 0, + "(5, '2024-11-17', 6)": 0, + "(5, '2024-11-17', 7)": 0, + "(5, '2024-11-18', 0)": 0, + "(5, '2024-11-18', 1)": 0, + "(5, '2024-11-18', 2)": 0, + "(5, '2024-11-18', 3)": 0, + "(5, '2024-11-18', 4)": 0, + "(5, '2024-11-18', 5)": 0, + "(5, '2024-11-18', 6)": 0, + "(5, '2024-11-18', 7)": 0, + "(5, '2024-11-19', 0)": 0, + "(5, '2024-11-19', 1)": 0, + "(5, '2024-11-19', 2)": 0, + "(5, '2024-11-19', 3)": 0, + "(5, '2024-11-19', 4)": 0, + "(5, '2024-11-19', 5)": 0, + "(5, '2024-11-19', 6)": 0, + "(5, '2024-11-19', 7)": 0, + "(5, '2024-11-20', 0)": 0, + "(5, '2024-11-20', 1)": 0, + "(5, '2024-11-20', 2)": 0, + "(5, '2024-11-20', 3)": 0, + "(5, '2024-11-20', 4)": 0, + "(5, '2024-11-20', 5)": 0, + "(5, '2024-11-20', 6)": 0, + "(5, '2024-11-20', 7)": 0, + "(5, '2024-11-21', 0)": 0, + "(5, '2024-11-21', 1)": 0, + "(5, '2024-11-21', 2)": 0, + "(5, '2024-11-21', 3)": 0, + "(5, '2024-11-21', 4)": 0, + "(5, '2024-11-21', 5)": 0, + "(5, '2024-11-21', 6)": 0, + "(5, '2024-11-21', 7)": 0, + "(5, '2024-11-22', 0)": 0, + "(5, '2024-11-22', 1)": 0, + "(5, '2024-11-22', 2)": 0, + "(5, '2024-11-22', 3)": 0, + "(5, '2024-11-22', 4)": 0, + "(5, '2024-11-22', 5)": 0, + "(5, '2024-11-22', 6)": 0, + "(5, '2024-11-22', 7)": 0, + "(5, '2024-11-23', 0)": 0, + "(5, '2024-11-23', 1)": 0, + "(5, '2024-11-23', 2)": 0, + "(5, '2024-11-23', 3)": 0, + "(5, '2024-11-23', 4)": 0, + "(5, '2024-11-23', 5)": 0, + "(5, '2024-11-23', 6)": 0, + "(5, '2024-11-23', 7)": 0, + "(5, '2024-11-24', 0)": 0, + "(5, '2024-11-24', 1)": 0, + "(5, '2024-11-24', 2)": 0, + "(5, '2024-11-24', 3)": 0, + "(5, '2024-11-24', 4)": 0, + "(5, '2024-11-24', 5)": 0, + "(5, '2024-11-24', 6)": 0, + "(5, '2024-11-24', 7)": 0, + "(5, '2024-11-25', 0)": 0, + "(5, '2024-11-25', 1)": 0, + "(5, '2024-11-25', 2)": 0, + "(5, '2024-11-25', 3)": 0, + "(5, '2024-11-25', 4)": 0, + "(5, '2024-11-25', 5)": 0, + "(5, '2024-11-25', 6)": 0, + "(5, '2024-11-25', 7)": 0, + "(5, '2024-11-26', 0)": 0, + "(5, '2024-11-26', 1)": 0, + "(5, '2024-11-26', 2)": 0, + "(5, '2024-11-26', 3)": 0, + "(5, '2024-11-26', 4)": 0, + "(5, '2024-11-26', 5)": 0, + "(5, '2024-11-26', 6)": 0, + "(5, '2024-11-26', 7)": 0, + "(5, '2024-11-27', 0)": 0, + "(5, '2024-11-27', 1)": 0, + "(5, '2024-11-27', 2)": 0, + "(5, '2024-11-27', 3)": 0, + "(5, '2024-11-27', 4)": 0, + "(5, '2024-11-27', 5)": 0, + "(5, '2024-11-27', 6)": 0, + "(5, '2024-11-27', 7)": 0, + "(5, '2024-11-28', 0)": 0, + "(5, '2024-11-28', 1)": 0, + "(5, '2024-11-28', 2)": 0, + "(5, '2024-11-28', 3)": 0, + "(5, '2024-11-28', 4)": 0, + "(5, '2024-11-28', 5)": 0, + "(5, '2024-11-28', 6)": 0, + "(5, '2024-11-28', 7)": 0, + "(5, '2024-11-29', 0)": 0, + "(5, '2024-11-29', 1)": 0, + "(5, '2024-11-29', 2)": 0, + "(5, '2024-11-29', 3)": 0, + "(5, '2024-11-29', 4)": 0, + "(5, '2024-11-29', 5)": 0, + "(5, '2024-11-29', 6)": 0, + "(5, '2024-11-29', 7)": 0, + "(5, '2024-11-30', 0)": 0, + "(5, '2024-11-30', 1)": 0, + "(5, '2024-11-30', 2)": 0, + "(5, '2024-11-30', 3)": 0, + "(5, '2024-11-30', 4)": 0, + "(5, '2024-11-30', 5)": 0, + "(5, '2024-11-30', 6)": 0, + "(5, '2024-11-30', 7)": 0, + "(5367, '2024-11-01', 0)": 0, + "(5367, '2024-11-01', 1)": 0, + "(5367, '2024-11-01', 2)": 1, + "(5367, '2024-11-01', 3)": 0, + "(5367, '2024-11-01', 4)": 0, + "(5367, '2024-11-01', 5)": 0, + "(5367, '2024-11-01', 6)": 0, + "(5367, '2024-11-01', 7)": 0, + "(5367, '2024-11-02', 0)": 0, + "(5367, '2024-11-02', 1)": 0, + "(5367, '2024-11-02', 2)": 1, + "(5367, '2024-11-02', 3)": 0, + "(5367, '2024-11-02', 4)": 0, + "(5367, '2024-11-02', 5)": 0, + "(5367, '2024-11-02', 6)": 0, + "(5367, '2024-11-02', 7)": 0, + "(5367, '2024-11-03', 0)": 0, + "(5367, '2024-11-03', 1)": 0, + "(5367, '2024-11-03', 2)": 1, + "(5367, '2024-11-03', 3)": 0, + "(5367, '2024-11-03', 4)": 0, + "(5367, '2024-11-03', 5)": 0, + "(5367, '2024-11-03', 6)": 0, + "(5367, '2024-11-03', 7)": 0, + "(5367, '2024-11-04', 0)": 0, + "(5367, '2024-11-04', 1)": 0, + "(5367, '2024-11-04', 2)": 0, + "(5367, '2024-11-04', 3)": 0, + "(5367, '2024-11-04', 4)": 0, + "(5367, '2024-11-04', 5)": 0, + "(5367, '2024-11-04', 6)": 0, + "(5367, '2024-11-04', 7)": 0, + "(5367, '2024-11-05', 0)": 0, + "(5367, '2024-11-05', 1)": 0, + "(5367, '2024-11-05', 2)": 0, + "(5367, '2024-11-05', 3)": 0, + "(5367, '2024-11-05', 4)": 0, + "(5367, '2024-11-05', 5)": 0, + "(5367, '2024-11-05', 6)": 0, + "(5367, '2024-11-05', 7)": 0, + "(5367, '2024-11-06', 0)": 0, + "(5367, '2024-11-06', 1)": 0, + "(5367, '2024-11-06', 2)": 1, + "(5367, '2024-11-06', 3)": 0, + "(5367, '2024-11-06', 4)": 0, + "(5367, '2024-11-06', 5)": 0, + "(5367, '2024-11-06', 6)": 0, + "(5367, '2024-11-06', 7)": 0, + "(5367, '2024-11-07', 0)": 0, + "(5367, '2024-11-07', 1)": 0, + "(5367, '2024-11-07', 2)": 0, + "(5367, '2024-11-07', 3)": 0, + "(5367, '2024-11-07', 4)": 0, + "(5367, '2024-11-07', 5)": 0, + "(5367, '2024-11-07', 6)": 0, + "(5367, '2024-11-07', 7)": 0, + "(5367, '2024-11-08', 0)": 0, + "(5367, '2024-11-08', 1)": 0, + "(5367, '2024-11-08', 2)": 0, + "(5367, '2024-11-08', 3)": 0, + "(5367, '2024-11-08', 4)": 0, + "(5367, '2024-11-08', 5)": 0, + "(5367, '2024-11-08', 6)": 0, + "(5367, '2024-11-08', 7)": 0, + "(5367, '2024-11-09', 0)": 1, + "(5367, '2024-11-09', 1)": 0, + "(5367, '2024-11-09', 2)": 0, + "(5367, '2024-11-09', 3)": 0, + "(5367, '2024-11-09', 4)": 0, + "(5367, '2024-11-09', 5)": 0, + "(5367, '2024-11-09', 6)": 0, + "(5367, '2024-11-09', 7)": 0, + "(5367, '2024-11-10', 0)": 0, + "(5367, '2024-11-10', 1)": 0, + "(5367, '2024-11-10', 2)": 0, + "(5367, '2024-11-10', 3)": 0, + "(5367, '2024-11-10', 4)": 0, + "(5367, '2024-11-10', 5)": 0, + "(5367, '2024-11-10', 6)": 0, + "(5367, '2024-11-10', 7)": 0, + "(5367, '2024-11-11', 0)": 1, + "(5367, '2024-11-11', 1)": 0, + "(5367, '2024-11-11', 2)": 0, + "(5367, '2024-11-11', 3)": 0, + "(5367, '2024-11-11', 4)": 0, + "(5367, '2024-11-11', 5)": 0, + "(5367, '2024-11-11', 6)": 0, + "(5367, '2024-11-11', 7)": 0, + "(5367, '2024-11-12', 0)": 0, + "(5367, '2024-11-12', 1)": 0, + "(5367, '2024-11-12', 2)": 1, + "(5367, '2024-11-12', 3)": 0, + "(5367, '2024-11-12', 4)": 0, + "(5367, '2024-11-12', 5)": 0, + "(5367, '2024-11-12', 6)": 0, + "(5367, '2024-11-12', 7)": 0, + "(5367, '2024-11-13', 0)": 0, + "(5367, '2024-11-13', 1)": 0, + "(5367, '2024-11-13', 2)": 0, + "(5367, '2024-11-13', 3)": 0, + "(5367, '2024-11-13', 4)": 0, + "(5367, '2024-11-13', 5)": 0, + "(5367, '2024-11-13', 6)": 0, + "(5367, '2024-11-13', 7)": 0, + "(5367, '2024-11-14', 0)": 1, + "(5367, '2024-11-14', 1)": 0, + "(5367, '2024-11-14', 2)": 0, + "(5367, '2024-11-14', 3)": 0, + "(5367, '2024-11-14', 4)": 0, + "(5367, '2024-11-14', 5)": 0, + "(5367, '2024-11-14', 6)": 0, + "(5367, '2024-11-14', 7)": 0, + "(5367, '2024-11-15', 0)": 0, + "(5367, '2024-11-15', 1)": 0, + "(5367, '2024-11-15', 2)": 1, + "(5367, '2024-11-15', 3)": 0, + "(5367, '2024-11-15', 4)": 0, + "(5367, '2024-11-15', 5)": 0, + "(5367, '2024-11-15', 6)": 0, + "(5367, '2024-11-15', 7)": 0, + "(5367, '2024-11-16', 0)": 0, + "(5367, '2024-11-16', 1)": 0, + "(5367, '2024-11-16', 2)": 0, + "(5367, '2024-11-16', 3)": 1, + "(5367, '2024-11-16', 4)": 0, + "(5367, '2024-11-16', 5)": 0, + "(5367, '2024-11-16', 6)": 0, + "(5367, '2024-11-16', 7)": 0, + "(5367, '2024-11-17', 0)": 0, + "(5367, '2024-11-17', 1)": 0, + "(5367, '2024-11-17', 2)": 0, + "(5367, '2024-11-17', 3)": 0, + "(5367, '2024-11-17', 4)": 0, + "(5367, '2024-11-17', 5)": 0, + "(5367, '2024-11-17', 6)": 0, + "(5367, '2024-11-17', 7)": 0, + "(5367, '2024-11-18', 0)": 0, + "(5367, '2024-11-18', 1)": 0, + "(5367, '2024-11-18', 2)": 1, + "(5367, '2024-11-18', 3)": 0, + "(5367, '2024-11-18', 4)": 0, + "(5367, '2024-11-18', 5)": 0, + "(5367, '2024-11-18', 6)": 0, + "(5367, '2024-11-18', 7)": 0, + "(5367, '2024-11-19', 0)": 0, + "(5367, '2024-11-19', 1)": 0, + "(5367, '2024-11-19', 2)": 0, + "(5367, '2024-11-19', 3)": 0, + "(5367, '2024-11-19', 4)": 0, + "(5367, '2024-11-19', 5)": 0, + "(5367, '2024-11-19', 6)": 0, + "(5367, '2024-11-19', 7)": 0, + "(5367, '2024-11-20', 0)": 1, + "(5367, '2024-11-20', 1)": 0, + "(5367, '2024-11-20', 2)": 0, + "(5367, '2024-11-20', 3)": 0, + "(5367, '2024-11-20', 4)": 0, + "(5367, '2024-11-20', 5)": 0, + "(5367, '2024-11-20', 6)": 0, + "(5367, '2024-11-20', 7)": 0, + "(5367, '2024-11-21', 0)": 1, + "(5367, '2024-11-21', 1)": 0, + "(5367, '2024-11-21', 2)": 0, + "(5367, '2024-11-21', 3)": 0, + "(5367, '2024-11-21', 4)": 0, + "(5367, '2024-11-21', 5)": 0, + "(5367, '2024-11-21', 6)": 0, + "(5367, '2024-11-21', 7)": 0, + "(5367, '2024-11-22', 0)": 1, + "(5367, '2024-11-22', 1)": 0, + "(5367, '2024-11-22', 2)": 0, + "(5367, '2024-11-22', 3)": 0, + "(5367, '2024-11-22', 4)": 0, + "(5367, '2024-11-22', 5)": 0, + "(5367, '2024-11-22', 6)": 0, + "(5367, '2024-11-22', 7)": 0, + "(5367, '2024-11-23', 0)": 0, + "(5367, '2024-11-23', 1)": 0, + "(5367, '2024-11-23', 2)": 0, + "(5367, '2024-11-23', 3)": 0, + "(5367, '2024-11-23', 4)": 0, + "(5367, '2024-11-23', 5)": 0, + "(5367, '2024-11-23', 6)": 0, + "(5367, '2024-11-23', 7)": 0, + "(5367, '2024-11-24', 0)": 1, + "(5367, '2024-11-24', 1)": 0, + "(5367, '2024-11-24', 2)": 0, + "(5367, '2024-11-24', 3)": 0, + "(5367, '2024-11-24', 4)": 0, + "(5367, '2024-11-24', 5)": 0, + "(5367, '2024-11-24', 6)": 0, + "(5367, '2024-11-24', 7)": 0, + "(5367, '2024-11-25', 0)": 1, + "(5367, '2024-11-25', 1)": 0, + "(5367, '2024-11-25', 2)": 0, + "(5367, '2024-11-25', 3)": 0, + "(5367, '2024-11-25', 4)": 0, + "(5367, '2024-11-25', 5)": 0, + "(5367, '2024-11-25', 6)": 0, + "(5367, '2024-11-25', 7)": 0, + "(5367, '2024-11-26', 0)": 0, + "(5367, '2024-11-26', 1)": 0, + "(5367, '2024-11-26', 2)": 0, + "(5367, '2024-11-26', 3)": 0, + "(5367, '2024-11-26', 4)": 0, + "(5367, '2024-11-26', 5)": 0, + "(5367, '2024-11-26', 6)": 0, + "(5367, '2024-11-26', 7)": 0, + "(5367, '2024-11-27', 0)": 1, + "(5367, '2024-11-27', 1)": 0, + "(5367, '2024-11-27', 2)": 0, + "(5367, '2024-11-27', 3)": 0, + "(5367, '2024-11-27', 4)": 0, + "(5367, '2024-11-27', 5)": 0, + "(5367, '2024-11-27', 6)": 0, + "(5367, '2024-11-27', 7)": 0, + "(5367, '2024-11-28', 0)": 1, + "(5367, '2024-11-28', 1)": 0, + "(5367, '2024-11-28', 2)": 0, + "(5367, '2024-11-28', 3)": 0, + "(5367, '2024-11-28', 4)": 0, + "(5367, '2024-11-28', 5)": 0, + "(5367, '2024-11-28', 6)": 0, + "(5367, '2024-11-28', 7)": 0, + "(5367, '2024-11-29', 0)": 1, + "(5367, '2024-11-29', 1)": 0, + "(5367, '2024-11-29', 2)": 0, + "(5367, '2024-11-29', 3)": 0, + "(5367, '2024-11-29', 4)": 0, + "(5367, '2024-11-29', 5)": 0, + "(5367, '2024-11-29', 6)": 0, + "(5367, '2024-11-29', 7)": 0, + "(5367, '2024-11-30', 0)": 0, + "(5367, '2024-11-30', 1)": 0, + "(5367, '2024-11-30', 2)": 0, + "(5367, '2024-11-30', 3)": 0, + "(5367, '2024-11-30', 4)": 0, + "(5367, '2024-11-30', 5)": 0, + "(5367, '2024-11-30', 6)": 0, + "(5367, '2024-11-30', 7)": 0, + "(5920, '2024-11-01', 0)": 0, + "(5920, '2024-11-01', 1)": 1, + "(5920, '2024-11-01', 2)": 0, + "(5920, '2024-11-01', 3)": 0, + "(5920, '2024-11-01', 4)": 0, + "(5920, '2024-11-01', 5)": 0, + "(5920, '2024-11-01', 6)": 0, + "(5920, '2024-11-01', 7)": 0, + "(5920, '2024-11-02', 0)": 0, + "(5920, '2024-11-02', 1)": 0, + "(5920, '2024-11-02', 2)": 0, + "(5920, '2024-11-02', 3)": 0, + "(5920, '2024-11-02', 4)": 0, + "(5920, '2024-11-02', 5)": 0, + "(5920, '2024-11-02', 6)": 0, + "(5920, '2024-11-02', 7)": 0, + "(5920, '2024-11-03', 0)": 0, + "(5920, '2024-11-03', 1)": 0, + "(5920, '2024-11-03', 2)": 0, + "(5920, '2024-11-03', 3)": 0, + "(5920, '2024-11-03', 4)": 0, + "(5920, '2024-11-03', 5)": 0, + "(5920, '2024-11-03', 6)": 0, + "(5920, '2024-11-03', 7)": 0, + "(5920, '2024-11-04', 0)": 0, + "(5920, '2024-11-04', 1)": 0, + "(5920, '2024-11-04', 2)": 0, + "(5920, '2024-11-04', 3)": 0, + "(5920, '2024-11-04', 4)": 0, + "(5920, '2024-11-04', 5)": 0, + "(5920, '2024-11-04', 6)": 0, + "(5920, '2024-11-04', 7)": 0, + "(5920, '2024-11-05', 0)": 0, + "(5920, '2024-11-05', 1)": 1, + "(5920, '2024-11-05', 2)": 0, + "(5920, '2024-11-05', 3)": 0, + "(5920, '2024-11-05', 4)": 0, + "(5920, '2024-11-05', 5)": 0, + "(5920, '2024-11-05', 6)": 0, + "(5920, '2024-11-05', 7)": 0, + "(5920, '2024-11-06', 0)": 0, + "(5920, '2024-11-06', 1)": 0, + "(5920, '2024-11-06', 2)": 0, + "(5920, '2024-11-06', 3)": 1, + "(5920, '2024-11-06', 4)": 0, + "(5920, '2024-11-06', 5)": 0, + "(5920, '2024-11-06', 6)": 0, + "(5920, '2024-11-06', 7)": 0, + "(5920, '2024-11-07', 0)": 0, + "(5920, '2024-11-07', 1)": 0, + "(5920, '2024-11-07', 2)": 0, + "(5920, '2024-11-07', 3)": 1, + "(5920, '2024-11-07', 4)": 0, + "(5920, '2024-11-07', 5)": 0, + "(5920, '2024-11-07', 6)": 0, + "(5920, '2024-11-07', 7)": 0, + "(5920, '2024-11-08', 0)": 0, + "(5920, '2024-11-08', 1)": 0, + "(5920, '2024-11-08', 2)": 0, + "(5920, '2024-11-08', 3)": 1, + "(5920, '2024-11-08', 4)": 0, + "(5920, '2024-11-08', 5)": 0, + "(5920, '2024-11-08', 6)": 0, + "(5920, '2024-11-08', 7)": 0, + "(5920, '2024-11-09', 0)": 0, + "(5920, '2024-11-09', 1)": 0, + "(5920, '2024-11-09', 2)": 0, + "(5920, '2024-11-09', 3)": 0, + "(5920, '2024-11-09', 4)": 0, + "(5920, '2024-11-09', 5)": 0, + "(5920, '2024-11-09', 6)": 0, + "(5920, '2024-11-09', 7)": 0, + "(5920, '2024-11-10', 0)": 0, + "(5920, '2024-11-10', 1)": 0, + "(5920, '2024-11-10', 2)": 1, + "(5920, '2024-11-10', 3)": 0, + "(5920, '2024-11-10', 4)": 0, + "(5920, '2024-11-10', 5)": 0, + "(5920, '2024-11-10', 6)": 0, + "(5920, '2024-11-10', 7)": 0, + "(5920, '2024-11-11', 0)": 0, + "(5920, '2024-11-11', 1)": 0, + "(5920, '2024-11-11', 2)": 1, + "(5920, '2024-11-11', 3)": 0, + "(5920, '2024-11-11', 4)": 0, + "(5920, '2024-11-11', 5)": 0, + "(5920, '2024-11-11', 6)": 0, + "(5920, '2024-11-11', 7)": 0, + "(5920, '2024-11-12', 0)": 0, + "(5920, '2024-11-12', 1)": 0, + "(5920, '2024-11-12', 2)": 0, + "(5920, '2024-11-12', 3)": 0, + "(5920, '2024-11-12', 4)": 0, + "(5920, '2024-11-12', 5)": 0, + "(5920, '2024-11-12', 6)": 0, + "(5920, '2024-11-12', 7)": 0, + "(5920, '2024-11-13', 0)": 1, + "(5920, '2024-11-13', 1)": 0, + "(5920, '2024-11-13', 2)": 0, + "(5920, '2024-11-13', 3)": 0, + "(5920, '2024-11-13', 4)": 0, + "(5920, '2024-11-13', 5)": 0, + "(5920, '2024-11-13', 6)": 0, + "(5920, '2024-11-13', 7)": 0, + "(5920, '2024-11-14', 0)": 0, + "(5920, '2024-11-14', 1)": 0, + "(5920, '2024-11-14', 2)": 0, + "(5920, '2024-11-14', 3)": 1, + "(5920, '2024-11-14', 4)": 0, + "(5920, '2024-11-14', 5)": 0, + "(5920, '2024-11-14', 6)": 0, + "(5920, '2024-11-14', 7)": 0, + "(5920, '2024-11-15', 0)": 0, + "(5920, '2024-11-15', 1)": 0, + "(5920, '2024-11-15', 2)": 0, + "(5920, '2024-11-15', 3)": 0, + "(5920, '2024-11-15', 4)": 0, + "(5920, '2024-11-15', 5)": 0, + "(5920, '2024-11-15', 6)": 0, + "(5920, '2024-11-15', 7)": 0, + "(5920, '2024-11-16', 0)": 1, + "(5920, '2024-11-16', 1)": 0, + "(5920, '2024-11-16', 2)": 0, + "(5920, '2024-11-16', 3)": 0, + "(5920, '2024-11-16', 4)": 0, + "(5920, '2024-11-16', 5)": 0, + "(5920, '2024-11-16', 6)": 0, + "(5920, '2024-11-16', 7)": 0, + "(5920, '2024-11-17', 0)": 0, + "(5920, '2024-11-17', 1)": 0, + "(5920, '2024-11-17', 2)": 1, + "(5920, '2024-11-17', 3)": 0, + "(5920, '2024-11-17', 4)": 0, + "(5920, '2024-11-17', 5)": 0, + "(5920, '2024-11-17', 6)": 0, + "(5920, '2024-11-17', 7)": 0, + "(5920, '2024-11-18', 0)": 0, + "(5920, '2024-11-18', 1)": 0, + "(5920, '2024-11-18', 2)": 0, + "(5920, '2024-11-18', 3)": 0, + "(5920, '2024-11-18', 4)": 0, + "(5920, '2024-11-18', 5)": 0, + "(5920, '2024-11-18', 6)": 0, + "(5920, '2024-11-18', 7)": 0, + "(5920, '2024-11-19', 0)": 0, + "(5920, '2024-11-19', 1)": 0, + "(5920, '2024-11-19', 2)": 1, + "(5920, '2024-11-19', 3)": 0, + "(5920, '2024-11-19', 4)": 0, + "(5920, '2024-11-19', 5)": 0, + "(5920, '2024-11-19', 6)": 0, + "(5920, '2024-11-19', 7)": 0, + "(5920, '2024-11-20', 0)": 0, + "(5920, '2024-11-20', 1)": 0, + "(5920, '2024-11-20', 2)": 0, + "(5920, '2024-11-20', 3)": 0, + "(5920, '2024-11-20', 4)": 0, + "(5920, '2024-11-20', 5)": 0, + "(5920, '2024-11-20', 6)": 0, + "(5920, '2024-11-20', 7)": 0, + "(5920, '2024-11-21', 0)": 0, + "(5920, '2024-11-21', 1)": 0, + "(5920, '2024-11-21', 2)": 0, + "(5920, '2024-11-21', 3)": 0, + "(5920, '2024-11-21', 4)": 0, + "(5920, '2024-11-21', 5)": 0, + "(5920, '2024-11-21', 6)": 0, + "(5920, '2024-11-21', 7)": 0, + "(5920, '2024-11-22', 0)": 0, + "(5920, '2024-11-22', 1)": 0, + "(5920, '2024-11-22', 2)": 1, + "(5920, '2024-11-22', 3)": 0, + "(5920, '2024-11-22', 4)": 0, + "(5920, '2024-11-22', 5)": 0, + "(5920, '2024-11-22', 6)": 0, + "(5920, '2024-11-22', 7)": 0, + "(5920, '2024-11-23', 0)": 0, + "(5920, '2024-11-23', 1)": 1, + "(5920, '2024-11-23', 2)": 0, + "(5920, '2024-11-23', 3)": 0, + "(5920, '2024-11-23', 4)": 0, + "(5920, '2024-11-23', 5)": 0, + "(5920, '2024-11-23', 6)": 0, + "(5920, '2024-11-23', 7)": 0, + "(5920, '2024-11-24', 0)": 0, + "(5920, '2024-11-24', 1)": 0, + "(5920, '2024-11-24', 2)": 0, + "(5920, '2024-11-24', 3)": 0, + "(5920, '2024-11-24', 4)": 0, + "(5920, '2024-11-24', 5)": 0, + "(5920, '2024-11-24', 6)": 0, + "(5920, '2024-11-24', 7)": 0, + "(5920, '2024-11-25', 0)": 0, + "(5920, '2024-11-25', 1)": 0, + "(5920, '2024-11-25', 2)": 0, + "(5920, '2024-11-25', 3)": 1, + "(5920, '2024-11-25', 4)": 0, + "(5920, '2024-11-25', 5)": 0, + "(5920, '2024-11-25', 6)": 0, + "(5920, '2024-11-25', 7)": 0, + "(5920, '2024-11-26', 0)": 0, + "(5920, '2024-11-26', 1)": 0, + "(5920, '2024-11-26', 2)": 0, + "(5920, '2024-11-26', 3)": 0, + "(5920, '2024-11-26', 4)": 0, + "(5920, '2024-11-26', 5)": 0, + "(5920, '2024-11-26', 6)": 0, + "(5920, '2024-11-26', 7)": 0, + "(5920, '2024-11-27', 0)": 1, + "(5920, '2024-11-27', 1)": 0, + "(5920, '2024-11-27', 2)": 0, + "(5920, '2024-11-27', 3)": 0, + "(5920, '2024-11-27', 4)": 0, + "(5920, '2024-11-27', 5)": 0, + "(5920, '2024-11-27', 6)": 0, + "(5920, '2024-11-27', 7)": 0, + "(5920, '2024-11-28', 0)": 0, + "(5920, '2024-11-28', 1)": 1, + "(5920, '2024-11-28', 2)": 0, + "(5920, '2024-11-28', 3)": 0, + "(5920, '2024-11-28', 4)": 0, + "(5920, '2024-11-28', 5)": 0, + "(5920, '2024-11-28', 6)": 0, + "(5920, '2024-11-28', 7)": 0, + "(5920, '2024-11-29', 0)": 0, + "(5920, '2024-11-29', 1)": 0, + "(5920, '2024-11-29', 2)": 0, + "(5920, '2024-11-29', 3)": 1, + "(5920, '2024-11-29', 4)": 0, + "(5920, '2024-11-29', 5)": 0, + "(5920, '2024-11-29', 6)": 0, + "(5920, '2024-11-29', 7)": 0, + "(5920, '2024-11-30', 0)": 0, + "(5920, '2024-11-30', 1)": 0, + "(5920, '2024-11-30', 2)": 0, + "(5920, '2024-11-30', 3)": 0, + "(5920, '2024-11-30', 4)": 0, + "(5920, '2024-11-30', 5)": 0, + "(5920, '2024-11-30', 6)": 0, + "(5920, '2024-11-30', 7)": 0, + "(6, '2024-11-01', 0)": 1, + "(6, '2024-11-01', 1)": 0, + "(6, '2024-11-01', 2)": 0, + "(6, '2024-11-01', 3)": 0, + "(6, '2024-11-01', 4)": 0, + "(6, '2024-11-01', 5)": 0, + "(6, '2024-11-01', 6)": 0, + "(6, '2024-11-01', 7)": 0, + "(6, '2024-11-02', 0)": 0, + "(6, '2024-11-02', 1)": 0, + "(6, '2024-11-02', 2)": 0, + "(6, '2024-11-02', 3)": 0, + "(6, '2024-11-02', 4)": 0, + "(6, '2024-11-02', 5)": 0, + "(6, '2024-11-02', 6)": 0, + "(6, '2024-11-02', 7)": 0, + "(6, '2024-11-03', 0)": 0, + "(6, '2024-11-03', 1)": 0, + "(6, '2024-11-03', 2)": 0, + "(6, '2024-11-03', 3)": 0, + "(6, '2024-11-03', 4)": 0, + "(6, '2024-11-03', 5)": 0, + "(6, '2024-11-03', 6)": 0, + "(6, '2024-11-03', 7)": 0, + "(6, '2024-11-04', 0)": 0, + "(6, '2024-11-04', 1)": 0, + "(6, '2024-11-04', 2)": 0, + "(6, '2024-11-04', 3)": 0, + "(6, '2024-11-04', 4)": 0, + "(6, '2024-11-04', 5)": 0, + "(6, '2024-11-04', 6)": 0, + "(6, '2024-11-04', 7)": 0, + "(6, '2024-11-05', 0)": 0, + "(6, '2024-11-05', 1)": 0, + "(6, '2024-11-05', 2)": 0, + "(6, '2024-11-05', 3)": 0, + "(6, '2024-11-05', 4)": 0, + "(6, '2024-11-05', 5)": 0, + "(6, '2024-11-05', 6)": 0, + "(6, '2024-11-05', 7)": 0, + "(6, '2024-11-06', 0)": 1, + "(6, '2024-11-06', 1)": 0, + "(6, '2024-11-06', 2)": 0, + "(6, '2024-11-06', 3)": 0, + "(6, '2024-11-06', 4)": 0, + "(6, '2024-11-06', 5)": 0, + "(6, '2024-11-06', 6)": 0, + "(6, '2024-11-06', 7)": 0, + "(6, '2024-11-07', 0)": 0, + "(6, '2024-11-07', 1)": 0, + "(6, '2024-11-07', 2)": 0, + "(6, '2024-11-07', 3)": 0, + "(6, '2024-11-07', 4)": 0, + "(6, '2024-11-07', 5)": 0, + "(6, '2024-11-07', 6)": 0, + "(6, '2024-11-07', 7)": 0, + "(6, '2024-11-08', 0)": 1, + "(6, '2024-11-08', 1)": 0, + "(6, '2024-11-08', 2)": 0, + "(6, '2024-11-08', 3)": 0, + "(6, '2024-11-08', 4)": 0, + "(6, '2024-11-08', 5)": 0, + "(6, '2024-11-08', 6)": 0, + "(6, '2024-11-08', 7)": 0, + "(6, '2024-11-09', 0)": 0, + "(6, '2024-11-09', 1)": 0, + "(6, '2024-11-09', 2)": 0, + "(6, '2024-11-09', 3)": 0, + "(6, '2024-11-09', 4)": 0, + "(6, '2024-11-09', 5)": 0, + "(6, '2024-11-09', 6)": 0, + "(6, '2024-11-09', 7)": 0, + "(6, '2024-11-10', 0)": 1, + "(6, '2024-11-10', 1)": 0, + "(6, '2024-11-10', 2)": 0, + "(6, '2024-11-10', 3)": 0, + "(6, '2024-11-10', 4)": 0, + "(6, '2024-11-10', 5)": 0, + "(6, '2024-11-10', 6)": 0, + "(6, '2024-11-10', 7)": 0, + "(6, '2024-11-11', 0)": 0, + "(6, '2024-11-11', 1)": 0, + "(6, '2024-11-11', 2)": 0, + "(6, '2024-11-11', 3)": 0, + "(6, '2024-11-11', 4)": 0, + "(6, '2024-11-11', 5)": 0, + "(6, '2024-11-11', 6)": 0, + "(6, '2024-11-11', 7)": 0, + "(6, '2024-11-12', 0)": 0, + "(6, '2024-11-12', 1)": 0, + "(6, '2024-11-12', 2)": 0, + "(6, '2024-11-12', 3)": 0, + "(6, '2024-11-12', 4)": 0, + "(6, '2024-11-12', 5)": 0, + "(6, '2024-11-12', 6)": 0, + "(6, '2024-11-12', 7)": 0, + "(6, '2024-11-13', 0)": 0, + "(6, '2024-11-13', 1)": 0, + "(6, '2024-11-13', 2)": 0, + "(6, '2024-11-13', 3)": 0, + "(6, '2024-11-13', 4)": 0, + "(6, '2024-11-13', 5)": 0, + "(6, '2024-11-13', 6)": 0, + "(6, '2024-11-13', 7)": 0, + "(6, '2024-11-14', 0)": 1, + "(6, '2024-11-14', 1)": 0, + "(6, '2024-11-14', 2)": 0, + "(6, '2024-11-14', 3)": 0, + "(6, '2024-11-14', 4)": 0, + "(6, '2024-11-14', 5)": 0, + "(6, '2024-11-14', 6)": 0, + "(6, '2024-11-14', 7)": 0, + "(6, '2024-11-15', 0)": 0, + "(6, '2024-11-15', 1)": 0, + "(6, '2024-11-15', 2)": 0, + "(6, '2024-11-15', 3)": 0, + "(6, '2024-11-15', 4)": 0, + "(6, '2024-11-15', 5)": 0, + "(6, '2024-11-15', 6)": 0, + "(6, '2024-11-15', 7)": 0, + "(6, '2024-11-16', 0)": 1, + "(6, '2024-11-16', 1)": 0, + "(6, '2024-11-16', 2)": 0, + "(6, '2024-11-16', 3)": 0, + "(6, '2024-11-16', 4)": 0, + "(6, '2024-11-16', 5)": 0, + "(6, '2024-11-16', 6)": 0, + "(6, '2024-11-16', 7)": 0, + "(6, '2024-11-17', 0)": 0, + "(6, '2024-11-17', 1)": 0, + "(6, '2024-11-17', 2)": 0, + "(6, '2024-11-17', 3)": 1, + "(6, '2024-11-17', 4)": 0, + "(6, '2024-11-17', 5)": 0, + "(6, '2024-11-17', 6)": 0, + "(6, '2024-11-17', 7)": 0, + "(6, '2024-11-18', 0)": 0, + "(6, '2024-11-18', 1)": 0, + "(6, '2024-11-18', 2)": 0, + "(6, '2024-11-18', 3)": 0, + "(6, '2024-11-18', 4)": 0, + "(6, '2024-11-18', 5)": 0, + "(6, '2024-11-18', 6)": 0, + "(6, '2024-11-18', 7)": 0, + "(6, '2024-11-19', 0)": 0, + "(6, '2024-11-19', 1)": 0, + "(6, '2024-11-19', 2)": 1, + "(6, '2024-11-19', 3)": 0, + "(6, '2024-11-19', 4)": 0, + "(6, '2024-11-19', 5)": 0, + "(6, '2024-11-19', 6)": 0, + "(6, '2024-11-19', 7)": 0, + "(6, '2024-11-20', 0)": 0, + "(6, '2024-11-20', 1)": 0, + "(6, '2024-11-20', 2)": 0, + "(6, '2024-11-20', 3)": 0, + "(6, '2024-11-20', 4)": 0, + "(6, '2024-11-20', 5)": 0, + "(6, '2024-11-20', 6)": 0, + "(6, '2024-11-20', 7)": 0, + "(6, '2024-11-21', 0)": 0, + "(6, '2024-11-21', 1)": 0, + "(6, '2024-11-21', 2)": 0, + "(6, '2024-11-21', 3)": 0, + "(6, '2024-11-21', 4)": 0, + "(6, '2024-11-21', 5)": 0, + "(6, '2024-11-21', 6)": 0, + "(6, '2024-11-21', 7)": 0, + "(6, '2024-11-22', 0)": 0, + "(6, '2024-11-22', 1)": 0, + "(6, '2024-11-22', 2)": 0, + "(6, '2024-11-22', 3)": 0, + "(6, '2024-11-22', 4)": 0, + "(6, '2024-11-22', 5)": 0, + "(6, '2024-11-22', 6)": 0, + "(6, '2024-11-22', 7)": 0, + "(6, '2024-11-23', 0)": 0, + "(6, '2024-11-23', 1)": 0, + "(6, '2024-11-23', 2)": 0, + "(6, '2024-11-23', 3)": 0, + "(6, '2024-11-23', 4)": 0, + "(6, '2024-11-23', 5)": 0, + "(6, '2024-11-23', 6)": 0, + "(6, '2024-11-23', 7)": 0, + "(6, '2024-11-24', 0)": 0, + "(6, '2024-11-24', 1)": 0, + "(6, '2024-11-24', 2)": 1, + "(6, '2024-11-24', 3)": 0, + "(6, '2024-11-24', 4)": 0, + "(6, '2024-11-24', 5)": 0, + "(6, '2024-11-24', 6)": 0, + "(6, '2024-11-24', 7)": 0, + "(6, '2024-11-25', 0)": 0, + "(6, '2024-11-25', 1)": 0, + "(6, '2024-11-25', 2)": 0, + "(6, '2024-11-25', 3)": 0, + "(6, '2024-11-25', 4)": 0, + "(6, '2024-11-25', 5)": 0, + "(6, '2024-11-25', 6)": 0, + "(6, '2024-11-25', 7)": 0, + "(6, '2024-11-26', 0)": 0, + "(6, '2024-11-26', 1)": 0, + "(6, '2024-11-26', 2)": 0, + "(6, '2024-11-26', 3)": 0, + "(6, '2024-11-26', 4)": 0, + "(6, '2024-11-26', 5)": 0, + "(6, '2024-11-26', 6)": 0, + "(6, '2024-11-26', 7)": 0, + "(6, '2024-11-27', 0)": 0, + "(6, '2024-11-27', 1)": 0, + "(6, '2024-11-27', 2)": 0, + "(6, '2024-11-27', 3)": 0, + "(6, '2024-11-27', 4)": 0, + "(6, '2024-11-27', 5)": 0, + "(6, '2024-11-27', 6)": 0, + "(6, '2024-11-27', 7)": 0, + "(6, '2024-11-28', 0)": 0, + "(6, '2024-11-28', 1)": 0, + "(6, '2024-11-28', 2)": 0, + "(6, '2024-11-28', 3)": 0, + "(6, '2024-11-28', 4)": 0, + "(6, '2024-11-28', 5)": 0, + "(6, '2024-11-28', 6)": 0, + "(6, '2024-11-28', 7)": 0, + "(6, '2024-11-29', 0)": 0, + "(6, '2024-11-29', 1)": 0, + "(6, '2024-11-29', 2)": 0, + "(6, '2024-11-29', 3)": 0, + "(6, '2024-11-29', 4)": 0, + "(6, '2024-11-29', 5)": 0, + "(6, '2024-11-29', 6)": 0, + "(6, '2024-11-29', 7)": 0, + "(6, '2024-11-30', 0)": 0, + "(6, '2024-11-30', 1)": 0, + "(6, '2024-11-30', 2)": 0, + "(6, '2024-11-30', 3)": 0, + "(6, '2024-11-30', 4)": 0, + "(6, '2024-11-30', 5)": 0, + "(6, '2024-11-30', 6)": 0, + "(6, '2024-11-30', 7)": 0, + "(6475, '2024-11-01', 0)": 0, + "(6475, '2024-11-01', 1)": 0, + "(6475, '2024-11-01', 2)": 0, + "(6475, '2024-11-01', 3)": 0, + "(6475, '2024-11-01', 4)": 0, + "(6475, '2024-11-01', 5)": 0, + "(6475, '2024-11-01', 6)": 0, + "(6475, '2024-11-01', 7)": 0, + "(6475, '2024-11-02', 0)": 0, + "(6475, '2024-11-02', 1)": 0, + "(6475, '2024-11-02', 2)": 0, + "(6475, '2024-11-02', 3)": 0, + "(6475, '2024-11-02', 4)": 0, + "(6475, '2024-11-02', 5)": 0, + "(6475, '2024-11-02', 6)": 0, + "(6475, '2024-11-02', 7)": 0, + "(6475, '2024-11-03', 0)": 0, + "(6475, '2024-11-03', 1)": 0, + "(6475, '2024-11-03', 2)": 0, + "(6475, '2024-11-03', 3)": 0, + "(6475, '2024-11-03', 4)": 0, + "(6475, '2024-11-03', 5)": 0, + "(6475, '2024-11-03', 6)": 0, + "(6475, '2024-11-03', 7)": 0, + "(6475, '2024-11-04', 0)": 0, + "(6475, '2024-11-04', 1)": 0, + "(6475, '2024-11-04', 2)": 0, + "(6475, '2024-11-04', 3)": 0, + "(6475, '2024-11-04', 4)": 0, + "(6475, '2024-11-04', 5)": 0, + "(6475, '2024-11-04', 6)": 0, + "(6475, '2024-11-04', 7)": 0, + "(6475, '2024-11-05', 0)": 0, + "(6475, '2024-11-05', 1)": 0, + "(6475, '2024-11-05', 2)": 0, + "(6475, '2024-11-05', 3)": 0, + "(6475, '2024-11-05', 4)": 0, + "(6475, '2024-11-05', 5)": 0, + "(6475, '2024-11-05', 6)": 0, + "(6475, '2024-11-05', 7)": 0, + "(6475, '2024-11-06', 0)": 0, + "(6475, '2024-11-06', 1)": 0, + "(6475, '2024-11-06', 2)": 0, + "(6475, '2024-11-06', 3)": 0, + "(6475, '2024-11-06', 4)": 0, + "(6475, '2024-11-06', 5)": 0, + "(6475, '2024-11-06', 6)": 0, + "(6475, '2024-11-06', 7)": 0, + "(6475, '2024-11-07', 0)": 0, + "(6475, '2024-11-07', 1)": 0, + "(6475, '2024-11-07', 2)": 0, + "(6475, '2024-11-07', 3)": 0, + "(6475, '2024-11-07', 4)": 0, + "(6475, '2024-11-07', 5)": 0, + "(6475, '2024-11-07', 6)": 0, + "(6475, '2024-11-07', 7)": 0, + "(6475, '2024-11-08', 0)": 0, + "(6475, '2024-11-08', 1)": 0, + "(6475, '2024-11-08', 2)": 0, + "(6475, '2024-11-08', 3)": 0, + "(6475, '2024-11-08', 4)": 0, + "(6475, '2024-11-08', 5)": 0, + "(6475, '2024-11-08', 6)": 0, + "(6475, '2024-11-08', 7)": 0, + "(6475, '2024-11-09', 0)": 0, + "(6475, '2024-11-09', 1)": 0, + "(6475, '2024-11-09', 2)": 0, + "(6475, '2024-11-09', 3)": 0, + "(6475, '2024-11-09', 4)": 0, + "(6475, '2024-11-09', 5)": 0, + "(6475, '2024-11-09', 6)": 0, + "(6475, '2024-11-09', 7)": 0, + "(6475, '2024-11-10', 0)": 0, + "(6475, '2024-11-10', 1)": 0, + "(6475, '2024-11-10', 2)": 0, + "(6475, '2024-11-10', 3)": 0, + "(6475, '2024-11-10', 4)": 0, + "(6475, '2024-11-10', 5)": 0, + "(6475, '2024-11-10', 6)": 0, + "(6475, '2024-11-10', 7)": 0, + "(6475, '2024-11-11', 0)": 0, + "(6475, '2024-11-11', 1)": 0, + "(6475, '2024-11-11', 2)": 0, + "(6475, '2024-11-11', 3)": 0, + "(6475, '2024-11-11', 4)": 0, + "(6475, '2024-11-11', 5)": 0, + "(6475, '2024-11-11', 6)": 0, + "(6475, '2024-11-11', 7)": 0, + "(6475, '2024-11-12', 0)": 0, + "(6475, '2024-11-12', 1)": 0, + "(6475, '2024-11-12', 2)": 0, + "(6475, '2024-11-12', 3)": 0, + "(6475, '2024-11-12', 4)": 0, + "(6475, '2024-11-12', 5)": 0, + "(6475, '2024-11-12', 6)": 0, + "(6475, '2024-11-12', 7)": 0, + "(6475, '2024-11-13', 0)": 0, + "(6475, '2024-11-13', 1)": 0, + "(6475, '2024-11-13', 2)": 0, + "(6475, '2024-11-13', 3)": 0, + "(6475, '2024-11-13', 4)": 0, + "(6475, '2024-11-13', 5)": 0, + "(6475, '2024-11-13', 6)": 0, + "(6475, '2024-11-13', 7)": 0, + "(6475, '2024-11-14', 0)": 0, + "(6475, '2024-11-14', 1)": 0, + "(6475, '2024-11-14', 2)": 0, + "(6475, '2024-11-14', 3)": 0, + "(6475, '2024-11-14', 4)": 0, + "(6475, '2024-11-14', 5)": 0, + "(6475, '2024-11-14', 6)": 0, + "(6475, '2024-11-14', 7)": 0, + "(6475, '2024-11-15', 0)": 0, + "(6475, '2024-11-15', 1)": 0, + "(6475, '2024-11-15', 2)": 0, + "(6475, '2024-11-15', 3)": 0, + "(6475, '2024-11-15', 4)": 0, + "(6475, '2024-11-15', 5)": 0, + "(6475, '2024-11-15', 6)": 0, + "(6475, '2024-11-15', 7)": 0, + "(6475, '2024-11-16', 0)": 0, + "(6475, '2024-11-16', 1)": 0, + "(6475, '2024-11-16', 2)": 0, + "(6475, '2024-11-16', 3)": 0, + "(6475, '2024-11-16', 4)": 0, + "(6475, '2024-11-16', 5)": 0, + "(6475, '2024-11-16', 6)": 0, + "(6475, '2024-11-16', 7)": 0, + "(6475, '2024-11-17', 0)": 0, + "(6475, '2024-11-17', 1)": 0, + "(6475, '2024-11-17', 2)": 0, + "(6475, '2024-11-17', 3)": 0, + "(6475, '2024-11-17', 4)": 0, + "(6475, '2024-11-17', 5)": 0, + "(6475, '2024-11-17', 6)": 0, + "(6475, '2024-11-17', 7)": 0, + "(6475, '2024-11-18', 0)": 0, + "(6475, '2024-11-18', 1)": 0, + "(6475, '2024-11-18', 2)": 0, + "(6475, '2024-11-18', 3)": 0, + "(6475, '2024-11-18', 4)": 0, + "(6475, '2024-11-18', 5)": 0, + "(6475, '2024-11-18', 6)": 0, + "(6475, '2024-11-18', 7)": 0, + "(6475, '2024-11-19', 0)": 0, + "(6475, '2024-11-19', 1)": 0, + "(6475, '2024-11-19', 2)": 0, + "(6475, '2024-11-19', 3)": 0, + "(6475, '2024-11-19', 4)": 0, + "(6475, '2024-11-19', 5)": 0, + "(6475, '2024-11-19', 6)": 0, + "(6475, '2024-11-19', 7)": 0, + "(6475, '2024-11-20', 0)": 0, + "(6475, '2024-11-20', 1)": 0, + "(6475, '2024-11-20', 2)": 0, + "(6475, '2024-11-20', 3)": 0, + "(6475, '2024-11-20', 4)": 0, + "(6475, '2024-11-20', 5)": 0, + "(6475, '2024-11-20', 6)": 0, + "(6475, '2024-11-20', 7)": 0, + "(6475, '2024-11-21', 0)": 0, + "(6475, '2024-11-21', 1)": 0, + "(6475, '2024-11-21', 2)": 0, + "(6475, '2024-11-21', 3)": 0, + "(6475, '2024-11-21', 4)": 0, + "(6475, '2024-11-21', 5)": 0, + "(6475, '2024-11-21', 6)": 0, + "(6475, '2024-11-21', 7)": 0, + "(6475, '2024-11-22', 0)": 0, + "(6475, '2024-11-22', 1)": 0, + "(6475, '2024-11-22', 2)": 0, + "(6475, '2024-11-22', 3)": 0, + "(6475, '2024-11-22', 4)": 0, + "(6475, '2024-11-22', 5)": 0, + "(6475, '2024-11-22', 6)": 0, + "(6475, '2024-11-22', 7)": 0, + "(6475, '2024-11-23', 0)": 0, + "(6475, '2024-11-23', 1)": 0, + "(6475, '2024-11-23', 2)": 0, + "(6475, '2024-11-23', 3)": 0, + "(6475, '2024-11-23', 4)": 0, + "(6475, '2024-11-23', 5)": 0, + "(6475, '2024-11-23', 6)": 0, + "(6475, '2024-11-23', 7)": 0, + "(6475, '2024-11-24', 0)": 0, + "(6475, '2024-11-24', 1)": 0, + "(6475, '2024-11-24', 2)": 0, + "(6475, '2024-11-24', 3)": 0, + "(6475, '2024-11-24', 4)": 0, + "(6475, '2024-11-24', 5)": 0, + "(6475, '2024-11-24', 6)": 0, + "(6475, '2024-11-24', 7)": 0, + "(6475, '2024-11-25', 0)": 0, + "(6475, '2024-11-25', 1)": 0, + "(6475, '2024-11-25', 2)": 0, + "(6475, '2024-11-25', 3)": 0, + "(6475, '2024-11-25', 4)": 0, + "(6475, '2024-11-25', 5)": 0, + "(6475, '2024-11-25', 6)": 0, + "(6475, '2024-11-25', 7)": 0, + "(6475, '2024-11-26', 0)": 0, + "(6475, '2024-11-26', 1)": 0, + "(6475, '2024-11-26', 2)": 0, + "(6475, '2024-11-26', 3)": 0, + "(6475, '2024-11-26', 4)": 0, + "(6475, '2024-11-26', 5)": 0, + "(6475, '2024-11-26', 6)": 0, + "(6475, '2024-11-26', 7)": 0, + "(6475, '2024-11-27', 0)": 0, + "(6475, '2024-11-27', 1)": 0, + "(6475, '2024-11-27', 2)": 0, + "(6475, '2024-11-27', 3)": 0, + "(6475, '2024-11-27', 4)": 0, + "(6475, '2024-11-27', 5)": 0, + "(6475, '2024-11-27', 6)": 0, + "(6475, '2024-11-27', 7)": 0, + "(6475, '2024-11-28', 0)": 0, + "(6475, '2024-11-28', 1)": 0, + "(6475, '2024-11-28', 2)": 0, + "(6475, '2024-11-28', 3)": 0, + "(6475, '2024-11-28', 4)": 0, + "(6475, '2024-11-28', 5)": 0, + "(6475, '2024-11-28', 6)": 0, + "(6475, '2024-11-28', 7)": 0, + "(6475, '2024-11-29', 0)": 0, + "(6475, '2024-11-29', 1)": 0, + "(6475, '2024-11-29', 2)": 0, + "(6475, '2024-11-29', 3)": 0, + "(6475, '2024-11-29', 4)": 0, + "(6475, '2024-11-29', 5)": 0, + "(6475, '2024-11-29', 6)": 0, + "(6475, '2024-11-29', 7)": 0, + "(6475, '2024-11-30', 0)": 1, + "(6475, '2024-11-30', 1)": 0, + "(6475, '2024-11-30', 2)": 0, + "(6475, '2024-11-30', 3)": 0, + "(6475, '2024-11-30', 4)": 0, + "(6475, '2024-11-30', 5)": 0, + "(6475, '2024-11-30', 6)": 0, + "(6475, '2024-11-30', 7)": 0, + "(6507, '2024-11-01', 0)": 0, + "(6507, '2024-11-01', 1)": 0, + "(6507, '2024-11-01', 2)": 0, + "(6507, '2024-11-01', 3)": 0, + "(6507, '2024-11-01', 4)": 0, + "(6507, '2024-11-01', 5)": 0, + "(6507, '2024-11-01', 6)": 0, + "(6507, '2024-11-01', 7)": 0, + "(6507, '2024-11-02', 0)": 1, + "(6507, '2024-11-02', 1)": 0, + "(6507, '2024-11-02', 2)": 0, + "(6507, '2024-11-02', 3)": 0, + "(6507, '2024-11-02', 4)": 0, + "(6507, '2024-11-02', 5)": 0, + "(6507, '2024-11-02', 6)": 0, + "(6507, '2024-11-02', 7)": 0, + "(6507, '2024-11-03', 0)": 0, + "(6507, '2024-11-03', 1)": 0, + "(6507, '2024-11-03', 2)": 0, + "(6507, '2024-11-03', 3)": 0, + "(6507, '2024-11-03', 4)": 0, + "(6507, '2024-11-03', 5)": 0, + "(6507, '2024-11-03', 6)": 0, + "(6507, '2024-11-03', 7)": 0, + "(6507, '2024-11-04', 0)": 1, + "(6507, '2024-11-04', 1)": 0, + "(6507, '2024-11-04', 2)": 0, + "(6507, '2024-11-04', 3)": 0, + "(6507, '2024-11-04', 4)": 0, + "(6507, '2024-11-04', 5)": 0, + "(6507, '2024-11-04', 6)": 0, + "(6507, '2024-11-04', 7)": 0, + "(6507, '2024-11-05', 0)": 1, + "(6507, '2024-11-05', 1)": 0, + "(6507, '2024-11-05', 2)": 0, + "(6507, '2024-11-05', 3)": 0, + "(6507, '2024-11-05', 4)": 0, + "(6507, '2024-11-05', 5)": 0, + "(6507, '2024-11-05', 6)": 0, + "(6507, '2024-11-05', 7)": 0, + "(6507, '2024-11-06', 0)": 1, + "(6507, '2024-11-06', 1)": 0, + "(6507, '2024-11-06', 2)": 0, + "(6507, '2024-11-06', 3)": 0, + "(6507, '2024-11-06', 4)": 0, + "(6507, '2024-11-06', 5)": 0, + "(6507, '2024-11-06', 6)": 0, + "(6507, '2024-11-06', 7)": 0, + "(6507, '2024-11-07', 0)": 1, + "(6507, '2024-11-07', 1)": 0, + "(6507, '2024-11-07', 2)": 0, + "(6507, '2024-11-07', 3)": 0, + "(6507, '2024-11-07', 4)": 0, + "(6507, '2024-11-07', 5)": 0, + "(6507, '2024-11-07', 6)": 0, + "(6507, '2024-11-07', 7)": 0, + "(6507, '2024-11-08', 0)": 1, + "(6507, '2024-11-08', 1)": 0, + "(6507, '2024-11-08', 2)": 0, + "(6507, '2024-11-08', 3)": 0, + "(6507, '2024-11-08', 4)": 0, + "(6507, '2024-11-08', 5)": 0, + "(6507, '2024-11-08', 6)": 0, + "(6507, '2024-11-08', 7)": 0, + "(6507, '2024-11-09', 0)": 0, + "(6507, '2024-11-09', 1)": 0, + "(6507, '2024-11-09', 2)": 1, + "(6507, '2024-11-09', 3)": 0, + "(6507, '2024-11-09', 4)": 0, + "(6507, '2024-11-09', 5)": 0, + "(6507, '2024-11-09', 6)": 0, + "(6507, '2024-11-09', 7)": 0, + "(6507, '2024-11-10', 0)": 0, + "(6507, '2024-11-10', 1)": 1, + "(6507, '2024-11-10', 2)": 0, + "(6507, '2024-11-10', 3)": 0, + "(6507, '2024-11-10', 4)": 0, + "(6507, '2024-11-10', 5)": 0, + "(6507, '2024-11-10', 6)": 0, + "(6507, '2024-11-10', 7)": 0, + "(6507, '2024-11-11', 0)": 1, + "(6507, '2024-11-11', 1)": 0, + "(6507, '2024-11-11', 2)": 0, + "(6507, '2024-11-11', 3)": 0, + "(6507, '2024-11-11', 4)": 0, + "(6507, '2024-11-11', 5)": 0, + "(6507, '2024-11-11', 6)": 0, + "(6507, '2024-11-11', 7)": 0, + "(6507, '2024-11-12', 0)": 0, + "(6507, '2024-11-12', 1)": 0, + "(6507, '2024-11-12', 2)": 0, + "(6507, '2024-11-12', 3)": 0, + "(6507, '2024-11-12', 4)": 0, + "(6507, '2024-11-12', 5)": 0, + "(6507, '2024-11-12', 6)": 0, + "(6507, '2024-11-12', 7)": 0, + "(6507, '2024-11-13', 0)": 1, + "(6507, '2024-11-13', 1)": 0, + "(6507, '2024-11-13', 2)": 0, + "(6507, '2024-11-13', 3)": 0, + "(6507, '2024-11-13', 4)": 0, + "(6507, '2024-11-13', 5)": 0, + "(6507, '2024-11-13', 6)": 0, + "(6507, '2024-11-13', 7)": 0, + "(6507, '2024-11-14', 0)": 0, + "(6507, '2024-11-14', 1)": 0, + "(6507, '2024-11-14', 2)": 0, + "(6507, '2024-11-14', 3)": 0, + "(6507, '2024-11-14', 4)": 0, + "(6507, '2024-11-14', 5)": 0, + "(6507, '2024-11-14', 6)": 0, + "(6507, '2024-11-14', 7)": 0, + "(6507, '2024-11-15', 0)": 0, + "(6507, '2024-11-15', 1)": 0, + "(6507, '2024-11-15', 2)": 0, + "(6507, '2024-11-15', 3)": 0, + "(6507, '2024-11-15', 4)": 0, + "(6507, '2024-11-15', 5)": 0, + "(6507, '2024-11-15', 6)": 0, + "(6507, '2024-11-15', 7)": 0, + "(6507, '2024-11-16', 0)": 0, + "(6507, '2024-11-16', 1)": 0, + "(6507, '2024-11-16', 2)": 1, + "(6507, '2024-11-16', 3)": 0, + "(6507, '2024-11-16', 4)": 0, + "(6507, '2024-11-16', 5)": 0, + "(6507, '2024-11-16', 6)": 0, + "(6507, '2024-11-16', 7)": 0, + "(6507, '2024-11-17', 0)": 0, + "(6507, '2024-11-17', 1)": 0, + "(6507, '2024-11-17', 2)": 0, + "(6507, '2024-11-17', 3)": 0, + "(6507, '2024-11-17', 4)": 0, + "(6507, '2024-11-17', 5)": 0, + "(6507, '2024-11-17', 6)": 0, + "(6507, '2024-11-17', 7)": 0, + "(6507, '2024-11-18', 0)": 0, + "(6507, '2024-11-18', 1)": 0, + "(6507, '2024-11-18', 2)": 0, + "(6507, '2024-11-18', 3)": 0, + "(6507, '2024-11-18', 4)": 0, + "(6507, '2024-11-18', 5)": 0, + "(6507, '2024-11-18', 6)": 0, + "(6507, '2024-11-18', 7)": 0, + "(6507, '2024-11-19', 0)": 1, + "(6507, '2024-11-19', 1)": 0, + "(6507, '2024-11-19', 2)": 0, + "(6507, '2024-11-19', 3)": 0, + "(6507, '2024-11-19', 4)": 0, + "(6507, '2024-11-19', 5)": 0, + "(6507, '2024-11-19', 6)": 0, + "(6507, '2024-11-19', 7)": 0, + "(6507, '2024-11-20', 0)": 1, + "(6507, '2024-11-20', 1)": 0, + "(6507, '2024-11-20', 2)": 0, + "(6507, '2024-11-20', 3)": 0, + "(6507, '2024-11-20', 4)": 0, + "(6507, '2024-11-20', 5)": 0, + "(6507, '2024-11-20', 6)": 0, + "(6507, '2024-11-20', 7)": 0, + "(6507, '2024-11-21', 0)": 1, + "(6507, '2024-11-21', 1)": 0, + "(6507, '2024-11-21', 2)": 0, + "(6507, '2024-11-21', 3)": 0, + "(6507, '2024-11-21', 4)": 0, + "(6507, '2024-11-21', 5)": 0, + "(6507, '2024-11-21', 6)": 0, + "(6507, '2024-11-21', 7)": 0, + "(6507, '2024-11-22', 0)": 1, + "(6507, '2024-11-22', 1)": 0, + "(6507, '2024-11-22', 2)": 0, + "(6507, '2024-11-22', 3)": 0, + "(6507, '2024-11-22', 4)": 0, + "(6507, '2024-11-22', 5)": 0, + "(6507, '2024-11-22', 6)": 0, + "(6507, '2024-11-22', 7)": 0, + "(6507, '2024-11-23', 0)": 0, + "(6507, '2024-11-23', 1)": 0, + "(6507, '2024-11-23', 2)": 0, + "(6507, '2024-11-23', 3)": 0, + "(6507, '2024-11-23', 4)": 0, + "(6507, '2024-11-23', 5)": 0, + "(6507, '2024-11-23', 6)": 0, + "(6507, '2024-11-23', 7)": 0, + "(6507, '2024-11-24', 0)": 0, + "(6507, '2024-11-24', 1)": 0, + "(6507, '2024-11-24', 2)": 0, + "(6507, '2024-11-24', 3)": 0, + "(6507, '2024-11-24', 4)": 0, + "(6507, '2024-11-24', 5)": 0, + "(6507, '2024-11-24', 6)": 0, + "(6507, '2024-11-24', 7)": 0, + "(6507, '2024-11-25', 0)": 1, + "(6507, '2024-11-25', 1)": 0, + "(6507, '2024-11-25', 2)": 0, + "(6507, '2024-11-25', 3)": 0, + "(6507, '2024-11-25', 4)": 0, + "(6507, '2024-11-25', 5)": 0, + "(6507, '2024-11-25', 6)": 0, + "(6507, '2024-11-25', 7)": 0, + "(6507, '2024-11-26', 0)": 1, + "(6507, '2024-11-26', 1)": 0, + "(6507, '2024-11-26', 2)": 0, + "(6507, '2024-11-26', 3)": 0, + "(6507, '2024-11-26', 4)": 0, + "(6507, '2024-11-26', 5)": 0, + "(6507, '2024-11-26', 6)": 0, + "(6507, '2024-11-26', 7)": 0, + "(6507, '2024-11-27', 0)": 1, + "(6507, '2024-11-27', 1)": 0, + "(6507, '2024-11-27', 2)": 0, + "(6507, '2024-11-27', 3)": 0, + "(6507, '2024-11-27', 4)": 0, + "(6507, '2024-11-27', 5)": 0, + "(6507, '2024-11-27', 6)": 0, + "(6507, '2024-11-27', 7)": 0, + "(6507, '2024-11-28', 0)": 1, + "(6507, '2024-11-28', 1)": 0, + "(6507, '2024-11-28', 2)": 0, + "(6507, '2024-11-28', 3)": 0, + "(6507, '2024-11-28', 4)": 0, + "(6507, '2024-11-28', 5)": 0, + "(6507, '2024-11-28', 6)": 0, + "(6507, '2024-11-28', 7)": 0, + "(6507, '2024-11-29', 0)": 0, + "(6507, '2024-11-29', 1)": 0, + "(6507, '2024-11-29', 2)": 0, + "(6507, '2024-11-29', 3)": 0, + "(6507, '2024-11-29', 4)": 0, + "(6507, '2024-11-29', 5)": 0, + "(6507, '2024-11-29', 6)": 0, + "(6507, '2024-11-29', 7)": 0, + "(6507, '2024-11-30', 0)": 1, + "(6507, '2024-11-30', 1)": 0, + "(6507, '2024-11-30', 2)": 0, + "(6507, '2024-11-30', 3)": 0, + "(6507, '2024-11-30', 4)": 0, + "(6507, '2024-11-30', 5)": 0, + "(6507, '2024-11-30', 6)": 0, + "(6507, '2024-11-30', 7)": 0, + "(6677, '2024-11-01', 0)": 0, + "(6677, '2024-11-01', 1)": 0, + "(6677, '2024-11-01', 2)": 0, + "(6677, '2024-11-01', 3)": 0, + "(6677, '2024-11-01', 4)": 0, + "(6677, '2024-11-01', 5)": 0, + "(6677, '2024-11-01', 6)": 0, + "(6677, '2024-11-01', 7)": 0, + "(6677, '2024-11-02', 0)": 0, + "(6677, '2024-11-02', 1)": 0, + "(6677, '2024-11-02', 2)": 0, + "(6677, '2024-11-02', 3)": 0, + "(6677, '2024-11-02', 4)": 0, + "(6677, '2024-11-02', 5)": 0, + "(6677, '2024-11-02', 6)": 0, + "(6677, '2024-11-02', 7)": 0, + "(6677, '2024-11-03', 0)": 1, + "(6677, '2024-11-03', 1)": 0, + "(6677, '2024-11-03', 2)": 0, + "(6677, '2024-11-03', 3)": 0, + "(6677, '2024-11-03', 4)": 0, + "(6677, '2024-11-03', 5)": 0, + "(6677, '2024-11-03', 6)": 0, + "(6677, '2024-11-03', 7)": 0, + "(6677, '2024-11-04', 0)": 1, + "(6677, '2024-11-04', 1)": 0, + "(6677, '2024-11-04', 2)": 0, + "(6677, '2024-11-04', 3)": 0, + "(6677, '2024-11-04', 4)": 0, + "(6677, '2024-11-04', 5)": 0, + "(6677, '2024-11-04', 6)": 0, + "(6677, '2024-11-04', 7)": 0, + "(6677, '2024-11-05', 0)": 1, + "(6677, '2024-11-05', 1)": 0, + "(6677, '2024-11-05', 2)": 0, + "(6677, '2024-11-05', 3)": 0, + "(6677, '2024-11-05', 4)": 0, + "(6677, '2024-11-05', 5)": 0, + "(6677, '2024-11-05', 6)": 0, + "(6677, '2024-11-05', 7)": 0, + "(6677, '2024-11-06', 0)": 1, + "(6677, '2024-11-06', 1)": 0, + "(6677, '2024-11-06', 2)": 0, + "(6677, '2024-11-06', 3)": 0, + "(6677, '2024-11-06', 4)": 0, + "(6677, '2024-11-06', 5)": 0, + "(6677, '2024-11-06', 6)": 0, + "(6677, '2024-11-06', 7)": 0, + "(6677, '2024-11-07', 0)": 1, + "(6677, '2024-11-07', 1)": 0, + "(6677, '2024-11-07', 2)": 0, + "(6677, '2024-11-07', 3)": 0, + "(6677, '2024-11-07', 4)": 0, + "(6677, '2024-11-07', 5)": 0, + "(6677, '2024-11-07', 6)": 0, + "(6677, '2024-11-07', 7)": 0, + "(6677, '2024-11-08', 0)": 0, + "(6677, '2024-11-08', 1)": 0, + "(6677, '2024-11-08', 2)": 0, + "(6677, '2024-11-08', 3)": 0, + "(6677, '2024-11-08', 4)": 0, + "(6677, '2024-11-08', 5)": 0, + "(6677, '2024-11-08', 6)": 0, + "(6677, '2024-11-08', 7)": 0, + "(6677, '2024-11-09', 0)": 0, + "(6677, '2024-11-09', 1)": 0, + "(6677, '2024-11-09', 2)": 1, + "(6677, '2024-11-09', 3)": 0, + "(6677, '2024-11-09', 4)": 0, + "(6677, '2024-11-09', 5)": 0, + "(6677, '2024-11-09', 6)": 0, + "(6677, '2024-11-09', 7)": 0, + "(6677, '2024-11-10', 0)": 0, + "(6677, '2024-11-10', 1)": 0, + "(6677, '2024-11-10', 2)": 1, + "(6677, '2024-11-10', 3)": 0, + "(6677, '2024-11-10', 4)": 0, + "(6677, '2024-11-10', 5)": 0, + "(6677, '2024-11-10', 6)": 0, + "(6677, '2024-11-10', 7)": 0, + "(6677, '2024-11-11', 0)": 0, + "(6677, '2024-11-11', 1)": 0, + "(6677, '2024-11-11', 2)": 0, + "(6677, '2024-11-11', 3)": 0, + "(6677, '2024-11-11', 4)": 0, + "(6677, '2024-11-11', 5)": 0, + "(6677, '2024-11-11', 6)": 0, + "(6677, '2024-11-11', 7)": 0, + "(6677, '2024-11-12', 0)": 1, + "(6677, '2024-11-12', 1)": 0, + "(6677, '2024-11-12', 2)": 0, + "(6677, '2024-11-12', 3)": 0, + "(6677, '2024-11-12', 4)": 0, + "(6677, '2024-11-12', 5)": 0, + "(6677, '2024-11-12', 6)": 0, + "(6677, '2024-11-12', 7)": 0, + "(6677, '2024-11-13', 0)": 1, + "(6677, '2024-11-13', 1)": 0, + "(6677, '2024-11-13', 2)": 0, + "(6677, '2024-11-13', 3)": 0, + "(6677, '2024-11-13', 4)": 0, + "(6677, '2024-11-13', 5)": 0, + "(6677, '2024-11-13', 6)": 0, + "(6677, '2024-11-13', 7)": 0, + "(6677, '2024-11-14', 0)": 0, + "(6677, '2024-11-14', 1)": 0, + "(6677, '2024-11-14', 2)": 1, + "(6677, '2024-11-14', 3)": 0, + "(6677, '2024-11-14', 4)": 0, + "(6677, '2024-11-14', 5)": 0, + "(6677, '2024-11-14', 6)": 0, + "(6677, '2024-11-14', 7)": 0, + "(6677, '2024-11-15', 0)": 0, + "(6677, '2024-11-15', 1)": 0, + "(6677, '2024-11-15', 2)": 0, + "(6677, '2024-11-15', 3)": 0, + "(6677, '2024-11-15', 4)": 0, + "(6677, '2024-11-15', 5)": 0, + "(6677, '2024-11-15', 6)": 0, + "(6677, '2024-11-15', 7)": 0, + "(6677, '2024-11-16', 0)": 0, + "(6677, '2024-11-16', 1)": 0, + "(6677, '2024-11-16', 2)": 0, + "(6677, '2024-11-16', 3)": 0, + "(6677, '2024-11-16', 4)": 0, + "(6677, '2024-11-16', 5)": 0, + "(6677, '2024-11-16', 6)": 0, + "(6677, '2024-11-16', 7)": 0, + "(6677, '2024-11-17', 0)": 1, + "(6677, '2024-11-17', 1)": 0, + "(6677, '2024-11-17', 2)": 0, + "(6677, '2024-11-17', 3)": 0, + "(6677, '2024-11-17', 4)": 0, + "(6677, '2024-11-17', 5)": 0, + "(6677, '2024-11-17', 6)": 0, + "(6677, '2024-11-17', 7)": 0, + "(6677, '2024-11-18', 0)": 1, + "(6677, '2024-11-18', 1)": 0, + "(6677, '2024-11-18', 2)": 0, + "(6677, '2024-11-18', 3)": 0, + "(6677, '2024-11-18', 4)": 0, + "(6677, '2024-11-18', 5)": 0, + "(6677, '2024-11-18', 6)": 0, + "(6677, '2024-11-18', 7)": 0, + "(6677, '2024-11-19', 0)": 0, + "(6677, '2024-11-19', 1)": 0, + "(6677, '2024-11-19', 2)": 0, + "(6677, '2024-11-19', 3)": 0, + "(6677, '2024-11-19', 4)": 0, + "(6677, '2024-11-19', 5)": 0, + "(6677, '2024-11-19', 6)": 0, + "(6677, '2024-11-19', 7)": 0, + "(6677, '2024-11-20', 0)": 0, + "(6677, '2024-11-20', 1)": 0, + "(6677, '2024-11-20', 2)": 0, + "(6677, '2024-11-20', 3)": 0, + "(6677, '2024-11-20', 4)": 0, + "(6677, '2024-11-20', 5)": 0, + "(6677, '2024-11-20', 6)": 0, + "(6677, '2024-11-20', 7)": 0, + "(6677, '2024-11-21', 0)": 0, + "(6677, '2024-11-21', 1)": 0, + "(6677, '2024-11-21', 2)": 0, + "(6677, '2024-11-21', 3)": 0, + "(6677, '2024-11-21', 4)": 0, + "(6677, '2024-11-21', 5)": 0, + "(6677, '2024-11-21', 6)": 0, + "(6677, '2024-11-21', 7)": 0, + "(6677, '2024-11-22', 0)": 0, + "(6677, '2024-11-22', 1)": 0, + "(6677, '2024-11-22', 2)": 0, + "(6677, '2024-11-22', 3)": 0, + "(6677, '2024-11-22', 4)": 0, + "(6677, '2024-11-22', 5)": 0, + "(6677, '2024-11-22', 6)": 0, + "(6677, '2024-11-22', 7)": 0, + "(6677, '2024-11-23', 0)": 1, + "(6677, '2024-11-23', 1)": 0, + "(6677, '2024-11-23', 2)": 0, + "(6677, '2024-11-23', 3)": 0, + "(6677, '2024-11-23', 4)": 0, + "(6677, '2024-11-23', 5)": 0, + "(6677, '2024-11-23', 6)": 0, + "(6677, '2024-11-23', 7)": 0, + "(6677, '2024-11-24', 0)": 1, + "(6677, '2024-11-24', 1)": 0, + "(6677, '2024-11-24', 2)": 0, + "(6677, '2024-11-24', 3)": 0, + "(6677, '2024-11-24', 4)": 0, + "(6677, '2024-11-24', 5)": 0, + "(6677, '2024-11-24', 6)": 0, + "(6677, '2024-11-24', 7)": 0, + "(6677, '2024-11-25', 0)": 0, + "(6677, '2024-11-25', 1)": 0, + "(6677, '2024-11-25', 2)": 1, + "(6677, '2024-11-25', 3)": 0, + "(6677, '2024-11-25', 4)": 0, + "(6677, '2024-11-25', 5)": 0, + "(6677, '2024-11-25', 6)": 0, + "(6677, '2024-11-25', 7)": 0, + "(6677, '2024-11-26', 0)": 0, + "(6677, '2024-11-26', 1)": 0, + "(6677, '2024-11-26', 2)": 1, + "(6677, '2024-11-26', 3)": 0, + "(6677, '2024-11-26', 4)": 0, + "(6677, '2024-11-26', 5)": 0, + "(6677, '2024-11-26', 6)": 0, + "(6677, '2024-11-26', 7)": 0, + "(6677, '2024-11-27', 0)": 0, + "(6677, '2024-11-27', 1)": 0, + "(6677, '2024-11-27', 2)": 1, + "(6677, '2024-11-27', 3)": 0, + "(6677, '2024-11-27', 4)": 0, + "(6677, '2024-11-27', 5)": 0, + "(6677, '2024-11-27', 6)": 0, + "(6677, '2024-11-27', 7)": 0, + "(6677, '2024-11-28', 0)": 0, + "(6677, '2024-11-28', 1)": 0, + "(6677, '2024-11-28', 2)": 1, + "(6677, '2024-11-28', 3)": 0, + "(6677, '2024-11-28', 4)": 0, + "(6677, '2024-11-28', 5)": 0, + "(6677, '2024-11-28', 6)": 0, + "(6677, '2024-11-28', 7)": 0, + "(6677, '2024-11-29', 0)": 0, + "(6677, '2024-11-29', 1)": 0, + "(6677, '2024-11-29', 2)": 1, + "(6677, '2024-11-29', 3)": 0, + "(6677, '2024-11-29', 4)": 0, + "(6677, '2024-11-29', 5)": 0, + "(6677, '2024-11-29', 6)": 0, + "(6677, '2024-11-29', 7)": 0, + "(6677, '2024-11-30', 0)": 0, + "(6677, '2024-11-30', 1)": 0, + "(6677, '2024-11-30', 2)": 1, + "(6677, '2024-11-30', 3)": 0, + "(6677, '2024-11-30', 4)": 0, + "(6677, '2024-11-30', 5)": 0, + "(6677, '2024-11-30', 6)": 0, + "(6677, '2024-11-30', 7)": 0, + "(6681, '2024-11-01', 0)": 0, + "(6681, '2024-11-01', 1)": 0, + "(6681, '2024-11-01', 2)": 0, + "(6681, '2024-11-01', 3)": 0, + "(6681, '2024-11-01', 4)": 0, + "(6681, '2024-11-01', 5)": 0, + "(6681, '2024-11-01', 6)": 0, + "(6681, '2024-11-01', 7)": 0, + "(6681, '2024-11-02', 0)": 0, + "(6681, '2024-11-02', 1)": 0, + "(6681, '2024-11-02', 2)": 0, + "(6681, '2024-11-02', 3)": 1, + "(6681, '2024-11-02', 4)": 0, + "(6681, '2024-11-02', 5)": 0, + "(6681, '2024-11-02', 6)": 0, + "(6681, '2024-11-02', 7)": 0, + "(6681, '2024-11-03', 0)": 0, + "(6681, '2024-11-03', 1)": 0, + "(6681, '2024-11-03', 2)": 0, + "(6681, '2024-11-03', 3)": 0, + "(6681, '2024-11-03', 4)": 0, + "(6681, '2024-11-03', 5)": 0, + "(6681, '2024-11-03', 6)": 0, + "(6681, '2024-11-03', 7)": 0, + "(6681, '2024-11-04', 0)": 0, + "(6681, '2024-11-04', 1)": 0, + "(6681, '2024-11-04', 2)": 0, + "(6681, '2024-11-04', 3)": 0, + "(6681, '2024-11-04', 4)": 0, + "(6681, '2024-11-04', 5)": 0, + "(6681, '2024-11-04', 6)": 0, + "(6681, '2024-11-04', 7)": 0, + "(6681, '2024-11-05', 0)": 0, + "(6681, '2024-11-05', 1)": 0, + "(6681, '2024-11-05', 2)": 0, + "(6681, '2024-11-05', 3)": 1, + "(6681, '2024-11-05', 4)": 0, + "(6681, '2024-11-05', 5)": 0, + "(6681, '2024-11-05', 6)": 0, + "(6681, '2024-11-05', 7)": 0, + "(6681, '2024-11-06', 0)": 0, + "(6681, '2024-11-06', 1)": 0, + "(6681, '2024-11-06', 2)": 0, + "(6681, '2024-11-06', 3)": 0, + "(6681, '2024-11-06', 4)": 0, + "(6681, '2024-11-06', 5)": 0, + "(6681, '2024-11-06', 6)": 0, + "(6681, '2024-11-06', 7)": 0, + "(6681, '2024-11-07', 0)": 0, + "(6681, '2024-11-07', 1)": 0, + "(6681, '2024-11-07', 2)": 0, + "(6681, '2024-11-07', 3)": 1, + "(6681, '2024-11-07', 4)": 0, + "(6681, '2024-11-07', 5)": 0, + "(6681, '2024-11-07', 6)": 0, + "(6681, '2024-11-07', 7)": 0, + "(6681, '2024-11-08', 0)": 0, + "(6681, '2024-11-08', 1)": 0, + "(6681, '2024-11-08', 2)": 0, + "(6681, '2024-11-08', 3)": 0, + "(6681, '2024-11-08', 4)": 0, + "(6681, '2024-11-08', 5)": 0, + "(6681, '2024-11-08', 6)": 0, + "(6681, '2024-11-08', 7)": 0, + "(6681, '2024-11-09', 0)": 0, + "(6681, '2024-11-09', 1)": 0, + "(6681, '2024-11-09', 2)": 0, + "(6681, '2024-11-09', 3)": 0, + "(6681, '2024-11-09', 4)": 0, + "(6681, '2024-11-09', 5)": 0, + "(6681, '2024-11-09', 6)": 0, + "(6681, '2024-11-09', 7)": 0, + "(6681, '2024-11-10', 0)": 0, + "(6681, '2024-11-10', 1)": 0, + "(6681, '2024-11-10', 2)": 0, + "(6681, '2024-11-10', 3)": 0, + "(6681, '2024-11-10', 4)": 0, + "(6681, '2024-11-10', 5)": 0, + "(6681, '2024-11-10', 6)": 0, + "(6681, '2024-11-10', 7)": 0, + "(6681, '2024-11-11', 0)": 0, + "(6681, '2024-11-11', 1)": 0, + "(6681, '2024-11-11', 2)": 0, + "(6681, '2024-11-11', 3)": 0, + "(6681, '2024-11-11', 4)": 0, + "(6681, '2024-11-11', 5)": 0, + "(6681, '2024-11-11', 6)": 0, + "(6681, '2024-11-11', 7)": 0, + "(6681, '2024-11-12', 0)": 0, + "(6681, '2024-11-12', 1)": 0, + "(6681, '2024-11-12', 2)": 0, + "(6681, '2024-11-12', 3)": 1, + "(6681, '2024-11-12', 4)": 0, + "(6681, '2024-11-12', 5)": 0, + "(6681, '2024-11-12', 6)": 0, + "(6681, '2024-11-12', 7)": 0, + "(6681, '2024-11-13', 0)": 0, + "(6681, '2024-11-13', 1)": 0, + "(6681, '2024-11-13', 2)": 0, + "(6681, '2024-11-13', 3)": 1, + "(6681, '2024-11-13', 4)": 0, + "(6681, '2024-11-13', 5)": 0, + "(6681, '2024-11-13', 6)": 0, + "(6681, '2024-11-13', 7)": 0, + "(6681, '2024-11-14', 0)": 0, + "(6681, '2024-11-14', 1)": 0, + "(6681, '2024-11-14', 2)": 0, + "(6681, '2024-11-14', 3)": 0, + "(6681, '2024-11-14', 4)": 0, + "(6681, '2024-11-14', 5)": 0, + "(6681, '2024-11-14', 6)": 0, + "(6681, '2024-11-14', 7)": 0, + "(6681, '2024-11-15', 0)": 0, + "(6681, '2024-11-15', 1)": 0, + "(6681, '2024-11-15', 2)": 0, + "(6681, '2024-11-15', 3)": 1, + "(6681, '2024-11-15', 4)": 0, + "(6681, '2024-11-15', 5)": 0, + "(6681, '2024-11-15', 6)": 0, + "(6681, '2024-11-15', 7)": 0, + "(6681, '2024-11-16', 0)": 0, + "(6681, '2024-11-16', 1)": 0, + "(6681, '2024-11-16', 2)": 0, + "(6681, '2024-11-16', 3)": 0, + "(6681, '2024-11-16', 4)": 0, + "(6681, '2024-11-16', 5)": 0, + "(6681, '2024-11-16', 6)": 0, + "(6681, '2024-11-16', 7)": 0, + "(6681, '2024-11-17', 0)": 0, + "(6681, '2024-11-17', 1)": 0, + "(6681, '2024-11-17', 2)": 0, + "(6681, '2024-11-17', 3)": 0, + "(6681, '2024-11-17', 4)": 0, + "(6681, '2024-11-17', 5)": 0, + "(6681, '2024-11-17', 6)": 0, + "(6681, '2024-11-17', 7)": 0, + "(6681, '2024-11-18', 0)": 0, + "(6681, '2024-11-18', 1)": 0, + "(6681, '2024-11-18', 2)": 0, + "(6681, '2024-11-18', 3)": 0, + "(6681, '2024-11-18', 4)": 0, + "(6681, '2024-11-18', 5)": 0, + "(6681, '2024-11-18', 6)": 0, + "(6681, '2024-11-18', 7)": 0, + "(6681, '2024-11-19', 0)": 0, + "(6681, '2024-11-19', 1)": 0, + "(6681, '2024-11-19', 2)": 0, + "(6681, '2024-11-19', 3)": 0, + "(6681, '2024-11-19', 4)": 0, + "(6681, '2024-11-19', 5)": 0, + "(6681, '2024-11-19', 6)": 0, + "(6681, '2024-11-19', 7)": 0, + "(6681, '2024-11-20', 0)": 0, + "(6681, '2024-11-20', 1)": 0, + "(6681, '2024-11-20', 2)": 0, + "(6681, '2024-11-20', 3)": 0, + "(6681, '2024-11-20', 4)": 0, + "(6681, '2024-11-20', 5)": 0, + "(6681, '2024-11-20', 6)": 0, + "(6681, '2024-11-20', 7)": 0, + "(6681, '2024-11-21', 0)": 0, + "(6681, '2024-11-21', 1)": 0, + "(6681, '2024-11-21', 2)": 0, + "(6681, '2024-11-21', 3)": 0, + "(6681, '2024-11-21', 4)": 0, + "(6681, '2024-11-21', 5)": 0, + "(6681, '2024-11-21', 6)": 0, + "(6681, '2024-11-21', 7)": 0, + "(6681, '2024-11-22', 0)": 0, + "(6681, '2024-11-22', 1)": 0, + "(6681, '2024-11-22', 2)": 0, + "(6681, '2024-11-22', 3)": 0, + "(6681, '2024-11-22', 4)": 0, + "(6681, '2024-11-22', 5)": 0, + "(6681, '2024-11-22', 6)": 0, + "(6681, '2024-11-22', 7)": 0, + "(6681, '2024-11-23', 0)": 0, + "(6681, '2024-11-23', 1)": 0, + "(6681, '2024-11-23', 2)": 0, + "(6681, '2024-11-23', 3)": 0, + "(6681, '2024-11-23', 4)": 0, + "(6681, '2024-11-23', 5)": 0, + "(6681, '2024-11-23', 6)": 0, + "(6681, '2024-11-23', 7)": 0, + "(6681, '2024-11-24', 0)": 0, + "(6681, '2024-11-24', 1)": 0, + "(6681, '2024-11-24', 2)": 0, + "(6681, '2024-11-24', 3)": 0, + "(6681, '2024-11-24', 4)": 0, + "(6681, '2024-11-24', 5)": 0, + "(6681, '2024-11-24', 6)": 0, + "(6681, '2024-11-24', 7)": 0, + "(6681, '2024-11-25', 0)": 0, + "(6681, '2024-11-25', 1)": 0, + "(6681, '2024-11-25', 2)": 0, + "(6681, '2024-11-25', 3)": 0, + "(6681, '2024-11-25', 4)": 0, + "(6681, '2024-11-25', 5)": 0, + "(6681, '2024-11-25', 6)": 0, + "(6681, '2024-11-25', 7)": 0, + "(6681, '2024-11-26', 0)": 0, + "(6681, '2024-11-26', 1)": 0, + "(6681, '2024-11-26', 2)": 0, + "(6681, '2024-11-26', 3)": 0, + "(6681, '2024-11-26', 4)": 0, + "(6681, '2024-11-26', 5)": 0, + "(6681, '2024-11-26', 6)": 0, + "(6681, '2024-11-26', 7)": 0, + "(6681, '2024-11-27', 0)": 0, + "(6681, '2024-11-27', 1)": 0, + "(6681, '2024-11-27', 2)": 0, + "(6681, '2024-11-27', 3)": 0, + "(6681, '2024-11-27', 4)": 0, + "(6681, '2024-11-27', 5)": 0, + "(6681, '2024-11-27', 6)": 0, + "(6681, '2024-11-27', 7)": 0, + "(6681, '2024-11-28', 0)": 0, + "(6681, '2024-11-28', 1)": 0, + "(6681, '2024-11-28', 2)": 0, + "(6681, '2024-11-28', 3)": 0, + "(6681, '2024-11-28', 4)": 0, + "(6681, '2024-11-28', 5)": 0, + "(6681, '2024-11-28', 6)": 0, + "(6681, '2024-11-28', 7)": 0, + "(6681, '2024-11-29', 0)": 0, + "(6681, '2024-11-29', 1)": 0, + "(6681, '2024-11-29', 2)": 0, + "(6681, '2024-11-29', 3)": 0, + "(6681, '2024-11-29', 4)": 0, + "(6681, '2024-11-29', 5)": 0, + "(6681, '2024-11-29', 6)": 0, + "(6681, '2024-11-29', 7)": 0, + "(6681, '2024-11-30', 0)": 0, + "(6681, '2024-11-30', 1)": 0, + "(6681, '2024-11-30', 2)": 0, + "(6681, '2024-11-30', 3)": 0, + "(6681, '2024-11-30', 4)": 0, + "(6681, '2024-11-30', 5)": 0, + "(6681, '2024-11-30', 6)": 0, + "(6681, '2024-11-30', 7)": 0, + "(6715, '2024-11-01', 0)": 0, + "(6715, '2024-11-01', 1)": 0, + "(6715, '2024-11-01', 2)": 0, + "(6715, '2024-11-01', 3)": 0, + "(6715, '2024-11-01', 4)": 0, + "(6715, '2024-11-01', 5)": 0, + "(6715, '2024-11-01', 6)": 0, + "(6715, '2024-11-01', 7)": 0, + "(6715, '2024-11-02', 0)": 0, + "(6715, '2024-11-02', 1)": 0, + "(6715, '2024-11-02', 2)": 0, + "(6715, '2024-11-02', 3)": 0, + "(6715, '2024-11-02', 4)": 0, + "(6715, '2024-11-02', 5)": 0, + "(6715, '2024-11-02', 6)": 0, + "(6715, '2024-11-02', 7)": 0, + "(6715, '2024-11-03', 0)": 0, + "(6715, '2024-11-03', 1)": 0, + "(6715, '2024-11-03', 2)": 0, + "(6715, '2024-11-03', 3)": 0, + "(6715, '2024-11-03', 4)": 0, + "(6715, '2024-11-03', 5)": 0, + "(6715, '2024-11-03', 6)": 0, + "(6715, '2024-11-03', 7)": 0, + "(6715, '2024-11-04', 0)": 0, + "(6715, '2024-11-04', 1)": 0, + "(6715, '2024-11-04', 2)": 0, + "(6715, '2024-11-04', 3)": 0, + "(6715, '2024-11-04', 4)": 0, + "(6715, '2024-11-04', 5)": 0, + "(6715, '2024-11-04', 6)": 0, + "(6715, '2024-11-04', 7)": 0, + "(6715, '2024-11-05', 0)": 0, + "(6715, '2024-11-05', 1)": 0, + "(6715, '2024-11-05', 2)": 0, + "(6715, '2024-11-05', 3)": 0, + "(6715, '2024-11-05', 4)": 0, + "(6715, '2024-11-05', 5)": 0, + "(6715, '2024-11-05', 6)": 0, + "(6715, '2024-11-05', 7)": 0, + "(6715, '2024-11-06', 0)": 0, + "(6715, '2024-11-06', 1)": 1, + "(6715, '2024-11-06', 2)": 0, + "(6715, '2024-11-06', 3)": 0, + "(6715, '2024-11-06', 4)": 0, + "(6715, '2024-11-06', 5)": 0, + "(6715, '2024-11-06', 6)": 0, + "(6715, '2024-11-06', 7)": 0, + "(6715, '2024-11-07', 0)": 0, + "(6715, '2024-11-07', 1)": 0, + "(6715, '2024-11-07', 2)": 0, + "(6715, '2024-11-07', 3)": 0, + "(6715, '2024-11-07', 4)": 0, + "(6715, '2024-11-07', 5)": 0, + "(6715, '2024-11-07', 6)": 0, + "(6715, '2024-11-07', 7)": 0, + "(6715, '2024-11-08', 0)": 0, + "(6715, '2024-11-08', 1)": 0, + "(6715, '2024-11-08', 2)": 0, + "(6715, '2024-11-08', 3)": 0, + "(6715, '2024-11-08', 4)": 0, + "(6715, '2024-11-08', 5)": 0, + "(6715, '2024-11-08', 6)": 0, + "(6715, '2024-11-08', 7)": 0, + "(6715, '2024-11-09', 0)": 0, + "(6715, '2024-11-09', 1)": 0, + "(6715, '2024-11-09', 2)": 0, + "(6715, '2024-11-09', 3)": 0, + "(6715, '2024-11-09', 4)": 0, + "(6715, '2024-11-09', 5)": 0, + "(6715, '2024-11-09', 6)": 0, + "(6715, '2024-11-09', 7)": 0, + "(6715, '2024-11-10', 0)": 0, + "(6715, '2024-11-10', 1)": 0, + "(6715, '2024-11-10', 2)": 0, + "(6715, '2024-11-10', 3)": 0, + "(6715, '2024-11-10', 4)": 0, + "(6715, '2024-11-10', 5)": 0, + "(6715, '2024-11-10', 6)": 0, + "(6715, '2024-11-10', 7)": 0, + "(6715, '2024-11-11', 0)": 0, + "(6715, '2024-11-11', 1)": 0, + "(6715, '2024-11-11', 2)": 0, + "(6715, '2024-11-11', 3)": 0, + "(6715, '2024-11-11', 4)": 0, + "(6715, '2024-11-11', 5)": 0, + "(6715, '2024-11-11', 6)": 0, + "(6715, '2024-11-11', 7)": 0, + "(6715, '2024-11-12', 0)": 0, + "(6715, '2024-11-12', 1)": 0, + "(6715, '2024-11-12', 2)": 0, + "(6715, '2024-11-12', 3)": 0, + "(6715, '2024-11-12', 4)": 0, + "(6715, '2024-11-12', 5)": 0, + "(6715, '2024-11-12', 6)": 0, + "(6715, '2024-11-12', 7)": 0, + "(6715, '2024-11-13', 0)": 0, + "(6715, '2024-11-13', 1)": 0, + "(6715, '2024-11-13', 2)": 0, + "(6715, '2024-11-13', 3)": 0, + "(6715, '2024-11-13', 4)": 0, + "(6715, '2024-11-13', 5)": 0, + "(6715, '2024-11-13', 6)": 0, + "(6715, '2024-11-13', 7)": 0, + "(6715, '2024-11-14', 0)": 0, + "(6715, '2024-11-14', 1)": 0, + "(6715, '2024-11-14', 2)": 0, + "(6715, '2024-11-14', 3)": 0, + "(6715, '2024-11-14', 4)": 0, + "(6715, '2024-11-14', 5)": 0, + "(6715, '2024-11-14', 6)": 0, + "(6715, '2024-11-14', 7)": 0, + "(6715, '2024-11-15', 0)": 0, + "(6715, '2024-11-15', 1)": 0, + "(6715, '2024-11-15', 2)": 0, + "(6715, '2024-11-15', 3)": 0, + "(6715, '2024-11-15', 4)": 0, + "(6715, '2024-11-15', 5)": 0, + "(6715, '2024-11-15', 6)": 0, + "(6715, '2024-11-15', 7)": 0, + "(6715, '2024-11-16', 0)": 0, + "(6715, '2024-11-16', 1)": 0, + "(6715, '2024-11-16', 2)": 0, + "(6715, '2024-11-16', 3)": 0, + "(6715, '2024-11-16', 4)": 0, + "(6715, '2024-11-16', 5)": 0, + "(6715, '2024-11-16', 6)": 0, + "(6715, '2024-11-16', 7)": 0, + "(6715, '2024-11-17', 0)": 0, + "(6715, '2024-11-17', 1)": 0, + "(6715, '2024-11-17', 2)": 0, + "(6715, '2024-11-17', 3)": 0, + "(6715, '2024-11-17', 4)": 0, + "(6715, '2024-11-17', 5)": 0, + "(6715, '2024-11-17', 6)": 0, + "(6715, '2024-11-17', 7)": 0, + "(6715, '2024-11-18', 0)": 0, + "(6715, '2024-11-18', 1)": 0, + "(6715, '2024-11-18', 2)": 0, + "(6715, '2024-11-18', 3)": 0, + "(6715, '2024-11-18', 4)": 0, + "(6715, '2024-11-18', 5)": 0, + "(6715, '2024-11-18', 6)": 0, + "(6715, '2024-11-18', 7)": 0, + "(6715, '2024-11-19', 0)": 0, + "(6715, '2024-11-19', 1)": 0, + "(6715, '2024-11-19', 2)": 0, + "(6715, '2024-11-19', 3)": 0, + "(6715, '2024-11-19', 4)": 0, + "(6715, '2024-11-19', 5)": 0, + "(6715, '2024-11-19', 6)": 0, + "(6715, '2024-11-19', 7)": 0, + "(6715, '2024-11-20', 0)": 0, + "(6715, '2024-11-20', 1)": 0, + "(6715, '2024-11-20', 2)": 0, + "(6715, '2024-11-20', 3)": 0, + "(6715, '2024-11-20', 4)": 0, + "(6715, '2024-11-20', 5)": 0, + "(6715, '2024-11-20', 6)": 0, + "(6715, '2024-11-20', 7)": 0, + "(6715, '2024-11-21', 0)": 0, + "(6715, '2024-11-21', 1)": 0, + "(6715, '2024-11-21', 2)": 0, + "(6715, '2024-11-21', 3)": 0, + "(6715, '2024-11-21', 4)": 0, + "(6715, '2024-11-21', 5)": 0, + "(6715, '2024-11-21', 6)": 0, + "(6715, '2024-11-21', 7)": 0, + "(6715, '2024-11-22', 0)": 0, + "(6715, '2024-11-22', 1)": 0, + "(6715, '2024-11-22', 2)": 0, + "(6715, '2024-11-22', 3)": 0, + "(6715, '2024-11-22', 4)": 0, + "(6715, '2024-11-22', 5)": 0, + "(6715, '2024-11-22', 6)": 0, + "(6715, '2024-11-22', 7)": 0, + "(6715, '2024-11-23', 0)": 0, + "(6715, '2024-11-23', 1)": 0, + "(6715, '2024-11-23', 2)": 0, + "(6715, '2024-11-23', 3)": 0, + "(6715, '2024-11-23', 4)": 0, + "(6715, '2024-11-23', 5)": 0, + "(6715, '2024-11-23', 6)": 0, + "(6715, '2024-11-23', 7)": 0, + "(6715, '2024-11-24', 0)": 0, + "(6715, '2024-11-24', 1)": 0, + "(6715, '2024-11-24', 2)": 0, + "(6715, '2024-11-24', 3)": 0, + "(6715, '2024-11-24', 4)": 0, + "(6715, '2024-11-24', 5)": 0, + "(6715, '2024-11-24', 6)": 0, + "(6715, '2024-11-24', 7)": 0, + "(6715, '2024-11-25', 0)": 0, + "(6715, '2024-11-25', 1)": 0, + "(6715, '2024-11-25', 2)": 0, + "(6715, '2024-11-25', 3)": 0, + "(6715, '2024-11-25', 4)": 0, + "(6715, '2024-11-25', 5)": 0, + "(6715, '2024-11-25', 6)": 0, + "(6715, '2024-11-25', 7)": 0, + "(6715, '2024-11-26', 0)": 0, + "(6715, '2024-11-26', 1)": 0, + "(6715, '2024-11-26', 2)": 0, + "(6715, '2024-11-26', 3)": 0, + "(6715, '2024-11-26', 4)": 0, + "(6715, '2024-11-26', 5)": 0, + "(6715, '2024-11-26', 6)": 0, + "(6715, '2024-11-26', 7)": 0, + "(6715, '2024-11-27', 0)": 0, + "(6715, '2024-11-27', 1)": 0, + "(6715, '2024-11-27', 2)": 0, + "(6715, '2024-11-27', 3)": 0, + "(6715, '2024-11-27', 4)": 0, + "(6715, '2024-11-27', 5)": 0, + "(6715, '2024-11-27', 6)": 0, + "(6715, '2024-11-27', 7)": 0, + "(6715, '2024-11-28', 0)": 0, + "(6715, '2024-11-28', 1)": 0, + "(6715, '2024-11-28', 2)": 0, + "(6715, '2024-11-28', 3)": 0, + "(6715, '2024-11-28', 4)": 0, + "(6715, '2024-11-28', 5)": 0, + "(6715, '2024-11-28', 6)": 0, + "(6715, '2024-11-28', 7)": 0, + "(6715, '2024-11-29', 0)": 0, + "(6715, '2024-11-29', 1)": 0, + "(6715, '2024-11-29', 2)": 0, + "(6715, '2024-11-29', 3)": 0, + "(6715, '2024-11-29', 4)": 0, + "(6715, '2024-11-29', 5)": 0, + "(6715, '2024-11-29', 6)": 0, + "(6715, '2024-11-29', 7)": 0, + "(6715, '2024-11-30', 0)": 0, + "(6715, '2024-11-30', 1)": 0, + "(6715, '2024-11-30', 2)": 0, + "(6715, '2024-11-30', 3)": 0, + "(6715, '2024-11-30', 4)": 0, + "(6715, '2024-11-30', 5)": 0, + "(6715, '2024-11-30', 6)": 0, + "(6715, '2024-11-30', 7)": 0, + "(6836, '2024-11-01', 0)": 0, + "(6836, '2024-11-01', 1)": 0, + "(6836, '2024-11-01', 2)": 0, + "(6836, '2024-11-01', 3)": 1, + "(6836, '2024-11-01', 4)": 0, + "(6836, '2024-11-01', 5)": 0, + "(6836, '2024-11-01', 6)": 0, + "(6836, '2024-11-01', 7)": 0, + "(6836, '2024-11-02', 0)": 0, + "(6836, '2024-11-02', 1)": 0, + "(6836, '2024-11-02', 2)": 0, + "(6836, '2024-11-02', 3)": 0, + "(6836, '2024-11-02', 4)": 0, + "(6836, '2024-11-02', 5)": 0, + "(6836, '2024-11-02', 6)": 0, + "(6836, '2024-11-02', 7)": 0, + "(6836, '2024-11-03', 0)": 1, + "(6836, '2024-11-03', 1)": 0, + "(6836, '2024-11-03', 2)": 0, + "(6836, '2024-11-03', 3)": 0, + "(6836, '2024-11-03', 4)": 0, + "(6836, '2024-11-03', 5)": 0, + "(6836, '2024-11-03', 6)": 0, + "(6836, '2024-11-03', 7)": 0, + "(6836, '2024-11-04', 0)": 1, + "(6836, '2024-11-04', 1)": 0, + "(6836, '2024-11-04', 2)": 0, + "(6836, '2024-11-04', 3)": 0, + "(6836, '2024-11-04', 4)": 0, + "(6836, '2024-11-04', 5)": 0, + "(6836, '2024-11-04', 6)": 0, + "(6836, '2024-11-04', 7)": 0, + "(6836, '2024-11-05', 0)": 0, + "(6836, '2024-11-05', 1)": 0, + "(6836, '2024-11-05', 2)": 0, + "(6836, '2024-11-05', 3)": 1, + "(6836, '2024-11-05', 4)": 0, + "(6836, '2024-11-05', 5)": 0, + "(6836, '2024-11-05', 6)": 0, + "(6836, '2024-11-05', 7)": 0, + "(6836, '2024-11-06', 0)": 0, + "(6836, '2024-11-06', 1)": 0, + "(6836, '2024-11-06', 2)": 0, + "(6836, '2024-11-06', 3)": 0, + "(6836, '2024-11-06', 4)": 0, + "(6836, '2024-11-06', 5)": 0, + "(6836, '2024-11-06', 6)": 0, + "(6836, '2024-11-06', 7)": 0, + "(6836, '2024-11-07', 0)": 0, + "(6836, '2024-11-07', 1)": 0, + "(6836, '2024-11-07', 2)": 0, + "(6836, '2024-11-07', 3)": 0, + "(6836, '2024-11-07', 4)": 0, + "(6836, '2024-11-07', 5)": 0, + "(6836, '2024-11-07', 6)": 0, + "(6836, '2024-11-07', 7)": 0, + "(6836, '2024-11-08', 0)": 0, + "(6836, '2024-11-08', 1)": 0, + "(6836, '2024-11-08', 2)": 0, + "(6836, '2024-11-08', 3)": 0, + "(6836, '2024-11-08', 4)": 0, + "(6836, '2024-11-08', 5)": 0, + "(6836, '2024-11-08', 6)": 0, + "(6836, '2024-11-08', 7)": 0, + "(6836, '2024-11-09', 0)": 0, + "(6836, '2024-11-09', 1)": 0, + "(6836, '2024-11-09', 2)": 1, + "(6836, '2024-11-09', 3)": 0, + "(6836, '2024-11-09', 4)": 0, + "(6836, '2024-11-09', 5)": 0, + "(6836, '2024-11-09', 6)": 0, + "(6836, '2024-11-09', 7)": 0, + "(6836, '2024-11-10', 0)": 0, + "(6836, '2024-11-10', 1)": 0, + "(6836, '2024-11-10', 2)": 0, + "(6836, '2024-11-10', 3)": 1, + "(6836, '2024-11-10', 4)": 0, + "(6836, '2024-11-10', 5)": 0, + "(6836, '2024-11-10', 6)": 0, + "(6836, '2024-11-10', 7)": 0, + "(6836, '2024-11-11', 0)": 0, + "(6836, '2024-11-11', 1)": 0, + "(6836, '2024-11-11', 2)": 0, + "(6836, '2024-11-11', 3)": 0, + "(6836, '2024-11-11', 4)": 0, + "(6836, '2024-11-11', 5)": 0, + "(6836, '2024-11-11', 6)": 0, + "(6836, '2024-11-11', 7)": 0, + "(6836, '2024-11-12', 0)": 0, + "(6836, '2024-11-12', 1)": 0, + "(6836, '2024-11-12', 2)": 0, + "(6836, '2024-11-12', 3)": 0, + "(6836, '2024-11-12', 4)": 0, + "(6836, '2024-11-12', 5)": 0, + "(6836, '2024-11-12', 6)": 0, + "(6836, '2024-11-12', 7)": 0, + "(6836, '2024-11-13', 0)": 1, + "(6836, '2024-11-13', 1)": 0, + "(6836, '2024-11-13', 2)": 0, + "(6836, '2024-11-13', 3)": 0, + "(6836, '2024-11-13', 4)": 0, + "(6836, '2024-11-13', 5)": 0, + "(6836, '2024-11-13', 6)": 0, + "(6836, '2024-11-13', 7)": 0, + "(6836, '2024-11-14', 0)": 1, + "(6836, '2024-11-14', 1)": 0, + "(6836, '2024-11-14', 2)": 0, + "(6836, '2024-11-14', 3)": 0, + "(6836, '2024-11-14', 4)": 0, + "(6836, '2024-11-14', 5)": 0, + "(6836, '2024-11-14', 6)": 0, + "(6836, '2024-11-14', 7)": 0, + "(6836, '2024-11-15', 0)": 1, + "(6836, '2024-11-15', 1)": 0, + "(6836, '2024-11-15', 2)": 0, + "(6836, '2024-11-15', 3)": 0, + "(6836, '2024-11-15', 4)": 0, + "(6836, '2024-11-15', 5)": 0, + "(6836, '2024-11-15', 6)": 0, + "(6836, '2024-11-15', 7)": 0, + "(6836, '2024-11-16', 0)": 1, + "(6836, '2024-11-16', 1)": 0, + "(6836, '2024-11-16', 2)": 0, + "(6836, '2024-11-16', 3)": 0, + "(6836, '2024-11-16', 4)": 0, + "(6836, '2024-11-16', 5)": 0, + "(6836, '2024-11-16', 6)": 0, + "(6836, '2024-11-16', 7)": 0, + "(6836, '2024-11-17', 0)": 0, + "(6836, '2024-11-17', 1)": 0, + "(6836, '2024-11-17', 2)": 0, + "(6836, '2024-11-17', 3)": 0, + "(6836, '2024-11-17', 4)": 0, + "(6836, '2024-11-17', 5)": 1, + "(6836, '2024-11-17', 6)": 0, + "(6836, '2024-11-17', 7)": 0, + "(6836, '2024-11-18', 0)": 0, + "(6836, '2024-11-18', 1)": 0, + "(6836, '2024-11-18', 2)": 1, + "(6836, '2024-11-18', 3)": 0, + "(6836, '2024-11-18', 4)": 0, + "(6836, '2024-11-18', 5)": 0, + "(6836, '2024-11-18', 6)": 0, + "(6836, '2024-11-18', 7)": 0, + "(6836, '2024-11-19', 0)": 0, + "(6836, '2024-11-19', 1)": 0, + "(6836, '2024-11-19', 2)": 0, + "(6836, '2024-11-19', 3)": 1, + "(6836, '2024-11-19', 4)": 0, + "(6836, '2024-11-19', 5)": 0, + "(6836, '2024-11-19', 6)": 0, + "(6836, '2024-11-19', 7)": 0, + "(6836, '2024-11-20', 0)": 0, + "(6836, '2024-11-20', 1)": 0, + "(6836, '2024-11-20', 2)": 0, + "(6836, '2024-11-20', 3)": 0, + "(6836, '2024-11-20', 4)": 0, + "(6836, '2024-11-20', 5)": 0, + "(6836, '2024-11-20', 6)": 0, + "(6836, '2024-11-20', 7)": 0, + "(6836, '2024-11-21', 0)": 0, + "(6836, '2024-11-21', 1)": 0, + "(6836, '2024-11-21', 2)": 0, + "(6836, '2024-11-21', 3)": 0, + "(6836, '2024-11-21', 4)": 0, + "(6836, '2024-11-21', 5)": 0, + "(6836, '2024-11-21', 6)": 0, + "(6836, '2024-11-21', 7)": 0, + "(6836, '2024-11-22', 0)": 1, + "(6836, '2024-11-22', 1)": 0, + "(6836, '2024-11-22', 2)": 0, + "(6836, '2024-11-22', 3)": 0, + "(6836, '2024-11-22', 4)": 0, + "(6836, '2024-11-22', 5)": 0, + "(6836, '2024-11-22', 6)": 0, + "(6836, '2024-11-22', 7)": 0, + "(6836, '2024-11-23', 0)": 0, + "(6836, '2024-11-23', 1)": 1, + "(6836, '2024-11-23', 2)": 0, + "(6836, '2024-11-23', 3)": 0, + "(6836, '2024-11-23', 4)": 0, + "(6836, '2024-11-23', 5)": 0, + "(6836, '2024-11-23', 6)": 0, + "(6836, '2024-11-23', 7)": 0, + "(6836, '2024-11-24', 0)": 0, + "(6836, '2024-11-24', 1)": 0, + "(6836, '2024-11-24', 2)": 1, + "(6836, '2024-11-24', 3)": 0, + "(6836, '2024-11-24', 4)": 0, + "(6836, '2024-11-24', 5)": 0, + "(6836, '2024-11-24', 6)": 0, + "(6836, '2024-11-24', 7)": 0, + "(6836, '2024-11-25', 0)": 0, + "(6836, '2024-11-25', 1)": 0, + "(6836, '2024-11-25', 2)": 0, + "(6836, '2024-11-25', 3)": 0, + "(6836, '2024-11-25', 4)": 0, + "(6836, '2024-11-25', 5)": 0, + "(6836, '2024-11-25', 6)": 0, + "(6836, '2024-11-25', 7)": 0, + "(6836, '2024-11-26', 0)": 0, + "(6836, '2024-11-26', 1)": 0, + "(6836, '2024-11-26', 2)": 0, + "(6836, '2024-11-26', 3)": 0, + "(6836, '2024-11-26', 4)": 0, + "(6836, '2024-11-26', 5)": 0, + "(6836, '2024-11-26', 6)": 0, + "(6836, '2024-11-26', 7)": 0, + "(6836, '2024-11-27', 0)": 0, + "(6836, '2024-11-27', 1)": 0, + "(6836, '2024-11-27', 2)": 1, + "(6836, '2024-11-27', 3)": 0, + "(6836, '2024-11-27', 4)": 0, + "(6836, '2024-11-27', 5)": 0, + "(6836, '2024-11-27', 6)": 0, + "(6836, '2024-11-27', 7)": 0, + "(6836, '2024-11-28', 0)": 0, + "(6836, '2024-11-28', 1)": 0, + "(6836, '2024-11-28', 2)": 0, + "(6836, '2024-11-28', 3)": 0, + "(6836, '2024-11-28', 4)": 0, + "(6836, '2024-11-28', 5)": 0, + "(6836, '2024-11-28', 6)": 0, + "(6836, '2024-11-28', 7)": 0, + "(6836, '2024-11-29', 0)": 0, + "(6836, '2024-11-29', 1)": 0, + "(6836, '2024-11-29', 2)": 1, + "(6836, '2024-11-29', 3)": 0, + "(6836, '2024-11-29', 4)": 0, + "(6836, '2024-11-29', 5)": 0, + "(6836, '2024-11-29', 6)": 0, + "(6836, '2024-11-29', 7)": 0, + "(6836, '2024-11-30', 0)": 0, + "(6836, '2024-11-30', 1)": 0, + "(6836, '2024-11-30', 2)": 1, + "(6836, '2024-11-30', 3)": 0, + "(6836, '2024-11-30', 4)": 0, + "(6836, '2024-11-30', 5)": 0, + "(6836, '2024-11-30', 6)": 0, + "(6836, '2024-11-30', 7)": 0, + "(6928, '2024-11-01', 0)": 1, + "(6928, '2024-11-01', 1)": 0, + "(6928, '2024-11-01', 2)": 0, + "(6928, '2024-11-01', 3)": 0, + "(6928, '2024-11-01', 4)": 0, + "(6928, '2024-11-01', 5)": 0, + "(6928, '2024-11-01', 6)": 0, + "(6928, '2024-11-01', 7)": 0, + "(6928, '2024-11-02', 0)": 0, + "(6928, '2024-11-02', 1)": 0, + "(6928, '2024-11-02', 2)": 1, + "(6928, '2024-11-02', 3)": 0, + "(6928, '2024-11-02', 4)": 0, + "(6928, '2024-11-02', 5)": 0, + "(6928, '2024-11-02', 6)": 0, + "(6928, '2024-11-02', 7)": 0, + "(6928, '2024-11-03', 0)": 0, + "(6928, '2024-11-03', 1)": 0, + "(6928, '2024-11-03', 2)": 1, + "(6928, '2024-11-03', 3)": 0, + "(6928, '2024-11-03', 4)": 0, + "(6928, '2024-11-03', 5)": 0, + "(6928, '2024-11-03', 6)": 0, + "(6928, '2024-11-03', 7)": 0, + "(6928, '2024-11-04', 0)": 0, + "(6928, '2024-11-04', 1)": 0, + "(6928, '2024-11-04', 2)": 1, + "(6928, '2024-11-04', 3)": 0, + "(6928, '2024-11-04', 4)": 0, + "(6928, '2024-11-04', 5)": 0, + "(6928, '2024-11-04', 6)": 0, + "(6928, '2024-11-04', 7)": 0, + "(6928, '2024-11-05', 0)": 0, + "(6928, '2024-11-05', 1)": 0, + "(6928, '2024-11-05', 2)": 0, + "(6928, '2024-11-05', 3)": 0, + "(6928, '2024-11-05', 4)": 0, + "(6928, '2024-11-05', 5)": 0, + "(6928, '2024-11-05', 6)": 0, + "(6928, '2024-11-05', 7)": 0, + "(6928, '2024-11-06', 0)": 0, + "(6928, '2024-11-06', 1)": 0, + "(6928, '2024-11-06', 2)": 0, + "(6928, '2024-11-06', 3)": 0, + "(6928, '2024-11-06', 4)": 0, + "(6928, '2024-11-06', 5)": 0, + "(6928, '2024-11-06', 6)": 0, + "(6928, '2024-11-06', 7)": 0, + "(6928, '2024-11-07', 0)": 0, + "(6928, '2024-11-07', 1)": 0, + "(6928, '2024-11-07', 2)": 1, + "(6928, '2024-11-07', 3)": 0, + "(6928, '2024-11-07', 4)": 0, + "(6928, '2024-11-07', 5)": 0, + "(6928, '2024-11-07', 6)": 0, + "(6928, '2024-11-07', 7)": 0, + "(6928, '2024-11-08', 0)": 0, + "(6928, '2024-11-08', 1)": 0, + "(6928, '2024-11-08', 2)": 1, + "(6928, '2024-11-08', 3)": 0, + "(6928, '2024-11-08', 4)": 0, + "(6928, '2024-11-08', 5)": 0, + "(6928, '2024-11-08', 6)": 0, + "(6928, '2024-11-08', 7)": 0, + "(6928, '2024-11-09', 0)": 0, + "(6928, '2024-11-09', 1)": 0, + "(6928, '2024-11-09', 2)": 0, + "(6928, '2024-11-09', 3)": 0, + "(6928, '2024-11-09', 4)": 0, + "(6928, '2024-11-09', 5)": 0, + "(6928, '2024-11-09', 6)": 0, + "(6928, '2024-11-09', 7)": 0, + "(6928, '2024-11-10', 0)": 0, + "(6928, '2024-11-10', 1)": 0, + "(6928, '2024-11-10', 2)": 1, + "(6928, '2024-11-10', 3)": 0, + "(6928, '2024-11-10', 4)": 0, + "(6928, '2024-11-10', 5)": 0, + "(6928, '2024-11-10', 6)": 0, + "(6928, '2024-11-10', 7)": 0, + "(6928, '2024-11-11', 0)": 0, + "(6928, '2024-11-11', 1)": 0, + "(6928, '2024-11-11', 2)": 0, + "(6928, '2024-11-11', 3)": 0, + "(6928, '2024-11-11', 4)": 0, + "(6928, '2024-11-11', 5)": 0, + "(6928, '2024-11-11', 6)": 0, + "(6928, '2024-11-11', 7)": 0, + "(6928, '2024-11-12', 0)": 1, + "(6928, '2024-11-12', 1)": 0, + "(6928, '2024-11-12', 2)": 0, + "(6928, '2024-11-12', 3)": 0, + "(6928, '2024-11-12', 4)": 0, + "(6928, '2024-11-12', 5)": 0, + "(6928, '2024-11-12', 6)": 0, + "(6928, '2024-11-12', 7)": 0, + "(6928, '2024-11-13', 0)": 1, + "(6928, '2024-11-13', 1)": 0, + "(6928, '2024-11-13', 2)": 0, + "(6928, '2024-11-13', 3)": 0, + "(6928, '2024-11-13', 4)": 0, + "(6928, '2024-11-13', 5)": 0, + "(6928, '2024-11-13', 6)": 0, + "(6928, '2024-11-13', 7)": 0, + "(6928, '2024-11-14', 0)": 0, + "(6928, '2024-11-14', 1)": 0, + "(6928, '2024-11-14', 2)": 0, + "(6928, '2024-11-14', 3)": 0, + "(6928, '2024-11-14', 4)": 0, + "(6928, '2024-11-14', 5)": 0, + "(6928, '2024-11-14', 6)": 0, + "(6928, '2024-11-14', 7)": 0, + "(6928, '2024-11-15', 0)": 1, + "(6928, '2024-11-15', 1)": 0, + "(6928, '2024-11-15', 2)": 0, + "(6928, '2024-11-15', 3)": 0, + "(6928, '2024-11-15', 4)": 0, + "(6928, '2024-11-15', 5)": 0, + "(6928, '2024-11-15', 6)": 0, + "(6928, '2024-11-15', 7)": 0, + "(6928, '2024-11-16', 0)": 1, + "(6928, '2024-11-16', 1)": 0, + "(6928, '2024-11-16', 2)": 0, + "(6928, '2024-11-16', 3)": 0, + "(6928, '2024-11-16', 4)": 0, + "(6928, '2024-11-16', 5)": 0, + "(6928, '2024-11-16', 6)": 0, + "(6928, '2024-11-16', 7)": 0, + "(6928, '2024-11-17', 0)": 0, + "(6928, '2024-11-17', 1)": 0, + "(6928, '2024-11-17', 2)": 1, + "(6928, '2024-11-17', 3)": 0, + "(6928, '2024-11-17', 4)": 0, + "(6928, '2024-11-17', 5)": 0, + "(6928, '2024-11-17', 6)": 0, + "(6928, '2024-11-17', 7)": 0, + "(6928, '2024-11-18', 0)": 0, + "(6928, '2024-11-18', 1)": 0, + "(6928, '2024-11-18', 2)": 1, + "(6928, '2024-11-18', 3)": 0, + "(6928, '2024-11-18', 4)": 0, + "(6928, '2024-11-18', 5)": 0, + "(6928, '2024-11-18', 6)": 0, + "(6928, '2024-11-18', 7)": 0, + "(6928, '2024-11-19', 0)": 0, + "(6928, '2024-11-19', 1)": 0, + "(6928, '2024-11-19', 2)": 1, + "(6928, '2024-11-19', 3)": 0, + "(6928, '2024-11-19', 4)": 0, + "(6928, '2024-11-19', 5)": 0, + "(6928, '2024-11-19', 6)": 0, + "(6928, '2024-11-19', 7)": 0, + "(6928, '2024-11-20', 0)": 0, + "(6928, '2024-11-20', 1)": 0, + "(6928, '2024-11-20', 2)": 0, + "(6928, '2024-11-20', 3)": 0, + "(6928, '2024-11-20', 4)": 0, + "(6928, '2024-11-20', 5)": 0, + "(6928, '2024-11-20', 6)": 0, + "(6928, '2024-11-20', 7)": 0, + "(6928, '2024-11-21', 0)": 0, + "(6928, '2024-11-21', 1)": 0, + "(6928, '2024-11-21', 2)": 0, + "(6928, '2024-11-21', 3)": 0, + "(6928, '2024-11-21', 4)": 0, + "(6928, '2024-11-21', 5)": 0, + "(6928, '2024-11-21', 6)": 0, + "(6928, '2024-11-21', 7)": 0, + "(6928, '2024-11-22', 0)": 1, + "(6928, '2024-11-22', 1)": 0, + "(6928, '2024-11-22', 2)": 0, + "(6928, '2024-11-22', 3)": 0, + "(6928, '2024-11-22', 4)": 0, + "(6928, '2024-11-22', 5)": 0, + "(6928, '2024-11-22', 6)": 0, + "(6928, '2024-11-22', 7)": 0, + "(6928, '2024-11-23', 0)": 0, + "(6928, '2024-11-23', 1)": 0, + "(6928, '2024-11-23', 2)": 0, + "(6928, '2024-11-23', 3)": 1, + "(6928, '2024-11-23', 4)": 0, + "(6928, '2024-11-23', 5)": 0, + "(6928, '2024-11-23', 6)": 0, + "(6928, '2024-11-23', 7)": 0, + "(6928, '2024-11-24', 0)": 0, + "(6928, '2024-11-24', 1)": 0, + "(6928, '2024-11-24', 2)": 0, + "(6928, '2024-11-24', 3)": 0, + "(6928, '2024-11-24', 4)": 0, + "(6928, '2024-11-24', 5)": 0, + "(6928, '2024-11-24', 6)": 0, + "(6928, '2024-11-24', 7)": 0, + "(6928, '2024-11-25', 0)": 0, + "(6928, '2024-11-25', 1)": 0, + "(6928, '2024-11-25', 2)": 0, + "(6928, '2024-11-25', 3)": 0, + "(6928, '2024-11-25', 4)": 0, + "(6928, '2024-11-25', 5)": 0, + "(6928, '2024-11-25', 6)": 0, + "(6928, '2024-11-25', 7)": 0, + "(6928, '2024-11-26', 0)": 1, + "(6928, '2024-11-26', 1)": 0, + "(6928, '2024-11-26', 2)": 0, + "(6928, '2024-11-26', 3)": 0, + "(6928, '2024-11-26', 4)": 0, + "(6928, '2024-11-26', 5)": 0, + "(6928, '2024-11-26', 6)": 0, + "(6928, '2024-11-26', 7)": 0, + "(6928, '2024-11-27', 0)": 0, + "(6928, '2024-11-27', 1)": 0, + "(6928, '2024-11-27', 2)": 0, + "(6928, '2024-11-27', 3)": 0, + "(6928, '2024-11-27', 4)": 0, + "(6928, '2024-11-27', 5)": 0, + "(6928, '2024-11-27', 6)": 0, + "(6928, '2024-11-27', 7)": 0, + "(6928, '2024-11-28', 0)": 1, + "(6928, '2024-11-28', 1)": 0, + "(6928, '2024-11-28', 2)": 0, + "(6928, '2024-11-28', 3)": 0, + "(6928, '2024-11-28', 4)": 0, + "(6928, '2024-11-28', 5)": 0, + "(6928, '2024-11-28', 6)": 0, + "(6928, '2024-11-28', 7)": 0, + "(6928, '2024-11-29', 0)": 0, + "(6928, '2024-11-29', 1)": 0, + "(6928, '2024-11-29', 2)": 1, + "(6928, '2024-11-29', 3)": 0, + "(6928, '2024-11-29', 4)": 0, + "(6928, '2024-11-29', 5)": 0, + "(6928, '2024-11-29', 6)": 0, + "(6928, '2024-11-29', 7)": 0, + "(6928, '2024-11-30', 0)": 0, + "(6928, '2024-11-30', 1)": 0, + "(6928, '2024-11-30', 2)": 0, + "(6928, '2024-11-30', 3)": 1, + "(6928, '2024-11-30', 4)": 0, + "(6928, '2024-11-30', 5)": 0, + "(6928, '2024-11-30', 6)": 0, + "(6928, '2024-11-30', 7)": 0, + "(7, '2024-11-01', 0)": 0, + "(7, '2024-11-01', 1)": 0, + "(7, '2024-11-01', 2)": 0, + "(7, '2024-11-01', 3)": 0, + "(7, '2024-11-01', 4)": 0, + "(7, '2024-11-01', 5)": 0, + "(7, '2024-11-01', 6)": 0, + "(7, '2024-11-01', 7)": 0, + "(7, '2024-11-02', 0)": 1, + "(7, '2024-11-02', 1)": 0, + "(7, '2024-11-02', 2)": 0, + "(7, '2024-11-02', 3)": 0, + "(7, '2024-11-02', 4)": 0, + "(7, '2024-11-02', 5)": 0, + "(7, '2024-11-02', 6)": 0, + "(7, '2024-11-02', 7)": 0, + "(7, '2024-11-03', 0)": 1, + "(7, '2024-11-03', 1)": 0, + "(7, '2024-11-03', 2)": 0, + "(7, '2024-11-03', 3)": 0, + "(7, '2024-11-03', 4)": 0, + "(7, '2024-11-03', 5)": 0, + "(7, '2024-11-03', 6)": 0, + "(7, '2024-11-03', 7)": 0, + "(7, '2024-11-04', 0)": 0, + "(7, '2024-11-04', 1)": 0, + "(7, '2024-11-04', 2)": 1, + "(7, '2024-11-04', 3)": 0, + "(7, '2024-11-04', 4)": 0, + "(7, '2024-11-04', 5)": 0, + "(7, '2024-11-04', 6)": 0, + "(7, '2024-11-04', 7)": 0, + "(7, '2024-11-05', 0)": 0, + "(7, '2024-11-05', 1)": 0, + "(7, '2024-11-05', 2)": 0, + "(7, '2024-11-05', 3)": 0, + "(7, '2024-11-05', 4)": 0, + "(7, '2024-11-05', 5)": 0, + "(7, '2024-11-05', 6)": 0, + "(7, '2024-11-05', 7)": 0, + "(7, '2024-11-06', 0)": 0, + "(7, '2024-11-06', 1)": 0, + "(7, '2024-11-06', 2)": 0, + "(7, '2024-11-06', 3)": 0, + "(7, '2024-11-06', 4)": 0, + "(7, '2024-11-06', 5)": 0, + "(7, '2024-11-06', 6)": 0, + "(7, '2024-11-06', 7)": 0, + "(7, '2024-11-07', 0)": 0, + "(7, '2024-11-07', 1)": 0, + "(7, '2024-11-07', 2)": 0, + "(7, '2024-11-07', 3)": 0, + "(7, '2024-11-07', 4)": 0, + "(7, '2024-11-07', 5)": 0, + "(7, '2024-11-07', 6)": 0, + "(7, '2024-11-07', 7)": 0, + "(7, '2024-11-08', 0)": 0, + "(7, '2024-11-08', 1)": 0, + "(7, '2024-11-08', 2)": 0, + "(7, '2024-11-08', 3)": 0, + "(7, '2024-11-08', 4)": 0, + "(7, '2024-11-08', 5)": 0, + "(7, '2024-11-08', 6)": 0, + "(7, '2024-11-08', 7)": 0, + "(7, '2024-11-09', 0)": 0, + "(7, '2024-11-09', 1)": 0, + "(7, '2024-11-09', 2)": 1, + "(7, '2024-11-09', 3)": 0, + "(7, '2024-11-09', 4)": 0, + "(7, '2024-11-09', 5)": 0, + "(7, '2024-11-09', 6)": 0, + "(7, '2024-11-09', 7)": 0, + "(7, '2024-11-10', 0)": 0, + "(7, '2024-11-10', 1)": 0, + "(7, '2024-11-10', 2)": 0, + "(7, '2024-11-10', 3)": 0, + "(7, '2024-11-10', 4)": 0, + "(7, '2024-11-10', 5)": 0, + "(7, '2024-11-10', 6)": 0, + "(7, '2024-11-10', 7)": 0, + "(7, '2024-11-11', 0)": 0, + "(7, '2024-11-11', 1)": 0, + "(7, '2024-11-11', 2)": 1, + "(7, '2024-11-11', 3)": 0, + "(7, '2024-11-11', 4)": 0, + "(7, '2024-11-11', 5)": 0, + "(7, '2024-11-11', 6)": 0, + "(7, '2024-11-11', 7)": 0, + "(7, '2024-11-12', 0)": 0, + "(7, '2024-11-12', 1)": 0, + "(7, '2024-11-12', 2)": 0, + "(7, '2024-11-12', 3)": 0, + "(7, '2024-11-12', 4)": 0, + "(7, '2024-11-12', 5)": 0, + "(7, '2024-11-12', 6)": 0, + "(7, '2024-11-12', 7)": 0, + "(7, '2024-11-13', 0)": 0, + "(7, '2024-11-13', 1)": 0, + "(7, '2024-11-13', 2)": 1, + "(7, '2024-11-13', 3)": 0, + "(7, '2024-11-13', 4)": 0, + "(7, '2024-11-13', 5)": 0, + "(7, '2024-11-13', 6)": 0, + "(7, '2024-11-13', 7)": 0, + "(7, '2024-11-14', 0)": 0, + "(7, '2024-11-14', 1)": 0, + "(7, '2024-11-14', 2)": 0, + "(7, '2024-11-14', 3)": 0, + "(7, '2024-11-14', 4)": 0, + "(7, '2024-11-14', 5)": 0, + "(7, '2024-11-14', 6)": 0, + "(7, '2024-11-14', 7)": 0, + "(7, '2024-11-15', 0)": 0, + "(7, '2024-11-15', 1)": 0, + "(7, '2024-11-15', 2)": 0, + "(7, '2024-11-15', 3)": 0, + "(7, '2024-11-15', 4)": 0, + "(7, '2024-11-15', 5)": 0, + "(7, '2024-11-15', 6)": 0, + "(7, '2024-11-15', 7)": 0, + "(7, '2024-11-16', 0)": 0, + "(7, '2024-11-16', 1)": 0, + "(7, '2024-11-16', 2)": 0, + "(7, '2024-11-16', 3)": 0, + "(7, '2024-11-16', 4)": 0, + "(7, '2024-11-16', 5)": 0, + "(7, '2024-11-16', 6)": 0, + "(7, '2024-11-16', 7)": 0, + "(7, '2024-11-17', 0)": 1, + "(7, '2024-11-17', 1)": 0, + "(7, '2024-11-17', 2)": 0, + "(7, '2024-11-17', 3)": 0, + "(7, '2024-11-17', 4)": 0, + "(7, '2024-11-17', 5)": 0, + "(7, '2024-11-17', 6)": 0, + "(7, '2024-11-17', 7)": 0, + "(7, '2024-11-18', 0)": 0, + "(7, '2024-11-18', 1)": 0, + "(7, '2024-11-18', 2)": 0, + "(7, '2024-11-18', 3)": 0, + "(7, '2024-11-18', 4)": 0, + "(7, '2024-11-18', 5)": 0, + "(7, '2024-11-18', 6)": 0, + "(7, '2024-11-18', 7)": 0, + "(7, '2024-11-19', 0)": 0, + "(7, '2024-11-19', 1)": 0, + "(7, '2024-11-19', 2)": 0, + "(7, '2024-11-19', 3)": 0, + "(7, '2024-11-19', 4)": 0, + "(7, '2024-11-19', 5)": 0, + "(7, '2024-11-19', 6)": 0, + "(7, '2024-11-19', 7)": 0, + "(7, '2024-11-20', 0)": 0, + "(7, '2024-11-20', 1)": 0, + "(7, '2024-11-20', 2)": 1, + "(7, '2024-11-20', 3)": 0, + "(7, '2024-11-20', 4)": 0, + "(7, '2024-11-20', 5)": 0, + "(7, '2024-11-20', 6)": 0, + "(7, '2024-11-20', 7)": 0, + "(7, '2024-11-21', 0)": 0, + "(7, '2024-11-21', 1)": 0, + "(7, '2024-11-21', 2)": 0, + "(7, '2024-11-21', 3)": 0, + "(7, '2024-11-21', 4)": 0, + "(7, '2024-11-21', 5)": 0, + "(7, '2024-11-21', 6)": 0, + "(7, '2024-11-21', 7)": 0, + "(7, '2024-11-22', 0)": 0, + "(7, '2024-11-22', 1)": 0, + "(7, '2024-11-22', 2)": 0, + "(7, '2024-11-22', 3)": 0, + "(7, '2024-11-22', 4)": 0, + "(7, '2024-11-22', 5)": 0, + "(7, '2024-11-22', 6)": 0, + "(7, '2024-11-22', 7)": 0, + "(7, '2024-11-23', 0)": 1, + "(7, '2024-11-23', 1)": 0, + "(7, '2024-11-23', 2)": 0, + "(7, '2024-11-23', 3)": 0, + "(7, '2024-11-23', 4)": 0, + "(7, '2024-11-23', 5)": 0, + "(7, '2024-11-23', 6)": 0, + "(7, '2024-11-23', 7)": 0, + "(7, '2024-11-24', 0)": 0, + "(7, '2024-11-24', 1)": 0, + "(7, '2024-11-24', 2)": 0, + "(7, '2024-11-24', 3)": 0, + "(7, '2024-11-24', 4)": 0, + "(7, '2024-11-24', 5)": 0, + "(7, '2024-11-24', 6)": 0, + "(7, '2024-11-24', 7)": 0, + "(7, '2024-11-25', 0)": 1, + "(7, '2024-11-25', 1)": 0, + "(7, '2024-11-25', 2)": 0, + "(7, '2024-11-25', 3)": 0, + "(7, '2024-11-25', 4)": 0, + "(7, '2024-11-25', 5)": 0, + "(7, '2024-11-25', 6)": 0, + "(7, '2024-11-25', 7)": 0, + "(7, '2024-11-26', 0)": 0, + "(7, '2024-11-26', 1)": 0, + "(7, '2024-11-26', 2)": 0, + "(7, '2024-11-26', 3)": 0, + "(7, '2024-11-26', 4)": 0, + "(7, '2024-11-26', 5)": 0, + "(7, '2024-11-26', 6)": 0, + "(7, '2024-11-26', 7)": 0, + "(7, '2024-11-27', 0)": 0, + "(7, '2024-11-27', 1)": 0, + "(7, '2024-11-27', 2)": 0, + "(7, '2024-11-27', 3)": 0, + "(7, '2024-11-27', 4)": 0, + "(7, '2024-11-27', 5)": 0, + "(7, '2024-11-27', 6)": 0, + "(7, '2024-11-27', 7)": 0, + "(7, '2024-11-28', 0)": 0, + "(7, '2024-11-28', 1)": 0, + "(7, '2024-11-28', 2)": 0, + "(7, '2024-11-28', 3)": 0, + "(7, '2024-11-28', 4)": 0, + "(7, '2024-11-28', 5)": 0, + "(7, '2024-11-28', 6)": 0, + "(7, '2024-11-28', 7)": 0, + "(7, '2024-11-29', 0)": 0, + "(7, '2024-11-29', 1)": 0, + "(7, '2024-11-29', 2)": 0, + "(7, '2024-11-29', 3)": 0, + "(7, '2024-11-29', 4)": 0, + "(7, '2024-11-29', 5)": 0, + "(7, '2024-11-29', 6)": 0, + "(7, '2024-11-29', 7)": 0, + "(7, '2024-11-30', 0)": 0, + "(7, '2024-11-30', 1)": 0, + "(7, '2024-11-30', 2)": 0, + "(7, '2024-11-30', 3)": 0, + "(7, '2024-11-30', 4)": 0, + "(7, '2024-11-30', 5)": 0, + "(7, '2024-11-30', 6)": 0, + "(7, '2024-11-30', 7)": 0, + "(7496, '2024-11-01', 0)": 0, + "(7496, '2024-11-01', 1)": 0, + "(7496, '2024-11-01', 2)": 0, + "(7496, '2024-11-01', 3)": 0, + "(7496, '2024-11-01', 4)": 0, + "(7496, '2024-11-01', 5)": 0, + "(7496, '2024-11-01', 6)": 0, + "(7496, '2024-11-01', 7)": 0, + "(7496, '2024-11-02', 0)": 0, + "(7496, '2024-11-02', 1)": 0, + "(7496, '2024-11-02', 2)": 0, + "(7496, '2024-11-02', 3)": 0, + "(7496, '2024-11-02', 4)": 0, + "(7496, '2024-11-02', 5)": 0, + "(7496, '2024-11-02', 6)": 0, + "(7496, '2024-11-02', 7)": 0, + "(7496, '2024-11-03', 0)": 0, + "(7496, '2024-11-03', 1)": 0, + "(7496, '2024-11-03', 2)": 0, + "(7496, '2024-11-03', 3)": 0, + "(7496, '2024-11-03', 4)": 0, + "(7496, '2024-11-03', 5)": 0, + "(7496, '2024-11-03', 6)": 0, + "(7496, '2024-11-03', 7)": 0, + "(7496, '2024-11-04', 0)": 0, + "(7496, '2024-11-04', 1)": 0, + "(7496, '2024-11-04', 2)": 0, + "(7496, '2024-11-04', 3)": 0, + "(7496, '2024-11-04', 4)": 0, + "(7496, '2024-11-04', 5)": 0, + "(7496, '2024-11-04', 6)": 0, + "(7496, '2024-11-04', 7)": 0, + "(7496, '2024-11-05', 0)": 0, + "(7496, '2024-11-05', 1)": 0, + "(7496, '2024-11-05', 2)": 0, + "(7496, '2024-11-05', 3)": 0, + "(7496, '2024-11-05', 4)": 0, + "(7496, '2024-11-05', 5)": 0, + "(7496, '2024-11-05', 6)": 0, + "(7496, '2024-11-05', 7)": 0, + "(7496, '2024-11-06', 0)": 0, + "(7496, '2024-11-06', 1)": 0, + "(7496, '2024-11-06', 2)": 0, + "(7496, '2024-11-06', 3)": 0, + "(7496, '2024-11-06', 4)": 0, + "(7496, '2024-11-06', 5)": 0, + "(7496, '2024-11-06', 6)": 0, + "(7496, '2024-11-06', 7)": 0, + "(7496, '2024-11-07', 0)": 0, + "(7496, '2024-11-07', 1)": 0, + "(7496, '2024-11-07', 2)": 0, + "(7496, '2024-11-07', 3)": 0, + "(7496, '2024-11-07', 4)": 0, + "(7496, '2024-11-07', 5)": 0, + "(7496, '2024-11-07', 6)": 0, + "(7496, '2024-11-07', 7)": 0, + "(7496, '2024-11-08', 0)": 0, + "(7496, '2024-11-08', 1)": 0, + "(7496, '2024-11-08', 2)": 0, + "(7496, '2024-11-08', 3)": 0, + "(7496, '2024-11-08', 4)": 0, + "(7496, '2024-11-08', 5)": 0, + "(7496, '2024-11-08', 6)": 0, + "(7496, '2024-11-08', 7)": 0, + "(7496, '2024-11-09', 0)": 0, + "(7496, '2024-11-09', 1)": 0, + "(7496, '2024-11-09', 2)": 0, + "(7496, '2024-11-09', 3)": 0, + "(7496, '2024-11-09', 4)": 0, + "(7496, '2024-11-09', 5)": 0, + "(7496, '2024-11-09', 6)": 0, + "(7496, '2024-11-09', 7)": 0, + "(7496, '2024-11-10', 0)": 0, + "(7496, '2024-11-10', 1)": 0, + "(7496, '2024-11-10', 2)": 0, + "(7496, '2024-11-10', 3)": 0, + "(7496, '2024-11-10', 4)": 0, + "(7496, '2024-11-10', 5)": 0, + "(7496, '2024-11-10', 6)": 0, + "(7496, '2024-11-10', 7)": 0, + "(7496, '2024-11-11', 0)": 0, + "(7496, '2024-11-11', 1)": 0, + "(7496, '2024-11-11', 2)": 0, + "(7496, '2024-11-11', 3)": 0, + "(7496, '2024-11-11', 4)": 0, + "(7496, '2024-11-11', 5)": 0, + "(7496, '2024-11-11', 6)": 0, + "(7496, '2024-11-11', 7)": 0, + "(7496, '2024-11-12', 0)": 0, + "(7496, '2024-11-12', 1)": 0, + "(7496, '2024-11-12', 2)": 0, + "(7496, '2024-11-12', 3)": 0, + "(7496, '2024-11-12', 4)": 0, + "(7496, '2024-11-12', 5)": 0, + "(7496, '2024-11-12', 6)": 0, + "(7496, '2024-11-12', 7)": 0, + "(7496, '2024-11-13', 0)": 0, + "(7496, '2024-11-13', 1)": 0, + "(7496, '2024-11-13', 2)": 0, + "(7496, '2024-11-13', 3)": 0, + "(7496, '2024-11-13', 4)": 0, + "(7496, '2024-11-13', 5)": 0, + "(7496, '2024-11-13', 6)": 0, + "(7496, '2024-11-13', 7)": 0, + "(7496, '2024-11-14', 0)": 0, + "(7496, '2024-11-14', 1)": 0, + "(7496, '2024-11-14', 2)": 0, + "(7496, '2024-11-14', 3)": 0, + "(7496, '2024-11-14', 4)": 0, + "(7496, '2024-11-14', 5)": 0, + "(7496, '2024-11-14', 6)": 0, + "(7496, '2024-11-14', 7)": 0, + "(7496, '2024-11-15', 0)": 0, + "(7496, '2024-11-15', 1)": 0, + "(7496, '2024-11-15', 2)": 0, + "(7496, '2024-11-15', 3)": 0, + "(7496, '2024-11-15', 4)": 0, + "(7496, '2024-11-15', 5)": 0, + "(7496, '2024-11-15', 6)": 1, + "(7496, '2024-11-15', 7)": 0, + "(7496, '2024-11-16', 0)": 0, + "(7496, '2024-11-16', 1)": 0, + "(7496, '2024-11-16', 2)": 0, + "(7496, '2024-11-16', 3)": 0, + "(7496, '2024-11-16', 4)": 0, + "(7496, '2024-11-16', 5)": 0, + "(7496, '2024-11-16', 6)": 0, + "(7496, '2024-11-16', 7)": 0, + "(7496, '2024-11-17', 0)": 0, + "(7496, '2024-11-17', 1)": 0, + "(7496, '2024-11-17', 2)": 0, + "(7496, '2024-11-17', 3)": 0, + "(7496, '2024-11-17', 4)": 0, + "(7496, '2024-11-17', 5)": 1, + "(7496, '2024-11-17', 6)": 0, + "(7496, '2024-11-17', 7)": 0, + "(7496, '2024-11-18', 0)": 0, + "(7496, '2024-11-18', 1)": 0, + "(7496, '2024-11-18', 2)": 0, + "(7496, '2024-11-18', 3)": 0, + "(7496, '2024-11-18', 4)": 0, + "(7496, '2024-11-18', 5)": 1, + "(7496, '2024-11-18', 6)": 0, + "(7496, '2024-11-18', 7)": 0, + "(7496, '2024-11-19', 0)": 0, + "(7496, '2024-11-19', 1)": 0, + "(7496, '2024-11-19', 2)": 0, + "(7496, '2024-11-19', 3)": 0, + "(7496, '2024-11-19', 4)": 0, + "(7496, '2024-11-19', 5)": 0, + "(7496, '2024-11-19', 6)": 0, + "(7496, '2024-11-19', 7)": 1, + "(7496, '2024-11-20', 0)": 0, + "(7496, '2024-11-20', 1)": 0, + "(7496, '2024-11-20', 2)": 0, + "(7496, '2024-11-20', 3)": 0, + "(7496, '2024-11-20', 4)": 0, + "(7496, '2024-11-20', 5)": 0, + "(7496, '2024-11-20', 6)": 0, + "(7496, '2024-11-20', 7)": 1, + "(7496, '2024-11-21', 0)": 0, + "(7496, '2024-11-21', 1)": 0, + "(7496, '2024-11-21', 2)": 0, + "(7496, '2024-11-21', 3)": 0, + "(7496, '2024-11-21', 4)": 0, + "(7496, '2024-11-21', 5)": 0, + "(7496, '2024-11-21', 6)": 0, + "(7496, '2024-11-21', 7)": 0, + "(7496, '2024-11-22', 0)": 0, + "(7496, '2024-11-22', 1)": 0, + "(7496, '2024-11-22', 2)": 0, + "(7496, '2024-11-22', 3)": 0, + "(7496, '2024-11-22', 4)": 0, + "(7496, '2024-11-22', 5)": 0, + "(7496, '2024-11-22', 6)": 0, + "(7496, '2024-11-22', 7)": 0, + "(7496, '2024-11-23', 0)": 0, + "(7496, '2024-11-23', 1)": 0, + "(7496, '2024-11-23', 2)": 0, + "(7496, '2024-11-23', 3)": 0, + "(7496, '2024-11-23', 4)": 0, + "(7496, '2024-11-23', 5)": 0, + "(7496, '2024-11-23', 6)": 0, + "(7496, '2024-11-23', 7)": 0, + "(7496, '2024-11-24', 0)": 0, + "(7496, '2024-11-24', 1)": 0, + "(7496, '2024-11-24', 2)": 0, + "(7496, '2024-11-24', 3)": 0, + "(7496, '2024-11-24', 4)": 0, + "(7496, '2024-11-24', 5)": 0, + "(7496, '2024-11-24', 6)": 0, + "(7496, '2024-11-24', 7)": 0, + "(7496, '2024-11-25', 0)": 0, + "(7496, '2024-11-25', 1)": 0, + "(7496, '2024-11-25', 2)": 0, + "(7496, '2024-11-25', 3)": 0, + "(7496, '2024-11-25', 4)": 0, + "(7496, '2024-11-25', 5)": 1, + "(7496, '2024-11-25', 6)": 0, + "(7496, '2024-11-25', 7)": 0, + "(7496, '2024-11-26', 0)": 0, + "(7496, '2024-11-26', 1)": 0, + "(7496, '2024-11-26', 2)": 0, + "(7496, '2024-11-26', 3)": 0, + "(7496, '2024-11-26', 4)": 0, + "(7496, '2024-11-26', 5)": 1, + "(7496, '2024-11-26', 6)": 0, + "(7496, '2024-11-26', 7)": 0, + "(7496, '2024-11-27', 0)": 0, + "(7496, '2024-11-27', 1)": 0, + "(7496, '2024-11-27', 2)": 0, + "(7496, '2024-11-27', 3)": 0, + "(7496, '2024-11-27', 4)": 0, + "(7496, '2024-11-27', 5)": 1, + "(7496, '2024-11-27', 6)": 0, + "(7496, '2024-11-27', 7)": 0, + "(7496, '2024-11-28', 0)": 0, + "(7496, '2024-11-28', 1)": 0, + "(7496, '2024-11-28', 2)": 0, + "(7496, '2024-11-28', 3)": 0, + "(7496, '2024-11-28', 4)": 0, + "(7496, '2024-11-28', 5)": 1, + "(7496, '2024-11-28', 6)": 0, + "(7496, '2024-11-28', 7)": 0, + "(7496, '2024-11-29', 0)": 0, + "(7496, '2024-11-29', 1)": 0, + "(7496, '2024-11-29', 2)": 0, + "(7496, '2024-11-29', 3)": 0, + "(7496, '2024-11-29', 4)": 0, + "(7496, '2024-11-29', 5)": 1, + "(7496, '2024-11-29', 6)": 0, + "(7496, '2024-11-29', 7)": 0, + "(7496, '2024-11-30', 0)": 0, + "(7496, '2024-11-30', 1)": 0, + "(7496, '2024-11-30', 2)": 0, + "(7496, '2024-11-30', 3)": 0, + "(7496, '2024-11-30', 4)": 0, + "(7496, '2024-11-30', 5)": 0, + "(7496, '2024-11-30', 6)": 0, + "(7496, '2024-11-30', 7)": 0, + "(7603, '2024-11-01', 0)": 1, + "(7603, '2024-11-01', 1)": 0, + "(7603, '2024-11-01', 2)": 0, + "(7603, '2024-11-01', 3)": 0, + "(7603, '2024-11-01', 4)": 0, + "(7603, '2024-11-01', 5)": 0, + "(7603, '2024-11-01', 6)": 0, + "(7603, '2024-11-01', 7)": 0, + "(7603, '2024-11-02', 0)": 0, + "(7603, '2024-11-02', 1)": 1, + "(7603, '2024-11-02', 2)": 0, + "(7603, '2024-11-02', 3)": 0, + "(7603, '2024-11-02', 4)": 0, + "(7603, '2024-11-02', 5)": 0, + "(7603, '2024-11-02', 6)": 0, + "(7603, '2024-11-02', 7)": 0, + "(7603, '2024-11-03', 0)": 0, + "(7603, '2024-11-03', 1)": 1, + "(7603, '2024-11-03', 2)": 0, + "(7603, '2024-11-03', 3)": 0, + "(7603, '2024-11-03', 4)": 0, + "(7603, '2024-11-03', 5)": 0, + "(7603, '2024-11-03', 6)": 0, + "(7603, '2024-11-03', 7)": 0, + "(7603, '2024-11-04', 0)": 0, + "(7603, '2024-11-04', 1)": 1, + "(7603, '2024-11-04', 2)": 0, + "(7603, '2024-11-04', 3)": 0, + "(7603, '2024-11-04', 4)": 0, + "(7603, '2024-11-04', 5)": 0, + "(7603, '2024-11-04', 6)": 0, + "(7603, '2024-11-04', 7)": 0, + "(7603, '2024-11-05', 0)": 0, + "(7603, '2024-11-05', 1)": 0, + "(7603, '2024-11-05', 2)": 0, + "(7603, '2024-11-05', 3)": 0, + "(7603, '2024-11-05', 4)": 0, + "(7603, '2024-11-05', 5)": 0, + "(7603, '2024-11-05', 6)": 0, + "(7603, '2024-11-05', 7)": 0, + "(7603, '2024-11-06', 0)": 0, + "(7603, '2024-11-06', 1)": 0, + "(7603, '2024-11-06', 2)": 1, + "(7603, '2024-11-06', 3)": 0, + "(7603, '2024-11-06', 4)": 0, + "(7603, '2024-11-06', 5)": 0, + "(7603, '2024-11-06', 6)": 0, + "(7603, '2024-11-06', 7)": 0, + "(7603, '2024-11-07', 0)": 0, + "(7603, '2024-11-07', 1)": 0, + "(7603, '2024-11-07', 2)": 1, + "(7603, '2024-11-07', 3)": 0, + "(7603, '2024-11-07', 4)": 0, + "(7603, '2024-11-07', 5)": 0, + "(7603, '2024-11-07', 6)": 0, + "(7603, '2024-11-07', 7)": 0, + "(7603, '2024-11-08', 0)": 0, + "(7603, '2024-11-08', 1)": 0, + "(7603, '2024-11-08', 2)": 1, + "(7603, '2024-11-08', 3)": 0, + "(7603, '2024-11-08', 4)": 0, + "(7603, '2024-11-08', 5)": 0, + "(7603, '2024-11-08', 6)": 0, + "(7603, '2024-11-08', 7)": 0, + "(7603, '2024-11-09', 0)": 0, + "(7603, '2024-11-09', 1)": 1, + "(7603, '2024-11-09', 2)": 0, + "(7603, '2024-11-09', 3)": 0, + "(7603, '2024-11-09', 4)": 0, + "(7603, '2024-11-09', 5)": 0, + "(7603, '2024-11-09', 6)": 0, + "(7603, '2024-11-09', 7)": 0, + "(7603, '2024-11-10', 0)": 0, + "(7603, '2024-11-10', 1)": 0, + "(7603, '2024-11-10', 2)": 0, + "(7603, '2024-11-10', 3)": 0, + "(7603, '2024-11-10', 4)": 0, + "(7603, '2024-11-10', 5)": 0, + "(7603, '2024-11-10', 6)": 0, + "(7603, '2024-11-10', 7)": 0, + "(7603, '2024-11-11', 0)": 0, + "(7603, '2024-11-11', 1)": 0, + "(7603, '2024-11-11', 2)": 0, + "(7603, '2024-11-11', 3)": 0, + "(7603, '2024-11-11', 4)": 0, + "(7603, '2024-11-11', 5)": 0, + "(7603, '2024-11-11', 6)": 0, + "(7603, '2024-11-11', 7)": 0, + "(7603, '2024-11-12', 0)": 1, + "(7603, '2024-11-12', 1)": 0, + "(7603, '2024-11-12', 2)": 0, + "(7603, '2024-11-12', 3)": 0, + "(7603, '2024-11-12', 4)": 0, + "(7603, '2024-11-12', 5)": 0, + "(7603, '2024-11-12', 6)": 0, + "(7603, '2024-11-12', 7)": 0, + "(7603, '2024-11-13', 0)": 0, + "(7603, '2024-11-13', 1)": 0, + "(7603, '2024-11-13', 2)": 0, + "(7603, '2024-11-13', 3)": 0, + "(7603, '2024-11-13', 4)": 0, + "(7603, '2024-11-13', 5)": 0, + "(7603, '2024-11-13', 6)": 0, + "(7603, '2024-11-13', 7)": 0, + "(7603, '2024-11-14', 0)": 0, + "(7603, '2024-11-14', 1)": 0, + "(7603, '2024-11-14', 2)": 0, + "(7603, '2024-11-14', 3)": 0, + "(7603, '2024-11-14', 4)": 0, + "(7603, '2024-11-14', 5)": 0, + "(7603, '2024-11-14', 6)": 0, + "(7603, '2024-11-14', 7)": 0, + "(7603, '2024-11-15', 0)": 0, + "(7603, '2024-11-15', 1)": 0, + "(7603, '2024-11-15', 2)": 1, + "(7603, '2024-11-15', 3)": 0, + "(7603, '2024-11-15', 4)": 0, + "(7603, '2024-11-15', 5)": 0, + "(7603, '2024-11-15', 6)": 0, + "(7603, '2024-11-15', 7)": 0, + "(7603, '2024-11-16', 0)": 0, + "(7603, '2024-11-16', 1)": 0, + "(7603, '2024-11-16', 2)": 0, + "(7603, '2024-11-16', 3)": 0, + "(7603, '2024-11-16', 4)": 0, + "(7603, '2024-11-16', 5)": 0, + "(7603, '2024-11-16', 6)": 0, + "(7603, '2024-11-16', 7)": 0, + "(7603, '2024-11-17', 0)": 0, + "(7603, '2024-11-17', 1)": 1, + "(7603, '2024-11-17', 2)": 0, + "(7603, '2024-11-17', 3)": 0, + "(7603, '2024-11-17', 4)": 0, + "(7603, '2024-11-17', 5)": 0, + "(7603, '2024-11-17', 6)": 0, + "(7603, '2024-11-17', 7)": 0, + "(7603, '2024-11-18', 0)": 1, + "(7603, '2024-11-18', 1)": 0, + "(7603, '2024-11-18', 2)": 0, + "(7603, '2024-11-18', 3)": 0, + "(7603, '2024-11-18', 4)": 0, + "(7603, '2024-11-18', 5)": 0, + "(7603, '2024-11-18', 6)": 0, + "(7603, '2024-11-18', 7)": 0, + "(7603, '2024-11-19', 0)": 0, + "(7603, '2024-11-19', 1)": 1, + "(7603, '2024-11-19', 2)": 0, + "(7603, '2024-11-19', 3)": 0, + "(7603, '2024-11-19', 4)": 0, + "(7603, '2024-11-19', 5)": 0, + "(7603, '2024-11-19', 6)": 0, + "(7603, '2024-11-19', 7)": 0, + "(7603, '2024-11-20', 0)": 0, + "(7603, '2024-11-20', 1)": 0, + "(7603, '2024-11-20', 2)": 1, + "(7603, '2024-11-20', 3)": 0, + "(7603, '2024-11-20', 4)": 0, + "(7603, '2024-11-20', 5)": 0, + "(7603, '2024-11-20', 6)": 0, + "(7603, '2024-11-20', 7)": 0, + "(7603, '2024-11-21', 0)": 0, + "(7603, '2024-11-21', 1)": 0, + "(7603, '2024-11-21', 2)": 1, + "(7603, '2024-11-21', 3)": 0, + "(7603, '2024-11-21', 4)": 0, + "(7603, '2024-11-21', 5)": 0, + "(7603, '2024-11-21', 6)": 0, + "(7603, '2024-11-21', 7)": 0, + "(7603, '2024-11-22', 0)": 0, + "(7603, '2024-11-22', 1)": 0, + "(7603, '2024-11-22', 2)": 1, + "(7603, '2024-11-22', 3)": 0, + "(7603, '2024-11-22', 4)": 0, + "(7603, '2024-11-22', 5)": 0, + "(7603, '2024-11-22', 6)": 0, + "(7603, '2024-11-22', 7)": 0, + "(7603, '2024-11-23', 0)": 0, + "(7603, '2024-11-23', 1)": 0, + "(7603, '2024-11-23', 2)": 1, + "(7603, '2024-11-23', 3)": 0, + "(7603, '2024-11-23', 4)": 0, + "(7603, '2024-11-23', 5)": 0, + "(7603, '2024-11-23', 6)": 0, + "(7603, '2024-11-23', 7)": 0, + "(7603, '2024-11-24', 0)": 0, + "(7603, '2024-11-24', 1)": 0, + "(7603, '2024-11-24', 2)": 0, + "(7603, '2024-11-24', 3)": 0, + "(7603, '2024-11-24', 4)": 0, + "(7603, '2024-11-24', 5)": 0, + "(7603, '2024-11-24', 6)": 0, + "(7603, '2024-11-24', 7)": 0, + "(7603, '2024-11-25', 0)": 1, + "(7603, '2024-11-25', 1)": 0, + "(7603, '2024-11-25', 2)": 0, + "(7603, '2024-11-25', 3)": 0, + "(7603, '2024-11-25', 4)": 0, + "(7603, '2024-11-25', 5)": 0, + "(7603, '2024-11-25', 6)": 0, + "(7603, '2024-11-25', 7)": 0, + "(7603, '2024-11-26', 0)": 0, + "(7603, '2024-11-26', 1)": 0, + "(7603, '2024-11-26', 2)": 0, + "(7603, '2024-11-26', 3)": 0, + "(7603, '2024-11-26', 4)": 0, + "(7603, '2024-11-26', 5)": 0, + "(7603, '2024-11-26', 6)": 0, + "(7603, '2024-11-26', 7)": 0, + "(7603, '2024-11-27', 0)": 0, + "(7603, '2024-11-27', 1)": 1, + "(7603, '2024-11-27', 2)": 0, + "(7603, '2024-11-27', 3)": 0, + "(7603, '2024-11-27', 4)": 0, + "(7603, '2024-11-27', 5)": 0, + "(7603, '2024-11-27', 6)": 0, + "(7603, '2024-11-27', 7)": 0, + "(7603, '2024-11-28', 0)": 0, + "(7603, '2024-11-28', 1)": 0, + "(7603, '2024-11-28', 2)": 0, + "(7603, '2024-11-28', 3)": 0, + "(7603, '2024-11-28', 4)": 0, + "(7603, '2024-11-28', 5)": 0, + "(7603, '2024-11-28', 6)": 0, + "(7603, '2024-11-28', 7)": 0, + "(7603, '2024-11-29', 0)": 1, + "(7603, '2024-11-29', 1)": 0, + "(7603, '2024-11-29', 2)": 0, + "(7603, '2024-11-29', 3)": 0, + "(7603, '2024-11-29', 4)": 0, + "(7603, '2024-11-29', 5)": 0, + "(7603, '2024-11-29', 6)": 0, + "(7603, '2024-11-29', 7)": 0, + "(7603, '2024-11-30', 0)": 0, + "(7603, '2024-11-30', 1)": 0, + "(7603, '2024-11-30', 2)": 0, + "(7603, '2024-11-30', 3)": 0, + "(7603, '2024-11-30', 4)": 0, + "(7603, '2024-11-30', 5)": 0, + "(7603, '2024-11-30', 6)": 0, + "(7603, '2024-11-30', 7)": 0, + "(7741, '2024-11-01', 0)": 0, + "(7741, '2024-11-01', 1)": 0, + "(7741, '2024-11-01', 2)": 0, + "(7741, '2024-11-01', 3)": 0, + "(7741, '2024-11-01', 4)": 0, + "(7741, '2024-11-01', 5)": 0, + "(7741, '2024-11-01', 6)": 0, + "(7741, '2024-11-01', 7)": 0, + "(7741, '2024-11-02', 0)": 0, + "(7741, '2024-11-02', 1)": 0, + "(7741, '2024-11-02', 2)": 0, + "(7741, '2024-11-02', 3)": 0, + "(7741, '2024-11-02', 4)": 0, + "(7741, '2024-11-02', 5)": 0, + "(7741, '2024-11-02', 6)": 0, + "(7741, '2024-11-02', 7)": 0, + "(7741, '2024-11-03', 0)": 0, + "(7741, '2024-11-03', 1)": 0, + "(7741, '2024-11-03', 2)": 0, + "(7741, '2024-11-03', 3)": 0, + "(7741, '2024-11-03', 4)": 0, + "(7741, '2024-11-03', 5)": 0, + "(7741, '2024-11-03', 6)": 0, + "(7741, '2024-11-03', 7)": 0, + "(7741, '2024-11-04', 0)": 0, + "(7741, '2024-11-04', 1)": 0, + "(7741, '2024-11-04', 2)": 0, + "(7741, '2024-11-04', 3)": 0, + "(7741, '2024-11-04', 4)": 0, + "(7741, '2024-11-04', 5)": 0, + "(7741, '2024-11-04', 6)": 0, + "(7741, '2024-11-04', 7)": 0, + "(7741, '2024-11-05', 0)": 0, + "(7741, '2024-11-05', 1)": 0, + "(7741, '2024-11-05', 2)": 0, + "(7741, '2024-11-05', 3)": 0, + "(7741, '2024-11-05', 4)": 0, + "(7741, '2024-11-05', 5)": 0, + "(7741, '2024-11-05', 6)": 0, + "(7741, '2024-11-05', 7)": 0, + "(7741, '2024-11-06', 0)": 0, + "(7741, '2024-11-06', 1)": 0, + "(7741, '2024-11-06', 2)": 0, + "(7741, '2024-11-06', 3)": 0, + "(7741, '2024-11-06', 4)": 0, + "(7741, '2024-11-06', 5)": 0, + "(7741, '2024-11-06', 6)": 0, + "(7741, '2024-11-06', 7)": 0, + "(7741, '2024-11-07', 0)": 0, + "(7741, '2024-11-07', 1)": 0, + "(7741, '2024-11-07', 2)": 0, + "(7741, '2024-11-07', 3)": 0, + "(7741, '2024-11-07', 4)": 0, + "(7741, '2024-11-07', 5)": 0, + "(7741, '2024-11-07', 6)": 0, + "(7741, '2024-11-07', 7)": 0, + "(7741, '2024-11-08', 0)": 0, + "(7741, '2024-11-08', 1)": 0, + "(7741, '2024-11-08', 2)": 0, + "(7741, '2024-11-08', 3)": 0, + "(7741, '2024-11-08', 4)": 0, + "(7741, '2024-11-08', 5)": 0, + "(7741, '2024-11-08', 6)": 0, + "(7741, '2024-11-08', 7)": 0, + "(7741, '2024-11-09', 0)": 0, + "(7741, '2024-11-09', 1)": 0, + "(7741, '2024-11-09', 2)": 0, + "(7741, '2024-11-09', 3)": 0, + "(7741, '2024-11-09', 4)": 0, + "(7741, '2024-11-09', 5)": 0, + "(7741, '2024-11-09', 6)": 0, + "(7741, '2024-11-09', 7)": 0, + "(7741, '2024-11-10', 0)": 0, + "(7741, '2024-11-10', 1)": 0, + "(7741, '2024-11-10', 2)": 0, + "(7741, '2024-11-10', 3)": 0, + "(7741, '2024-11-10', 4)": 0, + "(7741, '2024-11-10', 5)": 0, + "(7741, '2024-11-10', 6)": 0, + "(7741, '2024-11-10', 7)": 0, + "(7741, '2024-11-11', 0)": 0, + "(7741, '2024-11-11', 1)": 0, + "(7741, '2024-11-11', 2)": 0, + "(7741, '2024-11-11', 3)": 0, + "(7741, '2024-11-11', 4)": 0, + "(7741, '2024-11-11', 5)": 0, + "(7741, '2024-11-11', 6)": 0, + "(7741, '2024-11-11', 7)": 0, + "(7741, '2024-11-12', 0)": 0, + "(7741, '2024-11-12', 1)": 0, + "(7741, '2024-11-12', 2)": 0, + "(7741, '2024-11-12', 3)": 0, + "(7741, '2024-11-12', 4)": 0, + "(7741, '2024-11-12', 5)": 0, + "(7741, '2024-11-12', 6)": 0, + "(7741, '2024-11-12', 7)": 0, + "(7741, '2024-11-13', 0)": 0, + "(7741, '2024-11-13', 1)": 0, + "(7741, '2024-11-13', 2)": 0, + "(7741, '2024-11-13', 3)": 0, + "(7741, '2024-11-13', 4)": 0, + "(7741, '2024-11-13', 5)": 0, + "(7741, '2024-11-13', 6)": 0, + "(7741, '2024-11-13', 7)": 0, + "(7741, '2024-11-14', 0)": 0, + "(7741, '2024-11-14', 1)": 0, + "(7741, '2024-11-14', 2)": 0, + "(7741, '2024-11-14', 3)": 0, + "(7741, '2024-11-14', 4)": 0, + "(7741, '2024-11-14', 5)": 0, + "(7741, '2024-11-14', 6)": 0, + "(7741, '2024-11-14', 7)": 0, + "(7741, '2024-11-15', 0)": 0, + "(7741, '2024-11-15', 1)": 0, + "(7741, '2024-11-15', 2)": 0, + "(7741, '2024-11-15', 3)": 0, + "(7741, '2024-11-15', 4)": 0, + "(7741, '2024-11-15', 5)": 0, + "(7741, '2024-11-15', 6)": 0, + "(7741, '2024-11-15', 7)": 0, + "(7741, '2024-11-16', 0)": 0, + "(7741, '2024-11-16', 1)": 0, + "(7741, '2024-11-16', 2)": 0, + "(7741, '2024-11-16', 3)": 0, + "(7741, '2024-11-16', 4)": 0, + "(7741, '2024-11-16', 5)": 0, + "(7741, '2024-11-16', 6)": 0, + "(7741, '2024-11-16', 7)": 0, + "(7741, '2024-11-17', 0)": 0, + "(7741, '2024-11-17', 1)": 0, + "(7741, '2024-11-17', 2)": 0, + "(7741, '2024-11-17', 3)": 0, + "(7741, '2024-11-17', 4)": 0, + "(7741, '2024-11-17', 5)": 0, + "(7741, '2024-11-17', 6)": 0, + "(7741, '2024-11-17', 7)": 0, + "(7741, '2024-11-18', 0)": 0, + "(7741, '2024-11-18', 1)": 0, + "(7741, '2024-11-18', 2)": 0, + "(7741, '2024-11-18', 3)": 0, + "(7741, '2024-11-18', 4)": 0, + "(7741, '2024-11-18', 5)": 0, + "(7741, '2024-11-18', 6)": 0, + "(7741, '2024-11-18', 7)": 0, + "(7741, '2024-11-19', 0)": 0, + "(7741, '2024-11-19', 1)": 0, + "(7741, '2024-11-19', 2)": 0, + "(7741, '2024-11-19', 3)": 0, + "(7741, '2024-11-19', 4)": 0, + "(7741, '2024-11-19', 5)": 0, + "(7741, '2024-11-19', 6)": 0, + "(7741, '2024-11-19', 7)": 0, + "(7741, '2024-11-20', 0)": 0, + "(7741, '2024-11-20', 1)": 0, + "(7741, '2024-11-20', 2)": 0, + "(7741, '2024-11-20', 3)": 0, + "(7741, '2024-11-20', 4)": 0, + "(7741, '2024-11-20', 5)": 0, + "(7741, '2024-11-20', 6)": 0, + "(7741, '2024-11-20', 7)": 0, + "(7741, '2024-11-21', 0)": 0, + "(7741, '2024-11-21', 1)": 0, + "(7741, '2024-11-21', 2)": 0, + "(7741, '2024-11-21', 3)": 0, + "(7741, '2024-11-21', 4)": 0, + "(7741, '2024-11-21', 5)": 0, + "(7741, '2024-11-21', 6)": 0, + "(7741, '2024-11-21', 7)": 0, + "(7741, '2024-11-22', 0)": 0, + "(7741, '2024-11-22', 1)": 0, + "(7741, '2024-11-22', 2)": 0, + "(7741, '2024-11-22', 3)": 0, + "(7741, '2024-11-22', 4)": 0, + "(7741, '2024-11-22', 5)": 0, + "(7741, '2024-11-22', 6)": 0, + "(7741, '2024-11-22', 7)": 0, + "(7741, '2024-11-23', 0)": 0, + "(7741, '2024-11-23', 1)": 0, + "(7741, '2024-11-23', 2)": 0, + "(7741, '2024-11-23', 3)": 0, + "(7741, '2024-11-23', 4)": 0, + "(7741, '2024-11-23', 5)": 0, + "(7741, '2024-11-23', 6)": 0, + "(7741, '2024-11-23', 7)": 0, + "(7741, '2024-11-24', 0)": 0, + "(7741, '2024-11-24', 1)": 0, + "(7741, '2024-11-24', 2)": 0, + "(7741, '2024-11-24', 3)": 0, + "(7741, '2024-11-24', 4)": 0, + "(7741, '2024-11-24', 5)": 0, + "(7741, '2024-11-24', 6)": 0, + "(7741, '2024-11-24', 7)": 0, + "(7741, '2024-11-25', 0)": 0, + "(7741, '2024-11-25', 1)": 0, + "(7741, '2024-11-25', 2)": 0, + "(7741, '2024-11-25', 3)": 0, + "(7741, '2024-11-25', 4)": 0, + "(7741, '2024-11-25', 5)": 0, + "(7741, '2024-11-25', 6)": 0, + "(7741, '2024-11-25', 7)": 0, + "(7741, '2024-11-26', 0)": 0, + "(7741, '2024-11-26', 1)": 0, + "(7741, '2024-11-26', 2)": 0, + "(7741, '2024-11-26', 3)": 0, + "(7741, '2024-11-26', 4)": 0, + "(7741, '2024-11-26', 5)": 0, + "(7741, '2024-11-26', 6)": 0, + "(7741, '2024-11-26', 7)": 0, + "(7741, '2024-11-27', 0)": 0, + "(7741, '2024-11-27', 1)": 0, + "(7741, '2024-11-27', 2)": 0, + "(7741, '2024-11-27', 3)": 0, + "(7741, '2024-11-27', 4)": 0, + "(7741, '2024-11-27', 5)": 0, + "(7741, '2024-11-27', 6)": 1, + "(7741, '2024-11-27', 7)": 0, + "(7741, '2024-11-28', 0)": 0, + "(7741, '2024-11-28', 1)": 0, + "(7741, '2024-11-28', 2)": 0, + "(7741, '2024-11-28', 3)": 0, + "(7741, '2024-11-28', 4)": 0, + "(7741, '2024-11-28', 5)": 0, + "(7741, '2024-11-28', 6)": 1, + "(7741, '2024-11-28', 7)": 0, + "(7741, '2024-11-29', 0)": 0, + "(7741, '2024-11-29', 1)": 0, + "(7741, '2024-11-29', 2)": 0, + "(7741, '2024-11-29', 3)": 0, + "(7741, '2024-11-29', 4)": 0, + "(7741, '2024-11-29', 5)": 0, + "(7741, '2024-11-29', 6)": 0, + "(7741, '2024-11-29', 7)": 0, + "(7741, '2024-11-30', 0)": 0, + "(7741, '2024-11-30', 1)": 0, + "(7741, '2024-11-30', 2)": 0, + "(7741, '2024-11-30', 3)": 0, + "(7741, '2024-11-30', 4)": 0, + "(7741, '2024-11-30', 5)": 0, + "(7741, '2024-11-30', 6)": 0, + "(7741, '2024-11-30', 7)": 0, + "(7752, '2024-11-01', 0)": 0, + "(7752, '2024-11-01', 1)": 0, + "(7752, '2024-11-01', 2)": 1, + "(7752, '2024-11-01', 3)": 0, + "(7752, '2024-11-01', 4)": 0, + "(7752, '2024-11-01', 5)": 0, + "(7752, '2024-11-01', 6)": 0, + "(7752, '2024-11-01', 7)": 0, + "(7752, '2024-11-02', 0)": 0, + "(7752, '2024-11-02', 1)": 0, + "(7752, '2024-11-02', 2)": 0, + "(7752, '2024-11-02', 3)": 1, + "(7752, '2024-11-02', 4)": 0, + "(7752, '2024-11-02', 5)": 0, + "(7752, '2024-11-02', 6)": 0, + "(7752, '2024-11-02', 7)": 0, + "(7752, '2024-11-03', 0)": 0, + "(7752, '2024-11-03', 1)": 0, + "(7752, '2024-11-03', 2)": 0, + "(7752, '2024-11-03', 3)": 0, + "(7752, '2024-11-03', 4)": 0, + "(7752, '2024-11-03', 5)": 0, + "(7752, '2024-11-03', 6)": 0, + "(7752, '2024-11-03', 7)": 0, + "(7752, '2024-11-04', 0)": 0, + "(7752, '2024-11-04', 1)": 0, + "(7752, '2024-11-04', 2)": 0, + "(7752, '2024-11-04', 3)": 0, + "(7752, '2024-11-04', 4)": 0, + "(7752, '2024-11-04', 5)": 0, + "(7752, '2024-11-04', 6)": 0, + "(7752, '2024-11-04', 7)": 0, + "(7752, '2024-11-05', 0)": 0, + "(7752, '2024-11-05', 1)": 0, + "(7752, '2024-11-05', 2)": 0, + "(7752, '2024-11-05', 3)": 0, + "(7752, '2024-11-05', 4)": 0, + "(7752, '2024-11-05', 5)": 0, + "(7752, '2024-11-05', 6)": 1, + "(7752, '2024-11-05', 7)": 0, + "(7752, '2024-11-06', 0)": 0, + "(7752, '2024-11-06', 1)": 0, + "(7752, '2024-11-06', 2)": 0, + "(7752, '2024-11-06', 3)": 0, + "(7752, '2024-11-06', 4)": 0, + "(7752, '2024-11-06', 5)": 0, + "(7752, '2024-11-06', 6)": 1, + "(7752, '2024-11-06', 7)": 0, + "(7752, '2024-11-07', 0)": 0, + "(7752, '2024-11-07', 1)": 0, + "(7752, '2024-11-07', 2)": 0, + "(7752, '2024-11-07', 3)": 0, + "(7752, '2024-11-07', 4)": 0, + "(7752, '2024-11-07', 5)": 0, + "(7752, '2024-11-07', 6)": 1, + "(7752, '2024-11-07', 7)": 0, + "(7752, '2024-11-08', 0)": 0, + "(7752, '2024-11-08', 1)": 0, + "(7752, '2024-11-08', 2)": 0, + "(7752, '2024-11-08', 3)": 0, + "(7752, '2024-11-08', 4)": 0, + "(7752, '2024-11-08', 5)": 0, + "(7752, '2024-11-08', 6)": 0, + "(7752, '2024-11-08', 7)": 0, + "(7752, '2024-11-09', 0)": 0, + "(7752, '2024-11-09', 1)": 0, + "(7752, '2024-11-09', 2)": 0, + "(7752, '2024-11-09', 3)": 0, + "(7752, '2024-11-09', 4)": 0, + "(7752, '2024-11-09', 5)": 0, + "(7752, '2024-11-09', 6)": 0, + "(7752, '2024-11-09', 7)": 0, + "(7752, '2024-11-10', 0)": 0, + "(7752, '2024-11-10', 1)": 0, + "(7752, '2024-11-10', 2)": 0, + "(7752, '2024-11-10', 3)": 0, + "(7752, '2024-11-10', 4)": 0, + "(7752, '2024-11-10', 5)": 0, + "(7752, '2024-11-10', 6)": 0, + "(7752, '2024-11-10', 7)": 0, + "(7752, '2024-11-11', 0)": 1, + "(7752, '2024-11-11', 1)": 0, + "(7752, '2024-11-11', 2)": 0, + "(7752, '2024-11-11', 3)": 0, + "(7752, '2024-11-11', 4)": 0, + "(7752, '2024-11-11', 5)": 0, + "(7752, '2024-11-11', 6)": 0, + "(7752, '2024-11-11', 7)": 0, + "(7752, '2024-11-12', 0)": 0, + "(7752, '2024-11-12', 1)": 0, + "(7752, '2024-11-12', 2)": 0, + "(7752, '2024-11-12', 3)": 0, + "(7752, '2024-11-12', 4)": 0, + "(7752, '2024-11-12', 5)": 0, + "(7752, '2024-11-12', 6)": 0, + "(7752, '2024-11-12', 7)": 0, + "(7752, '2024-11-13', 0)": 0, + "(7752, '2024-11-13', 1)": 0, + "(7752, '2024-11-13', 2)": 0, + "(7752, '2024-11-13', 3)": 0, + "(7752, '2024-11-13', 4)": 0, + "(7752, '2024-11-13', 5)": 0, + "(7752, '2024-11-13', 6)": 0, + "(7752, '2024-11-13', 7)": 0, + "(7752, '2024-11-14', 0)": 0, + "(7752, '2024-11-14', 1)": 0, + "(7752, '2024-11-14', 2)": 0, + "(7752, '2024-11-14', 3)": 0, + "(7752, '2024-11-14', 4)": 0, + "(7752, '2024-11-14', 5)": 0, + "(7752, '2024-11-14', 6)": 0, + "(7752, '2024-11-14', 7)": 0, + "(7752, '2024-11-15', 0)": 1, + "(7752, '2024-11-15', 1)": 0, + "(7752, '2024-11-15', 2)": 0, + "(7752, '2024-11-15', 3)": 0, + "(7752, '2024-11-15', 4)": 0, + "(7752, '2024-11-15', 5)": 0, + "(7752, '2024-11-15', 6)": 0, + "(7752, '2024-11-15', 7)": 0, + "(7752, '2024-11-16', 0)": 0, + "(7752, '2024-11-16', 1)": 0, + "(7752, '2024-11-16', 2)": 0, + "(7752, '2024-11-16', 3)": 0, + "(7752, '2024-11-16', 4)": 0, + "(7752, '2024-11-16', 5)": 0, + "(7752, '2024-11-16', 6)": 0, + "(7752, '2024-11-16', 7)": 0, + "(7752, '2024-11-17', 0)": 0, + "(7752, '2024-11-17', 1)": 0, + "(7752, '2024-11-17', 2)": 0, + "(7752, '2024-11-17', 3)": 0, + "(7752, '2024-11-17', 4)": 0, + "(7752, '2024-11-17', 5)": 0, + "(7752, '2024-11-17', 6)": 0, + "(7752, '2024-11-17', 7)": 0, + "(7752, '2024-11-18', 0)": 1, + "(7752, '2024-11-18', 1)": 0, + "(7752, '2024-11-18', 2)": 0, + "(7752, '2024-11-18', 3)": 0, + "(7752, '2024-11-18', 4)": 0, + "(7752, '2024-11-18', 5)": 0, + "(7752, '2024-11-18', 6)": 0, + "(7752, '2024-11-18', 7)": 0, + "(7752, '2024-11-19', 0)": 0, + "(7752, '2024-11-19', 1)": 0, + "(7752, '2024-11-19', 2)": 0, + "(7752, '2024-11-19', 3)": 0, + "(7752, '2024-11-19', 4)": 0, + "(7752, '2024-11-19', 5)": 0, + "(7752, '2024-11-19', 6)": 0, + "(7752, '2024-11-19', 7)": 0, + "(7752, '2024-11-20', 0)": 0, + "(7752, '2024-11-20', 1)": 0, + "(7752, '2024-11-20', 2)": 1, + "(7752, '2024-11-20', 3)": 0, + "(7752, '2024-11-20', 4)": 0, + "(7752, '2024-11-20', 5)": 0, + "(7752, '2024-11-20', 6)": 0, + "(7752, '2024-11-20', 7)": 0, + "(7752, '2024-11-21', 0)": 0, + "(7752, '2024-11-21', 1)": 0, + "(7752, '2024-11-21', 2)": 1, + "(7752, '2024-11-21', 3)": 0, + "(7752, '2024-11-21', 4)": 0, + "(7752, '2024-11-21', 5)": 0, + "(7752, '2024-11-21', 6)": 0, + "(7752, '2024-11-21', 7)": 0, + "(7752, '2024-11-22', 0)": 0, + "(7752, '2024-11-22', 1)": 0, + "(7752, '2024-11-22', 2)": 1, + "(7752, '2024-11-22', 3)": 0, + "(7752, '2024-11-22', 4)": 0, + "(7752, '2024-11-22', 5)": 0, + "(7752, '2024-11-22', 6)": 0, + "(7752, '2024-11-22', 7)": 0, + "(7752, '2024-11-23', 0)": 0, + "(7752, '2024-11-23', 1)": 0, + "(7752, '2024-11-23', 2)": 0, + "(7752, '2024-11-23', 3)": 0, + "(7752, '2024-11-23', 4)": 0, + "(7752, '2024-11-23', 5)": 0, + "(7752, '2024-11-23', 6)": 0, + "(7752, '2024-11-23', 7)": 0, + "(7752, '2024-11-24', 0)": 0, + "(7752, '2024-11-24', 1)": 0, + "(7752, '2024-11-24', 2)": 0, + "(7752, '2024-11-24', 3)": 1, + "(7752, '2024-11-24', 4)": 0, + "(7752, '2024-11-24', 5)": 0, + "(7752, '2024-11-24', 6)": 0, + "(7752, '2024-11-24', 7)": 0, + "(7752, '2024-11-25', 0)": 0, + "(7752, '2024-11-25', 1)": 0, + "(7752, '2024-11-25', 2)": 0, + "(7752, '2024-11-25', 3)": 0, + "(7752, '2024-11-25', 4)": 0, + "(7752, '2024-11-25', 5)": 0, + "(7752, '2024-11-25', 6)": 0, + "(7752, '2024-11-25', 7)": 0, + "(7752, '2024-11-26', 0)": 1, + "(7752, '2024-11-26', 1)": 0, + "(7752, '2024-11-26', 2)": 0, + "(7752, '2024-11-26', 3)": 0, + "(7752, '2024-11-26', 4)": 0, + "(7752, '2024-11-26', 5)": 0, + "(7752, '2024-11-26', 6)": 0, + "(7752, '2024-11-26', 7)": 0, + "(7752, '2024-11-27', 0)": 0, + "(7752, '2024-11-27', 1)": 0, + "(7752, '2024-11-27', 2)": 1, + "(7752, '2024-11-27', 3)": 0, + "(7752, '2024-11-27', 4)": 0, + "(7752, '2024-11-27', 5)": 0, + "(7752, '2024-11-27', 6)": 0, + "(7752, '2024-11-27', 7)": 0, + "(7752, '2024-11-28', 0)": 0, + "(7752, '2024-11-28', 1)": 0, + "(7752, '2024-11-28', 2)": 0, + "(7752, '2024-11-28', 3)": 0, + "(7752, '2024-11-28', 4)": 0, + "(7752, '2024-11-28', 5)": 0, + "(7752, '2024-11-28', 6)": 0, + "(7752, '2024-11-28', 7)": 0, + "(7752, '2024-11-29', 0)": 0, + "(7752, '2024-11-29', 1)": 0, + "(7752, '2024-11-29', 2)": 0, + "(7752, '2024-11-29', 3)": 0, + "(7752, '2024-11-29', 4)": 0, + "(7752, '2024-11-29', 5)": 0, + "(7752, '2024-11-29', 6)": 0, + "(7752, '2024-11-29', 7)": 0, + "(7752, '2024-11-30', 0)": 0, + "(7752, '2024-11-30', 1)": 0, + "(7752, '2024-11-30', 2)": 1, + "(7752, '2024-11-30', 3)": 0, + "(7752, '2024-11-30', 4)": 0, + "(7752, '2024-11-30', 5)": 0, + "(7752, '2024-11-30', 6)": 0, + "(7752, '2024-11-30', 7)": 0, + "(7770, '2024-11-01', 0)": 0, + "(7770, '2024-11-01', 1)": 0, + "(7770, '2024-11-01', 2)": 0, + "(7770, '2024-11-01', 3)": 0, + "(7770, '2024-11-01', 4)": 0, + "(7770, '2024-11-01', 5)": 0, + "(7770, '2024-11-01', 6)": 0, + "(7770, '2024-11-01', 7)": 0, + "(7770, '2024-11-02', 0)": 0, + "(7770, '2024-11-02', 1)": 0, + "(7770, '2024-11-02', 2)": 0, + "(7770, '2024-11-02', 3)": 0, + "(7770, '2024-11-02', 4)": 0, + "(7770, '2024-11-02', 5)": 0, + "(7770, '2024-11-02', 6)": 0, + "(7770, '2024-11-02', 7)": 0, + "(7770, '2024-11-03', 0)": 0, + "(7770, '2024-11-03', 1)": 0, + "(7770, '2024-11-03', 2)": 0, + "(7770, '2024-11-03', 3)": 0, + "(7770, '2024-11-03', 4)": 0, + "(7770, '2024-11-03', 5)": 0, + "(7770, '2024-11-03', 6)": 0, + "(7770, '2024-11-03', 7)": 0, + "(7770, '2024-11-04', 0)": 0, + "(7770, '2024-11-04', 1)": 0, + "(7770, '2024-11-04', 2)": 0, + "(7770, '2024-11-04', 3)": 0, + "(7770, '2024-11-04', 4)": 0, + "(7770, '2024-11-04', 5)": 0, + "(7770, '2024-11-04', 6)": 0, + "(7770, '2024-11-04', 7)": 0, + "(7770, '2024-11-05', 0)": 0, + "(7770, '2024-11-05', 1)": 0, + "(7770, '2024-11-05', 2)": 0, + "(7770, '2024-11-05', 3)": 0, + "(7770, '2024-11-05', 4)": 0, + "(7770, '2024-11-05', 5)": 0, + "(7770, '2024-11-05', 6)": 0, + "(7770, '2024-11-05', 7)": 0, + "(7770, '2024-11-06', 0)": 0, + "(7770, '2024-11-06', 1)": 0, + "(7770, '2024-11-06', 2)": 0, + "(7770, '2024-11-06', 3)": 0, + "(7770, '2024-11-06', 4)": 0, + "(7770, '2024-11-06', 5)": 0, + "(7770, '2024-11-06', 6)": 0, + "(7770, '2024-11-06', 7)": 0, + "(7770, '2024-11-07', 0)": 0, + "(7770, '2024-11-07', 1)": 1, + "(7770, '2024-11-07', 2)": 0, + "(7770, '2024-11-07', 3)": 0, + "(7770, '2024-11-07', 4)": 0, + "(7770, '2024-11-07', 5)": 0, + "(7770, '2024-11-07', 6)": 0, + "(7770, '2024-11-07', 7)": 0, + "(7770, '2024-11-08', 0)": 0, + "(7770, '2024-11-08', 1)": 0, + "(7770, '2024-11-08', 2)": 0, + "(7770, '2024-11-08', 3)": 0, + "(7770, '2024-11-08', 4)": 0, + "(7770, '2024-11-08', 5)": 0, + "(7770, '2024-11-08', 6)": 0, + "(7770, '2024-11-08', 7)": 0, + "(7770, '2024-11-09', 0)": 0, + "(7770, '2024-11-09', 1)": 0, + "(7770, '2024-11-09', 2)": 0, + "(7770, '2024-11-09', 3)": 0, + "(7770, '2024-11-09', 4)": 0, + "(7770, '2024-11-09', 5)": 0, + "(7770, '2024-11-09', 6)": 0, + "(7770, '2024-11-09', 7)": 0, + "(7770, '2024-11-10', 0)": 0, + "(7770, '2024-11-10', 1)": 0, + "(7770, '2024-11-10', 2)": 0, + "(7770, '2024-11-10', 3)": 0, + "(7770, '2024-11-10', 4)": 0, + "(7770, '2024-11-10', 5)": 0, + "(7770, '2024-11-10', 6)": 0, + "(7770, '2024-11-10', 7)": 0, + "(7770, '2024-11-11', 0)": 0, + "(7770, '2024-11-11', 1)": 1, + "(7770, '2024-11-11', 2)": 0, + "(7770, '2024-11-11', 3)": 0, + "(7770, '2024-11-11', 4)": 0, + "(7770, '2024-11-11', 5)": 0, + "(7770, '2024-11-11', 6)": 0, + "(7770, '2024-11-11', 7)": 0, + "(7770, '2024-11-12', 0)": 0, + "(7770, '2024-11-12', 1)": 0, + "(7770, '2024-11-12', 2)": 0, + "(7770, '2024-11-12', 3)": 0, + "(7770, '2024-11-12', 4)": 0, + "(7770, '2024-11-12', 5)": 0, + "(7770, '2024-11-12', 6)": 0, + "(7770, '2024-11-12', 7)": 0, + "(7770, '2024-11-13', 0)": 0, + "(7770, '2024-11-13', 1)": 0, + "(7770, '2024-11-13', 2)": 0, + "(7770, '2024-11-13', 3)": 0, + "(7770, '2024-11-13', 4)": 0, + "(7770, '2024-11-13', 5)": 0, + "(7770, '2024-11-13', 6)": 0, + "(7770, '2024-11-13', 7)": 0, + "(7770, '2024-11-14', 0)": 0, + "(7770, '2024-11-14', 1)": 0, + "(7770, '2024-11-14', 2)": 0, + "(7770, '2024-11-14', 3)": 0, + "(7770, '2024-11-14', 4)": 0, + "(7770, '2024-11-14', 5)": 0, + "(7770, '2024-11-14', 6)": 0, + "(7770, '2024-11-14', 7)": 0, + "(7770, '2024-11-15', 0)": 0, + "(7770, '2024-11-15', 1)": 0, + "(7770, '2024-11-15', 2)": 0, + "(7770, '2024-11-15', 3)": 0, + "(7770, '2024-11-15', 4)": 0, + "(7770, '2024-11-15', 5)": 0, + "(7770, '2024-11-15', 6)": 0, + "(7770, '2024-11-15', 7)": 0, + "(7770, '2024-11-16', 0)": 0, + "(7770, '2024-11-16', 1)": 0, + "(7770, '2024-11-16', 2)": 0, + "(7770, '2024-11-16', 3)": 0, + "(7770, '2024-11-16', 4)": 0, + "(7770, '2024-11-16', 5)": 0, + "(7770, '2024-11-16', 6)": 0, + "(7770, '2024-11-16', 7)": 0, + "(7770, '2024-11-17', 0)": 0, + "(7770, '2024-11-17', 1)": 0, + "(7770, '2024-11-17', 2)": 0, + "(7770, '2024-11-17', 3)": 0, + "(7770, '2024-11-17', 4)": 0, + "(7770, '2024-11-17', 5)": 0, + "(7770, '2024-11-17', 6)": 0, + "(7770, '2024-11-17', 7)": 0, + "(7770, '2024-11-18', 0)": 1, + "(7770, '2024-11-18', 1)": 0, + "(7770, '2024-11-18', 2)": 0, + "(7770, '2024-11-18', 3)": 0, + "(7770, '2024-11-18', 4)": 0, + "(7770, '2024-11-18', 5)": 0, + "(7770, '2024-11-18', 6)": 0, + "(7770, '2024-11-18', 7)": 0, + "(7770, '2024-11-19', 0)": 0, + "(7770, '2024-11-19', 1)": 0, + "(7770, '2024-11-19', 2)": 0, + "(7770, '2024-11-19', 3)": 0, + "(7770, '2024-11-19', 4)": 0, + "(7770, '2024-11-19', 5)": 0, + "(7770, '2024-11-19', 6)": 0, + "(7770, '2024-11-19', 7)": 0, + "(7770, '2024-11-20', 0)": 0, + "(7770, '2024-11-20', 1)": 0, + "(7770, '2024-11-20', 2)": 0, + "(7770, '2024-11-20', 3)": 0, + "(7770, '2024-11-20', 4)": 0, + "(7770, '2024-11-20', 5)": 0, + "(7770, '2024-11-20', 6)": 0, + "(7770, '2024-11-20', 7)": 0, + "(7770, '2024-11-21', 0)": 0, + "(7770, '2024-11-21', 1)": 1, + "(7770, '2024-11-21', 2)": 0, + "(7770, '2024-11-21', 3)": 0, + "(7770, '2024-11-21', 4)": 0, + "(7770, '2024-11-21', 5)": 0, + "(7770, '2024-11-21', 6)": 0, + "(7770, '2024-11-21', 7)": 0, + "(7770, '2024-11-22', 0)": 0, + "(7770, '2024-11-22', 1)": 0, + "(7770, '2024-11-22', 2)": 0, + "(7770, '2024-11-22', 3)": 0, + "(7770, '2024-11-22', 4)": 0, + "(7770, '2024-11-22', 5)": 0, + "(7770, '2024-11-22', 6)": 0, + "(7770, '2024-11-22', 7)": 0, + "(7770, '2024-11-23', 0)": 1, + "(7770, '2024-11-23', 1)": 0, + "(7770, '2024-11-23', 2)": 0, + "(7770, '2024-11-23', 3)": 0, + "(7770, '2024-11-23', 4)": 0, + "(7770, '2024-11-23', 5)": 0, + "(7770, '2024-11-23', 6)": 0, + "(7770, '2024-11-23', 7)": 0, + "(7770, '2024-11-24', 0)": 0, + "(7770, '2024-11-24', 1)": 1, + "(7770, '2024-11-24', 2)": 0, + "(7770, '2024-11-24', 3)": 0, + "(7770, '2024-11-24', 4)": 0, + "(7770, '2024-11-24', 5)": 0, + "(7770, '2024-11-24', 6)": 0, + "(7770, '2024-11-24', 7)": 0, + "(7770, '2024-11-25', 0)": 0, + "(7770, '2024-11-25', 1)": 0, + "(7770, '2024-11-25', 2)": 0, + "(7770, '2024-11-25', 3)": 0, + "(7770, '2024-11-25', 4)": 0, + "(7770, '2024-11-25', 5)": 0, + "(7770, '2024-11-25', 6)": 0, + "(7770, '2024-11-25', 7)": 0, + "(7770, '2024-11-26', 0)": 0, + "(7770, '2024-11-26', 1)": 0, + "(7770, '2024-11-26', 2)": 1, + "(7770, '2024-11-26', 3)": 0, + "(7770, '2024-11-26', 4)": 0, + "(7770, '2024-11-26', 5)": 0, + "(7770, '2024-11-26', 6)": 0, + "(7770, '2024-11-26', 7)": 0, + "(7770, '2024-11-27', 0)": 0, + "(7770, '2024-11-27', 1)": 0, + "(7770, '2024-11-27', 2)": 1, + "(7770, '2024-11-27', 3)": 0, + "(7770, '2024-11-27', 4)": 0, + "(7770, '2024-11-27', 5)": 0, + "(7770, '2024-11-27', 6)": 0, + "(7770, '2024-11-27', 7)": 0, + "(7770, '2024-11-28', 0)": 0, + "(7770, '2024-11-28', 1)": 0, + "(7770, '2024-11-28', 2)": 0, + "(7770, '2024-11-28', 3)": 0, + "(7770, '2024-11-28', 4)": 0, + "(7770, '2024-11-28', 5)": 0, + "(7770, '2024-11-28', 6)": 0, + "(7770, '2024-11-28', 7)": 0, + "(7770, '2024-11-29', 0)": 0, + "(7770, '2024-11-29', 1)": 1, + "(7770, '2024-11-29', 2)": 0, + "(7770, '2024-11-29', 3)": 0, + "(7770, '2024-11-29', 4)": 0, + "(7770, '2024-11-29', 5)": 0, + "(7770, '2024-11-29', 6)": 0, + "(7770, '2024-11-29', 7)": 0, + "(7770, '2024-11-30', 0)": 0, + "(7770, '2024-11-30', 1)": 0, + "(7770, '2024-11-30', 2)": 0, + "(7770, '2024-11-30', 3)": 0, + "(7770, '2024-11-30', 4)": 0, + "(7770, '2024-11-30', 5)": 0, + "(7770, '2024-11-30', 6)": 0, + "(7770, '2024-11-30', 7)": 0, + "(7796, '2024-11-01', 0)": 0, + "(7796, '2024-11-01', 1)": 0, + "(7796, '2024-11-01', 2)": 0, + "(7796, '2024-11-01', 3)": 0, + "(7796, '2024-11-01', 4)": 0, + "(7796, '2024-11-01', 5)": 0, + "(7796, '2024-11-01', 6)": 0, + "(7796, '2024-11-01', 7)": 0, + "(7796, '2024-11-02', 0)": 0, + "(7796, '2024-11-02', 1)": 0, + "(7796, '2024-11-02', 2)": 0, + "(7796, '2024-11-02', 3)": 0, + "(7796, '2024-11-02', 4)": 0, + "(7796, '2024-11-02', 5)": 0, + "(7796, '2024-11-02', 6)": 0, + "(7796, '2024-11-02', 7)": 0, + "(7796, '2024-11-03', 0)": 0, + "(7796, '2024-11-03', 1)": 0, + "(7796, '2024-11-03', 2)": 0, + "(7796, '2024-11-03', 3)": 0, + "(7796, '2024-11-03', 4)": 0, + "(7796, '2024-11-03', 5)": 0, + "(7796, '2024-11-03', 6)": 0, + "(7796, '2024-11-03', 7)": 0, + "(7796, '2024-11-04', 0)": 0, + "(7796, '2024-11-04', 1)": 0, + "(7796, '2024-11-04', 2)": 0, + "(7796, '2024-11-04', 3)": 0, + "(7796, '2024-11-04', 4)": 0, + "(7796, '2024-11-04', 5)": 0, + "(7796, '2024-11-04', 6)": 0, + "(7796, '2024-11-04', 7)": 0, + "(7796, '2024-11-05', 0)": 0, + "(7796, '2024-11-05', 1)": 0, + "(7796, '2024-11-05', 2)": 0, + "(7796, '2024-11-05', 3)": 0, + "(7796, '2024-11-05', 4)": 0, + "(7796, '2024-11-05', 5)": 0, + "(7796, '2024-11-05', 6)": 0, + "(7796, '2024-11-05', 7)": 0, + "(7796, '2024-11-06', 0)": 0, + "(7796, '2024-11-06', 1)": 0, + "(7796, '2024-11-06', 2)": 0, + "(7796, '2024-11-06', 3)": 0, + "(7796, '2024-11-06', 4)": 0, + "(7796, '2024-11-06', 5)": 0, + "(7796, '2024-11-06', 6)": 0, + "(7796, '2024-11-06', 7)": 0, + "(7796, '2024-11-07', 0)": 0, + "(7796, '2024-11-07', 1)": 1, + "(7796, '2024-11-07', 2)": 0, + "(7796, '2024-11-07', 3)": 0, + "(7796, '2024-11-07', 4)": 0, + "(7796, '2024-11-07', 5)": 0, + "(7796, '2024-11-07', 6)": 0, + "(7796, '2024-11-07', 7)": 0, + "(7796, '2024-11-08', 0)": 1, + "(7796, '2024-11-08', 1)": 0, + "(7796, '2024-11-08', 2)": 0, + "(7796, '2024-11-08', 3)": 0, + "(7796, '2024-11-08', 4)": 0, + "(7796, '2024-11-08', 5)": 0, + "(7796, '2024-11-08', 6)": 0, + "(7796, '2024-11-08', 7)": 0, + "(7796, '2024-11-09', 0)": 0, + "(7796, '2024-11-09', 1)": 0, + "(7796, '2024-11-09', 2)": 0, + "(7796, '2024-11-09', 3)": 0, + "(7796, '2024-11-09', 4)": 0, + "(7796, '2024-11-09', 5)": 0, + "(7796, '2024-11-09', 6)": 0, + "(7796, '2024-11-09', 7)": 0, + "(7796, '2024-11-10', 0)": 0, + "(7796, '2024-11-10', 1)": 0, + "(7796, '2024-11-10', 2)": 0, + "(7796, '2024-11-10', 3)": 0, + "(7796, '2024-11-10', 4)": 0, + "(7796, '2024-11-10', 5)": 0, + "(7796, '2024-11-10', 6)": 0, + "(7796, '2024-11-10', 7)": 0, + "(7796, '2024-11-11', 0)": 1, + "(7796, '2024-11-11', 1)": 0, + "(7796, '2024-11-11', 2)": 0, + "(7796, '2024-11-11', 3)": 0, + "(7796, '2024-11-11', 4)": 0, + "(7796, '2024-11-11', 5)": 0, + "(7796, '2024-11-11', 6)": 0, + "(7796, '2024-11-11', 7)": 0, + "(7796, '2024-11-12', 0)": 0, + "(7796, '2024-11-12', 1)": 0, + "(7796, '2024-11-12', 2)": 0, + "(7796, '2024-11-12', 3)": 0, + "(7796, '2024-11-12', 4)": 0, + "(7796, '2024-11-12', 5)": 0, + "(7796, '2024-11-12', 6)": 0, + "(7796, '2024-11-12', 7)": 0, + "(7796, '2024-11-13', 0)": 0, + "(7796, '2024-11-13', 1)": 0, + "(7796, '2024-11-13', 2)": 0, + "(7796, '2024-11-13', 3)": 0, + "(7796, '2024-11-13', 4)": 0, + "(7796, '2024-11-13', 5)": 0, + "(7796, '2024-11-13', 6)": 0, + "(7796, '2024-11-13', 7)": 0, + "(7796, '2024-11-14', 0)": 0, + "(7796, '2024-11-14', 1)": 1, + "(7796, '2024-11-14', 2)": 0, + "(7796, '2024-11-14', 3)": 0, + "(7796, '2024-11-14', 4)": 0, + "(7796, '2024-11-14', 5)": 0, + "(7796, '2024-11-14', 6)": 0, + "(7796, '2024-11-14', 7)": 0, + "(7796, '2024-11-15', 0)": 0, + "(7796, '2024-11-15', 1)": 0, + "(7796, '2024-11-15', 2)": 0, + "(7796, '2024-11-15', 3)": 0, + "(7796, '2024-11-15', 4)": 0, + "(7796, '2024-11-15', 5)": 0, + "(7796, '2024-11-15', 6)": 0, + "(7796, '2024-11-15', 7)": 0, + "(7796, '2024-11-16', 0)": 0, + "(7796, '2024-11-16', 1)": 0, + "(7796, '2024-11-16', 2)": 0, + "(7796, '2024-11-16', 3)": 0, + "(7796, '2024-11-16', 4)": 0, + "(7796, '2024-11-16', 5)": 0, + "(7796, '2024-11-16', 6)": 0, + "(7796, '2024-11-16', 7)": 0, + "(7796, '2024-11-17', 0)": 0, + "(7796, '2024-11-17', 1)": 1, + "(7796, '2024-11-17', 2)": 0, + "(7796, '2024-11-17', 3)": 0, + "(7796, '2024-11-17', 4)": 0, + "(7796, '2024-11-17', 5)": 0, + "(7796, '2024-11-17', 6)": 0, + "(7796, '2024-11-17', 7)": 0, + "(7796, '2024-11-18', 0)": 0, + "(7796, '2024-11-18', 1)": 0, + "(7796, '2024-11-18', 2)": 0, + "(7796, '2024-11-18', 3)": 0, + "(7796, '2024-11-18', 4)": 0, + "(7796, '2024-11-18', 5)": 0, + "(7796, '2024-11-18', 6)": 0, + "(7796, '2024-11-18', 7)": 0, + "(7796, '2024-11-19', 0)": 0, + "(7796, '2024-11-19', 1)": 0, + "(7796, '2024-11-19', 2)": 0, + "(7796, '2024-11-19', 3)": 0, + "(7796, '2024-11-19', 4)": 0, + "(7796, '2024-11-19', 5)": 0, + "(7796, '2024-11-19', 6)": 0, + "(7796, '2024-11-19', 7)": 0, + "(7796, '2024-11-20', 0)": 0, + "(7796, '2024-11-20', 1)": 0, + "(7796, '2024-11-20', 2)": 0, + "(7796, '2024-11-20', 3)": 0, + "(7796, '2024-11-20', 4)": 0, + "(7796, '2024-11-20', 5)": 0, + "(7796, '2024-11-20', 6)": 0, + "(7796, '2024-11-20', 7)": 0, + "(7796, '2024-11-21', 0)": 0, + "(7796, '2024-11-21', 1)": 0, + "(7796, '2024-11-21', 2)": 0, + "(7796, '2024-11-21', 3)": 0, + "(7796, '2024-11-21', 4)": 0, + "(7796, '2024-11-21', 5)": 0, + "(7796, '2024-11-21', 6)": 0, + "(7796, '2024-11-21', 7)": 0, + "(7796, '2024-11-22', 0)": 1, + "(7796, '2024-11-22', 1)": 0, + "(7796, '2024-11-22', 2)": 0, + "(7796, '2024-11-22', 3)": 0, + "(7796, '2024-11-22', 4)": 0, + "(7796, '2024-11-22', 5)": 0, + "(7796, '2024-11-22', 6)": 0, + "(7796, '2024-11-22', 7)": 0, + "(7796, '2024-11-23', 0)": 0, + "(7796, '2024-11-23', 1)": 0, + "(7796, '2024-11-23', 2)": 0, + "(7796, '2024-11-23', 3)": 0, + "(7796, '2024-11-23', 4)": 0, + "(7796, '2024-11-23', 5)": 0, + "(7796, '2024-11-23', 6)": 0, + "(7796, '2024-11-23', 7)": 0, + "(7796, '2024-11-24', 0)": 0, + "(7796, '2024-11-24', 1)": 0, + "(7796, '2024-11-24', 2)": 0, + "(7796, '2024-11-24', 3)": 0, + "(7796, '2024-11-24', 4)": 0, + "(7796, '2024-11-24', 5)": 0, + "(7796, '2024-11-24', 6)": 0, + "(7796, '2024-11-24', 7)": 0, + "(7796, '2024-11-25', 0)": 0, + "(7796, '2024-11-25', 1)": 1, + "(7796, '2024-11-25', 2)": 0, + "(7796, '2024-11-25', 3)": 0, + "(7796, '2024-11-25', 4)": 0, + "(7796, '2024-11-25', 5)": 0, + "(7796, '2024-11-25', 6)": 0, + "(7796, '2024-11-25', 7)": 0, + "(7796, '2024-11-26', 0)": 0, + "(7796, '2024-11-26', 1)": 1, + "(7796, '2024-11-26', 2)": 0, + "(7796, '2024-11-26', 3)": 0, + "(7796, '2024-11-26', 4)": 0, + "(7796, '2024-11-26', 5)": 0, + "(7796, '2024-11-26', 6)": 0, + "(7796, '2024-11-26', 7)": 0, + "(7796, '2024-11-27', 0)": 0, + "(7796, '2024-11-27', 1)": 0, + "(7796, '2024-11-27', 2)": 0, + "(7796, '2024-11-27', 3)": 0, + "(7796, '2024-11-27', 4)": 0, + "(7796, '2024-11-27', 5)": 0, + "(7796, '2024-11-27', 6)": 0, + "(7796, '2024-11-27', 7)": 0, + "(7796, '2024-11-28', 0)": 0, + "(7796, '2024-11-28', 1)": 0, + "(7796, '2024-11-28', 2)": 0, + "(7796, '2024-11-28', 3)": 0, + "(7796, '2024-11-28', 4)": 0, + "(7796, '2024-11-28', 5)": 0, + "(7796, '2024-11-28', 6)": 0, + "(7796, '2024-11-28', 7)": 0, + "(7796, '2024-11-29', 0)": 0, + "(7796, '2024-11-29', 1)": 0, + "(7796, '2024-11-29', 2)": 0, + "(7796, '2024-11-29', 3)": 0, + "(7796, '2024-11-29', 4)": 0, + "(7796, '2024-11-29', 5)": 0, + "(7796, '2024-11-29', 6)": 0, + "(7796, '2024-11-29', 7)": 0, + "(7796, '2024-11-30', 0)": 1, + "(7796, '2024-11-30', 1)": 0, + "(7796, '2024-11-30', 2)": 0, + "(7796, '2024-11-30', 3)": 0, + "(7796, '2024-11-30', 4)": 0, + "(7796, '2024-11-30', 5)": 0, + "(7796, '2024-11-30', 6)": 0, + "(7796, '2024-11-30', 7)": 0, + "(7835, '2024-11-01', 0)": 0, + "(7835, '2024-11-01', 1)": 0, + "(7835, '2024-11-01', 2)": 0, + "(7835, '2024-11-01', 3)": 0, + "(7835, '2024-11-01', 4)": 0, + "(7835, '2024-11-01', 5)": 0, + "(7835, '2024-11-01', 6)": 0, + "(7835, '2024-11-01', 7)": 0, + "(7835, '2024-11-02', 0)": 0, + "(7835, '2024-11-02', 1)": 0, + "(7835, '2024-11-02', 2)": 0, + "(7835, '2024-11-02', 3)": 0, + "(7835, '2024-11-02', 4)": 0, + "(7835, '2024-11-02', 5)": 0, + "(7835, '2024-11-02', 6)": 0, + "(7835, '2024-11-02', 7)": 0, + "(7835, '2024-11-03', 0)": 0, + "(7835, '2024-11-03', 1)": 0, + "(7835, '2024-11-03', 2)": 0, + "(7835, '2024-11-03', 3)": 0, + "(7835, '2024-11-03', 4)": 0, + "(7835, '2024-11-03', 5)": 0, + "(7835, '2024-11-03', 6)": 0, + "(7835, '2024-11-03', 7)": 0, + "(7835, '2024-11-04', 0)": 0, + "(7835, '2024-11-04', 1)": 0, + "(7835, '2024-11-04', 2)": 0, + "(7835, '2024-11-04', 3)": 0, + "(7835, '2024-11-04', 4)": 0, + "(7835, '2024-11-04', 5)": 0, + "(7835, '2024-11-04', 6)": 0, + "(7835, '2024-11-04', 7)": 0, + "(7835, '2024-11-05', 0)": 0, + "(7835, '2024-11-05', 1)": 0, + "(7835, '2024-11-05', 2)": 0, + "(7835, '2024-11-05', 3)": 0, + "(7835, '2024-11-05', 4)": 0, + "(7835, '2024-11-05', 5)": 0, + "(7835, '2024-11-05', 6)": 0, + "(7835, '2024-11-05', 7)": 0, + "(7835, '2024-11-06', 0)": 0, + "(7835, '2024-11-06', 1)": 0, + "(7835, '2024-11-06', 2)": 0, + "(7835, '2024-11-06', 3)": 0, + "(7835, '2024-11-06', 4)": 0, + "(7835, '2024-11-06', 5)": 0, + "(7835, '2024-11-06', 6)": 0, + "(7835, '2024-11-06', 7)": 0, + "(7835, '2024-11-07', 0)": 0, + "(7835, '2024-11-07', 1)": 0, + "(7835, '2024-11-07', 2)": 0, + "(7835, '2024-11-07', 3)": 0, + "(7835, '2024-11-07', 4)": 0, + "(7835, '2024-11-07', 5)": 0, + "(7835, '2024-11-07', 6)": 0, + "(7835, '2024-11-07', 7)": 0, + "(7835, '2024-11-08', 0)": 0, + "(7835, '2024-11-08', 1)": 1, + "(7835, '2024-11-08', 2)": 0, + "(7835, '2024-11-08', 3)": 0, + "(7835, '2024-11-08', 4)": 0, + "(7835, '2024-11-08', 5)": 0, + "(7835, '2024-11-08', 6)": 0, + "(7835, '2024-11-08', 7)": 0, + "(7835, '2024-11-09', 0)": 1, + "(7835, '2024-11-09', 1)": 0, + "(7835, '2024-11-09', 2)": 0, + "(7835, '2024-11-09', 3)": 0, + "(7835, '2024-11-09', 4)": 0, + "(7835, '2024-11-09', 5)": 0, + "(7835, '2024-11-09', 6)": 0, + "(7835, '2024-11-09', 7)": 0, + "(7835, '2024-11-10', 0)": 0, + "(7835, '2024-11-10', 1)": 0, + "(7835, '2024-11-10', 2)": 0, + "(7835, '2024-11-10', 3)": 0, + "(7835, '2024-11-10', 4)": 0, + "(7835, '2024-11-10', 5)": 0, + "(7835, '2024-11-10', 6)": 0, + "(7835, '2024-11-10', 7)": 0, + "(7835, '2024-11-11', 0)": 0, + "(7835, '2024-11-11', 1)": 0, + "(7835, '2024-11-11', 2)": 1, + "(7835, '2024-11-11', 3)": 0, + "(7835, '2024-11-11', 4)": 0, + "(7835, '2024-11-11', 5)": 0, + "(7835, '2024-11-11', 6)": 0, + "(7835, '2024-11-11', 7)": 0, + "(7835, '2024-11-12', 0)": 0, + "(7835, '2024-11-12', 1)": 1, + "(7835, '2024-11-12', 2)": 0, + "(7835, '2024-11-12', 3)": 0, + "(7835, '2024-11-12', 4)": 0, + "(7835, '2024-11-12', 5)": 0, + "(7835, '2024-11-12', 6)": 0, + "(7835, '2024-11-12', 7)": 0, + "(7835, '2024-11-13', 0)": 0, + "(7835, '2024-11-13', 1)": 0, + "(7835, '2024-11-13', 2)": 0, + "(7835, '2024-11-13', 3)": 0, + "(7835, '2024-11-13', 4)": 0, + "(7835, '2024-11-13', 5)": 0, + "(7835, '2024-11-13', 6)": 0, + "(7835, '2024-11-13', 7)": 0, + "(7835, '2024-11-14', 0)": 0, + "(7835, '2024-11-14', 1)": 0, + "(7835, '2024-11-14', 2)": 0, + "(7835, '2024-11-14', 3)": 0, + "(7835, '2024-11-14', 4)": 0, + "(7835, '2024-11-14', 5)": 0, + "(7835, '2024-11-14', 6)": 0, + "(7835, '2024-11-14', 7)": 0, + "(7835, '2024-11-15', 0)": 0, + "(7835, '2024-11-15', 1)": 0, + "(7835, '2024-11-15', 2)": 0, + "(7835, '2024-11-15', 3)": 0, + "(7835, '2024-11-15', 4)": 0, + "(7835, '2024-11-15', 5)": 0, + "(7835, '2024-11-15', 6)": 0, + "(7835, '2024-11-15', 7)": 0, + "(7835, '2024-11-16', 0)": 0, + "(7835, '2024-11-16', 1)": 0, + "(7835, '2024-11-16', 2)": 0, + "(7835, '2024-11-16', 3)": 0, + "(7835, '2024-11-16', 4)": 0, + "(7835, '2024-11-16', 5)": 0, + "(7835, '2024-11-16', 6)": 0, + "(7835, '2024-11-16', 7)": 0, + "(7835, '2024-11-17', 0)": 0, + "(7835, '2024-11-17', 1)": 0, + "(7835, '2024-11-17', 2)": 1, + "(7835, '2024-11-17', 3)": 0, + "(7835, '2024-11-17', 4)": 0, + "(7835, '2024-11-17', 5)": 0, + "(7835, '2024-11-17', 6)": 0, + "(7835, '2024-11-17', 7)": 0, + "(7835, '2024-11-18', 0)": 0, + "(7835, '2024-11-18', 1)": 1, + "(7835, '2024-11-18', 2)": 0, + "(7835, '2024-11-18', 3)": 0, + "(7835, '2024-11-18', 4)": 0, + "(7835, '2024-11-18', 5)": 0, + "(7835, '2024-11-18', 6)": 0, + "(7835, '2024-11-18', 7)": 0, + "(7835, '2024-11-19', 0)": 1, + "(7835, '2024-11-19', 1)": 0, + "(7835, '2024-11-19', 2)": 0, + "(7835, '2024-11-19', 3)": 0, + "(7835, '2024-11-19', 4)": 0, + "(7835, '2024-11-19', 5)": 0, + "(7835, '2024-11-19', 6)": 0, + "(7835, '2024-11-19', 7)": 0, + "(7835, '2024-11-20', 0)": 0, + "(7835, '2024-11-20', 1)": 0, + "(7835, '2024-11-20', 2)": 0, + "(7835, '2024-11-20', 3)": 0, + "(7835, '2024-11-20', 4)": 0, + "(7835, '2024-11-20', 5)": 0, + "(7835, '2024-11-20', 6)": 0, + "(7835, '2024-11-20', 7)": 0, + "(7835, '2024-11-21', 0)": 0, + "(7835, '2024-11-21', 1)": 0, + "(7835, '2024-11-21', 2)": 0, + "(7835, '2024-11-21', 3)": 0, + "(7835, '2024-11-21', 4)": 0, + "(7835, '2024-11-21', 5)": 0, + "(7835, '2024-11-21', 6)": 0, + "(7835, '2024-11-21', 7)": 0, + "(7835, '2024-11-22', 0)": 0, + "(7835, '2024-11-22', 1)": 0, + "(7835, '2024-11-22', 2)": 0, + "(7835, '2024-11-22', 3)": 0, + "(7835, '2024-11-22', 4)": 0, + "(7835, '2024-11-22', 5)": 0, + "(7835, '2024-11-22', 6)": 0, + "(7835, '2024-11-22', 7)": 0, + "(7835, '2024-11-23', 0)": 0, + "(7835, '2024-11-23', 1)": 0, + "(7835, '2024-11-23', 2)": 0, + "(7835, '2024-11-23', 3)": 0, + "(7835, '2024-11-23', 4)": 0, + "(7835, '2024-11-23', 5)": 0, + "(7835, '2024-11-23', 6)": 0, + "(7835, '2024-11-23', 7)": 0, + "(7835, '2024-11-24', 0)": 0, + "(7835, '2024-11-24', 1)": 0, + "(7835, '2024-11-24', 2)": 0, + "(7835, '2024-11-24', 3)": 0, + "(7835, '2024-11-24', 4)": 0, + "(7835, '2024-11-24', 5)": 0, + "(7835, '2024-11-24', 6)": 0, + "(7835, '2024-11-24', 7)": 0, + "(7835, '2024-11-25', 0)": 0, + "(7835, '2024-11-25', 1)": 0, + "(7835, '2024-11-25', 2)": 0, + "(7835, '2024-11-25', 3)": 0, + "(7835, '2024-11-25', 4)": 0, + "(7835, '2024-11-25', 5)": 0, + "(7835, '2024-11-25', 6)": 0, + "(7835, '2024-11-25', 7)": 0, + "(7835, '2024-11-26', 0)": 0, + "(7835, '2024-11-26', 1)": 0, + "(7835, '2024-11-26', 2)": 0, + "(7835, '2024-11-26', 3)": 0, + "(7835, '2024-11-26', 4)": 0, + "(7835, '2024-11-26', 5)": 0, + "(7835, '2024-11-26', 6)": 0, + "(7835, '2024-11-26', 7)": 0, + "(7835, '2024-11-27', 0)": 0, + "(7835, '2024-11-27', 1)": 0, + "(7835, '2024-11-27', 2)": 0, + "(7835, '2024-11-27', 3)": 0, + "(7835, '2024-11-27', 4)": 0, + "(7835, '2024-11-27', 5)": 0, + "(7835, '2024-11-27', 6)": 0, + "(7835, '2024-11-27', 7)": 0, + "(7835, '2024-11-28', 0)": 0, + "(7835, '2024-11-28', 1)": 0, + "(7835, '2024-11-28', 2)": 1, + "(7835, '2024-11-28', 3)": 0, + "(7835, '2024-11-28', 4)": 0, + "(7835, '2024-11-28', 5)": 0, + "(7835, '2024-11-28', 6)": 0, + "(7835, '2024-11-28', 7)": 0, + "(7835, '2024-11-29', 0)": 0, + "(7835, '2024-11-29', 1)": 0, + "(7835, '2024-11-29', 2)": 1, + "(7835, '2024-11-29', 3)": 0, + "(7835, '2024-11-29', 4)": 0, + "(7835, '2024-11-29', 5)": 0, + "(7835, '2024-11-29', 6)": 0, + "(7835, '2024-11-29', 7)": 0, + "(7835, '2024-11-30', 0)": 0, + "(7835, '2024-11-30', 1)": 0, + "(7835, '2024-11-30', 2)": 0, + "(7835, '2024-11-30', 3)": 0, + "(7835, '2024-11-30', 4)": 0, + "(7835, '2024-11-30', 5)": 0, + "(7835, '2024-11-30', 6)": 0, + "(7835, '2024-11-30', 7)": 0, + "(7848, '2024-11-01', 0)": 0, + "(7848, '2024-11-01', 1)": 0, + "(7848, '2024-11-01', 2)": 1, + "(7848, '2024-11-01', 3)": 0, + "(7848, '2024-11-01', 4)": 0, + "(7848, '2024-11-01', 5)": 0, + "(7848, '2024-11-01', 6)": 0, + "(7848, '2024-11-01', 7)": 0, + "(7848, '2024-11-02', 0)": 0, + "(7848, '2024-11-02', 1)": 0, + "(7848, '2024-11-02', 2)": 0, + "(7848, '2024-11-02', 3)": 0, + "(7848, '2024-11-02', 4)": 0, + "(7848, '2024-11-02', 5)": 0, + "(7848, '2024-11-02', 6)": 0, + "(7848, '2024-11-02', 7)": 0, + "(7848, '2024-11-03', 0)": 1, + "(7848, '2024-11-03', 1)": 0, + "(7848, '2024-11-03', 2)": 0, + "(7848, '2024-11-03', 3)": 0, + "(7848, '2024-11-03', 4)": 0, + "(7848, '2024-11-03', 5)": 0, + "(7848, '2024-11-03', 6)": 0, + "(7848, '2024-11-03', 7)": 0, + "(7848, '2024-11-04', 0)": 1, + "(7848, '2024-11-04', 1)": 0, + "(7848, '2024-11-04', 2)": 0, + "(7848, '2024-11-04', 3)": 0, + "(7848, '2024-11-04', 4)": 0, + "(7848, '2024-11-04', 5)": 0, + "(7848, '2024-11-04', 6)": 0, + "(7848, '2024-11-04', 7)": 0, + "(7848, '2024-11-05', 0)": 1, + "(7848, '2024-11-05', 1)": 0, + "(7848, '2024-11-05', 2)": 0, + "(7848, '2024-11-05', 3)": 0, + "(7848, '2024-11-05', 4)": 0, + "(7848, '2024-11-05', 5)": 0, + "(7848, '2024-11-05', 6)": 0, + "(7848, '2024-11-05', 7)": 0, + "(7848, '2024-11-06', 0)": 0, + "(7848, '2024-11-06', 1)": 1, + "(7848, '2024-11-06', 2)": 0, + "(7848, '2024-11-06', 3)": 0, + "(7848, '2024-11-06', 4)": 0, + "(7848, '2024-11-06', 5)": 0, + "(7848, '2024-11-06', 6)": 0, + "(7848, '2024-11-06', 7)": 0, + "(7848, '2024-11-07', 0)": 1, + "(7848, '2024-11-07', 1)": 0, + "(7848, '2024-11-07', 2)": 0, + "(7848, '2024-11-07', 3)": 0, + "(7848, '2024-11-07', 4)": 0, + "(7848, '2024-11-07', 5)": 0, + "(7848, '2024-11-07', 6)": 0, + "(7848, '2024-11-07', 7)": 0, + "(7848, '2024-11-08', 0)": 0, + "(7848, '2024-11-08', 1)": 0, + "(7848, '2024-11-08', 2)": 0, + "(7848, '2024-11-08', 3)": 0, + "(7848, '2024-11-08', 4)": 0, + "(7848, '2024-11-08', 5)": 0, + "(7848, '2024-11-08', 6)": 0, + "(7848, '2024-11-08', 7)": 0, + "(7848, '2024-11-09', 0)": 0, + "(7848, '2024-11-09', 1)": 0, + "(7848, '2024-11-09', 2)": 0, + "(7848, '2024-11-09', 3)": 0, + "(7848, '2024-11-09', 4)": 0, + "(7848, '2024-11-09', 5)": 0, + "(7848, '2024-11-09', 6)": 0, + "(7848, '2024-11-09', 7)": 0, + "(7848, '2024-11-10', 0)": 0, + "(7848, '2024-11-10', 1)": 0, + "(7848, '2024-11-10', 2)": 1, + "(7848, '2024-11-10', 3)": 0, + "(7848, '2024-11-10', 4)": 0, + "(7848, '2024-11-10', 5)": 0, + "(7848, '2024-11-10', 6)": 0, + "(7848, '2024-11-10', 7)": 0, + "(7848, '2024-11-11', 0)": 0, + "(7848, '2024-11-11', 1)": 0, + "(7848, '2024-11-11', 2)": 0, + "(7848, '2024-11-11', 3)": 0, + "(7848, '2024-11-11', 4)": 0, + "(7848, '2024-11-11', 5)": 0, + "(7848, '2024-11-11', 6)": 0, + "(7848, '2024-11-11', 7)": 0, + "(7848, '2024-11-12', 0)": 0, + "(7848, '2024-11-12', 1)": 0, + "(7848, '2024-11-12', 2)": 1, + "(7848, '2024-11-12', 3)": 0, + "(7848, '2024-11-12', 4)": 0, + "(7848, '2024-11-12', 5)": 0, + "(7848, '2024-11-12', 6)": 0, + "(7848, '2024-11-12', 7)": 0, + "(7848, '2024-11-13', 0)": 0, + "(7848, '2024-11-13', 1)": 0, + "(7848, '2024-11-13', 2)": 0, + "(7848, '2024-11-13', 3)": 0, + "(7848, '2024-11-13', 4)": 0, + "(7848, '2024-11-13', 5)": 0, + "(7848, '2024-11-13', 6)": 0, + "(7848, '2024-11-13', 7)": 0, + "(7848, '2024-11-14', 0)": 0, + "(7848, '2024-11-14', 1)": 0, + "(7848, '2024-11-14', 2)": 0, + "(7848, '2024-11-14', 3)": 0, + "(7848, '2024-11-14', 4)": 0, + "(7848, '2024-11-14', 5)": 0, + "(7848, '2024-11-14', 6)": 0, + "(7848, '2024-11-14', 7)": 0, + "(7848, '2024-11-15', 0)": 0, + "(7848, '2024-11-15', 1)": 1, + "(7848, '2024-11-15', 2)": 0, + "(7848, '2024-11-15', 3)": 0, + "(7848, '2024-11-15', 4)": 0, + "(7848, '2024-11-15', 5)": 0, + "(7848, '2024-11-15', 6)": 0, + "(7848, '2024-11-15', 7)": 0, + "(7848, '2024-11-16', 0)": 1, + "(7848, '2024-11-16', 1)": 0, + "(7848, '2024-11-16', 2)": 0, + "(7848, '2024-11-16', 3)": 0, + "(7848, '2024-11-16', 4)": 0, + "(7848, '2024-11-16', 5)": 0, + "(7848, '2024-11-16', 6)": 0, + "(7848, '2024-11-16', 7)": 0, + "(7848, '2024-11-17', 0)": 1, + "(7848, '2024-11-17', 1)": 0, + "(7848, '2024-11-17', 2)": 0, + "(7848, '2024-11-17', 3)": 0, + "(7848, '2024-11-17', 4)": 0, + "(7848, '2024-11-17', 5)": 0, + "(7848, '2024-11-17', 6)": 0, + "(7848, '2024-11-17', 7)": 0, + "(7848, '2024-11-18', 0)": 0, + "(7848, '2024-11-18', 1)": 0, + "(7848, '2024-11-18', 2)": 0, + "(7848, '2024-11-18', 3)": 0, + "(7848, '2024-11-18', 4)": 0, + "(7848, '2024-11-18', 5)": 0, + "(7848, '2024-11-18', 6)": 0, + "(7848, '2024-11-18', 7)": 0, + "(7848, '2024-11-19', 0)": 0, + "(7848, '2024-11-19', 1)": 0, + "(7848, '2024-11-19', 2)": 1, + "(7848, '2024-11-19', 3)": 0, + "(7848, '2024-11-19', 4)": 0, + "(7848, '2024-11-19', 5)": 0, + "(7848, '2024-11-19', 6)": 0, + "(7848, '2024-11-19', 7)": 0, + "(7848, '2024-11-20', 0)": 0, + "(7848, '2024-11-20', 1)": 1, + "(7848, '2024-11-20', 2)": 0, + "(7848, '2024-11-20', 3)": 0, + "(7848, '2024-11-20', 4)": 0, + "(7848, '2024-11-20', 5)": 0, + "(7848, '2024-11-20', 6)": 0, + "(7848, '2024-11-20', 7)": 0, + "(7848, '2024-11-21', 0)": 1, + "(7848, '2024-11-21', 1)": 0, + "(7848, '2024-11-21', 2)": 0, + "(7848, '2024-11-21', 3)": 0, + "(7848, '2024-11-21', 4)": 0, + "(7848, '2024-11-21', 5)": 0, + "(7848, '2024-11-21', 6)": 0, + "(7848, '2024-11-21', 7)": 0, + "(7848, '2024-11-22', 0)": 0, + "(7848, '2024-11-22', 1)": 1, + "(7848, '2024-11-22', 2)": 0, + "(7848, '2024-11-22', 3)": 0, + "(7848, '2024-11-22', 4)": 0, + "(7848, '2024-11-22', 5)": 0, + "(7848, '2024-11-22', 6)": 0, + "(7848, '2024-11-22', 7)": 0, + "(7848, '2024-11-23', 0)": 0, + "(7848, '2024-11-23', 1)": 0, + "(7848, '2024-11-23', 2)": 0, + "(7848, '2024-11-23', 3)": 0, + "(7848, '2024-11-23', 4)": 0, + "(7848, '2024-11-23', 5)": 0, + "(7848, '2024-11-23', 6)": 0, + "(7848, '2024-11-23', 7)": 0, + "(7848, '2024-11-24', 0)": 1, + "(7848, '2024-11-24', 1)": 0, + "(7848, '2024-11-24', 2)": 0, + "(7848, '2024-11-24', 3)": 0, + "(7848, '2024-11-24', 4)": 0, + "(7848, '2024-11-24', 5)": 0, + "(7848, '2024-11-24', 6)": 0, + "(7848, '2024-11-24', 7)": 0, + "(7848, '2024-11-25', 0)": 0, + "(7848, '2024-11-25', 1)": 1, + "(7848, '2024-11-25', 2)": 0, + "(7848, '2024-11-25', 3)": 0, + "(7848, '2024-11-25', 4)": 0, + "(7848, '2024-11-25', 5)": 0, + "(7848, '2024-11-25', 6)": 0, + "(7848, '2024-11-25', 7)": 0, + "(7848, '2024-11-26', 0)": 0, + "(7848, '2024-11-26', 1)": 0, + "(7848, '2024-11-26', 2)": 0, + "(7848, '2024-11-26', 3)": 0, + "(7848, '2024-11-26', 4)": 0, + "(7848, '2024-11-26', 5)": 0, + "(7848, '2024-11-26', 6)": 0, + "(7848, '2024-11-26', 7)": 0, + "(7848, '2024-11-27', 0)": 0, + "(7848, '2024-11-27', 1)": 0, + "(7848, '2024-11-27', 2)": 0, + "(7848, '2024-11-27', 3)": 0, + "(7848, '2024-11-27', 4)": 0, + "(7848, '2024-11-27', 5)": 0, + "(7848, '2024-11-27', 6)": 0, + "(7848, '2024-11-27', 7)": 0, + "(7848, '2024-11-28', 0)": 0, + "(7848, '2024-11-28', 1)": 1, + "(7848, '2024-11-28', 2)": 0, + "(7848, '2024-11-28', 3)": 0, + "(7848, '2024-11-28', 4)": 0, + "(7848, '2024-11-28', 5)": 0, + "(7848, '2024-11-28', 6)": 0, + "(7848, '2024-11-28', 7)": 0, + "(7848, '2024-11-29', 0)": 0, + "(7848, '2024-11-29', 1)": 1, + "(7848, '2024-11-29', 2)": 0, + "(7848, '2024-11-29', 3)": 0, + "(7848, '2024-11-29', 4)": 0, + "(7848, '2024-11-29', 5)": 0, + "(7848, '2024-11-29', 6)": 0, + "(7848, '2024-11-29', 7)": 0, + "(7848, '2024-11-30', 0)": 0, + "(7848, '2024-11-30', 1)": 0, + "(7848, '2024-11-30', 2)": 0, + "(7848, '2024-11-30', 3)": 0, + "(7848, '2024-11-30', 4)": 0, + "(7848, '2024-11-30', 5)": 0, + "(7848, '2024-11-30', 6)": 0, + "(7848, '2024-11-30', 7)": 0, + "(7877, '2024-11-01', 0)": 0, + "(7877, '2024-11-01', 1)": 0, + "(7877, '2024-11-01', 2)": 0, + "(7877, '2024-11-01', 3)": 0, + "(7877, '2024-11-01', 4)": 0, + "(7877, '2024-11-01', 5)": 0, + "(7877, '2024-11-01', 6)": 0, + "(7877, '2024-11-01', 7)": 0, + "(7877, '2024-11-02', 0)": 0, + "(7877, '2024-11-02', 1)": 0, + "(7877, '2024-11-02', 2)": 0, + "(7877, '2024-11-02', 3)": 0, + "(7877, '2024-11-02', 4)": 0, + "(7877, '2024-11-02', 5)": 0, + "(7877, '2024-11-02', 6)": 0, + "(7877, '2024-11-02', 7)": 0, + "(7877, '2024-11-03', 0)": 0, + "(7877, '2024-11-03', 1)": 0, + "(7877, '2024-11-03', 2)": 0, + "(7877, '2024-11-03', 3)": 0, + "(7877, '2024-11-03', 4)": 0, + "(7877, '2024-11-03', 5)": 0, + "(7877, '2024-11-03', 6)": 0, + "(7877, '2024-11-03', 7)": 0, + "(7877, '2024-11-04', 0)": 0, + "(7877, '2024-11-04', 1)": 0, + "(7877, '2024-11-04', 2)": 0, + "(7877, '2024-11-04', 3)": 0, + "(7877, '2024-11-04', 4)": 0, + "(7877, '2024-11-04', 5)": 0, + "(7877, '2024-11-04', 6)": 0, + "(7877, '2024-11-04', 7)": 0, + "(7877, '2024-11-05', 0)": 0, + "(7877, '2024-11-05', 1)": 0, + "(7877, '2024-11-05', 2)": 0, + "(7877, '2024-11-05', 3)": 0, + "(7877, '2024-11-05', 4)": 0, + "(7877, '2024-11-05', 5)": 0, + "(7877, '2024-11-05', 6)": 0, + "(7877, '2024-11-05', 7)": 0, + "(7877, '2024-11-06', 0)": 0, + "(7877, '2024-11-06', 1)": 0, + "(7877, '2024-11-06', 2)": 0, + "(7877, '2024-11-06', 3)": 0, + "(7877, '2024-11-06', 4)": 0, + "(7877, '2024-11-06', 5)": 0, + "(7877, '2024-11-06', 6)": 0, + "(7877, '2024-11-06', 7)": 0, + "(7877, '2024-11-07', 0)": 0, + "(7877, '2024-11-07', 1)": 0, + "(7877, '2024-11-07', 2)": 1, + "(7877, '2024-11-07', 3)": 0, + "(7877, '2024-11-07', 4)": 0, + "(7877, '2024-11-07', 5)": 0, + "(7877, '2024-11-07', 6)": 0, + "(7877, '2024-11-07', 7)": 0, + "(7877, '2024-11-08', 0)": 0, + "(7877, '2024-11-08', 1)": 0, + "(7877, '2024-11-08', 2)": 0, + "(7877, '2024-11-08', 3)": 0, + "(7877, '2024-11-08', 4)": 0, + "(7877, '2024-11-08', 5)": 0, + "(7877, '2024-11-08', 6)": 0, + "(7877, '2024-11-08', 7)": 0, + "(7877, '2024-11-09', 0)": 0, + "(7877, '2024-11-09', 1)": 0, + "(7877, '2024-11-09', 2)": 1, + "(7877, '2024-11-09', 3)": 0, + "(7877, '2024-11-09', 4)": 0, + "(7877, '2024-11-09', 5)": 0, + "(7877, '2024-11-09', 6)": 0, + "(7877, '2024-11-09', 7)": 0, + "(7877, '2024-11-10', 0)": 0, + "(7877, '2024-11-10', 1)": 0, + "(7877, '2024-11-10', 2)": 0, + "(7877, '2024-11-10', 3)": 0, + "(7877, '2024-11-10', 4)": 0, + "(7877, '2024-11-10', 5)": 0, + "(7877, '2024-11-10', 6)": 0, + "(7877, '2024-11-10', 7)": 0, + "(7877, '2024-11-11', 0)": 0, + "(7877, '2024-11-11', 1)": 1, + "(7877, '2024-11-11', 2)": 0, + "(7877, '2024-11-11', 3)": 0, + "(7877, '2024-11-11', 4)": 0, + "(7877, '2024-11-11', 5)": 0, + "(7877, '2024-11-11', 6)": 0, + "(7877, '2024-11-11', 7)": 0, + "(7877, '2024-11-12', 0)": 0, + "(7877, '2024-11-12', 1)": 1, + "(7877, '2024-11-12', 2)": 0, + "(7877, '2024-11-12', 3)": 0, + "(7877, '2024-11-12', 4)": 0, + "(7877, '2024-11-12', 5)": 0, + "(7877, '2024-11-12', 6)": 0, + "(7877, '2024-11-12', 7)": 0, + "(7877, '2024-11-13', 0)": 0, + "(7877, '2024-11-13', 1)": 1, + "(7877, '2024-11-13', 2)": 0, + "(7877, '2024-11-13', 3)": 0, + "(7877, '2024-11-13', 4)": 0, + "(7877, '2024-11-13', 5)": 0, + "(7877, '2024-11-13', 6)": 0, + "(7877, '2024-11-13', 7)": 0, + "(7877, '2024-11-14', 0)": 1, + "(7877, '2024-11-14', 1)": 0, + "(7877, '2024-11-14', 2)": 0, + "(7877, '2024-11-14', 3)": 0, + "(7877, '2024-11-14', 4)": 0, + "(7877, '2024-11-14', 5)": 0, + "(7877, '2024-11-14', 6)": 0, + "(7877, '2024-11-14', 7)": 0, + "(7877, '2024-11-15', 0)": 1, + "(7877, '2024-11-15', 1)": 0, + "(7877, '2024-11-15', 2)": 0, + "(7877, '2024-11-15', 3)": 0, + "(7877, '2024-11-15', 4)": 0, + "(7877, '2024-11-15', 5)": 0, + "(7877, '2024-11-15', 6)": 0, + "(7877, '2024-11-15', 7)": 0, + "(7877, '2024-11-16', 0)": 0, + "(7877, '2024-11-16', 1)": 1, + "(7877, '2024-11-16', 2)": 0, + "(7877, '2024-11-16', 3)": 0, + "(7877, '2024-11-16', 4)": 0, + "(7877, '2024-11-16', 5)": 0, + "(7877, '2024-11-16', 6)": 0, + "(7877, '2024-11-16', 7)": 0, + "(7877, '2024-11-17', 0)": 0, + "(7877, '2024-11-17', 1)": 0, + "(7877, '2024-11-17', 2)": 0, + "(7877, '2024-11-17', 3)": 0, + "(7877, '2024-11-17', 4)": 0, + "(7877, '2024-11-17', 5)": 0, + "(7877, '2024-11-17', 6)": 0, + "(7877, '2024-11-17', 7)": 0, + "(7877, '2024-11-18', 0)": 0, + "(7877, '2024-11-18', 1)": 0, + "(7877, '2024-11-18', 2)": 1, + "(7877, '2024-11-18', 3)": 0, + "(7877, '2024-11-18', 4)": 0, + "(7877, '2024-11-18', 5)": 0, + "(7877, '2024-11-18', 6)": 0, + "(7877, '2024-11-18', 7)": 0, + "(7877, '2024-11-19', 0)": 0, + "(7877, '2024-11-19', 1)": 0, + "(7877, '2024-11-19', 2)": 0, + "(7877, '2024-11-19', 3)": 0, + "(7877, '2024-11-19', 4)": 0, + "(7877, '2024-11-19', 5)": 0, + "(7877, '2024-11-19', 6)": 0, + "(7877, '2024-11-19', 7)": 0, + "(7877, '2024-11-20', 0)": 0, + "(7877, '2024-11-20', 1)": 1, + "(7877, '2024-11-20', 2)": 0, + "(7877, '2024-11-20', 3)": 0, + "(7877, '2024-11-20', 4)": 0, + "(7877, '2024-11-20', 5)": 0, + "(7877, '2024-11-20', 6)": 0, + "(7877, '2024-11-20', 7)": 0, + "(7877, '2024-11-21', 0)": 0, + "(7877, '2024-11-21', 1)": 0, + "(7877, '2024-11-21', 2)": 0, + "(7877, '2024-11-21', 3)": 0, + "(7877, '2024-11-21', 4)": 0, + "(7877, '2024-11-21', 5)": 0, + "(7877, '2024-11-21', 6)": 0, + "(7877, '2024-11-21', 7)": 0, + "(7877, '2024-11-22', 0)": 0, + "(7877, '2024-11-22', 1)": 0, + "(7877, '2024-11-22', 2)": 0, + "(7877, '2024-11-22', 3)": 0, + "(7877, '2024-11-22', 4)": 0, + "(7877, '2024-11-22', 5)": 0, + "(7877, '2024-11-22', 6)": 0, + "(7877, '2024-11-22', 7)": 0, + "(7877, '2024-11-23', 0)": 0, + "(7877, '2024-11-23', 1)": 0, + "(7877, '2024-11-23', 2)": 0, + "(7877, '2024-11-23', 3)": 0, + "(7877, '2024-11-23', 4)": 0, + "(7877, '2024-11-23', 5)": 0, + "(7877, '2024-11-23', 6)": 0, + "(7877, '2024-11-23', 7)": 0, + "(7877, '2024-11-24', 0)": 0, + "(7877, '2024-11-24', 1)": 0, + "(7877, '2024-11-24', 2)": 1, + "(7877, '2024-11-24', 3)": 0, + "(7877, '2024-11-24', 4)": 0, + "(7877, '2024-11-24', 5)": 0, + "(7877, '2024-11-24', 6)": 0, + "(7877, '2024-11-24', 7)": 0, + "(7877, '2024-11-25', 0)": 0, + "(7877, '2024-11-25', 1)": 0, + "(7877, '2024-11-25', 2)": 1, + "(7877, '2024-11-25', 3)": 0, + "(7877, '2024-11-25', 4)": 0, + "(7877, '2024-11-25', 5)": 0, + "(7877, '2024-11-25', 6)": 0, + "(7877, '2024-11-25', 7)": 0, + "(7877, '2024-11-26', 0)": 0, + "(7877, '2024-11-26', 1)": 0, + "(7877, '2024-11-26', 2)": 0, + "(7877, '2024-11-26', 3)": 0, + "(7877, '2024-11-26', 4)": 0, + "(7877, '2024-11-26', 5)": 0, + "(7877, '2024-11-26', 6)": 0, + "(7877, '2024-11-26', 7)": 0, + "(7877, '2024-11-27', 0)": 0, + "(7877, '2024-11-27', 1)": 0, + "(7877, '2024-11-27', 2)": 0, + "(7877, '2024-11-27', 3)": 0, + "(7877, '2024-11-27', 4)": 0, + "(7877, '2024-11-27', 5)": 0, + "(7877, '2024-11-27', 6)": 0, + "(7877, '2024-11-27', 7)": 0, + "(7877, '2024-11-28', 0)": 1, + "(7877, '2024-11-28', 1)": 0, + "(7877, '2024-11-28', 2)": 0, + "(7877, '2024-11-28', 3)": 0, + "(7877, '2024-11-28', 4)": 0, + "(7877, '2024-11-28', 5)": 0, + "(7877, '2024-11-28', 6)": 0, + "(7877, '2024-11-28', 7)": 0, + "(7877, '2024-11-29', 0)": 0, + "(7877, '2024-11-29', 1)": 0, + "(7877, '2024-11-29', 2)": 0, + "(7877, '2024-11-29', 3)": 0, + "(7877, '2024-11-29', 4)": 0, + "(7877, '2024-11-29', 5)": 0, + "(7877, '2024-11-29', 6)": 0, + "(7877, '2024-11-29', 7)": 0, + "(7877, '2024-11-30', 0)": 0, + "(7877, '2024-11-30', 1)": 0, + "(7877, '2024-11-30', 2)": 0, + "(7877, '2024-11-30', 3)": 0, + "(7877, '2024-11-30', 4)": 0, + "(7877, '2024-11-30', 5)": 0, + "(7877, '2024-11-30', 6)": 0, + "(7877, '2024-11-30', 7)": 0, + "(790, '2024-11-01', 0)": 0, + "(790, '2024-11-01', 1)": 0, + "(790, '2024-11-01', 2)": 0, + "(790, '2024-11-01', 3)": 0, + "(790, '2024-11-01', 4)": 0, + "(790, '2024-11-01', 5)": 0, + "(790, '2024-11-01', 6)": 0, + "(790, '2024-11-01', 7)": 0, + "(790, '2024-11-02', 0)": 0, + "(790, '2024-11-02', 1)": 0, + "(790, '2024-11-02', 2)": 0, + "(790, '2024-11-02', 3)": 0, + "(790, '2024-11-02', 4)": 0, + "(790, '2024-11-02', 5)": 0, + "(790, '2024-11-02', 6)": 0, + "(790, '2024-11-02', 7)": 0, + "(790, '2024-11-03', 0)": 0, + "(790, '2024-11-03', 1)": 0, + "(790, '2024-11-03', 2)": 0, + "(790, '2024-11-03', 3)": 0, + "(790, '2024-11-03', 4)": 0, + "(790, '2024-11-03', 5)": 0, + "(790, '2024-11-03', 6)": 0, + "(790, '2024-11-03', 7)": 0, + "(790, '2024-11-04', 0)": 0, + "(790, '2024-11-04', 1)": 0, + "(790, '2024-11-04', 2)": 0, + "(790, '2024-11-04', 3)": 0, + "(790, '2024-11-04', 4)": 0, + "(790, '2024-11-04', 5)": 0, + "(790, '2024-11-04', 6)": 0, + "(790, '2024-11-04', 7)": 0, + "(790, '2024-11-05', 0)": 0, + "(790, '2024-11-05', 1)": 0, + "(790, '2024-11-05', 2)": 0, + "(790, '2024-11-05', 3)": 0, + "(790, '2024-11-05', 4)": 0, + "(790, '2024-11-05', 5)": 0, + "(790, '2024-11-05', 6)": 0, + "(790, '2024-11-05', 7)": 0, + "(790, '2024-11-06', 0)": 0, + "(790, '2024-11-06', 1)": 0, + "(790, '2024-11-06', 2)": 0, + "(790, '2024-11-06', 3)": 0, + "(790, '2024-11-06', 4)": 0, + "(790, '2024-11-06', 5)": 0, + "(790, '2024-11-06', 6)": 0, + "(790, '2024-11-06', 7)": 0, + "(790, '2024-11-07', 0)": 0, + "(790, '2024-11-07', 1)": 0, + "(790, '2024-11-07', 2)": 0, + "(790, '2024-11-07', 3)": 0, + "(790, '2024-11-07', 4)": 0, + "(790, '2024-11-07', 5)": 0, + "(790, '2024-11-07', 6)": 0, + "(790, '2024-11-07', 7)": 0, + "(790, '2024-11-08', 0)": 0, + "(790, '2024-11-08', 1)": 0, + "(790, '2024-11-08', 2)": 0, + "(790, '2024-11-08', 3)": 0, + "(790, '2024-11-08', 4)": 0, + "(790, '2024-11-08', 5)": 0, + "(790, '2024-11-08', 6)": 0, + "(790, '2024-11-08', 7)": 0, + "(790, '2024-11-09', 0)": 0, + "(790, '2024-11-09', 1)": 0, + "(790, '2024-11-09', 2)": 0, + "(790, '2024-11-09', 3)": 0, + "(790, '2024-11-09', 4)": 0, + "(790, '2024-11-09', 5)": 0, + "(790, '2024-11-09', 6)": 0, + "(790, '2024-11-09', 7)": 0, + "(790, '2024-11-10', 0)": 0, + "(790, '2024-11-10', 1)": 0, + "(790, '2024-11-10', 2)": 0, + "(790, '2024-11-10', 3)": 0, + "(790, '2024-11-10', 4)": 0, + "(790, '2024-11-10', 5)": 0, + "(790, '2024-11-10', 6)": 0, + "(790, '2024-11-10', 7)": 0, + "(790, '2024-11-11', 0)": 0, + "(790, '2024-11-11', 1)": 0, + "(790, '2024-11-11', 2)": 0, + "(790, '2024-11-11', 3)": 0, + "(790, '2024-11-11', 4)": 0, + "(790, '2024-11-11', 5)": 0, + "(790, '2024-11-11', 6)": 0, + "(790, '2024-11-11', 7)": 0, + "(790, '2024-11-12', 0)": 0, + "(790, '2024-11-12', 1)": 0, + "(790, '2024-11-12', 2)": 0, + "(790, '2024-11-12', 3)": 0, + "(790, '2024-11-12', 4)": 0, + "(790, '2024-11-12', 5)": 0, + "(790, '2024-11-12', 6)": 0, + "(790, '2024-11-12', 7)": 0, + "(790, '2024-11-13', 0)": 0, + "(790, '2024-11-13', 1)": 0, + "(790, '2024-11-13', 2)": 0, + "(790, '2024-11-13', 3)": 0, + "(790, '2024-11-13', 4)": 0, + "(790, '2024-11-13', 5)": 0, + "(790, '2024-11-13', 6)": 0, + "(790, '2024-11-13', 7)": 0, + "(790, '2024-11-14', 0)": 0, + "(790, '2024-11-14', 1)": 0, + "(790, '2024-11-14', 2)": 0, + "(790, '2024-11-14', 3)": 0, + "(790, '2024-11-14', 4)": 0, + "(790, '2024-11-14', 5)": 0, + "(790, '2024-11-14', 6)": 0, + "(790, '2024-11-14', 7)": 0, + "(790, '2024-11-15', 0)": 0, + "(790, '2024-11-15', 1)": 0, + "(790, '2024-11-15', 2)": 0, + "(790, '2024-11-15', 3)": 0, + "(790, '2024-11-15', 4)": 0, + "(790, '2024-11-15', 5)": 0, + "(790, '2024-11-15', 6)": 0, + "(790, '2024-11-15', 7)": 0, + "(790, '2024-11-16', 0)": 0, + "(790, '2024-11-16', 1)": 0, + "(790, '2024-11-16', 2)": 0, + "(790, '2024-11-16', 3)": 0, + "(790, '2024-11-16', 4)": 0, + "(790, '2024-11-16', 5)": 0, + "(790, '2024-11-16', 6)": 0, + "(790, '2024-11-16', 7)": 0, + "(790, '2024-11-17', 0)": 0, + "(790, '2024-11-17', 1)": 0, + "(790, '2024-11-17', 2)": 0, + "(790, '2024-11-17', 3)": 0, + "(790, '2024-11-17', 4)": 0, + "(790, '2024-11-17', 5)": 0, + "(790, '2024-11-17', 6)": 0, + "(790, '2024-11-17', 7)": 0, + "(790, '2024-11-18', 0)": 0, + "(790, '2024-11-18', 1)": 0, + "(790, '2024-11-18', 2)": 0, + "(790, '2024-11-18', 3)": 0, + "(790, '2024-11-18', 4)": 0, + "(790, '2024-11-18', 5)": 0, + "(790, '2024-11-18', 6)": 0, + "(790, '2024-11-18', 7)": 0, + "(790, '2024-11-19', 0)": 0, + "(790, '2024-11-19', 1)": 0, + "(790, '2024-11-19', 2)": 0, + "(790, '2024-11-19', 3)": 0, + "(790, '2024-11-19', 4)": 0, + "(790, '2024-11-19', 5)": 0, + "(790, '2024-11-19', 6)": 0, + "(790, '2024-11-19', 7)": 0, + "(790, '2024-11-20', 0)": 0, + "(790, '2024-11-20', 1)": 0, + "(790, '2024-11-20', 2)": 0, + "(790, '2024-11-20', 3)": 0, + "(790, '2024-11-20', 4)": 0, + "(790, '2024-11-20', 5)": 0, + "(790, '2024-11-20', 6)": 0, + "(790, '2024-11-20', 7)": 0, + "(790, '2024-11-21', 0)": 0, + "(790, '2024-11-21', 1)": 0, + "(790, '2024-11-21', 2)": 0, + "(790, '2024-11-21', 3)": 0, + "(790, '2024-11-21', 4)": 0, + "(790, '2024-11-21', 5)": 0, + "(790, '2024-11-21', 6)": 0, + "(790, '2024-11-21', 7)": 0, + "(790, '2024-11-22', 0)": 0, + "(790, '2024-11-22', 1)": 0, + "(790, '2024-11-22', 2)": 0, + "(790, '2024-11-22', 3)": 0, + "(790, '2024-11-22', 4)": 0, + "(790, '2024-11-22', 5)": 0, + "(790, '2024-11-22', 6)": 0, + "(790, '2024-11-22', 7)": 0, + "(790, '2024-11-23', 0)": 0, + "(790, '2024-11-23', 1)": 0, + "(790, '2024-11-23', 2)": 0, + "(790, '2024-11-23', 3)": 0, + "(790, '2024-11-23', 4)": 0, + "(790, '2024-11-23', 5)": 0, + "(790, '2024-11-23', 6)": 0, + "(790, '2024-11-23', 7)": 0, + "(790, '2024-11-24', 0)": 0, + "(790, '2024-11-24', 1)": 0, + "(790, '2024-11-24', 2)": 0, + "(790, '2024-11-24', 3)": 0, + "(790, '2024-11-24', 4)": 0, + "(790, '2024-11-24', 5)": 0, + "(790, '2024-11-24', 6)": 0, + "(790, '2024-11-24', 7)": 0, + "(790, '2024-11-25', 0)": 0, + "(790, '2024-11-25', 1)": 0, + "(790, '2024-11-25', 2)": 0, + "(790, '2024-11-25', 3)": 0, + "(790, '2024-11-25', 4)": 0, + "(790, '2024-11-25', 5)": 0, + "(790, '2024-11-25', 6)": 0, + "(790, '2024-11-25', 7)": 0, + "(790, '2024-11-26', 0)": 0, + "(790, '2024-11-26', 1)": 0, + "(790, '2024-11-26', 2)": 0, + "(790, '2024-11-26', 3)": 0, + "(790, '2024-11-26', 4)": 0, + "(790, '2024-11-26', 5)": 0, + "(790, '2024-11-26', 6)": 0, + "(790, '2024-11-26', 7)": 0, + "(790, '2024-11-27', 0)": 0, + "(790, '2024-11-27', 1)": 0, + "(790, '2024-11-27', 2)": 0, + "(790, '2024-11-27', 3)": 0, + "(790, '2024-11-27', 4)": 0, + "(790, '2024-11-27', 5)": 0, + "(790, '2024-11-27', 6)": 0, + "(790, '2024-11-27', 7)": 0, + "(790, '2024-11-28', 0)": 0, + "(790, '2024-11-28', 1)": 0, + "(790, '2024-11-28', 2)": 0, + "(790, '2024-11-28', 3)": 0, + "(790, '2024-11-28', 4)": 0, + "(790, '2024-11-28', 5)": 0, + "(790, '2024-11-28', 6)": 0, + "(790, '2024-11-28', 7)": 0, + "(790, '2024-11-29', 0)": 0, + "(790, '2024-11-29', 1)": 0, + "(790, '2024-11-29', 2)": 0, + "(790, '2024-11-29', 3)": 0, + "(790, '2024-11-29', 4)": 0, + "(790, '2024-11-29', 5)": 0, + "(790, '2024-11-29', 6)": 0, + "(790, '2024-11-29', 7)": 0, + "(790, '2024-11-30', 0)": 0, + "(790, '2024-11-30', 1)": 0, + "(790, '2024-11-30', 2)": 0, + "(790, '2024-11-30', 3)": 0, + "(790, '2024-11-30', 4)": 0, + "(790, '2024-11-30', 5)": 0, + "(790, '2024-11-30', 6)": 0, + "(790, '2024-11-30', 7)": 0, + "(791, '2024-11-01', 0)": 0, + "(791, '2024-11-01', 1)": 0, + "(791, '2024-11-01', 2)": 0, + "(791, '2024-11-01', 3)": 0, + "(791, '2024-11-01', 4)": 0, + "(791, '2024-11-01', 5)": 0, + "(791, '2024-11-01', 6)": 0, + "(791, '2024-11-01', 7)": 0, + "(791, '2024-11-02', 0)": 0, + "(791, '2024-11-02', 1)": 0, + "(791, '2024-11-02', 2)": 1, + "(791, '2024-11-02', 3)": 0, + "(791, '2024-11-02', 4)": 0, + "(791, '2024-11-02', 5)": 0, + "(791, '2024-11-02', 6)": 0, + "(791, '2024-11-02', 7)": 0, + "(791, '2024-11-03', 0)": 0, + "(791, '2024-11-03', 1)": 0, + "(791, '2024-11-03', 2)": 0, + "(791, '2024-11-03', 3)": 0, + "(791, '2024-11-03', 4)": 0, + "(791, '2024-11-03', 5)": 0, + "(791, '2024-11-03', 6)": 0, + "(791, '2024-11-03', 7)": 0, + "(791, '2024-11-04', 0)": 0, + "(791, '2024-11-04', 1)": 0, + "(791, '2024-11-04', 2)": 0, + "(791, '2024-11-04', 3)": 0, + "(791, '2024-11-04', 4)": 0, + "(791, '2024-11-04', 5)": 0, + "(791, '2024-11-04', 6)": 0, + "(791, '2024-11-04', 7)": 0, + "(791, '2024-11-05', 0)": 0, + "(791, '2024-11-05', 1)": 0, + "(791, '2024-11-05', 2)": 0, + "(791, '2024-11-05', 3)": 0, + "(791, '2024-11-05', 4)": 0, + "(791, '2024-11-05', 5)": 0, + "(791, '2024-11-05', 6)": 0, + "(791, '2024-11-05', 7)": 0, + "(791, '2024-11-06', 0)": 1, + "(791, '2024-11-06', 1)": 0, + "(791, '2024-11-06', 2)": 0, + "(791, '2024-11-06', 3)": 0, + "(791, '2024-11-06', 4)": 0, + "(791, '2024-11-06', 5)": 0, + "(791, '2024-11-06', 6)": 0, + "(791, '2024-11-06', 7)": 0, + "(791, '2024-11-07', 0)": 1, + "(791, '2024-11-07', 1)": 0, + "(791, '2024-11-07', 2)": 0, + "(791, '2024-11-07', 3)": 0, + "(791, '2024-11-07', 4)": 0, + "(791, '2024-11-07', 5)": 0, + "(791, '2024-11-07', 6)": 0, + "(791, '2024-11-07', 7)": 0, + "(791, '2024-11-08', 0)": 0, + "(791, '2024-11-08', 1)": 0, + "(791, '2024-11-08', 2)": 0, + "(791, '2024-11-08', 3)": 0, + "(791, '2024-11-08', 4)": 0, + "(791, '2024-11-08', 5)": 0, + "(791, '2024-11-08', 6)": 0, + "(791, '2024-11-08', 7)": 0, + "(791, '2024-11-09', 0)": 1, + "(791, '2024-11-09', 1)": 0, + "(791, '2024-11-09', 2)": 0, + "(791, '2024-11-09', 3)": 0, + "(791, '2024-11-09', 4)": 0, + "(791, '2024-11-09', 5)": 0, + "(791, '2024-11-09', 6)": 0, + "(791, '2024-11-09', 7)": 0, + "(791, '2024-11-10', 0)": 0, + "(791, '2024-11-10', 1)": 0, + "(791, '2024-11-10', 2)": 0, + "(791, '2024-11-10', 3)": 0, + "(791, '2024-11-10', 4)": 0, + "(791, '2024-11-10', 5)": 0, + "(791, '2024-11-10', 6)": 0, + "(791, '2024-11-10', 7)": 0, + "(791, '2024-11-11', 0)": 1, + "(791, '2024-11-11', 1)": 0, + "(791, '2024-11-11', 2)": 0, + "(791, '2024-11-11', 3)": 0, + "(791, '2024-11-11', 4)": 0, + "(791, '2024-11-11', 5)": 0, + "(791, '2024-11-11', 6)": 0, + "(791, '2024-11-11', 7)": 0, + "(791, '2024-11-12', 0)": 1, + "(791, '2024-11-12', 1)": 0, + "(791, '2024-11-12', 2)": 0, + "(791, '2024-11-12', 3)": 0, + "(791, '2024-11-12', 4)": 0, + "(791, '2024-11-12', 5)": 0, + "(791, '2024-11-12', 6)": 0, + "(791, '2024-11-12', 7)": 0, + "(791, '2024-11-13', 0)": 1, + "(791, '2024-11-13', 1)": 0, + "(791, '2024-11-13', 2)": 0, + "(791, '2024-11-13', 3)": 0, + "(791, '2024-11-13', 4)": 0, + "(791, '2024-11-13', 5)": 0, + "(791, '2024-11-13', 6)": 0, + "(791, '2024-11-13', 7)": 0, + "(791, '2024-11-14', 0)": 1, + "(791, '2024-11-14', 1)": 0, + "(791, '2024-11-14', 2)": 0, + "(791, '2024-11-14', 3)": 0, + "(791, '2024-11-14', 4)": 0, + "(791, '2024-11-14', 5)": 0, + "(791, '2024-11-14', 6)": 0, + "(791, '2024-11-14', 7)": 0, + "(791, '2024-11-15', 0)": 1, + "(791, '2024-11-15', 1)": 0, + "(791, '2024-11-15', 2)": 0, + "(791, '2024-11-15', 3)": 0, + "(791, '2024-11-15', 4)": 0, + "(791, '2024-11-15', 5)": 0, + "(791, '2024-11-15', 6)": 0, + "(791, '2024-11-15', 7)": 0, + "(791, '2024-11-16', 0)": 0, + "(791, '2024-11-16', 1)": 0, + "(791, '2024-11-16', 2)": 0, + "(791, '2024-11-16', 3)": 0, + "(791, '2024-11-16', 4)": 0, + "(791, '2024-11-16', 5)": 0, + "(791, '2024-11-16', 6)": 0, + "(791, '2024-11-16', 7)": 0, + "(791, '2024-11-17', 0)": 1, + "(791, '2024-11-17', 1)": 0, + "(791, '2024-11-17', 2)": 0, + "(791, '2024-11-17', 3)": 0, + "(791, '2024-11-17', 4)": 0, + "(791, '2024-11-17', 5)": 0, + "(791, '2024-11-17', 6)": 0, + "(791, '2024-11-17', 7)": 0, + "(791, '2024-11-18', 0)": 0, + "(791, '2024-11-18', 1)": 0, + "(791, '2024-11-18', 2)": 0, + "(791, '2024-11-18', 3)": 0, + "(791, '2024-11-18', 4)": 0, + "(791, '2024-11-18', 5)": 0, + "(791, '2024-11-18', 6)": 0, + "(791, '2024-11-18', 7)": 0, + "(791, '2024-11-19', 0)": 1, + "(791, '2024-11-19', 1)": 0, + "(791, '2024-11-19', 2)": 0, + "(791, '2024-11-19', 3)": 0, + "(791, '2024-11-19', 4)": 0, + "(791, '2024-11-19', 5)": 0, + "(791, '2024-11-19', 6)": 0, + "(791, '2024-11-19', 7)": 0, + "(791, '2024-11-20', 0)": 1, + "(791, '2024-11-20', 1)": 0, + "(791, '2024-11-20', 2)": 0, + "(791, '2024-11-20', 3)": 0, + "(791, '2024-11-20', 4)": 0, + "(791, '2024-11-20', 5)": 0, + "(791, '2024-11-20', 6)": 0, + "(791, '2024-11-20', 7)": 0, + "(791, '2024-11-21', 0)": 0, + "(791, '2024-11-21', 1)": 0, + "(791, '2024-11-21', 2)": 0, + "(791, '2024-11-21', 3)": 0, + "(791, '2024-11-21', 4)": 0, + "(791, '2024-11-21', 5)": 0, + "(791, '2024-11-21', 6)": 0, + "(791, '2024-11-21', 7)": 0, + "(791, '2024-11-22', 0)": 0, + "(791, '2024-11-22', 1)": 0, + "(791, '2024-11-22', 2)": 0, + "(791, '2024-11-22', 3)": 0, + "(791, '2024-11-22', 4)": 0, + "(791, '2024-11-22', 5)": 0, + "(791, '2024-11-22', 6)": 0, + "(791, '2024-11-22', 7)": 0, + "(791, '2024-11-23', 0)": 0, + "(791, '2024-11-23', 1)": 0, + "(791, '2024-11-23', 2)": 1, + "(791, '2024-11-23', 3)": 0, + "(791, '2024-11-23', 4)": 0, + "(791, '2024-11-23', 5)": 0, + "(791, '2024-11-23', 6)": 0, + "(791, '2024-11-23', 7)": 0, + "(791, '2024-11-24', 0)": 0, + "(791, '2024-11-24', 1)": 0, + "(791, '2024-11-24', 2)": 0, + "(791, '2024-11-24', 3)": 0, + "(791, '2024-11-24', 4)": 0, + "(791, '2024-11-24', 5)": 0, + "(791, '2024-11-24', 6)": 0, + "(791, '2024-11-24', 7)": 0, + "(791, '2024-11-25', 0)": 0, + "(791, '2024-11-25', 1)": 0, + "(791, '2024-11-25', 2)": 0, + "(791, '2024-11-25', 3)": 0, + "(791, '2024-11-25', 4)": 0, + "(791, '2024-11-25', 5)": 0, + "(791, '2024-11-25', 6)": 0, + "(791, '2024-11-25', 7)": 0, + "(791, '2024-11-26', 0)": 1, + "(791, '2024-11-26', 1)": 0, + "(791, '2024-11-26', 2)": 0, + "(791, '2024-11-26', 3)": 0, + "(791, '2024-11-26', 4)": 0, + "(791, '2024-11-26', 5)": 0, + "(791, '2024-11-26', 6)": 0, + "(791, '2024-11-26', 7)": 0, + "(791, '2024-11-27', 0)": 1, + "(791, '2024-11-27', 1)": 0, + "(791, '2024-11-27', 2)": 0, + "(791, '2024-11-27', 3)": 0, + "(791, '2024-11-27', 4)": 0, + "(791, '2024-11-27', 5)": 0, + "(791, '2024-11-27', 6)": 0, + "(791, '2024-11-27', 7)": 0, + "(791, '2024-11-28', 0)": 0, + "(791, '2024-11-28', 1)": 0, + "(791, '2024-11-28', 2)": 1, + "(791, '2024-11-28', 3)": 0, + "(791, '2024-11-28', 4)": 0, + "(791, '2024-11-28', 5)": 0, + "(791, '2024-11-28', 6)": 0, + "(791, '2024-11-28', 7)": 0, + "(791, '2024-11-29', 0)": 0, + "(791, '2024-11-29', 1)": 0, + "(791, '2024-11-29', 2)": 0, + "(791, '2024-11-29', 3)": 0, + "(791, '2024-11-29', 4)": 0, + "(791, '2024-11-29', 5)": 0, + "(791, '2024-11-29', 6)": 0, + "(791, '2024-11-29', 7)": 0, + "(791, '2024-11-30', 0)": 0, + "(791, '2024-11-30', 1)": 0, + "(791, '2024-11-30', 2)": 0, + "(791, '2024-11-30', 3)": 0, + "(791, '2024-11-30', 4)": 0, + "(791, '2024-11-30', 5)": 0, + "(791, '2024-11-30', 6)": 0, + "(791, '2024-11-30', 7)": 0, + "(7919, '2024-11-01', 0)": 0, + "(7919, '2024-11-01', 1)": 0, + "(7919, '2024-11-01', 2)": 0, + "(7919, '2024-11-01', 3)": 0, + "(7919, '2024-11-01', 4)": 0, + "(7919, '2024-11-01', 5)": 0, + "(7919, '2024-11-01', 6)": 0, + "(7919, '2024-11-01', 7)": 0, + "(7919, '2024-11-02', 0)": 1, + "(7919, '2024-11-02', 1)": 0, + "(7919, '2024-11-02', 2)": 0, + "(7919, '2024-11-02', 3)": 0, + "(7919, '2024-11-02', 4)": 0, + "(7919, '2024-11-02', 5)": 0, + "(7919, '2024-11-02', 6)": 0, + "(7919, '2024-11-02', 7)": 0, + "(7919, '2024-11-03', 0)": 0, + "(7919, '2024-11-03', 1)": 0, + "(7919, '2024-11-03', 2)": 1, + "(7919, '2024-11-03', 3)": 0, + "(7919, '2024-11-03', 4)": 0, + "(7919, '2024-11-03', 5)": 0, + "(7919, '2024-11-03', 6)": 0, + "(7919, '2024-11-03', 7)": 0, + "(7919, '2024-11-04', 0)": 0, + "(7919, '2024-11-04', 1)": 1, + "(7919, '2024-11-04', 2)": 0, + "(7919, '2024-11-04', 3)": 0, + "(7919, '2024-11-04', 4)": 0, + "(7919, '2024-11-04', 5)": 0, + "(7919, '2024-11-04', 6)": 0, + "(7919, '2024-11-04', 7)": 0, + "(7919, '2024-11-05', 0)": 1, + "(7919, '2024-11-05', 1)": 0, + "(7919, '2024-11-05', 2)": 0, + "(7919, '2024-11-05', 3)": 0, + "(7919, '2024-11-05', 4)": 0, + "(7919, '2024-11-05', 5)": 0, + "(7919, '2024-11-05', 6)": 0, + "(7919, '2024-11-05', 7)": 0, + "(7919, '2024-11-06', 0)": 0, + "(7919, '2024-11-06', 1)": 0, + "(7919, '2024-11-06', 2)": 0, + "(7919, '2024-11-06', 3)": 0, + "(7919, '2024-11-06', 4)": 0, + "(7919, '2024-11-06', 5)": 0, + "(7919, '2024-11-06', 6)": 0, + "(7919, '2024-11-06', 7)": 0, + "(7919, '2024-11-07', 0)": 0, + "(7919, '2024-11-07', 1)": 0, + "(7919, '2024-11-07', 2)": 1, + "(7919, '2024-11-07', 3)": 0, + "(7919, '2024-11-07', 4)": 0, + "(7919, '2024-11-07', 5)": 0, + "(7919, '2024-11-07', 6)": 0, + "(7919, '2024-11-07', 7)": 0, + "(7919, '2024-11-08', 0)": 0, + "(7919, '2024-11-08', 1)": 0, + "(7919, '2024-11-08', 2)": 1, + "(7919, '2024-11-08', 3)": 0, + "(7919, '2024-11-08', 4)": 0, + "(7919, '2024-11-08', 5)": 0, + "(7919, '2024-11-08', 6)": 0, + "(7919, '2024-11-08', 7)": 0, + "(7919, '2024-11-09', 0)": 0, + "(7919, '2024-11-09', 1)": 0, + "(7919, '2024-11-09', 2)": 0, + "(7919, '2024-11-09', 3)": 0, + "(7919, '2024-11-09', 4)": 0, + "(7919, '2024-11-09', 5)": 0, + "(7919, '2024-11-09', 6)": 0, + "(7919, '2024-11-09', 7)": 0, + "(7919, '2024-11-10', 0)": 0, + "(7919, '2024-11-10', 1)": 0, + "(7919, '2024-11-10', 2)": 1, + "(7919, '2024-11-10', 3)": 0, + "(7919, '2024-11-10', 4)": 0, + "(7919, '2024-11-10', 5)": 0, + "(7919, '2024-11-10', 6)": 0, + "(7919, '2024-11-10', 7)": 0, + "(7919, '2024-11-11', 0)": 0, + "(7919, '2024-11-11', 1)": 0, + "(7919, '2024-11-11', 2)": 1, + "(7919, '2024-11-11', 3)": 0, + "(7919, '2024-11-11', 4)": 0, + "(7919, '2024-11-11', 5)": 0, + "(7919, '2024-11-11', 6)": 0, + "(7919, '2024-11-11', 7)": 0, + "(7919, '2024-11-12', 0)": 0, + "(7919, '2024-11-12', 1)": 0, + "(7919, '2024-11-12', 2)": 0, + "(7919, '2024-11-12', 3)": 0, + "(7919, '2024-11-12', 4)": 0, + "(7919, '2024-11-12', 5)": 0, + "(7919, '2024-11-12', 6)": 0, + "(7919, '2024-11-12', 7)": 0, + "(7919, '2024-11-13', 0)": 0, + "(7919, '2024-11-13', 1)": 0, + "(7919, '2024-11-13', 2)": 0, + "(7919, '2024-11-13', 3)": 0, + "(7919, '2024-11-13', 4)": 0, + "(7919, '2024-11-13', 5)": 0, + "(7919, '2024-11-13', 6)": 0, + "(7919, '2024-11-13', 7)": 0, + "(7919, '2024-11-14', 0)": 0, + "(7919, '2024-11-14', 1)": 0, + "(7919, '2024-11-14', 2)": 0, + "(7919, '2024-11-14', 3)": 0, + "(7919, '2024-11-14', 4)": 0, + "(7919, '2024-11-14', 5)": 0, + "(7919, '2024-11-14', 6)": 0, + "(7919, '2024-11-14', 7)": 0, + "(7919, '2024-11-15', 0)": 1, + "(7919, '2024-11-15', 1)": 0, + "(7919, '2024-11-15', 2)": 0, + "(7919, '2024-11-15', 3)": 0, + "(7919, '2024-11-15', 4)": 0, + "(7919, '2024-11-15', 5)": 0, + "(7919, '2024-11-15', 6)": 0, + "(7919, '2024-11-15', 7)": 0, + "(7919, '2024-11-16', 0)": 0, + "(7919, '2024-11-16', 1)": 0, + "(7919, '2024-11-16', 2)": 1, + "(7919, '2024-11-16', 3)": 0, + "(7919, '2024-11-16', 4)": 0, + "(7919, '2024-11-16', 5)": 0, + "(7919, '2024-11-16', 6)": 0, + "(7919, '2024-11-16', 7)": 0, + "(7919, '2024-11-17', 0)": 0, + "(7919, '2024-11-17', 1)": 0, + "(7919, '2024-11-17', 2)": 1, + "(7919, '2024-11-17', 3)": 0, + "(7919, '2024-11-17', 4)": 0, + "(7919, '2024-11-17', 5)": 0, + "(7919, '2024-11-17', 6)": 0, + "(7919, '2024-11-17', 7)": 0, + "(7919, '2024-11-18', 0)": 0, + "(7919, '2024-11-18', 1)": 0, + "(7919, '2024-11-18', 2)": 0, + "(7919, '2024-11-18', 3)": 1, + "(7919, '2024-11-18', 4)": 0, + "(7919, '2024-11-18', 5)": 0, + "(7919, '2024-11-18', 6)": 0, + "(7919, '2024-11-18', 7)": 0, + "(7919, '2024-11-19', 0)": 0, + "(7919, '2024-11-19', 1)": 0, + "(7919, '2024-11-19', 2)": 0, + "(7919, '2024-11-19', 3)": 0, + "(7919, '2024-11-19', 4)": 0, + "(7919, '2024-11-19', 5)": 0, + "(7919, '2024-11-19', 6)": 0, + "(7919, '2024-11-19', 7)": 0, + "(7919, '2024-11-20', 0)": 1, + "(7919, '2024-11-20', 1)": 0, + "(7919, '2024-11-20', 2)": 0, + "(7919, '2024-11-20', 3)": 0, + "(7919, '2024-11-20', 4)": 0, + "(7919, '2024-11-20', 5)": 0, + "(7919, '2024-11-20', 6)": 0, + "(7919, '2024-11-20', 7)": 0, + "(7919, '2024-11-21', 0)": 0, + "(7919, '2024-11-21', 1)": 0, + "(7919, '2024-11-21', 2)": 0, + "(7919, '2024-11-21', 3)": 1, + "(7919, '2024-11-21', 4)": 0, + "(7919, '2024-11-21', 5)": 0, + "(7919, '2024-11-21', 6)": 0, + "(7919, '2024-11-21', 7)": 0, + "(7919, '2024-11-22', 0)": 0, + "(7919, '2024-11-22', 1)": 0, + "(7919, '2024-11-22', 2)": 0, + "(7919, '2024-11-22', 3)": 0, + "(7919, '2024-11-22', 4)": 0, + "(7919, '2024-11-22', 5)": 0, + "(7919, '2024-11-22', 6)": 0, + "(7919, '2024-11-22', 7)": 0, + "(7919, '2024-11-23', 0)": 0, + "(7919, '2024-11-23', 1)": 0, + "(7919, '2024-11-23', 2)": 0, + "(7919, '2024-11-23', 3)": 1, + "(7919, '2024-11-23', 4)": 0, + "(7919, '2024-11-23', 5)": 0, + "(7919, '2024-11-23', 6)": 0, + "(7919, '2024-11-23', 7)": 0, + "(7919, '2024-11-24', 0)": 0, + "(7919, '2024-11-24', 1)": 0, + "(7919, '2024-11-24', 2)": 0, + "(7919, '2024-11-24', 3)": 0, + "(7919, '2024-11-24', 4)": 0, + "(7919, '2024-11-24', 5)": 0, + "(7919, '2024-11-24', 6)": 0, + "(7919, '2024-11-24', 7)": 0, + "(7919, '2024-11-25', 0)": 0, + "(7919, '2024-11-25', 1)": 0, + "(7919, '2024-11-25', 2)": 1, + "(7919, '2024-11-25', 3)": 0, + "(7919, '2024-11-25', 4)": 0, + "(7919, '2024-11-25', 5)": 0, + "(7919, '2024-11-25', 6)": 0, + "(7919, '2024-11-25', 7)": 0, + "(7919, '2024-11-26', 0)": 0, + "(7919, '2024-11-26', 1)": 0, + "(7919, '2024-11-26', 2)": 1, + "(7919, '2024-11-26', 3)": 0, + "(7919, '2024-11-26', 4)": 0, + "(7919, '2024-11-26', 5)": 0, + "(7919, '2024-11-26', 6)": 0, + "(7919, '2024-11-26', 7)": 0, + "(7919, '2024-11-27', 0)": 0, + "(7919, '2024-11-27', 1)": 0, + "(7919, '2024-11-27', 2)": 0, + "(7919, '2024-11-27', 3)": 1, + "(7919, '2024-11-27', 4)": 0, + "(7919, '2024-11-27', 5)": 0, + "(7919, '2024-11-27', 6)": 0, + "(7919, '2024-11-27', 7)": 0, + "(7919, '2024-11-28', 0)": 0, + "(7919, '2024-11-28', 1)": 0, + "(7919, '2024-11-28', 2)": 0, + "(7919, '2024-11-28', 3)": 0, + "(7919, '2024-11-28', 4)": 0, + "(7919, '2024-11-28', 5)": 0, + "(7919, '2024-11-28', 6)": 0, + "(7919, '2024-11-28', 7)": 0, + "(7919, '2024-11-29', 0)": 0, + "(7919, '2024-11-29', 1)": 0, + "(7919, '2024-11-29', 2)": 1, + "(7919, '2024-11-29', 3)": 0, + "(7919, '2024-11-29', 4)": 0, + "(7919, '2024-11-29', 5)": 0, + "(7919, '2024-11-29', 6)": 0, + "(7919, '2024-11-29', 7)": 0, + "(7919, '2024-11-30', 0)": 0, + "(7919, '2024-11-30', 1)": 0, + "(7919, '2024-11-30', 2)": 0, + "(7919, '2024-11-30', 3)": 0, + "(7919, '2024-11-30', 4)": 0, + "(7919, '2024-11-30', 5)": 0, + "(7919, '2024-11-30', 6)": 0, + "(7919, '2024-11-30', 7)": 0, + "(7990, '2024-11-01', 0)": 0, + "(7990, '2024-11-01', 1)": 0, + "(7990, '2024-11-01', 2)": 0, + "(7990, '2024-11-01', 3)": 0, + "(7990, '2024-11-01', 4)": 0, + "(7990, '2024-11-01', 5)": 0, + "(7990, '2024-11-01', 6)": 0, + "(7990, '2024-11-01', 7)": 0, + "(7990, '2024-11-02', 0)": 0, + "(7990, '2024-11-02', 1)": 0, + "(7990, '2024-11-02', 2)": 0, + "(7990, '2024-11-02', 3)": 0, + "(7990, '2024-11-02', 4)": 0, + "(7990, '2024-11-02', 5)": 0, + "(7990, '2024-11-02', 6)": 0, + "(7990, '2024-11-02', 7)": 0, + "(7990, '2024-11-03', 0)": 0, + "(7990, '2024-11-03', 1)": 0, + "(7990, '2024-11-03', 2)": 0, + "(7990, '2024-11-03', 3)": 0, + "(7990, '2024-11-03', 4)": 0, + "(7990, '2024-11-03', 5)": 0, + "(7990, '2024-11-03', 6)": 0, + "(7990, '2024-11-03', 7)": 0, + "(7990, '2024-11-04', 0)": 0, + "(7990, '2024-11-04', 1)": 0, + "(7990, '2024-11-04', 2)": 0, + "(7990, '2024-11-04', 3)": 0, + "(7990, '2024-11-04', 4)": 0, + "(7990, '2024-11-04', 5)": 0, + "(7990, '2024-11-04', 6)": 0, + "(7990, '2024-11-04', 7)": 0, + "(7990, '2024-11-05', 0)": 0, + "(7990, '2024-11-05', 1)": 0, + "(7990, '2024-11-05', 2)": 0, + "(7990, '2024-11-05', 3)": 0, + "(7990, '2024-11-05', 4)": 0, + "(7990, '2024-11-05', 5)": 0, + "(7990, '2024-11-05', 6)": 0, + "(7990, '2024-11-05', 7)": 0, + "(7990, '2024-11-06', 0)": 0, + "(7990, '2024-11-06', 1)": 0, + "(7990, '2024-11-06', 2)": 0, + "(7990, '2024-11-06', 3)": 0, + "(7990, '2024-11-06', 4)": 0, + "(7990, '2024-11-06', 5)": 0, + "(7990, '2024-11-06', 6)": 0, + "(7990, '2024-11-06', 7)": 0, + "(7990, '2024-11-07', 0)": 0, + "(7990, '2024-11-07', 1)": 0, + "(7990, '2024-11-07', 2)": 0, + "(7990, '2024-11-07', 3)": 0, + "(7990, '2024-11-07', 4)": 0, + "(7990, '2024-11-07', 5)": 0, + "(7990, '2024-11-07', 6)": 0, + "(7990, '2024-11-07', 7)": 0, + "(7990, '2024-11-08', 0)": 0, + "(7990, '2024-11-08', 1)": 0, + "(7990, '2024-11-08', 2)": 0, + "(7990, '2024-11-08', 3)": 0, + "(7990, '2024-11-08', 4)": 0, + "(7990, '2024-11-08', 5)": 0, + "(7990, '2024-11-08', 6)": 0, + "(7990, '2024-11-08', 7)": 0, + "(7990, '2024-11-09', 0)": 0, + "(7990, '2024-11-09', 1)": 0, + "(7990, '2024-11-09', 2)": 0, + "(7990, '2024-11-09', 3)": 0, + "(7990, '2024-11-09', 4)": 0, + "(7990, '2024-11-09', 5)": 0, + "(7990, '2024-11-09', 6)": 0, + "(7990, '2024-11-09', 7)": 0, + "(7990, '2024-11-10', 0)": 0, + "(7990, '2024-11-10', 1)": 0, + "(7990, '2024-11-10', 2)": 0, + "(7990, '2024-11-10', 3)": 0, + "(7990, '2024-11-10', 4)": 0, + "(7990, '2024-11-10', 5)": 0, + "(7990, '2024-11-10', 6)": 0, + "(7990, '2024-11-10', 7)": 0, + "(7990, '2024-11-11', 0)": 0, + "(7990, '2024-11-11', 1)": 0, + "(7990, '2024-11-11', 2)": 0, + "(7990, '2024-11-11', 3)": 0, + "(7990, '2024-11-11', 4)": 0, + "(7990, '2024-11-11', 5)": 0, + "(7990, '2024-11-11', 6)": 0, + "(7990, '2024-11-11', 7)": 0, + "(7990, '2024-11-12', 0)": 0, + "(7990, '2024-11-12', 1)": 0, + "(7990, '2024-11-12', 2)": 0, + "(7990, '2024-11-12', 3)": 0, + "(7990, '2024-11-12', 4)": 0, + "(7990, '2024-11-12', 5)": 0, + "(7990, '2024-11-12', 6)": 0, + "(7990, '2024-11-12', 7)": 0, + "(7990, '2024-11-13', 0)": 0, + "(7990, '2024-11-13', 1)": 0, + "(7990, '2024-11-13', 2)": 0, + "(7990, '2024-11-13', 3)": 0, + "(7990, '2024-11-13', 4)": 0, + "(7990, '2024-11-13', 5)": 0, + "(7990, '2024-11-13', 6)": 0, + "(7990, '2024-11-13', 7)": 0, + "(7990, '2024-11-14', 0)": 0, + "(7990, '2024-11-14', 1)": 0, + "(7990, '2024-11-14', 2)": 0, + "(7990, '2024-11-14', 3)": 0, + "(7990, '2024-11-14', 4)": 0, + "(7990, '2024-11-14', 5)": 0, + "(7990, '2024-11-14', 6)": 0, + "(7990, '2024-11-14', 7)": 0, + "(7990, '2024-11-15', 0)": 0, + "(7990, '2024-11-15', 1)": 0, + "(7990, '2024-11-15', 2)": 0, + "(7990, '2024-11-15', 3)": 0, + "(7990, '2024-11-15', 4)": 0, + "(7990, '2024-11-15', 5)": 0, + "(7990, '2024-11-15', 6)": 0, + "(7990, '2024-11-15', 7)": 0, + "(7990, '2024-11-16', 0)": 0, + "(7990, '2024-11-16', 1)": 0, + "(7990, '2024-11-16', 2)": 0, + "(7990, '2024-11-16', 3)": 0, + "(7990, '2024-11-16', 4)": 0, + "(7990, '2024-11-16', 5)": 0, + "(7990, '2024-11-16', 6)": 0, + "(7990, '2024-11-16', 7)": 0, + "(7990, '2024-11-17', 0)": 0, + "(7990, '2024-11-17', 1)": 0, + "(7990, '2024-11-17', 2)": 0, + "(7990, '2024-11-17', 3)": 0, + "(7990, '2024-11-17', 4)": 0, + "(7990, '2024-11-17', 5)": 0, + "(7990, '2024-11-17', 6)": 0, + "(7990, '2024-11-17', 7)": 0, + "(7990, '2024-11-18', 0)": 0, + "(7990, '2024-11-18', 1)": 0, + "(7990, '2024-11-18', 2)": 0, + "(7990, '2024-11-18', 3)": 0, + "(7990, '2024-11-18', 4)": 0, + "(7990, '2024-11-18', 5)": 0, + "(7990, '2024-11-18', 6)": 0, + "(7990, '2024-11-18', 7)": 0, + "(7990, '2024-11-19', 0)": 0, + "(7990, '2024-11-19', 1)": 0, + "(7990, '2024-11-19', 2)": 0, + "(7990, '2024-11-19', 3)": 0, + "(7990, '2024-11-19', 4)": 0, + "(7990, '2024-11-19', 5)": 0, + "(7990, '2024-11-19', 6)": 0, + "(7990, '2024-11-19', 7)": 0, + "(7990, '2024-11-20', 0)": 0, + "(7990, '2024-11-20', 1)": 0, + "(7990, '2024-11-20', 2)": 0, + "(7990, '2024-11-20', 3)": 0, + "(7990, '2024-11-20', 4)": 0, + "(7990, '2024-11-20', 5)": 0, + "(7990, '2024-11-20', 6)": 0, + "(7990, '2024-11-20', 7)": 0, + "(7990, '2024-11-21', 0)": 0, + "(7990, '2024-11-21', 1)": 0, + "(7990, '2024-11-21', 2)": 0, + "(7990, '2024-11-21', 3)": 0, + "(7990, '2024-11-21', 4)": 0, + "(7990, '2024-11-21', 5)": 0, + "(7990, '2024-11-21', 6)": 0, + "(7990, '2024-11-21', 7)": 0, + "(7990, '2024-11-22', 0)": 0, + "(7990, '2024-11-22', 1)": 0, + "(7990, '2024-11-22', 2)": 0, + "(7990, '2024-11-22', 3)": 0, + "(7990, '2024-11-22', 4)": 0, + "(7990, '2024-11-22', 5)": 0, + "(7990, '2024-11-22', 6)": 0, + "(7990, '2024-11-22', 7)": 0, + "(7990, '2024-11-23', 0)": 0, + "(7990, '2024-11-23', 1)": 0, + "(7990, '2024-11-23', 2)": 0, + "(7990, '2024-11-23', 3)": 0, + "(7990, '2024-11-23', 4)": 0, + "(7990, '2024-11-23', 5)": 0, + "(7990, '2024-11-23', 6)": 0, + "(7990, '2024-11-23', 7)": 0, + "(7990, '2024-11-24', 0)": 0, + "(7990, '2024-11-24', 1)": 0, + "(7990, '2024-11-24', 2)": 0, + "(7990, '2024-11-24', 3)": 0, + "(7990, '2024-11-24', 4)": 0, + "(7990, '2024-11-24', 5)": 0, + "(7990, '2024-11-24', 6)": 0, + "(7990, '2024-11-24', 7)": 0, + "(7990, '2024-11-25', 0)": 0, + "(7990, '2024-11-25', 1)": 0, + "(7990, '2024-11-25', 2)": 0, + "(7990, '2024-11-25', 3)": 0, + "(7990, '2024-11-25', 4)": 0, + "(7990, '2024-11-25', 5)": 0, + "(7990, '2024-11-25', 6)": 0, + "(7990, '2024-11-25', 7)": 0, + "(7990, '2024-11-26', 0)": 0, + "(7990, '2024-11-26', 1)": 0, + "(7990, '2024-11-26', 2)": 0, + "(7990, '2024-11-26', 3)": 0, + "(7990, '2024-11-26', 4)": 0, + "(7990, '2024-11-26', 5)": 0, + "(7990, '2024-11-26', 6)": 0, + "(7990, '2024-11-26', 7)": 0, + "(7990, '2024-11-27', 0)": 0, + "(7990, '2024-11-27', 1)": 0, + "(7990, '2024-11-27', 2)": 0, + "(7990, '2024-11-27', 3)": 0, + "(7990, '2024-11-27', 4)": 0, + "(7990, '2024-11-27', 5)": 0, + "(7990, '2024-11-27', 6)": 0, + "(7990, '2024-11-27', 7)": 0, + "(7990, '2024-11-28', 0)": 0, + "(7990, '2024-11-28', 1)": 0, + "(7990, '2024-11-28', 2)": 0, + "(7990, '2024-11-28', 3)": 0, + "(7990, '2024-11-28', 4)": 0, + "(7990, '2024-11-28', 5)": 0, + "(7990, '2024-11-28', 6)": 0, + "(7990, '2024-11-28', 7)": 0, + "(7990, '2024-11-29', 0)": 0, + "(7990, '2024-11-29', 1)": 0, + "(7990, '2024-11-29', 2)": 0, + "(7990, '2024-11-29', 3)": 0, + "(7990, '2024-11-29', 4)": 0, + "(7990, '2024-11-29', 5)": 0, + "(7990, '2024-11-29', 6)": 0, + "(7990, '2024-11-29', 7)": 0, + "(7990, '2024-11-30', 0)": 0, + "(7990, '2024-11-30', 1)": 0, + "(7990, '2024-11-30', 2)": 0, + "(7990, '2024-11-30', 3)": 0, + "(7990, '2024-11-30', 4)": 0, + "(7990, '2024-11-30', 5)": 0, + "(7990, '2024-11-30', 6)": 0, + "(7990, '2024-11-30', 7)": 0, + "(8, '2024-11-01', 0)": 0, + "(8, '2024-11-01', 1)": 0, + "(8, '2024-11-01', 2)": 0, + "(8, '2024-11-01', 3)": 0, + "(8, '2024-11-01', 4)": 0, + "(8, '2024-11-01', 5)": 0, + "(8, '2024-11-01', 6)": 0, + "(8, '2024-11-01', 7)": 0, + "(8, '2024-11-02', 0)": 0, + "(8, '2024-11-02', 1)": 0, + "(8, '2024-11-02', 2)": 0, + "(8, '2024-11-02', 3)": 0, + "(8, '2024-11-02', 4)": 0, + "(8, '2024-11-02', 5)": 0, + "(8, '2024-11-02', 6)": 0, + "(8, '2024-11-02', 7)": 0, + "(8, '2024-11-03', 0)": 0, + "(8, '2024-11-03', 1)": 0, + "(8, '2024-11-03', 2)": 0, + "(8, '2024-11-03', 3)": 0, + "(8, '2024-11-03', 4)": 0, + "(8, '2024-11-03', 5)": 0, + "(8, '2024-11-03', 6)": 0, + "(8, '2024-11-03', 7)": 0, + "(8, '2024-11-04', 0)": 1, + "(8, '2024-11-04', 1)": 0, + "(8, '2024-11-04', 2)": 0, + "(8, '2024-11-04', 3)": 0, + "(8, '2024-11-04', 4)": 0, + "(8, '2024-11-04', 5)": 0, + "(8, '2024-11-04', 6)": 0, + "(8, '2024-11-04', 7)": 0, + "(8, '2024-11-05', 0)": 1, + "(8, '2024-11-05', 1)": 0, + "(8, '2024-11-05', 2)": 0, + "(8, '2024-11-05', 3)": 0, + "(8, '2024-11-05', 4)": 0, + "(8, '2024-11-05', 5)": 0, + "(8, '2024-11-05', 6)": 0, + "(8, '2024-11-05', 7)": 0, + "(8, '2024-11-06', 0)": 0, + "(8, '2024-11-06', 1)": 0, + "(8, '2024-11-06', 2)": 0, + "(8, '2024-11-06', 3)": 0, + "(8, '2024-11-06', 4)": 0, + "(8, '2024-11-06', 5)": 0, + "(8, '2024-11-06', 6)": 0, + "(8, '2024-11-06', 7)": 0, + "(8, '2024-11-07', 0)": 0, + "(8, '2024-11-07', 1)": 0, + "(8, '2024-11-07', 2)": 0, + "(8, '2024-11-07', 3)": 0, + "(8, '2024-11-07', 4)": 0, + "(8, '2024-11-07', 5)": 0, + "(8, '2024-11-07', 6)": 0, + "(8, '2024-11-07', 7)": 0, + "(8, '2024-11-08', 0)": 0, + "(8, '2024-11-08', 1)": 0, + "(8, '2024-11-08', 2)": 0, + "(8, '2024-11-08', 3)": 0, + "(8, '2024-11-08', 4)": 0, + "(8, '2024-11-08', 5)": 0, + "(8, '2024-11-08', 6)": 0, + "(8, '2024-11-08', 7)": 0, + "(8, '2024-11-09', 0)": 0, + "(8, '2024-11-09', 1)": 0, + "(8, '2024-11-09', 2)": 0, + "(8, '2024-11-09', 3)": 1, + "(8, '2024-11-09', 4)": 0, + "(8, '2024-11-09', 5)": 0, + "(8, '2024-11-09', 6)": 0, + "(8, '2024-11-09', 7)": 0, + "(8, '2024-11-10', 0)": 0, + "(8, '2024-11-10', 1)": 0, + "(8, '2024-11-10', 2)": 0, + "(8, '2024-11-10', 3)": 1, + "(8, '2024-11-10', 4)": 0, + "(8, '2024-11-10', 5)": 0, + "(8, '2024-11-10', 6)": 0, + "(8, '2024-11-10', 7)": 0, + "(8, '2024-11-11', 0)": 0, + "(8, '2024-11-11', 1)": 0, + "(8, '2024-11-11', 2)": 0, + "(8, '2024-11-11', 3)": 0, + "(8, '2024-11-11', 4)": 0, + "(8, '2024-11-11', 5)": 0, + "(8, '2024-11-11', 6)": 0, + "(8, '2024-11-11', 7)": 0, + "(8, '2024-11-12', 0)": 0, + "(8, '2024-11-12', 1)": 0, + "(8, '2024-11-12', 2)": 0, + "(8, '2024-11-12', 3)": 0, + "(8, '2024-11-12', 4)": 0, + "(8, '2024-11-12', 5)": 0, + "(8, '2024-11-12', 6)": 0, + "(8, '2024-11-12', 7)": 0, + "(8, '2024-11-13', 0)": 0, + "(8, '2024-11-13', 1)": 0, + "(8, '2024-11-13', 2)": 0, + "(8, '2024-11-13', 3)": 0, + "(8, '2024-11-13', 4)": 0, + "(8, '2024-11-13', 5)": 0, + "(8, '2024-11-13', 6)": 0, + "(8, '2024-11-13', 7)": 0, + "(8, '2024-11-14', 0)": 0, + "(8, '2024-11-14', 1)": 0, + "(8, '2024-11-14', 2)": 1, + "(8, '2024-11-14', 3)": 0, + "(8, '2024-11-14', 4)": 0, + "(8, '2024-11-14', 5)": 0, + "(8, '2024-11-14', 6)": 0, + "(8, '2024-11-14', 7)": 0, + "(8, '2024-11-15', 0)": 0, + "(8, '2024-11-15', 1)": 0, + "(8, '2024-11-15', 2)": 0, + "(8, '2024-11-15', 3)": 0, + "(8, '2024-11-15', 4)": 0, + "(8, '2024-11-15', 5)": 0, + "(8, '2024-11-15', 6)": 0, + "(8, '2024-11-15', 7)": 0, + "(8, '2024-11-16', 0)": 0, + "(8, '2024-11-16', 1)": 0, + "(8, '2024-11-16', 2)": 1, + "(8, '2024-11-16', 3)": 0, + "(8, '2024-11-16', 4)": 0, + "(8, '2024-11-16', 5)": 0, + "(8, '2024-11-16', 6)": 0, + "(8, '2024-11-16', 7)": 0, + "(8, '2024-11-17', 0)": 0, + "(8, '2024-11-17', 1)": 0, + "(8, '2024-11-17', 2)": 0, + "(8, '2024-11-17', 3)": 0, + "(8, '2024-11-17', 4)": 0, + "(8, '2024-11-17', 5)": 0, + "(8, '2024-11-17', 6)": 0, + "(8, '2024-11-17', 7)": 0, + "(8, '2024-11-18', 0)": 0, + "(8, '2024-11-18', 1)": 0, + "(8, '2024-11-18', 2)": 0, + "(8, '2024-11-18', 3)": 0, + "(8, '2024-11-18', 4)": 0, + "(8, '2024-11-18', 5)": 0, + "(8, '2024-11-18', 6)": 0, + "(8, '2024-11-18', 7)": 0, + "(8, '2024-11-19', 0)": 0, + "(8, '2024-11-19', 1)": 0, + "(8, '2024-11-19', 2)": 0, + "(8, '2024-11-19', 3)": 0, + "(8, '2024-11-19', 4)": 0, + "(8, '2024-11-19', 5)": 0, + "(8, '2024-11-19', 6)": 0, + "(8, '2024-11-19', 7)": 0, + "(8, '2024-11-20', 0)": 1, + "(8, '2024-11-20', 1)": 0, + "(8, '2024-11-20', 2)": 0, + "(8, '2024-11-20', 3)": 0, + "(8, '2024-11-20', 4)": 0, + "(8, '2024-11-20', 5)": 0, + "(8, '2024-11-20', 6)": 0, + "(8, '2024-11-20', 7)": 0, + "(8, '2024-11-21', 0)": 0, + "(8, '2024-11-21', 1)": 0, + "(8, '2024-11-21', 2)": 1, + "(8, '2024-11-21', 3)": 0, + "(8, '2024-11-21', 4)": 0, + "(8, '2024-11-21', 5)": 0, + "(8, '2024-11-21', 6)": 0, + "(8, '2024-11-21', 7)": 0, + "(8, '2024-11-22', 0)": 0, + "(8, '2024-11-22', 1)": 0, + "(8, '2024-11-22', 2)": 0, + "(8, '2024-11-22', 3)": 0, + "(8, '2024-11-22', 4)": 0, + "(8, '2024-11-22', 5)": 0, + "(8, '2024-11-22', 6)": 0, + "(8, '2024-11-22', 7)": 0, + "(8, '2024-11-23', 0)": 0, + "(8, '2024-11-23', 1)": 0, + "(8, '2024-11-23', 2)": 1, + "(8, '2024-11-23', 3)": 0, + "(8, '2024-11-23', 4)": 0, + "(8, '2024-11-23', 5)": 0, + "(8, '2024-11-23', 6)": 0, + "(8, '2024-11-23', 7)": 0, + "(8, '2024-11-24', 0)": 0, + "(8, '2024-11-24', 1)": 0, + "(8, '2024-11-24', 2)": 0, + "(8, '2024-11-24', 3)": 0, + "(8, '2024-11-24', 4)": 0, + "(8, '2024-11-24', 5)": 0, + "(8, '2024-11-24', 6)": 0, + "(8, '2024-11-24', 7)": 0, + "(8, '2024-11-25', 0)": 0, + "(8, '2024-11-25', 1)": 0, + "(8, '2024-11-25', 2)": 0, + "(8, '2024-11-25', 3)": 0, + "(8, '2024-11-25', 4)": 0, + "(8, '2024-11-25', 5)": 0, + "(8, '2024-11-25', 6)": 0, + "(8, '2024-11-25', 7)": 0, + "(8, '2024-11-26', 0)": 0, + "(8, '2024-11-26', 1)": 0, + "(8, '2024-11-26', 2)": 0, + "(8, '2024-11-26', 3)": 0, + "(8, '2024-11-26', 4)": 0, + "(8, '2024-11-26', 5)": 0, + "(8, '2024-11-26', 6)": 0, + "(8, '2024-11-26', 7)": 0, + "(8, '2024-11-27', 0)": 1, + "(8, '2024-11-27', 1)": 0, + "(8, '2024-11-27', 2)": 0, + "(8, '2024-11-27', 3)": 0, + "(8, '2024-11-27', 4)": 0, + "(8, '2024-11-27', 5)": 0, + "(8, '2024-11-27', 6)": 0, + "(8, '2024-11-27', 7)": 0, + "(8, '2024-11-28', 0)": 0, + "(8, '2024-11-28', 1)": 0, + "(8, '2024-11-28', 2)": 0, + "(8, '2024-11-28', 3)": 0, + "(8, '2024-11-28', 4)": 0, + "(8, '2024-11-28', 5)": 0, + "(8, '2024-11-28', 6)": 0, + "(8, '2024-11-28', 7)": 0, + "(8, '2024-11-29', 0)": 0, + "(8, '2024-11-29', 1)": 0, + "(8, '2024-11-29', 2)": 0, + "(8, '2024-11-29', 3)": 0, + "(8, '2024-11-29', 4)": 0, + "(8, '2024-11-29', 5)": 0, + "(8, '2024-11-29', 6)": 0, + "(8, '2024-11-29', 7)": 0, + "(8, '2024-11-30', 0)": 1, + "(8, '2024-11-30', 1)": 0, + "(8, '2024-11-30', 2)": 0, + "(8, '2024-11-30', 3)": 0, + "(8, '2024-11-30', 4)": 0, + "(8, '2024-11-30', 5)": 0, + "(8, '2024-11-30', 6)": 0, + "(8, '2024-11-30', 7)": 0, + "(822, '2024-11-01', 0)": 0, + "(822, '2024-11-01', 1)": 0, + "(822, '2024-11-01', 2)": 0, + "(822, '2024-11-01', 3)": 0, + "(822, '2024-11-01', 4)": 0, + "(822, '2024-11-01', 5)": 0, + "(822, '2024-11-01', 6)": 0, + "(822, '2024-11-01', 7)": 1, + "(822, '2024-11-02', 0)": 0, + "(822, '2024-11-02', 1)": 0, + "(822, '2024-11-02', 2)": 0, + "(822, '2024-11-02', 3)": 0, + "(822, '2024-11-02', 4)": 0, + "(822, '2024-11-02', 5)": 0, + "(822, '2024-11-02', 6)": 0, + "(822, '2024-11-02', 7)": 1, + "(822, '2024-11-03', 0)": 0, + "(822, '2024-11-03', 1)": 0, + "(822, '2024-11-03', 2)": 0, + "(822, '2024-11-03', 3)": 0, + "(822, '2024-11-03', 4)": 0, + "(822, '2024-11-03', 5)": 0, + "(822, '2024-11-03', 6)": 0, + "(822, '2024-11-03', 7)": 1, + "(822, '2024-11-04', 0)": 0, + "(822, '2024-11-04', 1)": 0, + "(822, '2024-11-04', 2)": 0, + "(822, '2024-11-04', 3)": 0, + "(822, '2024-11-04', 4)": 0, + "(822, '2024-11-04', 5)": 0, + "(822, '2024-11-04', 6)": 0, + "(822, '2024-11-04', 7)": 0, + "(822, '2024-11-05', 0)": 0, + "(822, '2024-11-05', 1)": 0, + "(822, '2024-11-05', 2)": 0, + "(822, '2024-11-05', 3)": 0, + "(822, '2024-11-05', 4)": 0, + "(822, '2024-11-05', 5)": 0, + "(822, '2024-11-05', 6)": 0, + "(822, '2024-11-05', 7)": 0, + "(822, '2024-11-06', 0)": 0, + "(822, '2024-11-06', 1)": 0, + "(822, '2024-11-06', 2)": 0, + "(822, '2024-11-06', 3)": 0, + "(822, '2024-11-06', 4)": 0, + "(822, '2024-11-06', 5)": 0, + "(822, '2024-11-06', 6)": 1, + "(822, '2024-11-06', 7)": 0, + "(822, '2024-11-07', 0)": 0, + "(822, '2024-11-07', 1)": 0, + "(822, '2024-11-07', 2)": 0, + "(822, '2024-11-07', 3)": 0, + "(822, '2024-11-07', 4)": 0, + "(822, '2024-11-07', 5)": 0, + "(822, '2024-11-07', 6)": 1, + "(822, '2024-11-07', 7)": 0, + "(822, '2024-11-08', 0)": 0, + "(822, '2024-11-08', 1)": 0, + "(822, '2024-11-08', 2)": 0, + "(822, '2024-11-08', 3)": 0, + "(822, '2024-11-08', 4)": 0, + "(822, '2024-11-08', 5)": 0, + "(822, '2024-11-08', 6)": 1, + "(822, '2024-11-08', 7)": 0, + "(822, '2024-11-09', 0)": 0, + "(822, '2024-11-09', 1)": 0, + "(822, '2024-11-09', 2)": 0, + "(822, '2024-11-09', 3)": 0, + "(822, '2024-11-09', 4)": 0, + "(822, '2024-11-09', 5)": 0, + "(822, '2024-11-09', 6)": 0, + "(822, '2024-11-09', 7)": 0, + "(822, '2024-11-10', 0)": 0, + "(822, '2024-11-10', 1)": 0, + "(822, '2024-11-10', 2)": 0, + "(822, '2024-11-10', 3)": 0, + "(822, '2024-11-10', 4)": 0, + "(822, '2024-11-10', 5)": 0, + "(822, '2024-11-10', 6)": 0, + "(822, '2024-11-10', 7)": 0, + "(822, '2024-11-11', 0)": 0, + "(822, '2024-11-11', 1)": 0, + "(822, '2024-11-11', 2)": 0, + "(822, '2024-11-11', 3)": 0, + "(822, '2024-11-11', 4)": 0, + "(822, '2024-11-11', 5)": 1, + "(822, '2024-11-11', 6)": 0, + "(822, '2024-11-11', 7)": 0, + "(822, '2024-11-12', 0)": 0, + "(822, '2024-11-12', 1)": 0, + "(822, '2024-11-12', 2)": 0, + "(822, '2024-11-12', 3)": 0, + "(822, '2024-11-12', 4)": 0, + "(822, '2024-11-12', 5)": 1, + "(822, '2024-11-12', 6)": 0, + "(822, '2024-11-12', 7)": 0, + "(822, '2024-11-13', 0)": 0, + "(822, '2024-11-13', 1)": 0, + "(822, '2024-11-13', 2)": 0, + "(822, '2024-11-13', 3)": 0, + "(822, '2024-11-13', 4)": 0, + "(822, '2024-11-13', 5)": 1, + "(822, '2024-11-13', 6)": 0, + "(822, '2024-11-13', 7)": 0, + "(822, '2024-11-14', 0)": 0, + "(822, '2024-11-14', 1)": 0, + "(822, '2024-11-14', 2)": 0, + "(822, '2024-11-14', 3)": 0, + "(822, '2024-11-14', 4)": 0, + "(822, '2024-11-14', 5)": 1, + "(822, '2024-11-14', 6)": 0, + "(822, '2024-11-14', 7)": 0, + "(822, '2024-11-15', 0)": 0, + "(822, '2024-11-15', 1)": 0, + "(822, '2024-11-15', 2)": 0, + "(822, '2024-11-15', 3)": 0, + "(822, '2024-11-15', 4)": 0, + "(822, '2024-11-15', 5)": 0, + "(822, '2024-11-15', 6)": 0, + "(822, '2024-11-15', 7)": 0, + "(822, '2024-11-16', 0)": 0, + "(822, '2024-11-16', 1)": 0, + "(822, '2024-11-16', 2)": 0, + "(822, '2024-11-16', 3)": 0, + "(822, '2024-11-16', 4)": 0, + "(822, '2024-11-16', 5)": 0, + "(822, '2024-11-16', 6)": 0, + "(822, '2024-11-16', 7)": 0, + "(822, '2024-11-17', 0)": 0, + "(822, '2024-11-17', 1)": 0, + "(822, '2024-11-17', 2)": 0, + "(822, '2024-11-17', 3)": 0, + "(822, '2024-11-17', 4)": 0, + "(822, '2024-11-17', 5)": 0, + "(822, '2024-11-17', 6)": 0, + "(822, '2024-11-17', 7)": 0, + "(822, '2024-11-18', 0)": 0, + "(822, '2024-11-18', 1)": 0, + "(822, '2024-11-18', 2)": 0, + "(822, '2024-11-18', 3)": 0, + "(822, '2024-11-18', 4)": 0, + "(822, '2024-11-18', 5)": 0, + "(822, '2024-11-18', 6)": 0, + "(822, '2024-11-18', 7)": 0, + "(822, '2024-11-19', 0)": 0, + "(822, '2024-11-19', 1)": 0, + "(822, '2024-11-19', 2)": 0, + "(822, '2024-11-19', 3)": 0, + "(822, '2024-11-19', 4)": 0, + "(822, '2024-11-19', 5)": 0, + "(822, '2024-11-19', 6)": 0, + "(822, '2024-11-19', 7)": 0, + "(822, '2024-11-20', 0)": 0, + "(822, '2024-11-20', 1)": 0, + "(822, '2024-11-20', 2)": 0, + "(822, '2024-11-20', 3)": 0, + "(822, '2024-11-20', 4)": 0, + "(822, '2024-11-20', 5)": 0, + "(822, '2024-11-20', 6)": 1, + "(822, '2024-11-20', 7)": 0, + "(822, '2024-11-21', 0)": 0, + "(822, '2024-11-21', 1)": 0, + "(822, '2024-11-21', 2)": 0, + "(822, '2024-11-21', 3)": 0, + "(822, '2024-11-21', 4)": 0, + "(822, '2024-11-21', 5)": 0, + "(822, '2024-11-21', 6)": 0, + "(822, '2024-11-21', 7)": 1, + "(822, '2024-11-22', 0)": 0, + "(822, '2024-11-22', 1)": 0, + "(822, '2024-11-22', 2)": 0, + "(822, '2024-11-22', 3)": 0, + "(822, '2024-11-22', 4)": 0, + "(822, '2024-11-22', 5)": 0, + "(822, '2024-11-22', 6)": 0, + "(822, '2024-11-22', 7)": 1, + "(822, '2024-11-23', 0)": 0, + "(822, '2024-11-23', 1)": 0, + "(822, '2024-11-23', 2)": 0, + "(822, '2024-11-23', 3)": 0, + "(822, '2024-11-23', 4)": 0, + "(822, '2024-11-23', 5)": 0, + "(822, '2024-11-23', 6)": 0, + "(822, '2024-11-23', 7)": 1, + "(822, '2024-11-24', 0)": 0, + "(822, '2024-11-24', 1)": 0, + "(822, '2024-11-24', 2)": 0, + "(822, '2024-11-24', 3)": 0, + "(822, '2024-11-24', 4)": 0, + "(822, '2024-11-24', 5)": 0, + "(822, '2024-11-24', 6)": 0, + "(822, '2024-11-24', 7)": 1, + "(822, '2024-11-25', 0)": 0, + "(822, '2024-11-25', 1)": 0, + "(822, '2024-11-25', 2)": 0, + "(822, '2024-11-25', 3)": 0, + "(822, '2024-11-25', 4)": 0, + "(822, '2024-11-25', 5)": 0, + "(822, '2024-11-25', 6)": 0, + "(822, '2024-11-25', 7)": 0, + "(822, '2024-11-26', 0)": 0, + "(822, '2024-11-26', 1)": 0, + "(822, '2024-11-26', 2)": 0, + "(822, '2024-11-26', 3)": 0, + "(822, '2024-11-26', 4)": 0, + "(822, '2024-11-26', 5)": 0, + "(822, '2024-11-26', 6)": 0, + "(822, '2024-11-26', 7)": 0, + "(822, '2024-11-27', 0)": 0, + "(822, '2024-11-27', 1)": 0, + "(822, '2024-11-27', 2)": 0, + "(822, '2024-11-27', 3)": 0, + "(822, '2024-11-27', 4)": 0, + "(822, '2024-11-27', 5)": 0, + "(822, '2024-11-27', 6)": 0, + "(822, '2024-11-27', 7)": 0, + "(822, '2024-11-28', 0)": 0, + "(822, '2024-11-28', 1)": 0, + "(822, '2024-11-28', 2)": 1, + "(822, '2024-11-28', 3)": 0, + "(822, '2024-11-28', 4)": 0, + "(822, '2024-11-28', 5)": 0, + "(822, '2024-11-28', 6)": 0, + "(822, '2024-11-28', 7)": 0, + "(822, '2024-11-29', 0)": 0, + "(822, '2024-11-29', 1)": 0, + "(822, '2024-11-29', 2)": 0, + "(822, '2024-11-29', 3)": 0, + "(822, '2024-11-29', 4)": 0, + "(822, '2024-11-29', 5)": 0, + "(822, '2024-11-29', 6)": 0, + "(822, '2024-11-29', 7)": 1, + "(822, '2024-11-30', 0)": 0, + "(822, '2024-11-30', 1)": 0, + "(822, '2024-11-30', 2)": 0, + "(822, '2024-11-30', 3)": 0, + "(822, '2024-11-30', 4)": 0, + "(822, '2024-11-30', 5)": 0, + "(822, '2024-11-30', 6)": 0, + "(822, '2024-11-30', 7)": 1, + "(839, '2024-11-01', 0)": 0, + "(839, '2024-11-01', 1)": 0, + "(839, '2024-11-01', 2)": 0, + "(839, '2024-11-01', 3)": 0, + "(839, '2024-11-01', 4)": 0, + "(839, '2024-11-01', 5)": 0, + "(839, '2024-11-01', 6)": 0, + "(839, '2024-11-01', 7)": 0, + "(839, '2024-11-02', 0)": 0, + "(839, '2024-11-02', 1)": 0, + "(839, '2024-11-02', 2)": 0, + "(839, '2024-11-02', 3)": 0, + "(839, '2024-11-02', 4)": 0, + "(839, '2024-11-02', 5)": 0, + "(839, '2024-11-02', 6)": 0, + "(839, '2024-11-02', 7)": 0, + "(839, '2024-11-03', 0)": 0, + "(839, '2024-11-03', 1)": 0, + "(839, '2024-11-03', 2)": 0, + "(839, '2024-11-03', 3)": 0, + "(839, '2024-11-03', 4)": 0, + "(839, '2024-11-03', 5)": 0, + "(839, '2024-11-03', 6)": 0, + "(839, '2024-11-03', 7)": 0, + "(839, '2024-11-04', 0)": 0, + "(839, '2024-11-04', 1)": 0, + "(839, '2024-11-04', 2)": 0, + "(839, '2024-11-04', 3)": 0, + "(839, '2024-11-04', 4)": 0, + "(839, '2024-11-04', 5)": 0, + "(839, '2024-11-04', 6)": 0, + "(839, '2024-11-04', 7)": 0, + "(839, '2024-11-05', 0)": 0, + "(839, '2024-11-05', 1)": 0, + "(839, '2024-11-05', 2)": 1, + "(839, '2024-11-05', 3)": 0, + "(839, '2024-11-05', 4)": 0, + "(839, '2024-11-05', 5)": 0, + "(839, '2024-11-05', 6)": 0, + "(839, '2024-11-05', 7)": 0, + "(839, '2024-11-06', 0)": 0, + "(839, '2024-11-06', 1)": 0, + "(839, '2024-11-06', 2)": 0, + "(839, '2024-11-06', 3)": 0, + "(839, '2024-11-06', 4)": 0, + "(839, '2024-11-06', 5)": 0, + "(839, '2024-11-06', 6)": 0, + "(839, '2024-11-06', 7)": 0, + "(839, '2024-11-07', 0)": 0, + "(839, '2024-11-07', 1)": 0, + "(839, '2024-11-07', 2)": 0, + "(839, '2024-11-07', 3)": 0, + "(839, '2024-11-07', 4)": 0, + "(839, '2024-11-07', 5)": 0, + "(839, '2024-11-07', 6)": 0, + "(839, '2024-11-07', 7)": 0, + "(839, '2024-11-08', 0)": 0, + "(839, '2024-11-08', 1)": 0, + "(839, '2024-11-08', 2)": 0, + "(839, '2024-11-08', 3)": 0, + "(839, '2024-11-08', 4)": 0, + "(839, '2024-11-08', 5)": 0, + "(839, '2024-11-08', 6)": 0, + "(839, '2024-11-08', 7)": 0, + "(839, '2024-11-09', 0)": 0, + "(839, '2024-11-09', 1)": 0, + "(839, '2024-11-09', 2)": 0, + "(839, '2024-11-09', 3)": 0, + "(839, '2024-11-09', 4)": 0, + "(839, '2024-11-09', 5)": 0, + "(839, '2024-11-09', 6)": 0, + "(839, '2024-11-09', 7)": 0, + "(839, '2024-11-10', 0)": 0, + "(839, '2024-11-10', 1)": 0, + "(839, '2024-11-10', 2)": 0, + "(839, '2024-11-10', 3)": 0, + "(839, '2024-11-10', 4)": 0, + "(839, '2024-11-10', 5)": 0, + "(839, '2024-11-10', 6)": 0, + "(839, '2024-11-10', 7)": 1, + "(839, '2024-11-11', 0)": 0, + "(839, '2024-11-11', 1)": 0, + "(839, '2024-11-11', 2)": 0, + "(839, '2024-11-11', 3)": 0, + "(839, '2024-11-11', 4)": 0, + "(839, '2024-11-11', 5)": 0, + "(839, '2024-11-11', 6)": 0, + "(839, '2024-11-11', 7)": 1, + "(839, '2024-11-12', 0)": 0, + "(839, '2024-11-12', 1)": 0, + "(839, '2024-11-12', 2)": 0, + "(839, '2024-11-12', 3)": 0, + "(839, '2024-11-12', 4)": 0, + "(839, '2024-11-12', 5)": 0, + "(839, '2024-11-12', 6)": 0, + "(839, '2024-11-12', 7)": 0, + "(839, '2024-11-13', 0)": 0, + "(839, '2024-11-13', 1)": 0, + "(839, '2024-11-13', 2)": 0, + "(839, '2024-11-13', 3)": 0, + "(839, '2024-11-13', 4)": 0, + "(839, '2024-11-13', 5)": 0, + "(839, '2024-11-13', 6)": 0, + "(839, '2024-11-13', 7)": 0, + "(839, '2024-11-14', 0)": 0, + "(839, '2024-11-14', 1)": 0, + "(839, '2024-11-14', 2)": 0, + "(839, '2024-11-14', 3)": 0, + "(839, '2024-11-14', 4)": 0, + "(839, '2024-11-14', 5)": 0, + "(839, '2024-11-14', 6)": 0, + "(839, '2024-11-14', 7)": 0, + "(839, '2024-11-15', 0)": 0, + "(839, '2024-11-15', 1)": 0, + "(839, '2024-11-15', 2)": 0, + "(839, '2024-11-15', 3)": 0, + "(839, '2024-11-15', 4)": 0, + "(839, '2024-11-15', 5)": 0, + "(839, '2024-11-15', 6)": 0, + "(839, '2024-11-15', 7)": 1, + "(839, '2024-11-16', 0)": 0, + "(839, '2024-11-16', 1)": 0, + "(839, '2024-11-16', 2)": 0, + "(839, '2024-11-16', 3)": 0, + "(839, '2024-11-16', 4)": 0, + "(839, '2024-11-16', 5)": 0, + "(839, '2024-11-16', 6)": 0, + "(839, '2024-11-16', 7)": 0, + "(839, '2024-11-17', 0)": 0, + "(839, '2024-11-17', 1)": 0, + "(839, '2024-11-17', 2)": 0, + "(839, '2024-11-17', 3)": 0, + "(839, '2024-11-17', 4)": 0, + "(839, '2024-11-17', 5)": 0, + "(839, '2024-11-17', 6)": 0, + "(839, '2024-11-17', 7)": 0, + "(839, '2024-11-18', 0)": 0, + "(839, '2024-11-18', 1)": 0, + "(839, '2024-11-18', 2)": 0, + "(839, '2024-11-18', 3)": 0, + "(839, '2024-11-18', 4)": 0, + "(839, '2024-11-18', 5)": 0, + "(839, '2024-11-18', 6)": 0, + "(839, '2024-11-18', 7)": 0, + "(839, '2024-11-19', 0)": 0, + "(839, '2024-11-19', 1)": 0, + "(839, '2024-11-19', 2)": 0, + "(839, '2024-11-19', 3)": 0, + "(839, '2024-11-19', 4)": 0, + "(839, '2024-11-19', 5)": 0, + "(839, '2024-11-19', 6)": 0, + "(839, '2024-11-19', 7)": 1, + "(839, '2024-11-20', 0)": 0, + "(839, '2024-11-20', 1)": 0, + "(839, '2024-11-20', 2)": 0, + "(839, '2024-11-20', 3)": 0, + "(839, '2024-11-20', 4)": 0, + "(839, '2024-11-20', 5)": 0, + "(839, '2024-11-20', 6)": 0, + "(839, '2024-11-20', 7)": 1, + "(839, '2024-11-21', 0)": 0, + "(839, '2024-11-21', 1)": 0, + "(839, '2024-11-21', 2)": 0, + "(839, '2024-11-21', 3)": 0, + "(839, '2024-11-21', 4)": 0, + "(839, '2024-11-21', 5)": 0, + "(839, '2024-11-21', 6)": 0, + "(839, '2024-11-21', 7)": 0, + "(839, '2024-11-22', 0)": 0, + "(839, '2024-11-22', 1)": 0, + "(839, '2024-11-22', 2)": 0, + "(839, '2024-11-22', 3)": 0, + "(839, '2024-11-22', 4)": 0, + "(839, '2024-11-22', 5)": 0, + "(839, '2024-11-22', 6)": 0, + "(839, '2024-11-22', 7)": 0, + "(839, '2024-11-23', 0)": 0, + "(839, '2024-11-23', 1)": 0, + "(839, '2024-11-23', 2)": 0, + "(839, '2024-11-23', 3)": 0, + "(839, '2024-11-23', 4)": 0, + "(839, '2024-11-23', 5)": 0, + "(839, '2024-11-23', 6)": 0, + "(839, '2024-11-23', 7)": 0, + "(839, '2024-11-24', 0)": 0, + "(839, '2024-11-24', 1)": 0, + "(839, '2024-11-24', 2)": 0, + "(839, '2024-11-24', 3)": 0, + "(839, '2024-11-24', 4)": 0, + "(839, '2024-11-24', 5)": 0, + "(839, '2024-11-24', 6)": 0, + "(839, '2024-11-24', 7)": 0, + "(839, '2024-11-25', 0)": 0, + "(839, '2024-11-25', 1)": 0, + "(839, '2024-11-25', 2)": 0, + "(839, '2024-11-25', 3)": 0, + "(839, '2024-11-25', 4)": 0, + "(839, '2024-11-25', 5)": 0, + "(839, '2024-11-25', 6)": 0, + "(839, '2024-11-25', 7)": 0, + "(839, '2024-11-26', 0)": 0, + "(839, '2024-11-26', 1)": 0, + "(839, '2024-11-26', 2)": 0, + "(839, '2024-11-26', 3)": 0, + "(839, '2024-11-26', 4)": 0, + "(839, '2024-11-26', 5)": 0, + "(839, '2024-11-26', 6)": 0, + "(839, '2024-11-26', 7)": 1, + "(839, '2024-11-27', 0)": 0, + "(839, '2024-11-27', 1)": 0, + "(839, '2024-11-27', 2)": 0, + "(839, '2024-11-27', 3)": 0, + "(839, '2024-11-27', 4)": 0, + "(839, '2024-11-27', 5)": 0, + "(839, '2024-11-27', 6)": 0, + "(839, '2024-11-27', 7)": 1, + "(839, '2024-11-28', 0)": 0, + "(839, '2024-11-28', 1)": 0, + "(839, '2024-11-28', 2)": 0, + "(839, '2024-11-28', 3)": 0, + "(839, '2024-11-28', 4)": 0, + "(839, '2024-11-28', 5)": 0, + "(839, '2024-11-28', 6)": 0, + "(839, '2024-11-28', 7)": 0, + "(839, '2024-11-29', 0)": 0, + "(839, '2024-11-29', 1)": 0, + "(839, '2024-11-29', 2)": 0, + "(839, '2024-11-29', 3)": 0, + "(839, '2024-11-29', 4)": 0, + "(839, '2024-11-29', 5)": 0, + "(839, '2024-11-29', 6)": 0, + "(839, '2024-11-29', 7)": 0, + "(839, '2024-11-30', 0)": 0, + "(839, '2024-11-30', 1)": 0, + "(839, '2024-11-30', 2)": 0, + "(839, '2024-11-30', 3)": 0, + "(839, '2024-11-30', 4)": 0, + "(839, '2024-11-30', 5)": 0, + "(839, '2024-11-30', 6)": 0, + "(839, '2024-11-30', 7)": 0, + "(914, '2024-11-01', 0)": 0, + "(914, '2024-11-01', 1)": 0, + "(914, '2024-11-01', 2)": 0, + "(914, '2024-11-01', 3)": 0, + "(914, '2024-11-01', 4)": 0, + "(914, '2024-11-01', 5)": 0, + "(914, '2024-11-01', 6)": 0, + "(914, '2024-11-01', 7)": 0, + "(914, '2024-11-02', 0)": 1, + "(914, '2024-11-02', 1)": 0, + "(914, '2024-11-02', 2)": 0, + "(914, '2024-11-02', 3)": 0, + "(914, '2024-11-02', 4)": 0, + "(914, '2024-11-02', 5)": 0, + "(914, '2024-11-02', 6)": 0, + "(914, '2024-11-02', 7)": 0, + "(914, '2024-11-03', 0)": 0, + "(914, '2024-11-03', 1)": 0, + "(914, '2024-11-03', 2)": 0, + "(914, '2024-11-03', 3)": 0, + "(914, '2024-11-03', 4)": 0, + "(914, '2024-11-03', 5)": 0, + "(914, '2024-11-03', 6)": 0, + "(914, '2024-11-03', 7)": 0, + "(914, '2024-11-04', 0)": 0, + "(914, '2024-11-04', 1)": 0, + "(914, '2024-11-04', 2)": 0, + "(914, '2024-11-04', 3)": 0, + "(914, '2024-11-04', 4)": 0, + "(914, '2024-11-04', 5)": 0, + "(914, '2024-11-04', 6)": 0, + "(914, '2024-11-04', 7)": 0, + "(914, '2024-11-05', 0)": 0, + "(914, '2024-11-05', 1)": 0, + "(914, '2024-11-05', 2)": 1, + "(914, '2024-11-05', 3)": 0, + "(914, '2024-11-05', 4)": 0, + "(914, '2024-11-05', 5)": 0, + "(914, '2024-11-05', 6)": 0, + "(914, '2024-11-05', 7)": 0, + "(914, '2024-11-06', 0)": 0, + "(914, '2024-11-06', 1)": 0, + "(914, '2024-11-06', 2)": 1, + "(914, '2024-11-06', 3)": 0, + "(914, '2024-11-06', 4)": 0, + "(914, '2024-11-06', 5)": 0, + "(914, '2024-11-06', 6)": 0, + "(914, '2024-11-06', 7)": 0, + "(914, '2024-11-07', 0)": 0, + "(914, '2024-11-07', 1)": 0, + "(914, '2024-11-07', 2)": 1, + "(914, '2024-11-07', 3)": 0, + "(914, '2024-11-07', 4)": 0, + "(914, '2024-11-07', 5)": 0, + "(914, '2024-11-07', 6)": 0, + "(914, '2024-11-07', 7)": 0, + "(914, '2024-11-08', 0)": 0, + "(914, '2024-11-08', 1)": 0, + "(914, '2024-11-08', 2)": 1, + "(914, '2024-11-08', 3)": 0, + "(914, '2024-11-08', 4)": 0, + "(914, '2024-11-08', 5)": 0, + "(914, '2024-11-08', 6)": 0, + "(914, '2024-11-08', 7)": 0, + "(914, '2024-11-09', 0)": 0, + "(914, '2024-11-09', 1)": 0, + "(914, '2024-11-09', 2)": 0, + "(914, '2024-11-09', 3)": 0, + "(914, '2024-11-09', 4)": 0, + "(914, '2024-11-09', 5)": 0, + "(914, '2024-11-09', 6)": 0, + "(914, '2024-11-09', 7)": 0, + "(914, '2024-11-10', 0)": 0, + "(914, '2024-11-10', 1)": 0, + "(914, '2024-11-10', 2)": 0, + "(914, '2024-11-10', 3)": 0, + "(914, '2024-11-10', 4)": 0, + "(914, '2024-11-10', 5)": 0, + "(914, '2024-11-10', 6)": 0, + "(914, '2024-11-10', 7)": 0, + "(914, '2024-11-11', 0)": 0, + "(914, '2024-11-11', 1)": 0, + "(914, '2024-11-11', 2)": 0, + "(914, '2024-11-11', 3)": 0, + "(914, '2024-11-11', 4)": 0, + "(914, '2024-11-11', 5)": 0, + "(914, '2024-11-11', 6)": 0, + "(914, '2024-11-11', 7)": 0, + "(914, '2024-11-12', 0)": 0, + "(914, '2024-11-12', 1)": 0, + "(914, '2024-11-12', 2)": 0, + "(914, '2024-11-12', 3)": 0, + "(914, '2024-11-12', 4)": 0, + "(914, '2024-11-12', 5)": 0, + "(914, '2024-11-12', 6)": 0, + "(914, '2024-11-12', 7)": 0, + "(914, '2024-11-13', 0)": 0, + "(914, '2024-11-13', 1)": 0, + "(914, '2024-11-13', 2)": 0, + "(914, '2024-11-13', 3)": 0, + "(914, '2024-11-13', 4)": 0, + "(914, '2024-11-13', 5)": 0, + "(914, '2024-11-13', 6)": 0, + "(914, '2024-11-13', 7)": 0, + "(914, '2024-11-14', 0)": 0, + "(914, '2024-11-14', 1)": 0, + "(914, '2024-11-14', 2)": 0, + "(914, '2024-11-14', 3)": 0, + "(914, '2024-11-14', 4)": 0, + "(914, '2024-11-14', 5)": 0, + "(914, '2024-11-14', 6)": 0, + "(914, '2024-11-14', 7)": 0, + "(914, '2024-11-15', 0)": 0, + "(914, '2024-11-15', 1)": 0, + "(914, '2024-11-15', 2)": 1, + "(914, '2024-11-15', 3)": 0, + "(914, '2024-11-15', 4)": 0, + "(914, '2024-11-15', 5)": 0, + "(914, '2024-11-15', 6)": 0, + "(914, '2024-11-15', 7)": 0, + "(914, '2024-11-16', 0)": 0, + "(914, '2024-11-16', 1)": 0, + "(914, '2024-11-16', 2)": 0, + "(914, '2024-11-16', 3)": 0, + "(914, '2024-11-16', 4)": 0, + "(914, '2024-11-16', 5)": 0, + "(914, '2024-11-16', 6)": 0, + "(914, '2024-11-16', 7)": 0, + "(914, '2024-11-17', 0)": 0, + "(914, '2024-11-17', 1)": 0, + "(914, '2024-11-17', 2)": 0, + "(914, '2024-11-17', 3)": 0, + "(914, '2024-11-17', 4)": 0, + "(914, '2024-11-17', 5)": 0, + "(914, '2024-11-17', 6)": 0, + "(914, '2024-11-17', 7)": 0, + "(914, '2024-11-18', 0)": 0, + "(914, '2024-11-18', 1)": 0, + "(914, '2024-11-18', 2)": 0, + "(914, '2024-11-18', 3)": 0, + "(914, '2024-11-18', 4)": 0, + "(914, '2024-11-18', 5)": 0, + "(914, '2024-11-18', 6)": 0, + "(914, '2024-11-18', 7)": 0, + "(914, '2024-11-19', 0)": 1, + "(914, '2024-11-19', 1)": 0, + "(914, '2024-11-19', 2)": 0, + "(914, '2024-11-19', 3)": 0, + "(914, '2024-11-19', 4)": 0, + "(914, '2024-11-19', 5)": 0, + "(914, '2024-11-19', 6)": 0, + "(914, '2024-11-19', 7)": 0, + "(914, '2024-11-20', 0)": 0, + "(914, '2024-11-20', 1)": 0, + "(914, '2024-11-20', 2)": 0, + "(914, '2024-11-20', 3)": 0, + "(914, '2024-11-20', 4)": 0, + "(914, '2024-11-20', 5)": 0, + "(914, '2024-11-20', 6)": 0, + "(914, '2024-11-20', 7)": 0, + "(914, '2024-11-21', 0)": 0, + "(914, '2024-11-21', 1)": 0, + "(914, '2024-11-21', 2)": 0, + "(914, '2024-11-21', 3)": 0, + "(914, '2024-11-21', 4)": 0, + "(914, '2024-11-21', 5)": 0, + "(914, '2024-11-21', 6)": 0, + "(914, '2024-11-21', 7)": 0, + "(914, '2024-11-22', 0)": 0, + "(914, '2024-11-22', 1)": 0, + "(914, '2024-11-22', 2)": 0, + "(914, '2024-11-22', 3)": 0, + "(914, '2024-11-22', 4)": 0, + "(914, '2024-11-22', 5)": 0, + "(914, '2024-11-22', 6)": 0, + "(914, '2024-11-22', 7)": 0, + "(914, '2024-11-23', 0)": 0, + "(914, '2024-11-23', 1)": 0, + "(914, '2024-11-23', 2)": 0, + "(914, '2024-11-23', 3)": 0, + "(914, '2024-11-23', 4)": 0, + "(914, '2024-11-23', 5)": 0, + "(914, '2024-11-23', 6)": 0, + "(914, '2024-11-23', 7)": 0, + "(914, '2024-11-24', 0)": 0, + "(914, '2024-11-24', 1)": 0, + "(914, '2024-11-24', 2)": 0, + "(914, '2024-11-24', 3)": 0, + "(914, '2024-11-24', 4)": 0, + "(914, '2024-11-24', 5)": 0, + "(914, '2024-11-24', 6)": 0, + "(914, '2024-11-24', 7)": 0, + "(914, '2024-11-25', 0)": 0, + "(914, '2024-11-25', 1)": 0, + "(914, '2024-11-25', 2)": 0, + "(914, '2024-11-25', 3)": 0, + "(914, '2024-11-25', 4)": 0, + "(914, '2024-11-25', 5)": 0, + "(914, '2024-11-25', 6)": 0, + "(914, '2024-11-25', 7)": 0, + "(914, '2024-11-26', 0)": 0, + "(914, '2024-11-26', 1)": 0, + "(914, '2024-11-26', 2)": 0, + "(914, '2024-11-26', 3)": 0, + "(914, '2024-11-26', 4)": 0, + "(914, '2024-11-26', 5)": 0, + "(914, '2024-11-26', 6)": 0, + "(914, '2024-11-26', 7)": 0, + "(914, '2024-11-27', 0)": 0, + "(914, '2024-11-27', 1)": 0, + "(914, '2024-11-27', 2)": 0, + "(914, '2024-11-27', 3)": 0, + "(914, '2024-11-27', 4)": 0, + "(914, '2024-11-27', 5)": 0, + "(914, '2024-11-27', 6)": 0, + "(914, '2024-11-27', 7)": 0, + "(914, '2024-11-28', 0)": 0, + "(914, '2024-11-28', 1)": 0, + "(914, '2024-11-28', 2)": 0, + "(914, '2024-11-28', 3)": 0, + "(914, '2024-11-28', 4)": 0, + "(914, '2024-11-28', 5)": 0, + "(914, '2024-11-28', 6)": 0, + "(914, '2024-11-28', 7)": 0, + "(914, '2024-11-29', 0)": 0, + "(914, '2024-11-29', 1)": 0, + "(914, '2024-11-29', 2)": 0, + "(914, '2024-11-29', 3)": 0, + "(914, '2024-11-29', 4)": 0, + "(914, '2024-11-29', 5)": 0, + "(914, '2024-11-29', 6)": 0, + "(914, '2024-11-29', 7)": 0, + "(914, '2024-11-30', 0)": 0, + "(914, '2024-11-30', 1)": 0, + "(914, '2024-11-30', 2)": 0, + "(914, '2024-11-30', 3)": 0, + "(914, '2024-11-30', 4)": 0, + "(914, '2024-11-30', 5)": 0, + "(914, '2024-11-30', 6)": 0, + "(914, '2024-11-30', 7)": 0, + "(917, '2024-11-01', 0)": 1, + "(917, '2024-11-01', 1)": 0, + "(917, '2024-11-01', 2)": 0, + "(917, '2024-11-01', 3)": 0, + "(917, '2024-11-01', 4)": 0, + "(917, '2024-11-01', 5)": 0, + "(917, '2024-11-01', 6)": 0, + "(917, '2024-11-01', 7)": 0, + "(917, '2024-11-02', 0)": 0, + "(917, '2024-11-02', 1)": 0, + "(917, '2024-11-02', 2)": 0, + "(917, '2024-11-02', 3)": 0, + "(917, '2024-11-02', 4)": 0, + "(917, '2024-11-02', 5)": 0, + "(917, '2024-11-02', 6)": 0, + "(917, '2024-11-02', 7)": 0, + "(917, '2024-11-03', 0)": 0, + "(917, '2024-11-03', 1)": 0, + "(917, '2024-11-03', 2)": 0, + "(917, '2024-11-03', 3)": 1, + "(917, '2024-11-03', 4)": 0, + "(917, '2024-11-03', 5)": 0, + "(917, '2024-11-03', 6)": 0, + "(917, '2024-11-03', 7)": 0, + "(917, '2024-11-04', 0)": 0, + "(917, '2024-11-04', 1)": 0, + "(917, '2024-11-04', 2)": 0, + "(917, '2024-11-04', 3)": 0, + "(917, '2024-11-04', 4)": 0, + "(917, '2024-11-04', 5)": 0, + "(917, '2024-11-04', 6)": 0, + "(917, '2024-11-04', 7)": 0, + "(917, '2024-11-05', 0)": 0, + "(917, '2024-11-05', 1)": 0, + "(917, '2024-11-05', 2)": 1, + "(917, '2024-11-05', 3)": 0, + "(917, '2024-11-05', 4)": 0, + "(917, '2024-11-05', 5)": 0, + "(917, '2024-11-05', 6)": 0, + "(917, '2024-11-05', 7)": 0, + "(917, '2024-11-06', 0)": 0, + "(917, '2024-11-06', 1)": 0, + "(917, '2024-11-06', 2)": 1, + "(917, '2024-11-06', 3)": 0, + "(917, '2024-11-06', 4)": 0, + "(917, '2024-11-06', 5)": 0, + "(917, '2024-11-06', 6)": 0, + "(917, '2024-11-06', 7)": 0, + "(917, '2024-11-07', 0)": 0, + "(917, '2024-11-07', 1)": 0, + "(917, '2024-11-07', 2)": 0, + "(917, '2024-11-07', 3)": 0, + "(917, '2024-11-07', 4)": 0, + "(917, '2024-11-07', 5)": 0, + "(917, '2024-11-07', 6)": 0, + "(917, '2024-11-07', 7)": 0, + "(917, '2024-11-08', 0)": 1, + "(917, '2024-11-08', 1)": 0, + "(917, '2024-11-08', 2)": 0, + "(917, '2024-11-08', 3)": 0, + "(917, '2024-11-08', 4)": 0, + "(917, '2024-11-08', 5)": 0, + "(917, '2024-11-08', 6)": 0, + "(917, '2024-11-08', 7)": 0, + "(917, '2024-11-09', 0)": 0, + "(917, '2024-11-09', 1)": 0, + "(917, '2024-11-09', 2)": 0, + "(917, '2024-11-09', 3)": 0, + "(917, '2024-11-09', 4)": 0, + "(917, '2024-11-09', 5)": 0, + "(917, '2024-11-09', 6)": 0, + "(917, '2024-11-09', 7)": 0, + "(917, '2024-11-10', 0)": 0, + "(917, '2024-11-10', 1)": 0, + "(917, '2024-11-10', 2)": 0, + "(917, '2024-11-10', 3)": 0, + "(917, '2024-11-10', 4)": 0, + "(917, '2024-11-10', 5)": 0, + "(917, '2024-11-10', 6)": 0, + "(917, '2024-11-10', 7)": 0, + "(917, '2024-11-11', 0)": 0, + "(917, '2024-11-11', 1)": 0, + "(917, '2024-11-11', 2)": 0, + "(917, '2024-11-11', 3)": 0, + "(917, '2024-11-11', 4)": 0, + "(917, '2024-11-11', 5)": 0, + "(917, '2024-11-11', 6)": 0, + "(917, '2024-11-11', 7)": 0, + "(917, '2024-11-12', 0)": 0, + "(917, '2024-11-12', 1)": 0, + "(917, '2024-11-12', 2)": 1, + "(917, '2024-11-12', 3)": 0, + "(917, '2024-11-12', 4)": 0, + "(917, '2024-11-12', 5)": 0, + "(917, '2024-11-12', 6)": 0, + "(917, '2024-11-12', 7)": 0, + "(917, '2024-11-13', 0)": 0, + "(917, '2024-11-13', 1)": 0, + "(917, '2024-11-13', 2)": 1, + "(917, '2024-11-13', 3)": 0, + "(917, '2024-11-13', 4)": 0, + "(917, '2024-11-13', 5)": 0, + "(917, '2024-11-13', 6)": 0, + "(917, '2024-11-13', 7)": 0, + "(917, '2024-11-14', 0)": 0, + "(917, '2024-11-14', 1)": 0, + "(917, '2024-11-14', 2)": 1, + "(917, '2024-11-14', 3)": 0, + "(917, '2024-11-14', 4)": 0, + "(917, '2024-11-14', 5)": 0, + "(917, '2024-11-14', 6)": 0, + "(917, '2024-11-14', 7)": 0, + "(917, '2024-11-15', 0)": 0, + "(917, '2024-11-15', 1)": 0, + "(917, '2024-11-15', 2)": 0, + "(917, '2024-11-15', 3)": 0, + "(917, '2024-11-15', 4)": 0, + "(917, '2024-11-15', 5)": 0, + "(917, '2024-11-15', 6)": 1, + "(917, '2024-11-15', 7)": 0, + "(917, '2024-11-16', 0)": 0, + "(917, '2024-11-16', 1)": 0, + "(917, '2024-11-16', 2)": 0, + "(917, '2024-11-16', 3)": 0, + "(917, '2024-11-16', 4)": 0, + "(917, '2024-11-16', 5)": 0, + "(917, '2024-11-16', 6)": 0, + "(917, '2024-11-16', 7)": 0, + "(917, '2024-11-17', 0)": 0, + "(917, '2024-11-17', 1)": 0, + "(917, '2024-11-17', 2)": 0, + "(917, '2024-11-17', 3)": 0, + "(917, '2024-11-17', 4)": 0, + "(917, '2024-11-17', 5)": 0, + "(917, '2024-11-17', 6)": 0, + "(917, '2024-11-17', 7)": 0, + "(917, '2024-11-18', 0)": 0, + "(917, '2024-11-18', 1)": 0, + "(917, '2024-11-18', 2)": 0, + "(917, '2024-11-18', 3)": 0, + "(917, '2024-11-18', 4)": 0, + "(917, '2024-11-18', 5)": 0, + "(917, '2024-11-18', 6)": 0, + "(917, '2024-11-18', 7)": 0, + "(917, '2024-11-19', 0)": 0, + "(917, '2024-11-19', 1)": 0, + "(917, '2024-11-19', 2)": 0, + "(917, '2024-11-19', 3)": 1, + "(917, '2024-11-19', 4)": 0, + "(917, '2024-11-19', 5)": 0, + "(917, '2024-11-19', 6)": 0, + "(917, '2024-11-19', 7)": 0, + "(917, '2024-11-20', 0)": 0, + "(917, '2024-11-20', 1)": 0, + "(917, '2024-11-20', 2)": 0, + "(917, '2024-11-20', 3)": 1, + "(917, '2024-11-20', 4)": 0, + "(917, '2024-11-20', 5)": 0, + "(917, '2024-11-20', 6)": 0, + "(917, '2024-11-20', 7)": 0, + "(917, '2024-11-21', 0)": 0, + "(917, '2024-11-21', 1)": 0, + "(917, '2024-11-21', 2)": 0, + "(917, '2024-11-21', 3)": 0, + "(917, '2024-11-21', 4)": 0, + "(917, '2024-11-21', 5)": 0, + "(917, '2024-11-21', 6)": 0, + "(917, '2024-11-21', 7)": 0, + "(917, '2024-11-22', 0)": 0, + "(917, '2024-11-22', 1)": 0, + "(917, '2024-11-22', 2)": 0, + "(917, '2024-11-22', 3)": 0, + "(917, '2024-11-22', 4)": 0, + "(917, '2024-11-22', 5)": 0, + "(917, '2024-11-22', 6)": 0, + "(917, '2024-11-22', 7)": 0, + "(917, '2024-11-23', 0)": 0, + "(917, '2024-11-23', 1)": 0, + "(917, '2024-11-23', 2)": 0, + "(917, '2024-11-23', 3)": 0, + "(917, '2024-11-23', 4)": 0, + "(917, '2024-11-23', 5)": 0, + "(917, '2024-11-23', 6)": 0, + "(917, '2024-11-23', 7)": 0, + "(917, '2024-11-24', 0)": 0, + "(917, '2024-11-24', 1)": 0, + "(917, '2024-11-24', 2)": 0, + "(917, '2024-11-24', 3)": 1, + "(917, '2024-11-24', 4)": 0, + "(917, '2024-11-24', 5)": 0, + "(917, '2024-11-24', 6)": 0, + "(917, '2024-11-24', 7)": 0, + "(917, '2024-11-25', 0)": 0, + "(917, '2024-11-25', 1)": 0, + "(917, '2024-11-25', 2)": 0, + "(917, '2024-11-25', 3)": 0, + "(917, '2024-11-25', 4)": 0, + "(917, '2024-11-25', 5)": 0, + "(917, '2024-11-25', 6)": 0, + "(917, '2024-11-25', 7)": 0, + "(917, '2024-11-26', 0)": 1, + "(917, '2024-11-26', 1)": 0, + "(917, '2024-11-26', 2)": 0, + "(917, '2024-11-26', 3)": 0, + "(917, '2024-11-26', 4)": 0, + "(917, '2024-11-26', 5)": 0, + "(917, '2024-11-26', 6)": 0, + "(917, '2024-11-26', 7)": 0, + "(917, '2024-11-27', 0)": 0, + "(917, '2024-11-27', 1)": 0, + "(917, '2024-11-27', 2)": 0, + "(917, '2024-11-27', 3)": 0, + "(917, '2024-11-27', 4)": 0, + "(917, '2024-11-27', 5)": 0, + "(917, '2024-11-27', 6)": 0, + "(917, '2024-11-27', 7)": 0, + "(917, '2024-11-28', 0)": 0, + "(917, '2024-11-28', 1)": 0, + "(917, '2024-11-28', 2)": 1, + "(917, '2024-11-28', 3)": 0, + "(917, '2024-11-28', 4)": 0, + "(917, '2024-11-28', 5)": 0, + "(917, '2024-11-28', 6)": 0, + "(917, '2024-11-28', 7)": 0, + "(917, '2024-11-29', 0)": 0, + "(917, '2024-11-29', 1)": 0, + "(917, '2024-11-29', 2)": 0, + "(917, '2024-11-29', 3)": 0, + "(917, '2024-11-29', 4)": 0, + "(917, '2024-11-29', 5)": 0, + "(917, '2024-11-29', 6)": 0, + "(917, '2024-11-29', 7)": 0, + "(917, '2024-11-30', 0)": 1, + "(917, '2024-11-30', 1)": 0, + "(917, '2024-11-30', 2)": 0, + "(917, '2024-11-30', 3)": 0, + "(917, '2024-11-30', 4)": 0, + "(917, '2024-11-30', 5)": 0, + "(917, '2024-11-30', 6)": 0, + "(917, '2024-11-30', 7)": 0, + "(921, '2024-11-01', 0)": 0, + "(921, '2024-11-01', 1)": 0, + "(921, '2024-11-01', 2)": 0, + "(921, '2024-11-01', 3)": 0, + "(921, '2024-11-01', 4)": 0, + "(921, '2024-11-01', 5)": 0, + "(921, '2024-11-01', 6)": 0, + "(921, '2024-11-01', 7)": 0, + "(921, '2024-11-02', 0)": 0, + "(921, '2024-11-02', 1)": 0, + "(921, '2024-11-02', 2)": 0, + "(921, '2024-11-02', 3)": 0, + "(921, '2024-11-02', 4)": 0, + "(921, '2024-11-02', 5)": 0, + "(921, '2024-11-02', 6)": 0, + "(921, '2024-11-02', 7)": 0, + "(921, '2024-11-03', 0)": 0, + "(921, '2024-11-03', 1)": 0, + "(921, '2024-11-03', 2)": 0, + "(921, '2024-11-03', 3)": 0, + "(921, '2024-11-03', 4)": 0, + "(921, '2024-11-03', 5)": 0, + "(921, '2024-11-03', 6)": 0, + "(921, '2024-11-03', 7)": 0, + "(921, '2024-11-04', 0)": 0, + "(921, '2024-11-04', 1)": 0, + "(921, '2024-11-04', 2)": 0, + "(921, '2024-11-04', 3)": 0, + "(921, '2024-11-04', 4)": 0, + "(921, '2024-11-04', 5)": 0, + "(921, '2024-11-04', 6)": 0, + "(921, '2024-11-04', 7)": 0, + "(921, '2024-11-05', 0)": 0, + "(921, '2024-11-05', 1)": 0, + "(921, '2024-11-05', 2)": 0, + "(921, '2024-11-05', 3)": 0, + "(921, '2024-11-05', 4)": 0, + "(921, '2024-11-05', 5)": 0, + "(921, '2024-11-05', 6)": 0, + "(921, '2024-11-05', 7)": 0, + "(921, '2024-11-06', 0)": 0, + "(921, '2024-11-06', 1)": 0, + "(921, '2024-11-06', 2)": 0, + "(921, '2024-11-06', 3)": 0, + "(921, '2024-11-06', 4)": 0, + "(921, '2024-11-06', 5)": 0, + "(921, '2024-11-06', 6)": 0, + "(921, '2024-11-06', 7)": 0, + "(921, '2024-11-07', 0)": 0, + "(921, '2024-11-07', 1)": 0, + "(921, '2024-11-07', 2)": 0, + "(921, '2024-11-07', 3)": 0, + "(921, '2024-11-07', 4)": 0, + "(921, '2024-11-07', 5)": 0, + "(921, '2024-11-07', 6)": 0, + "(921, '2024-11-07', 7)": 0, + "(921, '2024-11-08', 0)": 0, + "(921, '2024-11-08', 1)": 0, + "(921, '2024-11-08', 2)": 0, + "(921, '2024-11-08', 3)": 0, + "(921, '2024-11-08', 4)": 0, + "(921, '2024-11-08', 5)": 0, + "(921, '2024-11-08', 6)": 0, + "(921, '2024-11-08', 7)": 0, + "(921, '2024-11-09', 0)": 0, + "(921, '2024-11-09', 1)": 0, + "(921, '2024-11-09', 2)": 0, + "(921, '2024-11-09', 3)": 0, + "(921, '2024-11-09', 4)": 0, + "(921, '2024-11-09', 5)": 0, + "(921, '2024-11-09', 6)": 0, + "(921, '2024-11-09', 7)": 0, + "(921, '2024-11-10', 0)": 0, + "(921, '2024-11-10', 1)": 0, + "(921, '2024-11-10', 2)": 0, + "(921, '2024-11-10', 3)": 0, + "(921, '2024-11-10', 4)": 0, + "(921, '2024-11-10', 5)": 0, + "(921, '2024-11-10', 6)": 0, + "(921, '2024-11-10', 7)": 0, + "(921, '2024-11-11', 0)": 0, + "(921, '2024-11-11', 1)": 0, + "(921, '2024-11-11', 2)": 0, + "(921, '2024-11-11', 3)": 0, + "(921, '2024-11-11', 4)": 0, + "(921, '2024-11-11', 5)": 0, + "(921, '2024-11-11', 6)": 0, + "(921, '2024-11-11', 7)": 0, + "(921, '2024-11-12', 0)": 0, + "(921, '2024-11-12', 1)": 0, + "(921, '2024-11-12', 2)": 0, + "(921, '2024-11-12', 3)": 0, + "(921, '2024-11-12', 4)": 0, + "(921, '2024-11-12', 5)": 0, + "(921, '2024-11-12', 6)": 0, + "(921, '2024-11-12', 7)": 0, + "(921, '2024-11-13', 0)": 0, + "(921, '2024-11-13', 1)": 0, + "(921, '2024-11-13', 2)": 0, + "(921, '2024-11-13', 3)": 0, + "(921, '2024-11-13', 4)": 0, + "(921, '2024-11-13', 5)": 0, + "(921, '2024-11-13', 6)": 0, + "(921, '2024-11-13', 7)": 0, + "(921, '2024-11-14', 0)": 0, + "(921, '2024-11-14', 1)": 0, + "(921, '2024-11-14', 2)": 0, + "(921, '2024-11-14', 3)": 0, + "(921, '2024-11-14', 4)": 0, + "(921, '2024-11-14', 5)": 0, + "(921, '2024-11-14', 6)": 0, + "(921, '2024-11-14', 7)": 0, + "(921, '2024-11-15', 0)": 0, + "(921, '2024-11-15', 1)": 0, + "(921, '2024-11-15', 2)": 0, + "(921, '2024-11-15', 3)": 0, + "(921, '2024-11-15', 4)": 0, + "(921, '2024-11-15', 5)": 0, + "(921, '2024-11-15', 6)": 0, + "(921, '2024-11-15', 7)": 0, + "(921, '2024-11-16', 0)": 0, + "(921, '2024-11-16', 1)": 0, + "(921, '2024-11-16', 2)": 0, + "(921, '2024-11-16', 3)": 0, + "(921, '2024-11-16', 4)": 0, + "(921, '2024-11-16', 5)": 0, + "(921, '2024-11-16', 6)": 0, + "(921, '2024-11-16', 7)": 0, + "(921, '2024-11-17', 0)": 0, + "(921, '2024-11-17', 1)": 0, + "(921, '2024-11-17', 2)": 0, + "(921, '2024-11-17', 3)": 0, + "(921, '2024-11-17', 4)": 0, + "(921, '2024-11-17', 5)": 0, + "(921, '2024-11-17', 6)": 0, + "(921, '2024-11-17', 7)": 0, + "(921, '2024-11-18', 0)": 0, + "(921, '2024-11-18', 1)": 0, + "(921, '2024-11-18', 2)": 0, + "(921, '2024-11-18', 3)": 0, + "(921, '2024-11-18', 4)": 0, + "(921, '2024-11-18', 5)": 0, + "(921, '2024-11-18', 6)": 0, + "(921, '2024-11-18', 7)": 0, + "(921, '2024-11-19', 0)": 0, + "(921, '2024-11-19', 1)": 0, + "(921, '2024-11-19', 2)": 0, + "(921, '2024-11-19', 3)": 0, + "(921, '2024-11-19', 4)": 0, + "(921, '2024-11-19', 5)": 0, + "(921, '2024-11-19', 6)": 0, + "(921, '2024-11-19', 7)": 0, + "(921, '2024-11-20', 0)": 0, + "(921, '2024-11-20', 1)": 0, + "(921, '2024-11-20', 2)": 0, + "(921, '2024-11-20', 3)": 0, + "(921, '2024-11-20', 4)": 0, + "(921, '2024-11-20', 5)": 0, + "(921, '2024-11-20', 6)": 0, + "(921, '2024-11-20', 7)": 0, + "(921, '2024-11-21', 0)": 0, + "(921, '2024-11-21', 1)": 0, + "(921, '2024-11-21', 2)": 0, + "(921, '2024-11-21', 3)": 0, + "(921, '2024-11-21', 4)": 0, + "(921, '2024-11-21', 5)": 0, + "(921, '2024-11-21', 6)": 0, + "(921, '2024-11-21', 7)": 0, + "(921, '2024-11-22', 0)": 0, + "(921, '2024-11-22', 1)": 0, + "(921, '2024-11-22', 2)": 0, + "(921, '2024-11-22', 3)": 0, + "(921, '2024-11-22', 4)": 0, + "(921, '2024-11-22', 5)": 0, + "(921, '2024-11-22', 6)": 0, + "(921, '2024-11-22', 7)": 0, + "(921, '2024-11-23', 0)": 0, + "(921, '2024-11-23', 1)": 0, + "(921, '2024-11-23', 2)": 0, + "(921, '2024-11-23', 3)": 0, + "(921, '2024-11-23', 4)": 0, + "(921, '2024-11-23', 5)": 0, + "(921, '2024-11-23', 6)": 0, + "(921, '2024-11-23', 7)": 0, + "(921, '2024-11-24', 0)": 0, + "(921, '2024-11-24', 1)": 0, + "(921, '2024-11-24', 2)": 0, + "(921, '2024-11-24', 3)": 0, + "(921, '2024-11-24', 4)": 0, + "(921, '2024-11-24', 5)": 0, + "(921, '2024-11-24', 6)": 0, + "(921, '2024-11-24', 7)": 0, + "(921, '2024-11-25', 0)": 0, + "(921, '2024-11-25', 1)": 0, + "(921, '2024-11-25', 2)": 0, + "(921, '2024-11-25', 3)": 0, + "(921, '2024-11-25', 4)": 0, + "(921, '2024-11-25', 5)": 0, + "(921, '2024-11-25', 6)": 0, + "(921, '2024-11-25', 7)": 0, + "(921, '2024-11-26', 0)": 0, + "(921, '2024-11-26', 1)": 0, + "(921, '2024-11-26', 2)": 0, + "(921, '2024-11-26', 3)": 0, + "(921, '2024-11-26', 4)": 0, + "(921, '2024-11-26', 5)": 0, + "(921, '2024-11-26', 6)": 0, + "(921, '2024-11-26', 7)": 0, + "(921, '2024-11-27', 0)": 0, + "(921, '2024-11-27', 1)": 0, + "(921, '2024-11-27', 2)": 0, + "(921, '2024-11-27', 3)": 0, + "(921, '2024-11-27', 4)": 0, + "(921, '2024-11-27', 5)": 0, + "(921, '2024-11-27', 6)": 0, + "(921, '2024-11-27', 7)": 0, + "(921, '2024-11-28', 0)": 0, + "(921, '2024-11-28', 1)": 0, + "(921, '2024-11-28', 2)": 0, + "(921, '2024-11-28', 3)": 0, + "(921, '2024-11-28', 4)": 0, + "(921, '2024-11-28', 5)": 0, + "(921, '2024-11-28', 6)": 0, + "(921, '2024-11-28', 7)": 0, + "(921, '2024-11-29', 0)": 0, + "(921, '2024-11-29', 1)": 0, + "(921, '2024-11-29', 2)": 0, + "(921, '2024-11-29', 3)": 0, + "(921, '2024-11-29', 4)": 0, + "(921, '2024-11-29', 5)": 0, + "(921, '2024-11-29', 6)": 0, + "(921, '2024-11-29', 7)": 0, + "(921, '2024-11-30', 0)": 0, + "(921, '2024-11-30', 1)": 0, + "(921, '2024-11-30', 2)": 0, + "(921, '2024-11-30', 3)": 0, + "(921, '2024-11-30', 4)": 0, + "(921, '2024-11-30', 5)": 0, + "(921, '2024-11-30', 6)": 0, + "(921, '2024-11-30', 7)": 0, + "(924, '2024-11-01', 0)": 0, + "(924, '2024-11-01', 1)": 0, + "(924, '2024-11-01', 2)": 0, + "(924, '2024-11-01', 3)": 0, + "(924, '2024-11-01', 4)": 0, + "(924, '2024-11-01', 5)": 0, + "(924, '2024-11-01', 6)": 0, + "(924, '2024-11-01', 7)": 0, + "(924, '2024-11-02', 0)": 0, + "(924, '2024-11-02', 1)": 0, + "(924, '2024-11-02', 2)": 0, + "(924, '2024-11-02', 3)": 0, + "(924, '2024-11-02', 4)": 0, + "(924, '2024-11-02', 5)": 0, + "(924, '2024-11-02', 6)": 0, + "(924, '2024-11-02', 7)": 0, + "(924, '2024-11-03', 0)": 0, + "(924, '2024-11-03', 1)": 0, + "(924, '2024-11-03', 2)": 0, + "(924, '2024-11-03', 3)": 0, + "(924, '2024-11-03', 4)": 0, + "(924, '2024-11-03', 5)": 0, + "(924, '2024-11-03', 6)": 0, + "(924, '2024-11-03', 7)": 0, + "(924, '2024-11-04', 0)": 0, + "(924, '2024-11-04', 1)": 0, + "(924, '2024-11-04', 2)": 0, + "(924, '2024-11-04', 3)": 0, + "(924, '2024-11-04', 4)": 0, + "(924, '2024-11-04', 5)": 0, + "(924, '2024-11-04', 6)": 0, + "(924, '2024-11-04', 7)": 0, + "(924, '2024-11-05', 0)": 0, + "(924, '2024-11-05', 1)": 0, + "(924, '2024-11-05', 2)": 0, + "(924, '2024-11-05', 3)": 0, + "(924, '2024-11-05', 4)": 0, + "(924, '2024-11-05', 5)": 0, + "(924, '2024-11-05', 6)": 0, + "(924, '2024-11-05', 7)": 0, + "(924, '2024-11-06', 0)": 0, + "(924, '2024-11-06', 1)": 0, + "(924, '2024-11-06', 2)": 0, + "(924, '2024-11-06', 3)": 0, + "(924, '2024-11-06', 4)": 0, + "(924, '2024-11-06', 5)": 0, + "(924, '2024-11-06', 6)": 0, + "(924, '2024-11-06', 7)": 0, + "(924, '2024-11-07', 0)": 0, + "(924, '2024-11-07', 1)": 0, + "(924, '2024-11-07', 2)": 0, + "(924, '2024-11-07', 3)": 0, + "(924, '2024-11-07', 4)": 0, + "(924, '2024-11-07', 5)": 0, + "(924, '2024-11-07', 6)": 0, + "(924, '2024-11-07', 7)": 0, + "(924, '2024-11-08', 0)": 0, + "(924, '2024-11-08', 1)": 0, + "(924, '2024-11-08', 2)": 0, + "(924, '2024-11-08', 3)": 0, + "(924, '2024-11-08', 4)": 0, + "(924, '2024-11-08', 5)": 0, + "(924, '2024-11-08', 6)": 0, + "(924, '2024-11-08', 7)": 0, + "(924, '2024-11-09', 0)": 0, + "(924, '2024-11-09', 1)": 1, + "(924, '2024-11-09', 2)": 0, + "(924, '2024-11-09', 3)": 0, + "(924, '2024-11-09', 4)": 0, + "(924, '2024-11-09', 5)": 0, + "(924, '2024-11-09', 6)": 0, + "(924, '2024-11-09', 7)": 0, + "(924, '2024-11-10', 0)": 0, + "(924, '2024-11-10', 1)": 0, + "(924, '2024-11-10', 2)": 0, + "(924, '2024-11-10', 3)": 0, + "(924, '2024-11-10', 4)": 0, + "(924, '2024-11-10', 5)": 0, + "(924, '2024-11-10', 6)": 0, + "(924, '2024-11-10', 7)": 0, + "(924, '2024-11-11', 0)": 0, + "(924, '2024-11-11', 1)": 0, + "(924, '2024-11-11', 2)": 0, + "(924, '2024-11-11', 3)": 0, + "(924, '2024-11-11', 4)": 0, + "(924, '2024-11-11', 5)": 0, + "(924, '2024-11-11', 6)": 0, + "(924, '2024-11-11', 7)": 0, + "(924, '2024-11-12', 0)": 0, + "(924, '2024-11-12', 1)": 0, + "(924, '2024-11-12', 2)": 0, + "(924, '2024-11-12', 3)": 0, + "(924, '2024-11-12', 4)": 0, + "(924, '2024-11-12', 5)": 0, + "(924, '2024-11-12', 6)": 0, + "(924, '2024-11-12', 7)": 0, + "(924, '2024-11-13', 0)": 0, + "(924, '2024-11-13', 1)": 1, + "(924, '2024-11-13', 2)": 0, + "(924, '2024-11-13', 3)": 0, + "(924, '2024-11-13', 4)": 0, + "(924, '2024-11-13', 5)": 0, + "(924, '2024-11-13', 6)": 0, + "(924, '2024-11-13', 7)": 0, + "(924, '2024-11-14', 0)": 0, + "(924, '2024-11-14', 1)": 0, + "(924, '2024-11-14', 2)": 0, + "(924, '2024-11-14', 3)": 0, + "(924, '2024-11-14', 4)": 0, + "(924, '2024-11-14', 5)": 0, + "(924, '2024-11-14', 6)": 0, + "(924, '2024-11-14', 7)": 0, + "(924, '2024-11-15', 0)": 0, + "(924, '2024-11-15', 1)": 0, + "(924, '2024-11-15', 2)": 0, + "(924, '2024-11-15', 3)": 0, + "(924, '2024-11-15', 4)": 0, + "(924, '2024-11-15', 5)": 0, + "(924, '2024-11-15', 6)": 0, + "(924, '2024-11-15', 7)": 0, + "(924, '2024-11-16', 0)": 0, + "(924, '2024-11-16', 1)": 0, + "(924, '2024-11-16', 2)": 0, + "(924, '2024-11-16', 3)": 0, + "(924, '2024-11-16', 4)": 0, + "(924, '2024-11-16', 5)": 0, + "(924, '2024-11-16', 6)": 0, + "(924, '2024-11-16', 7)": 0, + "(924, '2024-11-17', 0)": 0, + "(924, '2024-11-17', 1)": 0, + "(924, '2024-11-17', 2)": 0, + "(924, '2024-11-17', 3)": 0, + "(924, '2024-11-17', 4)": 0, + "(924, '2024-11-17', 5)": 0, + "(924, '2024-11-17', 6)": 0, + "(924, '2024-11-17', 7)": 0, + "(924, '2024-11-18', 0)": 1, + "(924, '2024-11-18', 1)": 0, + "(924, '2024-11-18', 2)": 0, + "(924, '2024-11-18', 3)": 0, + "(924, '2024-11-18', 4)": 0, + "(924, '2024-11-18', 5)": 0, + "(924, '2024-11-18', 6)": 0, + "(924, '2024-11-18', 7)": 0, + "(924, '2024-11-19', 0)": 0, + "(924, '2024-11-19', 1)": 0, + "(924, '2024-11-19', 2)": 0, + "(924, '2024-11-19', 3)": 0, + "(924, '2024-11-19', 4)": 0, + "(924, '2024-11-19', 5)": 0, + "(924, '2024-11-19', 6)": 0, + "(924, '2024-11-19', 7)": 0, + "(924, '2024-11-20', 0)": 0, + "(924, '2024-11-20', 1)": 0, + "(924, '2024-11-20', 2)": 0, + "(924, '2024-11-20', 3)": 0, + "(924, '2024-11-20', 4)": 0, + "(924, '2024-11-20', 5)": 0, + "(924, '2024-11-20', 6)": 0, + "(924, '2024-11-20', 7)": 0, + "(924, '2024-11-21', 0)": 0, + "(924, '2024-11-21', 1)": 0, + "(924, '2024-11-21', 2)": 0, + "(924, '2024-11-21', 3)": 0, + "(924, '2024-11-21', 4)": 0, + "(924, '2024-11-21', 5)": 0, + "(924, '2024-11-21', 6)": 0, + "(924, '2024-11-21', 7)": 0, + "(924, '2024-11-22', 0)": 0, + "(924, '2024-11-22', 1)": 0, + "(924, '2024-11-22', 2)": 0, + "(924, '2024-11-22', 3)": 0, + "(924, '2024-11-22', 4)": 0, + "(924, '2024-11-22', 5)": 0, + "(924, '2024-11-22', 6)": 0, + "(924, '2024-11-22', 7)": 0, + "(924, '2024-11-23', 0)": 0, + "(924, '2024-11-23', 1)": 0, + "(924, '2024-11-23', 2)": 0, + "(924, '2024-11-23', 3)": 0, + "(924, '2024-11-23', 4)": 0, + "(924, '2024-11-23', 5)": 0, + "(924, '2024-11-23', 6)": 0, + "(924, '2024-11-23', 7)": 0, + "(924, '2024-11-24', 0)": 0, + "(924, '2024-11-24', 1)": 0, + "(924, '2024-11-24', 2)": 0, + "(924, '2024-11-24', 3)": 0, + "(924, '2024-11-24', 4)": 0, + "(924, '2024-11-24', 5)": 0, + "(924, '2024-11-24', 6)": 0, + "(924, '2024-11-24', 7)": 0, + "(924, '2024-11-25', 0)": 0, + "(924, '2024-11-25', 1)": 0, + "(924, '2024-11-25', 2)": 0, + "(924, '2024-11-25', 3)": 0, + "(924, '2024-11-25', 4)": 0, + "(924, '2024-11-25', 5)": 0, + "(924, '2024-11-25', 6)": 0, + "(924, '2024-11-25', 7)": 0, + "(924, '2024-11-26', 0)": 0, + "(924, '2024-11-26', 1)": 1, + "(924, '2024-11-26', 2)": 0, + "(924, '2024-11-26', 3)": 0, + "(924, '2024-11-26', 4)": 0, + "(924, '2024-11-26', 5)": 0, + "(924, '2024-11-26', 6)": 0, + "(924, '2024-11-26', 7)": 0, + "(924, '2024-11-27', 0)": 0, + "(924, '2024-11-27', 1)": 1, + "(924, '2024-11-27', 2)": 0, + "(924, '2024-11-27', 3)": 0, + "(924, '2024-11-27', 4)": 0, + "(924, '2024-11-27', 5)": 0, + "(924, '2024-11-27', 6)": 0, + "(924, '2024-11-27', 7)": 0, + "(924, '2024-11-28', 0)": 0, + "(924, '2024-11-28', 1)": 0, + "(924, '2024-11-28', 2)": 0, + "(924, '2024-11-28', 3)": 0, + "(924, '2024-11-28', 4)": 0, + "(924, '2024-11-28', 5)": 0, + "(924, '2024-11-28', 6)": 0, + "(924, '2024-11-28', 7)": 0, + "(924, '2024-11-29', 0)": 0, + "(924, '2024-11-29', 1)": 0, + "(924, '2024-11-29', 2)": 0, + "(924, '2024-11-29', 3)": 0, + "(924, '2024-11-29', 4)": 0, + "(924, '2024-11-29', 5)": 0, + "(924, '2024-11-29', 6)": 0, + "(924, '2024-11-29', 7)": 0, + "(924, '2024-11-30', 0)": 0, + "(924, '2024-11-30', 1)": 0, + "(924, '2024-11-30', 2)": 0, + "(924, '2024-11-30', 3)": 0, + "(924, '2024-11-30', 4)": 0, + "(924, '2024-11-30', 5)": 0, + "(924, '2024-11-30', 6)": 0, + "(924, '2024-11-30', 7)": 0, + "(925, '2024-11-01', 0)": 0, + "(925, '2024-11-01', 1)": 0, + "(925, '2024-11-01', 2)": 0, + "(925, '2024-11-01', 3)": 0, + "(925, '2024-11-01', 4)": 0, + "(925, '2024-11-01', 5)": 0, + "(925, '2024-11-01', 6)": 0, + "(925, '2024-11-01', 7)": 0, + "(925, '2024-11-02', 0)": 0, + "(925, '2024-11-02', 1)": 0, + "(925, '2024-11-02', 2)": 0, + "(925, '2024-11-02', 3)": 0, + "(925, '2024-11-02', 4)": 0, + "(925, '2024-11-02', 5)": 0, + "(925, '2024-11-02', 6)": 0, + "(925, '2024-11-02', 7)": 0, + "(925, '2024-11-03', 0)": 0, + "(925, '2024-11-03', 1)": 0, + "(925, '2024-11-03', 2)": 0, + "(925, '2024-11-03', 3)": 0, + "(925, '2024-11-03', 4)": 0, + "(925, '2024-11-03', 5)": 0, + "(925, '2024-11-03', 6)": 0, + "(925, '2024-11-03', 7)": 0, + "(925, '2024-11-04', 0)": 0, + "(925, '2024-11-04', 1)": 0, + "(925, '2024-11-04', 2)": 0, + "(925, '2024-11-04', 3)": 1, + "(925, '2024-11-04', 4)": 0, + "(925, '2024-11-04', 5)": 0, + "(925, '2024-11-04', 6)": 0, + "(925, '2024-11-04', 7)": 0, + "(925, '2024-11-05', 0)": 0, + "(925, '2024-11-05', 1)": 0, + "(925, '2024-11-05', 2)": 0, + "(925, '2024-11-05', 3)": 0, + "(925, '2024-11-05', 4)": 0, + "(925, '2024-11-05', 5)": 0, + "(925, '2024-11-05', 6)": 0, + "(925, '2024-11-05', 7)": 0, + "(925, '2024-11-06', 0)": 0, + "(925, '2024-11-06', 1)": 0, + "(925, '2024-11-06', 2)": 0, + "(925, '2024-11-06', 3)": 0, + "(925, '2024-11-06', 4)": 0, + "(925, '2024-11-06', 5)": 0, + "(925, '2024-11-06', 6)": 0, + "(925, '2024-11-06', 7)": 0, + "(925, '2024-11-07', 0)": 0, + "(925, '2024-11-07', 1)": 0, + "(925, '2024-11-07', 2)": 0, + "(925, '2024-11-07', 3)": 0, + "(925, '2024-11-07', 4)": 0, + "(925, '2024-11-07', 5)": 0, + "(925, '2024-11-07', 6)": 0, + "(925, '2024-11-07', 7)": 0, + "(925, '2024-11-08', 0)": 0, + "(925, '2024-11-08', 1)": 0, + "(925, '2024-11-08', 2)": 0, + "(925, '2024-11-08', 3)": 0, + "(925, '2024-11-08', 4)": 0, + "(925, '2024-11-08', 5)": 0, + "(925, '2024-11-08', 6)": 0, + "(925, '2024-11-08', 7)": 0, + "(925, '2024-11-09', 0)": 0, + "(925, '2024-11-09', 1)": 0, + "(925, '2024-11-09', 2)": 0, + "(925, '2024-11-09', 3)": 0, + "(925, '2024-11-09', 4)": 0, + "(925, '2024-11-09', 5)": 0, + "(925, '2024-11-09', 6)": 0, + "(925, '2024-11-09', 7)": 0, + "(925, '2024-11-10', 0)": 0, + "(925, '2024-11-10', 1)": 0, + "(925, '2024-11-10', 2)": 0, + "(925, '2024-11-10', 3)": 0, + "(925, '2024-11-10', 4)": 0, + "(925, '2024-11-10', 5)": 0, + "(925, '2024-11-10', 6)": 0, + "(925, '2024-11-10', 7)": 0, + "(925, '2024-11-11', 0)": 0, + "(925, '2024-11-11', 1)": 0, + "(925, '2024-11-11', 2)": 0, + "(925, '2024-11-11', 3)": 1, + "(925, '2024-11-11', 4)": 0, + "(925, '2024-11-11', 5)": 0, + "(925, '2024-11-11', 6)": 0, + "(925, '2024-11-11', 7)": 0, + "(925, '2024-11-12', 0)": 0, + "(925, '2024-11-12', 1)": 0, + "(925, '2024-11-12', 2)": 0, + "(925, '2024-11-12', 3)": 1, + "(925, '2024-11-12', 4)": 0, + "(925, '2024-11-12', 5)": 0, + "(925, '2024-11-12', 6)": 0, + "(925, '2024-11-12', 7)": 0, + "(925, '2024-11-13', 0)": 0, + "(925, '2024-11-13', 1)": 0, + "(925, '2024-11-13', 2)": 0, + "(925, '2024-11-13', 3)": 0, + "(925, '2024-11-13', 4)": 0, + "(925, '2024-11-13', 5)": 0, + "(925, '2024-11-13', 6)": 0, + "(925, '2024-11-13', 7)": 0, + "(925, '2024-11-14', 0)": 0, + "(925, '2024-11-14', 1)": 0, + "(925, '2024-11-14', 2)": 0, + "(925, '2024-11-14', 3)": 1, + "(925, '2024-11-14', 4)": 0, + "(925, '2024-11-14', 5)": 0, + "(925, '2024-11-14', 6)": 0, + "(925, '2024-11-14', 7)": 0, + "(925, '2024-11-15', 0)": 0, + "(925, '2024-11-15', 1)": 0, + "(925, '2024-11-15', 2)": 0, + "(925, '2024-11-15', 3)": 0, + "(925, '2024-11-15', 4)": 0, + "(925, '2024-11-15', 5)": 0, + "(925, '2024-11-15', 6)": 0, + "(925, '2024-11-15', 7)": 0, + "(925, '2024-11-16', 0)": 0, + "(925, '2024-11-16', 1)": 0, + "(925, '2024-11-16', 2)": 0, + "(925, '2024-11-16', 3)": 0, + "(925, '2024-11-16', 4)": 0, + "(925, '2024-11-16', 5)": 0, + "(925, '2024-11-16', 6)": 0, + "(925, '2024-11-16', 7)": 0, + "(925, '2024-11-17', 0)": 0, + "(925, '2024-11-17', 1)": 0, + "(925, '2024-11-17', 2)": 0, + "(925, '2024-11-17', 3)": 0, + "(925, '2024-11-17', 4)": 0, + "(925, '2024-11-17', 5)": 0, + "(925, '2024-11-17', 6)": 0, + "(925, '2024-11-17', 7)": 0, + "(925, '2024-11-18', 0)": 0, + "(925, '2024-11-18', 1)": 0, + "(925, '2024-11-18', 2)": 0, + "(925, '2024-11-18', 3)": 0, + "(925, '2024-11-18', 4)": 0, + "(925, '2024-11-18', 5)": 0, + "(925, '2024-11-18', 6)": 0, + "(925, '2024-11-18', 7)": 0, + "(925, '2024-11-19', 0)": 0, + "(925, '2024-11-19', 1)": 0, + "(925, '2024-11-19', 2)": 0, + "(925, '2024-11-19', 3)": 0, + "(925, '2024-11-19', 4)": 0, + "(925, '2024-11-19', 5)": 0, + "(925, '2024-11-19', 6)": 0, + "(925, '2024-11-19', 7)": 0, + "(925, '2024-11-20', 0)": 0, + "(925, '2024-11-20', 1)": 0, + "(925, '2024-11-20', 2)": 0, + "(925, '2024-11-20', 3)": 1, + "(925, '2024-11-20', 4)": 0, + "(925, '2024-11-20', 5)": 0, + "(925, '2024-11-20', 6)": 0, + "(925, '2024-11-20', 7)": 0, + "(925, '2024-11-21', 0)": 0, + "(925, '2024-11-21', 1)": 0, + "(925, '2024-11-21', 2)": 0, + "(925, '2024-11-21', 3)": 0, + "(925, '2024-11-21', 4)": 0, + "(925, '2024-11-21', 5)": 0, + "(925, '2024-11-21', 6)": 0, + "(925, '2024-11-21', 7)": 0, + "(925, '2024-11-22', 0)": 0, + "(925, '2024-11-22', 1)": 0, + "(925, '2024-11-22', 2)": 0, + "(925, '2024-11-22', 3)": 1, + "(925, '2024-11-22', 4)": 0, + "(925, '2024-11-22', 5)": 0, + "(925, '2024-11-22', 6)": 0, + "(925, '2024-11-22', 7)": 0, + "(925, '2024-11-23', 0)": 0, + "(925, '2024-11-23', 1)": 0, + "(925, '2024-11-23', 2)": 0, + "(925, '2024-11-23', 3)": 0, + "(925, '2024-11-23', 4)": 0, + "(925, '2024-11-23', 5)": 0, + "(925, '2024-11-23', 6)": 0, + "(925, '2024-11-23', 7)": 0, + "(925, '2024-11-24', 0)": 0, + "(925, '2024-11-24', 1)": 0, + "(925, '2024-11-24', 2)": 0, + "(925, '2024-11-24', 3)": 0, + "(925, '2024-11-24', 4)": 0, + "(925, '2024-11-24', 5)": 0, + "(925, '2024-11-24', 6)": 0, + "(925, '2024-11-24', 7)": 0, + "(925, '2024-11-25', 0)": 0, + "(925, '2024-11-25', 1)": 0, + "(925, '2024-11-25', 2)": 0, + "(925, '2024-11-25', 3)": 0, + "(925, '2024-11-25', 4)": 0, + "(925, '2024-11-25', 5)": 0, + "(925, '2024-11-25', 6)": 0, + "(925, '2024-11-25', 7)": 0, + "(925, '2024-11-26', 0)": 0, + "(925, '2024-11-26', 1)": 0, + "(925, '2024-11-26', 2)": 0, + "(925, '2024-11-26', 3)": 1, + "(925, '2024-11-26', 4)": 0, + "(925, '2024-11-26', 5)": 0, + "(925, '2024-11-26', 6)": 0, + "(925, '2024-11-26', 7)": 0, + "(925, '2024-11-27', 0)": 0, + "(925, '2024-11-27', 1)": 0, + "(925, '2024-11-27', 2)": 0, + "(925, '2024-11-27', 3)": 0, + "(925, '2024-11-27', 4)": 0, + "(925, '2024-11-27', 5)": 0, + "(925, '2024-11-27', 6)": 0, + "(925, '2024-11-27', 7)": 0, + "(925, '2024-11-28', 0)": 0, + "(925, '2024-11-28', 1)": 0, + "(925, '2024-11-28', 2)": 0, + "(925, '2024-11-28', 3)": 1, + "(925, '2024-11-28', 4)": 0, + "(925, '2024-11-28', 5)": 0, + "(925, '2024-11-28', 6)": 0, + "(925, '2024-11-28', 7)": 0, + "(925, '2024-11-29', 0)": 0, + "(925, '2024-11-29', 1)": 0, + "(925, '2024-11-29', 2)": 0, + "(925, '2024-11-29', 3)": 0, + "(925, '2024-11-29', 4)": 0, + "(925, '2024-11-29', 5)": 0, + "(925, '2024-11-29', 6)": 0, + "(925, '2024-11-29', 7)": 0, + "(925, '2024-11-30', 0)": 0, + "(925, '2024-11-30', 1)": 0, + "(925, '2024-11-30', 2)": 0, + "(925, '2024-11-30', 3)": 1, + "(925, '2024-11-30', 4)": 0, + "(925, '2024-11-30', 5)": 0, + "(925, '2024-11-30', 6)": 0, + "(925, '2024-11-30', 7)": 0, + "(927, '2024-11-01', 0)": 0, + "(927, '2024-11-01', 1)": 0, + "(927, '2024-11-01', 2)": 0, + "(927, '2024-11-01', 3)": 0, + "(927, '2024-11-01', 4)": 0, + "(927, '2024-11-01', 5)": 0, + "(927, '2024-11-01', 6)": 0, + "(927, '2024-11-01', 7)": 0, + "(927, '2024-11-02', 0)": 0, + "(927, '2024-11-02', 1)": 0, + "(927, '2024-11-02', 2)": 0, + "(927, '2024-11-02', 3)": 0, + "(927, '2024-11-02', 4)": 0, + "(927, '2024-11-02', 5)": 0, + "(927, '2024-11-02', 6)": 0, + "(927, '2024-11-02', 7)": 0, + "(927, '2024-11-03', 0)": 0, + "(927, '2024-11-03', 1)": 0, + "(927, '2024-11-03', 2)": 0, + "(927, '2024-11-03', 3)": 1, + "(927, '2024-11-03', 4)": 0, + "(927, '2024-11-03', 5)": 0, + "(927, '2024-11-03', 6)": 0, + "(927, '2024-11-03', 7)": 0, + "(927, '2024-11-04', 0)": 0, + "(927, '2024-11-04', 1)": 0, + "(927, '2024-11-04', 2)": 0, + "(927, '2024-11-04', 3)": 0, + "(927, '2024-11-04', 4)": 0, + "(927, '2024-11-04', 5)": 0, + "(927, '2024-11-04', 6)": 0, + "(927, '2024-11-04', 7)": 0, + "(927, '2024-11-05', 0)": 0, + "(927, '2024-11-05', 1)": 0, + "(927, '2024-11-05', 2)": 0, + "(927, '2024-11-05', 3)": 0, + "(927, '2024-11-05', 4)": 0, + "(927, '2024-11-05', 5)": 0, + "(927, '2024-11-05', 6)": 0, + "(927, '2024-11-05', 7)": 0, + "(927, '2024-11-06', 0)": 0, + "(927, '2024-11-06', 1)": 0, + "(927, '2024-11-06', 2)": 0, + "(927, '2024-11-06', 3)": 0, + "(927, '2024-11-06', 4)": 0, + "(927, '2024-11-06', 5)": 0, + "(927, '2024-11-06', 6)": 0, + "(927, '2024-11-06', 7)": 0, + "(927, '2024-11-07', 0)": 1, + "(927, '2024-11-07', 1)": 0, + "(927, '2024-11-07', 2)": 0, + "(927, '2024-11-07', 3)": 0, + "(927, '2024-11-07', 4)": 0, + "(927, '2024-11-07', 5)": 0, + "(927, '2024-11-07', 6)": 0, + "(927, '2024-11-07', 7)": 0, + "(927, '2024-11-08', 0)": 1, + "(927, '2024-11-08', 1)": 0, + "(927, '2024-11-08', 2)": 0, + "(927, '2024-11-08', 3)": 0, + "(927, '2024-11-08', 4)": 0, + "(927, '2024-11-08', 5)": 0, + "(927, '2024-11-08', 6)": 0, + "(927, '2024-11-08', 7)": 0, + "(927, '2024-11-09', 0)": 1, + "(927, '2024-11-09', 1)": 0, + "(927, '2024-11-09', 2)": 0, + "(927, '2024-11-09', 3)": 0, + "(927, '2024-11-09', 4)": 0, + "(927, '2024-11-09', 5)": 0, + "(927, '2024-11-09', 6)": 0, + "(927, '2024-11-09', 7)": 0, + "(927, '2024-11-10', 0)": 1, + "(927, '2024-11-10', 1)": 0, + "(927, '2024-11-10', 2)": 0, + "(927, '2024-11-10', 3)": 0, + "(927, '2024-11-10', 4)": 0, + "(927, '2024-11-10', 5)": 0, + "(927, '2024-11-10', 6)": 0, + "(927, '2024-11-10', 7)": 0, + "(927, '2024-11-11', 0)": 0, + "(927, '2024-11-11', 1)": 0, + "(927, '2024-11-11', 2)": 1, + "(927, '2024-11-11', 3)": 0, + "(927, '2024-11-11', 4)": 0, + "(927, '2024-11-11', 5)": 0, + "(927, '2024-11-11', 6)": 0, + "(927, '2024-11-11', 7)": 0, + "(927, '2024-11-12', 0)": 0, + "(927, '2024-11-12', 1)": 0, + "(927, '2024-11-12', 2)": 1, + "(927, '2024-11-12', 3)": 0, + "(927, '2024-11-12', 4)": 0, + "(927, '2024-11-12', 5)": 0, + "(927, '2024-11-12', 6)": 0, + "(927, '2024-11-12', 7)": 0, + "(927, '2024-11-13', 0)": 0, + "(927, '2024-11-13', 1)": 0, + "(927, '2024-11-13', 2)": 1, + "(927, '2024-11-13', 3)": 0, + "(927, '2024-11-13', 4)": 0, + "(927, '2024-11-13', 5)": 0, + "(927, '2024-11-13', 6)": 0, + "(927, '2024-11-13', 7)": 0, + "(927, '2024-11-14', 0)": 0, + "(927, '2024-11-14', 1)": 0, + "(927, '2024-11-14', 2)": 0, + "(927, '2024-11-14', 3)": 0, + "(927, '2024-11-14', 4)": 0, + "(927, '2024-11-14', 5)": 0, + "(927, '2024-11-14', 6)": 0, + "(927, '2024-11-14', 7)": 0, + "(927, '2024-11-15', 0)": 0, + "(927, '2024-11-15', 1)": 0, + "(927, '2024-11-15', 2)": 0, + "(927, '2024-11-15', 3)": 0, + "(927, '2024-11-15', 4)": 0, + "(927, '2024-11-15', 5)": 0, + "(927, '2024-11-15', 6)": 0, + "(927, '2024-11-15', 7)": 0, + "(927, '2024-11-16', 0)": 0, + "(927, '2024-11-16', 1)": 0, + "(927, '2024-11-16', 2)": 1, + "(927, '2024-11-16', 3)": 0, + "(927, '2024-11-16', 4)": 0, + "(927, '2024-11-16', 5)": 0, + "(927, '2024-11-16', 6)": 0, + "(927, '2024-11-16', 7)": 0, + "(927, '2024-11-17', 0)": 0, + "(927, '2024-11-17', 1)": 0, + "(927, '2024-11-17', 2)": 1, + "(927, '2024-11-17', 3)": 0, + "(927, '2024-11-17', 4)": 0, + "(927, '2024-11-17', 5)": 0, + "(927, '2024-11-17', 6)": 0, + "(927, '2024-11-17', 7)": 0, + "(927, '2024-11-18', 0)": 0, + "(927, '2024-11-18', 1)": 0, + "(927, '2024-11-18', 2)": 0, + "(927, '2024-11-18', 3)": 0, + "(927, '2024-11-18', 4)": 0, + "(927, '2024-11-18', 5)": 0, + "(927, '2024-11-18', 6)": 0, + "(927, '2024-11-18', 7)": 0, + "(927, '2024-11-19', 0)": 1, + "(927, '2024-11-19', 1)": 0, + "(927, '2024-11-19', 2)": 0, + "(927, '2024-11-19', 3)": 0, + "(927, '2024-11-19', 4)": 0, + "(927, '2024-11-19', 5)": 0, + "(927, '2024-11-19', 6)": 0, + "(927, '2024-11-19', 7)": 0, + "(927, '2024-11-20', 0)": 0, + "(927, '2024-11-20', 1)": 0, + "(927, '2024-11-20', 2)": 0, + "(927, '2024-11-20', 3)": 0, + "(927, '2024-11-20', 4)": 0, + "(927, '2024-11-20', 5)": 0, + "(927, '2024-11-20', 6)": 0, + "(927, '2024-11-20', 7)": 0, + "(927, '2024-11-21', 0)": 1, + "(927, '2024-11-21', 1)": 0, + "(927, '2024-11-21', 2)": 0, + "(927, '2024-11-21', 3)": 0, + "(927, '2024-11-21', 4)": 0, + "(927, '2024-11-21', 5)": 0, + "(927, '2024-11-21', 6)": 0, + "(927, '2024-11-21', 7)": 0, + "(927, '2024-11-22', 0)": 0, + "(927, '2024-11-22', 1)": 0, + "(927, '2024-11-22', 2)": 1, + "(927, '2024-11-22', 3)": 0, + "(927, '2024-11-22', 4)": 0, + "(927, '2024-11-22', 5)": 0, + "(927, '2024-11-22', 6)": 0, + "(927, '2024-11-22', 7)": 0, + "(927, '2024-11-23', 0)": 0, + "(927, '2024-11-23', 1)": 0, + "(927, '2024-11-23', 2)": 1, + "(927, '2024-11-23', 3)": 0, + "(927, '2024-11-23', 4)": 0, + "(927, '2024-11-23', 5)": 0, + "(927, '2024-11-23', 6)": 0, + "(927, '2024-11-23', 7)": 0, + "(927, '2024-11-24', 0)": 0, + "(927, '2024-11-24', 1)": 0, + "(927, '2024-11-24', 2)": 1, + "(927, '2024-11-24', 3)": 0, + "(927, '2024-11-24', 4)": 0, + "(927, '2024-11-24', 5)": 0, + "(927, '2024-11-24', 6)": 0, + "(927, '2024-11-24', 7)": 0, + "(927, '2024-11-25', 0)": 0, + "(927, '2024-11-25', 1)": 0, + "(927, '2024-11-25', 2)": 1, + "(927, '2024-11-25', 3)": 0, + "(927, '2024-11-25', 4)": 0, + "(927, '2024-11-25', 5)": 0, + "(927, '2024-11-25', 6)": 0, + "(927, '2024-11-25', 7)": 0, + "(927, '2024-11-26', 0)": 0, + "(927, '2024-11-26', 1)": 0, + "(927, '2024-11-26', 2)": 1, + "(927, '2024-11-26', 3)": 0, + "(927, '2024-11-26', 4)": 0, + "(927, '2024-11-26', 5)": 0, + "(927, '2024-11-26', 6)": 0, + "(927, '2024-11-26', 7)": 0, + "(927, '2024-11-27', 0)": 0, + "(927, '2024-11-27', 1)": 0, + "(927, '2024-11-27', 2)": 0, + "(927, '2024-11-27', 3)": 0, + "(927, '2024-11-27', 4)": 0, + "(927, '2024-11-27', 5)": 0, + "(927, '2024-11-27', 6)": 0, + "(927, '2024-11-27', 7)": 0, + "(927, '2024-11-28', 0)": 0, + "(927, '2024-11-28', 1)": 0, + "(927, '2024-11-28', 2)": 0, + "(927, '2024-11-28', 3)": 0, + "(927, '2024-11-28', 4)": 0, + "(927, '2024-11-28', 5)": 0, + "(927, '2024-11-28', 6)": 0, + "(927, '2024-11-28', 7)": 0, + "(927, '2024-11-29', 0)": 1, + "(927, '2024-11-29', 1)": 0, + "(927, '2024-11-29', 2)": 0, + "(927, '2024-11-29', 3)": 0, + "(927, '2024-11-29', 4)": 0, + "(927, '2024-11-29', 5)": 0, + "(927, '2024-11-29', 6)": 0, + "(927, '2024-11-29', 7)": 0, + "(927, '2024-11-30', 0)": 0, + "(927, '2024-11-30', 1)": 0, + "(927, '2024-11-30', 2)": 0, + "(927, '2024-11-30', 3)": 0, + "(927, '2024-11-30', 4)": 0, + "(927, '2024-11-30', 5)": 0, + "(927, '2024-11-30', 6)": 0, + "(927, '2024-11-30', 7)": 0, + "(928, '2024-11-01', 0)": 0, + "(928, '2024-11-01', 1)": 0, + "(928, '2024-11-01', 2)": 0, + "(928, '2024-11-01', 3)": 0, + "(928, '2024-11-01', 4)": 0, + "(928, '2024-11-01', 5)": 0, + "(928, '2024-11-01', 6)": 0, + "(928, '2024-11-01', 7)": 0, + "(928, '2024-11-02', 0)": 0, + "(928, '2024-11-02', 1)": 0, + "(928, '2024-11-02', 2)": 0, + "(928, '2024-11-02', 3)": 0, + "(928, '2024-11-02', 4)": 0, + "(928, '2024-11-02', 5)": 0, + "(928, '2024-11-02', 6)": 0, + "(928, '2024-11-02', 7)": 0, + "(928, '2024-11-03', 0)": 0, + "(928, '2024-11-03', 1)": 0, + "(928, '2024-11-03', 2)": 0, + "(928, '2024-11-03', 3)": 0, + "(928, '2024-11-03', 4)": 0, + "(928, '2024-11-03', 5)": 0, + "(928, '2024-11-03', 6)": 0, + "(928, '2024-11-03', 7)": 0, + "(928, '2024-11-04', 0)": 0, + "(928, '2024-11-04', 1)": 0, + "(928, '2024-11-04', 2)": 0, + "(928, '2024-11-04', 3)": 1, + "(928, '2024-11-04', 4)": 0, + "(928, '2024-11-04', 5)": 0, + "(928, '2024-11-04', 6)": 0, + "(928, '2024-11-04', 7)": 0, + "(928, '2024-11-05', 0)": 0, + "(928, '2024-11-05', 1)": 0, + "(928, '2024-11-05', 2)": 0, + "(928, '2024-11-05', 3)": 0, + "(928, '2024-11-05', 4)": 0, + "(928, '2024-11-05', 5)": 0, + "(928, '2024-11-05', 6)": 0, + "(928, '2024-11-05', 7)": 0, + "(928, '2024-11-06', 0)": 0, + "(928, '2024-11-06', 1)": 0, + "(928, '2024-11-06', 2)": 0, + "(928, '2024-11-06', 3)": 0, + "(928, '2024-11-06', 4)": 0, + "(928, '2024-11-06', 5)": 0, + "(928, '2024-11-06', 6)": 0, + "(928, '2024-11-06', 7)": 0, + "(928, '2024-11-07', 0)": 0, + "(928, '2024-11-07', 1)": 0, + "(928, '2024-11-07', 2)": 0, + "(928, '2024-11-07', 3)": 0, + "(928, '2024-11-07', 4)": 0, + "(928, '2024-11-07', 5)": 0, + "(928, '2024-11-07', 6)": 0, + "(928, '2024-11-07', 7)": 0, + "(928, '2024-11-08', 0)": 0, + "(928, '2024-11-08', 1)": 0, + "(928, '2024-11-08', 2)": 0, + "(928, '2024-11-08', 3)": 1, + "(928, '2024-11-08', 4)": 0, + "(928, '2024-11-08', 5)": 0, + "(928, '2024-11-08', 6)": 0, + "(928, '2024-11-08', 7)": 0, + "(928, '2024-11-09', 0)": 0, + "(928, '2024-11-09', 1)": 0, + "(928, '2024-11-09', 2)": 0, + "(928, '2024-11-09', 3)": 0, + "(928, '2024-11-09', 4)": 0, + "(928, '2024-11-09', 5)": 0, + "(928, '2024-11-09', 6)": 0, + "(928, '2024-11-09', 7)": 0, + "(928, '2024-11-10', 0)": 0, + "(928, '2024-11-10', 1)": 0, + "(928, '2024-11-10', 2)": 0, + "(928, '2024-11-10', 3)": 0, + "(928, '2024-11-10', 4)": 0, + "(928, '2024-11-10', 5)": 0, + "(928, '2024-11-10', 6)": 0, + "(928, '2024-11-10', 7)": 0, + "(928, '2024-11-11', 0)": 0, + "(928, '2024-11-11', 1)": 0, + "(928, '2024-11-11', 2)": 0, + "(928, '2024-11-11', 3)": 0, + "(928, '2024-11-11', 4)": 0, + "(928, '2024-11-11', 5)": 0, + "(928, '2024-11-11', 6)": 0, + "(928, '2024-11-11', 7)": 0, + "(928, '2024-11-12', 0)": 0, + "(928, '2024-11-12', 1)": 0, + "(928, '2024-11-12', 2)": 0, + "(928, '2024-11-12', 3)": 0, + "(928, '2024-11-12', 4)": 0, + "(928, '2024-11-12', 5)": 0, + "(928, '2024-11-12', 6)": 0, + "(928, '2024-11-12', 7)": 0, + "(928, '2024-11-13', 0)": 0, + "(928, '2024-11-13', 1)": 0, + "(928, '2024-11-13', 2)": 0, + "(928, '2024-11-13', 3)": 0, + "(928, '2024-11-13', 4)": 0, + "(928, '2024-11-13', 5)": 0, + "(928, '2024-11-13', 6)": 0, + "(928, '2024-11-13', 7)": 0, + "(928, '2024-11-14', 0)": 0, + "(928, '2024-11-14', 1)": 0, + "(928, '2024-11-14', 2)": 0, + "(928, '2024-11-14', 3)": 0, + "(928, '2024-11-14', 4)": 0, + "(928, '2024-11-14', 5)": 0, + "(928, '2024-11-14', 6)": 0, + "(928, '2024-11-14', 7)": 0, + "(928, '2024-11-15', 0)": 0, + "(928, '2024-11-15', 1)": 0, + "(928, '2024-11-15', 2)": 0, + "(928, '2024-11-15', 3)": 0, + "(928, '2024-11-15', 4)": 0, + "(928, '2024-11-15', 5)": 0, + "(928, '2024-11-15', 6)": 0, + "(928, '2024-11-15', 7)": 0, + "(928, '2024-11-16', 0)": 0, + "(928, '2024-11-16', 1)": 0, + "(928, '2024-11-16', 2)": 0, + "(928, '2024-11-16', 3)": 0, + "(928, '2024-11-16', 4)": 0, + "(928, '2024-11-16', 5)": 0, + "(928, '2024-11-16', 6)": 0, + "(928, '2024-11-16', 7)": 0, + "(928, '2024-11-17', 0)": 0, + "(928, '2024-11-17', 1)": 0, + "(928, '2024-11-17', 2)": 0, + "(928, '2024-11-17', 3)": 0, + "(928, '2024-11-17', 4)": 0, + "(928, '2024-11-17', 5)": 0, + "(928, '2024-11-17', 6)": 0, + "(928, '2024-11-17', 7)": 0, + "(928, '2024-11-18', 0)": 0, + "(928, '2024-11-18', 1)": 0, + "(928, '2024-11-18', 2)": 0, + "(928, '2024-11-18', 3)": 0, + "(928, '2024-11-18', 4)": 0, + "(928, '2024-11-18', 5)": 0, + "(928, '2024-11-18', 6)": 0, + "(928, '2024-11-18', 7)": 0, + "(928, '2024-11-19', 0)": 0, + "(928, '2024-11-19', 1)": 0, + "(928, '2024-11-19', 2)": 0, + "(928, '2024-11-19', 3)": 0, + "(928, '2024-11-19', 4)": 0, + "(928, '2024-11-19', 5)": 0, + "(928, '2024-11-19', 6)": 0, + "(928, '2024-11-19', 7)": 0, + "(928, '2024-11-20', 0)": 0, + "(928, '2024-11-20', 1)": 0, + "(928, '2024-11-20', 2)": 0, + "(928, '2024-11-20', 3)": 0, + "(928, '2024-11-20', 4)": 0, + "(928, '2024-11-20', 5)": 0, + "(928, '2024-11-20', 6)": 0, + "(928, '2024-11-20', 7)": 0, + "(928, '2024-11-21', 0)": 0, + "(928, '2024-11-21', 1)": 0, + "(928, '2024-11-21', 2)": 0, + "(928, '2024-11-21', 3)": 1, + "(928, '2024-11-21', 4)": 0, + "(928, '2024-11-21', 5)": 0, + "(928, '2024-11-21', 6)": 0, + "(928, '2024-11-21', 7)": 0, + "(928, '2024-11-22', 0)": 0, + "(928, '2024-11-22', 1)": 0, + "(928, '2024-11-22', 2)": 0, + "(928, '2024-11-22', 3)": 0, + "(928, '2024-11-22', 4)": 0, + "(928, '2024-11-22', 5)": 0, + "(928, '2024-11-22', 6)": 0, + "(928, '2024-11-22', 7)": 0, + "(928, '2024-11-23', 0)": 0, + "(928, '2024-11-23', 1)": 0, + "(928, '2024-11-23', 2)": 0, + "(928, '2024-11-23', 3)": 0, + "(928, '2024-11-23', 4)": 0, + "(928, '2024-11-23', 5)": 0, + "(928, '2024-11-23', 6)": 0, + "(928, '2024-11-23', 7)": 0, + "(928, '2024-11-24', 0)": 0, + "(928, '2024-11-24', 1)": 0, + "(928, '2024-11-24', 2)": 0, + "(928, '2024-11-24', 3)": 0, + "(928, '2024-11-24', 4)": 0, + "(928, '2024-11-24', 5)": 0, + "(928, '2024-11-24', 6)": 0, + "(928, '2024-11-24', 7)": 0, + "(928, '2024-11-25', 0)": 0, + "(928, '2024-11-25', 1)": 0, + "(928, '2024-11-25', 2)": 0, + "(928, '2024-11-25', 3)": 0, + "(928, '2024-11-25', 4)": 0, + "(928, '2024-11-25', 5)": 0, + "(928, '2024-11-25', 6)": 0, + "(928, '2024-11-25', 7)": 0, + "(928, '2024-11-26', 0)": 0, + "(928, '2024-11-26', 1)": 0, + "(928, '2024-11-26', 2)": 0, + "(928, '2024-11-26', 3)": 0, + "(928, '2024-11-26', 4)": 0, + "(928, '2024-11-26', 5)": 0, + "(928, '2024-11-26', 6)": 0, + "(928, '2024-11-26', 7)": 0, + "(928, '2024-11-27', 0)": 0, + "(928, '2024-11-27', 1)": 0, + "(928, '2024-11-27', 2)": 0, + "(928, '2024-11-27', 3)": 0, + "(928, '2024-11-27', 4)": 0, + "(928, '2024-11-27', 5)": 0, + "(928, '2024-11-27', 6)": 0, + "(928, '2024-11-27', 7)": 0, + "(928, '2024-11-28', 0)": 0, + "(928, '2024-11-28', 1)": 0, + "(928, '2024-11-28', 2)": 0, + "(928, '2024-11-28', 3)": 0, + "(928, '2024-11-28', 4)": 0, + "(928, '2024-11-28', 5)": 0, + "(928, '2024-11-28', 6)": 0, + "(928, '2024-11-28', 7)": 0, + "(928, '2024-11-29', 0)": 0, + "(928, '2024-11-29', 1)": 0, + "(928, '2024-11-29', 2)": 0, + "(928, '2024-11-29', 3)": 0, + "(928, '2024-11-29', 4)": 0, + "(928, '2024-11-29', 5)": 0, + "(928, '2024-11-29', 6)": 0, + "(928, '2024-11-29', 7)": 0, + "(928, '2024-11-30', 0)": 0, + "(928, '2024-11-30', 1)": 0, + "(928, '2024-11-30', 2)": 0, + "(928, '2024-11-30', 3)": 0, + "(928, '2024-11-30', 4)": 0, + "(928, '2024-11-30', 5)": 0, + "(928, '2024-11-30', 6)": 0, + "(928, '2024-11-30', 7)": 0, + "e:0_d:2024-11-01": 0, + "e:0_d:2024-11-02": 0, + "e:0_d:2024-11-03": 0, + "e:0_d:2024-11-04": 0, + "e:0_d:2024-11-05": 0, + "e:0_d:2024-11-06": 1, + "e:0_d:2024-11-07": 0, + "e:0_d:2024-11-08": 0, + "e:0_d:2024-11-09": 0, + "e:0_d:2024-11-10": 0, + "e:0_d:2024-11-11": 0, + "e:0_d:2024-11-12": 0, + "e:0_d:2024-11-13": 1, + "e:0_d:2024-11-14": 0, + "e:0_d:2024-11-15": 0, + "e:0_d:2024-11-16": 0, + "e:0_d:2024-11-17": 0, + "e:0_d:2024-11-18": 0, + "e:0_d:2024-11-19": 0, + "e:0_d:2024-11-20": 0, + "e:0_d:2024-11-21": 1, + "e:0_d:2024-11-22": 0, + "e:0_d:2024-11-23": 0, + "e:0_d:2024-11-24": 0, + "e:0_d:2024-11-25": 0, + "e:0_d:2024-11-26": 0, + "e:0_d:2024-11-27": 0, + "e:0_d:2024-11-28": 0, + "e:0_d:2024-11-29": 0, + "e:0_d:2024-11-30": 0, + "e:1230_d:2024-11-01": 1, + "e:1230_d:2024-11-02": 0, + "e:1230_d:2024-11-03": 0, + "e:1230_d:2024-11-04": 1, + "e:1230_d:2024-11-05": 0, + "e:1230_d:2024-11-06": 1, + "e:1230_d:2024-11-07": 1, + "e:1230_d:2024-11-08": 0, + "e:1230_d:2024-11-09": 1, + "e:1230_d:2024-11-10": 1, + "e:1230_d:2024-11-11": 0, + "e:1230_d:2024-11-12": 1, + "e:1230_d:2024-11-13": 1, + "e:1230_d:2024-11-14": 0, + "e:1230_d:2024-11-15": 0, + "e:1230_d:2024-11-16": 0, + "e:1230_d:2024-11-17": 0, + "e:1230_d:2024-11-18": 1, + "e:1230_d:2024-11-19": 0, + "e:1230_d:2024-11-20": 1, + "e:1230_d:2024-11-21": 0, + "e:1230_d:2024-11-22": 1, + "e:1230_d:2024-11-23": 0, + "e:1230_d:2024-11-24": 1, + "e:1230_d:2024-11-25": 1, + "e:1230_d:2024-11-26": 1, + "e:1230_d:2024-11-27": 0, + "e:1230_d:2024-11-28": 1, + "e:1230_d:2024-11-29": 1, + "e:1230_d:2024-11-30": 1, + "e:1_d:2024-11-01": 0, + "e:1_d:2024-11-02": 1, + "e:1_d:2024-11-03": 0, + "e:1_d:2024-11-04": 0, + "e:1_d:2024-11-05": 0, + "e:1_d:2024-11-06": 0, + "e:1_d:2024-11-07": 0, + "e:1_d:2024-11-08": 0, + "e:1_d:2024-11-09": 0, + "e:1_d:2024-11-10": 0, + "e:1_d:2024-11-11": 0, + "e:1_d:2024-11-12": 0, + "e:1_d:2024-11-13": 0, + "e:1_d:2024-11-14": 0, + "e:1_d:2024-11-15": 0, + "e:1_d:2024-11-16": 0, + "e:1_d:2024-11-17": 0, + "e:1_d:2024-11-18": 0, + "e:1_d:2024-11-19": 0, + "e:1_d:2024-11-20": 0, + "e:1_d:2024-11-21": 0, + "e:1_d:2024-11-22": 1, + "e:1_d:2024-11-23": 0, + "e:1_d:2024-11-24": 0, + "e:1_d:2024-11-25": 1, + "e:1_d:2024-11-26": 0, + "e:1_d:2024-11-27": 0, + "e:1_d:2024-11-28": 0, + "e:1_d:2024-11-29": 0, + "e:1_d:2024-11-30": 0, + "e:2932_d:2024-11-01": 1, + "e:2932_d:2024-11-02": 0, + "e:2932_d:2024-11-03": 1, + "e:2932_d:2024-11-04": 1, + "e:2932_d:2024-11-05": 1, + "e:2932_d:2024-11-06": 1, + "e:2932_d:2024-11-07": 0, + "e:2932_d:2024-11-08": 0, + "e:2932_d:2024-11-09": 0, + "e:2932_d:2024-11-10": 1, + "e:2932_d:2024-11-11": 1, + "e:2932_d:2024-11-12": 0, + "e:2932_d:2024-11-13": 0, + "e:2932_d:2024-11-14": 1, + "e:2932_d:2024-11-15": 1, + "e:2932_d:2024-11-16": 1, + "e:2932_d:2024-11-17": 1, + "e:2932_d:2024-11-18": 1, + "e:2932_d:2024-11-19": 1, + "e:2932_d:2024-11-20": 0, + "e:2932_d:2024-11-21": 1, + "e:2932_d:2024-11-22": 0, + "e:2932_d:2024-11-23": 0, + "e:2932_d:2024-11-24": 1, + "e:2932_d:2024-11-25": 1, + "e:2932_d:2024-11-26": 1, + "e:2932_d:2024-11-27": 1, + "e:2932_d:2024-11-28": 0, + "e:2932_d:2024-11-29": 0, + "e:2932_d:2024-11-30": 0, + "e:2963_d:2024-11-01": 1, + "e:2963_d:2024-11-02": 1, + "e:2963_d:2024-11-03": 1, + "e:2963_d:2024-11-04": 0, + "e:2963_d:2024-11-05": 1, + "e:2963_d:2024-11-06": 1, + "e:2963_d:2024-11-07": 0, + "e:2963_d:2024-11-08": 0, + "e:2963_d:2024-11-09": 1, + "e:2963_d:2024-11-10": 0, + "e:2963_d:2024-11-11": 0, + "e:2963_d:2024-11-12": 0, + "e:2963_d:2024-11-13": 0, + "e:2963_d:2024-11-14": 1, + "e:2963_d:2024-11-15": 0, + "e:2963_d:2024-11-16": 1, + "e:2963_d:2024-11-17": 0, + "e:2963_d:2024-11-18": 1, + "e:2963_d:2024-11-19": 1, + "e:2963_d:2024-11-20": 1, + "e:2963_d:2024-11-21": 1, + "e:2963_d:2024-11-22": 1, + "e:2963_d:2024-11-23": 1, + "e:2963_d:2024-11-24": 1, + "e:2963_d:2024-11-25": 1, + "e:2963_d:2024-11-26": 0, + "e:2963_d:2024-11-27": 1, + "e:2963_d:2024-11-28": 1, + "e:2963_d:2024-11-29": 1, + "e:2963_d:2024-11-30": 0, + "e:2_d:2024-11-01": 0, + "e:2_d:2024-11-02": 1, + "e:2_d:2024-11-03": 0, + "e:2_d:2024-11-04": 0, + "e:2_d:2024-11-05": 0, + "e:2_d:2024-11-06": 1, + "e:2_d:2024-11-07": 0, + "e:2_d:2024-11-08": 1, + "e:2_d:2024-11-09": 0, + "e:2_d:2024-11-10": 0, + "e:2_d:2024-11-11": 0, + "e:2_d:2024-11-12": 0, + "e:2_d:2024-11-13": 0, + "e:2_d:2024-11-14": 0, + "e:2_d:2024-11-15": 0, + "e:2_d:2024-11-16": 1, + "e:2_d:2024-11-17": 0, + "e:2_d:2024-11-18": 0, + "e:2_d:2024-11-19": 0, + "e:2_d:2024-11-20": 1, + "e:2_d:2024-11-21": 0, + "e:2_d:2024-11-22": 0, + "e:2_d:2024-11-23": 0, + "e:2_d:2024-11-24": 0, + "e:2_d:2024-11-25": 0, + "e:2_d:2024-11-26": 0, + "e:2_d:2024-11-27": 0, + "e:2_d:2024-11-28": 0, + "e:2_d:2024-11-29": 0, + "e:2_d:2024-11-30": 0, + "e:3566_d:2024-11-01": 0, + "e:3566_d:2024-11-02": 0, + "e:3566_d:2024-11-03": 0, + "e:3566_d:2024-11-04": 0, + "e:3566_d:2024-11-05": 0, + "e:3566_d:2024-11-06": 0, + "e:3566_d:2024-11-07": 0, + "e:3566_d:2024-11-08": 0, + "e:3566_d:2024-11-09": 0, + "e:3566_d:2024-11-10": 0, + "e:3566_d:2024-11-11": 0, + "e:3566_d:2024-11-12": 0, + "e:3566_d:2024-11-13": 0, + "e:3566_d:2024-11-14": 0, + "e:3566_d:2024-11-15": 0, + "e:3566_d:2024-11-16": 0, + "e:3566_d:2024-11-17": 0, + "e:3566_d:2024-11-18": 0, + "e:3566_d:2024-11-19": 0, + "e:3566_d:2024-11-20": 0, + "e:3566_d:2024-11-21": 0, + "e:3566_d:2024-11-22": 0, + "e:3566_d:2024-11-23": 0, + "e:3566_d:2024-11-24": 0, + "e:3566_d:2024-11-25": 0, + "e:3566_d:2024-11-26": 0, + "e:3566_d:2024-11-27": 0, + "e:3566_d:2024-11-28": 0, + "e:3566_d:2024-11-29": 0, + "e:3566_d:2024-11-30": 0, + "e:3868_d:2024-11-01": 0, + "e:3868_d:2024-11-02": 0, + "e:3868_d:2024-11-03": 0, + "e:3868_d:2024-11-04": 1, + "e:3868_d:2024-11-05": 1, + "e:3868_d:2024-11-06": 0, + "e:3868_d:2024-11-07": 0, + "e:3868_d:2024-11-08": 1, + "e:3868_d:2024-11-09": 0, + "e:3868_d:2024-11-10": 1, + "e:3868_d:2024-11-11": 1, + "e:3868_d:2024-11-12": 1, + "e:3868_d:2024-11-13": 1, + "e:3868_d:2024-11-14": 0, + "e:3868_d:2024-11-15": 1, + "e:3868_d:2024-11-16": 0, + "e:3868_d:2024-11-17": 1, + "e:3868_d:2024-11-18": 1, + "e:3868_d:2024-11-19": 0, + "e:3868_d:2024-11-20": 0, + "e:3868_d:2024-11-21": 1, + "e:3868_d:2024-11-22": 1, + "e:3868_d:2024-11-23": 1, + "e:3868_d:2024-11-24": 1, + "e:3868_d:2024-11-25": 1, + "e:3868_d:2024-11-26": 0, + "e:3868_d:2024-11-27": 1, + "e:3868_d:2024-11-28": 1, + "e:3868_d:2024-11-29": 1, + "e:3868_d:2024-11-30": 0, + "e:3_d:2024-11-01": 0, + "e:3_d:2024-11-02": 0, + "e:3_d:2024-11-03": 0, + "e:3_d:2024-11-04": 0, + "e:3_d:2024-11-05": 0, + "e:3_d:2024-11-06": 0, + "e:3_d:2024-11-07": 0, + "e:3_d:2024-11-08": 0, + "e:3_d:2024-11-09": 0, + "e:3_d:2024-11-10": 0, + "e:3_d:2024-11-11": 0, + "e:3_d:2024-11-12": 0, + "e:3_d:2024-11-13": 0, + "e:3_d:2024-11-14": 0, + "e:3_d:2024-11-15": 0, + "e:3_d:2024-11-16": 0, + "e:3_d:2024-11-17": 0, + "e:3_d:2024-11-18": 0, + "e:3_d:2024-11-19": 0, + "e:3_d:2024-11-20": 0, + "e:3_d:2024-11-21": 0, + "e:3_d:2024-11-22": 0, + "e:3_d:2024-11-23": 0, + "e:3_d:2024-11-24": 0, + "e:3_d:2024-11-25": 0, + "e:3_d:2024-11-26": 0, + "e:3_d:2024-11-27": 0, + "e:3_d:2024-11-28": 0, + "e:3_d:2024-11-29": 0, + "e:3_d:2024-11-30": 0, + "e:4566_d:2024-11-01": 1, + "e:4566_d:2024-11-02": 0, + "e:4566_d:2024-11-03": 1, + "e:4566_d:2024-11-04": 1, + "e:4566_d:2024-11-05": 1, + "e:4566_d:2024-11-06": 0, + "e:4566_d:2024-11-07": 0, + "e:4566_d:2024-11-08": 1, + "e:4566_d:2024-11-09": 0, + "e:4566_d:2024-11-10": 1, + "e:4566_d:2024-11-11": 0, + "e:4566_d:2024-11-12": 1, + "e:4566_d:2024-11-13": 1, + "e:4566_d:2024-11-14": 1, + "e:4566_d:2024-11-15": 1, + "e:4566_d:2024-11-16": 0, + "e:4566_d:2024-11-17": 0, + "e:4566_d:2024-11-18": 0, + "e:4566_d:2024-11-19": 1, + "e:4566_d:2024-11-20": 1, + "e:4566_d:2024-11-21": 1, + "e:4566_d:2024-11-22": 0, + "e:4566_d:2024-11-23": 1, + "e:4566_d:2024-11-24": 0, + "e:4566_d:2024-11-25": 0, + "e:4566_d:2024-11-26": 1, + "e:4566_d:2024-11-27": 1, + "e:4566_d:2024-11-28": 0, + "e:4566_d:2024-11-29": 1, + "e:4566_d:2024-11-30": 1, + "e:459_d:2024-11-01": 1, + "e:459_d:2024-11-02": 0, + "e:459_d:2024-11-03": 0, + "e:459_d:2024-11-04": 0, + "e:459_d:2024-11-05": 0, + "e:459_d:2024-11-06": 0, + "e:459_d:2024-11-07": 0, + "e:459_d:2024-11-08": 0, + "e:459_d:2024-11-09": 0, + "e:459_d:2024-11-10": 0, + "e:459_d:2024-11-11": 0, + "e:459_d:2024-11-12": 0, + "e:459_d:2024-11-13": 0, + "e:459_d:2024-11-14": 1, + "e:459_d:2024-11-15": 1, + "e:459_d:2024-11-16": 0, + "e:459_d:2024-11-17": 0, + "e:459_d:2024-11-18": 0, + "e:459_d:2024-11-19": 0, + "e:459_d:2024-11-20": 0, + "e:459_d:2024-11-21": 0, + "e:459_d:2024-11-22": 0, + "e:459_d:2024-11-23": 0, + "e:459_d:2024-11-24": 0, + "e:459_d:2024-11-25": 0, + "e:459_d:2024-11-26": 0, + "e:459_d:2024-11-27": 0, + "e:459_d:2024-11-28": 0, + "e:459_d:2024-11-29": 0, + "e:459_d:2024-11-30": 1, + "e:4_d:2024-11-01": 0, + "e:4_d:2024-11-02": 0, + "e:4_d:2024-11-03": 0, + "e:4_d:2024-11-04": 0, + "e:4_d:2024-11-05": 0, + "e:4_d:2024-11-06": 0, + "e:4_d:2024-11-07": 0, + "e:4_d:2024-11-08": 0, + "e:4_d:2024-11-09": 0, + "e:4_d:2024-11-10": 0, + "e:4_d:2024-11-11": 0, + "e:4_d:2024-11-12": 0, + "e:4_d:2024-11-13": 0, + "e:4_d:2024-11-14": 0, + "e:4_d:2024-11-15": 0, + "e:4_d:2024-11-16": 0, + "e:4_d:2024-11-17": 0, + "e:4_d:2024-11-18": 0, + "e:4_d:2024-11-19": 0, + "e:4_d:2024-11-20": 0, + "e:4_d:2024-11-21": 0, + "e:4_d:2024-11-22": 0, + "e:4_d:2024-11-23": 0, + "e:4_d:2024-11-24": 0, + "e:4_d:2024-11-25": 0, + "e:4_d:2024-11-26": 0, + "e:4_d:2024-11-27": 0, + "e:4_d:2024-11-28": 0, + "e:4_d:2024-11-29": 0, + "e:4_d:2024-11-30": 0, + "e:5367_d:2024-11-01": 1, + "e:5367_d:2024-11-02": 1, + "e:5367_d:2024-11-03": 1, + "e:5367_d:2024-11-04": 0, + "e:5367_d:2024-11-05": 0, + "e:5367_d:2024-11-06": 1, + "e:5367_d:2024-11-07": 0, + "e:5367_d:2024-11-08": 0, + "e:5367_d:2024-11-09": 1, + "e:5367_d:2024-11-10": 0, + "e:5367_d:2024-11-11": 1, + "e:5367_d:2024-11-12": 1, + "e:5367_d:2024-11-13": 0, + "e:5367_d:2024-11-14": 1, + "e:5367_d:2024-11-15": 1, + "e:5367_d:2024-11-16": 1, + "e:5367_d:2024-11-17": 0, + "e:5367_d:2024-11-18": 1, + "e:5367_d:2024-11-19": 0, + "e:5367_d:2024-11-20": 1, + "e:5367_d:2024-11-21": 1, + "e:5367_d:2024-11-22": 1, + "e:5367_d:2024-11-23": 0, + "e:5367_d:2024-11-24": 1, + "e:5367_d:2024-11-25": 1, + "e:5367_d:2024-11-26": 0, + "e:5367_d:2024-11-27": 1, + "e:5367_d:2024-11-28": 1, + "e:5367_d:2024-11-29": 1, + "e:5367_d:2024-11-30": 0, + "e:5920_d:2024-11-01": 1, + "e:5920_d:2024-11-02": 0, + "e:5920_d:2024-11-03": 0, + "e:5920_d:2024-11-04": 0, + "e:5920_d:2024-11-05": 1, + "e:5920_d:2024-11-06": 1, + "e:5920_d:2024-11-07": 1, + "e:5920_d:2024-11-08": 1, + "e:5920_d:2024-11-09": 0, + "e:5920_d:2024-11-10": 1, + "e:5920_d:2024-11-11": 1, + "e:5920_d:2024-11-12": 0, + "e:5920_d:2024-11-13": 1, + "e:5920_d:2024-11-14": 1, + "e:5920_d:2024-11-15": 0, + "e:5920_d:2024-11-16": 1, + "e:5920_d:2024-11-17": 1, + "e:5920_d:2024-11-18": 0, + "e:5920_d:2024-11-19": 1, + "e:5920_d:2024-11-20": 0, + "e:5920_d:2024-11-21": 0, + "e:5920_d:2024-11-22": 1, + "e:5920_d:2024-11-23": 1, + "e:5920_d:2024-11-24": 0, + "e:5920_d:2024-11-25": 1, + "e:5920_d:2024-11-26": 0, + "e:5920_d:2024-11-27": 1, + "e:5920_d:2024-11-28": 1, + "e:5920_d:2024-11-29": 1, + "e:5920_d:2024-11-30": 0, + "e:5_d:2024-11-01": 0, + "e:5_d:2024-11-02": 0, + "e:5_d:2024-11-03": 0, + "e:5_d:2024-11-04": 0, + "e:5_d:2024-11-05": 0, + "e:5_d:2024-11-06": 0, + "e:5_d:2024-11-07": 0, + "e:5_d:2024-11-08": 0, + "e:5_d:2024-11-09": 0, + "e:5_d:2024-11-10": 0, + "e:5_d:2024-11-11": 0, + "e:5_d:2024-11-12": 0, + "e:5_d:2024-11-13": 0, + "e:5_d:2024-11-14": 0, + "e:5_d:2024-11-15": 0, + "e:5_d:2024-11-16": 0, + "e:5_d:2024-11-17": 0, + "e:5_d:2024-11-18": 0, + "e:5_d:2024-11-19": 0, + "e:5_d:2024-11-20": 0, + "e:5_d:2024-11-21": 0, + "e:5_d:2024-11-22": 0, + "e:5_d:2024-11-23": 0, + "e:5_d:2024-11-24": 0, + "e:5_d:2024-11-25": 0, + "e:5_d:2024-11-26": 0, + "e:5_d:2024-11-27": 0, + "e:5_d:2024-11-28": 0, + "e:5_d:2024-11-29": 0, + "e:5_d:2024-11-30": 0, + "e:6475_d:2024-11-01": 0, + "e:6475_d:2024-11-02": 0, + "e:6475_d:2024-11-03": 0, + "e:6475_d:2024-11-04": 0, + "e:6475_d:2024-11-05": 0, + "e:6475_d:2024-11-06": 0, + "e:6475_d:2024-11-07": 0, + "e:6475_d:2024-11-08": 0, + "e:6475_d:2024-11-09": 0, + "e:6475_d:2024-11-10": 0, + "e:6475_d:2024-11-11": 0, + "e:6475_d:2024-11-12": 0, + "e:6475_d:2024-11-13": 0, + "e:6475_d:2024-11-14": 0, + "e:6475_d:2024-11-15": 0, + "e:6475_d:2024-11-16": 0, + "e:6475_d:2024-11-17": 0, + "e:6475_d:2024-11-18": 0, + "e:6475_d:2024-11-19": 0, + "e:6475_d:2024-11-20": 0, + "e:6475_d:2024-11-21": 0, + "e:6475_d:2024-11-22": 0, + "e:6475_d:2024-11-23": 0, + "e:6475_d:2024-11-24": 0, + "e:6475_d:2024-11-25": 0, + "e:6475_d:2024-11-26": 0, + "e:6475_d:2024-11-27": 0, + "e:6475_d:2024-11-28": 0, + "e:6475_d:2024-11-29": 0, + "e:6475_d:2024-11-30": 1, + "e:6507_d:2024-11-01": 0, + "e:6507_d:2024-11-02": 1, + "e:6507_d:2024-11-03": 0, + "e:6507_d:2024-11-04": 1, + "e:6507_d:2024-11-05": 1, + "e:6507_d:2024-11-06": 1, + "e:6507_d:2024-11-07": 1, + "e:6507_d:2024-11-08": 1, + "e:6507_d:2024-11-09": 1, + "e:6507_d:2024-11-10": 1, + "e:6507_d:2024-11-11": 1, + "e:6507_d:2024-11-12": 0, + "e:6507_d:2024-11-13": 1, + "e:6507_d:2024-11-14": 0, + "e:6507_d:2024-11-15": 0, + "e:6507_d:2024-11-16": 1, + "e:6507_d:2024-11-17": 0, + "e:6507_d:2024-11-18": 0, + "e:6507_d:2024-11-19": 1, + "e:6507_d:2024-11-20": 1, + "e:6507_d:2024-11-21": 1, + "e:6507_d:2024-11-22": 1, + "e:6507_d:2024-11-23": 0, + "e:6507_d:2024-11-24": 0, + "e:6507_d:2024-11-25": 1, + "e:6507_d:2024-11-26": 1, + "e:6507_d:2024-11-27": 1, + "e:6507_d:2024-11-28": 1, + "e:6507_d:2024-11-29": 0, + "e:6507_d:2024-11-30": 1, + "e:6677_d:2024-11-01": 0, + "e:6677_d:2024-11-02": 0, + "e:6677_d:2024-11-03": 1, + "e:6677_d:2024-11-04": 1, + "e:6677_d:2024-11-05": 1, + "e:6677_d:2024-11-06": 1, + "e:6677_d:2024-11-07": 1, + "e:6677_d:2024-11-08": 0, + "e:6677_d:2024-11-09": 1, + "e:6677_d:2024-11-10": 1, + "e:6677_d:2024-11-11": 0, + "e:6677_d:2024-11-12": 1, + "e:6677_d:2024-11-13": 1, + "e:6677_d:2024-11-14": 1, + "e:6677_d:2024-11-15": 0, + "e:6677_d:2024-11-16": 0, + "e:6677_d:2024-11-17": 1, + "e:6677_d:2024-11-18": 1, + "e:6677_d:2024-11-19": 0, + "e:6677_d:2024-11-20": 0, + "e:6677_d:2024-11-21": 0, + "e:6677_d:2024-11-22": 0, + "e:6677_d:2024-11-23": 1, + "e:6677_d:2024-11-24": 1, + "e:6677_d:2024-11-25": 1, + "e:6677_d:2024-11-26": 1, + "e:6677_d:2024-11-27": 1, + "e:6677_d:2024-11-28": 1, + "e:6677_d:2024-11-29": 1, + "e:6677_d:2024-11-30": 1, + "e:6681_d:2024-11-01": 0, + "e:6681_d:2024-11-02": 1, + "e:6681_d:2024-11-03": 0, + "e:6681_d:2024-11-04": 0, + "e:6681_d:2024-11-05": 1, + "e:6681_d:2024-11-06": 0, + "e:6681_d:2024-11-07": 1, + "e:6681_d:2024-11-08": 0, + "e:6681_d:2024-11-09": 0, + "e:6681_d:2024-11-10": 0, + "e:6681_d:2024-11-11": 0, + "e:6681_d:2024-11-12": 1, + "e:6681_d:2024-11-13": 1, + "e:6681_d:2024-11-14": 0, + "e:6681_d:2024-11-15": 1, + "e:6681_d:2024-11-16": 0, + "e:6681_d:2024-11-17": 0, + "e:6681_d:2024-11-18": 0, + "e:6681_d:2024-11-19": 0, + "e:6681_d:2024-11-20": 0, + "e:6681_d:2024-11-21": 0, + "e:6681_d:2024-11-22": 0, + "e:6681_d:2024-11-23": 0, + "e:6681_d:2024-11-24": 0, + "e:6681_d:2024-11-25": 0, + "e:6681_d:2024-11-26": 0, + "e:6681_d:2024-11-27": 0, + "e:6681_d:2024-11-28": 0, + "e:6681_d:2024-11-29": 0, + "e:6681_d:2024-11-30": 0, + "e:6715_d:2024-11-01": 0, + "e:6715_d:2024-11-02": 0, + "e:6715_d:2024-11-03": 0, + "e:6715_d:2024-11-04": 0, + "e:6715_d:2024-11-05": 0, + "e:6715_d:2024-11-06": 1, + "e:6715_d:2024-11-07": 0, + "e:6715_d:2024-11-08": 0, + "e:6715_d:2024-11-09": 0, + "e:6715_d:2024-11-10": 0, + "e:6715_d:2024-11-11": 0, + "e:6715_d:2024-11-12": 0, + "e:6715_d:2024-11-13": 0, + "e:6715_d:2024-11-14": 0, + "e:6715_d:2024-11-15": 0, + "e:6715_d:2024-11-16": 0, + "e:6715_d:2024-11-17": 0, + "e:6715_d:2024-11-18": 0, + "e:6715_d:2024-11-19": 0, + "e:6715_d:2024-11-20": 0, + "e:6715_d:2024-11-21": 0, + "e:6715_d:2024-11-22": 0, + "e:6715_d:2024-11-23": 0, + "e:6715_d:2024-11-24": 0, + "e:6715_d:2024-11-25": 0, + "e:6715_d:2024-11-26": 0, + "e:6715_d:2024-11-27": 0, + "e:6715_d:2024-11-28": 0, + "e:6715_d:2024-11-29": 0, + "e:6715_d:2024-11-30": 0, + "e:6836_d:2024-11-01": 1, + "e:6836_d:2024-11-02": 0, + "e:6836_d:2024-11-03": 1, + "e:6836_d:2024-11-04": 1, + "e:6836_d:2024-11-05": 1, + "e:6836_d:2024-11-06": 0, + "e:6836_d:2024-11-07": 0, + "e:6836_d:2024-11-08": 0, + "e:6836_d:2024-11-09": 1, + "e:6836_d:2024-11-10": 1, + "e:6836_d:2024-11-11": 0, + "e:6836_d:2024-11-12": 0, + "e:6836_d:2024-11-13": 1, + "e:6836_d:2024-11-14": 1, + "e:6836_d:2024-11-15": 1, + "e:6836_d:2024-11-16": 1, + "e:6836_d:2024-11-17": 1, + "e:6836_d:2024-11-18": 1, + "e:6836_d:2024-11-19": 1, + "e:6836_d:2024-11-20": 0, + "e:6836_d:2024-11-21": 0, + "e:6836_d:2024-11-22": 1, + "e:6836_d:2024-11-23": 1, + "e:6836_d:2024-11-24": 1, + "e:6836_d:2024-11-25": 0, + "e:6836_d:2024-11-26": 0, + "e:6836_d:2024-11-27": 1, + "e:6836_d:2024-11-28": 0, + "e:6836_d:2024-11-29": 1, + "e:6836_d:2024-11-30": 1, + "e:6928_d:2024-11-01": 1, + "e:6928_d:2024-11-02": 1, + "e:6928_d:2024-11-03": 1, + "e:6928_d:2024-11-04": 1, + "e:6928_d:2024-11-05": 0, + "e:6928_d:2024-11-06": 0, + "e:6928_d:2024-11-07": 1, + "e:6928_d:2024-11-08": 1, + "e:6928_d:2024-11-09": 0, + "e:6928_d:2024-11-10": 1, + "e:6928_d:2024-11-11": 0, + "e:6928_d:2024-11-12": 1, + "e:6928_d:2024-11-13": 1, + "e:6928_d:2024-11-14": 0, + "e:6928_d:2024-11-15": 1, + "e:6928_d:2024-11-16": 1, + "e:6928_d:2024-11-17": 1, + "e:6928_d:2024-11-18": 1, + "e:6928_d:2024-11-19": 1, + "e:6928_d:2024-11-20": 0, + "e:6928_d:2024-11-21": 0, + "e:6928_d:2024-11-22": 1, + "e:6928_d:2024-11-23": 1, + "e:6928_d:2024-11-24": 0, + "e:6928_d:2024-11-25": 0, + "e:6928_d:2024-11-26": 1, + "e:6928_d:2024-11-27": 0, + "e:6928_d:2024-11-28": 1, + "e:6928_d:2024-11-29": 1, + "e:6928_d:2024-11-30": 1, + "e:6_d:2024-11-01": 1, + "e:6_d:2024-11-02": 0, + "e:6_d:2024-11-03": 0, + "e:6_d:2024-11-04": 0, + "e:6_d:2024-11-05": 0, + "e:6_d:2024-11-06": 1, + "e:6_d:2024-11-07": 0, + "e:6_d:2024-11-08": 1, + "e:6_d:2024-11-09": 0, + "e:6_d:2024-11-10": 1, + "e:6_d:2024-11-11": 0, + "e:6_d:2024-11-12": 0, + "e:6_d:2024-11-13": 0, + "e:6_d:2024-11-14": 1, + "e:6_d:2024-11-15": 0, + "e:6_d:2024-11-16": 1, + "e:6_d:2024-11-17": 1, + "e:6_d:2024-11-18": 0, + "e:6_d:2024-11-19": 1, + "e:6_d:2024-11-20": 0, + "e:6_d:2024-11-21": 0, + "e:6_d:2024-11-22": 0, + "e:6_d:2024-11-23": 0, + "e:6_d:2024-11-24": 1, + "e:6_d:2024-11-25": 0, + "e:6_d:2024-11-26": 0, + "e:6_d:2024-11-27": 0, + "e:6_d:2024-11-28": 0, + "e:6_d:2024-11-29": 0, + "e:6_d:2024-11-30": 0, + "e:7496_d:2024-11-01": 0, + "e:7496_d:2024-11-02": 0, + "e:7496_d:2024-11-03": 0, + "e:7496_d:2024-11-04": 0, + "e:7496_d:2024-11-05": 0, + "e:7496_d:2024-11-06": 0, + "e:7496_d:2024-11-07": 0, + "e:7496_d:2024-11-08": 0, + "e:7496_d:2024-11-09": 0, + "e:7496_d:2024-11-10": 0, + "e:7496_d:2024-11-11": 0, + "e:7496_d:2024-11-12": 0, + "e:7496_d:2024-11-13": 0, + "e:7496_d:2024-11-14": 0, + "e:7496_d:2024-11-15": 1, + "e:7496_d:2024-11-16": 0, + "e:7496_d:2024-11-17": 1, + "e:7496_d:2024-11-18": 1, + "e:7496_d:2024-11-19": 1, + "e:7496_d:2024-11-20": 1, + "e:7496_d:2024-11-21": 0, + "e:7496_d:2024-11-22": 0, + "e:7496_d:2024-11-23": 0, + "e:7496_d:2024-11-24": 0, + "e:7496_d:2024-11-25": 1, + "e:7496_d:2024-11-26": 1, + "e:7496_d:2024-11-27": 1, + "e:7496_d:2024-11-28": 1, + "e:7496_d:2024-11-29": 1, + "e:7496_d:2024-11-30": 0, + "e:7603_d:2024-11-01": 1, + "e:7603_d:2024-11-02": 1, + "e:7603_d:2024-11-03": 1, + "e:7603_d:2024-11-04": 1, + "e:7603_d:2024-11-05": 0, + "e:7603_d:2024-11-06": 1, + "e:7603_d:2024-11-07": 1, + "e:7603_d:2024-11-08": 1, + "e:7603_d:2024-11-09": 1, + "e:7603_d:2024-11-10": 0, + "e:7603_d:2024-11-11": 0, + "e:7603_d:2024-11-12": 1, + "e:7603_d:2024-11-13": 0, + "e:7603_d:2024-11-14": 0, + "e:7603_d:2024-11-15": 1, + "e:7603_d:2024-11-16": 0, + "e:7603_d:2024-11-17": 1, + "e:7603_d:2024-11-18": 1, + "e:7603_d:2024-11-19": 1, + "e:7603_d:2024-11-20": 1, + "e:7603_d:2024-11-21": 1, + "e:7603_d:2024-11-22": 1, + "e:7603_d:2024-11-23": 1, + "e:7603_d:2024-11-24": 0, + "e:7603_d:2024-11-25": 1, + "e:7603_d:2024-11-26": 0, + "e:7603_d:2024-11-27": 1, + "e:7603_d:2024-11-28": 0, + "e:7603_d:2024-11-29": 1, + "e:7603_d:2024-11-30": 0, + "e:7741_d:2024-11-01": 0, + "e:7741_d:2024-11-02": 0, + "e:7741_d:2024-11-03": 0, + "e:7741_d:2024-11-04": 0, + "e:7741_d:2024-11-05": 0, + "e:7741_d:2024-11-06": 0, + "e:7741_d:2024-11-07": 0, + "e:7741_d:2024-11-08": 0, + "e:7741_d:2024-11-09": 0, + "e:7741_d:2024-11-10": 0, + "e:7741_d:2024-11-11": 0, + "e:7741_d:2024-11-12": 0, + "e:7741_d:2024-11-13": 0, + "e:7741_d:2024-11-14": 0, + "e:7741_d:2024-11-15": 0, + "e:7741_d:2024-11-16": 0, + "e:7741_d:2024-11-17": 0, + "e:7741_d:2024-11-18": 0, + "e:7741_d:2024-11-19": 0, + "e:7741_d:2024-11-20": 0, + "e:7741_d:2024-11-21": 0, + "e:7741_d:2024-11-22": 0, + "e:7741_d:2024-11-23": 0, + "e:7741_d:2024-11-24": 0, + "e:7741_d:2024-11-25": 0, + "e:7741_d:2024-11-26": 0, + "e:7741_d:2024-11-27": 1, + "e:7741_d:2024-11-28": 1, + "e:7741_d:2024-11-29": 0, + "e:7741_d:2024-11-30": 0, + "e:7752_d:2024-11-01": 1, + "e:7752_d:2024-11-02": 1, + "e:7752_d:2024-11-03": 0, + "e:7752_d:2024-11-04": 0, + "e:7752_d:2024-11-05": 1, + "e:7752_d:2024-11-06": 1, + "e:7752_d:2024-11-07": 1, + "e:7752_d:2024-11-08": 0, + "e:7752_d:2024-11-09": 0, + "e:7752_d:2024-11-10": 0, + "e:7752_d:2024-11-11": 1, + "e:7752_d:2024-11-12": 0, + "e:7752_d:2024-11-13": 0, + "e:7752_d:2024-11-14": 0, + "e:7752_d:2024-11-15": 1, + "e:7752_d:2024-11-16": 0, + "e:7752_d:2024-11-17": 0, + "e:7752_d:2024-11-18": 1, + "e:7752_d:2024-11-19": 0, + "e:7752_d:2024-11-20": 1, + "e:7752_d:2024-11-21": 1, + "e:7752_d:2024-11-22": 1, + "e:7752_d:2024-11-23": 0, + "e:7752_d:2024-11-24": 1, + "e:7752_d:2024-11-25": 0, + "e:7752_d:2024-11-26": 1, + "e:7752_d:2024-11-27": 1, + "e:7752_d:2024-11-28": 0, + "e:7752_d:2024-11-29": 0, + "e:7752_d:2024-11-30": 1, + "e:7770_d:2024-11-01": 0, + "e:7770_d:2024-11-02": 0, + "e:7770_d:2024-11-03": 0, + "e:7770_d:2024-11-04": 0, + "e:7770_d:2024-11-05": 0, + "e:7770_d:2024-11-06": 0, + "e:7770_d:2024-11-07": 1, + "e:7770_d:2024-11-08": 0, + "e:7770_d:2024-11-09": 0, + "e:7770_d:2024-11-10": 0, + "e:7770_d:2024-11-11": 1, + "e:7770_d:2024-11-12": 0, + "e:7770_d:2024-11-13": 0, + "e:7770_d:2024-11-14": 0, + "e:7770_d:2024-11-15": 0, + "e:7770_d:2024-11-16": 0, + "e:7770_d:2024-11-17": 0, + "e:7770_d:2024-11-18": 1, + "e:7770_d:2024-11-19": 0, + "e:7770_d:2024-11-20": 0, + "e:7770_d:2024-11-21": 1, + "e:7770_d:2024-11-22": 0, + "e:7770_d:2024-11-23": 1, + "e:7770_d:2024-11-24": 1, + "e:7770_d:2024-11-25": 0, + "e:7770_d:2024-11-26": 1, + "e:7770_d:2024-11-27": 1, + "e:7770_d:2024-11-28": 0, + "e:7770_d:2024-11-29": 1, + "e:7770_d:2024-11-30": 0, + "e:7796_d:2024-11-01": 0, + "e:7796_d:2024-11-02": 0, + "e:7796_d:2024-11-03": 0, + "e:7796_d:2024-11-04": 0, + "e:7796_d:2024-11-05": 0, + "e:7796_d:2024-11-06": 0, + "e:7796_d:2024-11-07": 1, + "e:7796_d:2024-11-08": 1, + "e:7796_d:2024-11-09": 0, + "e:7796_d:2024-11-10": 0, + "e:7796_d:2024-11-11": 1, + "e:7796_d:2024-11-12": 0, + "e:7796_d:2024-11-13": 0, + "e:7796_d:2024-11-14": 1, + "e:7796_d:2024-11-15": 0, + "e:7796_d:2024-11-16": 0, + "e:7796_d:2024-11-17": 1, + "e:7796_d:2024-11-18": 0, + "e:7796_d:2024-11-19": 0, + "e:7796_d:2024-11-20": 0, + "e:7796_d:2024-11-21": 0, + "e:7796_d:2024-11-22": 1, + "e:7796_d:2024-11-23": 0, + "e:7796_d:2024-11-24": 0, + "e:7796_d:2024-11-25": 1, + "e:7796_d:2024-11-26": 1, + "e:7796_d:2024-11-27": 0, + "e:7796_d:2024-11-28": 0, + "e:7796_d:2024-11-29": 0, + "e:7796_d:2024-11-30": 1, + "e:7835_d:2024-11-01": 0, + "e:7835_d:2024-11-02": 0, + "e:7835_d:2024-11-03": 0, + "e:7835_d:2024-11-04": 0, + "e:7835_d:2024-11-05": 0, + "e:7835_d:2024-11-06": 0, + "e:7835_d:2024-11-07": 0, + "e:7835_d:2024-11-08": 1, + "e:7835_d:2024-11-09": 1, + "e:7835_d:2024-11-10": 0, + "e:7835_d:2024-11-11": 1, + "e:7835_d:2024-11-12": 1, + "e:7835_d:2024-11-13": 0, + "e:7835_d:2024-11-14": 0, + "e:7835_d:2024-11-15": 0, + "e:7835_d:2024-11-16": 0, + "e:7835_d:2024-11-17": 1, + "e:7835_d:2024-11-18": 1, + "e:7835_d:2024-11-19": 1, + "e:7835_d:2024-11-20": 0, + "e:7835_d:2024-11-21": 0, + "e:7835_d:2024-11-22": 0, + "e:7835_d:2024-11-23": 0, + "e:7835_d:2024-11-24": 0, + "e:7835_d:2024-11-25": 0, + "e:7835_d:2024-11-26": 0, + "e:7835_d:2024-11-27": 0, + "e:7835_d:2024-11-28": 1, + "e:7835_d:2024-11-29": 1, + "e:7835_d:2024-11-30": 0, + "e:7848_d:2024-11-01": 1, + "e:7848_d:2024-11-02": 0, + "e:7848_d:2024-11-03": 1, + "e:7848_d:2024-11-04": 1, + "e:7848_d:2024-11-05": 1, + "e:7848_d:2024-11-06": 1, + "e:7848_d:2024-11-07": 1, + "e:7848_d:2024-11-08": 0, + "e:7848_d:2024-11-09": 0, + "e:7848_d:2024-11-10": 1, + "e:7848_d:2024-11-11": 0, + "e:7848_d:2024-11-12": 1, + "e:7848_d:2024-11-13": 0, + "e:7848_d:2024-11-14": 0, + "e:7848_d:2024-11-15": 1, + "e:7848_d:2024-11-16": 1, + "e:7848_d:2024-11-17": 1, + "e:7848_d:2024-11-18": 0, + "e:7848_d:2024-11-19": 1, + "e:7848_d:2024-11-20": 1, + "e:7848_d:2024-11-21": 1, + "e:7848_d:2024-11-22": 1, + "e:7848_d:2024-11-23": 0, + "e:7848_d:2024-11-24": 1, + "e:7848_d:2024-11-25": 1, + "e:7848_d:2024-11-26": 0, + "e:7848_d:2024-11-27": 0, + "e:7848_d:2024-11-28": 1, + "e:7848_d:2024-11-29": 1, + "e:7848_d:2024-11-30": 0, + "e:7877_d:2024-11-01": 0, + "e:7877_d:2024-11-02": 0, + "e:7877_d:2024-11-03": 0, + "e:7877_d:2024-11-04": 0, + "e:7877_d:2024-11-05": 0, + "e:7877_d:2024-11-06": 0, + "e:7877_d:2024-11-07": 1, + "e:7877_d:2024-11-08": 0, + "e:7877_d:2024-11-09": 1, + "e:7877_d:2024-11-10": 0, + "e:7877_d:2024-11-11": 1, + "e:7877_d:2024-11-12": 1, + "e:7877_d:2024-11-13": 1, + "e:7877_d:2024-11-14": 1, + "e:7877_d:2024-11-15": 1, + "e:7877_d:2024-11-16": 1, + "e:7877_d:2024-11-17": 0, + "e:7877_d:2024-11-18": 1, + "e:7877_d:2024-11-19": 0, + "e:7877_d:2024-11-20": 1, + "e:7877_d:2024-11-21": 0, + "e:7877_d:2024-11-22": 0, + "e:7877_d:2024-11-23": 0, + "e:7877_d:2024-11-24": 1, + "e:7877_d:2024-11-25": 1, + "e:7877_d:2024-11-26": 0, + "e:7877_d:2024-11-27": 0, + "e:7877_d:2024-11-28": 1, + "e:7877_d:2024-11-29": 0, + "e:7877_d:2024-11-30": 0, + "e:790_d:2024-11-01": 0, + "e:790_d:2024-11-02": 0, + "e:790_d:2024-11-03": 0, + "e:790_d:2024-11-04": 0, + "e:790_d:2024-11-05": 0, + "e:790_d:2024-11-06": 0, + "e:790_d:2024-11-07": 0, + "e:790_d:2024-11-08": 0, + "e:790_d:2024-11-09": 0, + "e:790_d:2024-11-10": 0, + "e:790_d:2024-11-11": 0, + "e:790_d:2024-11-12": 0, + "e:790_d:2024-11-13": 0, + "e:790_d:2024-11-14": 0, + "e:790_d:2024-11-15": 0, + "e:790_d:2024-11-16": 0, + "e:790_d:2024-11-17": 0, + "e:790_d:2024-11-18": 0, + "e:790_d:2024-11-19": 0, + "e:790_d:2024-11-20": 0, + "e:790_d:2024-11-21": 0, + "e:790_d:2024-11-22": 0, + "e:790_d:2024-11-23": 0, + "e:790_d:2024-11-24": 0, + "e:790_d:2024-11-25": 0, + "e:790_d:2024-11-26": 0, + "e:790_d:2024-11-27": 0, + "e:790_d:2024-11-28": 0, + "e:790_d:2024-11-29": 0, + "e:790_d:2024-11-30": 0, + "e:7919_d:2024-11-01": 0, + "e:7919_d:2024-11-02": 1, + "e:7919_d:2024-11-03": 1, + "e:7919_d:2024-11-04": 1, + "e:7919_d:2024-11-05": 1, + "e:7919_d:2024-11-06": 0, + "e:7919_d:2024-11-07": 1, + "e:7919_d:2024-11-08": 1, + "e:7919_d:2024-11-09": 0, + "e:7919_d:2024-11-10": 1, + "e:7919_d:2024-11-11": 1, + "e:7919_d:2024-11-12": 0, + "e:7919_d:2024-11-13": 0, + "e:7919_d:2024-11-14": 0, + "e:7919_d:2024-11-15": 1, + "e:7919_d:2024-11-16": 1, + "e:7919_d:2024-11-17": 1, + "e:7919_d:2024-11-18": 1, + "e:7919_d:2024-11-19": 0, + "e:7919_d:2024-11-20": 1, + "e:7919_d:2024-11-21": 1, + "e:7919_d:2024-11-22": 0, + "e:7919_d:2024-11-23": 1, + "e:7919_d:2024-11-24": 0, + "e:7919_d:2024-11-25": 1, + "e:7919_d:2024-11-26": 1, + "e:7919_d:2024-11-27": 1, + "e:7919_d:2024-11-28": 0, + "e:7919_d:2024-11-29": 1, + "e:7919_d:2024-11-30": 0, + "e:791_d:2024-11-01": 0, + "e:791_d:2024-11-02": 1, + "e:791_d:2024-11-03": 0, + "e:791_d:2024-11-04": 0, + "e:791_d:2024-11-05": 0, + "e:791_d:2024-11-06": 1, + "e:791_d:2024-11-07": 1, + "e:791_d:2024-11-08": 0, + "e:791_d:2024-11-09": 1, + "e:791_d:2024-11-10": 0, + "e:791_d:2024-11-11": 1, + "e:791_d:2024-11-12": 1, + "e:791_d:2024-11-13": 1, + "e:791_d:2024-11-14": 1, + "e:791_d:2024-11-15": 1, + "e:791_d:2024-11-16": 0, + "e:791_d:2024-11-17": 1, + "e:791_d:2024-11-18": 0, + "e:791_d:2024-11-19": 1, + "e:791_d:2024-11-20": 1, + "e:791_d:2024-11-21": 0, + "e:791_d:2024-11-22": 0, + "e:791_d:2024-11-23": 1, + "e:791_d:2024-11-24": 0, + "e:791_d:2024-11-25": 0, + "e:791_d:2024-11-26": 1, + "e:791_d:2024-11-27": 1, + "e:791_d:2024-11-28": 1, + "e:791_d:2024-11-29": 0, + "e:791_d:2024-11-30": 0, + "e:7990_d:2024-11-01": 0, + "e:7990_d:2024-11-02": 0, + "e:7990_d:2024-11-03": 0, + "e:7990_d:2024-11-04": 0, + "e:7990_d:2024-11-05": 0, + "e:7990_d:2024-11-06": 0, + "e:7990_d:2024-11-07": 0, + "e:7990_d:2024-11-08": 0, + "e:7990_d:2024-11-09": 0, + "e:7990_d:2024-11-10": 0, + "e:7990_d:2024-11-11": 0, + "e:7990_d:2024-11-12": 0, + "e:7990_d:2024-11-13": 0, + "e:7990_d:2024-11-14": 0, + "e:7990_d:2024-11-15": 0, + "e:7990_d:2024-11-16": 0, + "e:7990_d:2024-11-17": 0, + "e:7990_d:2024-11-18": 0, + "e:7990_d:2024-11-19": 0, + "e:7990_d:2024-11-20": 0, + "e:7990_d:2024-11-21": 0, + "e:7990_d:2024-11-22": 0, + "e:7990_d:2024-11-23": 0, + "e:7990_d:2024-11-24": 0, + "e:7990_d:2024-11-25": 0, + "e:7990_d:2024-11-26": 0, + "e:7990_d:2024-11-27": 0, + "e:7990_d:2024-11-28": 0, + "e:7990_d:2024-11-29": 0, + "e:7990_d:2024-11-30": 0, + "e:7_d:2024-11-01": 0, + "e:7_d:2024-11-02": 1, + "e:7_d:2024-11-03": 1, + "e:7_d:2024-11-04": 1, + "e:7_d:2024-11-05": 0, + "e:7_d:2024-11-06": 0, + "e:7_d:2024-11-07": 0, + "e:7_d:2024-11-08": 0, + "e:7_d:2024-11-09": 1, + "e:7_d:2024-11-10": 0, + "e:7_d:2024-11-11": 1, + "e:7_d:2024-11-12": 0, + "e:7_d:2024-11-13": 1, + "e:7_d:2024-11-14": 0, + "e:7_d:2024-11-15": 0, + "e:7_d:2024-11-16": 0, + "e:7_d:2024-11-17": 1, + "e:7_d:2024-11-18": 0, + "e:7_d:2024-11-19": 0, + "e:7_d:2024-11-20": 1, + "e:7_d:2024-11-21": 0, + "e:7_d:2024-11-22": 0, + "e:7_d:2024-11-23": 1, + "e:7_d:2024-11-24": 0, + "e:7_d:2024-11-25": 1, + "e:7_d:2024-11-26": 0, + "e:7_d:2024-11-27": 0, + "e:7_d:2024-11-28": 0, + "e:7_d:2024-11-29": 0, + "e:7_d:2024-11-30": 0, + "e:822_d:2024-11-01": 1, + "e:822_d:2024-11-02": 1, + "e:822_d:2024-11-03": 1, + "e:822_d:2024-11-04": 0, + "e:822_d:2024-11-05": 0, + "e:822_d:2024-11-06": 1, + "e:822_d:2024-11-07": 1, + "e:822_d:2024-11-08": 1, + "e:822_d:2024-11-09": 0, + "e:822_d:2024-11-10": 0, + "e:822_d:2024-11-11": 1, + "e:822_d:2024-11-12": 1, + "e:822_d:2024-11-13": 1, + "e:822_d:2024-11-14": 1, + "e:822_d:2024-11-15": 0, + "e:822_d:2024-11-16": 0, + "e:822_d:2024-11-17": 0, + "e:822_d:2024-11-18": 0, + "e:822_d:2024-11-19": 0, + "e:822_d:2024-11-20": 1, + "e:822_d:2024-11-21": 1, + "e:822_d:2024-11-22": 1, + "e:822_d:2024-11-23": 1, + "e:822_d:2024-11-24": 1, + "e:822_d:2024-11-25": 0, + "e:822_d:2024-11-26": 0, + "e:822_d:2024-11-27": 0, + "e:822_d:2024-11-28": 1, + "e:822_d:2024-11-29": 1, + "e:822_d:2024-11-30": 1, + "e:839_d:2024-11-01": 0, + "e:839_d:2024-11-02": 0, + "e:839_d:2024-11-03": 0, + "e:839_d:2024-11-04": 0, + "e:839_d:2024-11-05": 1, + "e:839_d:2024-11-06": 0, + "e:839_d:2024-11-07": 0, + "e:839_d:2024-11-08": 0, + "e:839_d:2024-11-09": 0, + "e:839_d:2024-11-10": 1, + "e:839_d:2024-11-11": 1, + "e:839_d:2024-11-12": 0, + "e:839_d:2024-11-13": 0, + "e:839_d:2024-11-14": 0, + "e:839_d:2024-11-15": 1, + "e:839_d:2024-11-16": 0, + "e:839_d:2024-11-17": 0, + "e:839_d:2024-11-18": 0, + "e:839_d:2024-11-19": 1, + "e:839_d:2024-11-20": 1, + "e:839_d:2024-11-21": 0, + "e:839_d:2024-11-22": 0, + "e:839_d:2024-11-23": 0, + "e:839_d:2024-11-24": 0, + "e:839_d:2024-11-25": 0, + "e:839_d:2024-11-26": 1, + "e:839_d:2024-11-27": 1, + "e:839_d:2024-11-28": 0, + "e:839_d:2024-11-29": 0, + "e:839_d:2024-11-30": 0, + "e:8_d:2024-11-01": 0, + "e:8_d:2024-11-02": 0, + "e:8_d:2024-11-03": 0, + "e:8_d:2024-11-04": 1, + "e:8_d:2024-11-05": 1, + "e:8_d:2024-11-06": 0, + "e:8_d:2024-11-07": 0, + "e:8_d:2024-11-08": 0, + "e:8_d:2024-11-09": 1, + "e:8_d:2024-11-10": 1, + "e:8_d:2024-11-11": 0, + "e:8_d:2024-11-12": 0, + "e:8_d:2024-11-13": 0, + "e:8_d:2024-11-14": 1, + "e:8_d:2024-11-15": 0, + "e:8_d:2024-11-16": 1, + "e:8_d:2024-11-17": 0, + "e:8_d:2024-11-18": 0, + "e:8_d:2024-11-19": 0, + "e:8_d:2024-11-20": 1, + "e:8_d:2024-11-21": 1, + "e:8_d:2024-11-22": 0, + "e:8_d:2024-11-23": 1, + "e:8_d:2024-11-24": 0, + "e:8_d:2024-11-25": 0, + "e:8_d:2024-11-26": 0, + "e:8_d:2024-11-27": 1, + "e:8_d:2024-11-28": 0, + "e:8_d:2024-11-29": 0, + "e:8_d:2024-11-30": 1, + "e:914_d:2024-11-01": 0, + "e:914_d:2024-11-02": 1, + "e:914_d:2024-11-03": 0, + "e:914_d:2024-11-04": 0, + "e:914_d:2024-11-05": 1, + "e:914_d:2024-11-06": 1, + "e:914_d:2024-11-07": 1, + "e:914_d:2024-11-08": 1, + "e:914_d:2024-11-09": 0, + "e:914_d:2024-11-10": 0, + "e:914_d:2024-11-11": 0, + "e:914_d:2024-11-12": 0, + "e:914_d:2024-11-13": 0, + "e:914_d:2024-11-14": 0, + "e:914_d:2024-11-15": 1, + "e:914_d:2024-11-16": 0, + "e:914_d:2024-11-17": 0, + "e:914_d:2024-11-18": 0, + "e:914_d:2024-11-19": 1, + "e:914_d:2024-11-20": 0, + "e:914_d:2024-11-21": 0, + "e:914_d:2024-11-22": 0, + "e:914_d:2024-11-23": 0, + "e:914_d:2024-11-24": 0, + "e:914_d:2024-11-25": 0, + "e:914_d:2024-11-26": 0, + "e:914_d:2024-11-27": 0, + "e:914_d:2024-11-28": 0, + "e:914_d:2024-11-29": 0, + "e:914_d:2024-11-30": 0, + "e:917_d:2024-11-01": 1, + "e:917_d:2024-11-02": 0, + "e:917_d:2024-11-03": 1, + "e:917_d:2024-11-04": 0, + "e:917_d:2024-11-05": 1, + "e:917_d:2024-11-06": 1, + "e:917_d:2024-11-07": 0, + "e:917_d:2024-11-08": 1, + "e:917_d:2024-11-09": 0, + "e:917_d:2024-11-10": 0, + "e:917_d:2024-11-11": 0, + "e:917_d:2024-11-12": 1, + "e:917_d:2024-11-13": 1, + "e:917_d:2024-11-14": 1, + "e:917_d:2024-11-15": 1, + "e:917_d:2024-11-16": 0, + "e:917_d:2024-11-17": 0, + "e:917_d:2024-11-18": 0, + "e:917_d:2024-11-19": 1, + "e:917_d:2024-11-20": 1, + "e:917_d:2024-11-21": 0, + "e:917_d:2024-11-22": 0, + "e:917_d:2024-11-23": 0, + "e:917_d:2024-11-24": 1, + "e:917_d:2024-11-25": 0, + "e:917_d:2024-11-26": 1, + "e:917_d:2024-11-27": 0, + "e:917_d:2024-11-28": 1, + "e:917_d:2024-11-29": 0, + "e:917_d:2024-11-30": 1, + "e:921_d:2024-11-01": 0, + "e:921_d:2024-11-02": 0, + "e:921_d:2024-11-03": 0, + "e:921_d:2024-11-04": 0, + "e:921_d:2024-11-05": 0, + "e:921_d:2024-11-06": 0, + "e:921_d:2024-11-07": 0, + "e:921_d:2024-11-08": 0, + "e:921_d:2024-11-09": 0, + "e:921_d:2024-11-10": 0, + "e:921_d:2024-11-11": 0, + "e:921_d:2024-11-12": 0, + "e:921_d:2024-11-13": 0, + "e:921_d:2024-11-14": 0, + "e:921_d:2024-11-15": 0, + "e:921_d:2024-11-16": 0, + "e:921_d:2024-11-17": 0, + "e:921_d:2024-11-18": 0, + "e:921_d:2024-11-19": 0, + "e:921_d:2024-11-20": 0, + "e:921_d:2024-11-21": 0, + "e:921_d:2024-11-22": 0, + "e:921_d:2024-11-23": 0, + "e:921_d:2024-11-24": 0, + "e:921_d:2024-11-25": 0, + "e:921_d:2024-11-26": 0, + "e:921_d:2024-11-27": 0, + "e:921_d:2024-11-28": 0, + "e:921_d:2024-11-29": 0, + "e:921_d:2024-11-30": 0, + "e:924_d:2024-11-01": 0, + "e:924_d:2024-11-02": 0, + "e:924_d:2024-11-03": 0, + "e:924_d:2024-11-04": 0, + "e:924_d:2024-11-05": 0, + "e:924_d:2024-11-06": 0, + "e:924_d:2024-11-07": 0, + "e:924_d:2024-11-08": 0, + "e:924_d:2024-11-09": 1, + "e:924_d:2024-11-10": 0, + "e:924_d:2024-11-11": 0, + "e:924_d:2024-11-12": 0, + "e:924_d:2024-11-13": 1, + "e:924_d:2024-11-14": 0, + "e:924_d:2024-11-15": 0, + "e:924_d:2024-11-16": 0, + "e:924_d:2024-11-17": 0, + "e:924_d:2024-11-18": 1, + "e:924_d:2024-11-19": 0, + "e:924_d:2024-11-20": 0, + "e:924_d:2024-11-21": 0, + "e:924_d:2024-11-22": 0, + "e:924_d:2024-11-23": 0, + "e:924_d:2024-11-24": 0, + "e:924_d:2024-11-25": 0, + "e:924_d:2024-11-26": 1, + "e:924_d:2024-11-27": 1, + "e:924_d:2024-11-28": 0, + "e:924_d:2024-11-29": 0, + "e:924_d:2024-11-30": 0, + "e:925_d:2024-11-01": 0, + "e:925_d:2024-11-02": 0, + "e:925_d:2024-11-03": 0, + "e:925_d:2024-11-04": 1, + "e:925_d:2024-11-05": 0, + "e:925_d:2024-11-06": 0, + "e:925_d:2024-11-07": 0, + "e:925_d:2024-11-08": 0, + "e:925_d:2024-11-09": 0, + "e:925_d:2024-11-10": 0, + "e:925_d:2024-11-11": 1, + "e:925_d:2024-11-12": 1, + "e:925_d:2024-11-13": 0, + "e:925_d:2024-11-14": 1, + "e:925_d:2024-11-15": 0, + "e:925_d:2024-11-16": 0, + "e:925_d:2024-11-17": 0, + "e:925_d:2024-11-18": 0, + "e:925_d:2024-11-19": 0, + "e:925_d:2024-11-20": 1, + "e:925_d:2024-11-21": 0, + "e:925_d:2024-11-22": 1, + "e:925_d:2024-11-23": 0, + "e:925_d:2024-11-24": 0, + "e:925_d:2024-11-25": 0, + "e:925_d:2024-11-26": 1, + "e:925_d:2024-11-27": 0, + "e:925_d:2024-11-28": 1, + "e:925_d:2024-11-29": 0, + "e:925_d:2024-11-30": 1, + "e:927_d:2024-11-01": 0, + "e:927_d:2024-11-02": 0, + "e:927_d:2024-11-03": 1, + "e:927_d:2024-11-04": 0, + "e:927_d:2024-11-05": 0, + "e:927_d:2024-11-06": 0, + "e:927_d:2024-11-07": 1, + "e:927_d:2024-11-08": 1, + "e:927_d:2024-11-09": 1, + "e:927_d:2024-11-10": 1, + "e:927_d:2024-11-11": 1, + "e:927_d:2024-11-12": 1, + "e:927_d:2024-11-13": 1, + "e:927_d:2024-11-14": 0, + "e:927_d:2024-11-15": 0, + "e:927_d:2024-11-16": 1, + "e:927_d:2024-11-17": 1, + "e:927_d:2024-11-18": 0, + "e:927_d:2024-11-19": 1, + "e:927_d:2024-11-20": 0, + "e:927_d:2024-11-21": 1, + "e:927_d:2024-11-22": 1, + "e:927_d:2024-11-23": 1, + "e:927_d:2024-11-24": 1, + "e:927_d:2024-11-25": 1, + "e:927_d:2024-11-26": 1, + "e:927_d:2024-11-27": 0, + "e:927_d:2024-11-28": 0, + "e:927_d:2024-11-29": 1, + "e:927_d:2024-11-30": 0, + "e:928_d:2024-11-01": 0, + "e:928_d:2024-11-02": 0, + "e:928_d:2024-11-03": 0, + "e:928_d:2024-11-04": 1, + "e:928_d:2024-11-05": 0, + "e:928_d:2024-11-06": 0, + "e:928_d:2024-11-07": 0, + "e:928_d:2024-11-08": 1, + "e:928_d:2024-11-09": 0, + "e:928_d:2024-11-10": 0, + "e:928_d:2024-11-11": 0, + "e:928_d:2024-11-12": 0, + "e:928_d:2024-11-13": 0, + "e:928_d:2024-11-14": 0, + "e:928_d:2024-11-15": 0, + "e:928_d:2024-11-16": 0, + "e:928_d:2024-11-17": 0, + "e:928_d:2024-11-18": 0, + "e:928_d:2024-11-19": 0, + "e:928_d:2024-11-20": 0, + "e:928_d:2024-11-21": 1, + "e:928_d:2024-11-22": 0, + "e:928_d:2024-11-23": 0, + "e:928_d:2024-11-24": 0, + "e:928_d:2024-11-25": 0, + "e:928_d:2024-11-26": 0, + "e:928_d:2024-11-27": 0, + "e:928_d:2024-11-28": 0, + "e:928_d:2024-11-29": 0, + "e:928_d:2024-11-30": 0 + } +} diff --git a/legacy/processed_solutions/solution_77_2024-11-01-2024-11-30_wdefault_processed.json b/legacy/processed_solutions/solution_77_2024-11-01-2024-11-30_wdefault_processed.json new file mode 100644 index 00000000..96493b5c --- /dev/null +++ b/legacy/processed_solutions/solution_77_2024-11-01-2024-11-30_wdefault_processed.json @@ -0,0 +1,14438 @@ +{ + "all_day_off_wish_cells": [ + [ + 925, + "2024-11-20" + ], + [ + 6677, + "2024-11-28" + ], + [ + 2963, + "2024-11-26" + ], + [ + 925, + "2024-11-21" + ], + [ + 917, + "2024-11-08" + ], + [ + 6677, + "2024-11-29" + ], + [ + 3868, + "2024-11-11" + ] + ], + "all_shift_wish_colors": { + "3868-2024-11-01": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-02": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-03": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-04": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-05": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-06": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-07": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-08": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-09": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-10": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-11": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-12": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-13": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-14": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-15": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-16": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-17": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-18": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-19": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-20": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-21": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-22": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-23": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-24": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-25": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-26": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-27": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-28": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-29": [ + "#225e62", + "#dadada" + ], + "3868-2024-11-30": [ + "#225e62", + "#dadada" + ], + "5367-2024-11-06": [ + "#f69e17", + "#dadada", + "#225e62", + "#dadada", + "#3a9ea1", + "oklch(82.1% 0.087 285.6)" + ], + "6836-2024-11-15": [ + "#a8d51f", + "#dadada", + "#f69e17", + "#dadada", + "#3a9ea1", + "oklch(82.1% 0.087 285.6)" + ], + "6836-2024-11-16": [ + "#a8d51f", + "#dadada", + "#f69e17", + "#dadada", + "#3a9ea1", + "oklch(82.1% 0.087 285.6)" + ], + "7603-2024-11-11": [ + "#a8d51f", + "#dadada", + "#3a9ea1", + "oklch(82.1% 0.087 285.6)" + ], + "791-2024-11-25": [ + "#a8d51f", + "#dadada" + ], + "917-2024-11-30": [ + "#f69e17", + "#dadada", + "#225e62", + "#dadada" + ] + }, + "days": [ + "2024-11-01", + "2024-11-02", + "2024-11-03", + "2024-11-04", + "2024-11-05", + "2024-11-06", + "2024-11-07", + "2024-11-08", + "2024-11-09", + "2024-11-10", + "2024-11-11", + "2024-11-12", + "2024-11-13", + "2024-11-14", + "2024-11-15", + "2024-11-16", + "2024-11-17", + "2024-11-18", + "2024-11-19", + "2024-11-20", + "2024-11-21", + "2024-11-22", + "2024-11-23", + "2024-11-24", + "2024-11-25", + "2024-11-26", + "2024-11-27", + "2024-11-28", + "2024-11-29", + "2024-11-30" + ], + "employees": [ + { + "actual_working_time": 1920, + "forbidden_days": [ + 18, + 22, + 23, + 24, + 25 + ], + "forbidden_shifts": [ + [ + 1, + "N" + ], + [ + 2, + "N" + ], + [ + 3, + "N" + ], + [ + 4, + "N" + ], + [ + 5, + "N" + ], + [ + 6, + "N" + ], + [ + 7, + "N" + ], + [ + 8, + "N" + ], + [ + 9, + "N" + ], + [ + 10, + "N" + ], + [ + 11, + "N" + ], + [ + 12, + "N" + ], + [ + 13, + "N" + ], + [ + 14, + "N" + ], + [ + 15, + "N" + ], + [ + 16, + "N" + ], + [ + 17, + "N" + ], + [ + 18, + "N" + ], + [ + 19, + "N" + ], + [ + 20, + "N" + ], + [ + 21, + "N" + ], + [ + 22, + "N" + ], + [ + 23, + "N" + ], + [ + 24, + "N" + ], + [ + 25, + "N" + ], + [ + 26, + "N" + ], + [ + 27, + "N" + ], + [ + 28, + "N" + ], + [ + 29, + "N" + ], + [ + 30, + "N" + ] + ], + "hidden_actual_working_time": 1920, + "id": 459, + "level": "Fachkraft", + "name": "Shoemake Sandra", + "target_working_time": 7680, + "vacation_days": [ + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 12 + ], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 9360, + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "forbidden_shifts": [], + "hidden_actual_working_time": 9360, + "id": 790, + "level": "Hilfskraft", + "name": "Mccomas Adriane", + "target_working_time": 9360, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 0, + "forbidden_days": [], + "forbidden_shifts": [], + "hidden_actual_working_time": 0, + "id": 791, + "level": "Fachkraft", + "name": "Branz Janett", + "target_working_time": 6930, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [ + [ + 25, + "F" + ] + ] + } + }, + { + "actual_working_time": 8765, + "forbidden_days": [ + 4, + 5, + 9, + 10, + 15, + 16, + 17, + 18, + 19, + 25, + 26, + 27 + ], + "forbidden_shifts": [], + "hidden_actual_working_time": 0, + "id": 822, + "level": "Hilfskraft", + "name": "Sewell Nele", + "target_working_time": 9240, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 3955, + "forbidden_days": [ + 1, + 2, + 3, + 6, + 7, + 8, + 9, + 12, + 13, + 14, + 16, + 17, + 18, + 21, + 22, + 23, + 24, + 25, + 28, + 29, + 30 + ], + "forbidden_shifts": [], + "hidden_actual_working_time": 0, + "id": 839, + "level": "Hilfskraft", + "name": "S\u00e4uffert Liselotte", + "target_working_time": 4620, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 2079, + "forbidden_days": [ + 23, + 24, + 30 + ], + "forbidden_shifts": [], + "hidden_actual_working_time": 2079, + "id": 914, + "level": "Hilfskraft", + "name": "Harkins Silvia", + "target_working_time": 8316, + "vacation_days": [ + 21, + 22, + 25, + 26, + 27, + 28, + 29 + ], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 460, + "forbidden_days": [], + "forbidden_shifts": [], + "hidden_actual_working_time": 0, + "id": 917, + "level": "Fachkraft", + "name": "Catoe Ingbert", + "target_working_time": 7680, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [ + 8 + ], + "shift_wishes": [ + [ + 30, + "S" + ], + [ + 30, + "N" + ] + ] + } + }, + { + "actual_working_time": 4620, + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "forbidden_shifts": [], + "hidden_actual_working_time": 4620, + "id": 921, + "level": "Fachkraft", + "name": "Keese Jenny", + "target_working_time": 4620, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 0, + "forbidden_days": [ + 1, + 2, + 3, + 8, + 14, + 15, + 21, + 22, + 28, + 29 + ], + "forbidden_shifts": [ + [ + 1, + "N" + ], + [ + 2, + "N" + ], + [ + 3, + "N" + ], + [ + 4, + "N" + ], + [ + 5, + "N" + ], + [ + 6, + "N" + ], + [ + 7, + "N" + ], + [ + 8, + "N" + ], + [ + 9, + "N" + ], + [ + 10, + "N" + ], + [ + 11, + "N" + ], + [ + 12, + "N" + ], + [ + 13, + "N" + ], + [ + 14, + "N" + ], + [ + 15, + "N" + ], + [ + 16, + "N" + ], + [ + 17, + "N" + ], + [ + 18, + "N" + ], + [ + 19, + "N" + ], + [ + 20, + "N" + ], + [ + 21, + "N" + ], + [ + 22, + "N" + ], + [ + 23, + "N" + ], + [ + 24, + "N" + ], + [ + 25, + "N" + ], + [ + 26, + "N" + ], + [ + 27, + "N" + ], + [ + 28, + "N" + ], + [ + 29, + "N" + ], + [ + 30, + "N" + ] + ], + "hidden_actual_working_time": 0, + "id": 924, + "level": "Fachkraft", + "name": "Merriweather B\u00e4rbl", + "target_working_time": 4561, + "vacation_days": [ + 4, + 5, + 6, + 7 + ], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 0, + "forbidden_days": [], + "forbidden_shifts": [ + [ + 1, + "F" + ], + [ + 2, + "F" + ], + [ + 3, + "F" + ], + [ + 4, + "F" + ], + [ + 5, + "F" + ], + [ + 6, + "F" + ], + [ + 7, + "F" + ], + [ + 8, + "F" + ], + [ + 9, + "F" + ], + [ + 10, + "F" + ], + [ + 11, + "F" + ], + [ + 12, + "F" + ], + [ + 13, + "F" + ], + [ + 14, + "F" + ], + [ + 15, + "F" + ], + [ + 16, + "F" + ], + [ + 17, + "F" + ], + [ + 18, + "F" + ], + [ + 19, + "F" + ], + [ + 20, + "F" + ], + [ + 21, + "F" + ], + [ + 22, + "F" + ], + [ + 23, + "F" + ], + [ + 24, + "F" + ], + [ + 25, + "F" + ], + [ + 26, + "F" + ], + [ + 27, + "F" + ], + [ + 28, + "F" + ], + [ + 29, + "F" + ], + [ + 30, + "F" + ], + [ + 1, + "S" + ], + [ + 2, + "S" + ], + [ + 3, + "S" + ], + [ + 4, + "S" + ], + [ + 5, + "S" + ], + [ + 6, + "S" + ], + [ + 7, + "S" + ], + [ + 8, + "S" + ], + [ + 9, + "S" + ], + [ + 10, + "S" + ], + [ + 11, + "S" + ], + [ + 12, + "S" + ], + [ + 13, + "S" + ], + [ + 14, + "S" + ], + [ + 15, + "S" + ], + [ + 16, + "S" + ], + [ + 17, + "S" + ], + [ + 18, + "S" + ], + [ + 19, + "S" + ], + [ + 20, + "S" + ], + [ + 21, + "S" + ], + [ + 22, + "S" + ], + [ + 23, + "S" + ], + [ + 24, + "S" + ], + [ + 25, + "S" + ], + [ + 26, + "S" + ], + [ + 27, + "S" + ], + [ + 28, + "S" + ], + [ + 29, + "S" + ], + [ + 30, + "S" + ], + [ + 1, + "Z" + ], + [ + 2, + "Z" + ], + [ + 3, + "Z" + ], + [ + 4, + "Z" + ], + [ + 5, + "Z" + ], + [ + 6, + "Z" + ], + [ + 7, + "Z" + ], + [ + 8, + "Z" + ], + [ + 9, + "Z" + ], + [ + 10, + "Z" + ], + [ + 11, + "Z" + ], + [ + 12, + "Z" + ], + [ + 13, + "Z" + ], + [ + 14, + "Z" + ], + [ + 15, + "Z" + ], + [ + 16, + "Z" + ], + [ + 17, + "Z" + ], + [ + 18, + "Z" + ], + [ + 19, + "Z" + ], + [ + 20, + "Z" + ], + [ + 21, + "Z" + ], + [ + 22, + "Z" + ], + [ + 23, + "Z" + ], + [ + 24, + "Z" + ], + [ + 25, + "Z" + ], + [ + 26, + "Z" + ], + [ + 27, + "Z" + ], + [ + 28, + "Z" + ], + [ + 29, + "Z" + ], + [ + 30, + "Z" + ] + ], + "hidden_actual_working_time": 0, + "id": 925, + "level": "Fachkraft", + "name": "Farniok Lina", + "target_working_time": 5544, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [ + 20, + 21 + ], + "shift_wishes": [] + } + }, + { + "actual_working_time": 468, + "forbidden_days": [], + "forbidden_shifts": [], + "hidden_actual_working_time": 468, + "id": 927, + "level": "Hilfskraft", + "name": "Mittrach Margaritt", + "target_working_time": 9360, + "vacation_days": [ + 18 + ], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 832, + "forbidden_days": [ + 1, + 6, + 7, + 16, + 17, + 18, + 19, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "forbidden_shifts": [ + [ + 1, + "F" + ], + [ + 2, + "F" + ], + [ + 3, + "F" + ], + [ + 4, + "F" + ], + [ + 5, + "F" + ], + [ + 6, + "F" + ], + [ + 7, + "F" + ], + [ + 8, + "F" + ], + [ + 9, + "F" + ], + [ + 10, + "F" + ], + [ + 11, + "F" + ], + [ + 12, + "F" + ], + [ + 13, + "F" + ], + [ + 14, + "F" + ], + [ + 15, + "F" + ], + [ + 16, + "F" + ], + [ + 17, + "F" + ], + [ + 18, + "F" + ], + [ + 19, + "F" + ], + [ + 20, + "F" + ], + [ + 21, + "F" + ], + [ + 22, + "F" + ], + [ + 23, + "F" + ], + [ + 24, + "F" + ], + [ + 25, + "F" + ], + [ + 26, + "F" + ], + [ + 27, + "F" + ], + [ + 28, + "F" + ], + [ + 29, + "F" + ], + [ + 30, + "F" + ], + [ + 1, + "S" + ], + [ + 2, + "S" + ], + [ + 3, + "S" + ], + [ + 4, + "S" + ], + [ + 5, + "S" + ], + [ + 6, + "S" + ], + [ + 7, + "S" + ], + [ + 8, + "S" + ], + [ + 9, + "S" + ], + [ + 10, + "S" + ], + [ + 11, + "S" + ], + [ + 12, + "S" + ], + [ + 13, + "S" + ], + [ + 14, + "S" + ], + [ + 15, + "S" + ], + [ + 16, + "S" + ], + [ + 17, + "S" + ], + [ + 18, + "S" + ], + [ + 19, + "S" + ], + [ + 20, + "S" + ], + [ + 21, + "S" + ], + [ + 22, + "S" + ], + [ + 23, + "S" + ], + [ + 24, + "S" + ], + [ + 25, + "S" + ], + [ + 26, + "S" + ], + [ + 27, + "S" + ], + [ + 28, + "S" + ], + [ + 29, + "S" + ], + [ + 30, + "S" + ], + [ + 1, + "Z" + ], + [ + 2, + "Z" + ], + [ + 3, + "Z" + ], + [ + 4, + "Z" + ], + [ + 5, + "Z" + ], + [ + 6, + "Z" + ], + [ + 7, + "Z" + ], + [ + 8, + "Z" + ], + [ + 9, + "Z" + ], + [ + 10, + "Z" + ], + [ + 11, + "Z" + ], + [ + 12, + "Z" + ], + [ + 13, + "Z" + ], + [ + 14, + "Z" + ], + [ + 15, + "Z" + ], + [ + 16, + "Z" + ], + [ + 17, + "Z" + ], + [ + 18, + "Z" + ], + [ + 19, + "Z" + ], + [ + 20, + "Z" + ], + [ + 21, + "Z" + ], + [ + 22, + "Z" + ], + [ + 23, + "Z" + ], + [ + 24, + "Z" + ], + [ + 25, + "Z" + ], + [ + 26, + "Z" + ], + [ + 27, + "Z" + ], + [ + 28, + "Z" + ], + [ + 29, + "Z" + ], + [ + 30, + "Z" + ] + ], + "hidden_actual_working_time": 832, + "id": 928, + "level": "Fachkraft", + "name": "Wunderlich Daniele", + "target_working_time": 5544, + "vacation_days": [ + 25, + 26, + 27, + 28, + 29 + ], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 462, + "forbidden_days": [], + "forbidden_shifts": [], + "hidden_actual_working_time": 462, + "id": 1230, + "level": "Fachkraft", + "name": "Palacio Constance", + "target_working_time": 9240, + "vacation_days": [ + 11 + ], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 0, + "forbidden_days": [], + "forbidden_shifts": [], + "hidden_actual_working_time": 0, + "id": 2932, + "level": "Fachkraft", + "name": "Devers Kersten", + "target_working_time": 9240, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 0, + "forbidden_days": [], + "forbidden_shifts": [ + [ + 1, + "N" + ], + [ + 1, + "S" + ], + [ + 1, + "Z" + ], + [ + 4, + "N" + ], + [ + 4, + "S" + ], + [ + 4, + "Z" + ], + [ + 5, + "N" + ], + [ + 5, + "S" + ], + [ + 5, + "Z" + ], + [ + 6, + "N" + ], + [ + 6, + "S" + ], + [ + 6, + "Z" + ], + [ + 7, + "N" + ], + [ + 7, + "S" + ], + [ + 7, + "Z" + ], + [ + 8, + "N" + ], + [ + 8, + "S" + ], + [ + 8, + "Z" + ], + [ + 11, + "N" + ], + [ + 11, + "S" + ], + [ + 11, + "Z" + ], + [ + 12, + "N" + ], + [ + 12, + "S" + ], + [ + 12, + "Z" + ], + [ + 13, + "N" + ], + [ + 13, + "S" + ], + [ + 13, + "Z" + ], + [ + 14, + "N" + ], + [ + 14, + "S" + ], + [ + 14, + "Z" + ], + [ + 15, + "N" + ], + [ + 15, + "S" + ], + [ + 15, + "Z" + ], + [ + 18, + "N" + ], + [ + 18, + "S" + ], + [ + 18, + "Z" + ], + [ + 19, + "N" + ], + [ + 19, + "S" + ], + [ + 19, + "Z" + ], + [ + 20, + "N" + ], + [ + 20, + "S" + ], + [ + 20, + "Z" + ], + [ + 21, + "N" + ], + [ + 21, + "S" + ], + [ + 21, + "Z" + ], + [ + 22, + "N" + ], + [ + 22, + "S" + ], + [ + 22, + "Z" + ], + [ + 25, + "N" + ], + [ + 25, + "S" + ], + [ + 25, + "Z" + ], + [ + 26, + "N" + ], + [ + 26, + "S" + ], + [ + 26, + "Z" + ], + [ + 27, + "N" + ], + [ + 27, + "S" + ], + [ + 27, + "Z" + ], + [ + 28, + "N" + ], + [ + 28, + "S" + ], + [ + 28, + "Z" + ], + [ + 29, + "N" + ], + [ + 29, + "S" + ], + [ + 29, + "Z" + ] + ], + "hidden_actual_working_time": 0, + "id": 2963, + "level": "Fachkraft", + "name": "Hoots Renilde", + "target_working_time": 9240, + "vacation_days": [ + 11 + ], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [ + 26 + ], + "shift_wishes": [] + } + }, + { + "actual_working_time": 9240, + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "forbidden_shifts": [], + "hidden_actual_working_time": 9240, + "id": 3566, + "level": "Fachkraft", + "name": "Seligman Elgine", + "target_working_time": 9240, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 460, + "forbidden_days": [], + "forbidden_shifts": [], + "hidden_actual_working_time": 0, + "id": 3868, + "level": "Fachkraft", + "name": "Vanfleet Eike", + "target_working_time": 9240, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [ + 11 + ], + "shift_wishes": [ + [ + 1, + "N" + ], + [ + 2, + "N" + ], + [ + 3, + "N" + ], + [ + 4, + "N" + ], + [ + 5, + "N" + ], + [ + 6, + "N" + ], + [ + 7, + "N" + ], + [ + 8, + "N" + ], + [ + 9, + "N" + ], + [ + 10, + "N" + ], + [ + 11, + "N" + ], + [ + 12, + "N" + ], + [ + 13, + "N" + ], + [ + 14, + "N" + ], + [ + 15, + "N" + ], + [ + 16, + "N" + ], + [ + 17, + "N" + ], + [ + 18, + "N" + ], + [ + 19, + "N" + ], + [ + 20, + "N" + ], + [ + 21, + "N" + ], + [ + 22, + "N" + ], + [ + 23, + "N" + ], + [ + 24, + "N" + ], + [ + 25, + "N" + ], + [ + 26, + "N" + ], + [ + 27, + "N" + ], + [ + 28, + "N" + ], + [ + 29, + "N" + ], + [ + 30, + "N" + ] + ] + } + }, + { + "actual_working_time": 462, + "forbidden_days": [ + 18 + ], + "forbidden_shifts": [], + "hidden_actual_working_time": 462, + "id": 4566, + "level": "Azubi", + "name": "Woodcock Hannah", + "target_working_time": 9240, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 0, + "forbidden_days": [], + "forbidden_shifts": [], + "hidden_actual_working_time": 0, + "id": 5367, + "level": "Hilfskraft", + "name": "Donis Henni", + "target_working_time": 9240, + "vacation_days": [ + 7, + 8 + ], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [ + [ + 6, + "S" + ], + [ + 6, + "N" + ], + [ + 6, + "Z" + ] + ] + } + }, + { + "actual_working_time": 0, + "forbidden_days": [], + "forbidden_shifts": [], + "hidden_actual_working_time": 0, + "id": 5920, + "level": "Fachkraft", + "name": "Carreras Augustin", + "target_working_time": 9240, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 1, + "forbidden_days": [ + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29 + ], + "forbidden_shifts": [], + "hidden_actual_working_time": 1, + "id": 6475, + "level": "Hilfskraft", + "name": "Binford Heinz", + "target_working_time": 2, + "vacation_days": [ + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21 + ], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 0, + "forbidden_days": [], + "forbidden_shifts": [ + [ + 1, + "N" + ], + [ + 1, + "S" + ], + [ + 1, + "Z" + ], + [ + 4, + "N" + ], + [ + 4, + "S" + ], + [ + 4, + "Z" + ], + [ + 5, + "N" + ], + [ + 5, + "S" + ], + [ + 5, + "Z" + ], + [ + 6, + "N" + ], + [ + 6, + "S" + ], + [ + 6, + "Z" + ], + [ + 7, + "N" + ], + [ + 7, + "S" + ], + [ + 7, + "Z" + ], + [ + 8, + "N" + ], + [ + 8, + "S" + ], + [ + 8, + "Z" + ], + [ + 11, + "N" + ], + [ + 11, + "S" + ], + [ + 11, + "Z" + ], + [ + 12, + "N" + ], + [ + 12, + "S" + ], + [ + 12, + "Z" + ], + [ + 13, + "N" + ], + [ + 13, + "S" + ], + [ + 13, + "Z" + ], + [ + 14, + "N" + ], + [ + 14, + "S" + ], + [ + 14, + "Z" + ], + [ + 15, + "N" + ], + [ + 15, + "S" + ], + [ + 15, + "Z" + ], + [ + 18, + "N" + ], + [ + 18, + "S" + ], + [ + 18, + "Z" + ], + [ + 19, + "N" + ], + [ + 19, + "S" + ], + [ + 19, + "Z" + ], + [ + 20, + "N" + ], + [ + 20, + "S" + ], + [ + 20, + "Z" + ], + [ + 21, + "N" + ], + [ + 21, + "S" + ], + [ + 21, + "Z" + ], + [ + 22, + "N" + ], + [ + 22, + "S" + ], + [ + 22, + "Z" + ], + [ + 25, + "N" + ], + [ + 25, + "S" + ], + [ + 25, + "Z" + ], + [ + 26, + "N" + ], + [ + 26, + "S" + ], + [ + 26, + "Z" + ], + [ + 27, + "N" + ], + [ + 27, + "S" + ], + [ + 27, + "Z" + ], + [ + 28, + "N" + ], + [ + 28, + "S" + ], + [ + 28, + "Z" + ], + [ + 29, + "N" + ], + [ + 29, + "S" + ], + [ + 29, + "Z" + ] + ], + "hidden_actual_working_time": 0, + "id": 6507, + "level": "Fachkraft", + "name": "Rashid Roseliese", + "target_working_time": 9240, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 0, + "forbidden_days": [], + "forbidden_shifts": [], + "hidden_actual_working_time": 0, + "id": 6677, + "level": "Hilfskraft", + "name": "Fullerton Christfri", + "target_working_time": 9240, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [ + 28, + 29 + ], + "shift_wishes": [] + } + }, + { + "actual_working_time": 1848, + "forbidden_days": [ + 23, + 24 + ], + "forbidden_shifts": [ + [ + 1, + "F" + ], + [ + 2, + "F" + ], + [ + 3, + "F" + ], + [ + 4, + "F" + ], + [ + 5, + "F" + ], + [ + 6, + "F" + ], + [ + 7, + "F" + ], + [ + 8, + "F" + ], + [ + 9, + "F" + ], + [ + 10, + "F" + ], + [ + 11, + "F" + ], + [ + 12, + "F" + ], + [ + 13, + "F" + ], + [ + 14, + "F" + ], + [ + 15, + "F" + ], + [ + 16, + "F" + ], + [ + 17, + "F" + ], + [ + 18, + "F" + ], + [ + 19, + "F" + ], + [ + 20, + "F" + ], + [ + 21, + "F" + ], + [ + 22, + "F" + ], + [ + 23, + "F" + ], + [ + 24, + "F" + ], + [ + 25, + "F" + ], + [ + 26, + "F" + ], + [ + 27, + "F" + ], + [ + 28, + "F" + ], + [ + 29, + "F" + ], + [ + 30, + "F" + ], + [ + 1, + "S" + ], + [ + 2, + "S" + ], + [ + 3, + "S" + ], + [ + 4, + "S" + ], + [ + 5, + "S" + ], + [ + 6, + "S" + ], + [ + 7, + "S" + ], + [ + 8, + "S" + ], + [ + 9, + "S" + ], + [ + 10, + "S" + ], + [ + 11, + "S" + ], + [ + 12, + "S" + ], + [ + 13, + "S" + ], + [ + 14, + "S" + ], + [ + 15, + "S" + ], + [ + 16, + "S" + ], + [ + 17, + "S" + ], + [ + 18, + "S" + ], + [ + 19, + "S" + ], + [ + 20, + "S" + ], + [ + 21, + "S" + ], + [ + 22, + "S" + ], + [ + 23, + "S" + ], + [ + 24, + "S" + ], + [ + 25, + "S" + ], + [ + 26, + "S" + ], + [ + 27, + "S" + ], + [ + 28, + "S" + ], + [ + 29, + "S" + ], + [ + 30, + "S" + ], + [ + 1, + "Z" + ], + [ + 2, + "Z" + ], + [ + 3, + "Z" + ], + [ + 4, + "Z" + ], + [ + 5, + "Z" + ], + [ + 6, + "Z" + ], + [ + 7, + "Z" + ], + [ + 8, + "Z" + ], + [ + 9, + "Z" + ], + [ + 10, + "Z" + ], + [ + 11, + "Z" + ], + [ + 12, + "Z" + ], + [ + 13, + "Z" + ], + [ + 14, + "Z" + ], + [ + 15, + "Z" + ], + [ + 16, + "Z" + ], + [ + 17, + "Z" + ], + [ + 18, + "Z" + ], + [ + 19, + "Z" + ], + [ + 20, + "Z" + ], + [ + 21, + "Z" + ], + [ + 22, + "Z" + ], + [ + 23, + "Z" + ], + [ + 24, + "Z" + ], + [ + 25, + "Z" + ], + [ + 26, + "Z" + ], + [ + 27, + "Z" + ], + [ + 28, + "Z" + ], + [ + 29, + "Z" + ], + [ + 30, + "Z" + ] + ], + "hidden_actual_working_time": 1848, + "id": 6681, + "level": "Fachkraft", + "name": "Labelle Saskia", + "target_working_time": 7392, + "vacation_days": [ + 18, + 19, + 20, + 21, + 22, + 25, + 26 + ], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 6930, + "forbidden_days": [ + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "forbidden_shifts": [], + "hidden_actual_working_time": 6930, + "id": 6715, + "level": "Azubi", + "name": "Burris Lioba", + "target_working_time": 9240, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 460, + "forbidden_days": [], + "forbidden_shifts": [], + "hidden_actual_working_time": 0, + "id": 6836, + "level": "Fachkraft", + "name": "Valentino Trude", + "target_working_time": 9240, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [ + [ + 15, + "F" + ], + [ + 16, + "F" + ], + [ + 15, + "S" + ], + [ + 16, + "S" + ], + [ + 15, + "Z" + ], + [ + 16, + "Z" + ] + ] + } + }, + { + "actual_working_time": 0, + "forbidden_days": [], + "forbidden_shifts": [], + "hidden_actual_working_time": 0, + "id": 6928, + "level": "Hilfskraft", + "name": "Izzo Annelene", + "target_working_time": 9360, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 8950, + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 16, + 21, + 22, + 23, + 24, + 30 + ], + "forbidden_shifts": [], + "hidden_actual_working_time": 4140, + "id": 7496, + "level": "Fachkraft", + "name": "Demarco Marcus", + "target_working_time": 8400, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 0, + "forbidden_days": [], + "forbidden_shifts": [ + [ + 1, + "N" + ], + [ + 2, + "N" + ], + [ + 3, + "N" + ], + [ + 4, + "N" + ], + [ + 5, + "N" + ], + [ + 6, + "N" + ], + [ + 7, + "N" + ], + [ + 8, + "N" + ], + [ + 9, + "N" + ], + [ + 10, + "N" + ], + [ + 11, + "N" + ], + [ + 12, + "N" + ], + [ + 13, + "N" + ], + [ + 14, + "N" + ], + [ + 15, + "N" + ], + [ + 16, + "N" + ], + [ + 17, + "N" + ], + [ + 18, + "N" + ], + [ + 19, + "N" + ], + [ + 20, + "N" + ], + [ + 21, + "N" + ], + [ + 22, + "N" + ], + [ + 23, + "N" + ], + [ + 24, + "N" + ], + [ + 25, + "N" + ], + [ + 26, + "N" + ], + [ + 27, + "N" + ], + [ + 28, + "N" + ], + [ + 29, + "N" + ], + [ + 30, + "N" + ] + ], + "hidden_actual_working_time": 0, + "id": 7603, + "level": "Fachkraft", + "name": "Roberson Ludger", + "target_working_time": 9240, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [ + [ + 11, + "F" + ], + [ + 11, + "Z" + ] + ] + } + }, + { + "actual_working_time": 920, + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 26, + 29, + 30 + ], + "forbidden_shifts": [], + "hidden_actual_working_time": 0, + "id": 7741, + "level": "Fachkraft", + "name": "Tharp Sieghardt", + "target_working_time": 960, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 2300, + "forbidden_days": [ + 4, + 8, + 9, + 10 + ], + "forbidden_shifts": [], + "hidden_actual_working_time": 920, + "id": 7752, + "level": "Hilfskraft", + "name": "Rodriques Kilian", + "target_working_time": 9240, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 2310, + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 22, + 25 + ], + "forbidden_shifts": [], + "hidden_actual_working_time": 2310, + "id": 7770, + "level": "Azubi", + "name": "Yeh Julia", + "target_working_time": 9240, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 2310, + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 28, + 29 + ], + "forbidden_shifts": [], + "hidden_actual_working_time": 2310, + "id": 7796, + "level": "Azubi", + "name": "Hertzler Burkhild", + "target_working_time": 9240, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 2310, + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 22, + 25 + ], + "forbidden_shifts": [], + "hidden_actual_working_time": 2310, + "id": 7835, + "level": "Azubi", + "name": "Driggers Karena", + "target_working_time": 9240, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 460, + "forbidden_days": [], + "forbidden_shifts": [], + "hidden_actual_working_time": 460, + "id": 7848, + "level": "Azubi", + "name": "Winters Gertraute", + "target_working_time": 9240, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 1386, + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6 + ], + "forbidden_shifts": [], + "hidden_actual_working_time": 1386, + "id": 7877, + "level": "Azubi", + "name": "Staggs Janett", + "target_working_time": 9240, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 0, + "forbidden_days": [ + 1 + ], + "forbidden_shifts": [], + "hidden_actual_working_time": 0, + "id": 7919, + "level": "Fachkraft", + "name": "Weathers Irma", + "target_working_time": 9240, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 0, + "forbidden_days": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27 + ], + "forbidden_shifts": [], + "hidden_actual_working_time": 0, + "id": 7990, + "level": "Azubi", + "name": "Milburn Loremarie", + "target_working_time": 2310, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 0, + "forbidden_days": [], + "forbidden_shifts": [], + "hidden_actual_working_time": 0, + "id": 0, + "level": "Hilfskraft", + "name": "Hilfskraft0 Hidden", + "target_working_time": 0, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + }, + { + "actual_working_time": 0, + "forbidden_days": [], + "forbidden_shifts": [], + "hidden_actual_working_time": 0, + "id": 1, + "level": "Hilfskraft", + "name": "Hilfskraft1 Hidden", + "target_working_time": 0, + "vacation_days": [], + "vacation_shifts": [], + "wishes": { + "day_off_wishes": [], + "shift_wishes": [] + } + } + ], + "fulfilled_day_off_cells": [ + [ + 925, + "2024-11-20" + ], + [ + 925, + "2024-11-21" + ] + ], + "fulfilled_shift_wish_cells": [ + [ + 3868, + "2024-11-18" + ], + [ + 3868, + "2024-11-21" + ], + [ + 3868, + "2024-11-08" + ], + [ + 917, + "2024-11-30" + ], + [ + 3868, + "2024-11-16" + ], + [ + 3868, + "2024-11-22" + ], + [ + 3868, + "2024-11-28" + ], + [ + 3868, + "2024-11-20" + ], + [ + 3868, + "2024-11-30" + ], + [ + 3868, + "2024-11-13" + ], + [ + 3868, + "2024-11-26" + ], + [ + 3868, + "2024-11-14" + ], + [ + 3868, + "2024-11-17" + ], + [ + 3868, + "2024-11-15" + ], + [ + 3868, + "2024-11-24" + ], + [ + 3868, + "2024-11-05" + ], + [ + 3868, + "2024-11-07" + ], + [ + 3868, + "2024-11-09" + ], + [ + 3868, + "2024-11-19" + ], + [ + 3868, + "2024-11-29" + ], + [ + 3868, + "2024-11-01" + ], + [ + 3868, + "2024-11-27" + ], + [ + 3868, + "2024-11-10" + ], + [ + 3868, + "2024-11-04" + ], + [ + 7603, + "2024-11-11" + ], + [ + 3868, + "2024-11-02" + ], + [ + 3868, + "2024-11-06" + ], + [ + 3868, + "2024-11-23" + ], + [ + 3868, + "2024-11-25" + ], + [ + 3868, + "2024-11-03" + ], + [ + 3868, + "2024-11-12" + ], + [ + 791, + "2024-11-25" + ] + ], + "selected_solution_file_name": "solution_77_2024-11-01-2024-11-30_wdefault", + "shifts": [ + { + "abbreviation": "F", + "color": "#a8d51f", + "duration": 460, + "id": 0, + "is_exclusive": false, + "name": "Fr\u00fch" + }, + { + "abbreviation": "Z", + "color": "#3a9ea1", + "duration": 460, + "id": 1, + "is_exclusive": false, + "name": "Zwischen" + }, + { + "abbreviation": "S", + "color": "#f69e17", + "duration": 460, + "id": 2, + "is_exclusive": false, + "name": "Sp\u00e4t" + }, + { + "abbreviation": "N", + "color": "#225e62", + "duration": 565, + "id": 3, + "is_exclusive": false, + "name": "Nacht" + }, + { + "abbreviation": "Z", + "color": "oklch(82.1% 0.087 285.6)", + "duration": 360, + "id": 4, + "is_exclusive": true, + "name": "Z60" + }, + { + "abbreviation": "F", + "color": "#dadada", + "duration": 460, + "id": 5, + "is_exclusive": true, + "name": "F2_" + }, + { + "abbreviation": "S", + "color": "#dadada", + "duration": 460, + "id": 6, + "is_exclusive": true, + "name": "S2_" + }, + { + "abbreviation": "N", + "color": "#dadada", + "duration": 565, + "id": 7, + "is_exclusive": true, + "name": "N5" + } + ], + "solution_file_names": [ + "solution_77_2024-11-01-2024-11-30_wdefault", + "solution_77_2024-12-01-2024-12-31_wdefault", + "solution_77_2025-02-01-2025-02-28_wdefault" + ], + "stats": { + "consecutive_night_shifts_gt_3": 11, + "consecutive_working_days_gt_5": 12, + "forward_rotation_violations": 66, + "no_free_days_around_weekend": 79, + "no_free_weekend": 29, + "not_free_after_night_shift": 37, + "total_overtime_hours": 355.48, + "violated_wish_total": 9 + }, + "variables": { + "(0, '2024-11-01', 0)": 0, + "(0, '2024-11-01', 1)": 0, + "(0, '2024-11-01', 2)": 0, + "(0, '2024-11-01', 3)": 0, + "(0, '2024-11-01', 4)": 0, + "(0, '2024-11-01', 5)": 0, + "(0, '2024-11-01', 6)": 0, + "(0, '2024-11-01', 7)": 0, + "(0, '2024-11-02', 0)": 0, + "(0, '2024-11-02', 1)": 0, + "(0, '2024-11-02', 2)": 1, + "(0, '2024-11-02', 3)": 0, + "(0, '2024-11-02', 4)": 0, + "(0, '2024-11-02', 5)": 0, + "(0, '2024-11-02', 6)": 0, + "(0, '2024-11-02', 7)": 0, + "(0, '2024-11-03', 0)": 0, + "(0, '2024-11-03', 1)": 0, + "(0, '2024-11-03', 2)": 1, + "(0, '2024-11-03', 3)": 0, + "(0, '2024-11-03', 4)": 0, + "(0, '2024-11-03', 5)": 0, + "(0, '2024-11-03', 6)": 0, + "(0, '2024-11-03', 7)": 0, + "(0, '2024-11-04', 0)": 0, + "(0, '2024-11-04', 1)": 0, + "(0, '2024-11-04', 2)": 1, + "(0, '2024-11-04', 3)": 0, + "(0, '2024-11-04', 4)": 0, + "(0, '2024-11-04', 5)": 0, + "(0, '2024-11-04', 6)": 0, + "(0, '2024-11-04', 7)": 0, + "(0, '2024-11-05', 0)": 0, + "(0, '2024-11-05', 1)": 0, + "(0, '2024-11-05', 2)": 1, + "(0, '2024-11-05', 3)": 0, + "(0, '2024-11-05', 4)": 0, + "(0, '2024-11-05', 5)": 0, + "(0, '2024-11-05', 6)": 0, + "(0, '2024-11-05', 7)": 0, + "(0, '2024-11-06', 0)": 0, + "(0, '2024-11-06', 1)": 0, + "(0, '2024-11-06', 2)": 0, + "(0, '2024-11-06', 3)": 0, + "(0, '2024-11-06', 4)": 0, + "(0, '2024-11-06', 5)": 0, + "(0, '2024-11-06', 6)": 0, + "(0, '2024-11-06', 7)": 0, + "(0, '2024-11-07', 0)": 0, + "(0, '2024-11-07', 1)": 0, + "(0, '2024-11-07', 2)": 0, + "(0, '2024-11-07', 3)": 0, + "(0, '2024-11-07', 4)": 0, + "(0, '2024-11-07', 5)": 0, + "(0, '2024-11-07', 6)": 0, + "(0, '2024-11-07', 7)": 0, + "(0, '2024-11-08', 0)": 0, + "(0, '2024-11-08', 1)": 0, + "(0, '2024-11-08', 2)": 0, + "(0, '2024-11-08', 3)": 0, + "(0, '2024-11-08', 4)": 0, + "(0, '2024-11-08', 5)": 0, + "(0, '2024-11-08', 6)": 0, + "(0, '2024-11-08', 7)": 0, + "(0, '2024-11-09', 0)": 1, + "(0, '2024-11-09', 1)": 0, + "(0, '2024-11-09', 2)": 0, + "(0, '2024-11-09', 3)": 0, + "(0, '2024-11-09', 4)": 0, + "(0, '2024-11-09', 5)": 0, + "(0, '2024-11-09', 6)": 0, + "(0, '2024-11-09', 7)": 0, + "(0, '2024-11-10', 0)": 1, + "(0, '2024-11-10', 1)": 0, + "(0, '2024-11-10', 2)": 0, + "(0, '2024-11-10', 3)": 0, + "(0, '2024-11-10', 4)": 0, + "(0, '2024-11-10', 5)": 0, + "(0, '2024-11-10', 6)": 0, + "(0, '2024-11-10', 7)": 0, + "(0, '2024-11-11', 0)": 0, + "(0, '2024-11-11', 1)": 0, + "(0, '2024-11-11', 2)": 1, + "(0, '2024-11-11', 3)": 0, + "(0, '2024-11-11', 4)": 0, + "(0, '2024-11-11', 5)": 0, + "(0, '2024-11-11', 6)": 0, + "(0, '2024-11-11', 7)": 0, + "(0, '2024-11-12', 0)": 0, + "(0, '2024-11-12', 1)": 0, + "(0, '2024-11-12', 2)": 1, + "(0, '2024-11-12', 3)": 0, + "(0, '2024-11-12', 4)": 0, + "(0, '2024-11-12', 5)": 0, + "(0, '2024-11-12', 6)": 0, + "(0, '2024-11-12', 7)": 0, + "(0, '2024-11-13', 0)": 0, + "(0, '2024-11-13', 1)": 0, + "(0, '2024-11-13', 2)": 1, + "(0, '2024-11-13', 3)": 0, + "(0, '2024-11-13', 4)": 0, + "(0, '2024-11-13', 5)": 0, + "(0, '2024-11-13', 6)": 0, + "(0, '2024-11-13', 7)": 0, + "(0, '2024-11-14', 0)": 0, + "(0, '2024-11-14', 1)": 0, + "(0, '2024-11-14', 2)": 0, + "(0, '2024-11-14', 3)": 0, + "(0, '2024-11-14', 4)": 0, + "(0, '2024-11-14', 5)": 0, + "(0, '2024-11-14', 6)": 0, + "(0, '2024-11-14', 7)": 0, + "(0, '2024-11-15', 0)": 1, + "(0, '2024-11-15', 1)": 0, + "(0, '2024-11-15', 2)": 0, + "(0, '2024-11-15', 3)": 0, + "(0, '2024-11-15', 4)": 0, + "(0, '2024-11-15', 5)": 0, + "(0, '2024-11-15', 6)": 0, + "(0, '2024-11-15', 7)": 0, + "(0, '2024-11-16', 0)": 0, + "(0, '2024-11-16', 1)": 0, + "(0, '2024-11-16', 2)": 1, + "(0, '2024-11-16', 3)": 0, + "(0, '2024-11-16', 4)": 0, + "(0, '2024-11-16', 5)": 0, + "(0, '2024-11-16', 6)": 0, + "(0, '2024-11-16', 7)": 0, + "(0, '2024-11-17', 0)": 0, + "(0, '2024-11-17', 1)": 0, + "(0, '2024-11-17', 2)": 1, + "(0, '2024-11-17', 3)": 0, + "(0, '2024-11-17', 4)": 0, + "(0, '2024-11-17', 5)": 0, + "(0, '2024-11-17', 6)": 0, + "(0, '2024-11-17', 7)": 0, + "(0, '2024-11-18', 0)": 0, + "(0, '2024-11-18', 1)": 0, + "(0, '2024-11-18', 2)": 0, + "(0, '2024-11-18', 3)": 0, + "(0, '2024-11-18', 4)": 0, + "(0, '2024-11-18', 5)": 0, + "(0, '2024-11-18', 6)": 0, + "(0, '2024-11-18', 7)": 0, + "(0, '2024-11-19', 0)": 0, + "(0, '2024-11-19', 1)": 1, + "(0, '2024-11-19', 2)": 0, + "(0, '2024-11-19', 3)": 0, + "(0, '2024-11-19', 4)": 0, + "(0, '2024-11-19', 5)": 0, + "(0, '2024-11-19', 6)": 0, + "(0, '2024-11-19', 7)": 0, + "(0, '2024-11-20', 0)": 1, + "(0, '2024-11-20', 1)": 0, + "(0, '2024-11-20', 2)": 0, + "(0, '2024-11-20', 3)": 0, + "(0, '2024-11-20', 4)": 0, + "(0, '2024-11-20', 5)": 0, + "(0, '2024-11-20', 6)": 0, + "(0, '2024-11-20', 7)": 0, + "(0, '2024-11-21', 0)": 1, + "(0, '2024-11-21', 1)": 0, + "(0, '2024-11-21', 2)": 0, + "(0, '2024-11-21', 3)": 0, + "(0, '2024-11-21', 4)": 0, + "(0, '2024-11-21', 5)": 0, + "(0, '2024-11-21', 6)": 0, + "(0, '2024-11-21', 7)": 0, + "(0, '2024-11-22', 0)": 0, + "(0, '2024-11-22', 1)": 0, + "(0, '2024-11-22', 2)": 1, + "(0, '2024-11-22', 3)": 0, + "(0, '2024-11-22', 4)": 0, + "(0, '2024-11-22', 5)": 0, + "(0, '2024-11-22', 6)": 0, + "(0, '2024-11-22', 7)": 0, + "(0, '2024-11-23', 0)": 0, + "(0, '2024-11-23', 1)": 0, + "(0, '2024-11-23', 2)": 1, + "(0, '2024-11-23', 3)": 0, + "(0, '2024-11-23', 4)": 0, + "(0, '2024-11-23', 5)": 0, + "(0, '2024-11-23', 6)": 0, + "(0, '2024-11-23', 7)": 0, + "(0, '2024-11-24', 0)": 0, + "(0, '2024-11-24', 1)": 0, + "(0, '2024-11-24', 2)": 1, + "(0, '2024-11-24', 3)": 0, + "(0, '2024-11-24', 4)": 0, + "(0, '2024-11-24', 5)": 0, + "(0, '2024-11-24', 6)": 0, + "(0, '2024-11-24', 7)": 0, + "(0, '2024-11-25', 0)": 0, + "(0, '2024-11-25', 1)": 0, + "(0, '2024-11-25', 2)": 1, + "(0, '2024-11-25', 3)": 0, + "(0, '2024-11-25', 4)": 0, + "(0, '2024-11-25', 5)": 0, + "(0, '2024-11-25', 6)": 0, + "(0, '2024-11-25', 7)": 0, + "(0, '2024-11-26', 0)": 0, + "(0, '2024-11-26', 1)": 0, + "(0, '2024-11-26', 2)": 0, + "(0, '2024-11-26', 3)": 0, + "(0, '2024-11-26', 4)": 0, + "(0, '2024-11-26', 5)": 0, + "(0, '2024-11-26', 6)": 0, + "(0, '2024-11-26', 7)": 0, + "(0, '2024-11-27', 0)": 0, + "(0, '2024-11-27', 1)": 0, + "(0, '2024-11-27', 2)": 1, + "(0, '2024-11-27', 3)": 0, + "(0, '2024-11-27', 4)": 0, + "(0, '2024-11-27', 5)": 0, + "(0, '2024-11-27', 6)": 0, + "(0, '2024-11-27', 7)": 0, + "(0, '2024-11-28', 0)": 0, + "(0, '2024-11-28', 1)": 0, + "(0, '2024-11-28', 2)": 0, + "(0, '2024-11-28', 3)": 0, + "(0, '2024-11-28', 4)": 0, + "(0, '2024-11-28', 5)": 0, + "(0, '2024-11-28', 6)": 0, + "(0, '2024-11-28', 7)": 0, + "(0, '2024-11-29', 0)": 0, + "(0, '2024-11-29', 1)": 0, + "(0, '2024-11-29', 2)": 0, + "(0, '2024-11-29', 3)": 0, + "(0, '2024-11-29', 4)": 0, + "(0, '2024-11-29', 5)": 0, + "(0, '2024-11-29', 6)": 0, + "(0, '2024-11-29', 7)": 0, + "(0, '2024-11-30', 0)": 1, + "(0, '2024-11-30', 1)": 0, + "(0, '2024-11-30', 2)": 0, + "(0, '2024-11-30', 3)": 0, + "(0, '2024-11-30', 4)": 0, + "(0, '2024-11-30', 5)": 0, + "(0, '2024-11-30', 6)": 0, + "(0, '2024-11-30', 7)": 0, + "(1, '2024-11-01', 0)": 1, + "(1, '2024-11-01', 1)": 0, + "(1, '2024-11-01', 2)": 0, + "(1, '2024-11-01', 3)": 0, + "(1, '2024-11-01', 4)": 0, + "(1, '2024-11-01', 5)": 0, + "(1, '2024-11-01', 6)": 0, + "(1, '2024-11-01', 7)": 0, + "(1, '2024-11-02', 0)": 1, + "(1, '2024-11-02', 1)": 0, + "(1, '2024-11-02', 2)": 0, + "(1, '2024-11-02', 3)": 0, + "(1, '2024-11-02', 4)": 0, + "(1, '2024-11-02', 5)": 0, + "(1, '2024-11-02', 6)": 0, + "(1, '2024-11-02', 7)": 0, + "(1, '2024-11-03', 0)": 0, + "(1, '2024-11-03', 1)": 0, + "(1, '2024-11-03', 2)": 1, + "(1, '2024-11-03', 3)": 0, + "(1, '2024-11-03', 4)": 0, + "(1, '2024-11-03', 5)": 0, + "(1, '2024-11-03', 6)": 0, + "(1, '2024-11-03', 7)": 0, + "(1, '2024-11-04', 0)": 0, + "(1, '2024-11-04', 1)": 0, + "(1, '2024-11-04', 2)": 0, + "(1, '2024-11-04', 3)": 0, + "(1, '2024-11-04', 4)": 0, + "(1, '2024-11-04', 5)": 0, + "(1, '2024-11-04', 6)": 0, + "(1, '2024-11-04', 7)": 0, + "(1, '2024-11-05', 0)": 0, + "(1, '2024-11-05', 1)": 0, + "(1, '2024-11-05', 2)": 0, + "(1, '2024-11-05', 3)": 0, + "(1, '2024-11-05', 4)": 0, + "(1, '2024-11-05', 5)": 0, + "(1, '2024-11-05', 6)": 0, + "(1, '2024-11-05', 7)": 0, + "(1, '2024-11-06', 0)": 0, + "(1, '2024-11-06', 1)": 0, + "(1, '2024-11-06', 2)": 0, + "(1, '2024-11-06', 3)": 0, + "(1, '2024-11-06', 4)": 0, + "(1, '2024-11-06', 5)": 0, + "(1, '2024-11-06', 6)": 0, + "(1, '2024-11-06', 7)": 0, + "(1, '2024-11-07', 0)": 1, + "(1, '2024-11-07', 1)": 0, + "(1, '2024-11-07', 2)": 0, + "(1, '2024-11-07', 3)": 0, + "(1, '2024-11-07', 4)": 0, + "(1, '2024-11-07', 5)": 0, + "(1, '2024-11-07', 6)": 0, + "(1, '2024-11-07', 7)": 0, + "(1, '2024-11-08', 0)": 1, + "(1, '2024-11-08', 1)": 0, + "(1, '2024-11-08', 2)": 0, + "(1, '2024-11-08', 3)": 0, + "(1, '2024-11-08', 4)": 0, + "(1, '2024-11-08', 5)": 0, + "(1, '2024-11-08', 6)": 0, + "(1, '2024-11-08', 7)": 0, + "(1, '2024-11-09', 0)": 1, + "(1, '2024-11-09', 1)": 0, + "(1, '2024-11-09', 2)": 0, + "(1, '2024-11-09', 3)": 0, + "(1, '2024-11-09', 4)": 0, + "(1, '2024-11-09', 5)": 0, + "(1, '2024-11-09', 6)": 0, + "(1, '2024-11-09', 7)": 0, + "(1, '2024-11-10', 0)": 0, + "(1, '2024-11-10', 1)": 0, + "(1, '2024-11-10', 2)": 1, + "(1, '2024-11-10', 3)": 0, + "(1, '2024-11-10', 4)": 0, + "(1, '2024-11-10', 5)": 0, + "(1, '2024-11-10', 6)": 0, + "(1, '2024-11-10', 7)": 0, + "(1, '2024-11-11', 0)": 0, + "(1, '2024-11-11', 1)": 0, + "(1, '2024-11-11', 2)": 0, + "(1, '2024-11-11', 3)": 0, + "(1, '2024-11-11', 4)": 0, + "(1, '2024-11-11', 5)": 0, + "(1, '2024-11-11', 6)": 0, + "(1, '2024-11-11', 7)": 0, + "(1, '2024-11-12', 0)": 0, + "(1, '2024-11-12', 1)": 0, + "(1, '2024-11-12', 2)": 0, + "(1, '2024-11-12', 3)": 0, + "(1, '2024-11-12', 4)": 0, + "(1, '2024-11-12', 5)": 0, + "(1, '2024-11-12', 6)": 0, + "(1, '2024-11-12', 7)": 0, + "(1, '2024-11-13', 0)": 0, + "(1, '2024-11-13', 1)": 0, + "(1, '2024-11-13', 2)": 0, + "(1, '2024-11-13', 3)": 0, + "(1, '2024-11-13', 4)": 0, + "(1, '2024-11-13', 5)": 0, + "(1, '2024-11-13', 6)": 0, + "(1, '2024-11-13', 7)": 0, + "(1, '2024-11-14', 0)": 0, + "(1, '2024-11-14', 1)": 0, + "(1, '2024-11-14', 2)": 1, + "(1, '2024-11-14', 3)": 0, + "(1, '2024-11-14', 4)": 0, + "(1, '2024-11-14', 5)": 0, + "(1, '2024-11-14', 6)": 0, + "(1, '2024-11-14', 7)": 0, + "(1, '2024-11-15', 0)": 0, + "(1, '2024-11-15', 1)": 0, + "(1, '2024-11-15', 2)": 0, + "(1, '2024-11-15', 3)": 0, + "(1, '2024-11-15', 4)": 0, + "(1, '2024-11-15', 5)": 0, + "(1, '2024-11-15', 6)": 0, + "(1, '2024-11-15', 7)": 0, + "(1, '2024-11-16', 0)": 1, + "(1, '2024-11-16', 1)": 0, + "(1, '2024-11-16', 2)": 0, + "(1, '2024-11-16', 3)": 0, + "(1, '2024-11-16', 4)": 0, + "(1, '2024-11-16', 5)": 0, + "(1, '2024-11-16', 6)": 0, + "(1, '2024-11-16', 7)": 0, + "(1, '2024-11-17', 0)": 1, + "(1, '2024-11-17', 1)": 0, + "(1, '2024-11-17', 2)": 0, + "(1, '2024-11-17', 3)": 0, + "(1, '2024-11-17', 4)": 0, + "(1, '2024-11-17', 5)": 0, + "(1, '2024-11-17', 6)": 0, + "(1, '2024-11-17', 7)": 0, + "(1, '2024-11-18', 0)": 0, + "(1, '2024-11-18', 1)": 0, + "(1, '2024-11-18', 2)": 1, + "(1, '2024-11-18', 3)": 0, + "(1, '2024-11-18', 4)": 0, + "(1, '2024-11-18', 5)": 0, + "(1, '2024-11-18', 6)": 0, + "(1, '2024-11-18', 7)": 0, + "(1, '2024-11-19', 0)": 0, + "(1, '2024-11-19', 1)": 0, + "(1, '2024-11-19', 2)": 1, + "(1, '2024-11-19', 3)": 0, + "(1, '2024-11-19', 4)": 0, + "(1, '2024-11-19', 5)": 0, + "(1, '2024-11-19', 6)": 0, + "(1, '2024-11-19', 7)": 0, + "(1, '2024-11-20', 0)": 0, + "(1, '2024-11-20', 1)": 0, + "(1, '2024-11-20', 2)": 1, + "(1, '2024-11-20', 3)": 0, + "(1, '2024-11-20', 4)": 0, + "(1, '2024-11-20', 5)": 0, + "(1, '2024-11-20', 6)": 0, + "(1, '2024-11-20', 7)": 0, + "(1, '2024-11-21', 0)": 0, + "(1, '2024-11-21', 1)": 0, + "(1, '2024-11-21', 2)": 1, + "(1, '2024-11-21', 3)": 0, + "(1, '2024-11-21', 4)": 0, + "(1, '2024-11-21', 5)": 0, + "(1, '2024-11-21', 6)": 0, + "(1, '2024-11-21', 7)": 0, + "(1, '2024-11-22', 0)": 0, + "(1, '2024-11-22', 1)": 0, + "(1, '2024-11-22', 2)": 0, + "(1, '2024-11-22', 3)": 0, + "(1, '2024-11-22', 4)": 0, + "(1, '2024-11-22', 5)": 0, + "(1, '2024-11-22', 6)": 0, + "(1, '2024-11-22', 7)": 0, + "(1, '2024-11-23', 0)": 1, + "(1, '2024-11-23', 1)": 0, + "(1, '2024-11-23', 2)": 0, + "(1, '2024-11-23', 3)": 0, + "(1, '2024-11-23', 4)": 0, + "(1, '2024-11-23', 5)": 0, + "(1, '2024-11-23', 6)": 0, + "(1, '2024-11-23', 7)": 0, + "(1, '2024-11-24', 0)": 1, + "(1, '2024-11-24', 1)": 0, + "(1, '2024-11-24', 2)": 0, + "(1, '2024-11-24', 3)": 0, + "(1, '2024-11-24', 4)": 0, + "(1, '2024-11-24', 5)": 0, + "(1, '2024-11-24', 6)": 0, + "(1, '2024-11-24', 7)": 0, + "(1, '2024-11-25', 0)": 0, + "(1, '2024-11-25', 1)": 0, + "(1, '2024-11-25', 2)": 0, + "(1, '2024-11-25', 3)": 0, + "(1, '2024-11-25', 4)": 0, + "(1, '2024-11-25', 5)": 0, + "(1, '2024-11-25', 6)": 0, + "(1, '2024-11-25', 7)": 0, + "(1, '2024-11-26', 0)": 0, + "(1, '2024-11-26', 1)": 0, + "(1, '2024-11-26', 2)": 1, + "(1, '2024-11-26', 3)": 0, + "(1, '2024-11-26', 4)": 0, + "(1, '2024-11-26', 5)": 0, + "(1, '2024-11-26', 6)": 0, + "(1, '2024-11-26', 7)": 0, + "(1, '2024-11-27', 0)": 0, + "(1, '2024-11-27', 1)": 0, + "(1, '2024-11-27', 2)": 0, + "(1, '2024-11-27', 3)": 0, + "(1, '2024-11-27', 4)": 0, + "(1, '2024-11-27', 5)": 0, + "(1, '2024-11-27', 6)": 0, + "(1, '2024-11-27', 7)": 0, + "(1, '2024-11-28', 0)": 0, + "(1, '2024-11-28', 1)": 0, + "(1, '2024-11-28', 2)": 0, + "(1, '2024-11-28', 3)": 0, + "(1, '2024-11-28', 4)": 0, + "(1, '2024-11-28', 5)": 0, + "(1, '2024-11-28', 6)": 0, + "(1, '2024-11-28', 7)": 0, + "(1, '2024-11-29', 0)": 0, + "(1, '2024-11-29', 1)": 0, + "(1, '2024-11-29', 2)": 0, + "(1, '2024-11-29', 3)": 0, + "(1, '2024-11-29', 4)": 0, + "(1, '2024-11-29', 5)": 0, + "(1, '2024-11-29', 6)": 0, + "(1, '2024-11-29', 7)": 0, + "(1, '2024-11-30', 0)": 1, + "(1, '2024-11-30', 1)": 0, + "(1, '2024-11-30', 2)": 0, + "(1, '2024-11-30', 3)": 0, + "(1, '2024-11-30', 4)": 0, + "(1, '2024-11-30', 5)": 0, + "(1, '2024-11-30', 6)": 0, + "(1, '2024-11-30', 7)": 0, + "(1230, '2024-11-01', 0)": 0, + "(1230, '2024-11-01', 1)": 0, + "(1230, '2024-11-01', 2)": 0, + "(1230, '2024-11-01', 3)": 0, + "(1230, '2024-11-01', 4)": 0, + "(1230, '2024-11-01', 5)": 0, + "(1230, '2024-11-01', 6)": 0, + "(1230, '2024-11-01', 7)": 0, + "(1230, '2024-11-02', 0)": 0, + "(1230, '2024-11-02', 1)": 0, + "(1230, '2024-11-02', 2)": 1, + "(1230, '2024-11-02', 3)": 0, + "(1230, '2024-11-02', 4)": 0, + "(1230, '2024-11-02', 5)": 0, + "(1230, '2024-11-02', 6)": 0, + "(1230, '2024-11-02', 7)": 0, + "(1230, '2024-11-03', 0)": 0, + "(1230, '2024-11-03', 1)": 0, + "(1230, '2024-11-03', 2)": 0, + "(1230, '2024-11-03', 3)": 0, + "(1230, '2024-11-03', 4)": 0, + "(1230, '2024-11-03', 5)": 0, + "(1230, '2024-11-03', 6)": 0, + "(1230, '2024-11-03', 7)": 0, + "(1230, '2024-11-04', 0)": 0, + "(1230, '2024-11-04', 1)": 0, + "(1230, '2024-11-04', 2)": 1, + "(1230, '2024-11-04', 3)": 0, + "(1230, '2024-11-04', 4)": 0, + "(1230, '2024-11-04', 5)": 0, + "(1230, '2024-11-04', 6)": 0, + "(1230, '2024-11-04', 7)": 0, + "(1230, '2024-11-05', 0)": 0, + "(1230, '2024-11-05', 1)": 0, + "(1230, '2024-11-05', 2)": 0, + "(1230, '2024-11-05', 3)": 0, + "(1230, '2024-11-05', 4)": 0, + "(1230, '2024-11-05', 5)": 0, + "(1230, '2024-11-05', 6)": 0, + "(1230, '2024-11-05', 7)": 0, + "(1230, '2024-11-06', 0)": 0, + "(1230, '2024-11-06', 1)": 0, + "(1230, '2024-11-06', 2)": 1, + "(1230, '2024-11-06', 3)": 0, + "(1230, '2024-11-06', 4)": 0, + "(1230, '2024-11-06', 5)": 0, + "(1230, '2024-11-06', 6)": 0, + "(1230, '2024-11-06', 7)": 0, + "(1230, '2024-11-07', 0)": 0, + "(1230, '2024-11-07', 1)": 0, + "(1230, '2024-11-07', 2)": 1, + "(1230, '2024-11-07', 3)": 0, + "(1230, '2024-11-07', 4)": 0, + "(1230, '2024-11-07', 5)": 0, + "(1230, '2024-11-07', 6)": 0, + "(1230, '2024-11-07', 7)": 0, + "(1230, '2024-11-08', 0)": 0, + "(1230, '2024-11-08', 1)": 1, + "(1230, '2024-11-08', 2)": 0, + "(1230, '2024-11-08', 3)": 0, + "(1230, '2024-11-08', 4)": 0, + "(1230, '2024-11-08', 5)": 0, + "(1230, '2024-11-08', 6)": 0, + "(1230, '2024-11-08', 7)": 0, + "(1230, '2024-11-09', 0)": 0, + "(1230, '2024-11-09', 1)": 0, + "(1230, '2024-11-09', 2)": 0, + "(1230, '2024-11-09', 3)": 0, + "(1230, '2024-11-09', 4)": 0, + "(1230, '2024-11-09', 5)": 0, + "(1230, '2024-11-09', 6)": 0, + "(1230, '2024-11-09', 7)": 0, + "(1230, '2024-11-10', 0)": 0, + "(1230, '2024-11-10', 1)": 0, + "(1230, '2024-11-10', 2)": 1, + "(1230, '2024-11-10', 3)": 0, + "(1230, '2024-11-10', 4)": 0, + "(1230, '2024-11-10', 5)": 0, + "(1230, '2024-11-10', 6)": 0, + "(1230, '2024-11-10', 7)": 0, + "(1230, '2024-11-11', 0)": 0, + "(1230, '2024-11-11', 1)": 0, + "(1230, '2024-11-11', 2)": 0, + "(1230, '2024-11-11', 3)": 0, + "(1230, '2024-11-11', 4)": 0, + "(1230, '2024-11-11', 5)": 0, + "(1230, '2024-11-11', 6)": 0, + "(1230, '2024-11-11', 7)": 0, + "(1230, '2024-11-12', 0)": 0, + "(1230, '2024-11-12', 1)": 0, + "(1230, '2024-11-12', 2)": 1, + "(1230, '2024-11-12', 3)": 0, + "(1230, '2024-11-12', 4)": 0, + "(1230, '2024-11-12', 5)": 0, + "(1230, '2024-11-12', 6)": 0, + "(1230, '2024-11-12', 7)": 0, + "(1230, '2024-11-13', 0)": 0, + "(1230, '2024-11-13', 1)": 0, + "(1230, '2024-11-13', 2)": 1, + "(1230, '2024-11-13', 3)": 0, + "(1230, '2024-11-13', 4)": 0, + "(1230, '2024-11-13', 5)": 0, + "(1230, '2024-11-13', 6)": 0, + "(1230, '2024-11-13', 7)": 0, + "(1230, '2024-11-14', 0)": 0, + "(1230, '2024-11-14', 1)": 0, + "(1230, '2024-11-14', 2)": 0, + "(1230, '2024-11-14', 3)": 0, + "(1230, '2024-11-14', 4)": 0, + "(1230, '2024-11-14', 5)": 0, + "(1230, '2024-11-14', 6)": 0, + "(1230, '2024-11-14', 7)": 0, + "(1230, '2024-11-15', 0)": 0, + "(1230, '2024-11-15', 1)": 0, + "(1230, '2024-11-15', 2)": 0, + "(1230, '2024-11-15', 3)": 1, + "(1230, '2024-11-15', 4)": 0, + "(1230, '2024-11-15', 5)": 0, + "(1230, '2024-11-15', 6)": 0, + "(1230, '2024-11-15', 7)": 0, + "(1230, '2024-11-16', 0)": 0, + "(1230, '2024-11-16', 1)": 0, + "(1230, '2024-11-16', 2)": 0, + "(1230, '2024-11-16', 3)": 0, + "(1230, '2024-11-16', 4)": 0, + "(1230, '2024-11-16', 5)": 0, + "(1230, '2024-11-16', 6)": 0, + "(1230, '2024-11-16', 7)": 0, + "(1230, '2024-11-17', 0)": 0, + "(1230, '2024-11-17', 1)": 0, + "(1230, '2024-11-17', 2)": 0, + "(1230, '2024-11-17', 3)": 0, + "(1230, '2024-11-17', 4)": 0, + "(1230, '2024-11-17', 5)": 0, + "(1230, '2024-11-17', 6)": 0, + "(1230, '2024-11-17', 7)": 0, + "(1230, '2024-11-18', 0)": 1, + "(1230, '2024-11-18', 1)": 0, + "(1230, '2024-11-18', 2)": 0, + "(1230, '2024-11-18', 3)": 0, + "(1230, '2024-11-18', 4)": 0, + "(1230, '2024-11-18', 5)": 0, + "(1230, '2024-11-18', 6)": 0, + "(1230, '2024-11-18', 7)": 0, + "(1230, '2024-11-19', 0)": 0, + "(1230, '2024-11-19', 1)": 0, + "(1230, '2024-11-19', 2)": 0, + "(1230, '2024-11-19', 3)": 1, + "(1230, '2024-11-19', 4)": 0, + "(1230, '2024-11-19', 5)": 0, + "(1230, '2024-11-19', 6)": 0, + "(1230, '2024-11-19', 7)": 0, + "(1230, '2024-11-20', 0)": 0, + "(1230, '2024-11-20', 1)": 0, + "(1230, '2024-11-20', 2)": 0, + "(1230, '2024-11-20', 3)": 1, + "(1230, '2024-11-20', 4)": 0, + "(1230, '2024-11-20', 5)": 0, + "(1230, '2024-11-20', 6)": 0, + "(1230, '2024-11-20', 7)": 0, + "(1230, '2024-11-21', 0)": 0, + "(1230, '2024-11-21', 1)": 0, + "(1230, '2024-11-21', 2)": 0, + "(1230, '2024-11-21', 3)": 0, + "(1230, '2024-11-21', 4)": 0, + "(1230, '2024-11-21', 5)": 0, + "(1230, '2024-11-21', 6)": 0, + "(1230, '2024-11-21', 7)": 0, + "(1230, '2024-11-22', 0)": 1, + "(1230, '2024-11-22', 1)": 0, + "(1230, '2024-11-22', 2)": 0, + "(1230, '2024-11-22', 3)": 0, + "(1230, '2024-11-22', 4)": 0, + "(1230, '2024-11-22', 5)": 0, + "(1230, '2024-11-22', 6)": 0, + "(1230, '2024-11-22', 7)": 0, + "(1230, '2024-11-23', 0)": 0, + "(1230, '2024-11-23', 1)": 0, + "(1230, '2024-11-23', 2)": 0, + "(1230, '2024-11-23', 3)": 0, + "(1230, '2024-11-23', 4)": 0, + "(1230, '2024-11-23', 5)": 0, + "(1230, '2024-11-23', 6)": 0, + "(1230, '2024-11-23', 7)": 0, + "(1230, '2024-11-24', 0)": 0, + "(1230, '2024-11-24', 1)": 0, + "(1230, '2024-11-24', 2)": 0, + "(1230, '2024-11-24', 3)": 0, + "(1230, '2024-11-24', 4)": 0, + "(1230, '2024-11-24', 5)": 0, + "(1230, '2024-11-24', 6)": 0, + "(1230, '2024-11-24', 7)": 0, + "(1230, '2024-11-25', 0)": 0, + "(1230, '2024-11-25', 1)": 0, + "(1230, '2024-11-25', 2)": 1, + "(1230, '2024-11-25', 3)": 0, + "(1230, '2024-11-25', 4)": 0, + "(1230, '2024-11-25', 5)": 0, + "(1230, '2024-11-25', 6)": 0, + "(1230, '2024-11-25', 7)": 0, + "(1230, '2024-11-26', 0)": 0, + "(1230, '2024-11-26', 1)": 1, + "(1230, '2024-11-26', 2)": 0, + "(1230, '2024-11-26', 3)": 0, + "(1230, '2024-11-26', 4)": 0, + "(1230, '2024-11-26', 5)": 0, + "(1230, '2024-11-26', 6)": 0, + "(1230, '2024-11-26', 7)": 0, + "(1230, '2024-11-27', 0)": 0, + "(1230, '2024-11-27', 1)": 1, + "(1230, '2024-11-27', 2)": 0, + "(1230, '2024-11-27', 3)": 0, + "(1230, '2024-11-27', 4)": 0, + "(1230, '2024-11-27', 5)": 0, + "(1230, '2024-11-27', 6)": 0, + "(1230, '2024-11-27', 7)": 0, + "(1230, '2024-11-28', 0)": 1, + "(1230, '2024-11-28', 1)": 0, + "(1230, '2024-11-28', 2)": 0, + "(1230, '2024-11-28', 3)": 0, + "(1230, '2024-11-28', 4)": 0, + "(1230, '2024-11-28', 5)": 0, + "(1230, '2024-11-28', 6)": 0, + "(1230, '2024-11-28', 7)": 0, + "(1230, '2024-11-29', 0)": 0, + "(1230, '2024-11-29', 1)": 0, + "(1230, '2024-11-29', 2)": 1, + "(1230, '2024-11-29', 3)": 0, + "(1230, '2024-11-29', 4)": 0, + "(1230, '2024-11-29', 5)": 0, + "(1230, '2024-11-29', 6)": 0, + "(1230, '2024-11-29', 7)": 0, + "(1230, '2024-11-30', 0)": 0, + "(1230, '2024-11-30', 1)": 0, + "(1230, '2024-11-30', 2)": 0, + "(1230, '2024-11-30', 3)": 0, + "(1230, '2024-11-30', 4)": 0, + "(1230, '2024-11-30', 5)": 0, + "(1230, '2024-11-30', 6)": 0, + "(1230, '2024-11-30', 7)": 0, + "(2932, '2024-11-01', 0)": 0, + "(2932, '2024-11-01', 1)": 0, + "(2932, '2024-11-01', 2)": 0, + "(2932, '2024-11-01', 3)": 0, + "(2932, '2024-11-01', 4)": 0, + "(2932, '2024-11-01', 5)": 0, + "(2932, '2024-11-01', 6)": 0, + "(2932, '2024-11-01', 7)": 0, + "(2932, '2024-11-02', 0)": 1, + "(2932, '2024-11-02', 1)": 0, + "(2932, '2024-11-02', 2)": 0, + "(2932, '2024-11-02', 3)": 0, + "(2932, '2024-11-02', 4)": 0, + "(2932, '2024-11-02', 5)": 0, + "(2932, '2024-11-02', 6)": 0, + "(2932, '2024-11-02', 7)": 0, + "(2932, '2024-11-03', 0)": 1, + "(2932, '2024-11-03', 1)": 0, + "(2932, '2024-11-03', 2)": 0, + "(2932, '2024-11-03', 3)": 0, + "(2932, '2024-11-03', 4)": 0, + "(2932, '2024-11-03', 5)": 0, + "(2932, '2024-11-03', 6)": 0, + "(2932, '2024-11-03', 7)": 0, + "(2932, '2024-11-04', 0)": 1, + "(2932, '2024-11-04', 1)": 0, + "(2932, '2024-11-04', 2)": 0, + "(2932, '2024-11-04', 3)": 0, + "(2932, '2024-11-04', 4)": 0, + "(2932, '2024-11-04', 5)": 0, + "(2932, '2024-11-04', 6)": 0, + "(2932, '2024-11-04', 7)": 0, + "(2932, '2024-11-05', 0)": 0, + "(2932, '2024-11-05', 1)": 0, + "(2932, '2024-11-05', 2)": 0, + "(2932, '2024-11-05', 3)": 0, + "(2932, '2024-11-05', 4)": 0, + "(2932, '2024-11-05', 5)": 0, + "(2932, '2024-11-05', 6)": 0, + "(2932, '2024-11-05', 7)": 0, + "(2932, '2024-11-06', 0)": 0, + "(2932, '2024-11-06', 1)": 0, + "(2932, '2024-11-06', 2)": 0, + "(2932, '2024-11-06', 3)": 0, + "(2932, '2024-11-06', 4)": 0, + "(2932, '2024-11-06', 5)": 0, + "(2932, '2024-11-06', 6)": 0, + "(2932, '2024-11-06', 7)": 0, + "(2932, '2024-11-07', 0)": 1, + "(2932, '2024-11-07', 1)": 0, + "(2932, '2024-11-07', 2)": 0, + "(2932, '2024-11-07', 3)": 0, + "(2932, '2024-11-07', 4)": 0, + "(2932, '2024-11-07', 5)": 0, + "(2932, '2024-11-07', 6)": 0, + "(2932, '2024-11-07', 7)": 0, + "(2932, '2024-11-08', 0)": 0, + "(2932, '2024-11-08', 1)": 0, + "(2932, '2024-11-08', 2)": 1, + "(2932, '2024-11-08', 3)": 0, + "(2932, '2024-11-08', 4)": 0, + "(2932, '2024-11-08', 5)": 0, + "(2932, '2024-11-08', 6)": 0, + "(2932, '2024-11-08', 7)": 0, + "(2932, '2024-11-09', 0)": 0, + "(2932, '2024-11-09', 1)": 0, + "(2932, '2024-11-09', 2)": 0, + "(2932, '2024-11-09', 3)": 0, + "(2932, '2024-11-09', 4)": 0, + "(2932, '2024-11-09', 5)": 0, + "(2932, '2024-11-09', 6)": 0, + "(2932, '2024-11-09', 7)": 0, + "(2932, '2024-11-10', 0)": 0, + "(2932, '2024-11-10', 1)": 0, + "(2932, '2024-11-10', 2)": 0, + "(2932, '2024-11-10', 3)": 1, + "(2932, '2024-11-10', 4)": 0, + "(2932, '2024-11-10', 5)": 0, + "(2932, '2024-11-10', 6)": 0, + "(2932, '2024-11-10', 7)": 0, + "(2932, '2024-11-11', 0)": 0, + "(2932, '2024-11-11', 1)": 0, + "(2932, '2024-11-11', 2)": 0, + "(2932, '2024-11-11', 3)": 0, + "(2932, '2024-11-11', 4)": 0, + "(2932, '2024-11-11', 5)": 0, + "(2932, '2024-11-11', 6)": 0, + "(2932, '2024-11-11', 7)": 0, + "(2932, '2024-11-12', 0)": 0, + "(2932, '2024-11-12', 1)": 0, + "(2932, '2024-11-12', 2)": 0, + "(2932, '2024-11-12', 3)": 0, + "(2932, '2024-11-12', 4)": 0, + "(2932, '2024-11-12', 5)": 0, + "(2932, '2024-11-12', 6)": 0, + "(2932, '2024-11-12', 7)": 0, + "(2932, '2024-11-13', 0)": 0, + "(2932, '2024-11-13', 1)": 0, + "(2932, '2024-11-13', 2)": 0, + "(2932, '2024-11-13', 3)": 0, + "(2932, '2024-11-13', 4)": 0, + "(2932, '2024-11-13', 5)": 0, + "(2932, '2024-11-13', 6)": 0, + "(2932, '2024-11-13', 7)": 0, + "(2932, '2024-11-14', 0)": 1, + "(2932, '2024-11-14', 1)": 0, + "(2932, '2024-11-14', 2)": 0, + "(2932, '2024-11-14', 3)": 0, + "(2932, '2024-11-14', 4)": 0, + "(2932, '2024-11-14', 5)": 0, + "(2932, '2024-11-14', 6)": 0, + "(2932, '2024-11-14', 7)": 0, + "(2932, '2024-11-15', 0)": 1, + "(2932, '2024-11-15', 1)": 0, + "(2932, '2024-11-15', 2)": 0, + "(2932, '2024-11-15', 3)": 0, + "(2932, '2024-11-15', 4)": 0, + "(2932, '2024-11-15', 5)": 0, + "(2932, '2024-11-15', 6)": 0, + "(2932, '2024-11-15', 7)": 0, + "(2932, '2024-11-16', 0)": 0, + "(2932, '2024-11-16', 1)": 0, + "(2932, '2024-11-16', 2)": 0, + "(2932, '2024-11-16', 3)": 0, + "(2932, '2024-11-16', 4)": 0, + "(2932, '2024-11-16', 5)": 0, + "(2932, '2024-11-16', 6)": 0, + "(2932, '2024-11-16', 7)": 0, + "(2932, '2024-11-17', 0)": 1, + "(2932, '2024-11-17', 1)": 0, + "(2932, '2024-11-17', 2)": 0, + "(2932, '2024-11-17', 3)": 0, + "(2932, '2024-11-17', 4)": 0, + "(2932, '2024-11-17', 5)": 0, + "(2932, '2024-11-17', 6)": 0, + "(2932, '2024-11-17', 7)": 0, + "(2932, '2024-11-18', 0)": 0, + "(2932, '2024-11-18', 1)": 0, + "(2932, '2024-11-18', 2)": 1, + "(2932, '2024-11-18', 3)": 0, + "(2932, '2024-11-18', 4)": 0, + "(2932, '2024-11-18', 5)": 0, + "(2932, '2024-11-18', 6)": 0, + "(2932, '2024-11-18', 7)": 0, + "(2932, '2024-11-19', 0)": 0, + "(2932, '2024-11-19', 1)": 0, + "(2932, '2024-11-19', 2)": 0, + "(2932, '2024-11-19', 3)": 1, + "(2932, '2024-11-19', 4)": 0, + "(2932, '2024-11-19', 5)": 0, + "(2932, '2024-11-19', 6)": 0, + "(2932, '2024-11-19', 7)": 0, + "(2932, '2024-11-20', 0)": 0, + "(2932, '2024-11-20', 1)": 0, + "(2932, '2024-11-20', 2)": 0, + "(2932, '2024-11-20', 3)": 0, + "(2932, '2024-11-20', 4)": 0, + "(2932, '2024-11-20', 5)": 0, + "(2932, '2024-11-20', 6)": 0, + "(2932, '2024-11-20', 7)": 0, + "(2932, '2024-11-21', 0)": 0, + "(2932, '2024-11-21', 1)": 0, + "(2932, '2024-11-21', 2)": 0, + "(2932, '2024-11-21', 3)": 0, + "(2932, '2024-11-21', 4)": 0, + "(2932, '2024-11-21', 5)": 0, + "(2932, '2024-11-21', 6)": 0, + "(2932, '2024-11-21', 7)": 0, + "(2932, '2024-11-22', 0)": 0, + "(2932, '2024-11-22', 1)": 0, + "(2932, '2024-11-22', 2)": 0, + "(2932, '2024-11-22', 3)": 1, + "(2932, '2024-11-22', 4)": 0, + "(2932, '2024-11-22', 5)": 0, + "(2932, '2024-11-22', 6)": 0, + "(2932, '2024-11-22', 7)": 0, + "(2932, '2024-11-23', 0)": 0, + "(2932, '2024-11-23', 1)": 0, + "(2932, '2024-11-23', 2)": 0, + "(2932, '2024-11-23', 3)": 0, + "(2932, '2024-11-23', 4)": 0, + "(2932, '2024-11-23', 5)": 0, + "(2932, '2024-11-23', 6)": 0, + "(2932, '2024-11-23', 7)": 0, + "(2932, '2024-11-24', 0)": 0, + "(2932, '2024-11-24', 1)": 0, + "(2932, '2024-11-24', 2)": 0, + "(2932, '2024-11-24', 3)": 0, + "(2932, '2024-11-24', 4)": 0, + "(2932, '2024-11-24', 5)": 0, + "(2932, '2024-11-24', 6)": 0, + "(2932, '2024-11-24', 7)": 0, + "(2932, '2024-11-25', 0)": 0, + "(2932, '2024-11-25', 1)": 0, + "(2932, '2024-11-25', 2)": 0, + "(2932, '2024-11-25', 3)": 1, + "(2932, '2024-11-25', 4)": 0, + "(2932, '2024-11-25', 5)": 0, + "(2932, '2024-11-25', 6)": 0, + "(2932, '2024-11-25', 7)": 0, + "(2932, '2024-11-26', 0)": 0, + "(2932, '2024-11-26', 1)": 0, + "(2932, '2024-11-26', 2)": 0, + "(2932, '2024-11-26', 3)": 1, + "(2932, '2024-11-26', 4)": 0, + "(2932, '2024-11-26', 5)": 0, + "(2932, '2024-11-26', 6)": 0, + "(2932, '2024-11-26', 7)": 0, + "(2932, '2024-11-27', 0)": 0, + "(2932, '2024-11-27', 1)": 0, + "(2932, '2024-11-27', 2)": 0, + "(2932, '2024-11-27', 3)": 1, + "(2932, '2024-11-27', 4)": 0, + "(2932, '2024-11-27', 5)": 0, + "(2932, '2024-11-27', 6)": 0, + "(2932, '2024-11-27', 7)": 0, + "(2932, '2024-11-28', 0)": 0, + "(2932, '2024-11-28', 1)": 0, + "(2932, '2024-11-28', 2)": 0, + "(2932, '2024-11-28', 3)": 1, + "(2932, '2024-11-28', 4)": 0, + "(2932, '2024-11-28', 5)": 0, + "(2932, '2024-11-28', 6)": 0, + "(2932, '2024-11-28', 7)": 0, + "(2932, '2024-11-29', 0)": 0, + "(2932, '2024-11-29', 1)": 0, + "(2932, '2024-11-29', 2)": 0, + "(2932, '2024-11-29', 3)": 1, + "(2932, '2024-11-29', 4)": 0, + "(2932, '2024-11-29', 5)": 0, + "(2932, '2024-11-29', 6)": 0, + "(2932, '2024-11-29', 7)": 0, + "(2932, '2024-11-30', 0)": 0, + "(2932, '2024-11-30', 1)": 0, + "(2932, '2024-11-30', 2)": 0, + "(2932, '2024-11-30', 3)": 1, + "(2932, '2024-11-30', 4)": 0, + "(2932, '2024-11-30', 5)": 0, + "(2932, '2024-11-30', 6)": 0, + "(2932, '2024-11-30', 7)": 0, + "(2963, '2024-11-01', 0)": 0, + "(2963, '2024-11-01', 1)": 0, + "(2963, '2024-11-01', 2)": 0, + "(2963, '2024-11-01', 3)": 0, + "(2963, '2024-11-01', 4)": 0, + "(2963, '2024-11-01', 5)": 0, + "(2963, '2024-11-01', 6)": 0, + "(2963, '2024-11-01', 7)": 0, + "(2963, '2024-11-02', 0)": 1, + "(2963, '2024-11-02', 1)": 0, + "(2963, '2024-11-02', 2)": 0, + "(2963, '2024-11-02', 3)": 0, + "(2963, '2024-11-02', 4)": 0, + "(2963, '2024-11-02', 5)": 0, + "(2963, '2024-11-02', 6)": 0, + "(2963, '2024-11-02', 7)": 0, + "(2963, '2024-11-03', 0)": 0, + "(2963, '2024-11-03', 1)": 1, + "(2963, '2024-11-03', 2)": 0, + "(2963, '2024-11-03', 3)": 0, + "(2963, '2024-11-03', 4)": 0, + "(2963, '2024-11-03', 5)": 0, + "(2963, '2024-11-03', 6)": 0, + "(2963, '2024-11-03', 7)": 0, + "(2963, '2024-11-04', 0)": 0, + "(2963, '2024-11-04', 1)": 0, + "(2963, '2024-11-04', 2)": 0, + "(2963, '2024-11-04', 3)": 0, + "(2963, '2024-11-04', 4)": 0, + "(2963, '2024-11-04', 5)": 0, + "(2963, '2024-11-04', 6)": 0, + "(2963, '2024-11-04', 7)": 0, + "(2963, '2024-11-05', 0)": 1, + "(2963, '2024-11-05', 1)": 0, + "(2963, '2024-11-05', 2)": 0, + "(2963, '2024-11-05', 3)": 0, + "(2963, '2024-11-05', 4)": 0, + "(2963, '2024-11-05', 5)": 0, + "(2963, '2024-11-05', 6)": 0, + "(2963, '2024-11-05', 7)": 0, + "(2963, '2024-11-06', 0)": 1, + "(2963, '2024-11-06', 1)": 0, + "(2963, '2024-11-06', 2)": 0, + "(2963, '2024-11-06', 3)": 0, + "(2963, '2024-11-06', 4)": 0, + "(2963, '2024-11-06', 5)": 0, + "(2963, '2024-11-06', 6)": 0, + "(2963, '2024-11-06', 7)": 0, + "(2963, '2024-11-07', 0)": 1, + "(2963, '2024-11-07', 1)": 0, + "(2963, '2024-11-07', 2)": 0, + "(2963, '2024-11-07', 3)": 0, + "(2963, '2024-11-07', 4)": 0, + "(2963, '2024-11-07', 5)": 0, + "(2963, '2024-11-07', 6)": 0, + "(2963, '2024-11-07', 7)": 0, + "(2963, '2024-11-08', 0)": 0, + "(2963, '2024-11-08', 1)": 0, + "(2963, '2024-11-08', 2)": 0, + "(2963, '2024-11-08', 3)": 0, + "(2963, '2024-11-08', 4)": 0, + "(2963, '2024-11-08', 5)": 0, + "(2963, '2024-11-08', 6)": 0, + "(2963, '2024-11-08', 7)": 0, + "(2963, '2024-11-09', 0)": 0, + "(2963, '2024-11-09', 1)": 0, + "(2963, '2024-11-09', 2)": 1, + "(2963, '2024-11-09', 3)": 0, + "(2963, '2024-11-09', 4)": 0, + "(2963, '2024-11-09', 5)": 0, + "(2963, '2024-11-09', 6)": 0, + "(2963, '2024-11-09', 7)": 0, + "(2963, '2024-11-10', 0)": 0, + "(2963, '2024-11-10', 1)": 1, + "(2963, '2024-11-10', 2)": 0, + "(2963, '2024-11-10', 3)": 0, + "(2963, '2024-11-10', 4)": 0, + "(2963, '2024-11-10', 5)": 0, + "(2963, '2024-11-10', 6)": 0, + "(2963, '2024-11-10', 7)": 0, + "(2963, '2024-11-11', 0)": 0, + "(2963, '2024-11-11', 1)": 0, + "(2963, '2024-11-11', 2)": 0, + "(2963, '2024-11-11', 3)": 0, + "(2963, '2024-11-11', 4)": 0, + "(2963, '2024-11-11', 5)": 0, + "(2963, '2024-11-11', 6)": 0, + "(2963, '2024-11-11', 7)": 0, + "(2963, '2024-11-12', 0)": 0, + "(2963, '2024-11-12', 1)": 0, + "(2963, '2024-11-12', 2)": 0, + "(2963, '2024-11-12', 3)": 0, + "(2963, '2024-11-12', 4)": 0, + "(2963, '2024-11-12', 5)": 0, + "(2963, '2024-11-12', 6)": 0, + "(2963, '2024-11-12', 7)": 0, + "(2963, '2024-11-13', 0)": 1, + "(2963, '2024-11-13', 1)": 0, + "(2963, '2024-11-13', 2)": 0, + "(2963, '2024-11-13', 3)": 0, + "(2963, '2024-11-13', 4)": 0, + "(2963, '2024-11-13', 5)": 0, + "(2963, '2024-11-13', 6)": 0, + "(2963, '2024-11-13', 7)": 0, + "(2963, '2024-11-14', 0)": 1, + "(2963, '2024-11-14', 1)": 0, + "(2963, '2024-11-14', 2)": 0, + "(2963, '2024-11-14', 3)": 0, + "(2963, '2024-11-14', 4)": 0, + "(2963, '2024-11-14', 5)": 0, + "(2963, '2024-11-14', 6)": 0, + "(2963, '2024-11-14', 7)": 0, + "(2963, '2024-11-15', 0)": 0, + "(2963, '2024-11-15', 1)": 0, + "(2963, '2024-11-15', 2)": 0, + "(2963, '2024-11-15', 3)": 0, + "(2963, '2024-11-15', 4)": 0, + "(2963, '2024-11-15', 5)": 0, + "(2963, '2024-11-15', 6)": 0, + "(2963, '2024-11-15', 7)": 0, + "(2963, '2024-11-16', 0)": 1, + "(2963, '2024-11-16', 1)": 0, + "(2963, '2024-11-16', 2)": 0, + "(2963, '2024-11-16', 3)": 0, + "(2963, '2024-11-16', 4)": 0, + "(2963, '2024-11-16', 5)": 0, + "(2963, '2024-11-16', 6)": 0, + "(2963, '2024-11-16', 7)": 0, + "(2963, '2024-11-17', 0)": 0, + "(2963, '2024-11-17', 1)": 0, + "(2963, '2024-11-17', 2)": 0, + "(2963, '2024-11-17', 3)": 0, + "(2963, '2024-11-17', 4)": 0, + "(2963, '2024-11-17', 5)": 0, + "(2963, '2024-11-17', 6)": 0, + "(2963, '2024-11-17', 7)": 0, + "(2963, '2024-11-18', 0)": 1, + "(2963, '2024-11-18', 1)": 0, + "(2963, '2024-11-18', 2)": 0, + "(2963, '2024-11-18', 3)": 0, + "(2963, '2024-11-18', 4)": 0, + "(2963, '2024-11-18', 5)": 0, + "(2963, '2024-11-18', 6)": 0, + "(2963, '2024-11-18', 7)": 0, + "(2963, '2024-11-19', 0)": 1, + "(2963, '2024-11-19', 1)": 0, + "(2963, '2024-11-19', 2)": 0, + "(2963, '2024-11-19', 3)": 0, + "(2963, '2024-11-19', 4)": 0, + "(2963, '2024-11-19', 5)": 0, + "(2963, '2024-11-19', 6)": 0, + "(2963, '2024-11-19', 7)": 0, + "(2963, '2024-11-20', 0)": 1, + "(2963, '2024-11-20', 1)": 0, + "(2963, '2024-11-20', 2)": 0, + "(2963, '2024-11-20', 3)": 0, + "(2963, '2024-11-20', 4)": 0, + "(2963, '2024-11-20', 5)": 0, + "(2963, '2024-11-20', 6)": 0, + "(2963, '2024-11-20', 7)": 0, + "(2963, '2024-11-21', 0)": 1, + "(2963, '2024-11-21', 1)": 0, + "(2963, '2024-11-21', 2)": 0, + "(2963, '2024-11-21', 3)": 0, + "(2963, '2024-11-21', 4)": 0, + "(2963, '2024-11-21', 5)": 0, + "(2963, '2024-11-21', 6)": 0, + "(2963, '2024-11-21', 7)": 0, + "(2963, '2024-11-22', 0)": 1, + "(2963, '2024-11-22', 1)": 0, + "(2963, '2024-11-22', 2)": 0, + "(2963, '2024-11-22', 3)": 0, + "(2963, '2024-11-22', 4)": 0, + "(2963, '2024-11-22', 5)": 0, + "(2963, '2024-11-22', 6)": 0, + "(2963, '2024-11-22', 7)": 0, + "(2963, '2024-11-23', 0)": 1, + "(2963, '2024-11-23', 1)": 0, + "(2963, '2024-11-23', 2)": 0, + "(2963, '2024-11-23', 3)": 0, + "(2963, '2024-11-23', 4)": 0, + "(2963, '2024-11-23', 5)": 0, + "(2963, '2024-11-23', 6)": 0, + "(2963, '2024-11-23', 7)": 0, + "(2963, '2024-11-24', 0)": 0, + "(2963, '2024-11-24', 1)": 0, + "(2963, '2024-11-24', 2)": 0, + "(2963, '2024-11-24', 3)": 0, + "(2963, '2024-11-24', 4)": 0, + "(2963, '2024-11-24', 5)": 0, + "(2963, '2024-11-24', 6)": 0, + "(2963, '2024-11-24', 7)": 0, + "(2963, '2024-11-25', 0)": 1, + "(2963, '2024-11-25', 1)": 0, + "(2963, '2024-11-25', 2)": 0, + "(2963, '2024-11-25', 3)": 0, + "(2963, '2024-11-25', 4)": 0, + "(2963, '2024-11-25', 5)": 0, + "(2963, '2024-11-25', 6)": 0, + "(2963, '2024-11-25', 7)": 0, + "(2963, '2024-11-26', 0)": 1, + "(2963, '2024-11-26', 1)": 0, + "(2963, '2024-11-26', 2)": 0, + "(2963, '2024-11-26', 3)": 0, + "(2963, '2024-11-26', 4)": 0, + "(2963, '2024-11-26', 5)": 0, + "(2963, '2024-11-26', 6)": 0, + "(2963, '2024-11-26', 7)": 0, + "(2963, '2024-11-27', 0)": 0, + "(2963, '2024-11-27', 1)": 0, + "(2963, '2024-11-27', 2)": 0, + "(2963, '2024-11-27', 3)": 0, + "(2963, '2024-11-27', 4)": 0, + "(2963, '2024-11-27', 5)": 0, + "(2963, '2024-11-27', 6)": 0, + "(2963, '2024-11-27', 7)": 0, + "(2963, '2024-11-28', 0)": 0, + "(2963, '2024-11-28', 1)": 0, + "(2963, '2024-11-28', 2)": 0, + "(2963, '2024-11-28', 3)": 0, + "(2963, '2024-11-28', 4)": 0, + "(2963, '2024-11-28', 5)": 0, + "(2963, '2024-11-28', 6)": 0, + "(2963, '2024-11-28', 7)": 0, + "(2963, '2024-11-29', 0)": 1, + "(2963, '2024-11-29', 1)": 0, + "(2963, '2024-11-29', 2)": 0, + "(2963, '2024-11-29', 3)": 0, + "(2963, '2024-11-29', 4)": 0, + "(2963, '2024-11-29', 5)": 0, + "(2963, '2024-11-29', 6)": 0, + "(2963, '2024-11-29', 7)": 0, + "(2963, '2024-11-30', 0)": 1, + "(2963, '2024-11-30', 1)": 0, + "(2963, '2024-11-30', 2)": 0, + "(2963, '2024-11-30', 3)": 0, + "(2963, '2024-11-30', 4)": 0, + "(2963, '2024-11-30', 5)": 0, + "(2963, '2024-11-30', 6)": 0, + "(2963, '2024-11-30', 7)": 0, + "(3566, '2024-11-01', 0)": 0, + "(3566, '2024-11-01', 1)": 0, + "(3566, '2024-11-01', 2)": 0, + "(3566, '2024-11-01', 3)": 0, + "(3566, '2024-11-01', 4)": 0, + "(3566, '2024-11-01', 5)": 0, + "(3566, '2024-11-01', 6)": 0, + "(3566, '2024-11-01', 7)": 0, + "(3566, '2024-11-02', 0)": 0, + "(3566, '2024-11-02', 1)": 0, + "(3566, '2024-11-02', 2)": 0, + "(3566, '2024-11-02', 3)": 0, + "(3566, '2024-11-02', 4)": 0, + "(3566, '2024-11-02', 5)": 0, + "(3566, '2024-11-02', 6)": 0, + "(3566, '2024-11-02', 7)": 0, + "(3566, '2024-11-03', 0)": 0, + "(3566, '2024-11-03', 1)": 0, + "(3566, '2024-11-03', 2)": 0, + "(3566, '2024-11-03', 3)": 0, + "(3566, '2024-11-03', 4)": 0, + "(3566, '2024-11-03', 5)": 0, + "(3566, '2024-11-03', 6)": 0, + "(3566, '2024-11-03', 7)": 0, + "(3566, '2024-11-04', 0)": 0, + "(3566, '2024-11-04', 1)": 0, + "(3566, '2024-11-04', 2)": 0, + "(3566, '2024-11-04', 3)": 0, + "(3566, '2024-11-04', 4)": 0, + "(3566, '2024-11-04', 5)": 0, + "(3566, '2024-11-04', 6)": 0, + "(3566, '2024-11-04', 7)": 0, + "(3566, '2024-11-05', 0)": 0, + "(3566, '2024-11-05', 1)": 0, + "(3566, '2024-11-05', 2)": 0, + "(3566, '2024-11-05', 3)": 0, + "(3566, '2024-11-05', 4)": 0, + "(3566, '2024-11-05', 5)": 0, + "(3566, '2024-11-05', 6)": 0, + "(3566, '2024-11-05', 7)": 0, + "(3566, '2024-11-06', 0)": 0, + "(3566, '2024-11-06', 1)": 0, + "(3566, '2024-11-06', 2)": 0, + "(3566, '2024-11-06', 3)": 0, + "(3566, '2024-11-06', 4)": 0, + "(3566, '2024-11-06', 5)": 0, + "(3566, '2024-11-06', 6)": 0, + "(3566, '2024-11-06', 7)": 0, + "(3566, '2024-11-07', 0)": 0, + "(3566, '2024-11-07', 1)": 0, + "(3566, '2024-11-07', 2)": 0, + "(3566, '2024-11-07', 3)": 0, + "(3566, '2024-11-07', 4)": 0, + "(3566, '2024-11-07', 5)": 0, + "(3566, '2024-11-07', 6)": 0, + "(3566, '2024-11-07', 7)": 0, + "(3566, '2024-11-08', 0)": 0, + "(3566, '2024-11-08', 1)": 0, + "(3566, '2024-11-08', 2)": 0, + "(3566, '2024-11-08', 3)": 0, + "(3566, '2024-11-08', 4)": 0, + "(3566, '2024-11-08', 5)": 0, + "(3566, '2024-11-08', 6)": 0, + "(3566, '2024-11-08', 7)": 0, + "(3566, '2024-11-09', 0)": 0, + "(3566, '2024-11-09', 1)": 0, + "(3566, '2024-11-09', 2)": 0, + "(3566, '2024-11-09', 3)": 0, + "(3566, '2024-11-09', 4)": 0, + "(3566, '2024-11-09', 5)": 0, + "(3566, '2024-11-09', 6)": 0, + "(3566, '2024-11-09', 7)": 0, + "(3566, '2024-11-10', 0)": 0, + "(3566, '2024-11-10', 1)": 0, + "(3566, '2024-11-10', 2)": 0, + "(3566, '2024-11-10', 3)": 0, + "(3566, '2024-11-10', 4)": 0, + "(3566, '2024-11-10', 5)": 0, + "(3566, '2024-11-10', 6)": 0, + "(3566, '2024-11-10', 7)": 0, + "(3566, '2024-11-11', 0)": 0, + "(3566, '2024-11-11', 1)": 0, + "(3566, '2024-11-11', 2)": 0, + "(3566, '2024-11-11', 3)": 0, + "(3566, '2024-11-11', 4)": 0, + "(3566, '2024-11-11', 5)": 0, + "(3566, '2024-11-11', 6)": 0, + "(3566, '2024-11-11', 7)": 0, + "(3566, '2024-11-12', 0)": 0, + "(3566, '2024-11-12', 1)": 0, + "(3566, '2024-11-12', 2)": 0, + "(3566, '2024-11-12', 3)": 0, + "(3566, '2024-11-12', 4)": 0, + "(3566, '2024-11-12', 5)": 0, + "(3566, '2024-11-12', 6)": 0, + "(3566, '2024-11-12', 7)": 0, + "(3566, '2024-11-13', 0)": 0, + "(3566, '2024-11-13', 1)": 0, + "(3566, '2024-11-13', 2)": 0, + "(3566, '2024-11-13', 3)": 0, + "(3566, '2024-11-13', 4)": 0, + "(3566, '2024-11-13', 5)": 0, + "(3566, '2024-11-13', 6)": 0, + "(3566, '2024-11-13', 7)": 0, + "(3566, '2024-11-14', 0)": 0, + "(3566, '2024-11-14', 1)": 0, + "(3566, '2024-11-14', 2)": 0, + "(3566, '2024-11-14', 3)": 0, + "(3566, '2024-11-14', 4)": 0, + "(3566, '2024-11-14', 5)": 0, + "(3566, '2024-11-14', 6)": 0, + "(3566, '2024-11-14', 7)": 0, + "(3566, '2024-11-15', 0)": 0, + "(3566, '2024-11-15', 1)": 0, + "(3566, '2024-11-15', 2)": 0, + "(3566, '2024-11-15', 3)": 0, + "(3566, '2024-11-15', 4)": 0, + "(3566, '2024-11-15', 5)": 0, + "(3566, '2024-11-15', 6)": 0, + "(3566, '2024-11-15', 7)": 0, + "(3566, '2024-11-16', 0)": 0, + "(3566, '2024-11-16', 1)": 0, + "(3566, '2024-11-16', 2)": 0, + "(3566, '2024-11-16', 3)": 0, + "(3566, '2024-11-16', 4)": 0, + "(3566, '2024-11-16', 5)": 0, + "(3566, '2024-11-16', 6)": 0, + "(3566, '2024-11-16', 7)": 0, + "(3566, '2024-11-17', 0)": 0, + "(3566, '2024-11-17', 1)": 0, + "(3566, '2024-11-17', 2)": 0, + "(3566, '2024-11-17', 3)": 0, + "(3566, '2024-11-17', 4)": 0, + "(3566, '2024-11-17', 5)": 0, + "(3566, '2024-11-17', 6)": 0, + "(3566, '2024-11-17', 7)": 0, + "(3566, '2024-11-18', 0)": 0, + "(3566, '2024-11-18', 1)": 0, + "(3566, '2024-11-18', 2)": 0, + "(3566, '2024-11-18', 3)": 0, + "(3566, '2024-11-18', 4)": 0, + "(3566, '2024-11-18', 5)": 0, + "(3566, '2024-11-18', 6)": 0, + "(3566, '2024-11-18', 7)": 0, + "(3566, '2024-11-19', 0)": 0, + "(3566, '2024-11-19', 1)": 0, + "(3566, '2024-11-19', 2)": 0, + "(3566, '2024-11-19', 3)": 0, + "(3566, '2024-11-19', 4)": 0, + "(3566, '2024-11-19', 5)": 0, + "(3566, '2024-11-19', 6)": 0, + "(3566, '2024-11-19', 7)": 0, + "(3566, '2024-11-20', 0)": 0, + "(3566, '2024-11-20', 1)": 0, + "(3566, '2024-11-20', 2)": 0, + "(3566, '2024-11-20', 3)": 0, + "(3566, '2024-11-20', 4)": 0, + "(3566, '2024-11-20', 5)": 0, + "(3566, '2024-11-20', 6)": 0, + "(3566, '2024-11-20', 7)": 0, + "(3566, '2024-11-21', 0)": 0, + "(3566, '2024-11-21', 1)": 0, + "(3566, '2024-11-21', 2)": 0, + "(3566, '2024-11-21', 3)": 0, + "(3566, '2024-11-21', 4)": 0, + "(3566, '2024-11-21', 5)": 0, + "(3566, '2024-11-21', 6)": 0, + "(3566, '2024-11-21', 7)": 0, + "(3566, '2024-11-22', 0)": 0, + "(3566, '2024-11-22', 1)": 0, + "(3566, '2024-11-22', 2)": 0, + "(3566, '2024-11-22', 3)": 0, + "(3566, '2024-11-22', 4)": 0, + "(3566, '2024-11-22', 5)": 0, + "(3566, '2024-11-22', 6)": 0, + "(3566, '2024-11-22', 7)": 0, + "(3566, '2024-11-23', 0)": 0, + "(3566, '2024-11-23', 1)": 0, + "(3566, '2024-11-23', 2)": 0, + "(3566, '2024-11-23', 3)": 0, + "(3566, '2024-11-23', 4)": 0, + "(3566, '2024-11-23', 5)": 0, + "(3566, '2024-11-23', 6)": 0, + "(3566, '2024-11-23', 7)": 0, + "(3566, '2024-11-24', 0)": 0, + "(3566, '2024-11-24', 1)": 0, + "(3566, '2024-11-24', 2)": 0, + "(3566, '2024-11-24', 3)": 0, + "(3566, '2024-11-24', 4)": 0, + "(3566, '2024-11-24', 5)": 0, + "(3566, '2024-11-24', 6)": 0, + "(3566, '2024-11-24', 7)": 0, + "(3566, '2024-11-25', 0)": 0, + "(3566, '2024-11-25', 1)": 0, + "(3566, '2024-11-25', 2)": 0, + "(3566, '2024-11-25', 3)": 0, + "(3566, '2024-11-25', 4)": 0, + "(3566, '2024-11-25', 5)": 0, + "(3566, '2024-11-25', 6)": 0, + "(3566, '2024-11-25', 7)": 0, + "(3566, '2024-11-26', 0)": 0, + "(3566, '2024-11-26', 1)": 0, + "(3566, '2024-11-26', 2)": 0, + "(3566, '2024-11-26', 3)": 0, + "(3566, '2024-11-26', 4)": 0, + "(3566, '2024-11-26', 5)": 0, + "(3566, '2024-11-26', 6)": 0, + "(3566, '2024-11-26', 7)": 0, + "(3566, '2024-11-27', 0)": 0, + "(3566, '2024-11-27', 1)": 0, + "(3566, '2024-11-27', 2)": 0, + "(3566, '2024-11-27', 3)": 0, + "(3566, '2024-11-27', 4)": 0, + "(3566, '2024-11-27', 5)": 0, + "(3566, '2024-11-27', 6)": 0, + "(3566, '2024-11-27', 7)": 0, + "(3566, '2024-11-28', 0)": 0, + "(3566, '2024-11-28', 1)": 0, + "(3566, '2024-11-28', 2)": 0, + "(3566, '2024-11-28', 3)": 0, + "(3566, '2024-11-28', 4)": 0, + "(3566, '2024-11-28', 5)": 0, + "(3566, '2024-11-28', 6)": 0, + "(3566, '2024-11-28', 7)": 0, + "(3566, '2024-11-29', 0)": 0, + "(3566, '2024-11-29', 1)": 0, + "(3566, '2024-11-29', 2)": 0, + "(3566, '2024-11-29', 3)": 0, + "(3566, '2024-11-29', 4)": 0, + "(3566, '2024-11-29', 5)": 0, + "(3566, '2024-11-29', 6)": 0, + "(3566, '2024-11-29', 7)": 0, + "(3566, '2024-11-30', 0)": 0, + "(3566, '2024-11-30', 1)": 0, + "(3566, '2024-11-30', 2)": 0, + "(3566, '2024-11-30', 3)": 0, + "(3566, '2024-11-30', 4)": 0, + "(3566, '2024-11-30', 5)": 0, + "(3566, '2024-11-30', 6)": 0, + "(3566, '2024-11-30', 7)": 0, + "(3868, '2024-11-01', 0)": 1, + "(3868, '2024-11-01', 1)": 0, + "(3868, '2024-11-01', 2)": 0, + "(3868, '2024-11-01', 3)": 0, + "(3868, '2024-11-01', 4)": 0, + "(3868, '2024-11-01', 5)": 0, + "(3868, '2024-11-01', 6)": 0, + "(3868, '2024-11-01', 7)": 0, + "(3868, '2024-11-02', 0)": 0, + "(3868, '2024-11-02', 1)": 1, + "(3868, '2024-11-02', 2)": 0, + "(3868, '2024-11-02', 3)": 0, + "(3868, '2024-11-02', 4)": 0, + "(3868, '2024-11-02', 5)": 0, + "(3868, '2024-11-02', 6)": 0, + "(3868, '2024-11-02', 7)": 0, + "(3868, '2024-11-03', 0)": 1, + "(3868, '2024-11-03', 1)": 0, + "(3868, '2024-11-03', 2)": 0, + "(3868, '2024-11-03', 3)": 0, + "(3868, '2024-11-03', 4)": 0, + "(3868, '2024-11-03', 5)": 0, + "(3868, '2024-11-03', 6)": 0, + "(3868, '2024-11-03', 7)": 0, + "(3868, '2024-11-04', 0)": 1, + "(3868, '2024-11-04', 1)": 0, + "(3868, '2024-11-04', 2)": 0, + "(3868, '2024-11-04', 3)": 0, + "(3868, '2024-11-04', 4)": 0, + "(3868, '2024-11-04', 5)": 0, + "(3868, '2024-11-04', 6)": 0, + "(3868, '2024-11-04', 7)": 0, + "(3868, '2024-11-05', 0)": 0, + "(3868, '2024-11-05', 1)": 0, + "(3868, '2024-11-05', 2)": 0, + "(3868, '2024-11-05', 3)": 0, + "(3868, '2024-11-05', 4)": 0, + "(3868, '2024-11-05', 5)": 0, + "(3868, '2024-11-05', 6)": 0, + "(3868, '2024-11-05', 7)": 0, + "(3868, '2024-11-06', 0)": 1, + "(3868, '2024-11-06', 1)": 0, + "(3868, '2024-11-06', 2)": 0, + "(3868, '2024-11-06', 3)": 0, + "(3868, '2024-11-06', 4)": 0, + "(3868, '2024-11-06', 5)": 0, + "(3868, '2024-11-06', 6)": 0, + "(3868, '2024-11-06', 7)": 0, + "(3868, '2024-11-07', 0)": 0, + "(3868, '2024-11-07', 1)": 0, + "(3868, '2024-11-07', 2)": 0, + "(3868, '2024-11-07', 3)": 0, + "(3868, '2024-11-07', 4)": 0, + "(3868, '2024-11-07', 5)": 0, + "(3868, '2024-11-07', 6)": 0, + "(3868, '2024-11-07', 7)": 0, + "(3868, '2024-11-08', 0)": 1, + "(3868, '2024-11-08', 1)": 0, + "(3868, '2024-11-08', 2)": 0, + "(3868, '2024-11-08', 3)": 0, + "(3868, '2024-11-08', 4)": 0, + "(3868, '2024-11-08', 5)": 0, + "(3868, '2024-11-08', 6)": 0, + "(3868, '2024-11-08', 7)": 0, + "(3868, '2024-11-09', 0)": 0, + "(3868, '2024-11-09', 1)": 0, + "(3868, '2024-11-09', 2)": 1, + "(3868, '2024-11-09', 3)": 0, + "(3868, '2024-11-09', 4)": 0, + "(3868, '2024-11-09', 5)": 0, + "(3868, '2024-11-09', 6)": 0, + "(3868, '2024-11-09', 7)": 0, + "(3868, '2024-11-10', 0)": 0, + "(3868, '2024-11-10', 1)": 0, + "(3868, '2024-11-10', 2)": 0, + "(3868, '2024-11-10', 3)": 0, + "(3868, '2024-11-10', 4)": 0, + "(3868, '2024-11-10', 5)": 0, + "(3868, '2024-11-10', 6)": 0, + "(3868, '2024-11-10', 7)": 0, + "(3868, '2024-11-11', 0)": 0, + "(3868, '2024-11-11', 1)": 0, + "(3868, '2024-11-11', 2)": 0, + "(3868, '2024-11-11', 3)": 1, + "(3868, '2024-11-11', 4)": 0, + "(3868, '2024-11-11', 5)": 0, + "(3868, '2024-11-11', 6)": 0, + "(3868, '2024-11-11', 7)": 0, + "(3868, '2024-11-12', 0)": 0, + "(3868, '2024-11-12', 1)": 0, + "(3868, '2024-11-12', 2)": 0, + "(3868, '2024-11-12', 3)": 0, + "(3868, '2024-11-12', 4)": 0, + "(3868, '2024-11-12', 5)": 0, + "(3868, '2024-11-12', 6)": 0, + "(3868, '2024-11-12', 7)": 0, + "(3868, '2024-11-13', 0)": 0, + "(3868, '2024-11-13', 1)": 0, + "(3868, '2024-11-13', 2)": 0, + "(3868, '2024-11-13', 3)": 0, + "(3868, '2024-11-13', 4)": 0, + "(3868, '2024-11-13', 5)": 0, + "(3868, '2024-11-13', 6)": 0, + "(3868, '2024-11-13', 7)": 0, + "(3868, '2024-11-14', 0)": 0, + "(3868, '2024-11-14', 1)": 0, + "(3868, '2024-11-14', 2)": 0, + "(3868, '2024-11-14', 3)": 0, + "(3868, '2024-11-14', 4)": 0, + "(3868, '2024-11-14', 5)": 0, + "(3868, '2024-11-14', 6)": 0, + "(3868, '2024-11-14', 7)": 0, + "(3868, '2024-11-15', 0)": 0, + "(3868, '2024-11-15', 1)": 0, + "(3868, '2024-11-15', 2)": 0, + "(3868, '2024-11-15', 3)": 0, + "(3868, '2024-11-15', 4)": 0, + "(3868, '2024-11-15', 5)": 0, + "(3868, '2024-11-15', 6)": 0, + "(3868, '2024-11-15', 7)": 0, + "(3868, '2024-11-16', 0)": 1, + "(3868, '2024-11-16', 1)": 0, + "(3868, '2024-11-16', 2)": 0, + "(3868, '2024-11-16', 3)": 0, + "(3868, '2024-11-16', 4)": 0, + "(3868, '2024-11-16', 5)": 0, + "(3868, '2024-11-16', 6)": 0, + "(3868, '2024-11-16', 7)": 0, + "(3868, '2024-11-17', 0)": 0, + "(3868, '2024-11-17', 1)": 0, + "(3868, '2024-11-17', 2)": 1, + "(3868, '2024-11-17', 3)": 0, + "(3868, '2024-11-17', 4)": 0, + "(3868, '2024-11-17', 5)": 0, + "(3868, '2024-11-17', 6)": 0, + "(3868, '2024-11-17', 7)": 0, + "(3868, '2024-11-18', 0)": 0, + "(3868, '2024-11-18', 1)": 0, + "(3868, '2024-11-18', 2)": 0, + "(3868, '2024-11-18', 3)": 0, + "(3868, '2024-11-18', 4)": 0, + "(3868, '2024-11-18', 5)": 0, + "(3868, '2024-11-18', 6)": 0, + "(3868, '2024-11-18', 7)": 0, + "(3868, '2024-11-19', 0)": 0, + "(3868, '2024-11-19', 1)": 0, + "(3868, '2024-11-19', 2)": 0, + "(3868, '2024-11-19', 3)": 0, + "(3868, '2024-11-19', 4)": 0, + "(3868, '2024-11-19', 5)": 0, + "(3868, '2024-11-19', 6)": 0, + "(3868, '2024-11-19', 7)": 0, + "(3868, '2024-11-20', 0)": 0, + "(3868, '2024-11-20', 1)": 0, + "(3868, '2024-11-20', 2)": 0, + "(3868, '2024-11-20', 3)": 0, + "(3868, '2024-11-20', 4)": 0, + "(3868, '2024-11-20', 5)": 0, + "(3868, '2024-11-20', 6)": 0, + "(3868, '2024-11-20', 7)": 0, + "(3868, '2024-11-21', 0)": 0, + "(3868, '2024-11-21', 1)": 0, + "(3868, '2024-11-21', 2)": 1, + "(3868, '2024-11-21', 3)": 0, + "(3868, '2024-11-21', 4)": 0, + "(3868, '2024-11-21', 5)": 0, + "(3868, '2024-11-21', 6)": 0, + "(3868, '2024-11-21', 7)": 0, + "(3868, '2024-11-22', 0)": 0, + "(3868, '2024-11-22', 1)": 0, + "(3868, '2024-11-22', 2)": 1, + "(3868, '2024-11-22', 3)": 0, + "(3868, '2024-11-22', 4)": 0, + "(3868, '2024-11-22', 5)": 0, + "(3868, '2024-11-22', 6)": 0, + "(3868, '2024-11-22', 7)": 0, + "(3868, '2024-11-23', 0)": 0, + "(3868, '2024-11-23', 1)": 0, + "(3868, '2024-11-23', 2)": 1, + "(3868, '2024-11-23', 3)": 0, + "(3868, '2024-11-23', 4)": 0, + "(3868, '2024-11-23', 5)": 0, + "(3868, '2024-11-23', 6)": 0, + "(3868, '2024-11-23', 7)": 0, + "(3868, '2024-11-24', 0)": 0, + "(3868, '2024-11-24', 1)": 0, + "(3868, '2024-11-24', 2)": 1, + "(3868, '2024-11-24', 3)": 0, + "(3868, '2024-11-24', 4)": 0, + "(3868, '2024-11-24', 5)": 0, + "(3868, '2024-11-24', 6)": 0, + "(3868, '2024-11-24', 7)": 0, + "(3868, '2024-11-25', 0)": 0, + "(3868, '2024-11-25', 1)": 0, + "(3868, '2024-11-25', 2)": 0, + "(3868, '2024-11-25', 3)": 0, + "(3868, '2024-11-25', 4)": 0, + "(3868, '2024-11-25', 5)": 0, + "(3868, '2024-11-25', 6)": 1, + "(3868, '2024-11-25', 7)": 0, + "(3868, '2024-11-26', 0)": 0, + "(3868, '2024-11-26', 1)": 0, + "(3868, '2024-11-26', 2)": 0, + "(3868, '2024-11-26', 3)": 0, + "(3868, '2024-11-26', 4)": 0, + "(3868, '2024-11-26', 5)": 0, + "(3868, '2024-11-26', 6)": 0, + "(3868, '2024-11-26', 7)": 0, + "(3868, '2024-11-27', 0)": 1, + "(3868, '2024-11-27', 1)": 0, + "(3868, '2024-11-27', 2)": 0, + "(3868, '2024-11-27', 3)": 0, + "(3868, '2024-11-27', 4)": 0, + "(3868, '2024-11-27', 5)": 0, + "(3868, '2024-11-27', 6)": 0, + "(3868, '2024-11-27', 7)": 0, + "(3868, '2024-11-28', 0)": 0, + "(3868, '2024-11-28', 1)": 0, + "(3868, '2024-11-28', 2)": 1, + "(3868, '2024-11-28', 3)": 0, + "(3868, '2024-11-28', 4)": 0, + "(3868, '2024-11-28', 5)": 0, + "(3868, '2024-11-28', 6)": 0, + "(3868, '2024-11-28', 7)": 0, + "(3868, '2024-11-29', 0)": 0, + "(3868, '2024-11-29', 1)": 1, + "(3868, '2024-11-29', 2)": 0, + "(3868, '2024-11-29', 3)": 0, + "(3868, '2024-11-29', 4)": 0, + "(3868, '2024-11-29', 5)": 0, + "(3868, '2024-11-29', 6)": 0, + "(3868, '2024-11-29', 7)": 0, + "(3868, '2024-11-30', 0)": 0, + "(3868, '2024-11-30', 1)": 0, + "(3868, '2024-11-30', 2)": 1, + "(3868, '2024-11-30', 3)": 0, + "(3868, '2024-11-30', 4)": 0, + "(3868, '2024-11-30', 5)": 0, + "(3868, '2024-11-30', 6)": 0, + "(3868, '2024-11-30', 7)": 0, + "(4566, '2024-11-01', 0)": 0, + "(4566, '2024-11-01', 1)": 1, + "(4566, '2024-11-01', 2)": 0, + "(4566, '2024-11-01', 3)": 0, + "(4566, '2024-11-01', 4)": 0, + "(4566, '2024-11-01', 5)": 0, + "(4566, '2024-11-01', 6)": 0, + "(4566, '2024-11-01', 7)": 0, + "(4566, '2024-11-02', 0)": 1, + "(4566, '2024-11-02', 1)": 0, + "(4566, '2024-11-02', 2)": 0, + "(4566, '2024-11-02', 3)": 0, + "(4566, '2024-11-02', 4)": 0, + "(4566, '2024-11-02', 5)": 0, + "(4566, '2024-11-02', 6)": 0, + "(4566, '2024-11-02', 7)": 0, + "(4566, '2024-11-03', 0)": 1, + "(4566, '2024-11-03', 1)": 0, + "(4566, '2024-11-03', 2)": 0, + "(4566, '2024-11-03', 3)": 0, + "(4566, '2024-11-03', 4)": 0, + "(4566, '2024-11-03', 5)": 0, + "(4566, '2024-11-03', 6)": 0, + "(4566, '2024-11-03', 7)": 0, + "(4566, '2024-11-04', 0)": 1, + "(4566, '2024-11-04', 1)": 0, + "(4566, '2024-11-04', 2)": 0, + "(4566, '2024-11-04', 3)": 0, + "(4566, '2024-11-04', 4)": 0, + "(4566, '2024-11-04', 5)": 0, + "(4566, '2024-11-04', 6)": 0, + "(4566, '2024-11-04', 7)": 0, + "(4566, '2024-11-05', 0)": 1, + "(4566, '2024-11-05', 1)": 0, + "(4566, '2024-11-05', 2)": 0, + "(4566, '2024-11-05', 3)": 0, + "(4566, '2024-11-05', 4)": 0, + "(4566, '2024-11-05', 5)": 0, + "(4566, '2024-11-05', 6)": 0, + "(4566, '2024-11-05', 7)": 0, + "(4566, '2024-11-06', 0)": 1, + "(4566, '2024-11-06', 1)": 0, + "(4566, '2024-11-06', 2)": 0, + "(4566, '2024-11-06', 3)": 0, + "(4566, '2024-11-06', 4)": 0, + "(4566, '2024-11-06', 5)": 0, + "(4566, '2024-11-06', 6)": 0, + "(4566, '2024-11-06', 7)": 0, + "(4566, '2024-11-07', 0)": 0, + "(4566, '2024-11-07', 1)": 0, + "(4566, '2024-11-07', 2)": 1, + "(4566, '2024-11-07', 3)": 0, + "(4566, '2024-11-07', 4)": 0, + "(4566, '2024-11-07', 5)": 0, + "(4566, '2024-11-07', 6)": 0, + "(4566, '2024-11-07', 7)": 0, + "(4566, '2024-11-08', 0)": 0, + "(4566, '2024-11-08', 1)": 0, + "(4566, '2024-11-08', 2)": 1, + "(4566, '2024-11-08', 3)": 0, + "(4566, '2024-11-08', 4)": 0, + "(4566, '2024-11-08', 5)": 0, + "(4566, '2024-11-08', 6)": 0, + "(4566, '2024-11-08', 7)": 0, + "(4566, '2024-11-09', 0)": 0, + "(4566, '2024-11-09', 1)": 0, + "(4566, '2024-11-09', 2)": 0, + "(4566, '2024-11-09', 3)": 0, + "(4566, '2024-11-09', 4)": 0, + "(4566, '2024-11-09', 5)": 0, + "(4566, '2024-11-09', 6)": 0, + "(4566, '2024-11-09', 7)": 0, + "(4566, '2024-11-10', 0)": 0, + "(4566, '2024-11-10', 1)": 0, + "(4566, '2024-11-10', 2)": 0, + "(4566, '2024-11-10', 3)": 0, + "(4566, '2024-11-10', 4)": 0, + "(4566, '2024-11-10', 5)": 0, + "(4566, '2024-11-10', 6)": 0, + "(4566, '2024-11-10', 7)": 0, + "(4566, '2024-11-11', 0)": 0, + "(4566, '2024-11-11', 1)": 0, + "(4566, '2024-11-11', 2)": 0, + "(4566, '2024-11-11', 3)": 0, + "(4566, '2024-11-11', 4)": 0, + "(4566, '2024-11-11', 5)": 0, + "(4566, '2024-11-11', 6)": 0, + "(4566, '2024-11-11', 7)": 0, + "(4566, '2024-11-12', 0)": 0, + "(4566, '2024-11-12', 1)": 0, + "(4566, '2024-11-12', 2)": 0, + "(4566, '2024-11-12', 3)": 0, + "(4566, '2024-11-12', 4)": 0, + "(4566, '2024-11-12', 5)": 0, + "(4566, '2024-11-12', 6)": 0, + "(4566, '2024-11-12', 7)": 0, + "(4566, '2024-11-13', 0)": 0, + "(4566, '2024-11-13', 1)": 0, + "(4566, '2024-11-13', 2)": 0, + "(4566, '2024-11-13', 3)": 0, + "(4566, '2024-11-13', 4)": 0, + "(4566, '2024-11-13', 5)": 0, + "(4566, '2024-11-13', 6)": 0, + "(4566, '2024-11-13', 7)": 0, + "(4566, '2024-11-14', 0)": 0, + "(4566, '2024-11-14', 1)": 0, + "(4566, '2024-11-14', 2)": 0, + "(4566, '2024-11-14', 3)": 0, + "(4566, '2024-11-14', 4)": 0, + "(4566, '2024-11-14', 5)": 0, + "(4566, '2024-11-14', 6)": 0, + "(4566, '2024-11-14', 7)": 0, + "(4566, '2024-11-15', 0)": 0, + "(4566, '2024-11-15', 1)": 0, + "(4566, '2024-11-15', 2)": 1, + "(4566, '2024-11-15', 3)": 0, + "(4566, '2024-11-15', 4)": 0, + "(4566, '2024-11-15', 5)": 0, + "(4566, '2024-11-15', 6)": 0, + "(4566, '2024-11-15', 7)": 0, + "(4566, '2024-11-16', 0)": 0, + "(4566, '2024-11-16', 1)": 0, + "(4566, '2024-11-16', 2)": 0, + "(4566, '2024-11-16', 3)": 0, + "(4566, '2024-11-16', 4)": 0, + "(4566, '2024-11-16', 5)": 0, + "(4566, '2024-11-16', 6)": 0, + "(4566, '2024-11-16', 7)": 0, + "(4566, '2024-11-17', 0)": 0, + "(4566, '2024-11-17', 1)": 0, + "(4566, '2024-11-17', 2)": 1, + "(4566, '2024-11-17', 3)": 0, + "(4566, '2024-11-17', 4)": 0, + "(4566, '2024-11-17', 5)": 0, + "(4566, '2024-11-17', 6)": 0, + "(4566, '2024-11-17', 7)": 0, + "(4566, '2024-11-18', 0)": 0, + "(4566, '2024-11-18', 1)": 0, + "(4566, '2024-11-18', 2)": 0, + "(4566, '2024-11-18', 3)": 0, + "(4566, '2024-11-18', 4)": 0, + "(4566, '2024-11-18', 5)": 0, + "(4566, '2024-11-18', 6)": 0, + "(4566, '2024-11-18', 7)": 0, + "(4566, '2024-11-19', 0)": 0, + "(4566, '2024-11-19', 1)": 1, + "(4566, '2024-11-19', 2)": 0, + "(4566, '2024-11-19', 3)": 0, + "(4566, '2024-11-19', 4)": 0, + "(4566, '2024-11-19', 5)": 0, + "(4566, '2024-11-19', 6)": 0, + "(4566, '2024-11-19', 7)": 0, + "(4566, '2024-11-20', 0)": 0, + "(4566, '2024-11-20', 1)": 1, + "(4566, '2024-11-20', 2)": 0, + "(4566, '2024-11-20', 3)": 0, + "(4566, '2024-11-20', 4)": 0, + "(4566, '2024-11-20', 5)": 0, + "(4566, '2024-11-20', 6)": 0, + "(4566, '2024-11-20', 7)": 0, + "(4566, '2024-11-21', 0)": 0, + "(4566, '2024-11-21', 1)": 1, + "(4566, '2024-11-21', 2)": 0, + "(4566, '2024-11-21', 3)": 0, + "(4566, '2024-11-21', 4)": 0, + "(4566, '2024-11-21', 5)": 0, + "(4566, '2024-11-21', 6)": 0, + "(4566, '2024-11-21', 7)": 0, + "(4566, '2024-11-22', 0)": 1, + "(4566, '2024-11-22', 1)": 0, + "(4566, '2024-11-22', 2)": 0, + "(4566, '2024-11-22', 3)": 0, + "(4566, '2024-11-22', 4)": 0, + "(4566, '2024-11-22', 5)": 0, + "(4566, '2024-11-22', 6)": 0, + "(4566, '2024-11-22', 7)": 0, + "(4566, '2024-11-23', 0)": 0, + "(4566, '2024-11-23', 1)": 0, + "(4566, '2024-11-23', 2)": 1, + "(4566, '2024-11-23', 3)": 0, + "(4566, '2024-11-23', 4)": 0, + "(4566, '2024-11-23', 5)": 0, + "(4566, '2024-11-23', 6)": 0, + "(4566, '2024-11-23', 7)": 0, + "(4566, '2024-11-24', 0)": 0, + "(4566, '2024-11-24', 1)": 0, + "(4566, '2024-11-24', 2)": 0, + "(4566, '2024-11-24', 3)": 0, + "(4566, '2024-11-24', 4)": 0, + "(4566, '2024-11-24', 5)": 0, + "(4566, '2024-11-24', 6)": 0, + "(4566, '2024-11-24', 7)": 0, + "(4566, '2024-11-25', 0)": 1, + "(4566, '2024-11-25', 1)": 0, + "(4566, '2024-11-25', 2)": 0, + "(4566, '2024-11-25', 3)": 0, + "(4566, '2024-11-25', 4)": 0, + "(4566, '2024-11-25', 5)": 0, + "(4566, '2024-11-25', 6)": 0, + "(4566, '2024-11-25', 7)": 0, + "(4566, '2024-11-26', 0)": 0, + "(4566, '2024-11-26', 1)": 0, + "(4566, '2024-11-26', 2)": 1, + "(4566, '2024-11-26', 3)": 0, + "(4566, '2024-11-26', 4)": 0, + "(4566, '2024-11-26', 5)": 0, + "(4566, '2024-11-26', 6)": 0, + "(4566, '2024-11-26', 7)": 0, + "(4566, '2024-11-27', 0)": 0, + "(4566, '2024-11-27', 1)": 0, + "(4566, '2024-11-27', 2)": 1, + "(4566, '2024-11-27', 3)": 0, + "(4566, '2024-11-27', 4)": 0, + "(4566, '2024-11-27', 5)": 0, + "(4566, '2024-11-27', 6)": 0, + "(4566, '2024-11-27', 7)": 0, + "(4566, '2024-11-28', 0)": 0, + "(4566, '2024-11-28', 1)": 0, + "(4566, '2024-11-28', 2)": 0, + "(4566, '2024-11-28', 3)": 0, + "(4566, '2024-11-28', 4)": 0, + "(4566, '2024-11-28', 5)": 0, + "(4566, '2024-11-28', 6)": 0, + "(4566, '2024-11-28', 7)": 0, + "(4566, '2024-11-29', 0)": 0, + "(4566, '2024-11-29', 1)": 1, + "(4566, '2024-11-29', 2)": 0, + "(4566, '2024-11-29', 3)": 0, + "(4566, '2024-11-29', 4)": 0, + "(4566, '2024-11-29', 5)": 0, + "(4566, '2024-11-29', 6)": 0, + "(4566, '2024-11-29', 7)": 0, + "(4566, '2024-11-30', 0)": 0, + "(4566, '2024-11-30', 1)": 0, + "(4566, '2024-11-30', 2)": 0, + "(4566, '2024-11-30', 3)": 0, + "(4566, '2024-11-30', 4)": 0, + "(4566, '2024-11-30', 5)": 0, + "(4566, '2024-11-30', 6)": 0, + "(4566, '2024-11-30', 7)": 0, + "(459, '2024-11-01', 0)": 0, + "(459, '2024-11-01', 1)": 0, + "(459, '2024-11-01', 2)": 0, + "(459, '2024-11-01', 3)": 0, + "(459, '2024-11-01', 4)": 0, + "(459, '2024-11-01', 5)": 0, + "(459, '2024-11-01', 6)": 0, + "(459, '2024-11-01', 7)": 0, + "(459, '2024-11-02', 0)": 0, + "(459, '2024-11-02', 1)": 0, + "(459, '2024-11-02', 2)": 0, + "(459, '2024-11-02', 3)": 0, + "(459, '2024-11-02', 4)": 0, + "(459, '2024-11-02', 5)": 0, + "(459, '2024-11-02', 6)": 0, + "(459, '2024-11-02', 7)": 0, + "(459, '2024-11-03', 0)": 0, + "(459, '2024-11-03', 1)": 0, + "(459, '2024-11-03', 2)": 0, + "(459, '2024-11-03', 3)": 0, + "(459, '2024-11-03', 4)": 0, + "(459, '2024-11-03', 5)": 0, + "(459, '2024-11-03', 6)": 0, + "(459, '2024-11-03', 7)": 0, + "(459, '2024-11-04', 0)": 0, + "(459, '2024-11-04', 1)": 0, + "(459, '2024-11-04', 2)": 0, + "(459, '2024-11-04', 3)": 0, + "(459, '2024-11-04', 4)": 0, + "(459, '2024-11-04', 5)": 0, + "(459, '2024-11-04', 6)": 0, + "(459, '2024-11-04', 7)": 0, + "(459, '2024-11-05', 0)": 0, + "(459, '2024-11-05', 1)": 0, + "(459, '2024-11-05', 2)": 0, + "(459, '2024-11-05', 3)": 0, + "(459, '2024-11-05', 4)": 0, + "(459, '2024-11-05', 5)": 0, + "(459, '2024-11-05', 6)": 0, + "(459, '2024-11-05', 7)": 0, + "(459, '2024-11-06', 0)": 0, + "(459, '2024-11-06', 1)": 0, + "(459, '2024-11-06', 2)": 0, + "(459, '2024-11-06', 3)": 0, + "(459, '2024-11-06', 4)": 0, + "(459, '2024-11-06', 5)": 0, + "(459, '2024-11-06', 6)": 0, + "(459, '2024-11-06', 7)": 0, + "(459, '2024-11-07', 0)": 0, + "(459, '2024-11-07', 1)": 0, + "(459, '2024-11-07', 2)": 0, + "(459, '2024-11-07', 3)": 0, + "(459, '2024-11-07', 4)": 0, + "(459, '2024-11-07', 5)": 0, + "(459, '2024-11-07', 6)": 0, + "(459, '2024-11-07', 7)": 0, + "(459, '2024-11-08', 0)": 0, + "(459, '2024-11-08', 1)": 0, + "(459, '2024-11-08', 2)": 0, + "(459, '2024-11-08', 3)": 0, + "(459, '2024-11-08', 4)": 0, + "(459, '2024-11-08', 5)": 0, + "(459, '2024-11-08', 6)": 0, + "(459, '2024-11-08', 7)": 0, + "(459, '2024-11-09', 0)": 0, + "(459, '2024-11-09', 1)": 0, + "(459, '2024-11-09', 2)": 0, + "(459, '2024-11-09', 3)": 0, + "(459, '2024-11-09', 4)": 0, + "(459, '2024-11-09', 5)": 0, + "(459, '2024-11-09', 6)": 0, + "(459, '2024-11-09', 7)": 0, + "(459, '2024-11-10', 0)": 0, + "(459, '2024-11-10', 1)": 0, + "(459, '2024-11-10', 2)": 0, + "(459, '2024-11-10', 3)": 0, + "(459, '2024-11-10', 4)": 0, + "(459, '2024-11-10', 5)": 0, + "(459, '2024-11-10', 6)": 0, + "(459, '2024-11-10', 7)": 0, + "(459, '2024-11-11', 0)": 0, + "(459, '2024-11-11', 1)": 0, + "(459, '2024-11-11', 2)": 0, + "(459, '2024-11-11', 3)": 0, + "(459, '2024-11-11', 4)": 0, + "(459, '2024-11-11', 5)": 0, + "(459, '2024-11-11', 6)": 0, + "(459, '2024-11-11', 7)": 0, + "(459, '2024-11-12', 0)": 0, + "(459, '2024-11-12', 1)": 0, + "(459, '2024-11-12', 2)": 0, + "(459, '2024-11-12', 3)": 0, + "(459, '2024-11-12', 4)": 0, + "(459, '2024-11-12', 5)": 0, + "(459, '2024-11-12', 6)": 0, + "(459, '2024-11-12', 7)": 0, + "(459, '2024-11-13', 0)": 0, + "(459, '2024-11-13', 1)": 0, + "(459, '2024-11-13', 2)": 1, + "(459, '2024-11-13', 3)": 0, + "(459, '2024-11-13', 4)": 0, + "(459, '2024-11-13', 5)": 0, + "(459, '2024-11-13', 6)": 0, + "(459, '2024-11-13', 7)": 0, + "(459, '2024-11-14', 0)": 0, + "(459, '2024-11-14', 1)": 0, + "(459, '2024-11-14', 2)": 0, + "(459, '2024-11-14', 3)": 0, + "(459, '2024-11-14', 4)": 0, + "(459, '2024-11-14', 5)": 0, + "(459, '2024-11-14', 6)": 0, + "(459, '2024-11-14', 7)": 0, + "(459, '2024-11-15', 0)": 0, + "(459, '2024-11-15', 1)": 1, + "(459, '2024-11-15', 2)": 0, + "(459, '2024-11-15', 3)": 0, + "(459, '2024-11-15', 4)": 0, + "(459, '2024-11-15', 5)": 0, + "(459, '2024-11-15', 6)": 0, + "(459, '2024-11-15', 7)": 0, + "(459, '2024-11-16', 0)": 0, + "(459, '2024-11-16', 1)": 0, + "(459, '2024-11-16', 2)": 0, + "(459, '2024-11-16', 3)": 0, + "(459, '2024-11-16', 4)": 0, + "(459, '2024-11-16', 5)": 0, + "(459, '2024-11-16', 6)": 0, + "(459, '2024-11-16', 7)": 0, + "(459, '2024-11-17', 0)": 0, + "(459, '2024-11-17', 1)": 0, + "(459, '2024-11-17', 2)": 0, + "(459, '2024-11-17', 3)": 0, + "(459, '2024-11-17', 4)": 0, + "(459, '2024-11-17', 5)": 0, + "(459, '2024-11-17', 6)": 0, + "(459, '2024-11-17', 7)": 0, + "(459, '2024-11-18', 0)": 0, + "(459, '2024-11-18', 1)": 0, + "(459, '2024-11-18', 2)": 0, + "(459, '2024-11-18', 3)": 0, + "(459, '2024-11-18', 4)": 0, + "(459, '2024-11-18', 5)": 0, + "(459, '2024-11-18', 6)": 0, + "(459, '2024-11-18', 7)": 0, + "(459, '2024-11-19', 0)": 1, + "(459, '2024-11-19', 1)": 0, + "(459, '2024-11-19', 2)": 0, + "(459, '2024-11-19', 3)": 0, + "(459, '2024-11-19', 4)": 0, + "(459, '2024-11-19', 5)": 0, + "(459, '2024-11-19', 6)": 0, + "(459, '2024-11-19', 7)": 0, + "(459, '2024-11-20', 0)": 0, + "(459, '2024-11-20', 1)": 0, + "(459, '2024-11-20', 2)": 1, + "(459, '2024-11-20', 3)": 0, + "(459, '2024-11-20', 4)": 0, + "(459, '2024-11-20', 5)": 0, + "(459, '2024-11-20', 6)": 0, + "(459, '2024-11-20', 7)": 0, + "(459, '2024-11-21', 0)": 0, + "(459, '2024-11-21', 1)": 0, + "(459, '2024-11-21', 2)": 0, + "(459, '2024-11-21', 3)": 0, + "(459, '2024-11-21', 4)": 0, + "(459, '2024-11-21', 5)": 0, + "(459, '2024-11-21', 6)": 0, + "(459, '2024-11-21', 7)": 0, + "(459, '2024-11-22', 0)": 0, + "(459, '2024-11-22', 1)": 0, + "(459, '2024-11-22', 2)": 0, + "(459, '2024-11-22', 3)": 0, + "(459, '2024-11-22', 4)": 0, + "(459, '2024-11-22', 5)": 0, + "(459, '2024-11-22', 6)": 0, + "(459, '2024-11-22', 7)": 0, + "(459, '2024-11-23', 0)": 0, + "(459, '2024-11-23', 1)": 0, + "(459, '2024-11-23', 2)": 0, + "(459, '2024-11-23', 3)": 0, + "(459, '2024-11-23', 4)": 0, + "(459, '2024-11-23', 5)": 0, + "(459, '2024-11-23', 6)": 0, + "(459, '2024-11-23', 7)": 0, + "(459, '2024-11-24', 0)": 0, + "(459, '2024-11-24', 1)": 0, + "(459, '2024-11-24', 2)": 0, + "(459, '2024-11-24', 3)": 0, + "(459, '2024-11-24', 4)": 0, + "(459, '2024-11-24', 5)": 0, + "(459, '2024-11-24', 6)": 0, + "(459, '2024-11-24', 7)": 0, + "(459, '2024-11-25', 0)": 0, + "(459, '2024-11-25', 1)": 0, + "(459, '2024-11-25', 2)": 0, + "(459, '2024-11-25', 3)": 0, + "(459, '2024-11-25', 4)": 0, + "(459, '2024-11-25', 5)": 0, + "(459, '2024-11-25', 6)": 0, + "(459, '2024-11-25', 7)": 0, + "(459, '2024-11-26', 0)": 0, + "(459, '2024-11-26', 1)": 0, + "(459, '2024-11-26', 2)": 0, + "(459, '2024-11-26', 3)": 0, + "(459, '2024-11-26', 4)": 0, + "(459, '2024-11-26', 5)": 0, + "(459, '2024-11-26', 6)": 0, + "(459, '2024-11-26', 7)": 0, + "(459, '2024-11-27', 0)": 1, + "(459, '2024-11-27', 1)": 0, + "(459, '2024-11-27', 2)": 0, + "(459, '2024-11-27', 3)": 0, + "(459, '2024-11-27', 4)": 0, + "(459, '2024-11-27', 5)": 0, + "(459, '2024-11-27', 6)": 0, + "(459, '2024-11-27', 7)": 0, + "(459, '2024-11-28', 0)": 0, + "(459, '2024-11-28', 1)": 0, + "(459, '2024-11-28', 2)": 0, + "(459, '2024-11-28', 3)": 0, + "(459, '2024-11-28', 4)": 0, + "(459, '2024-11-28', 5)": 0, + "(459, '2024-11-28', 6)": 0, + "(459, '2024-11-28', 7)": 0, + "(459, '2024-11-29', 0)": 0, + "(459, '2024-11-29', 1)": 0, + "(459, '2024-11-29', 2)": 0, + "(459, '2024-11-29', 3)": 0, + "(459, '2024-11-29', 4)": 0, + "(459, '2024-11-29', 5)": 0, + "(459, '2024-11-29', 6)": 0, + "(459, '2024-11-29', 7)": 0, + "(459, '2024-11-30', 0)": 0, + "(459, '2024-11-30', 1)": 0, + "(459, '2024-11-30', 2)": 0, + "(459, '2024-11-30', 3)": 0, + "(459, '2024-11-30', 4)": 0, + "(459, '2024-11-30', 5)": 0, + "(459, '2024-11-30', 6)": 0, + "(459, '2024-11-30', 7)": 0, + "(5367, '2024-11-01', 0)": 1, + "(5367, '2024-11-01', 1)": 0, + "(5367, '2024-11-01', 2)": 0, + "(5367, '2024-11-01', 3)": 0, + "(5367, '2024-11-01', 4)": 0, + "(5367, '2024-11-01', 5)": 0, + "(5367, '2024-11-01', 6)": 0, + "(5367, '2024-11-01', 7)": 0, + "(5367, '2024-11-02', 0)": 0, + "(5367, '2024-11-02', 1)": 0, + "(5367, '2024-11-02', 2)": 0, + "(5367, '2024-11-02', 3)": 0, + "(5367, '2024-11-02', 4)": 0, + "(5367, '2024-11-02', 5)": 0, + "(5367, '2024-11-02', 6)": 0, + "(5367, '2024-11-02', 7)": 0, + "(5367, '2024-11-03', 0)": 1, + "(5367, '2024-11-03', 1)": 0, + "(5367, '2024-11-03', 2)": 0, + "(5367, '2024-11-03', 3)": 0, + "(5367, '2024-11-03', 4)": 0, + "(5367, '2024-11-03', 5)": 0, + "(5367, '2024-11-03', 6)": 0, + "(5367, '2024-11-03', 7)": 0, + "(5367, '2024-11-04', 0)": 0, + "(5367, '2024-11-04', 1)": 0, + "(5367, '2024-11-04', 2)": 0, + "(5367, '2024-11-04', 3)": 0, + "(5367, '2024-11-04', 4)": 0, + "(5367, '2024-11-04', 5)": 0, + "(5367, '2024-11-04', 6)": 0, + "(5367, '2024-11-04', 7)": 0, + "(5367, '2024-11-05', 0)": 1, + "(5367, '2024-11-05', 1)": 0, + "(5367, '2024-11-05', 2)": 0, + "(5367, '2024-11-05', 3)": 0, + "(5367, '2024-11-05', 4)": 0, + "(5367, '2024-11-05', 5)": 0, + "(5367, '2024-11-05', 6)": 0, + "(5367, '2024-11-05', 7)": 0, + "(5367, '2024-11-06', 0)": 0, + "(5367, '2024-11-06', 1)": 0, + "(5367, '2024-11-06', 2)": 1, + "(5367, '2024-11-06', 3)": 0, + "(5367, '2024-11-06', 4)": 0, + "(5367, '2024-11-06', 5)": 0, + "(5367, '2024-11-06', 6)": 0, + "(5367, '2024-11-06', 7)": 0, + "(5367, '2024-11-07', 0)": 0, + "(5367, '2024-11-07', 1)": 0, + "(5367, '2024-11-07', 2)": 0, + "(5367, '2024-11-07', 3)": 0, + "(5367, '2024-11-07', 4)": 0, + "(5367, '2024-11-07', 5)": 0, + "(5367, '2024-11-07', 6)": 0, + "(5367, '2024-11-07', 7)": 0, + "(5367, '2024-11-08', 0)": 0, + "(5367, '2024-11-08', 1)": 0, + "(5367, '2024-11-08', 2)": 0, + "(5367, '2024-11-08', 3)": 0, + "(5367, '2024-11-08', 4)": 0, + "(5367, '2024-11-08', 5)": 0, + "(5367, '2024-11-08', 6)": 0, + "(5367, '2024-11-08', 7)": 0, + "(5367, '2024-11-09', 0)": 0, + "(5367, '2024-11-09', 1)": 0, + "(5367, '2024-11-09', 2)": 0, + "(5367, '2024-11-09', 3)": 0, + "(5367, '2024-11-09', 4)": 0, + "(5367, '2024-11-09', 5)": 0, + "(5367, '2024-11-09', 6)": 0, + "(5367, '2024-11-09', 7)": 0, + "(5367, '2024-11-10', 0)": 1, + "(5367, '2024-11-10', 1)": 0, + "(5367, '2024-11-10', 2)": 0, + "(5367, '2024-11-10', 3)": 0, + "(5367, '2024-11-10', 4)": 0, + "(5367, '2024-11-10', 5)": 0, + "(5367, '2024-11-10', 6)": 0, + "(5367, '2024-11-10', 7)": 0, + "(5367, '2024-11-11', 0)": 1, + "(5367, '2024-11-11', 1)": 0, + "(5367, '2024-11-11', 2)": 0, + "(5367, '2024-11-11', 3)": 0, + "(5367, '2024-11-11', 4)": 0, + "(5367, '2024-11-11', 5)": 0, + "(5367, '2024-11-11', 6)": 0, + "(5367, '2024-11-11', 7)": 0, + "(5367, '2024-11-12', 0)": 1, + "(5367, '2024-11-12', 1)": 0, + "(5367, '2024-11-12', 2)": 0, + "(5367, '2024-11-12', 3)": 0, + "(5367, '2024-11-12', 4)": 0, + "(5367, '2024-11-12', 5)": 0, + "(5367, '2024-11-12', 6)": 0, + "(5367, '2024-11-12', 7)": 0, + "(5367, '2024-11-13', 0)": 0, + "(5367, '2024-11-13', 1)": 0, + "(5367, '2024-11-13', 2)": 0, + "(5367, '2024-11-13', 3)": 0, + "(5367, '2024-11-13', 4)": 0, + "(5367, '2024-11-13', 5)": 0, + "(5367, '2024-11-13', 6)": 0, + "(5367, '2024-11-13', 7)": 0, + "(5367, '2024-11-14', 0)": 0, + "(5367, '2024-11-14', 1)": 0, + "(5367, '2024-11-14', 2)": 0, + "(5367, '2024-11-14', 3)": 0, + "(5367, '2024-11-14', 4)": 0, + "(5367, '2024-11-14', 5)": 0, + "(5367, '2024-11-14', 6)": 0, + "(5367, '2024-11-14', 7)": 0, + "(5367, '2024-11-15', 0)": 1, + "(5367, '2024-11-15', 1)": 0, + "(5367, '2024-11-15', 2)": 0, + "(5367, '2024-11-15', 3)": 0, + "(5367, '2024-11-15', 4)": 0, + "(5367, '2024-11-15', 5)": 0, + "(5367, '2024-11-15', 6)": 0, + "(5367, '2024-11-15', 7)": 0, + "(5367, '2024-11-16', 0)": 0, + "(5367, '2024-11-16', 1)": 0, + "(5367, '2024-11-16', 2)": 0, + "(5367, '2024-11-16', 3)": 0, + "(5367, '2024-11-16', 4)": 0, + "(5367, '2024-11-16', 5)": 0, + "(5367, '2024-11-16', 6)": 0, + "(5367, '2024-11-16', 7)": 0, + "(5367, '2024-11-17', 0)": 1, + "(5367, '2024-11-17', 1)": 0, + "(5367, '2024-11-17', 2)": 0, + "(5367, '2024-11-17', 3)": 0, + "(5367, '2024-11-17', 4)": 0, + "(5367, '2024-11-17', 5)": 0, + "(5367, '2024-11-17', 6)": 0, + "(5367, '2024-11-17', 7)": 0, + "(5367, '2024-11-18', 0)": 1, + "(5367, '2024-11-18', 1)": 0, + "(5367, '2024-11-18', 2)": 0, + "(5367, '2024-11-18', 3)": 0, + "(5367, '2024-11-18', 4)": 0, + "(5367, '2024-11-18', 5)": 0, + "(5367, '2024-11-18', 6)": 0, + "(5367, '2024-11-18', 7)": 0, + "(5367, '2024-11-19', 0)": 1, + "(5367, '2024-11-19', 1)": 0, + "(5367, '2024-11-19', 2)": 0, + "(5367, '2024-11-19', 3)": 0, + "(5367, '2024-11-19', 4)": 0, + "(5367, '2024-11-19', 5)": 0, + "(5367, '2024-11-19', 6)": 0, + "(5367, '2024-11-19', 7)": 0, + "(5367, '2024-11-20', 0)": 0, + "(5367, '2024-11-20', 1)": 0, + "(5367, '2024-11-20', 2)": 0, + "(5367, '2024-11-20', 3)": 0, + "(5367, '2024-11-20', 4)": 0, + "(5367, '2024-11-20', 5)": 0, + "(5367, '2024-11-20', 6)": 0, + "(5367, '2024-11-20', 7)": 0, + "(5367, '2024-11-21', 0)": 0, + "(5367, '2024-11-21', 1)": 0, + "(5367, '2024-11-21', 2)": 0, + "(5367, '2024-11-21', 3)": 0, + "(5367, '2024-11-21', 4)": 0, + "(5367, '2024-11-21', 5)": 0, + "(5367, '2024-11-21', 6)": 0, + "(5367, '2024-11-21', 7)": 0, + "(5367, '2024-11-22', 0)": 1, + "(5367, '2024-11-22', 1)": 0, + "(5367, '2024-11-22', 2)": 0, + "(5367, '2024-11-22', 3)": 0, + "(5367, '2024-11-22', 4)": 0, + "(5367, '2024-11-22', 5)": 0, + "(5367, '2024-11-22', 6)": 0, + "(5367, '2024-11-22', 7)": 0, + "(5367, '2024-11-23', 0)": 1, + "(5367, '2024-11-23', 1)": 0, + "(5367, '2024-11-23', 2)": 0, + "(5367, '2024-11-23', 3)": 0, + "(5367, '2024-11-23', 4)": 0, + "(5367, '2024-11-23', 5)": 0, + "(5367, '2024-11-23', 6)": 0, + "(5367, '2024-11-23', 7)": 0, + "(5367, '2024-11-24', 0)": 1, + "(5367, '2024-11-24', 1)": 0, + "(5367, '2024-11-24', 2)": 0, + "(5367, '2024-11-24', 3)": 0, + "(5367, '2024-11-24', 4)": 0, + "(5367, '2024-11-24', 5)": 0, + "(5367, '2024-11-24', 6)": 0, + "(5367, '2024-11-24', 7)": 0, + "(5367, '2024-11-25', 0)": 1, + "(5367, '2024-11-25', 1)": 0, + "(5367, '2024-11-25', 2)": 0, + "(5367, '2024-11-25', 3)": 0, + "(5367, '2024-11-25', 4)": 0, + "(5367, '2024-11-25', 5)": 0, + "(5367, '2024-11-25', 6)": 0, + "(5367, '2024-11-25', 7)": 0, + "(5367, '2024-11-26', 0)": 1, + "(5367, '2024-11-26', 1)": 0, + "(5367, '2024-11-26', 2)": 0, + "(5367, '2024-11-26', 3)": 0, + "(5367, '2024-11-26', 4)": 0, + "(5367, '2024-11-26', 5)": 0, + "(5367, '2024-11-26', 6)": 0, + "(5367, '2024-11-26', 7)": 0, + "(5367, '2024-11-27', 0)": 1, + "(5367, '2024-11-27', 1)": 0, + "(5367, '2024-11-27', 2)": 0, + "(5367, '2024-11-27', 3)": 0, + "(5367, '2024-11-27', 4)": 0, + "(5367, '2024-11-27', 5)": 0, + "(5367, '2024-11-27', 6)": 0, + "(5367, '2024-11-27', 7)": 0, + "(5367, '2024-11-28', 0)": 1, + "(5367, '2024-11-28', 1)": 0, + "(5367, '2024-11-28', 2)": 0, + "(5367, '2024-11-28', 3)": 0, + "(5367, '2024-11-28', 4)": 0, + "(5367, '2024-11-28', 5)": 0, + "(5367, '2024-11-28', 6)": 0, + "(5367, '2024-11-28', 7)": 0, + "(5367, '2024-11-29', 0)": 0, + "(5367, '2024-11-29', 1)": 0, + "(5367, '2024-11-29', 2)": 0, + "(5367, '2024-11-29', 3)": 0, + "(5367, '2024-11-29', 4)": 0, + "(5367, '2024-11-29', 5)": 0, + "(5367, '2024-11-29', 6)": 0, + "(5367, '2024-11-29', 7)": 0, + "(5367, '2024-11-30', 0)": 0, + "(5367, '2024-11-30', 1)": 0, + "(5367, '2024-11-30', 2)": 1, + "(5367, '2024-11-30', 3)": 0, + "(5367, '2024-11-30', 4)": 0, + "(5367, '2024-11-30', 5)": 0, + "(5367, '2024-11-30', 6)": 0, + "(5367, '2024-11-30', 7)": 0, + "(5920, '2024-11-01', 0)": 0, + "(5920, '2024-11-01', 1)": 0, + "(5920, '2024-11-01', 2)": 0, + "(5920, '2024-11-01', 3)": 1, + "(5920, '2024-11-01', 4)": 0, + "(5920, '2024-11-01', 5)": 0, + "(5920, '2024-11-01', 6)": 0, + "(5920, '2024-11-01', 7)": 0, + "(5920, '2024-11-02', 0)": 0, + "(5920, '2024-11-02', 1)": 0, + "(5920, '2024-11-02', 2)": 0, + "(5920, '2024-11-02', 3)": 0, + "(5920, '2024-11-02', 4)": 0, + "(5920, '2024-11-02', 5)": 0, + "(5920, '2024-11-02', 6)": 0, + "(5920, '2024-11-02', 7)": 0, + "(5920, '2024-11-03', 0)": 0, + "(5920, '2024-11-03', 1)": 0, + "(5920, '2024-11-03', 2)": 1, + "(5920, '2024-11-03', 3)": 0, + "(5920, '2024-11-03', 4)": 0, + "(5920, '2024-11-03', 5)": 0, + "(5920, '2024-11-03', 6)": 0, + "(5920, '2024-11-03', 7)": 0, + "(5920, '2024-11-04', 0)": 0, + "(5920, '2024-11-04', 1)": 1, + "(5920, '2024-11-04', 2)": 0, + "(5920, '2024-11-04', 3)": 0, + "(5920, '2024-11-04', 4)": 0, + "(5920, '2024-11-04', 5)": 0, + "(5920, '2024-11-04', 6)": 0, + "(5920, '2024-11-04', 7)": 0, + "(5920, '2024-11-05', 0)": 0, + "(5920, '2024-11-05', 1)": 0, + "(5920, '2024-11-05', 2)": 1, + "(5920, '2024-11-05', 3)": 0, + "(5920, '2024-11-05', 4)": 0, + "(5920, '2024-11-05', 5)": 0, + "(5920, '2024-11-05', 6)": 0, + "(5920, '2024-11-05', 7)": 0, + "(5920, '2024-11-06', 0)": 0, + "(5920, '2024-11-06', 1)": 0, + "(5920, '2024-11-06', 2)": 0, + "(5920, '2024-11-06', 3)": 0, + "(5920, '2024-11-06', 4)": 0, + "(5920, '2024-11-06', 5)": 0, + "(5920, '2024-11-06', 6)": 0, + "(5920, '2024-11-06', 7)": 0, + "(5920, '2024-11-07', 0)": 0, + "(5920, '2024-11-07', 1)": 0, + "(5920, '2024-11-07', 2)": 0, + "(5920, '2024-11-07', 3)": 0, + "(5920, '2024-11-07', 4)": 0, + "(5920, '2024-11-07', 5)": 0, + "(5920, '2024-11-07', 6)": 0, + "(5920, '2024-11-07', 7)": 0, + "(5920, '2024-11-08', 0)": 1, + "(5920, '2024-11-08', 1)": 0, + "(5920, '2024-11-08', 2)": 0, + "(5920, '2024-11-08', 3)": 0, + "(5920, '2024-11-08', 4)": 0, + "(5920, '2024-11-08', 5)": 0, + "(5920, '2024-11-08', 6)": 0, + "(5920, '2024-11-08', 7)": 0, + "(5920, '2024-11-09', 0)": 0, + "(5920, '2024-11-09', 1)": 0, + "(5920, '2024-11-09', 2)": 0, + "(5920, '2024-11-09', 3)": 0, + "(5920, '2024-11-09', 4)": 0, + "(5920, '2024-11-09', 5)": 0, + "(5920, '2024-11-09', 6)": 0, + "(5920, '2024-11-09', 7)": 0, + "(5920, '2024-11-10', 0)": 0, + "(5920, '2024-11-10', 1)": 0, + "(5920, '2024-11-10', 2)": 0, + "(5920, '2024-11-10', 3)": 0, + "(5920, '2024-11-10', 4)": 0, + "(5920, '2024-11-10', 5)": 0, + "(5920, '2024-11-10', 6)": 0, + "(5920, '2024-11-10', 7)": 0, + "(5920, '2024-11-11', 0)": 0, + "(5920, '2024-11-11', 1)": 1, + "(5920, '2024-11-11', 2)": 0, + "(5920, '2024-11-11', 3)": 0, + "(5920, '2024-11-11', 4)": 0, + "(5920, '2024-11-11', 5)": 0, + "(5920, '2024-11-11', 6)": 0, + "(5920, '2024-11-11', 7)": 0, + "(5920, '2024-11-12', 0)": 1, + "(5920, '2024-11-12', 1)": 0, + "(5920, '2024-11-12', 2)": 0, + "(5920, '2024-11-12', 3)": 0, + "(5920, '2024-11-12', 4)": 0, + "(5920, '2024-11-12', 5)": 0, + "(5920, '2024-11-12', 6)": 0, + "(5920, '2024-11-12', 7)": 0, + "(5920, '2024-11-13', 0)": 0, + "(5920, '2024-11-13', 1)": 0, + "(5920, '2024-11-13', 2)": 0, + "(5920, '2024-11-13', 3)": 0, + "(5920, '2024-11-13', 4)": 0, + "(5920, '2024-11-13', 5)": 0, + "(5920, '2024-11-13', 6)": 0, + "(5920, '2024-11-13', 7)": 0, + "(5920, '2024-11-14', 0)": 0, + "(5920, '2024-11-14', 1)": 0, + "(5920, '2024-11-14', 2)": 0, + "(5920, '2024-11-14', 3)": 0, + "(5920, '2024-11-14', 4)": 0, + "(5920, '2024-11-14', 5)": 0, + "(5920, '2024-11-14', 6)": 0, + "(5920, '2024-11-14', 7)": 0, + "(5920, '2024-11-15', 0)": 0, + "(5920, '2024-11-15', 1)": 0, + "(5920, '2024-11-15', 2)": 1, + "(5920, '2024-11-15', 3)": 0, + "(5920, '2024-11-15', 4)": 0, + "(5920, '2024-11-15', 5)": 0, + "(5920, '2024-11-15', 6)": 0, + "(5920, '2024-11-15', 7)": 0, + "(5920, '2024-11-16', 0)": 0, + "(5920, '2024-11-16', 1)": 0, + "(5920, '2024-11-16', 2)": 0, + "(5920, '2024-11-16', 3)": 1, + "(5920, '2024-11-16', 4)": 0, + "(5920, '2024-11-16', 5)": 0, + "(5920, '2024-11-16', 6)": 0, + "(5920, '2024-11-16', 7)": 0, + "(5920, '2024-11-17', 0)": 0, + "(5920, '2024-11-17', 1)": 0, + "(5920, '2024-11-17', 2)": 0, + "(5920, '2024-11-17', 3)": 1, + "(5920, '2024-11-17', 4)": 0, + "(5920, '2024-11-17', 5)": 0, + "(5920, '2024-11-17', 6)": 0, + "(5920, '2024-11-17', 7)": 0, + "(5920, '2024-11-18', 0)": 0, + "(5920, '2024-11-18', 1)": 0, + "(5920, '2024-11-18', 2)": 0, + "(5920, '2024-11-18', 3)": 1, + "(5920, '2024-11-18', 4)": 0, + "(5920, '2024-11-18', 5)": 0, + "(5920, '2024-11-18', 6)": 0, + "(5920, '2024-11-18', 7)": 0, + "(5920, '2024-11-19', 0)": 0, + "(5920, '2024-11-19', 1)": 0, + "(5920, '2024-11-19', 2)": 0, + "(5920, '2024-11-19', 3)": 0, + "(5920, '2024-11-19', 4)": 0, + "(5920, '2024-11-19', 5)": 0, + "(5920, '2024-11-19', 6)": 0, + "(5920, '2024-11-19', 7)": 0, + "(5920, '2024-11-20', 0)": 0, + "(5920, '2024-11-20', 1)": 0, + "(5920, '2024-11-20', 2)": 0, + "(5920, '2024-11-20', 3)": 0, + "(5920, '2024-11-20', 4)": 0, + "(5920, '2024-11-20', 5)": 0, + "(5920, '2024-11-20', 6)": 0, + "(5920, '2024-11-20', 7)": 0, + "(5920, '2024-11-21', 0)": 0, + "(5920, '2024-11-21', 1)": 0, + "(5920, '2024-11-21', 2)": 1, + "(5920, '2024-11-21', 3)": 0, + "(5920, '2024-11-21', 4)": 0, + "(5920, '2024-11-21', 5)": 0, + "(5920, '2024-11-21', 6)": 0, + "(5920, '2024-11-21', 7)": 0, + "(5920, '2024-11-22', 0)": 0, + "(5920, '2024-11-22', 1)": 0, + "(5920, '2024-11-22', 2)": 1, + "(5920, '2024-11-22', 3)": 0, + "(5920, '2024-11-22', 4)": 0, + "(5920, '2024-11-22', 5)": 0, + "(5920, '2024-11-22', 6)": 0, + "(5920, '2024-11-22', 7)": 0, + "(5920, '2024-11-23', 0)": 0, + "(5920, '2024-11-23', 1)": 1, + "(5920, '2024-11-23', 2)": 0, + "(5920, '2024-11-23', 3)": 0, + "(5920, '2024-11-23', 4)": 0, + "(5920, '2024-11-23', 5)": 0, + "(5920, '2024-11-23', 6)": 0, + "(5920, '2024-11-23', 7)": 0, + "(5920, '2024-11-24', 0)": 0, + "(5920, '2024-11-24', 1)": 0, + "(5920, '2024-11-24', 2)": 0, + "(5920, '2024-11-24', 3)": 0, + "(5920, '2024-11-24', 4)": 0, + "(5920, '2024-11-24', 5)": 0, + "(5920, '2024-11-24', 6)": 0, + "(5920, '2024-11-24', 7)": 0, + "(5920, '2024-11-25', 0)": 0, + "(5920, '2024-11-25', 1)": 0, + "(5920, '2024-11-25', 2)": 1, + "(5920, '2024-11-25', 3)": 0, + "(5920, '2024-11-25', 4)": 0, + "(5920, '2024-11-25', 5)": 0, + "(5920, '2024-11-25', 6)": 0, + "(5920, '2024-11-25', 7)": 0, + "(5920, '2024-11-26', 0)": 0, + "(5920, '2024-11-26', 1)": 0, + "(5920, '2024-11-26', 2)": 1, + "(5920, '2024-11-26', 3)": 0, + "(5920, '2024-11-26', 4)": 0, + "(5920, '2024-11-26', 5)": 0, + "(5920, '2024-11-26', 6)": 0, + "(5920, '2024-11-26', 7)": 0, + "(5920, '2024-11-27', 0)": 0, + "(5920, '2024-11-27', 1)": 0, + "(5920, '2024-11-27', 2)": 0, + "(5920, '2024-11-27', 3)": 0, + "(5920, '2024-11-27', 4)": 0, + "(5920, '2024-11-27', 5)": 0, + "(5920, '2024-11-27', 6)": 0, + "(5920, '2024-11-27', 7)": 0, + "(5920, '2024-11-28', 0)": 0, + "(5920, '2024-11-28', 1)": 1, + "(5920, '2024-11-28', 2)": 0, + "(5920, '2024-11-28', 3)": 0, + "(5920, '2024-11-28', 4)": 0, + "(5920, '2024-11-28', 5)": 0, + "(5920, '2024-11-28', 6)": 0, + "(5920, '2024-11-28', 7)": 0, + "(5920, '2024-11-29', 0)": 0, + "(5920, '2024-11-29', 1)": 1, + "(5920, '2024-11-29', 2)": 0, + "(5920, '2024-11-29', 3)": 0, + "(5920, '2024-11-29', 4)": 0, + "(5920, '2024-11-29', 5)": 0, + "(5920, '2024-11-29', 6)": 0, + "(5920, '2024-11-29', 7)": 0, + "(5920, '2024-11-30', 0)": 1, + "(5920, '2024-11-30', 1)": 0, + "(5920, '2024-11-30', 2)": 0, + "(5920, '2024-11-30', 3)": 0, + "(5920, '2024-11-30', 4)": 0, + "(5920, '2024-11-30', 5)": 0, + "(5920, '2024-11-30', 6)": 0, + "(5920, '2024-11-30', 7)": 0, + "(6475, '2024-11-01', 0)": 0, + "(6475, '2024-11-01', 1)": 1, + "(6475, '2024-11-01', 2)": 0, + "(6475, '2024-11-01', 3)": 0, + "(6475, '2024-11-01', 4)": 0, + "(6475, '2024-11-01', 5)": 0, + "(6475, '2024-11-01', 6)": 0, + "(6475, '2024-11-01', 7)": 0, + "(6475, '2024-11-02', 0)": 0, + "(6475, '2024-11-02', 1)": 0, + "(6475, '2024-11-02', 2)": 0, + "(6475, '2024-11-02', 3)": 0, + "(6475, '2024-11-02', 4)": 0, + "(6475, '2024-11-02', 5)": 0, + "(6475, '2024-11-02', 6)": 0, + "(6475, '2024-11-02', 7)": 0, + "(6475, '2024-11-03', 0)": 0, + "(6475, '2024-11-03', 1)": 0, + "(6475, '2024-11-03', 2)": 0, + "(6475, '2024-11-03', 3)": 0, + "(6475, '2024-11-03', 4)": 0, + "(6475, '2024-11-03', 5)": 0, + "(6475, '2024-11-03', 6)": 0, + "(6475, '2024-11-03', 7)": 0, + "(6475, '2024-11-04', 0)": 0, + "(6475, '2024-11-04', 1)": 0, + "(6475, '2024-11-04', 2)": 0, + "(6475, '2024-11-04', 3)": 0, + "(6475, '2024-11-04', 4)": 0, + "(6475, '2024-11-04', 5)": 0, + "(6475, '2024-11-04', 6)": 0, + "(6475, '2024-11-04', 7)": 0, + "(6475, '2024-11-05', 0)": 0, + "(6475, '2024-11-05', 1)": 0, + "(6475, '2024-11-05', 2)": 0, + "(6475, '2024-11-05', 3)": 0, + "(6475, '2024-11-05', 4)": 0, + "(6475, '2024-11-05', 5)": 0, + "(6475, '2024-11-05', 6)": 0, + "(6475, '2024-11-05', 7)": 0, + "(6475, '2024-11-06', 0)": 0, + "(6475, '2024-11-06', 1)": 0, + "(6475, '2024-11-06', 2)": 0, + "(6475, '2024-11-06', 3)": 0, + "(6475, '2024-11-06', 4)": 0, + "(6475, '2024-11-06', 5)": 0, + "(6475, '2024-11-06', 6)": 0, + "(6475, '2024-11-06', 7)": 0, + "(6475, '2024-11-07', 0)": 0, + "(6475, '2024-11-07', 1)": 0, + "(6475, '2024-11-07', 2)": 0, + "(6475, '2024-11-07', 3)": 0, + "(6475, '2024-11-07', 4)": 0, + "(6475, '2024-11-07', 5)": 0, + "(6475, '2024-11-07', 6)": 0, + "(6475, '2024-11-07', 7)": 0, + "(6475, '2024-11-08', 0)": 0, + "(6475, '2024-11-08', 1)": 0, + "(6475, '2024-11-08', 2)": 0, + "(6475, '2024-11-08', 3)": 0, + "(6475, '2024-11-08', 4)": 0, + "(6475, '2024-11-08', 5)": 0, + "(6475, '2024-11-08', 6)": 0, + "(6475, '2024-11-08', 7)": 0, + "(6475, '2024-11-09', 0)": 0, + "(6475, '2024-11-09', 1)": 0, + "(6475, '2024-11-09', 2)": 0, + "(6475, '2024-11-09', 3)": 0, + "(6475, '2024-11-09', 4)": 0, + "(6475, '2024-11-09', 5)": 0, + "(6475, '2024-11-09', 6)": 0, + "(6475, '2024-11-09', 7)": 0, + "(6475, '2024-11-10', 0)": 0, + "(6475, '2024-11-10', 1)": 0, + "(6475, '2024-11-10', 2)": 0, + "(6475, '2024-11-10', 3)": 0, + "(6475, '2024-11-10', 4)": 0, + "(6475, '2024-11-10', 5)": 0, + "(6475, '2024-11-10', 6)": 0, + "(6475, '2024-11-10', 7)": 0, + "(6475, '2024-11-11', 0)": 0, + "(6475, '2024-11-11', 1)": 0, + "(6475, '2024-11-11', 2)": 0, + "(6475, '2024-11-11', 3)": 0, + "(6475, '2024-11-11', 4)": 0, + "(6475, '2024-11-11', 5)": 0, + "(6475, '2024-11-11', 6)": 0, + "(6475, '2024-11-11', 7)": 0, + "(6475, '2024-11-12', 0)": 0, + "(6475, '2024-11-12', 1)": 0, + "(6475, '2024-11-12', 2)": 0, + "(6475, '2024-11-12', 3)": 0, + "(6475, '2024-11-12', 4)": 0, + "(6475, '2024-11-12', 5)": 0, + "(6475, '2024-11-12', 6)": 0, + "(6475, '2024-11-12', 7)": 0, + "(6475, '2024-11-13', 0)": 0, + "(6475, '2024-11-13', 1)": 0, + "(6475, '2024-11-13', 2)": 0, + "(6475, '2024-11-13', 3)": 0, + "(6475, '2024-11-13', 4)": 0, + "(6475, '2024-11-13', 5)": 0, + "(6475, '2024-11-13', 6)": 0, + "(6475, '2024-11-13', 7)": 0, + "(6475, '2024-11-14', 0)": 0, + "(6475, '2024-11-14', 1)": 0, + "(6475, '2024-11-14', 2)": 0, + "(6475, '2024-11-14', 3)": 0, + "(6475, '2024-11-14', 4)": 0, + "(6475, '2024-11-14', 5)": 0, + "(6475, '2024-11-14', 6)": 0, + "(6475, '2024-11-14', 7)": 0, + "(6475, '2024-11-15', 0)": 0, + "(6475, '2024-11-15', 1)": 0, + "(6475, '2024-11-15', 2)": 0, + "(6475, '2024-11-15', 3)": 0, + "(6475, '2024-11-15', 4)": 0, + "(6475, '2024-11-15', 5)": 0, + "(6475, '2024-11-15', 6)": 0, + "(6475, '2024-11-15', 7)": 0, + "(6475, '2024-11-16', 0)": 0, + "(6475, '2024-11-16', 1)": 0, + "(6475, '2024-11-16', 2)": 0, + "(6475, '2024-11-16', 3)": 0, + "(6475, '2024-11-16', 4)": 0, + "(6475, '2024-11-16', 5)": 0, + "(6475, '2024-11-16', 6)": 0, + "(6475, '2024-11-16', 7)": 0, + "(6475, '2024-11-17', 0)": 0, + "(6475, '2024-11-17', 1)": 0, + "(6475, '2024-11-17', 2)": 0, + "(6475, '2024-11-17', 3)": 0, + "(6475, '2024-11-17', 4)": 0, + "(6475, '2024-11-17', 5)": 0, + "(6475, '2024-11-17', 6)": 0, + "(6475, '2024-11-17', 7)": 0, + "(6475, '2024-11-18', 0)": 0, + "(6475, '2024-11-18', 1)": 0, + "(6475, '2024-11-18', 2)": 0, + "(6475, '2024-11-18', 3)": 0, + "(6475, '2024-11-18', 4)": 0, + "(6475, '2024-11-18', 5)": 0, + "(6475, '2024-11-18', 6)": 0, + "(6475, '2024-11-18', 7)": 0, + "(6475, '2024-11-19', 0)": 0, + "(6475, '2024-11-19', 1)": 0, + "(6475, '2024-11-19', 2)": 0, + "(6475, '2024-11-19', 3)": 0, + "(6475, '2024-11-19', 4)": 0, + "(6475, '2024-11-19', 5)": 0, + "(6475, '2024-11-19', 6)": 0, + "(6475, '2024-11-19', 7)": 0, + "(6475, '2024-11-20', 0)": 0, + "(6475, '2024-11-20', 1)": 0, + "(6475, '2024-11-20', 2)": 0, + "(6475, '2024-11-20', 3)": 0, + "(6475, '2024-11-20', 4)": 0, + "(6475, '2024-11-20', 5)": 0, + "(6475, '2024-11-20', 6)": 0, + "(6475, '2024-11-20', 7)": 0, + "(6475, '2024-11-21', 0)": 0, + "(6475, '2024-11-21', 1)": 0, + "(6475, '2024-11-21', 2)": 0, + "(6475, '2024-11-21', 3)": 0, + "(6475, '2024-11-21', 4)": 0, + "(6475, '2024-11-21', 5)": 0, + "(6475, '2024-11-21', 6)": 0, + "(6475, '2024-11-21', 7)": 0, + "(6475, '2024-11-22', 0)": 0, + "(6475, '2024-11-22', 1)": 0, + "(6475, '2024-11-22', 2)": 0, + "(6475, '2024-11-22', 3)": 0, + "(6475, '2024-11-22', 4)": 0, + "(6475, '2024-11-22', 5)": 0, + "(6475, '2024-11-22', 6)": 0, + "(6475, '2024-11-22', 7)": 0, + "(6475, '2024-11-23', 0)": 0, + "(6475, '2024-11-23', 1)": 0, + "(6475, '2024-11-23', 2)": 0, + "(6475, '2024-11-23', 3)": 0, + "(6475, '2024-11-23', 4)": 0, + "(6475, '2024-11-23', 5)": 0, + "(6475, '2024-11-23', 6)": 0, + "(6475, '2024-11-23', 7)": 0, + "(6475, '2024-11-24', 0)": 0, + "(6475, '2024-11-24', 1)": 0, + "(6475, '2024-11-24', 2)": 0, + "(6475, '2024-11-24', 3)": 0, + "(6475, '2024-11-24', 4)": 0, + "(6475, '2024-11-24', 5)": 0, + "(6475, '2024-11-24', 6)": 0, + "(6475, '2024-11-24', 7)": 0, + "(6475, '2024-11-25', 0)": 0, + "(6475, '2024-11-25', 1)": 0, + "(6475, '2024-11-25', 2)": 0, + "(6475, '2024-11-25', 3)": 0, + "(6475, '2024-11-25', 4)": 0, + "(6475, '2024-11-25', 5)": 0, + "(6475, '2024-11-25', 6)": 0, + "(6475, '2024-11-25', 7)": 0, + "(6475, '2024-11-26', 0)": 0, + "(6475, '2024-11-26', 1)": 0, + "(6475, '2024-11-26', 2)": 0, + "(6475, '2024-11-26', 3)": 0, + "(6475, '2024-11-26', 4)": 0, + "(6475, '2024-11-26', 5)": 0, + "(6475, '2024-11-26', 6)": 0, + "(6475, '2024-11-26', 7)": 0, + "(6475, '2024-11-27', 0)": 0, + "(6475, '2024-11-27', 1)": 0, + "(6475, '2024-11-27', 2)": 0, + "(6475, '2024-11-27', 3)": 0, + "(6475, '2024-11-27', 4)": 0, + "(6475, '2024-11-27', 5)": 0, + "(6475, '2024-11-27', 6)": 0, + "(6475, '2024-11-27', 7)": 0, + "(6475, '2024-11-28', 0)": 0, + "(6475, '2024-11-28', 1)": 0, + "(6475, '2024-11-28', 2)": 0, + "(6475, '2024-11-28', 3)": 0, + "(6475, '2024-11-28', 4)": 0, + "(6475, '2024-11-28', 5)": 0, + "(6475, '2024-11-28', 6)": 0, + "(6475, '2024-11-28', 7)": 0, + "(6475, '2024-11-29', 0)": 0, + "(6475, '2024-11-29', 1)": 0, + "(6475, '2024-11-29', 2)": 0, + "(6475, '2024-11-29', 3)": 0, + "(6475, '2024-11-29', 4)": 0, + "(6475, '2024-11-29', 5)": 0, + "(6475, '2024-11-29', 6)": 0, + "(6475, '2024-11-29', 7)": 0, + "(6475, '2024-11-30', 0)": 0, + "(6475, '2024-11-30', 1)": 0, + "(6475, '2024-11-30', 2)": 0, + "(6475, '2024-11-30', 3)": 0, + "(6475, '2024-11-30', 4)": 0, + "(6475, '2024-11-30', 5)": 0, + "(6475, '2024-11-30', 6)": 0, + "(6475, '2024-11-30', 7)": 0, + "(6507, '2024-11-01', 0)": 1, + "(6507, '2024-11-01', 1)": 0, + "(6507, '2024-11-01', 2)": 0, + "(6507, '2024-11-01', 3)": 0, + "(6507, '2024-11-01', 4)": 0, + "(6507, '2024-11-01', 5)": 0, + "(6507, '2024-11-01', 6)": 0, + "(6507, '2024-11-01', 7)": 0, + "(6507, '2024-11-02', 0)": 0, + "(6507, '2024-11-02', 1)": 0, + "(6507, '2024-11-02', 2)": 0, + "(6507, '2024-11-02', 3)": 1, + "(6507, '2024-11-02', 4)": 0, + "(6507, '2024-11-02', 5)": 0, + "(6507, '2024-11-02', 6)": 0, + "(6507, '2024-11-02', 7)": 0, + "(6507, '2024-11-03', 0)": 0, + "(6507, '2024-11-03', 1)": 0, + "(6507, '2024-11-03', 2)": 0, + "(6507, '2024-11-03', 3)": 0, + "(6507, '2024-11-03', 4)": 0, + "(6507, '2024-11-03', 5)": 0, + "(6507, '2024-11-03', 6)": 0, + "(6507, '2024-11-03', 7)": 0, + "(6507, '2024-11-04', 0)": 0, + "(6507, '2024-11-04', 1)": 0, + "(6507, '2024-11-04', 2)": 0, + "(6507, '2024-11-04', 3)": 0, + "(6507, '2024-11-04', 4)": 0, + "(6507, '2024-11-04', 5)": 0, + "(6507, '2024-11-04', 6)": 0, + "(6507, '2024-11-04', 7)": 0, + "(6507, '2024-11-05', 0)": 1, + "(6507, '2024-11-05', 1)": 0, + "(6507, '2024-11-05', 2)": 0, + "(6507, '2024-11-05', 3)": 0, + "(6507, '2024-11-05', 4)": 0, + "(6507, '2024-11-05', 5)": 0, + "(6507, '2024-11-05', 6)": 0, + "(6507, '2024-11-05', 7)": 0, + "(6507, '2024-11-06', 0)": 1, + "(6507, '2024-11-06', 1)": 0, + "(6507, '2024-11-06', 2)": 0, + "(6507, '2024-11-06', 3)": 0, + "(6507, '2024-11-06', 4)": 0, + "(6507, '2024-11-06', 5)": 0, + "(6507, '2024-11-06', 6)": 0, + "(6507, '2024-11-06', 7)": 0, + "(6507, '2024-11-07', 0)": 1, + "(6507, '2024-11-07', 1)": 0, + "(6507, '2024-11-07', 2)": 0, + "(6507, '2024-11-07', 3)": 0, + "(6507, '2024-11-07', 4)": 0, + "(6507, '2024-11-07', 5)": 0, + "(6507, '2024-11-07', 6)": 0, + "(6507, '2024-11-07', 7)": 0, + "(6507, '2024-11-08', 0)": 0, + "(6507, '2024-11-08', 1)": 0, + "(6507, '2024-11-08', 2)": 0, + "(6507, '2024-11-08', 3)": 0, + "(6507, '2024-11-08', 4)": 0, + "(6507, '2024-11-08', 5)": 0, + "(6507, '2024-11-08', 6)": 0, + "(6507, '2024-11-08', 7)": 0, + "(6507, '2024-11-09', 0)": 1, + "(6507, '2024-11-09', 1)": 0, + "(6507, '2024-11-09', 2)": 0, + "(6507, '2024-11-09', 3)": 0, + "(6507, '2024-11-09', 4)": 0, + "(6507, '2024-11-09', 5)": 0, + "(6507, '2024-11-09', 6)": 0, + "(6507, '2024-11-09', 7)": 0, + "(6507, '2024-11-10', 0)": 0, + "(6507, '2024-11-10', 1)": 0, + "(6507, '2024-11-10', 2)": 0, + "(6507, '2024-11-10', 3)": 0, + "(6507, '2024-11-10', 4)": 0, + "(6507, '2024-11-10', 5)": 0, + "(6507, '2024-11-10', 6)": 0, + "(6507, '2024-11-10', 7)": 0, + "(6507, '2024-11-11', 0)": 1, + "(6507, '2024-11-11', 1)": 0, + "(6507, '2024-11-11', 2)": 0, + "(6507, '2024-11-11', 3)": 0, + "(6507, '2024-11-11', 4)": 0, + "(6507, '2024-11-11', 5)": 0, + "(6507, '2024-11-11', 6)": 0, + "(6507, '2024-11-11', 7)": 0, + "(6507, '2024-11-12', 0)": 1, + "(6507, '2024-11-12', 1)": 0, + "(6507, '2024-11-12', 2)": 0, + "(6507, '2024-11-12', 3)": 0, + "(6507, '2024-11-12', 4)": 0, + "(6507, '2024-11-12', 5)": 0, + "(6507, '2024-11-12', 6)": 0, + "(6507, '2024-11-12', 7)": 0, + "(6507, '2024-11-13', 0)": 1, + "(6507, '2024-11-13', 1)": 0, + "(6507, '2024-11-13', 2)": 0, + "(6507, '2024-11-13', 3)": 0, + "(6507, '2024-11-13', 4)": 0, + "(6507, '2024-11-13', 5)": 0, + "(6507, '2024-11-13', 6)": 0, + "(6507, '2024-11-13', 7)": 0, + "(6507, '2024-11-14', 0)": 1, + "(6507, '2024-11-14', 1)": 0, + "(6507, '2024-11-14', 2)": 0, + "(6507, '2024-11-14', 3)": 0, + "(6507, '2024-11-14', 4)": 0, + "(6507, '2024-11-14', 5)": 0, + "(6507, '2024-11-14', 6)": 0, + "(6507, '2024-11-14', 7)": 0, + "(6507, '2024-11-15', 0)": 1, + "(6507, '2024-11-15', 1)": 0, + "(6507, '2024-11-15', 2)": 0, + "(6507, '2024-11-15', 3)": 0, + "(6507, '2024-11-15', 4)": 0, + "(6507, '2024-11-15', 5)": 0, + "(6507, '2024-11-15', 6)": 0, + "(6507, '2024-11-15', 7)": 0, + "(6507, '2024-11-16', 0)": 0, + "(6507, '2024-11-16', 1)": 0, + "(6507, '2024-11-16', 2)": 0, + "(6507, '2024-11-16', 3)": 0, + "(6507, '2024-11-16', 4)": 0, + "(6507, '2024-11-16', 5)": 0, + "(6507, '2024-11-16', 6)": 0, + "(6507, '2024-11-16', 7)": 0, + "(6507, '2024-11-17', 0)": 0, + "(6507, '2024-11-17', 1)": 0, + "(6507, '2024-11-17', 2)": 0, + "(6507, '2024-11-17', 3)": 0, + "(6507, '2024-11-17', 4)": 0, + "(6507, '2024-11-17', 5)": 0, + "(6507, '2024-11-17', 6)": 0, + "(6507, '2024-11-17', 7)": 0, + "(6507, '2024-11-18', 0)": 1, + "(6507, '2024-11-18', 1)": 0, + "(6507, '2024-11-18', 2)": 0, + "(6507, '2024-11-18', 3)": 0, + "(6507, '2024-11-18', 4)": 0, + "(6507, '2024-11-18', 5)": 0, + "(6507, '2024-11-18', 6)": 0, + "(6507, '2024-11-18', 7)": 0, + "(6507, '2024-11-19', 0)": 1, + "(6507, '2024-11-19', 1)": 0, + "(6507, '2024-11-19', 2)": 0, + "(6507, '2024-11-19', 3)": 0, + "(6507, '2024-11-19', 4)": 0, + "(6507, '2024-11-19', 5)": 0, + "(6507, '2024-11-19', 6)": 0, + "(6507, '2024-11-19', 7)": 0, + "(6507, '2024-11-20', 0)": 1, + "(6507, '2024-11-20', 1)": 0, + "(6507, '2024-11-20', 2)": 0, + "(6507, '2024-11-20', 3)": 0, + "(6507, '2024-11-20', 4)": 0, + "(6507, '2024-11-20', 5)": 0, + "(6507, '2024-11-20', 6)": 0, + "(6507, '2024-11-20', 7)": 0, + "(6507, '2024-11-21', 0)": 1, + "(6507, '2024-11-21', 1)": 0, + "(6507, '2024-11-21', 2)": 0, + "(6507, '2024-11-21', 3)": 0, + "(6507, '2024-11-21', 4)": 0, + "(6507, '2024-11-21', 5)": 0, + "(6507, '2024-11-21', 6)": 0, + "(6507, '2024-11-21', 7)": 0, + "(6507, '2024-11-22', 0)": 0, + "(6507, '2024-11-22', 1)": 0, + "(6507, '2024-11-22', 2)": 0, + "(6507, '2024-11-22', 3)": 0, + "(6507, '2024-11-22', 4)": 0, + "(6507, '2024-11-22', 5)": 0, + "(6507, '2024-11-22', 6)": 0, + "(6507, '2024-11-22', 7)": 0, + "(6507, '2024-11-23', 0)": 0, + "(6507, '2024-11-23', 1)": 0, + "(6507, '2024-11-23', 2)": 0, + "(6507, '2024-11-23', 3)": 0, + "(6507, '2024-11-23', 4)": 0, + "(6507, '2024-11-23', 5)": 0, + "(6507, '2024-11-23', 6)": 0, + "(6507, '2024-11-23', 7)": 0, + "(6507, '2024-11-24', 0)": 0, + "(6507, '2024-11-24', 1)": 0, + "(6507, '2024-11-24', 2)": 0, + "(6507, '2024-11-24', 3)": 0, + "(6507, '2024-11-24', 4)": 0, + "(6507, '2024-11-24', 5)": 0, + "(6507, '2024-11-24', 6)": 0, + "(6507, '2024-11-24', 7)": 0, + "(6507, '2024-11-25', 0)": 0, + "(6507, '2024-11-25', 1)": 0, + "(6507, '2024-11-25', 2)": 0, + "(6507, '2024-11-25', 3)": 0, + "(6507, '2024-11-25', 4)": 0, + "(6507, '2024-11-25', 5)": 0, + "(6507, '2024-11-25', 6)": 0, + "(6507, '2024-11-25', 7)": 0, + "(6507, '2024-11-26', 0)": 1, + "(6507, '2024-11-26', 1)": 0, + "(6507, '2024-11-26', 2)": 0, + "(6507, '2024-11-26', 3)": 0, + "(6507, '2024-11-26', 4)": 0, + "(6507, '2024-11-26', 5)": 0, + "(6507, '2024-11-26', 6)": 0, + "(6507, '2024-11-26', 7)": 0, + "(6507, '2024-11-27', 0)": 1, + "(6507, '2024-11-27', 1)": 0, + "(6507, '2024-11-27', 2)": 0, + "(6507, '2024-11-27', 3)": 0, + "(6507, '2024-11-27', 4)": 0, + "(6507, '2024-11-27', 5)": 0, + "(6507, '2024-11-27', 6)": 0, + "(6507, '2024-11-27', 7)": 0, + "(6507, '2024-11-28', 0)": 1, + "(6507, '2024-11-28', 1)": 0, + "(6507, '2024-11-28', 2)": 0, + "(6507, '2024-11-28', 3)": 0, + "(6507, '2024-11-28', 4)": 0, + "(6507, '2024-11-28', 5)": 0, + "(6507, '2024-11-28', 6)": 0, + "(6507, '2024-11-28', 7)": 0, + "(6507, '2024-11-29', 0)": 1, + "(6507, '2024-11-29', 1)": 0, + "(6507, '2024-11-29', 2)": 0, + "(6507, '2024-11-29', 3)": 0, + "(6507, '2024-11-29', 4)": 0, + "(6507, '2024-11-29', 5)": 0, + "(6507, '2024-11-29', 6)": 0, + "(6507, '2024-11-29', 7)": 0, + "(6507, '2024-11-30', 0)": 0, + "(6507, '2024-11-30', 1)": 1, + "(6507, '2024-11-30', 2)": 0, + "(6507, '2024-11-30', 3)": 0, + "(6507, '2024-11-30', 4)": 0, + "(6507, '2024-11-30', 5)": 0, + "(6507, '2024-11-30', 6)": 0, + "(6507, '2024-11-30', 7)": 0, + "(6677, '2024-11-01', 0)": 0, + "(6677, '2024-11-01', 1)": 0, + "(6677, '2024-11-01', 2)": 0, + "(6677, '2024-11-01', 3)": 0, + "(6677, '2024-11-01', 4)": 0, + "(6677, '2024-11-01', 5)": 0, + "(6677, '2024-11-01', 6)": 0, + "(6677, '2024-11-01', 7)": 0, + "(6677, '2024-11-02', 0)": 1, + "(6677, '2024-11-02', 1)": 0, + "(6677, '2024-11-02', 2)": 0, + "(6677, '2024-11-02', 3)": 0, + "(6677, '2024-11-02', 4)": 0, + "(6677, '2024-11-02', 5)": 0, + "(6677, '2024-11-02', 6)": 0, + "(6677, '2024-11-02', 7)": 0, + "(6677, '2024-11-03', 0)": 0, + "(6677, '2024-11-03', 1)": 0, + "(6677, '2024-11-03', 2)": 0, + "(6677, '2024-11-03', 3)": 1, + "(6677, '2024-11-03', 4)": 0, + "(6677, '2024-11-03', 5)": 0, + "(6677, '2024-11-03', 6)": 0, + "(6677, '2024-11-03', 7)": 0, + "(6677, '2024-11-04', 0)": 0, + "(6677, '2024-11-04', 1)": 0, + "(6677, '2024-11-04', 2)": 0, + "(6677, '2024-11-04', 3)": 0, + "(6677, '2024-11-04', 4)": 0, + "(6677, '2024-11-04', 5)": 0, + "(6677, '2024-11-04', 6)": 0, + "(6677, '2024-11-04', 7)": 0, + "(6677, '2024-11-05', 0)": 0, + "(6677, '2024-11-05', 1)": 0, + "(6677, '2024-11-05', 2)": 1, + "(6677, '2024-11-05', 3)": 0, + "(6677, '2024-11-05', 4)": 0, + "(6677, '2024-11-05', 5)": 0, + "(6677, '2024-11-05', 6)": 0, + "(6677, '2024-11-05', 7)": 0, + "(6677, '2024-11-06', 0)": 0, + "(6677, '2024-11-06', 1)": 0, + "(6677, '2024-11-06', 2)": 1, + "(6677, '2024-11-06', 3)": 0, + "(6677, '2024-11-06', 4)": 0, + "(6677, '2024-11-06', 5)": 0, + "(6677, '2024-11-06', 6)": 0, + "(6677, '2024-11-06', 7)": 0, + "(6677, '2024-11-07', 0)": 0, + "(6677, '2024-11-07', 1)": 0, + "(6677, '2024-11-07', 2)": 1, + "(6677, '2024-11-07', 3)": 0, + "(6677, '2024-11-07', 4)": 0, + "(6677, '2024-11-07', 5)": 0, + "(6677, '2024-11-07', 6)": 0, + "(6677, '2024-11-07', 7)": 0, + "(6677, '2024-11-08', 0)": 0, + "(6677, '2024-11-08', 1)": 0, + "(6677, '2024-11-08', 2)": 0, + "(6677, '2024-11-08', 3)": 0, + "(6677, '2024-11-08', 4)": 0, + "(6677, '2024-11-08', 5)": 0, + "(6677, '2024-11-08', 6)": 0, + "(6677, '2024-11-08', 7)": 0, + "(6677, '2024-11-09', 0)": 0, + "(6677, '2024-11-09', 1)": 0, + "(6677, '2024-11-09', 2)": 0, + "(6677, '2024-11-09', 3)": 0, + "(6677, '2024-11-09', 4)": 0, + "(6677, '2024-11-09', 5)": 0, + "(6677, '2024-11-09', 6)": 0, + "(6677, '2024-11-09', 7)": 0, + "(6677, '2024-11-10', 0)": 0, + "(6677, '2024-11-10', 1)": 0, + "(6677, '2024-11-10', 2)": 1, + "(6677, '2024-11-10', 3)": 0, + "(6677, '2024-11-10', 4)": 0, + "(6677, '2024-11-10', 5)": 0, + "(6677, '2024-11-10', 6)": 0, + "(6677, '2024-11-10', 7)": 0, + "(6677, '2024-11-11', 0)": 0, + "(6677, '2024-11-11', 1)": 0, + "(6677, '2024-11-11', 2)": 1, + "(6677, '2024-11-11', 3)": 0, + "(6677, '2024-11-11', 4)": 0, + "(6677, '2024-11-11', 5)": 0, + "(6677, '2024-11-11', 6)": 0, + "(6677, '2024-11-11', 7)": 0, + "(6677, '2024-11-12', 0)": 0, + "(6677, '2024-11-12', 1)": 0, + "(6677, '2024-11-12', 2)": 0, + "(6677, '2024-11-12', 3)": 0, + "(6677, '2024-11-12', 4)": 0, + "(6677, '2024-11-12', 5)": 0, + "(6677, '2024-11-12', 6)": 0, + "(6677, '2024-11-12', 7)": 0, + "(6677, '2024-11-13', 0)": 0, + "(6677, '2024-11-13', 1)": 0, + "(6677, '2024-11-13', 2)": 0, + "(6677, '2024-11-13', 3)": 0, + "(6677, '2024-11-13', 4)": 0, + "(6677, '2024-11-13', 5)": 0, + "(6677, '2024-11-13', 6)": 0, + "(6677, '2024-11-13', 7)": 0, + "(6677, '2024-11-14', 0)": 1, + "(6677, '2024-11-14', 1)": 0, + "(6677, '2024-11-14', 2)": 0, + "(6677, '2024-11-14', 3)": 0, + "(6677, '2024-11-14', 4)": 0, + "(6677, '2024-11-14', 5)": 0, + "(6677, '2024-11-14', 6)": 0, + "(6677, '2024-11-14', 7)": 0, + "(6677, '2024-11-15', 0)": 0, + "(6677, '2024-11-15', 1)": 0, + "(6677, '2024-11-15', 2)": 1, + "(6677, '2024-11-15', 3)": 0, + "(6677, '2024-11-15', 4)": 0, + "(6677, '2024-11-15', 5)": 0, + "(6677, '2024-11-15', 6)": 0, + "(6677, '2024-11-15', 7)": 0, + "(6677, '2024-11-16', 0)": 0, + "(6677, '2024-11-16', 1)": 1, + "(6677, '2024-11-16', 2)": 0, + "(6677, '2024-11-16', 3)": 0, + "(6677, '2024-11-16', 4)": 0, + "(6677, '2024-11-16', 5)": 0, + "(6677, '2024-11-16', 6)": 0, + "(6677, '2024-11-16', 7)": 0, + "(6677, '2024-11-17', 0)": 0, + "(6677, '2024-11-17', 1)": 0, + "(6677, '2024-11-17', 2)": 0, + "(6677, '2024-11-17', 3)": 1, + "(6677, '2024-11-17', 4)": 0, + "(6677, '2024-11-17', 5)": 0, + "(6677, '2024-11-17', 6)": 0, + "(6677, '2024-11-17', 7)": 0, + "(6677, '2024-11-18', 0)": 0, + "(6677, '2024-11-18', 1)": 0, + "(6677, '2024-11-18', 2)": 0, + "(6677, '2024-11-18', 3)": 0, + "(6677, '2024-11-18', 4)": 0, + "(6677, '2024-11-18', 5)": 0, + "(6677, '2024-11-18', 6)": 0, + "(6677, '2024-11-18', 7)": 0, + "(6677, '2024-11-19', 0)": 0, + "(6677, '2024-11-19', 1)": 0, + "(6677, '2024-11-19', 2)": 1, + "(6677, '2024-11-19', 3)": 0, + "(6677, '2024-11-19', 4)": 0, + "(6677, '2024-11-19', 5)": 0, + "(6677, '2024-11-19', 6)": 0, + "(6677, '2024-11-19', 7)": 0, + "(6677, '2024-11-20', 0)": 0, + "(6677, '2024-11-20', 1)": 0, + "(6677, '2024-11-20', 2)": 1, + "(6677, '2024-11-20', 3)": 0, + "(6677, '2024-11-20', 4)": 0, + "(6677, '2024-11-20', 5)": 0, + "(6677, '2024-11-20', 6)": 0, + "(6677, '2024-11-20', 7)": 0, + "(6677, '2024-11-21', 0)": 0, + "(6677, '2024-11-21', 1)": 0, + "(6677, '2024-11-21', 2)": 1, + "(6677, '2024-11-21', 3)": 0, + "(6677, '2024-11-21', 4)": 0, + "(6677, '2024-11-21', 5)": 0, + "(6677, '2024-11-21', 6)": 0, + "(6677, '2024-11-21', 7)": 0, + "(6677, '2024-11-22', 0)": 0, + "(6677, '2024-11-22', 1)": 0, + "(6677, '2024-11-22', 2)": 1, + "(6677, '2024-11-22', 3)": 0, + "(6677, '2024-11-22', 4)": 0, + "(6677, '2024-11-22', 5)": 0, + "(6677, '2024-11-22', 6)": 0, + "(6677, '2024-11-22', 7)": 0, + "(6677, '2024-11-23', 0)": 0, + "(6677, '2024-11-23', 1)": 0, + "(6677, '2024-11-23', 2)": 0, + "(6677, '2024-11-23', 3)": 1, + "(6677, '2024-11-23', 4)": 0, + "(6677, '2024-11-23', 5)": 0, + "(6677, '2024-11-23', 6)": 0, + "(6677, '2024-11-23', 7)": 0, + "(6677, '2024-11-24', 0)": 0, + "(6677, '2024-11-24', 1)": 0, + "(6677, '2024-11-24', 2)": 0, + "(6677, '2024-11-24', 3)": 0, + "(6677, '2024-11-24', 4)": 0, + "(6677, '2024-11-24', 5)": 0, + "(6677, '2024-11-24', 6)": 0, + "(6677, '2024-11-24', 7)": 0, + "(6677, '2024-11-25', 0)": 1, + "(6677, '2024-11-25', 1)": 0, + "(6677, '2024-11-25', 2)": 0, + "(6677, '2024-11-25', 3)": 0, + "(6677, '2024-11-25', 4)": 0, + "(6677, '2024-11-25', 5)": 0, + "(6677, '2024-11-25', 6)": 0, + "(6677, '2024-11-25', 7)": 0, + "(6677, '2024-11-26', 0)": 0, + "(6677, '2024-11-26', 1)": 0, + "(6677, '2024-11-26', 2)": 0, + "(6677, '2024-11-26', 3)": 0, + "(6677, '2024-11-26', 4)": 0, + "(6677, '2024-11-26', 5)": 0, + "(6677, '2024-11-26', 6)": 0, + "(6677, '2024-11-26', 7)": 0, + "(6677, '2024-11-27', 0)": 1, + "(6677, '2024-11-27', 1)": 0, + "(6677, '2024-11-27', 2)": 0, + "(6677, '2024-11-27', 3)": 0, + "(6677, '2024-11-27', 4)": 0, + "(6677, '2024-11-27', 5)": 0, + "(6677, '2024-11-27', 6)": 0, + "(6677, '2024-11-27', 7)": 0, + "(6677, '2024-11-28', 0)": 1, + "(6677, '2024-11-28', 1)": 0, + "(6677, '2024-11-28', 2)": 0, + "(6677, '2024-11-28', 3)": 0, + "(6677, '2024-11-28', 4)": 0, + "(6677, '2024-11-28', 5)": 0, + "(6677, '2024-11-28', 6)": 0, + "(6677, '2024-11-28', 7)": 0, + "(6677, '2024-11-29', 0)": 0, + "(6677, '2024-11-29', 1)": 0, + "(6677, '2024-11-29', 2)": 1, + "(6677, '2024-11-29', 3)": 0, + "(6677, '2024-11-29', 4)": 0, + "(6677, '2024-11-29', 5)": 0, + "(6677, '2024-11-29', 6)": 0, + "(6677, '2024-11-29', 7)": 0, + "(6677, '2024-11-30', 0)": 0, + "(6677, '2024-11-30', 1)": 0, + "(6677, '2024-11-30', 2)": 0, + "(6677, '2024-11-30', 3)": 0, + "(6677, '2024-11-30', 4)": 0, + "(6677, '2024-11-30', 5)": 0, + "(6677, '2024-11-30', 6)": 0, + "(6677, '2024-11-30', 7)": 0, + "(6681, '2024-11-01', 0)": 0, + "(6681, '2024-11-01', 1)": 0, + "(6681, '2024-11-01', 2)": 0, + "(6681, '2024-11-01', 3)": 1, + "(6681, '2024-11-01', 4)": 0, + "(6681, '2024-11-01', 5)": 0, + "(6681, '2024-11-01', 6)": 0, + "(6681, '2024-11-01', 7)": 0, + "(6681, '2024-11-02', 0)": 0, + "(6681, '2024-11-02', 1)": 0, + "(6681, '2024-11-02', 2)": 0, + "(6681, '2024-11-02', 3)": 0, + "(6681, '2024-11-02', 4)": 0, + "(6681, '2024-11-02', 5)": 0, + "(6681, '2024-11-02', 6)": 0, + "(6681, '2024-11-02', 7)": 0, + "(6681, '2024-11-03', 0)": 0, + "(6681, '2024-11-03', 1)": 0, + "(6681, '2024-11-03', 2)": 0, + "(6681, '2024-11-03', 3)": 0, + "(6681, '2024-11-03', 4)": 0, + "(6681, '2024-11-03', 5)": 0, + "(6681, '2024-11-03', 6)": 0, + "(6681, '2024-11-03', 7)": 0, + "(6681, '2024-11-04', 0)": 0, + "(6681, '2024-11-04', 1)": 0, + "(6681, '2024-11-04', 2)": 0, + "(6681, '2024-11-04', 3)": 0, + "(6681, '2024-11-04', 4)": 0, + "(6681, '2024-11-04', 5)": 0, + "(6681, '2024-11-04', 6)": 0, + "(6681, '2024-11-04', 7)": 0, + "(6681, '2024-11-05', 0)": 0, + "(6681, '2024-11-05', 1)": 0, + "(6681, '2024-11-05', 2)": 0, + "(6681, '2024-11-05', 3)": 1, + "(6681, '2024-11-05', 4)": 0, + "(6681, '2024-11-05', 5)": 0, + "(6681, '2024-11-05', 6)": 0, + "(6681, '2024-11-05', 7)": 0, + "(6681, '2024-11-06', 0)": 0, + "(6681, '2024-11-06', 1)": 0, + "(6681, '2024-11-06', 2)": 0, + "(6681, '2024-11-06', 3)": 1, + "(6681, '2024-11-06', 4)": 0, + "(6681, '2024-11-06', 5)": 0, + "(6681, '2024-11-06', 6)": 0, + "(6681, '2024-11-06', 7)": 0, + "(6681, '2024-11-07', 0)": 0, + "(6681, '2024-11-07', 1)": 0, + "(6681, '2024-11-07', 2)": 0, + "(6681, '2024-11-07', 3)": 0, + "(6681, '2024-11-07', 4)": 0, + "(6681, '2024-11-07', 5)": 0, + "(6681, '2024-11-07', 6)": 0, + "(6681, '2024-11-07', 7)": 0, + "(6681, '2024-11-08', 0)": 0, + "(6681, '2024-11-08', 1)": 0, + "(6681, '2024-11-08', 2)": 0, + "(6681, '2024-11-08', 3)": 1, + "(6681, '2024-11-08', 4)": 0, + "(6681, '2024-11-08', 5)": 0, + "(6681, '2024-11-08', 6)": 0, + "(6681, '2024-11-08', 7)": 0, + "(6681, '2024-11-09', 0)": 0, + "(6681, '2024-11-09', 1)": 0, + "(6681, '2024-11-09', 2)": 0, + "(6681, '2024-11-09', 3)": 0, + "(6681, '2024-11-09', 4)": 0, + "(6681, '2024-11-09', 5)": 0, + "(6681, '2024-11-09', 6)": 0, + "(6681, '2024-11-09', 7)": 0, + "(6681, '2024-11-10', 0)": 0, + "(6681, '2024-11-10', 1)": 0, + "(6681, '2024-11-10', 2)": 0, + "(6681, '2024-11-10', 3)": 0, + "(6681, '2024-11-10', 4)": 0, + "(6681, '2024-11-10', 5)": 0, + "(6681, '2024-11-10', 6)": 0, + "(6681, '2024-11-10', 7)": 0, + "(6681, '2024-11-11', 0)": 0, + "(6681, '2024-11-11', 1)": 0, + "(6681, '2024-11-11', 2)": 0, + "(6681, '2024-11-11', 3)": 0, + "(6681, '2024-11-11', 4)": 0, + "(6681, '2024-11-11', 5)": 0, + "(6681, '2024-11-11', 6)": 0, + "(6681, '2024-11-11', 7)": 0, + "(6681, '2024-11-12', 0)": 0, + "(6681, '2024-11-12', 1)": 0, + "(6681, '2024-11-12', 2)": 0, + "(6681, '2024-11-12', 3)": 1, + "(6681, '2024-11-12', 4)": 0, + "(6681, '2024-11-12', 5)": 0, + "(6681, '2024-11-12', 6)": 0, + "(6681, '2024-11-12', 7)": 0, + "(6681, '2024-11-13', 0)": 0, + "(6681, '2024-11-13', 1)": 0, + "(6681, '2024-11-13', 2)": 0, + "(6681, '2024-11-13', 3)": 0, + "(6681, '2024-11-13', 4)": 0, + "(6681, '2024-11-13', 5)": 0, + "(6681, '2024-11-13', 6)": 0, + "(6681, '2024-11-13', 7)": 0, + "(6681, '2024-11-14', 0)": 0, + "(6681, '2024-11-14', 1)": 0, + "(6681, '2024-11-14', 2)": 0, + "(6681, '2024-11-14', 3)": 1, + "(6681, '2024-11-14', 4)": 0, + "(6681, '2024-11-14', 5)": 0, + "(6681, '2024-11-14', 6)": 0, + "(6681, '2024-11-14', 7)": 0, + "(6681, '2024-11-15', 0)": 0, + "(6681, '2024-11-15', 1)": 0, + "(6681, '2024-11-15', 2)": 0, + "(6681, '2024-11-15', 3)": 0, + "(6681, '2024-11-15', 4)": 0, + "(6681, '2024-11-15', 5)": 0, + "(6681, '2024-11-15', 6)": 0, + "(6681, '2024-11-15', 7)": 0, + "(6681, '2024-11-16', 0)": 0, + "(6681, '2024-11-16', 1)": 0, + "(6681, '2024-11-16', 2)": 0, + "(6681, '2024-11-16', 3)": 0, + "(6681, '2024-11-16', 4)": 0, + "(6681, '2024-11-16', 5)": 0, + "(6681, '2024-11-16', 6)": 0, + "(6681, '2024-11-16', 7)": 0, + "(6681, '2024-11-17', 0)": 0, + "(6681, '2024-11-17', 1)": 0, + "(6681, '2024-11-17', 2)": 0, + "(6681, '2024-11-17', 3)": 0, + "(6681, '2024-11-17', 4)": 0, + "(6681, '2024-11-17', 5)": 0, + "(6681, '2024-11-17', 6)": 0, + "(6681, '2024-11-17', 7)": 0, + "(6681, '2024-11-18', 0)": 0, + "(6681, '2024-11-18', 1)": 0, + "(6681, '2024-11-18', 2)": 0, + "(6681, '2024-11-18', 3)": 0, + "(6681, '2024-11-18', 4)": 0, + "(6681, '2024-11-18', 5)": 0, + "(6681, '2024-11-18', 6)": 0, + "(6681, '2024-11-18', 7)": 0, + "(6681, '2024-11-19', 0)": 0, + "(6681, '2024-11-19', 1)": 0, + "(6681, '2024-11-19', 2)": 0, + "(6681, '2024-11-19', 3)": 0, + "(6681, '2024-11-19', 4)": 0, + "(6681, '2024-11-19', 5)": 0, + "(6681, '2024-11-19', 6)": 0, + "(6681, '2024-11-19', 7)": 0, + "(6681, '2024-11-20', 0)": 0, + "(6681, '2024-11-20', 1)": 0, + "(6681, '2024-11-20', 2)": 0, + "(6681, '2024-11-20', 3)": 0, + "(6681, '2024-11-20', 4)": 0, + "(6681, '2024-11-20', 5)": 0, + "(6681, '2024-11-20', 6)": 0, + "(6681, '2024-11-20', 7)": 0, + "(6681, '2024-11-21', 0)": 0, + "(6681, '2024-11-21', 1)": 0, + "(6681, '2024-11-21', 2)": 0, + "(6681, '2024-11-21', 3)": 0, + "(6681, '2024-11-21', 4)": 0, + "(6681, '2024-11-21', 5)": 0, + "(6681, '2024-11-21', 6)": 0, + "(6681, '2024-11-21', 7)": 0, + "(6681, '2024-11-22', 0)": 0, + "(6681, '2024-11-22', 1)": 0, + "(6681, '2024-11-22', 2)": 0, + "(6681, '2024-11-22', 3)": 0, + "(6681, '2024-11-22', 4)": 0, + "(6681, '2024-11-22', 5)": 0, + "(6681, '2024-11-22', 6)": 0, + "(6681, '2024-11-22', 7)": 0, + "(6681, '2024-11-23', 0)": 0, + "(6681, '2024-11-23', 1)": 0, + "(6681, '2024-11-23', 2)": 0, + "(6681, '2024-11-23', 3)": 0, + "(6681, '2024-11-23', 4)": 0, + "(6681, '2024-11-23', 5)": 0, + "(6681, '2024-11-23', 6)": 0, + "(6681, '2024-11-23', 7)": 0, + "(6681, '2024-11-24', 0)": 0, + "(6681, '2024-11-24', 1)": 0, + "(6681, '2024-11-24', 2)": 0, + "(6681, '2024-11-24', 3)": 0, + "(6681, '2024-11-24', 4)": 0, + "(6681, '2024-11-24', 5)": 0, + "(6681, '2024-11-24', 6)": 0, + "(6681, '2024-11-24', 7)": 0, + "(6681, '2024-11-25', 0)": 0, + "(6681, '2024-11-25', 1)": 0, + "(6681, '2024-11-25', 2)": 0, + "(6681, '2024-11-25', 3)": 0, + "(6681, '2024-11-25', 4)": 0, + "(6681, '2024-11-25', 5)": 0, + "(6681, '2024-11-25', 6)": 0, + "(6681, '2024-11-25', 7)": 0, + "(6681, '2024-11-26', 0)": 0, + "(6681, '2024-11-26', 1)": 0, + "(6681, '2024-11-26', 2)": 0, + "(6681, '2024-11-26', 3)": 0, + "(6681, '2024-11-26', 4)": 0, + "(6681, '2024-11-26', 5)": 0, + "(6681, '2024-11-26', 6)": 0, + "(6681, '2024-11-26', 7)": 0, + "(6681, '2024-11-27', 0)": 0, + "(6681, '2024-11-27', 1)": 0, + "(6681, '2024-11-27', 2)": 0, + "(6681, '2024-11-27', 3)": 0, + "(6681, '2024-11-27', 4)": 0, + "(6681, '2024-11-27', 5)": 0, + "(6681, '2024-11-27', 6)": 0, + "(6681, '2024-11-27', 7)": 0, + "(6681, '2024-11-28', 0)": 0, + "(6681, '2024-11-28', 1)": 0, + "(6681, '2024-11-28', 2)": 0, + "(6681, '2024-11-28', 3)": 0, + "(6681, '2024-11-28', 4)": 0, + "(6681, '2024-11-28', 5)": 0, + "(6681, '2024-11-28', 6)": 0, + "(6681, '2024-11-28', 7)": 0, + "(6681, '2024-11-29', 0)": 0, + "(6681, '2024-11-29', 1)": 0, + "(6681, '2024-11-29', 2)": 0, + "(6681, '2024-11-29', 3)": 0, + "(6681, '2024-11-29', 4)": 0, + "(6681, '2024-11-29', 5)": 0, + "(6681, '2024-11-29', 6)": 0, + "(6681, '2024-11-29', 7)": 0, + "(6681, '2024-11-30', 0)": 0, + "(6681, '2024-11-30', 1)": 0, + "(6681, '2024-11-30', 2)": 0, + "(6681, '2024-11-30', 3)": 0, + "(6681, '2024-11-30', 4)": 0, + "(6681, '2024-11-30', 5)": 0, + "(6681, '2024-11-30', 6)": 0, + "(6681, '2024-11-30', 7)": 0, + "(6715, '2024-11-01', 0)": 0, + "(6715, '2024-11-01', 1)": 0, + "(6715, '2024-11-01', 2)": 1, + "(6715, '2024-11-01', 3)": 0, + "(6715, '2024-11-01', 4)": 0, + "(6715, '2024-11-01', 5)": 0, + "(6715, '2024-11-01', 6)": 0, + "(6715, '2024-11-01', 7)": 0, + "(6715, '2024-11-02', 0)": 0, + "(6715, '2024-11-02', 1)": 0, + "(6715, '2024-11-02', 2)": 0, + "(6715, '2024-11-02', 3)": 0, + "(6715, '2024-11-02', 4)": 0, + "(6715, '2024-11-02', 5)": 0, + "(6715, '2024-11-02', 6)": 0, + "(6715, '2024-11-02', 7)": 0, + "(6715, '2024-11-03', 0)": 0, + "(6715, '2024-11-03', 1)": 0, + "(6715, '2024-11-03', 2)": 0, + "(6715, '2024-11-03', 3)": 0, + "(6715, '2024-11-03', 4)": 0, + "(6715, '2024-11-03', 5)": 0, + "(6715, '2024-11-03', 6)": 0, + "(6715, '2024-11-03', 7)": 0, + "(6715, '2024-11-04', 0)": 0, + "(6715, '2024-11-04', 1)": 0, + "(6715, '2024-11-04', 2)": 0, + "(6715, '2024-11-04', 3)": 0, + "(6715, '2024-11-04', 4)": 0, + "(6715, '2024-11-04', 5)": 0, + "(6715, '2024-11-04', 6)": 0, + "(6715, '2024-11-04', 7)": 0, + "(6715, '2024-11-05', 0)": 0, + "(6715, '2024-11-05', 1)": 0, + "(6715, '2024-11-05', 2)": 0, + "(6715, '2024-11-05', 3)": 0, + "(6715, '2024-11-05', 4)": 0, + "(6715, '2024-11-05', 5)": 0, + "(6715, '2024-11-05', 6)": 0, + "(6715, '2024-11-05', 7)": 0, + "(6715, '2024-11-06', 0)": 0, + "(6715, '2024-11-06', 1)": 0, + "(6715, '2024-11-06', 2)": 0, + "(6715, '2024-11-06', 3)": 0, + "(6715, '2024-11-06', 4)": 0, + "(6715, '2024-11-06', 5)": 0, + "(6715, '2024-11-06', 6)": 0, + "(6715, '2024-11-06', 7)": 0, + "(6715, '2024-11-07', 0)": 0, + "(6715, '2024-11-07', 1)": 0, + "(6715, '2024-11-07', 2)": 0, + "(6715, '2024-11-07', 3)": 0, + "(6715, '2024-11-07', 4)": 0, + "(6715, '2024-11-07', 5)": 0, + "(6715, '2024-11-07', 6)": 0, + "(6715, '2024-11-07', 7)": 0, + "(6715, '2024-11-08', 0)": 0, + "(6715, '2024-11-08', 1)": 0, + "(6715, '2024-11-08', 2)": 0, + "(6715, '2024-11-08', 3)": 0, + "(6715, '2024-11-08', 4)": 0, + "(6715, '2024-11-08', 5)": 0, + "(6715, '2024-11-08', 6)": 0, + "(6715, '2024-11-08', 7)": 0, + "(6715, '2024-11-09', 0)": 0, + "(6715, '2024-11-09', 1)": 0, + "(6715, '2024-11-09', 2)": 0, + "(6715, '2024-11-09', 3)": 0, + "(6715, '2024-11-09', 4)": 0, + "(6715, '2024-11-09', 5)": 0, + "(6715, '2024-11-09', 6)": 0, + "(6715, '2024-11-09', 7)": 0, + "(6715, '2024-11-10', 0)": 0, + "(6715, '2024-11-10', 1)": 0, + "(6715, '2024-11-10', 2)": 0, + "(6715, '2024-11-10', 3)": 0, + "(6715, '2024-11-10', 4)": 0, + "(6715, '2024-11-10', 5)": 0, + "(6715, '2024-11-10', 6)": 0, + "(6715, '2024-11-10', 7)": 0, + "(6715, '2024-11-11', 0)": 0, + "(6715, '2024-11-11', 1)": 0, + "(6715, '2024-11-11', 2)": 0, + "(6715, '2024-11-11', 3)": 0, + "(6715, '2024-11-11', 4)": 0, + "(6715, '2024-11-11', 5)": 0, + "(6715, '2024-11-11', 6)": 0, + "(6715, '2024-11-11', 7)": 0, + "(6715, '2024-11-12', 0)": 0, + "(6715, '2024-11-12', 1)": 0, + "(6715, '2024-11-12', 2)": 0, + "(6715, '2024-11-12', 3)": 0, + "(6715, '2024-11-12', 4)": 0, + "(6715, '2024-11-12', 5)": 0, + "(6715, '2024-11-12', 6)": 0, + "(6715, '2024-11-12', 7)": 0, + "(6715, '2024-11-13', 0)": 0, + "(6715, '2024-11-13', 1)": 0, + "(6715, '2024-11-13', 2)": 0, + "(6715, '2024-11-13', 3)": 0, + "(6715, '2024-11-13', 4)": 0, + "(6715, '2024-11-13', 5)": 0, + "(6715, '2024-11-13', 6)": 0, + "(6715, '2024-11-13', 7)": 0, + "(6715, '2024-11-14', 0)": 0, + "(6715, '2024-11-14', 1)": 0, + "(6715, '2024-11-14', 2)": 0, + "(6715, '2024-11-14', 3)": 0, + "(6715, '2024-11-14', 4)": 0, + "(6715, '2024-11-14', 5)": 0, + "(6715, '2024-11-14', 6)": 0, + "(6715, '2024-11-14', 7)": 0, + "(6715, '2024-11-15', 0)": 0, + "(6715, '2024-11-15', 1)": 0, + "(6715, '2024-11-15', 2)": 0, + "(6715, '2024-11-15', 3)": 0, + "(6715, '2024-11-15', 4)": 0, + "(6715, '2024-11-15', 5)": 0, + "(6715, '2024-11-15', 6)": 0, + "(6715, '2024-11-15', 7)": 0, + "(6715, '2024-11-16', 0)": 0, + "(6715, '2024-11-16', 1)": 0, + "(6715, '2024-11-16', 2)": 0, + "(6715, '2024-11-16', 3)": 0, + "(6715, '2024-11-16', 4)": 0, + "(6715, '2024-11-16', 5)": 0, + "(6715, '2024-11-16', 6)": 0, + "(6715, '2024-11-16', 7)": 0, + "(6715, '2024-11-17', 0)": 0, + "(6715, '2024-11-17', 1)": 0, + "(6715, '2024-11-17', 2)": 0, + "(6715, '2024-11-17', 3)": 0, + "(6715, '2024-11-17', 4)": 0, + "(6715, '2024-11-17', 5)": 0, + "(6715, '2024-11-17', 6)": 0, + "(6715, '2024-11-17', 7)": 0, + "(6715, '2024-11-18', 0)": 0, + "(6715, '2024-11-18', 1)": 0, + "(6715, '2024-11-18', 2)": 0, + "(6715, '2024-11-18', 3)": 0, + "(6715, '2024-11-18', 4)": 0, + "(6715, '2024-11-18', 5)": 0, + "(6715, '2024-11-18', 6)": 0, + "(6715, '2024-11-18', 7)": 0, + "(6715, '2024-11-19', 0)": 0, + "(6715, '2024-11-19', 1)": 0, + "(6715, '2024-11-19', 2)": 0, + "(6715, '2024-11-19', 3)": 0, + "(6715, '2024-11-19', 4)": 0, + "(6715, '2024-11-19', 5)": 0, + "(6715, '2024-11-19', 6)": 0, + "(6715, '2024-11-19', 7)": 0, + "(6715, '2024-11-20', 0)": 0, + "(6715, '2024-11-20', 1)": 0, + "(6715, '2024-11-20', 2)": 0, + "(6715, '2024-11-20', 3)": 0, + "(6715, '2024-11-20', 4)": 0, + "(6715, '2024-11-20', 5)": 0, + "(6715, '2024-11-20', 6)": 0, + "(6715, '2024-11-20', 7)": 0, + "(6715, '2024-11-21', 0)": 0, + "(6715, '2024-11-21', 1)": 0, + "(6715, '2024-11-21', 2)": 0, + "(6715, '2024-11-21', 3)": 0, + "(6715, '2024-11-21', 4)": 0, + "(6715, '2024-11-21', 5)": 0, + "(6715, '2024-11-21', 6)": 0, + "(6715, '2024-11-21', 7)": 0, + "(6715, '2024-11-22', 0)": 0, + "(6715, '2024-11-22', 1)": 0, + "(6715, '2024-11-22', 2)": 0, + "(6715, '2024-11-22', 3)": 0, + "(6715, '2024-11-22', 4)": 0, + "(6715, '2024-11-22', 5)": 0, + "(6715, '2024-11-22', 6)": 0, + "(6715, '2024-11-22', 7)": 0, + "(6715, '2024-11-23', 0)": 0, + "(6715, '2024-11-23', 1)": 0, + "(6715, '2024-11-23', 2)": 0, + "(6715, '2024-11-23', 3)": 0, + "(6715, '2024-11-23', 4)": 0, + "(6715, '2024-11-23', 5)": 0, + "(6715, '2024-11-23', 6)": 0, + "(6715, '2024-11-23', 7)": 0, + "(6715, '2024-11-24', 0)": 0, + "(6715, '2024-11-24', 1)": 0, + "(6715, '2024-11-24', 2)": 0, + "(6715, '2024-11-24', 3)": 0, + "(6715, '2024-11-24', 4)": 0, + "(6715, '2024-11-24', 5)": 0, + "(6715, '2024-11-24', 6)": 0, + "(6715, '2024-11-24', 7)": 0, + "(6715, '2024-11-25', 0)": 0, + "(6715, '2024-11-25', 1)": 0, + "(6715, '2024-11-25', 2)": 0, + "(6715, '2024-11-25', 3)": 0, + "(6715, '2024-11-25', 4)": 0, + "(6715, '2024-11-25', 5)": 0, + "(6715, '2024-11-25', 6)": 0, + "(6715, '2024-11-25', 7)": 0, + "(6715, '2024-11-26', 0)": 0, + "(6715, '2024-11-26', 1)": 0, + "(6715, '2024-11-26', 2)": 0, + "(6715, '2024-11-26', 3)": 0, + "(6715, '2024-11-26', 4)": 0, + "(6715, '2024-11-26', 5)": 0, + "(6715, '2024-11-26', 6)": 0, + "(6715, '2024-11-26', 7)": 0, + "(6715, '2024-11-27', 0)": 0, + "(6715, '2024-11-27', 1)": 0, + "(6715, '2024-11-27', 2)": 0, + "(6715, '2024-11-27', 3)": 0, + "(6715, '2024-11-27', 4)": 0, + "(6715, '2024-11-27', 5)": 0, + "(6715, '2024-11-27', 6)": 0, + "(6715, '2024-11-27', 7)": 0, + "(6715, '2024-11-28', 0)": 0, + "(6715, '2024-11-28', 1)": 0, + "(6715, '2024-11-28', 2)": 0, + "(6715, '2024-11-28', 3)": 0, + "(6715, '2024-11-28', 4)": 0, + "(6715, '2024-11-28', 5)": 0, + "(6715, '2024-11-28', 6)": 0, + "(6715, '2024-11-28', 7)": 0, + "(6715, '2024-11-29', 0)": 0, + "(6715, '2024-11-29', 1)": 0, + "(6715, '2024-11-29', 2)": 0, + "(6715, '2024-11-29', 3)": 0, + "(6715, '2024-11-29', 4)": 0, + "(6715, '2024-11-29', 5)": 0, + "(6715, '2024-11-29', 6)": 0, + "(6715, '2024-11-29', 7)": 0, + "(6715, '2024-11-30', 0)": 0, + "(6715, '2024-11-30', 1)": 0, + "(6715, '2024-11-30', 2)": 0, + "(6715, '2024-11-30', 3)": 0, + "(6715, '2024-11-30', 4)": 0, + "(6715, '2024-11-30', 5)": 0, + "(6715, '2024-11-30', 6)": 0, + "(6715, '2024-11-30', 7)": 0, + "(6836, '2024-11-01', 0)": 0, + "(6836, '2024-11-01', 1)": 0, + "(6836, '2024-11-01', 2)": 1, + "(6836, '2024-11-01', 3)": 0, + "(6836, '2024-11-01', 4)": 0, + "(6836, '2024-11-01', 5)": 0, + "(6836, '2024-11-01', 6)": 0, + "(6836, '2024-11-01', 7)": 0, + "(6836, '2024-11-02', 0)": 0, + "(6836, '2024-11-02', 1)": 1, + "(6836, '2024-11-02', 2)": 0, + "(6836, '2024-11-02', 3)": 0, + "(6836, '2024-11-02', 4)": 0, + "(6836, '2024-11-02', 5)": 0, + "(6836, '2024-11-02', 6)": 0, + "(6836, '2024-11-02', 7)": 0, + "(6836, '2024-11-03', 0)": 0, + "(6836, '2024-11-03', 1)": 0, + "(6836, '2024-11-03', 2)": 1, + "(6836, '2024-11-03', 3)": 0, + "(6836, '2024-11-03', 4)": 0, + "(6836, '2024-11-03', 5)": 0, + "(6836, '2024-11-03', 6)": 0, + "(6836, '2024-11-03', 7)": 0, + "(6836, '2024-11-04', 0)": 0, + "(6836, '2024-11-04', 1)": 0, + "(6836, '2024-11-04', 2)": 0, + "(6836, '2024-11-04', 3)": 0, + "(6836, '2024-11-04', 4)": 0, + "(6836, '2024-11-04', 5)": 0, + "(6836, '2024-11-04', 6)": 0, + "(6836, '2024-11-04', 7)": 0, + "(6836, '2024-11-05', 0)": 0, + "(6836, '2024-11-05', 1)": 1, + "(6836, '2024-11-05', 2)": 0, + "(6836, '2024-11-05', 3)": 0, + "(6836, '2024-11-05', 4)": 0, + "(6836, '2024-11-05', 5)": 0, + "(6836, '2024-11-05', 6)": 0, + "(6836, '2024-11-05', 7)": 0, + "(6836, '2024-11-06', 0)": 0, + "(6836, '2024-11-06', 1)": 0, + "(6836, '2024-11-06', 2)": 0, + "(6836, '2024-11-06', 3)": 0, + "(6836, '2024-11-06', 4)": 0, + "(6836, '2024-11-06', 5)": 0, + "(6836, '2024-11-06', 6)": 0, + "(6836, '2024-11-06', 7)": 0, + "(6836, '2024-11-07', 0)": 0, + "(6836, '2024-11-07', 1)": 0, + "(6836, '2024-11-07', 2)": 0, + "(6836, '2024-11-07', 3)": 1, + "(6836, '2024-11-07', 4)": 0, + "(6836, '2024-11-07', 5)": 0, + "(6836, '2024-11-07', 6)": 0, + "(6836, '2024-11-07', 7)": 0, + "(6836, '2024-11-08', 0)": 0, + "(6836, '2024-11-08', 1)": 0, + "(6836, '2024-11-08', 2)": 0, + "(6836, '2024-11-08', 3)": 1, + "(6836, '2024-11-08', 4)": 0, + "(6836, '2024-11-08', 5)": 0, + "(6836, '2024-11-08', 6)": 0, + "(6836, '2024-11-08', 7)": 0, + "(6836, '2024-11-09', 0)": 0, + "(6836, '2024-11-09', 1)": 0, + "(6836, '2024-11-09', 2)": 0, + "(6836, '2024-11-09', 3)": 0, + "(6836, '2024-11-09', 4)": 0, + "(6836, '2024-11-09', 5)": 0, + "(6836, '2024-11-09', 6)": 0, + "(6836, '2024-11-09', 7)": 0, + "(6836, '2024-11-10', 0)": 0, + "(6836, '2024-11-10', 1)": 0, + "(6836, '2024-11-10', 2)": 0, + "(6836, '2024-11-10', 3)": 0, + "(6836, '2024-11-10', 4)": 0, + "(6836, '2024-11-10', 5)": 0, + "(6836, '2024-11-10', 6)": 0, + "(6836, '2024-11-10', 7)": 0, + "(6836, '2024-11-11', 0)": 0, + "(6836, '2024-11-11', 1)": 0, + "(6836, '2024-11-11', 2)": 1, + "(6836, '2024-11-11', 3)": 0, + "(6836, '2024-11-11', 4)": 0, + "(6836, '2024-11-11', 5)": 0, + "(6836, '2024-11-11', 6)": 0, + "(6836, '2024-11-11', 7)": 0, + "(6836, '2024-11-12', 0)": 0, + "(6836, '2024-11-12', 1)": 0, + "(6836, '2024-11-12', 2)": 0, + "(6836, '2024-11-12', 3)": 0, + "(6836, '2024-11-12', 4)": 0, + "(6836, '2024-11-12', 5)": 0, + "(6836, '2024-11-12', 6)": 0, + "(6836, '2024-11-12', 7)": 0, + "(6836, '2024-11-13', 0)": 0, + "(6836, '2024-11-13', 1)": 0, + "(6836, '2024-11-13', 2)": 0, + "(6836, '2024-11-13', 3)": 0, + "(6836, '2024-11-13', 4)": 0, + "(6836, '2024-11-13', 5)": 0, + "(6836, '2024-11-13', 6)": 0, + "(6836, '2024-11-13', 7)": 0, + "(6836, '2024-11-14', 0)": 0, + "(6836, '2024-11-14', 1)": 0, + "(6836, '2024-11-14', 2)": 0, + "(6836, '2024-11-14', 3)": 0, + "(6836, '2024-11-14', 4)": 0, + "(6836, '2024-11-14', 5)": 0, + "(6836, '2024-11-14', 6)": 0, + "(6836, '2024-11-14', 7)": 0, + "(6836, '2024-11-15', 0)": 0, + "(6836, '2024-11-15', 1)": 0, + "(6836, '2024-11-15', 2)": 1, + "(6836, '2024-11-15', 3)": 0, + "(6836, '2024-11-15', 4)": 0, + "(6836, '2024-11-15', 5)": 0, + "(6836, '2024-11-15', 6)": 0, + "(6836, '2024-11-15', 7)": 0, + "(6836, '2024-11-16', 0)": 0, + "(6836, '2024-11-16', 1)": 0, + "(6836, '2024-11-16', 2)": 1, + "(6836, '2024-11-16', 3)": 0, + "(6836, '2024-11-16', 4)": 0, + "(6836, '2024-11-16', 5)": 0, + "(6836, '2024-11-16', 6)": 0, + "(6836, '2024-11-16', 7)": 0, + "(6836, '2024-11-17', 0)": 0, + "(6836, '2024-11-17', 1)": 0, + "(6836, '2024-11-17', 2)": 0, + "(6836, '2024-11-17', 3)": 0, + "(6836, '2024-11-17', 4)": 0, + "(6836, '2024-11-17', 5)": 1, + "(6836, '2024-11-17', 6)": 0, + "(6836, '2024-11-17', 7)": 0, + "(6836, '2024-11-18', 0)": 0, + "(6836, '2024-11-18', 1)": 0, + "(6836, '2024-11-18', 2)": 0, + "(6836, '2024-11-18', 3)": 1, + "(6836, '2024-11-18', 4)": 0, + "(6836, '2024-11-18', 5)": 0, + "(6836, '2024-11-18', 6)": 0, + "(6836, '2024-11-18', 7)": 0, + "(6836, '2024-11-19', 0)": 0, + "(6836, '2024-11-19', 1)": 0, + "(6836, '2024-11-19', 2)": 0, + "(6836, '2024-11-19', 3)": 0, + "(6836, '2024-11-19', 4)": 0, + "(6836, '2024-11-19', 5)": 0, + "(6836, '2024-11-19', 6)": 0, + "(6836, '2024-11-19', 7)": 0, + "(6836, '2024-11-20', 0)": 0, + "(6836, '2024-11-20', 1)": 0, + "(6836, '2024-11-20', 2)": 1, + "(6836, '2024-11-20', 3)": 0, + "(6836, '2024-11-20', 4)": 0, + "(6836, '2024-11-20', 5)": 0, + "(6836, '2024-11-20', 6)": 0, + "(6836, '2024-11-20', 7)": 0, + "(6836, '2024-11-21', 0)": 0, + "(6836, '2024-11-21', 1)": 0, + "(6836, '2024-11-21', 2)": 0, + "(6836, '2024-11-21', 3)": 1, + "(6836, '2024-11-21', 4)": 0, + "(6836, '2024-11-21', 5)": 0, + "(6836, '2024-11-21', 6)": 0, + "(6836, '2024-11-21', 7)": 0, + "(6836, '2024-11-22', 0)": 0, + "(6836, '2024-11-22', 1)": 0, + "(6836, '2024-11-22', 2)": 0, + "(6836, '2024-11-22', 3)": 0, + "(6836, '2024-11-22', 4)": 0, + "(6836, '2024-11-22', 5)": 0, + "(6836, '2024-11-22', 6)": 0, + "(6836, '2024-11-22', 7)": 0, + "(6836, '2024-11-23', 0)": 0, + "(6836, '2024-11-23', 1)": 0, + "(6836, '2024-11-23', 2)": 1, + "(6836, '2024-11-23', 3)": 0, + "(6836, '2024-11-23', 4)": 0, + "(6836, '2024-11-23', 5)": 0, + "(6836, '2024-11-23', 6)": 0, + "(6836, '2024-11-23', 7)": 0, + "(6836, '2024-11-24', 0)": 0, + "(6836, '2024-11-24', 1)": 0, + "(6836, '2024-11-24', 2)": 1, + "(6836, '2024-11-24', 3)": 0, + "(6836, '2024-11-24', 4)": 0, + "(6836, '2024-11-24', 5)": 0, + "(6836, '2024-11-24', 6)": 0, + "(6836, '2024-11-24', 7)": 0, + "(6836, '2024-11-25', 0)": 0, + "(6836, '2024-11-25', 1)": 1, + "(6836, '2024-11-25', 2)": 0, + "(6836, '2024-11-25', 3)": 0, + "(6836, '2024-11-25', 4)": 0, + "(6836, '2024-11-25', 5)": 0, + "(6836, '2024-11-25', 6)": 0, + "(6836, '2024-11-25', 7)": 0, + "(6836, '2024-11-26', 0)": 0, + "(6836, '2024-11-26', 1)": 1, + "(6836, '2024-11-26', 2)": 0, + "(6836, '2024-11-26', 3)": 0, + "(6836, '2024-11-26', 4)": 0, + "(6836, '2024-11-26', 5)": 0, + "(6836, '2024-11-26', 6)": 0, + "(6836, '2024-11-26', 7)": 0, + "(6836, '2024-11-27', 0)": 0, + "(6836, '2024-11-27', 1)": 1, + "(6836, '2024-11-27', 2)": 0, + "(6836, '2024-11-27', 3)": 0, + "(6836, '2024-11-27', 4)": 0, + "(6836, '2024-11-27', 5)": 0, + "(6836, '2024-11-27', 6)": 0, + "(6836, '2024-11-27', 7)": 0, + "(6836, '2024-11-28', 0)": 0, + "(6836, '2024-11-28', 1)": 0, + "(6836, '2024-11-28', 2)": 0, + "(6836, '2024-11-28', 3)": 0, + "(6836, '2024-11-28', 4)": 0, + "(6836, '2024-11-28', 5)": 0, + "(6836, '2024-11-28', 6)": 0, + "(6836, '2024-11-28', 7)": 0, + "(6836, '2024-11-29', 0)": 1, + "(6836, '2024-11-29', 1)": 0, + "(6836, '2024-11-29', 2)": 0, + "(6836, '2024-11-29', 3)": 0, + "(6836, '2024-11-29', 4)": 0, + "(6836, '2024-11-29', 5)": 0, + "(6836, '2024-11-29', 6)": 0, + "(6836, '2024-11-29', 7)": 0, + "(6836, '2024-11-30', 0)": 0, + "(6836, '2024-11-30', 1)": 0, + "(6836, '2024-11-30', 2)": 0, + "(6836, '2024-11-30', 3)": 0, + "(6836, '2024-11-30', 4)": 0, + "(6836, '2024-11-30', 5)": 0, + "(6836, '2024-11-30', 6)": 0, + "(6836, '2024-11-30', 7)": 0, + "(6928, '2024-11-01', 0)": 0, + "(6928, '2024-11-01', 1)": 0, + "(6928, '2024-11-01', 2)": 1, + "(6928, '2024-11-01', 3)": 0, + "(6928, '2024-11-01', 4)": 0, + "(6928, '2024-11-01', 5)": 0, + "(6928, '2024-11-01', 6)": 0, + "(6928, '2024-11-01', 7)": 0, + "(6928, '2024-11-02', 0)": 0, + "(6928, '2024-11-02', 1)": 0, + "(6928, '2024-11-02', 2)": 0, + "(6928, '2024-11-02', 3)": 0, + "(6928, '2024-11-02', 4)": 0, + "(6928, '2024-11-02', 5)": 0, + "(6928, '2024-11-02', 6)": 0, + "(6928, '2024-11-02', 7)": 0, + "(6928, '2024-11-03', 0)": 1, + "(6928, '2024-11-03', 1)": 0, + "(6928, '2024-11-03', 2)": 0, + "(6928, '2024-11-03', 3)": 0, + "(6928, '2024-11-03', 4)": 0, + "(6928, '2024-11-03', 5)": 0, + "(6928, '2024-11-03', 6)": 0, + "(6928, '2024-11-03', 7)": 0, + "(6928, '2024-11-04', 0)": 0, + "(6928, '2024-11-04', 1)": 0, + "(6928, '2024-11-04', 2)": 1, + "(6928, '2024-11-04', 3)": 0, + "(6928, '2024-11-04', 4)": 0, + "(6928, '2024-11-04', 5)": 0, + "(6928, '2024-11-04', 6)": 0, + "(6928, '2024-11-04', 7)": 0, + "(6928, '2024-11-05', 0)": 0, + "(6928, '2024-11-05', 1)": 0, + "(6928, '2024-11-05', 2)": 0, + "(6928, '2024-11-05', 3)": 0, + "(6928, '2024-11-05', 4)": 0, + "(6928, '2024-11-05', 5)": 0, + "(6928, '2024-11-05', 6)": 0, + "(6928, '2024-11-05', 7)": 0, + "(6928, '2024-11-06', 0)": 1, + "(6928, '2024-11-06', 1)": 0, + "(6928, '2024-11-06', 2)": 0, + "(6928, '2024-11-06', 3)": 0, + "(6928, '2024-11-06', 4)": 0, + "(6928, '2024-11-06', 5)": 0, + "(6928, '2024-11-06', 6)": 0, + "(6928, '2024-11-06', 7)": 0, + "(6928, '2024-11-07', 0)": 1, + "(6928, '2024-11-07', 1)": 0, + "(6928, '2024-11-07', 2)": 0, + "(6928, '2024-11-07', 3)": 0, + "(6928, '2024-11-07', 4)": 0, + "(6928, '2024-11-07', 5)": 0, + "(6928, '2024-11-07', 6)": 0, + "(6928, '2024-11-07', 7)": 0, + "(6928, '2024-11-08', 0)": 0, + "(6928, '2024-11-08', 1)": 0, + "(6928, '2024-11-08', 2)": 1, + "(6928, '2024-11-08', 3)": 0, + "(6928, '2024-11-08', 4)": 0, + "(6928, '2024-11-08', 5)": 0, + "(6928, '2024-11-08', 6)": 0, + "(6928, '2024-11-08', 7)": 0, + "(6928, '2024-11-09', 0)": 0, + "(6928, '2024-11-09', 1)": 0, + "(6928, '2024-11-09', 2)": 1, + "(6928, '2024-11-09', 3)": 0, + "(6928, '2024-11-09', 4)": 0, + "(6928, '2024-11-09', 5)": 0, + "(6928, '2024-11-09', 6)": 0, + "(6928, '2024-11-09', 7)": 0, + "(6928, '2024-11-10', 0)": 0, + "(6928, '2024-11-10', 1)": 0, + "(6928, '2024-11-10', 2)": 0, + "(6928, '2024-11-10', 3)": 0, + "(6928, '2024-11-10', 4)": 0, + "(6928, '2024-11-10', 5)": 0, + "(6928, '2024-11-10', 6)": 0, + "(6928, '2024-11-10', 7)": 0, + "(6928, '2024-11-11', 0)": 0, + "(6928, '2024-11-11', 1)": 1, + "(6928, '2024-11-11', 2)": 0, + "(6928, '2024-11-11', 3)": 0, + "(6928, '2024-11-11', 4)": 0, + "(6928, '2024-11-11', 5)": 0, + "(6928, '2024-11-11', 6)": 0, + "(6928, '2024-11-11', 7)": 0, + "(6928, '2024-11-12', 0)": 0, + "(6928, '2024-11-12', 1)": 0, + "(6928, '2024-11-12', 2)": 1, + "(6928, '2024-11-12', 3)": 0, + "(6928, '2024-11-12', 4)": 0, + "(6928, '2024-11-12', 5)": 0, + "(6928, '2024-11-12', 6)": 0, + "(6928, '2024-11-12', 7)": 0, + "(6928, '2024-11-13', 0)": 0, + "(6928, '2024-11-13', 1)": 0, + "(6928, '2024-11-13', 2)": 0, + "(6928, '2024-11-13', 3)": 0, + "(6928, '2024-11-13', 4)": 0, + "(6928, '2024-11-13', 5)": 0, + "(6928, '2024-11-13', 6)": 0, + "(6928, '2024-11-13', 7)": 0, + "(6928, '2024-11-14', 0)": 1, + "(6928, '2024-11-14', 1)": 0, + "(6928, '2024-11-14', 2)": 0, + "(6928, '2024-11-14', 3)": 0, + "(6928, '2024-11-14', 4)": 0, + "(6928, '2024-11-14', 5)": 0, + "(6928, '2024-11-14', 6)": 0, + "(6928, '2024-11-14', 7)": 0, + "(6928, '2024-11-15', 0)": 0, + "(6928, '2024-11-15', 1)": 0, + "(6928, '2024-11-15', 2)": 0, + "(6928, '2024-11-15', 3)": 0, + "(6928, '2024-11-15', 4)": 0, + "(6928, '2024-11-15', 5)": 0, + "(6928, '2024-11-15', 6)": 0, + "(6928, '2024-11-15', 7)": 0, + "(6928, '2024-11-16', 0)": 0, + "(6928, '2024-11-16', 1)": 0, + "(6928, '2024-11-16', 2)": 0, + "(6928, '2024-11-16', 3)": 1, + "(6928, '2024-11-16', 4)": 0, + "(6928, '2024-11-16', 5)": 0, + "(6928, '2024-11-16', 6)": 0, + "(6928, '2024-11-16', 7)": 0, + "(6928, '2024-11-17', 0)": 0, + "(6928, '2024-11-17', 1)": 0, + "(6928, '2024-11-17', 2)": 0, + "(6928, '2024-11-17', 3)": 0, + "(6928, '2024-11-17', 4)": 0, + "(6928, '2024-11-17', 5)": 0, + "(6928, '2024-11-17', 6)": 0, + "(6928, '2024-11-17', 7)": 0, + "(6928, '2024-11-18', 0)": 1, + "(6928, '2024-11-18', 1)": 0, + "(6928, '2024-11-18', 2)": 0, + "(6928, '2024-11-18', 3)": 0, + "(6928, '2024-11-18', 4)": 0, + "(6928, '2024-11-18', 5)": 0, + "(6928, '2024-11-18', 6)": 0, + "(6928, '2024-11-18', 7)": 0, + "(6928, '2024-11-19', 0)": 1, + "(6928, '2024-11-19', 1)": 0, + "(6928, '2024-11-19', 2)": 0, + "(6928, '2024-11-19', 3)": 0, + "(6928, '2024-11-19', 4)": 0, + "(6928, '2024-11-19', 5)": 0, + "(6928, '2024-11-19', 6)": 0, + "(6928, '2024-11-19', 7)": 0, + "(6928, '2024-11-20', 0)": 0, + "(6928, '2024-11-20', 1)": 0, + "(6928, '2024-11-20', 2)": 0, + "(6928, '2024-11-20', 3)": 0, + "(6928, '2024-11-20', 4)": 0, + "(6928, '2024-11-20', 5)": 0, + "(6928, '2024-11-20', 6)": 0, + "(6928, '2024-11-20', 7)": 0, + "(6928, '2024-11-21', 0)": 1, + "(6928, '2024-11-21', 1)": 0, + "(6928, '2024-11-21', 2)": 0, + "(6928, '2024-11-21', 3)": 0, + "(6928, '2024-11-21', 4)": 0, + "(6928, '2024-11-21', 5)": 0, + "(6928, '2024-11-21', 6)": 0, + "(6928, '2024-11-21', 7)": 0, + "(6928, '2024-11-22', 0)": 0, + "(6928, '2024-11-22', 1)": 0, + "(6928, '2024-11-22', 2)": 0, + "(6928, '2024-11-22', 3)": 0, + "(6928, '2024-11-22', 4)": 0, + "(6928, '2024-11-22', 5)": 0, + "(6928, '2024-11-22', 6)": 0, + "(6928, '2024-11-22', 7)": 0, + "(6928, '2024-11-23', 0)": 0, + "(6928, '2024-11-23', 1)": 0, + "(6928, '2024-11-23', 2)": 0, + "(6928, '2024-11-23', 3)": 0, + "(6928, '2024-11-23', 4)": 0, + "(6928, '2024-11-23', 5)": 0, + "(6928, '2024-11-23', 6)": 0, + "(6928, '2024-11-23', 7)": 0, + "(6928, '2024-11-24', 0)": 0, + "(6928, '2024-11-24', 1)": 0, + "(6928, '2024-11-24', 2)": 0, + "(6928, '2024-11-24', 3)": 0, + "(6928, '2024-11-24', 4)": 0, + "(6928, '2024-11-24', 5)": 0, + "(6928, '2024-11-24', 6)": 0, + "(6928, '2024-11-24', 7)": 0, + "(6928, '2024-11-25', 0)": 0, + "(6928, '2024-11-25', 1)": 0, + "(6928, '2024-11-25', 2)": 1, + "(6928, '2024-11-25', 3)": 0, + "(6928, '2024-11-25', 4)": 0, + "(6928, '2024-11-25', 5)": 0, + "(6928, '2024-11-25', 6)": 0, + "(6928, '2024-11-25', 7)": 0, + "(6928, '2024-11-26', 0)": 0, + "(6928, '2024-11-26', 1)": 1, + "(6928, '2024-11-26', 2)": 0, + "(6928, '2024-11-26', 3)": 0, + "(6928, '2024-11-26', 4)": 0, + "(6928, '2024-11-26', 5)": 0, + "(6928, '2024-11-26', 6)": 0, + "(6928, '2024-11-26', 7)": 0, + "(6928, '2024-11-27', 0)": 0, + "(6928, '2024-11-27', 1)": 0, + "(6928, '2024-11-27', 2)": 1, + "(6928, '2024-11-27', 3)": 0, + "(6928, '2024-11-27', 4)": 0, + "(6928, '2024-11-27', 5)": 0, + "(6928, '2024-11-27', 6)": 0, + "(6928, '2024-11-27', 7)": 0, + "(6928, '2024-11-28', 0)": 0, + "(6928, '2024-11-28', 1)": 1, + "(6928, '2024-11-28', 2)": 0, + "(6928, '2024-11-28', 3)": 0, + "(6928, '2024-11-28', 4)": 0, + "(6928, '2024-11-28', 5)": 0, + "(6928, '2024-11-28', 6)": 0, + "(6928, '2024-11-28', 7)": 0, + "(6928, '2024-11-29', 0)": 1, + "(6928, '2024-11-29', 1)": 0, + "(6928, '2024-11-29', 2)": 0, + "(6928, '2024-11-29', 3)": 0, + "(6928, '2024-11-29', 4)": 0, + "(6928, '2024-11-29', 5)": 0, + "(6928, '2024-11-29', 6)": 0, + "(6928, '2024-11-29', 7)": 0, + "(6928, '2024-11-30', 0)": 0, + "(6928, '2024-11-30', 1)": 0, + "(6928, '2024-11-30', 2)": 0, + "(6928, '2024-11-30', 3)": 1, + "(6928, '2024-11-30', 4)": 0, + "(6928, '2024-11-30', 5)": 0, + "(6928, '2024-11-30', 6)": 0, + "(6928, '2024-11-30', 7)": 0, + "(7496, '2024-11-01', 0)": 0, + "(7496, '2024-11-01', 1)": 0, + "(7496, '2024-11-01', 2)": 0, + "(7496, '2024-11-01', 3)": 0, + "(7496, '2024-11-01', 4)": 0, + "(7496, '2024-11-01', 5)": 0, + "(7496, '2024-11-01', 6)": 0, + "(7496, '2024-11-01', 7)": 0, + "(7496, '2024-11-02', 0)": 0, + "(7496, '2024-11-02', 1)": 0, + "(7496, '2024-11-02', 2)": 0, + "(7496, '2024-11-02', 3)": 0, + "(7496, '2024-11-02', 4)": 0, + "(7496, '2024-11-02', 5)": 0, + "(7496, '2024-11-02', 6)": 0, + "(7496, '2024-11-02', 7)": 0, + "(7496, '2024-11-03', 0)": 0, + "(7496, '2024-11-03', 1)": 0, + "(7496, '2024-11-03', 2)": 0, + "(7496, '2024-11-03', 3)": 0, + "(7496, '2024-11-03', 4)": 0, + "(7496, '2024-11-03', 5)": 0, + "(7496, '2024-11-03', 6)": 0, + "(7496, '2024-11-03', 7)": 0, + "(7496, '2024-11-04', 0)": 0, + "(7496, '2024-11-04', 1)": 0, + "(7496, '2024-11-04', 2)": 0, + "(7496, '2024-11-04', 3)": 0, + "(7496, '2024-11-04', 4)": 0, + "(7496, '2024-11-04', 5)": 0, + "(7496, '2024-11-04', 6)": 0, + "(7496, '2024-11-04', 7)": 0, + "(7496, '2024-11-05', 0)": 0, + "(7496, '2024-11-05', 1)": 0, + "(7496, '2024-11-05', 2)": 0, + "(7496, '2024-11-05', 3)": 0, + "(7496, '2024-11-05', 4)": 0, + "(7496, '2024-11-05', 5)": 0, + "(7496, '2024-11-05', 6)": 0, + "(7496, '2024-11-05', 7)": 0, + "(7496, '2024-11-06', 0)": 0, + "(7496, '2024-11-06', 1)": 0, + "(7496, '2024-11-06', 2)": 0, + "(7496, '2024-11-06', 3)": 0, + "(7496, '2024-11-06', 4)": 0, + "(7496, '2024-11-06', 5)": 0, + "(7496, '2024-11-06', 6)": 0, + "(7496, '2024-11-06', 7)": 0, + "(7496, '2024-11-07', 0)": 0, + "(7496, '2024-11-07', 1)": 0, + "(7496, '2024-11-07', 2)": 0, + "(7496, '2024-11-07', 3)": 0, + "(7496, '2024-11-07', 4)": 0, + "(7496, '2024-11-07', 5)": 0, + "(7496, '2024-11-07', 6)": 0, + "(7496, '2024-11-07', 7)": 0, + "(7496, '2024-11-08', 0)": 0, + "(7496, '2024-11-08', 1)": 0, + "(7496, '2024-11-08', 2)": 0, + "(7496, '2024-11-08', 3)": 0, + "(7496, '2024-11-08', 4)": 0, + "(7496, '2024-11-08', 5)": 0, + "(7496, '2024-11-08', 6)": 0, + "(7496, '2024-11-08', 7)": 0, + "(7496, '2024-11-09', 0)": 0, + "(7496, '2024-11-09', 1)": 0, + "(7496, '2024-11-09', 2)": 0, + "(7496, '2024-11-09', 3)": 0, + "(7496, '2024-11-09', 4)": 0, + "(7496, '2024-11-09', 5)": 0, + "(7496, '2024-11-09', 6)": 0, + "(7496, '2024-11-09', 7)": 0, + "(7496, '2024-11-10', 0)": 0, + "(7496, '2024-11-10', 1)": 0, + "(7496, '2024-11-10', 2)": 0, + "(7496, '2024-11-10', 3)": 0, + "(7496, '2024-11-10', 4)": 0, + "(7496, '2024-11-10', 5)": 0, + "(7496, '2024-11-10', 6)": 0, + "(7496, '2024-11-10', 7)": 0, + "(7496, '2024-11-11', 0)": 0, + "(7496, '2024-11-11', 1)": 0, + "(7496, '2024-11-11', 2)": 0, + "(7496, '2024-11-11', 3)": 0, + "(7496, '2024-11-11', 4)": 0, + "(7496, '2024-11-11', 5)": 0, + "(7496, '2024-11-11', 6)": 0, + "(7496, '2024-11-11', 7)": 0, + "(7496, '2024-11-12', 0)": 0, + "(7496, '2024-11-12', 1)": 0, + "(7496, '2024-11-12', 2)": 0, + "(7496, '2024-11-12', 3)": 0, + "(7496, '2024-11-12', 4)": 0, + "(7496, '2024-11-12', 5)": 0, + "(7496, '2024-11-12', 6)": 0, + "(7496, '2024-11-12', 7)": 0, + "(7496, '2024-11-13', 0)": 0, + "(7496, '2024-11-13', 1)": 0, + "(7496, '2024-11-13', 2)": 0, + "(7496, '2024-11-13', 3)": 0, + "(7496, '2024-11-13', 4)": 0, + "(7496, '2024-11-13', 5)": 0, + "(7496, '2024-11-13', 6)": 0, + "(7496, '2024-11-13', 7)": 0, + "(7496, '2024-11-14', 0)": 0, + "(7496, '2024-11-14', 1)": 0, + "(7496, '2024-11-14', 2)": 0, + "(7496, '2024-11-14', 3)": 0, + "(7496, '2024-11-14', 4)": 0, + "(7496, '2024-11-14', 5)": 0, + "(7496, '2024-11-14', 6)": 0, + "(7496, '2024-11-14', 7)": 0, + "(7496, '2024-11-15', 0)": 0, + "(7496, '2024-11-15', 1)": 0, + "(7496, '2024-11-15', 2)": 0, + "(7496, '2024-11-15', 3)": 0, + "(7496, '2024-11-15', 4)": 0, + "(7496, '2024-11-15', 5)": 0, + "(7496, '2024-11-15', 6)": 1, + "(7496, '2024-11-15', 7)": 0, + "(7496, '2024-11-16', 0)": 0, + "(7496, '2024-11-16', 1)": 0, + "(7496, '2024-11-16', 2)": 0, + "(7496, '2024-11-16', 3)": 0, + "(7496, '2024-11-16', 4)": 0, + "(7496, '2024-11-16', 5)": 0, + "(7496, '2024-11-16', 6)": 0, + "(7496, '2024-11-16', 7)": 0, + "(7496, '2024-11-17', 0)": 0, + "(7496, '2024-11-17', 1)": 0, + "(7496, '2024-11-17', 2)": 0, + "(7496, '2024-11-17', 3)": 0, + "(7496, '2024-11-17', 4)": 0, + "(7496, '2024-11-17', 5)": 1, + "(7496, '2024-11-17', 6)": 0, + "(7496, '2024-11-17', 7)": 0, + "(7496, '2024-11-18', 0)": 0, + "(7496, '2024-11-18', 1)": 0, + "(7496, '2024-11-18', 2)": 0, + "(7496, '2024-11-18', 3)": 0, + "(7496, '2024-11-18', 4)": 0, + "(7496, '2024-11-18', 5)": 1, + "(7496, '2024-11-18', 6)": 0, + "(7496, '2024-11-18', 7)": 0, + "(7496, '2024-11-19', 0)": 0, + "(7496, '2024-11-19', 1)": 0, + "(7496, '2024-11-19', 2)": 0, + "(7496, '2024-11-19', 3)": 0, + "(7496, '2024-11-19', 4)": 0, + "(7496, '2024-11-19', 5)": 0, + "(7496, '2024-11-19', 6)": 0, + "(7496, '2024-11-19', 7)": 1, + "(7496, '2024-11-20', 0)": 0, + "(7496, '2024-11-20', 1)": 0, + "(7496, '2024-11-20', 2)": 0, + "(7496, '2024-11-20', 3)": 0, + "(7496, '2024-11-20', 4)": 0, + "(7496, '2024-11-20', 5)": 0, + "(7496, '2024-11-20', 6)": 0, + "(7496, '2024-11-20', 7)": 1, + "(7496, '2024-11-21', 0)": 0, + "(7496, '2024-11-21', 1)": 0, + "(7496, '2024-11-21', 2)": 0, + "(7496, '2024-11-21', 3)": 0, + "(7496, '2024-11-21', 4)": 0, + "(7496, '2024-11-21', 5)": 0, + "(7496, '2024-11-21', 6)": 0, + "(7496, '2024-11-21', 7)": 0, + "(7496, '2024-11-22', 0)": 0, + "(7496, '2024-11-22', 1)": 0, + "(7496, '2024-11-22', 2)": 0, + "(7496, '2024-11-22', 3)": 0, + "(7496, '2024-11-22', 4)": 0, + "(7496, '2024-11-22', 5)": 0, + "(7496, '2024-11-22', 6)": 0, + "(7496, '2024-11-22', 7)": 0, + "(7496, '2024-11-23', 0)": 0, + "(7496, '2024-11-23', 1)": 0, + "(7496, '2024-11-23', 2)": 0, + "(7496, '2024-11-23', 3)": 0, + "(7496, '2024-11-23', 4)": 0, + "(7496, '2024-11-23', 5)": 0, + "(7496, '2024-11-23', 6)": 0, + "(7496, '2024-11-23', 7)": 0, + "(7496, '2024-11-24', 0)": 0, + "(7496, '2024-11-24', 1)": 0, + "(7496, '2024-11-24', 2)": 0, + "(7496, '2024-11-24', 3)": 0, + "(7496, '2024-11-24', 4)": 0, + "(7496, '2024-11-24', 5)": 0, + "(7496, '2024-11-24', 6)": 0, + "(7496, '2024-11-24', 7)": 0, + "(7496, '2024-11-25', 0)": 0, + "(7496, '2024-11-25', 1)": 0, + "(7496, '2024-11-25', 2)": 0, + "(7496, '2024-11-25', 3)": 0, + "(7496, '2024-11-25', 4)": 0, + "(7496, '2024-11-25', 5)": 1, + "(7496, '2024-11-25', 6)": 0, + "(7496, '2024-11-25', 7)": 0, + "(7496, '2024-11-26', 0)": 0, + "(7496, '2024-11-26', 1)": 0, + "(7496, '2024-11-26', 2)": 0, + "(7496, '2024-11-26', 3)": 0, + "(7496, '2024-11-26', 4)": 0, + "(7496, '2024-11-26', 5)": 1, + "(7496, '2024-11-26', 6)": 0, + "(7496, '2024-11-26', 7)": 0, + "(7496, '2024-11-27', 0)": 0, + "(7496, '2024-11-27', 1)": 0, + "(7496, '2024-11-27', 2)": 0, + "(7496, '2024-11-27', 3)": 0, + "(7496, '2024-11-27', 4)": 0, + "(7496, '2024-11-27', 5)": 1, + "(7496, '2024-11-27', 6)": 0, + "(7496, '2024-11-27', 7)": 0, + "(7496, '2024-11-28', 0)": 0, + "(7496, '2024-11-28', 1)": 0, + "(7496, '2024-11-28', 2)": 0, + "(7496, '2024-11-28', 3)": 0, + "(7496, '2024-11-28', 4)": 0, + "(7496, '2024-11-28', 5)": 1, + "(7496, '2024-11-28', 6)": 0, + "(7496, '2024-11-28', 7)": 0, + "(7496, '2024-11-29', 0)": 0, + "(7496, '2024-11-29', 1)": 0, + "(7496, '2024-11-29', 2)": 0, + "(7496, '2024-11-29', 3)": 0, + "(7496, '2024-11-29', 4)": 0, + "(7496, '2024-11-29', 5)": 1, + "(7496, '2024-11-29', 6)": 0, + "(7496, '2024-11-29', 7)": 0, + "(7496, '2024-11-30', 0)": 0, + "(7496, '2024-11-30', 1)": 0, + "(7496, '2024-11-30', 2)": 0, + "(7496, '2024-11-30', 3)": 0, + "(7496, '2024-11-30', 4)": 0, + "(7496, '2024-11-30', 5)": 0, + "(7496, '2024-11-30', 6)": 0, + "(7496, '2024-11-30', 7)": 0, + "(7603, '2024-11-01', 0)": 0, + "(7603, '2024-11-01', 1)": 1, + "(7603, '2024-11-01', 2)": 0, + "(7603, '2024-11-01', 3)": 0, + "(7603, '2024-11-01', 4)": 0, + "(7603, '2024-11-01', 5)": 0, + "(7603, '2024-11-01', 6)": 0, + "(7603, '2024-11-01', 7)": 0, + "(7603, '2024-11-02', 0)": 0, + "(7603, '2024-11-02', 1)": 0, + "(7603, '2024-11-02', 2)": 1, + "(7603, '2024-11-02', 3)": 0, + "(7603, '2024-11-02', 4)": 0, + "(7603, '2024-11-02', 5)": 0, + "(7603, '2024-11-02', 6)": 0, + "(7603, '2024-11-02', 7)": 0, + "(7603, '2024-11-03', 0)": 0, + "(7603, '2024-11-03', 1)": 1, + "(7603, '2024-11-03', 2)": 0, + "(7603, '2024-11-03', 3)": 0, + "(7603, '2024-11-03', 4)": 0, + "(7603, '2024-11-03', 5)": 0, + "(7603, '2024-11-03', 6)": 0, + "(7603, '2024-11-03', 7)": 0, + "(7603, '2024-11-04', 0)": 0, + "(7603, '2024-11-04', 1)": 1, + "(7603, '2024-11-04', 2)": 0, + "(7603, '2024-11-04', 3)": 0, + "(7603, '2024-11-04', 4)": 0, + "(7603, '2024-11-04', 5)": 0, + "(7603, '2024-11-04', 6)": 0, + "(7603, '2024-11-04', 7)": 0, + "(7603, '2024-11-05', 0)": 1, + "(7603, '2024-11-05', 1)": 0, + "(7603, '2024-11-05', 2)": 0, + "(7603, '2024-11-05', 3)": 0, + "(7603, '2024-11-05', 4)": 0, + "(7603, '2024-11-05', 5)": 0, + "(7603, '2024-11-05', 6)": 0, + "(7603, '2024-11-05', 7)": 0, + "(7603, '2024-11-06', 0)": 1, + "(7603, '2024-11-06', 1)": 0, + "(7603, '2024-11-06', 2)": 0, + "(7603, '2024-11-06', 3)": 0, + "(7603, '2024-11-06', 4)": 0, + "(7603, '2024-11-06', 5)": 0, + "(7603, '2024-11-06', 6)": 0, + "(7603, '2024-11-06', 7)": 0, + "(7603, '2024-11-07', 0)": 0, + "(7603, '2024-11-07', 1)": 0, + "(7603, '2024-11-07', 2)": 1, + "(7603, '2024-11-07', 3)": 0, + "(7603, '2024-11-07', 4)": 0, + "(7603, '2024-11-07', 5)": 0, + "(7603, '2024-11-07', 6)": 0, + "(7603, '2024-11-07', 7)": 0, + "(7603, '2024-11-08', 0)": 0, + "(7603, '2024-11-08', 1)": 0, + "(7603, '2024-11-08', 2)": 0, + "(7603, '2024-11-08', 3)": 0, + "(7603, '2024-11-08', 4)": 0, + "(7603, '2024-11-08', 5)": 0, + "(7603, '2024-11-08', 6)": 0, + "(7603, '2024-11-08', 7)": 0, + "(7603, '2024-11-09', 0)": 1, + "(7603, '2024-11-09', 1)": 0, + "(7603, '2024-11-09', 2)": 0, + "(7603, '2024-11-09', 3)": 0, + "(7603, '2024-11-09', 4)": 0, + "(7603, '2024-11-09', 5)": 0, + "(7603, '2024-11-09', 6)": 0, + "(7603, '2024-11-09', 7)": 0, + "(7603, '2024-11-10', 0)": 0, + "(7603, '2024-11-10', 1)": 0, + "(7603, '2024-11-10', 2)": 0, + "(7603, '2024-11-10', 3)": 0, + "(7603, '2024-11-10', 4)": 0, + "(7603, '2024-11-10', 5)": 0, + "(7603, '2024-11-10', 6)": 0, + "(7603, '2024-11-10', 7)": 0, + "(7603, '2024-11-11', 0)": 0, + "(7603, '2024-11-11', 1)": 0, + "(7603, '2024-11-11', 2)": 0, + "(7603, '2024-11-11', 3)": 0, + "(7603, '2024-11-11', 4)": 0, + "(7603, '2024-11-11', 5)": 0, + "(7603, '2024-11-11', 6)": 0, + "(7603, '2024-11-11', 7)": 0, + "(7603, '2024-11-12', 0)": 0, + "(7603, '2024-11-12', 1)": 0, + "(7603, '2024-11-12', 2)": 1, + "(7603, '2024-11-12', 3)": 0, + "(7603, '2024-11-12', 4)": 0, + "(7603, '2024-11-12', 5)": 0, + "(7603, '2024-11-12', 6)": 0, + "(7603, '2024-11-12', 7)": 0, + "(7603, '2024-11-13', 0)": 0, + "(7603, '2024-11-13', 1)": 0, + "(7603, '2024-11-13', 2)": 0, + "(7603, '2024-11-13', 3)": 0, + "(7603, '2024-11-13', 4)": 0, + "(7603, '2024-11-13', 5)": 0, + "(7603, '2024-11-13', 6)": 0, + "(7603, '2024-11-13', 7)": 0, + "(7603, '2024-11-14', 0)": 0, + "(7603, '2024-11-14', 1)": 0, + "(7603, '2024-11-14', 2)": 1, + "(7603, '2024-11-14', 3)": 0, + "(7603, '2024-11-14', 4)": 0, + "(7603, '2024-11-14', 5)": 0, + "(7603, '2024-11-14', 6)": 0, + "(7603, '2024-11-14', 7)": 0, + "(7603, '2024-11-15', 0)": 0, + "(7603, '2024-11-15', 1)": 0, + "(7603, '2024-11-15', 2)": 0, + "(7603, '2024-11-15', 3)": 0, + "(7603, '2024-11-15', 4)": 0, + "(7603, '2024-11-15', 5)": 0, + "(7603, '2024-11-15', 6)": 0, + "(7603, '2024-11-15', 7)": 0, + "(7603, '2024-11-16', 0)": 0, + "(7603, '2024-11-16', 1)": 0, + "(7603, '2024-11-16', 2)": 1, + "(7603, '2024-11-16', 3)": 0, + "(7603, '2024-11-16', 4)": 0, + "(7603, '2024-11-16', 5)": 0, + "(7603, '2024-11-16', 6)": 0, + "(7603, '2024-11-16', 7)": 0, + "(7603, '2024-11-17', 0)": 0, + "(7603, '2024-11-17', 1)": 0, + "(7603, '2024-11-17', 2)": 1, + "(7603, '2024-11-17', 3)": 0, + "(7603, '2024-11-17', 4)": 0, + "(7603, '2024-11-17', 5)": 0, + "(7603, '2024-11-17', 6)": 0, + "(7603, '2024-11-17', 7)": 0, + "(7603, '2024-11-18', 0)": 0, + "(7603, '2024-11-18', 1)": 0, + "(7603, '2024-11-18', 2)": 1, + "(7603, '2024-11-18', 3)": 0, + "(7603, '2024-11-18', 4)": 0, + "(7603, '2024-11-18', 5)": 0, + "(7603, '2024-11-18', 6)": 0, + "(7603, '2024-11-18', 7)": 0, + "(7603, '2024-11-19', 0)": 0, + "(7603, '2024-11-19', 1)": 0, + "(7603, '2024-11-19', 2)": 0, + "(7603, '2024-11-19', 3)": 0, + "(7603, '2024-11-19', 4)": 0, + "(7603, '2024-11-19', 5)": 0, + "(7603, '2024-11-19', 6)": 0, + "(7603, '2024-11-19', 7)": 0, + "(7603, '2024-11-20', 0)": 1, + "(7603, '2024-11-20', 1)": 0, + "(7603, '2024-11-20', 2)": 0, + "(7603, '2024-11-20', 3)": 0, + "(7603, '2024-11-20', 4)": 0, + "(7603, '2024-11-20', 5)": 0, + "(7603, '2024-11-20', 6)": 0, + "(7603, '2024-11-20', 7)": 0, + "(7603, '2024-11-21', 0)": 0, + "(7603, '2024-11-21', 1)": 0, + "(7603, '2024-11-21', 2)": 0, + "(7603, '2024-11-21', 3)": 0, + "(7603, '2024-11-21', 4)": 0, + "(7603, '2024-11-21', 5)": 0, + "(7603, '2024-11-21', 6)": 0, + "(7603, '2024-11-21', 7)": 0, + "(7603, '2024-11-22', 0)": 0, + "(7603, '2024-11-22', 1)": 0, + "(7603, '2024-11-22', 2)": 0, + "(7603, '2024-11-22', 3)": 0, + "(7603, '2024-11-22', 4)": 0, + "(7603, '2024-11-22', 5)": 0, + "(7603, '2024-11-22', 6)": 0, + "(7603, '2024-11-22', 7)": 0, + "(7603, '2024-11-23', 0)": 1, + "(7603, '2024-11-23', 1)": 0, + "(7603, '2024-11-23', 2)": 0, + "(7603, '2024-11-23', 3)": 0, + "(7603, '2024-11-23', 4)": 0, + "(7603, '2024-11-23', 5)": 0, + "(7603, '2024-11-23', 6)": 0, + "(7603, '2024-11-23', 7)": 0, + "(7603, '2024-11-24', 0)": 1, + "(7603, '2024-11-24', 1)": 0, + "(7603, '2024-11-24', 2)": 0, + "(7603, '2024-11-24', 3)": 0, + "(7603, '2024-11-24', 4)": 0, + "(7603, '2024-11-24', 5)": 0, + "(7603, '2024-11-24', 6)": 0, + "(7603, '2024-11-24', 7)": 0, + "(7603, '2024-11-25', 0)": 0, + "(7603, '2024-11-25', 1)": 1, + "(7603, '2024-11-25', 2)": 0, + "(7603, '2024-11-25', 3)": 0, + "(7603, '2024-11-25', 4)": 0, + "(7603, '2024-11-25', 5)": 0, + "(7603, '2024-11-25', 6)": 0, + "(7603, '2024-11-25', 7)": 0, + "(7603, '2024-11-26', 0)": 1, + "(7603, '2024-11-26', 1)": 0, + "(7603, '2024-11-26', 2)": 0, + "(7603, '2024-11-26', 3)": 0, + "(7603, '2024-11-26', 4)": 0, + "(7603, '2024-11-26', 5)": 0, + "(7603, '2024-11-26', 6)": 0, + "(7603, '2024-11-26', 7)": 0, + "(7603, '2024-11-27', 0)": 0, + "(7603, '2024-11-27', 1)": 0, + "(7603, '2024-11-27', 2)": 1, + "(7603, '2024-11-27', 3)": 0, + "(7603, '2024-11-27', 4)": 0, + "(7603, '2024-11-27', 5)": 0, + "(7603, '2024-11-27', 6)": 0, + "(7603, '2024-11-27', 7)": 0, + "(7603, '2024-11-28', 0)": 0, + "(7603, '2024-11-28', 1)": 0, + "(7603, '2024-11-28', 2)": 1, + "(7603, '2024-11-28', 3)": 0, + "(7603, '2024-11-28', 4)": 0, + "(7603, '2024-11-28', 5)": 0, + "(7603, '2024-11-28', 6)": 0, + "(7603, '2024-11-28', 7)": 0, + "(7603, '2024-11-29', 0)": 0, + "(7603, '2024-11-29', 1)": 0, + "(7603, '2024-11-29', 2)": 0, + "(7603, '2024-11-29', 3)": 0, + "(7603, '2024-11-29', 4)": 0, + "(7603, '2024-11-29', 5)": 0, + "(7603, '2024-11-29', 6)": 0, + "(7603, '2024-11-29', 7)": 0, + "(7603, '2024-11-30', 0)": 0, + "(7603, '2024-11-30', 1)": 0, + "(7603, '2024-11-30', 2)": 1, + "(7603, '2024-11-30', 3)": 0, + "(7603, '2024-11-30', 4)": 0, + "(7603, '2024-11-30', 5)": 0, + "(7603, '2024-11-30', 6)": 0, + "(7603, '2024-11-30', 7)": 0, + "(7741, '2024-11-01', 0)": 0, + "(7741, '2024-11-01', 1)": 0, + "(7741, '2024-11-01', 2)": 0, + "(7741, '2024-11-01', 3)": 0, + "(7741, '2024-11-01', 4)": 0, + "(7741, '2024-11-01', 5)": 0, + "(7741, '2024-11-01', 6)": 0, + "(7741, '2024-11-01', 7)": 0, + "(7741, '2024-11-02', 0)": 0, + "(7741, '2024-11-02', 1)": 0, + "(7741, '2024-11-02', 2)": 0, + "(7741, '2024-11-02', 3)": 0, + "(7741, '2024-11-02', 4)": 0, + "(7741, '2024-11-02', 5)": 0, + "(7741, '2024-11-02', 6)": 0, + "(7741, '2024-11-02', 7)": 0, + "(7741, '2024-11-03', 0)": 0, + "(7741, '2024-11-03', 1)": 0, + "(7741, '2024-11-03', 2)": 0, + "(7741, '2024-11-03', 3)": 0, + "(7741, '2024-11-03', 4)": 0, + "(7741, '2024-11-03', 5)": 0, + "(7741, '2024-11-03', 6)": 0, + "(7741, '2024-11-03', 7)": 0, + "(7741, '2024-11-04', 0)": 0, + "(7741, '2024-11-04', 1)": 0, + "(7741, '2024-11-04', 2)": 0, + "(7741, '2024-11-04', 3)": 0, + "(7741, '2024-11-04', 4)": 0, + "(7741, '2024-11-04', 5)": 0, + "(7741, '2024-11-04', 6)": 0, + "(7741, '2024-11-04', 7)": 0, + "(7741, '2024-11-05', 0)": 0, + "(7741, '2024-11-05', 1)": 0, + "(7741, '2024-11-05', 2)": 0, + "(7741, '2024-11-05', 3)": 0, + "(7741, '2024-11-05', 4)": 0, + "(7741, '2024-11-05', 5)": 0, + "(7741, '2024-11-05', 6)": 0, + "(7741, '2024-11-05', 7)": 0, + "(7741, '2024-11-06', 0)": 0, + "(7741, '2024-11-06', 1)": 0, + "(7741, '2024-11-06', 2)": 0, + "(7741, '2024-11-06', 3)": 0, + "(7741, '2024-11-06', 4)": 0, + "(7741, '2024-11-06', 5)": 0, + "(7741, '2024-11-06', 6)": 0, + "(7741, '2024-11-06', 7)": 0, + "(7741, '2024-11-07', 0)": 0, + "(7741, '2024-11-07', 1)": 0, + "(7741, '2024-11-07', 2)": 0, + "(7741, '2024-11-07', 3)": 0, + "(7741, '2024-11-07', 4)": 0, + "(7741, '2024-11-07', 5)": 0, + "(7741, '2024-11-07', 6)": 0, + "(7741, '2024-11-07', 7)": 0, + "(7741, '2024-11-08', 0)": 0, + "(7741, '2024-11-08', 1)": 0, + "(7741, '2024-11-08', 2)": 0, + "(7741, '2024-11-08', 3)": 0, + "(7741, '2024-11-08', 4)": 0, + "(7741, '2024-11-08', 5)": 0, + "(7741, '2024-11-08', 6)": 0, + "(7741, '2024-11-08', 7)": 0, + "(7741, '2024-11-09', 0)": 0, + "(7741, '2024-11-09', 1)": 0, + "(7741, '2024-11-09', 2)": 0, + "(7741, '2024-11-09', 3)": 0, + "(7741, '2024-11-09', 4)": 0, + "(7741, '2024-11-09', 5)": 0, + "(7741, '2024-11-09', 6)": 0, + "(7741, '2024-11-09', 7)": 0, + "(7741, '2024-11-10', 0)": 0, + "(7741, '2024-11-10', 1)": 0, + "(7741, '2024-11-10', 2)": 0, + "(7741, '2024-11-10', 3)": 0, + "(7741, '2024-11-10', 4)": 0, + "(7741, '2024-11-10', 5)": 0, + "(7741, '2024-11-10', 6)": 0, + "(7741, '2024-11-10', 7)": 0, + "(7741, '2024-11-11', 0)": 0, + "(7741, '2024-11-11', 1)": 0, + "(7741, '2024-11-11', 2)": 0, + "(7741, '2024-11-11', 3)": 0, + "(7741, '2024-11-11', 4)": 0, + "(7741, '2024-11-11', 5)": 0, + "(7741, '2024-11-11', 6)": 0, + "(7741, '2024-11-11', 7)": 0, + "(7741, '2024-11-12', 0)": 0, + "(7741, '2024-11-12', 1)": 0, + "(7741, '2024-11-12', 2)": 0, + "(7741, '2024-11-12', 3)": 0, + "(7741, '2024-11-12', 4)": 0, + "(7741, '2024-11-12', 5)": 0, + "(7741, '2024-11-12', 6)": 0, + "(7741, '2024-11-12', 7)": 0, + "(7741, '2024-11-13', 0)": 0, + "(7741, '2024-11-13', 1)": 0, + "(7741, '2024-11-13', 2)": 0, + "(7741, '2024-11-13', 3)": 0, + "(7741, '2024-11-13', 4)": 0, + "(7741, '2024-11-13', 5)": 0, + "(7741, '2024-11-13', 6)": 0, + "(7741, '2024-11-13', 7)": 0, + "(7741, '2024-11-14', 0)": 0, + "(7741, '2024-11-14', 1)": 0, + "(7741, '2024-11-14', 2)": 0, + "(7741, '2024-11-14', 3)": 0, + "(7741, '2024-11-14', 4)": 0, + "(7741, '2024-11-14', 5)": 0, + "(7741, '2024-11-14', 6)": 0, + "(7741, '2024-11-14', 7)": 0, + "(7741, '2024-11-15', 0)": 0, + "(7741, '2024-11-15', 1)": 0, + "(7741, '2024-11-15', 2)": 0, + "(7741, '2024-11-15', 3)": 0, + "(7741, '2024-11-15', 4)": 0, + "(7741, '2024-11-15', 5)": 0, + "(7741, '2024-11-15', 6)": 0, + "(7741, '2024-11-15', 7)": 0, + "(7741, '2024-11-16', 0)": 0, + "(7741, '2024-11-16', 1)": 0, + "(7741, '2024-11-16', 2)": 0, + "(7741, '2024-11-16', 3)": 0, + "(7741, '2024-11-16', 4)": 0, + "(7741, '2024-11-16', 5)": 0, + "(7741, '2024-11-16', 6)": 0, + "(7741, '2024-11-16', 7)": 0, + "(7741, '2024-11-17', 0)": 0, + "(7741, '2024-11-17', 1)": 0, + "(7741, '2024-11-17', 2)": 0, + "(7741, '2024-11-17', 3)": 0, + "(7741, '2024-11-17', 4)": 0, + "(7741, '2024-11-17', 5)": 0, + "(7741, '2024-11-17', 6)": 0, + "(7741, '2024-11-17', 7)": 0, + "(7741, '2024-11-18', 0)": 0, + "(7741, '2024-11-18', 1)": 0, + "(7741, '2024-11-18', 2)": 0, + "(7741, '2024-11-18', 3)": 0, + "(7741, '2024-11-18', 4)": 0, + "(7741, '2024-11-18', 5)": 0, + "(7741, '2024-11-18', 6)": 0, + "(7741, '2024-11-18', 7)": 0, + "(7741, '2024-11-19', 0)": 0, + "(7741, '2024-11-19', 1)": 0, + "(7741, '2024-11-19', 2)": 0, + "(7741, '2024-11-19', 3)": 0, + "(7741, '2024-11-19', 4)": 0, + "(7741, '2024-11-19', 5)": 0, + "(7741, '2024-11-19', 6)": 0, + "(7741, '2024-11-19', 7)": 0, + "(7741, '2024-11-20', 0)": 0, + "(7741, '2024-11-20', 1)": 0, + "(7741, '2024-11-20', 2)": 0, + "(7741, '2024-11-20', 3)": 0, + "(7741, '2024-11-20', 4)": 0, + "(7741, '2024-11-20', 5)": 0, + "(7741, '2024-11-20', 6)": 0, + "(7741, '2024-11-20', 7)": 0, + "(7741, '2024-11-21', 0)": 0, + "(7741, '2024-11-21', 1)": 0, + "(7741, '2024-11-21', 2)": 0, + "(7741, '2024-11-21', 3)": 0, + "(7741, '2024-11-21', 4)": 0, + "(7741, '2024-11-21', 5)": 0, + "(7741, '2024-11-21', 6)": 0, + "(7741, '2024-11-21', 7)": 0, + "(7741, '2024-11-22', 0)": 0, + "(7741, '2024-11-22', 1)": 0, + "(7741, '2024-11-22', 2)": 0, + "(7741, '2024-11-22', 3)": 0, + "(7741, '2024-11-22', 4)": 0, + "(7741, '2024-11-22', 5)": 0, + "(7741, '2024-11-22', 6)": 0, + "(7741, '2024-11-22', 7)": 0, + "(7741, '2024-11-23', 0)": 0, + "(7741, '2024-11-23', 1)": 0, + "(7741, '2024-11-23', 2)": 0, + "(7741, '2024-11-23', 3)": 0, + "(7741, '2024-11-23', 4)": 0, + "(7741, '2024-11-23', 5)": 0, + "(7741, '2024-11-23', 6)": 0, + "(7741, '2024-11-23', 7)": 0, + "(7741, '2024-11-24', 0)": 0, + "(7741, '2024-11-24', 1)": 0, + "(7741, '2024-11-24', 2)": 0, + "(7741, '2024-11-24', 3)": 0, + "(7741, '2024-11-24', 4)": 0, + "(7741, '2024-11-24', 5)": 0, + "(7741, '2024-11-24', 6)": 0, + "(7741, '2024-11-24', 7)": 0, + "(7741, '2024-11-25', 0)": 0, + "(7741, '2024-11-25', 1)": 0, + "(7741, '2024-11-25', 2)": 0, + "(7741, '2024-11-25', 3)": 0, + "(7741, '2024-11-25', 4)": 0, + "(7741, '2024-11-25', 5)": 0, + "(7741, '2024-11-25', 6)": 0, + "(7741, '2024-11-25', 7)": 0, + "(7741, '2024-11-26', 0)": 0, + "(7741, '2024-11-26', 1)": 0, + "(7741, '2024-11-26', 2)": 0, + "(7741, '2024-11-26', 3)": 0, + "(7741, '2024-11-26', 4)": 0, + "(7741, '2024-11-26', 5)": 0, + "(7741, '2024-11-26', 6)": 0, + "(7741, '2024-11-26', 7)": 0, + "(7741, '2024-11-27', 0)": 0, + "(7741, '2024-11-27', 1)": 0, + "(7741, '2024-11-27', 2)": 0, + "(7741, '2024-11-27', 3)": 0, + "(7741, '2024-11-27', 4)": 0, + "(7741, '2024-11-27', 5)": 0, + "(7741, '2024-11-27', 6)": 1, + "(7741, '2024-11-27', 7)": 0, + "(7741, '2024-11-28', 0)": 0, + "(7741, '2024-11-28', 1)": 0, + "(7741, '2024-11-28', 2)": 0, + "(7741, '2024-11-28', 3)": 0, + "(7741, '2024-11-28', 4)": 0, + "(7741, '2024-11-28', 5)": 0, + "(7741, '2024-11-28', 6)": 1, + "(7741, '2024-11-28', 7)": 0, + "(7741, '2024-11-29', 0)": 0, + "(7741, '2024-11-29', 1)": 0, + "(7741, '2024-11-29', 2)": 0, + "(7741, '2024-11-29', 3)": 0, + "(7741, '2024-11-29', 4)": 0, + "(7741, '2024-11-29', 5)": 0, + "(7741, '2024-11-29', 6)": 0, + "(7741, '2024-11-29', 7)": 0, + "(7741, '2024-11-30', 0)": 0, + "(7741, '2024-11-30', 1)": 0, + "(7741, '2024-11-30', 2)": 0, + "(7741, '2024-11-30', 3)": 0, + "(7741, '2024-11-30', 4)": 0, + "(7741, '2024-11-30', 5)": 0, + "(7741, '2024-11-30', 6)": 0, + "(7741, '2024-11-30', 7)": 0, + "(7752, '2024-11-01', 0)": 0, + "(7752, '2024-11-01', 1)": 0, + "(7752, '2024-11-01', 2)": 0, + "(7752, '2024-11-01', 3)": 0, + "(7752, '2024-11-01', 4)": 0, + "(7752, '2024-11-01', 5)": 0, + "(7752, '2024-11-01', 6)": 0, + "(7752, '2024-11-01', 7)": 0, + "(7752, '2024-11-02', 0)": 0, + "(7752, '2024-11-02', 1)": 0, + "(7752, '2024-11-02', 2)": 1, + "(7752, '2024-11-02', 3)": 0, + "(7752, '2024-11-02', 4)": 0, + "(7752, '2024-11-02', 5)": 0, + "(7752, '2024-11-02', 6)": 0, + "(7752, '2024-11-02', 7)": 0, + "(7752, '2024-11-03', 0)": 0, + "(7752, '2024-11-03', 1)": 0, + "(7752, '2024-11-03', 2)": 0, + "(7752, '2024-11-03', 3)": 0, + "(7752, '2024-11-03', 4)": 0, + "(7752, '2024-11-03', 5)": 0, + "(7752, '2024-11-03', 6)": 0, + "(7752, '2024-11-03', 7)": 0, + "(7752, '2024-11-04', 0)": 0, + "(7752, '2024-11-04', 1)": 0, + "(7752, '2024-11-04', 2)": 0, + "(7752, '2024-11-04', 3)": 0, + "(7752, '2024-11-04', 4)": 0, + "(7752, '2024-11-04', 5)": 0, + "(7752, '2024-11-04', 6)": 0, + "(7752, '2024-11-04', 7)": 0, + "(7752, '2024-11-05', 0)": 0, + "(7752, '2024-11-05', 1)": 0, + "(7752, '2024-11-05', 2)": 0, + "(7752, '2024-11-05', 3)": 0, + "(7752, '2024-11-05', 4)": 0, + "(7752, '2024-11-05', 5)": 0, + "(7752, '2024-11-05', 6)": 1, + "(7752, '2024-11-05', 7)": 0, + "(7752, '2024-11-06', 0)": 0, + "(7752, '2024-11-06', 1)": 0, + "(7752, '2024-11-06', 2)": 0, + "(7752, '2024-11-06', 3)": 0, + "(7752, '2024-11-06', 4)": 0, + "(7752, '2024-11-06', 5)": 0, + "(7752, '2024-11-06', 6)": 1, + "(7752, '2024-11-06', 7)": 0, + "(7752, '2024-11-07', 0)": 0, + "(7752, '2024-11-07', 1)": 0, + "(7752, '2024-11-07', 2)": 0, + "(7752, '2024-11-07', 3)": 0, + "(7752, '2024-11-07', 4)": 0, + "(7752, '2024-11-07', 5)": 0, + "(7752, '2024-11-07', 6)": 1, + "(7752, '2024-11-07', 7)": 0, + "(7752, '2024-11-08', 0)": 0, + "(7752, '2024-11-08', 1)": 0, + "(7752, '2024-11-08', 2)": 0, + "(7752, '2024-11-08', 3)": 0, + "(7752, '2024-11-08', 4)": 0, + "(7752, '2024-11-08', 5)": 0, + "(7752, '2024-11-08', 6)": 0, + "(7752, '2024-11-08', 7)": 0, + "(7752, '2024-11-09', 0)": 0, + "(7752, '2024-11-09', 1)": 0, + "(7752, '2024-11-09', 2)": 0, + "(7752, '2024-11-09', 3)": 0, + "(7752, '2024-11-09', 4)": 0, + "(7752, '2024-11-09', 5)": 0, + "(7752, '2024-11-09', 6)": 0, + "(7752, '2024-11-09', 7)": 0, + "(7752, '2024-11-10', 0)": 0, + "(7752, '2024-11-10', 1)": 0, + "(7752, '2024-11-10', 2)": 0, + "(7752, '2024-11-10', 3)": 0, + "(7752, '2024-11-10', 4)": 0, + "(7752, '2024-11-10', 5)": 0, + "(7752, '2024-11-10', 6)": 0, + "(7752, '2024-11-10', 7)": 0, + "(7752, '2024-11-11', 0)": 0, + "(7752, '2024-11-11', 1)": 0, + "(7752, '2024-11-11', 2)": 0, + "(7752, '2024-11-11', 3)": 0, + "(7752, '2024-11-11', 4)": 0, + "(7752, '2024-11-11', 5)": 0, + "(7752, '2024-11-11', 6)": 0, + "(7752, '2024-11-11', 7)": 0, + "(7752, '2024-11-12', 0)": 1, + "(7752, '2024-11-12', 1)": 0, + "(7752, '2024-11-12', 2)": 0, + "(7752, '2024-11-12', 3)": 0, + "(7752, '2024-11-12', 4)": 0, + "(7752, '2024-11-12', 5)": 0, + "(7752, '2024-11-12', 6)": 0, + "(7752, '2024-11-12', 7)": 0, + "(7752, '2024-11-13', 0)": 0, + "(7752, '2024-11-13', 1)": 0, + "(7752, '2024-11-13', 2)": 1, + "(7752, '2024-11-13', 3)": 0, + "(7752, '2024-11-13', 4)": 0, + "(7752, '2024-11-13', 5)": 0, + "(7752, '2024-11-13', 6)": 0, + "(7752, '2024-11-13', 7)": 0, + "(7752, '2024-11-14', 0)": 0, + "(7752, '2024-11-14', 1)": 0, + "(7752, '2024-11-14', 2)": 0, + "(7752, '2024-11-14', 3)": 0, + "(7752, '2024-11-14', 4)": 0, + "(7752, '2024-11-14', 5)": 0, + "(7752, '2024-11-14', 6)": 0, + "(7752, '2024-11-14', 7)": 0, + "(7752, '2024-11-15', 0)": 0, + "(7752, '2024-11-15', 1)": 0, + "(7752, '2024-11-15', 2)": 0, + "(7752, '2024-11-15', 3)": 0, + "(7752, '2024-11-15', 4)": 0, + "(7752, '2024-11-15', 5)": 0, + "(7752, '2024-11-15', 6)": 0, + "(7752, '2024-11-15', 7)": 0, + "(7752, '2024-11-16', 0)": 1, + "(7752, '2024-11-16', 1)": 0, + "(7752, '2024-11-16', 2)": 0, + "(7752, '2024-11-16', 3)": 0, + "(7752, '2024-11-16', 4)": 0, + "(7752, '2024-11-16', 5)": 0, + "(7752, '2024-11-16', 6)": 0, + "(7752, '2024-11-16', 7)": 0, + "(7752, '2024-11-17', 0)": 0, + "(7752, '2024-11-17', 1)": 0, + "(7752, '2024-11-17', 2)": 0, + "(7752, '2024-11-17', 3)": 0, + "(7752, '2024-11-17', 4)": 0, + "(7752, '2024-11-17', 5)": 0, + "(7752, '2024-11-17', 6)": 0, + "(7752, '2024-11-17', 7)": 0, + "(7752, '2024-11-18', 0)": 0, + "(7752, '2024-11-18', 1)": 0, + "(7752, '2024-11-18', 2)": 1, + "(7752, '2024-11-18', 3)": 0, + "(7752, '2024-11-18', 4)": 0, + "(7752, '2024-11-18', 5)": 0, + "(7752, '2024-11-18', 6)": 0, + "(7752, '2024-11-18', 7)": 0, + "(7752, '2024-11-19', 0)": 0, + "(7752, '2024-11-19', 1)": 0, + "(7752, '2024-11-19', 2)": 0, + "(7752, '2024-11-19', 3)": 0, + "(7752, '2024-11-19', 4)": 0, + "(7752, '2024-11-19', 5)": 0, + "(7752, '2024-11-19', 6)": 0, + "(7752, '2024-11-19', 7)": 0, + "(7752, '2024-11-20', 0)": 1, + "(7752, '2024-11-20', 1)": 0, + "(7752, '2024-11-20', 2)": 0, + "(7752, '2024-11-20', 3)": 0, + "(7752, '2024-11-20', 4)": 0, + "(7752, '2024-11-20', 5)": 0, + "(7752, '2024-11-20', 6)": 0, + "(7752, '2024-11-20', 7)": 0, + "(7752, '2024-11-21', 0)": 0, + "(7752, '2024-11-21', 1)": 0, + "(7752, '2024-11-21', 2)": 0, + "(7752, '2024-11-21', 3)": 0, + "(7752, '2024-11-21', 4)": 0, + "(7752, '2024-11-21', 5)": 0, + "(7752, '2024-11-21', 6)": 0, + "(7752, '2024-11-21', 7)": 0, + "(7752, '2024-11-22', 0)": 1, + "(7752, '2024-11-22', 1)": 0, + "(7752, '2024-11-22', 2)": 0, + "(7752, '2024-11-22', 3)": 0, + "(7752, '2024-11-22', 4)": 0, + "(7752, '2024-11-22', 5)": 0, + "(7752, '2024-11-22', 6)": 0, + "(7752, '2024-11-22', 7)": 0, + "(7752, '2024-11-23', 0)": 0, + "(7752, '2024-11-23', 1)": 0, + "(7752, '2024-11-23', 2)": 1, + "(7752, '2024-11-23', 3)": 0, + "(7752, '2024-11-23', 4)": 0, + "(7752, '2024-11-23', 5)": 0, + "(7752, '2024-11-23', 6)": 0, + "(7752, '2024-11-23', 7)": 0, + "(7752, '2024-11-24', 0)": 0, + "(7752, '2024-11-24', 1)": 0, + "(7752, '2024-11-24', 2)": 1, + "(7752, '2024-11-24', 3)": 0, + "(7752, '2024-11-24', 4)": 0, + "(7752, '2024-11-24', 5)": 0, + "(7752, '2024-11-24', 6)": 0, + "(7752, '2024-11-24', 7)": 0, + "(7752, '2024-11-25', 0)": 0, + "(7752, '2024-11-25', 1)": 1, + "(7752, '2024-11-25', 2)": 0, + "(7752, '2024-11-25', 3)": 0, + "(7752, '2024-11-25', 4)": 0, + "(7752, '2024-11-25', 5)": 0, + "(7752, '2024-11-25', 6)": 0, + "(7752, '2024-11-25', 7)": 0, + "(7752, '2024-11-26', 0)": 0, + "(7752, '2024-11-26', 1)": 0, + "(7752, '2024-11-26', 2)": 1, + "(7752, '2024-11-26', 3)": 0, + "(7752, '2024-11-26', 4)": 0, + "(7752, '2024-11-26', 5)": 0, + "(7752, '2024-11-26', 6)": 0, + "(7752, '2024-11-26', 7)": 0, + "(7752, '2024-11-27', 0)": 0, + "(7752, '2024-11-27', 1)": 0, + "(7752, '2024-11-27', 2)": 0, + "(7752, '2024-11-27', 3)": 0, + "(7752, '2024-11-27', 4)": 0, + "(7752, '2024-11-27', 5)": 0, + "(7752, '2024-11-27', 6)": 0, + "(7752, '2024-11-27', 7)": 0, + "(7752, '2024-11-28', 0)": 0, + "(7752, '2024-11-28', 1)": 0, + "(7752, '2024-11-28', 2)": 1, + "(7752, '2024-11-28', 3)": 0, + "(7752, '2024-11-28', 4)": 0, + "(7752, '2024-11-28', 5)": 0, + "(7752, '2024-11-28', 6)": 0, + "(7752, '2024-11-28', 7)": 0, + "(7752, '2024-11-29', 0)": 0, + "(7752, '2024-11-29', 1)": 0, + "(7752, '2024-11-29', 2)": 1, + "(7752, '2024-11-29', 3)": 0, + "(7752, '2024-11-29', 4)": 0, + "(7752, '2024-11-29', 5)": 0, + "(7752, '2024-11-29', 6)": 0, + "(7752, '2024-11-29', 7)": 0, + "(7752, '2024-11-30', 0)": 0, + "(7752, '2024-11-30', 1)": 0, + "(7752, '2024-11-30', 2)": 0, + "(7752, '2024-11-30', 3)": 0, + "(7752, '2024-11-30', 4)": 0, + "(7752, '2024-11-30', 5)": 0, + "(7752, '2024-11-30', 6)": 0, + "(7752, '2024-11-30', 7)": 0, + "(7770, '2024-11-01', 0)": 0, + "(7770, '2024-11-01', 1)": 0, + "(7770, '2024-11-01', 2)": 0, + "(7770, '2024-11-01', 3)": 0, + "(7770, '2024-11-01', 4)": 0, + "(7770, '2024-11-01', 5)": 0, + "(7770, '2024-11-01', 6)": 0, + "(7770, '2024-11-01', 7)": 0, + "(7770, '2024-11-02', 0)": 0, + "(7770, '2024-11-02', 1)": 0, + "(7770, '2024-11-02', 2)": 0, + "(7770, '2024-11-02', 3)": 0, + "(7770, '2024-11-02', 4)": 0, + "(7770, '2024-11-02', 5)": 0, + "(7770, '2024-11-02', 6)": 0, + "(7770, '2024-11-02', 7)": 0, + "(7770, '2024-11-03', 0)": 0, + "(7770, '2024-11-03', 1)": 0, + "(7770, '2024-11-03', 2)": 0, + "(7770, '2024-11-03', 3)": 0, + "(7770, '2024-11-03', 4)": 0, + "(7770, '2024-11-03', 5)": 0, + "(7770, '2024-11-03', 6)": 0, + "(7770, '2024-11-03', 7)": 0, + "(7770, '2024-11-04', 0)": 0, + "(7770, '2024-11-04', 1)": 0, + "(7770, '2024-11-04', 2)": 0, + "(7770, '2024-11-04', 3)": 0, + "(7770, '2024-11-04', 4)": 0, + "(7770, '2024-11-04', 5)": 0, + "(7770, '2024-11-04', 6)": 0, + "(7770, '2024-11-04', 7)": 0, + "(7770, '2024-11-05', 0)": 0, + "(7770, '2024-11-05', 1)": 0, + "(7770, '2024-11-05', 2)": 0, + "(7770, '2024-11-05', 3)": 0, + "(7770, '2024-11-05', 4)": 0, + "(7770, '2024-11-05', 5)": 0, + "(7770, '2024-11-05', 6)": 0, + "(7770, '2024-11-05', 7)": 0, + "(7770, '2024-11-06', 0)": 0, + "(7770, '2024-11-06', 1)": 0, + "(7770, '2024-11-06', 2)": 0, + "(7770, '2024-11-06', 3)": 0, + "(7770, '2024-11-06', 4)": 0, + "(7770, '2024-11-06', 5)": 0, + "(7770, '2024-11-06', 6)": 0, + "(7770, '2024-11-06', 7)": 0, + "(7770, '2024-11-07', 0)": 0, + "(7770, '2024-11-07', 1)": 1, + "(7770, '2024-11-07', 2)": 0, + "(7770, '2024-11-07', 3)": 0, + "(7770, '2024-11-07', 4)": 0, + "(7770, '2024-11-07', 5)": 0, + "(7770, '2024-11-07', 6)": 0, + "(7770, '2024-11-07', 7)": 0, + "(7770, '2024-11-08', 0)": 0, + "(7770, '2024-11-08', 1)": 0, + "(7770, '2024-11-08', 2)": 0, + "(7770, '2024-11-08', 3)": 0, + "(7770, '2024-11-08', 4)": 0, + "(7770, '2024-11-08', 5)": 0, + "(7770, '2024-11-08', 6)": 0, + "(7770, '2024-11-08', 7)": 0, + "(7770, '2024-11-09', 0)": 0, + "(7770, '2024-11-09', 1)": 0, + "(7770, '2024-11-09', 2)": 0, + "(7770, '2024-11-09', 3)": 0, + "(7770, '2024-11-09', 4)": 0, + "(7770, '2024-11-09', 5)": 0, + "(7770, '2024-11-09', 6)": 0, + "(7770, '2024-11-09', 7)": 0, + "(7770, '2024-11-10', 0)": 0, + "(7770, '2024-11-10', 1)": 0, + "(7770, '2024-11-10', 2)": 0, + "(7770, '2024-11-10', 3)": 0, + "(7770, '2024-11-10', 4)": 0, + "(7770, '2024-11-10', 5)": 0, + "(7770, '2024-11-10', 6)": 0, + "(7770, '2024-11-10', 7)": 0, + "(7770, '2024-11-11', 0)": 0, + "(7770, '2024-11-11', 1)": 0, + "(7770, '2024-11-11', 2)": 0, + "(7770, '2024-11-11', 3)": 0, + "(7770, '2024-11-11', 4)": 0, + "(7770, '2024-11-11', 5)": 0, + "(7770, '2024-11-11', 6)": 0, + "(7770, '2024-11-11', 7)": 0, + "(7770, '2024-11-12', 0)": 0, + "(7770, '2024-11-12', 1)": 0, + "(7770, '2024-11-12', 2)": 1, + "(7770, '2024-11-12', 3)": 0, + "(7770, '2024-11-12', 4)": 0, + "(7770, '2024-11-12', 5)": 0, + "(7770, '2024-11-12', 6)": 0, + "(7770, '2024-11-12', 7)": 0, + "(7770, '2024-11-13', 0)": 0, + "(7770, '2024-11-13', 1)": 0, + "(7770, '2024-11-13', 2)": 1, + "(7770, '2024-11-13', 3)": 0, + "(7770, '2024-11-13', 4)": 0, + "(7770, '2024-11-13', 5)": 0, + "(7770, '2024-11-13', 6)": 0, + "(7770, '2024-11-13', 7)": 0, + "(7770, '2024-11-14', 0)": 0, + "(7770, '2024-11-14', 1)": 1, + "(7770, '2024-11-14', 2)": 0, + "(7770, '2024-11-14', 3)": 0, + "(7770, '2024-11-14', 4)": 0, + "(7770, '2024-11-14', 5)": 0, + "(7770, '2024-11-14', 6)": 0, + "(7770, '2024-11-14', 7)": 0, + "(7770, '2024-11-15', 0)": 0, + "(7770, '2024-11-15', 1)": 0, + "(7770, '2024-11-15', 2)": 0, + "(7770, '2024-11-15', 3)": 0, + "(7770, '2024-11-15', 4)": 0, + "(7770, '2024-11-15', 5)": 0, + "(7770, '2024-11-15', 6)": 0, + "(7770, '2024-11-15', 7)": 0, + "(7770, '2024-11-16', 0)": 0, + "(7770, '2024-11-16', 1)": 0, + "(7770, '2024-11-16', 2)": 0, + "(7770, '2024-11-16', 3)": 0, + "(7770, '2024-11-16', 4)": 0, + "(7770, '2024-11-16', 5)": 0, + "(7770, '2024-11-16', 6)": 0, + "(7770, '2024-11-16', 7)": 0, + "(7770, '2024-11-17', 0)": 0, + "(7770, '2024-11-17', 1)": 0, + "(7770, '2024-11-17', 2)": 0, + "(7770, '2024-11-17', 3)": 0, + "(7770, '2024-11-17', 4)": 0, + "(7770, '2024-11-17', 5)": 0, + "(7770, '2024-11-17', 6)": 0, + "(7770, '2024-11-17', 7)": 0, + "(7770, '2024-11-18', 0)": 0, + "(7770, '2024-11-18', 1)": 0, + "(7770, '2024-11-18', 2)": 0, + "(7770, '2024-11-18', 3)": 0, + "(7770, '2024-11-18', 4)": 0, + "(7770, '2024-11-18', 5)": 0, + "(7770, '2024-11-18', 6)": 0, + "(7770, '2024-11-18', 7)": 0, + "(7770, '2024-11-19', 0)": 1, + "(7770, '2024-11-19', 1)": 0, + "(7770, '2024-11-19', 2)": 0, + "(7770, '2024-11-19', 3)": 0, + "(7770, '2024-11-19', 4)": 0, + "(7770, '2024-11-19', 5)": 0, + "(7770, '2024-11-19', 6)": 0, + "(7770, '2024-11-19', 7)": 0, + "(7770, '2024-11-20', 0)": 0, + "(7770, '2024-11-20', 1)": 0, + "(7770, '2024-11-20', 2)": 0, + "(7770, '2024-11-20', 3)": 0, + "(7770, '2024-11-20', 4)": 0, + "(7770, '2024-11-20', 5)": 0, + "(7770, '2024-11-20', 6)": 0, + "(7770, '2024-11-20', 7)": 0, + "(7770, '2024-11-21', 0)": 0, + "(7770, '2024-11-21', 1)": 0, + "(7770, '2024-11-21', 2)": 0, + "(7770, '2024-11-21', 3)": 0, + "(7770, '2024-11-21', 4)": 0, + "(7770, '2024-11-21', 5)": 0, + "(7770, '2024-11-21', 6)": 0, + "(7770, '2024-11-21', 7)": 0, + "(7770, '2024-11-22', 0)": 0, + "(7770, '2024-11-22', 1)": 0, + "(7770, '2024-11-22', 2)": 0, + "(7770, '2024-11-22', 3)": 0, + "(7770, '2024-11-22', 4)": 0, + "(7770, '2024-11-22', 5)": 0, + "(7770, '2024-11-22', 6)": 0, + "(7770, '2024-11-22', 7)": 0, + "(7770, '2024-11-23', 0)": 1, + "(7770, '2024-11-23', 1)": 0, + "(7770, '2024-11-23', 2)": 0, + "(7770, '2024-11-23', 3)": 0, + "(7770, '2024-11-23', 4)": 0, + "(7770, '2024-11-23', 5)": 0, + "(7770, '2024-11-23', 6)": 0, + "(7770, '2024-11-23', 7)": 0, + "(7770, '2024-11-24', 0)": 0, + "(7770, '2024-11-24', 1)": 0, + "(7770, '2024-11-24', 2)": 0, + "(7770, '2024-11-24', 3)": 0, + "(7770, '2024-11-24', 4)": 0, + "(7770, '2024-11-24', 5)": 0, + "(7770, '2024-11-24', 6)": 0, + "(7770, '2024-11-24', 7)": 0, + "(7770, '2024-11-25', 0)": 0, + "(7770, '2024-11-25', 1)": 0, + "(7770, '2024-11-25', 2)": 0, + "(7770, '2024-11-25', 3)": 0, + "(7770, '2024-11-25', 4)": 0, + "(7770, '2024-11-25', 5)": 0, + "(7770, '2024-11-25', 6)": 0, + "(7770, '2024-11-25', 7)": 0, + "(7770, '2024-11-26', 0)": 1, + "(7770, '2024-11-26', 1)": 0, + "(7770, '2024-11-26', 2)": 0, + "(7770, '2024-11-26', 3)": 0, + "(7770, '2024-11-26', 4)": 0, + "(7770, '2024-11-26', 5)": 0, + "(7770, '2024-11-26', 6)": 0, + "(7770, '2024-11-26', 7)": 0, + "(7770, '2024-11-27', 0)": 1, + "(7770, '2024-11-27', 1)": 0, + "(7770, '2024-11-27', 2)": 0, + "(7770, '2024-11-27', 3)": 0, + "(7770, '2024-11-27', 4)": 0, + "(7770, '2024-11-27', 5)": 0, + "(7770, '2024-11-27', 6)": 0, + "(7770, '2024-11-27', 7)": 0, + "(7770, '2024-11-28', 0)": 0, + "(7770, '2024-11-28', 1)": 0, + "(7770, '2024-11-28', 2)": 0, + "(7770, '2024-11-28', 3)": 0, + "(7770, '2024-11-28', 4)": 0, + "(7770, '2024-11-28', 5)": 0, + "(7770, '2024-11-28', 6)": 0, + "(7770, '2024-11-28', 7)": 0, + "(7770, '2024-11-29', 0)": 0, + "(7770, '2024-11-29', 1)": 0, + "(7770, '2024-11-29', 2)": 1, + "(7770, '2024-11-29', 3)": 0, + "(7770, '2024-11-29', 4)": 0, + "(7770, '2024-11-29', 5)": 0, + "(7770, '2024-11-29', 6)": 0, + "(7770, '2024-11-29', 7)": 0, + "(7770, '2024-11-30', 0)": 0, + "(7770, '2024-11-30', 1)": 0, + "(7770, '2024-11-30', 2)": 1, + "(7770, '2024-11-30', 3)": 0, + "(7770, '2024-11-30', 4)": 0, + "(7770, '2024-11-30', 5)": 0, + "(7770, '2024-11-30', 6)": 0, + "(7770, '2024-11-30', 7)": 0, + "(7796, '2024-11-01', 0)": 0, + "(7796, '2024-11-01', 1)": 0, + "(7796, '2024-11-01', 2)": 0, + "(7796, '2024-11-01', 3)": 0, + "(7796, '2024-11-01', 4)": 0, + "(7796, '2024-11-01', 5)": 0, + "(7796, '2024-11-01', 6)": 0, + "(7796, '2024-11-01', 7)": 0, + "(7796, '2024-11-02', 0)": 0, + "(7796, '2024-11-02', 1)": 0, + "(7796, '2024-11-02', 2)": 0, + "(7796, '2024-11-02', 3)": 0, + "(7796, '2024-11-02', 4)": 0, + "(7796, '2024-11-02', 5)": 0, + "(7796, '2024-11-02', 6)": 0, + "(7796, '2024-11-02', 7)": 0, + "(7796, '2024-11-03', 0)": 0, + "(7796, '2024-11-03', 1)": 0, + "(7796, '2024-11-03', 2)": 0, + "(7796, '2024-11-03', 3)": 0, + "(7796, '2024-11-03', 4)": 0, + "(7796, '2024-11-03', 5)": 0, + "(7796, '2024-11-03', 6)": 0, + "(7796, '2024-11-03', 7)": 0, + "(7796, '2024-11-04', 0)": 0, + "(7796, '2024-11-04', 1)": 0, + "(7796, '2024-11-04', 2)": 0, + "(7796, '2024-11-04', 3)": 0, + "(7796, '2024-11-04', 4)": 0, + "(7796, '2024-11-04', 5)": 0, + "(7796, '2024-11-04', 6)": 0, + "(7796, '2024-11-04', 7)": 0, + "(7796, '2024-11-05', 0)": 0, + "(7796, '2024-11-05', 1)": 0, + "(7796, '2024-11-05', 2)": 0, + "(7796, '2024-11-05', 3)": 0, + "(7796, '2024-11-05', 4)": 0, + "(7796, '2024-11-05', 5)": 0, + "(7796, '2024-11-05', 6)": 0, + "(7796, '2024-11-05', 7)": 0, + "(7796, '2024-11-06', 0)": 0, + "(7796, '2024-11-06', 1)": 0, + "(7796, '2024-11-06', 2)": 0, + "(7796, '2024-11-06', 3)": 0, + "(7796, '2024-11-06', 4)": 0, + "(7796, '2024-11-06', 5)": 0, + "(7796, '2024-11-06', 6)": 0, + "(7796, '2024-11-06', 7)": 0, + "(7796, '2024-11-07', 0)": 1, + "(7796, '2024-11-07', 1)": 0, + "(7796, '2024-11-07', 2)": 0, + "(7796, '2024-11-07', 3)": 0, + "(7796, '2024-11-07', 4)": 0, + "(7796, '2024-11-07', 5)": 0, + "(7796, '2024-11-07', 6)": 0, + "(7796, '2024-11-07', 7)": 0, + "(7796, '2024-11-08', 0)": 0, + "(7796, '2024-11-08', 1)": 0, + "(7796, '2024-11-08', 2)": 0, + "(7796, '2024-11-08', 3)": 0, + "(7796, '2024-11-08', 4)": 0, + "(7796, '2024-11-08', 5)": 0, + "(7796, '2024-11-08', 6)": 0, + "(7796, '2024-11-08', 7)": 0, + "(7796, '2024-11-09', 0)": 0, + "(7796, '2024-11-09', 1)": 0, + "(7796, '2024-11-09', 2)": 0, + "(7796, '2024-11-09', 3)": 0, + "(7796, '2024-11-09', 4)": 0, + "(7796, '2024-11-09', 5)": 0, + "(7796, '2024-11-09', 6)": 0, + "(7796, '2024-11-09', 7)": 0, + "(7796, '2024-11-10', 0)": 0, + "(7796, '2024-11-10', 1)": 0, + "(7796, '2024-11-10', 2)": 0, + "(7796, '2024-11-10', 3)": 0, + "(7796, '2024-11-10', 4)": 0, + "(7796, '2024-11-10', 5)": 0, + "(7796, '2024-11-10', 6)": 0, + "(7796, '2024-11-10', 7)": 0, + "(7796, '2024-11-11', 0)": 0, + "(7796, '2024-11-11', 1)": 0, + "(7796, '2024-11-11', 2)": 0, + "(7796, '2024-11-11', 3)": 0, + "(7796, '2024-11-11', 4)": 0, + "(7796, '2024-11-11', 5)": 0, + "(7796, '2024-11-11', 6)": 0, + "(7796, '2024-11-11', 7)": 0, + "(7796, '2024-11-12', 0)": 0, + "(7796, '2024-11-12', 1)": 1, + "(7796, '2024-11-12', 2)": 0, + "(7796, '2024-11-12', 3)": 0, + "(7796, '2024-11-12', 4)": 0, + "(7796, '2024-11-12', 5)": 0, + "(7796, '2024-11-12', 6)": 0, + "(7796, '2024-11-12', 7)": 0, + "(7796, '2024-11-13', 0)": 0, + "(7796, '2024-11-13', 1)": 1, + "(7796, '2024-11-13', 2)": 0, + "(7796, '2024-11-13', 3)": 0, + "(7796, '2024-11-13', 4)": 0, + "(7796, '2024-11-13', 5)": 0, + "(7796, '2024-11-13', 6)": 0, + "(7796, '2024-11-13', 7)": 0, + "(7796, '2024-11-14', 0)": 0, + "(7796, '2024-11-14', 1)": 0, + "(7796, '2024-11-14', 2)": 0, + "(7796, '2024-11-14', 3)": 0, + "(7796, '2024-11-14', 4)": 0, + "(7796, '2024-11-14', 5)": 0, + "(7796, '2024-11-14', 6)": 0, + "(7796, '2024-11-14', 7)": 0, + "(7796, '2024-11-15', 0)": 0, + "(7796, '2024-11-15', 1)": 1, + "(7796, '2024-11-15', 2)": 0, + "(7796, '2024-11-15', 3)": 0, + "(7796, '2024-11-15', 4)": 0, + "(7796, '2024-11-15', 5)": 0, + "(7796, '2024-11-15', 6)": 0, + "(7796, '2024-11-15', 7)": 0, + "(7796, '2024-11-16', 0)": 0, + "(7796, '2024-11-16', 1)": 0, + "(7796, '2024-11-16', 2)": 0, + "(7796, '2024-11-16', 3)": 0, + "(7796, '2024-11-16', 4)": 0, + "(7796, '2024-11-16', 5)": 0, + "(7796, '2024-11-16', 6)": 0, + "(7796, '2024-11-16', 7)": 0, + "(7796, '2024-11-17', 0)": 0, + "(7796, '2024-11-17', 1)": 1, + "(7796, '2024-11-17', 2)": 0, + "(7796, '2024-11-17', 3)": 0, + "(7796, '2024-11-17', 4)": 0, + "(7796, '2024-11-17', 5)": 0, + "(7796, '2024-11-17', 6)": 0, + "(7796, '2024-11-17', 7)": 0, + "(7796, '2024-11-18', 0)": 0, + "(7796, '2024-11-18', 1)": 0, + "(7796, '2024-11-18', 2)": 0, + "(7796, '2024-11-18', 3)": 0, + "(7796, '2024-11-18', 4)": 0, + "(7796, '2024-11-18', 5)": 0, + "(7796, '2024-11-18', 6)": 0, + "(7796, '2024-11-18', 7)": 0, + "(7796, '2024-11-19', 0)": 0, + "(7796, '2024-11-19', 1)": 1, + "(7796, '2024-11-19', 2)": 0, + "(7796, '2024-11-19', 3)": 0, + "(7796, '2024-11-19', 4)": 0, + "(7796, '2024-11-19', 5)": 0, + "(7796, '2024-11-19', 6)": 0, + "(7796, '2024-11-19', 7)": 0, + "(7796, '2024-11-20', 0)": 1, + "(7796, '2024-11-20', 1)": 0, + "(7796, '2024-11-20', 2)": 0, + "(7796, '2024-11-20', 3)": 0, + "(7796, '2024-11-20', 4)": 0, + "(7796, '2024-11-20', 5)": 0, + "(7796, '2024-11-20', 6)": 0, + "(7796, '2024-11-20', 7)": 0, + "(7796, '2024-11-21', 0)": 1, + "(7796, '2024-11-21', 1)": 0, + "(7796, '2024-11-21', 2)": 0, + "(7796, '2024-11-21', 3)": 0, + "(7796, '2024-11-21', 4)": 0, + "(7796, '2024-11-21', 5)": 0, + "(7796, '2024-11-21', 6)": 0, + "(7796, '2024-11-21', 7)": 0, + "(7796, '2024-11-22', 0)": 0, + "(7796, '2024-11-22', 1)": 0, + "(7796, '2024-11-22', 2)": 1, + "(7796, '2024-11-22', 3)": 0, + "(7796, '2024-11-22', 4)": 0, + "(7796, '2024-11-22', 5)": 0, + "(7796, '2024-11-22', 6)": 0, + "(7796, '2024-11-22', 7)": 0, + "(7796, '2024-11-23', 0)": 0, + "(7796, '2024-11-23', 1)": 0, + "(7796, '2024-11-23', 2)": 0, + "(7796, '2024-11-23', 3)": 0, + "(7796, '2024-11-23', 4)": 0, + "(7796, '2024-11-23', 5)": 0, + "(7796, '2024-11-23', 6)": 0, + "(7796, '2024-11-23', 7)": 0, + "(7796, '2024-11-24', 0)": 0, + "(7796, '2024-11-24', 1)": 0, + "(7796, '2024-11-24', 2)": 0, + "(7796, '2024-11-24', 3)": 0, + "(7796, '2024-11-24', 4)": 0, + "(7796, '2024-11-24', 5)": 0, + "(7796, '2024-11-24', 6)": 0, + "(7796, '2024-11-24', 7)": 0, + "(7796, '2024-11-25', 0)": 0, + "(7796, '2024-11-25', 1)": 0, + "(7796, '2024-11-25', 2)": 0, + "(7796, '2024-11-25', 3)": 0, + "(7796, '2024-11-25', 4)": 0, + "(7796, '2024-11-25', 5)": 0, + "(7796, '2024-11-25', 6)": 0, + "(7796, '2024-11-25', 7)": 0, + "(7796, '2024-11-26', 0)": 0, + "(7796, '2024-11-26', 1)": 0, + "(7796, '2024-11-26', 2)": 0, + "(7796, '2024-11-26', 3)": 0, + "(7796, '2024-11-26', 4)": 0, + "(7796, '2024-11-26', 5)": 0, + "(7796, '2024-11-26', 6)": 0, + "(7796, '2024-11-26', 7)": 0, + "(7796, '2024-11-27', 0)": 0, + "(7796, '2024-11-27', 1)": 1, + "(7796, '2024-11-27', 2)": 0, + "(7796, '2024-11-27', 3)": 0, + "(7796, '2024-11-27', 4)": 0, + "(7796, '2024-11-27', 5)": 0, + "(7796, '2024-11-27', 6)": 0, + "(7796, '2024-11-27', 7)": 0, + "(7796, '2024-11-28', 0)": 0, + "(7796, '2024-11-28', 1)": 0, + "(7796, '2024-11-28', 2)": 0, + "(7796, '2024-11-28', 3)": 0, + "(7796, '2024-11-28', 4)": 0, + "(7796, '2024-11-28', 5)": 0, + "(7796, '2024-11-28', 6)": 0, + "(7796, '2024-11-28', 7)": 0, + "(7796, '2024-11-29', 0)": 0, + "(7796, '2024-11-29', 1)": 0, + "(7796, '2024-11-29', 2)": 0, + "(7796, '2024-11-29', 3)": 0, + "(7796, '2024-11-29', 4)": 0, + "(7796, '2024-11-29', 5)": 0, + "(7796, '2024-11-29', 6)": 0, + "(7796, '2024-11-29', 7)": 0, + "(7796, '2024-11-30', 0)": 0, + "(7796, '2024-11-30', 1)": 0, + "(7796, '2024-11-30', 2)": 0, + "(7796, '2024-11-30', 3)": 0, + "(7796, '2024-11-30', 4)": 0, + "(7796, '2024-11-30', 5)": 0, + "(7796, '2024-11-30', 6)": 0, + "(7796, '2024-11-30', 7)": 0, + "(7835, '2024-11-01', 0)": 0, + "(7835, '2024-11-01', 1)": 0, + "(7835, '2024-11-01', 2)": 0, + "(7835, '2024-11-01', 3)": 0, + "(7835, '2024-11-01', 4)": 0, + "(7835, '2024-11-01', 5)": 0, + "(7835, '2024-11-01', 6)": 0, + "(7835, '2024-11-01', 7)": 0, + "(7835, '2024-11-02', 0)": 0, + "(7835, '2024-11-02', 1)": 0, + "(7835, '2024-11-02', 2)": 0, + "(7835, '2024-11-02', 3)": 0, + "(7835, '2024-11-02', 4)": 0, + "(7835, '2024-11-02', 5)": 0, + "(7835, '2024-11-02', 6)": 0, + "(7835, '2024-11-02', 7)": 0, + "(7835, '2024-11-03', 0)": 0, + "(7835, '2024-11-03', 1)": 0, + "(7835, '2024-11-03', 2)": 0, + "(7835, '2024-11-03', 3)": 0, + "(7835, '2024-11-03', 4)": 0, + "(7835, '2024-11-03', 5)": 0, + "(7835, '2024-11-03', 6)": 0, + "(7835, '2024-11-03', 7)": 0, + "(7835, '2024-11-04', 0)": 0, + "(7835, '2024-11-04', 1)": 0, + "(7835, '2024-11-04', 2)": 0, + "(7835, '2024-11-04', 3)": 0, + "(7835, '2024-11-04', 4)": 0, + "(7835, '2024-11-04', 5)": 0, + "(7835, '2024-11-04', 6)": 0, + "(7835, '2024-11-04', 7)": 0, + "(7835, '2024-11-05', 0)": 0, + "(7835, '2024-11-05', 1)": 0, + "(7835, '2024-11-05', 2)": 0, + "(7835, '2024-11-05', 3)": 0, + "(7835, '2024-11-05', 4)": 0, + "(7835, '2024-11-05', 5)": 0, + "(7835, '2024-11-05', 6)": 0, + "(7835, '2024-11-05', 7)": 0, + "(7835, '2024-11-06', 0)": 0, + "(7835, '2024-11-06', 1)": 0, + "(7835, '2024-11-06', 2)": 0, + "(7835, '2024-11-06', 3)": 0, + "(7835, '2024-11-06', 4)": 0, + "(7835, '2024-11-06', 5)": 0, + "(7835, '2024-11-06', 6)": 0, + "(7835, '2024-11-06', 7)": 0, + "(7835, '2024-11-07', 0)": 0, + "(7835, '2024-11-07', 1)": 0, + "(7835, '2024-11-07', 2)": 0, + "(7835, '2024-11-07', 3)": 0, + "(7835, '2024-11-07', 4)": 0, + "(7835, '2024-11-07', 5)": 0, + "(7835, '2024-11-07', 6)": 0, + "(7835, '2024-11-07', 7)": 0, + "(7835, '2024-11-08', 0)": 0, + "(7835, '2024-11-08', 1)": 0, + "(7835, '2024-11-08', 2)": 0, + "(7835, '2024-11-08', 3)": 0, + "(7835, '2024-11-08', 4)": 0, + "(7835, '2024-11-08', 5)": 0, + "(7835, '2024-11-08', 6)": 0, + "(7835, '2024-11-08', 7)": 0, + "(7835, '2024-11-09', 0)": 0, + "(7835, '2024-11-09', 1)": 1, + "(7835, '2024-11-09', 2)": 0, + "(7835, '2024-11-09', 3)": 0, + "(7835, '2024-11-09', 4)": 0, + "(7835, '2024-11-09', 5)": 0, + "(7835, '2024-11-09', 6)": 0, + "(7835, '2024-11-09', 7)": 0, + "(7835, '2024-11-10', 0)": 1, + "(7835, '2024-11-10', 1)": 0, + "(7835, '2024-11-10', 2)": 0, + "(7835, '2024-11-10', 3)": 0, + "(7835, '2024-11-10', 4)": 0, + "(7835, '2024-11-10', 5)": 0, + "(7835, '2024-11-10', 6)": 0, + "(7835, '2024-11-10', 7)": 0, + "(7835, '2024-11-11', 0)": 0, + "(7835, '2024-11-11', 1)": 0, + "(7835, '2024-11-11', 2)": 0, + "(7835, '2024-11-11', 3)": 0, + "(7835, '2024-11-11', 4)": 0, + "(7835, '2024-11-11', 5)": 0, + "(7835, '2024-11-11', 6)": 0, + "(7835, '2024-11-11', 7)": 0, + "(7835, '2024-11-12', 0)": 1, + "(7835, '2024-11-12', 1)": 0, + "(7835, '2024-11-12', 2)": 0, + "(7835, '2024-11-12', 3)": 0, + "(7835, '2024-11-12', 4)": 0, + "(7835, '2024-11-12', 5)": 0, + "(7835, '2024-11-12', 6)": 0, + "(7835, '2024-11-12', 7)": 0, + "(7835, '2024-11-13', 0)": 1, + "(7835, '2024-11-13', 1)": 0, + "(7835, '2024-11-13', 2)": 0, + "(7835, '2024-11-13', 3)": 0, + "(7835, '2024-11-13', 4)": 0, + "(7835, '2024-11-13', 5)": 0, + "(7835, '2024-11-13', 6)": 0, + "(7835, '2024-11-13', 7)": 0, + "(7835, '2024-11-14', 0)": 1, + "(7835, '2024-11-14', 1)": 0, + "(7835, '2024-11-14', 2)": 0, + "(7835, '2024-11-14', 3)": 0, + "(7835, '2024-11-14', 4)": 0, + "(7835, '2024-11-14', 5)": 0, + "(7835, '2024-11-14', 6)": 0, + "(7835, '2024-11-14', 7)": 0, + "(7835, '2024-11-15', 0)": 0, + "(7835, '2024-11-15', 1)": 0, + "(7835, '2024-11-15', 2)": 0, + "(7835, '2024-11-15', 3)": 0, + "(7835, '2024-11-15', 4)": 0, + "(7835, '2024-11-15', 5)": 0, + "(7835, '2024-11-15', 6)": 0, + "(7835, '2024-11-15', 7)": 0, + "(7835, '2024-11-16', 0)": 0, + "(7835, '2024-11-16', 1)": 0, + "(7835, '2024-11-16', 2)": 0, + "(7835, '2024-11-16', 3)": 0, + "(7835, '2024-11-16', 4)": 0, + "(7835, '2024-11-16', 5)": 0, + "(7835, '2024-11-16', 6)": 0, + "(7835, '2024-11-16', 7)": 0, + "(7835, '2024-11-17', 0)": 0, + "(7835, '2024-11-17', 1)": 0, + "(7835, '2024-11-17', 2)": 0, + "(7835, '2024-11-17', 3)": 0, + "(7835, '2024-11-17', 4)": 0, + "(7835, '2024-11-17', 5)": 0, + "(7835, '2024-11-17', 6)": 0, + "(7835, '2024-11-17', 7)": 0, + "(7835, '2024-11-18', 0)": 0, + "(7835, '2024-11-18', 1)": 1, + "(7835, '2024-11-18', 2)": 0, + "(7835, '2024-11-18', 3)": 0, + "(7835, '2024-11-18', 4)": 0, + "(7835, '2024-11-18', 5)": 0, + "(7835, '2024-11-18', 6)": 0, + "(7835, '2024-11-18', 7)": 0, + "(7835, '2024-11-19', 0)": 0, + "(7835, '2024-11-19', 1)": 0, + "(7835, '2024-11-19', 2)": 0, + "(7835, '2024-11-19', 3)": 0, + "(7835, '2024-11-19', 4)": 0, + "(7835, '2024-11-19', 5)": 0, + "(7835, '2024-11-19', 6)": 0, + "(7835, '2024-11-19', 7)": 0, + "(7835, '2024-11-20', 0)": 0, + "(7835, '2024-11-20', 1)": 0, + "(7835, '2024-11-20', 2)": 1, + "(7835, '2024-11-20', 3)": 0, + "(7835, '2024-11-20', 4)": 0, + "(7835, '2024-11-20', 5)": 0, + "(7835, '2024-11-20', 6)": 0, + "(7835, '2024-11-20', 7)": 0, + "(7835, '2024-11-21', 0)": 0, + "(7835, '2024-11-21', 1)": 0, + "(7835, '2024-11-21', 2)": 1, + "(7835, '2024-11-21', 3)": 0, + "(7835, '2024-11-21', 4)": 0, + "(7835, '2024-11-21', 5)": 0, + "(7835, '2024-11-21', 6)": 0, + "(7835, '2024-11-21', 7)": 0, + "(7835, '2024-11-22', 0)": 0, + "(7835, '2024-11-22', 1)": 0, + "(7835, '2024-11-22', 2)": 0, + "(7835, '2024-11-22', 3)": 0, + "(7835, '2024-11-22', 4)": 0, + "(7835, '2024-11-22', 5)": 0, + "(7835, '2024-11-22', 6)": 0, + "(7835, '2024-11-22', 7)": 0, + "(7835, '2024-11-23', 0)": 0, + "(7835, '2024-11-23', 1)": 0, + "(7835, '2024-11-23', 2)": 0, + "(7835, '2024-11-23', 3)": 0, + "(7835, '2024-11-23', 4)": 0, + "(7835, '2024-11-23', 5)": 0, + "(7835, '2024-11-23', 6)": 0, + "(7835, '2024-11-23', 7)": 0, + "(7835, '2024-11-24', 0)": 0, + "(7835, '2024-11-24', 1)": 1, + "(7835, '2024-11-24', 2)": 0, + "(7835, '2024-11-24', 3)": 0, + "(7835, '2024-11-24', 4)": 0, + "(7835, '2024-11-24', 5)": 0, + "(7835, '2024-11-24', 6)": 0, + "(7835, '2024-11-24', 7)": 0, + "(7835, '2024-11-25', 0)": 0, + "(7835, '2024-11-25', 1)": 0, + "(7835, '2024-11-25', 2)": 0, + "(7835, '2024-11-25', 3)": 0, + "(7835, '2024-11-25', 4)": 0, + "(7835, '2024-11-25', 5)": 0, + "(7835, '2024-11-25', 6)": 0, + "(7835, '2024-11-25', 7)": 0, + "(7835, '2024-11-26', 0)": 0, + "(7835, '2024-11-26', 1)": 0, + "(7835, '2024-11-26', 2)": 0, + "(7835, '2024-11-26', 3)": 0, + "(7835, '2024-11-26', 4)": 0, + "(7835, '2024-11-26', 5)": 0, + "(7835, '2024-11-26', 6)": 0, + "(7835, '2024-11-26', 7)": 0, + "(7835, '2024-11-27', 0)": 0, + "(7835, '2024-11-27', 1)": 0, + "(7835, '2024-11-27', 2)": 0, + "(7835, '2024-11-27', 3)": 0, + "(7835, '2024-11-27', 4)": 0, + "(7835, '2024-11-27', 5)": 0, + "(7835, '2024-11-27', 6)": 0, + "(7835, '2024-11-27', 7)": 0, + "(7835, '2024-11-28', 0)": 0, + "(7835, '2024-11-28', 1)": 0, + "(7835, '2024-11-28', 2)": 0, + "(7835, '2024-11-28', 3)": 0, + "(7835, '2024-11-28', 4)": 0, + "(7835, '2024-11-28', 5)": 0, + "(7835, '2024-11-28', 6)": 0, + "(7835, '2024-11-28', 7)": 0, + "(7835, '2024-11-29', 0)": 1, + "(7835, '2024-11-29', 1)": 0, + "(7835, '2024-11-29', 2)": 0, + "(7835, '2024-11-29', 3)": 0, + "(7835, '2024-11-29', 4)": 0, + "(7835, '2024-11-29', 5)": 0, + "(7835, '2024-11-29', 6)": 0, + "(7835, '2024-11-29', 7)": 0, + "(7835, '2024-11-30', 0)": 0, + "(7835, '2024-11-30', 1)": 0, + "(7835, '2024-11-30', 2)": 0, + "(7835, '2024-11-30', 3)": 0, + "(7835, '2024-11-30', 4)": 0, + "(7835, '2024-11-30', 5)": 0, + "(7835, '2024-11-30', 6)": 0, + "(7835, '2024-11-30', 7)": 0, + "(7848, '2024-11-01', 0)": 1, + "(7848, '2024-11-01', 1)": 0, + "(7848, '2024-11-01', 2)": 0, + "(7848, '2024-11-01', 3)": 0, + "(7848, '2024-11-01', 4)": 0, + "(7848, '2024-11-01', 5)": 0, + "(7848, '2024-11-01', 6)": 0, + "(7848, '2024-11-01', 7)": 0, + "(7848, '2024-11-02', 0)": 0, + "(7848, '2024-11-02', 1)": 0, + "(7848, '2024-11-02', 2)": 1, + "(7848, '2024-11-02', 3)": 0, + "(7848, '2024-11-02', 4)": 0, + "(7848, '2024-11-02', 5)": 0, + "(7848, '2024-11-02', 6)": 0, + "(7848, '2024-11-02', 7)": 0, + "(7848, '2024-11-03', 0)": 0, + "(7848, '2024-11-03', 1)": 0, + "(7848, '2024-11-03', 2)": 1, + "(7848, '2024-11-03', 3)": 0, + "(7848, '2024-11-03', 4)": 0, + "(7848, '2024-11-03', 5)": 0, + "(7848, '2024-11-03', 6)": 0, + "(7848, '2024-11-03', 7)": 0, + "(7848, '2024-11-04', 0)": 0, + "(7848, '2024-11-04', 1)": 0, + "(7848, '2024-11-04', 2)": 1, + "(7848, '2024-11-04', 3)": 0, + "(7848, '2024-11-04', 4)": 0, + "(7848, '2024-11-04', 5)": 0, + "(7848, '2024-11-04', 6)": 0, + "(7848, '2024-11-04', 7)": 0, + "(7848, '2024-11-05', 0)": 0, + "(7848, '2024-11-05', 1)": 0, + "(7848, '2024-11-05', 2)": 1, + "(7848, '2024-11-05', 3)": 0, + "(7848, '2024-11-05', 4)": 0, + "(7848, '2024-11-05', 5)": 0, + "(7848, '2024-11-05', 6)": 0, + "(7848, '2024-11-05', 7)": 0, + "(7848, '2024-11-06', 0)": 0, + "(7848, '2024-11-06', 1)": 0, + "(7848, '2024-11-06', 2)": 1, + "(7848, '2024-11-06', 3)": 0, + "(7848, '2024-11-06', 4)": 0, + "(7848, '2024-11-06', 5)": 0, + "(7848, '2024-11-06', 6)": 0, + "(7848, '2024-11-06', 7)": 0, + "(7848, '2024-11-07', 0)": 0, + "(7848, '2024-11-07', 1)": 0, + "(7848, '2024-11-07', 2)": 0, + "(7848, '2024-11-07', 3)": 0, + "(7848, '2024-11-07', 4)": 0, + "(7848, '2024-11-07', 5)": 0, + "(7848, '2024-11-07', 6)": 0, + "(7848, '2024-11-07', 7)": 0, + "(7848, '2024-11-08', 0)": 1, + "(7848, '2024-11-08', 1)": 0, + "(7848, '2024-11-08', 2)": 0, + "(7848, '2024-11-08', 3)": 0, + "(7848, '2024-11-08', 4)": 0, + "(7848, '2024-11-08', 5)": 0, + "(7848, '2024-11-08', 6)": 0, + "(7848, '2024-11-08', 7)": 0, + "(7848, '2024-11-09', 0)": 1, + "(7848, '2024-11-09', 1)": 0, + "(7848, '2024-11-09', 2)": 0, + "(7848, '2024-11-09', 3)": 0, + "(7848, '2024-11-09', 4)": 0, + "(7848, '2024-11-09', 5)": 0, + "(7848, '2024-11-09', 6)": 0, + "(7848, '2024-11-09', 7)": 0, + "(7848, '2024-11-10', 0)": 0, + "(7848, '2024-11-10', 1)": 0, + "(7848, '2024-11-10', 2)": 0, + "(7848, '2024-11-10', 3)": 0, + "(7848, '2024-11-10', 4)": 0, + "(7848, '2024-11-10', 5)": 0, + "(7848, '2024-11-10', 6)": 0, + "(7848, '2024-11-10', 7)": 0, + "(7848, '2024-11-11', 0)": 1, + "(7848, '2024-11-11', 1)": 0, + "(7848, '2024-11-11', 2)": 0, + "(7848, '2024-11-11', 3)": 0, + "(7848, '2024-11-11', 4)": 0, + "(7848, '2024-11-11', 5)": 0, + "(7848, '2024-11-11', 6)": 0, + "(7848, '2024-11-11', 7)": 0, + "(7848, '2024-11-12', 0)": 0, + "(7848, '2024-11-12', 1)": 0, + "(7848, '2024-11-12', 2)": 0, + "(7848, '2024-11-12', 3)": 0, + "(7848, '2024-11-12', 4)": 0, + "(7848, '2024-11-12', 5)": 0, + "(7848, '2024-11-12', 6)": 0, + "(7848, '2024-11-12', 7)": 0, + "(7848, '2024-11-13', 0)": 0, + "(7848, '2024-11-13', 1)": 0, + "(7848, '2024-11-13', 2)": 0, + "(7848, '2024-11-13', 3)": 0, + "(7848, '2024-11-13', 4)": 0, + "(7848, '2024-11-13', 5)": 0, + "(7848, '2024-11-13', 6)": 0, + "(7848, '2024-11-13', 7)": 0, + "(7848, '2024-11-14', 0)": 0, + "(7848, '2024-11-14', 1)": 0, + "(7848, '2024-11-14', 2)": 1, + "(7848, '2024-11-14', 3)": 0, + "(7848, '2024-11-14', 4)": 0, + "(7848, '2024-11-14', 5)": 0, + "(7848, '2024-11-14', 6)": 0, + "(7848, '2024-11-14', 7)": 0, + "(7848, '2024-11-15', 0)": 0, + "(7848, '2024-11-15', 1)": 0, + "(7848, '2024-11-15', 2)": 0, + "(7848, '2024-11-15', 3)": 0, + "(7848, '2024-11-15', 4)": 0, + "(7848, '2024-11-15', 5)": 0, + "(7848, '2024-11-15', 6)": 0, + "(7848, '2024-11-15', 7)": 0, + "(7848, '2024-11-16', 0)": 0, + "(7848, '2024-11-16', 1)": 0, + "(7848, '2024-11-16', 2)": 1, + "(7848, '2024-11-16', 3)": 0, + "(7848, '2024-11-16', 4)": 0, + "(7848, '2024-11-16', 5)": 0, + "(7848, '2024-11-16', 6)": 0, + "(7848, '2024-11-16', 7)": 0, + "(7848, '2024-11-17', 0)": 0, + "(7848, '2024-11-17', 1)": 1, + "(7848, '2024-11-17', 2)": 0, + "(7848, '2024-11-17', 3)": 0, + "(7848, '2024-11-17', 4)": 0, + "(7848, '2024-11-17', 5)": 0, + "(7848, '2024-11-17', 6)": 0, + "(7848, '2024-11-17', 7)": 0, + "(7848, '2024-11-18', 0)": 1, + "(7848, '2024-11-18', 1)": 0, + "(7848, '2024-11-18', 2)": 0, + "(7848, '2024-11-18', 3)": 0, + "(7848, '2024-11-18', 4)": 0, + "(7848, '2024-11-18', 5)": 0, + "(7848, '2024-11-18', 6)": 0, + "(7848, '2024-11-18', 7)": 0, + "(7848, '2024-11-19', 0)": 0, + "(7848, '2024-11-19', 1)": 0, + "(7848, '2024-11-19', 2)": 0, + "(7848, '2024-11-19', 3)": 0, + "(7848, '2024-11-19', 4)": 0, + "(7848, '2024-11-19', 5)": 0, + "(7848, '2024-11-19', 6)": 0, + "(7848, '2024-11-19', 7)": 0, + "(7848, '2024-11-20', 0)": 0, + "(7848, '2024-11-20', 1)": 1, + "(7848, '2024-11-20', 2)": 0, + "(7848, '2024-11-20', 3)": 0, + "(7848, '2024-11-20', 4)": 0, + "(7848, '2024-11-20', 5)": 0, + "(7848, '2024-11-20', 6)": 0, + "(7848, '2024-11-20', 7)": 0, + "(7848, '2024-11-21', 0)": 0, + "(7848, '2024-11-21', 1)": 1, + "(7848, '2024-11-21', 2)": 0, + "(7848, '2024-11-21', 3)": 0, + "(7848, '2024-11-21', 4)": 0, + "(7848, '2024-11-21', 5)": 0, + "(7848, '2024-11-21', 6)": 0, + "(7848, '2024-11-21', 7)": 0, + "(7848, '2024-11-22', 0)": 0, + "(7848, '2024-11-22', 1)": 0, + "(7848, '2024-11-22', 2)": 0, + "(7848, '2024-11-22', 3)": 0, + "(7848, '2024-11-22', 4)": 0, + "(7848, '2024-11-22', 5)": 0, + "(7848, '2024-11-22', 6)": 0, + "(7848, '2024-11-22', 7)": 0, + "(7848, '2024-11-23', 0)": 0, + "(7848, '2024-11-23', 1)": 0, + "(7848, '2024-11-23', 2)": 0, + "(7848, '2024-11-23', 3)": 0, + "(7848, '2024-11-23', 4)": 0, + "(7848, '2024-11-23', 5)": 0, + "(7848, '2024-11-23', 6)": 0, + "(7848, '2024-11-23', 7)": 0, + "(7848, '2024-11-24', 0)": 1, + "(7848, '2024-11-24', 1)": 0, + "(7848, '2024-11-24', 2)": 0, + "(7848, '2024-11-24', 3)": 0, + "(7848, '2024-11-24', 4)": 0, + "(7848, '2024-11-24', 5)": 0, + "(7848, '2024-11-24', 6)": 0, + "(7848, '2024-11-24', 7)": 0, + "(7848, '2024-11-25', 0)": 0, + "(7848, '2024-11-25', 1)": 0, + "(7848, '2024-11-25', 2)": 1, + "(7848, '2024-11-25', 3)": 0, + "(7848, '2024-11-25', 4)": 0, + "(7848, '2024-11-25', 5)": 0, + "(7848, '2024-11-25', 6)": 0, + "(7848, '2024-11-25', 7)": 0, + "(7848, '2024-11-26', 0)": 0, + "(7848, '2024-11-26', 1)": 0, + "(7848, '2024-11-26', 2)": 0, + "(7848, '2024-11-26', 3)": 0, + "(7848, '2024-11-26', 4)": 0, + "(7848, '2024-11-26', 5)": 0, + "(7848, '2024-11-26', 6)": 0, + "(7848, '2024-11-26', 7)": 0, + "(7848, '2024-11-27', 0)": 0, + "(7848, '2024-11-27', 1)": 0, + "(7848, '2024-11-27', 2)": 0, + "(7848, '2024-11-27', 3)": 0, + "(7848, '2024-11-27', 4)": 0, + "(7848, '2024-11-27', 5)": 0, + "(7848, '2024-11-27', 6)": 0, + "(7848, '2024-11-27', 7)": 0, + "(7848, '2024-11-28', 0)": 0, + "(7848, '2024-11-28', 1)": 1, + "(7848, '2024-11-28', 2)": 0, + "(7848, '2024-11-28', 3)": 0, + "(7848, '2024-11-28', 4)": 0, + "(7848, '2024-11-28', 5)": 0, + "(7848, '2024-11-28', 6)": 0, + "(7848, '2024-11-28', 7)": 0, + "(7848, '2024-11-29', 0)": 0, + "(7848, '2024-11-29', 1)": 0, + "(7848, '2024-11-29', 2)": 0, + "(7848, '2024-11-29', 3)": 0, + "(7848, '2024-11-29', 4)": 0, + "(7848, '2024-11-29', 5)": 0, + "(7848, '2024-11-29', 6)": 0, + "(7848, '2024-11-29', 7)": 0, + "(7848, '2024-11-30', 0)": 1, + "(7848, '2024-11-30', 1)": 0, + "(7848, '2024-11-30', 2)": 0, + "(7848, '2024-11-30', 3)": 0, + "(7848, '2024-11-30', 4)": 0, + "(7848, '2024-11-30', 5)": 0, + "(7848, '2024-11-30', 6)": 0, + "(7848, '2024-11-30', 7)": 0, + "(7877, '2024-11-01', 0)": 0, + "(7877, '2024-11-01', 1)": 0, + "(7877, '2024-11-01', 2)": 0, + "(7877, '2024-11-01', 3)": 0, + "(7877, '2024-11-01', 4)": 0, + "(7877, '2024-11-01', 5)": 0, + "(7877, '2024-11-01', 6)": 0, + "(7877, '2024-11-01', 7)": 0, + "(7877, '2024-11-02', 0)": 0, + "(7877, '2024-11-02', 1)": 0, + "(7877, '2024-11-02', 2)": 0, + "(7877, '2024-11-02', 3)": 0, + "(7877, '2024-11-02', 4)": 0, + "(7877, '2024-11-02', 5)": 0, + "(7877, '2024-11-02', 6)": 0, + "(7877, '2024-11-02', 7)": 0, + "(7877, '2024-11-03', 0)": 0, + "(7877, '2024-11-03', 1)": 0, + "(7877, '2024-11-03', 2)": 0, + "(7877, '2024-11-03', 3)": 0, + "(7877, '2024-11-03', 4)": 0, + "(7877, '2024-11-03', 5)": 0, + "(7877, '2024-11-03', 6)": 0, + "(7877, '2024-11-03', 7)": 0, + "(7877, '2024-11-04', 0)": 0, + "(7877, '2024-11-04', 1)": 0, + "(7877, '2024-11-04', 2)": 0, + "(7877, '2024-11-04', 3)": 0, + "(7877, '2024-11-04', 4)": 0, + "(7877, '2024-11-04', 5)": 0, + "(7877, '2024-11-04', 6)": 0, + "(7877, '2024-11-04', 7)": 0, + "(7877, '2024-11-05', 0)": 0, + "(7877, '2024-11-05', 1)": 0, + "(7877, '2024-11-05', 2)": 0, + "(7877, '2024-11-05', 3)": 0, + "(7877, '2024-11-05', 4)": 0, + "(7877, '2024-11-05', 5)": 0, + "(7877, '2024-11-05', 6)": 0, + "(7877, '2024-11-05', 7)": 0, + "(7877, '2024-11-06', 0)": 0, + "(7877, '2024-11-06', 1)": 0, + "(7877, '2024-11-06', 2)": 0, + "(7877, '2024-11-06', 3)": 0, + "(7877, '2024-11-06', 4)": 0, + "(7877, '2024-11-06', 5)": 0, + "(7877, '2024-11-06', 6)": 0, + "(7877, '2024-11-06', 7)": 0, + "(7877, '2024-11-07', 0)": 0, + "(7877, '2024-11-07', 1)": 0, + "(7877, '2024-11-07', 2)": 0, + "(7877, '2024-11-07', 3)": 0, + "(7877, '2024-11-07', 4)": 0, + "(7877, '2024-11-07', 5)": 0, + "(7877, '2024-11-07', 6)": 0, + "(7877, '2024-11-07', 7)": 0, + "(7877, '2024-11-08', 0)": 0, + "(7877, '2024-11-08', 1)": 0, + "(7877, '2024-11-08', 2)": 0, + "(7877, '2024-11-08', 3)": 0, + "(7877, '2024-11-08', 4)": 0, + "(7877, '2024-11-08', 5)": 0, + "(7877, '2024-11-08', 6)": 0, + "(7877, '2024-11-08', 7)": 0, + "(7877, '2024-11-09', 0)": 0, + "(7877, '2024-11-09', 1)": 0, + "(7877, '2024-11-09', 2)": 1, + "(7877, '2024-11-09', 3)": 0, + "(7877, '2024-11-09', 4)": 0, + "(7877, '2024-11-09', 5)": 0, + "(7877, '2024-11-09', 6)": 0, + "(7877, '2024-11-09', 7)": 0, + "(7877, '2024-11-10', 0)": 0, + "(7877, '2024-11-10', 1)": 0, + "(7877, '2024-11-10', 2)": 1, + "(7877, '2024-11-10', 3)": 0, + "(7877, '2024-11-10', 4)": 0, + "(7877, '2024-11-10', 5)": 0, + "(7877, '2024-11-10', 6)": 0, + "(7877, '2024-11-10', 7)": 0, + "(7877, '2024-11-11', 0)": 0, + "(7877, '2024-11-11', 1)": 0, + "(7877, '2024-11-11', 2)": 1, + "(7877, '2024-11-11', 3)": 0, + "(7877, '2024-11-11', 4)": 0, + "(7877, '2024-11-11', 5)": 0, + "(7877, '2024-11-11', 6)": 0, + "(7877, '2024-11-11', 7)": 0, + "(7877, '2024-11-12', 0)": 0, + "(7877, '2024-11-12', 1)": 0, + "(7877, '2024-11-12', 2)": 0, + "(7877, '2024-11-12', 3)": 0, + "(7877, '2024-11-12', 4)": 0, + "(7877, '2024-11-12', 5)": 0, + "(7877, '2024-11-12', 6)": 0, + "(7877, '2024-11-12', 7)": 0, + "(7877, '2024-11-13', 0)": 0, + "(7877, '2024-11-13', 1)": 1, + "(7877, '2024-11-13', 2)": 0, + "(7877, '2024-11-13', 3)": 0, + "(7877, '2024-11-13', 4)": 0, + "(7877, '2024-11-13', 5)": 0, + "(7877, '2024-11-13', 6)": 0, + "(7877, '2024-11-13', 7)": 0, + "(7877, '2024-11-14', 0)": 0, + "(7877, '2024-11-14', 1)": 1, + "(7877, '2024-11-14', 2)": 0, + "(7877, '2024-11-14', 3)": 0, + "(7877, '2024-11-14', 4)": 0, + "(7877, '2024-11-14', 5)": 0, + "(7877, '2024-11-14', 6)": 0, + "(7877, '2024-11-14', 7)": 0, + "(7877, '2024-11-15', 0)": 1, + "(7877, '2024-11-15', 1)": 0, + "(7877, '2024-11-15', 2)": 0, + "(7877, '2024-11-15', 3)": 0, + "(7877, '2024-11-15', 4)": 0, + "(7877, '2024-11-15', 5)": 0, + "(7877, '2024-11-15', 6)": 0, + "(7877, '2024-11-15', 7)": 0, + "(7877, '2024-11-16', 0)": 1, + "(7877, '2024-11-16', 1)": 0, + "(7877, '2024-11-16', 2)": 0, + "(7877, '2024-11-16', 3)": 0, + "(7877, '2024-11-16', 4)": 0, + "(7877, '2024-11-16', 5)": 0, + "(7877, '2024-11-16', 6)": 0, + "(7877, '2024-11-16', 7)": 0, + "(7877, '2024-11-17', 0)": 1, + "(7877, '2024-11-17', 1)": 0, + "(7877, '2024-11-17', 2)": 0, + "(7877, '2024-11-17', 3)": 0, + "(7877, '2024-11-17', 4)": 0, + "(7877, '2024-11-17', 5)": 0, + "(7877, '2024-11-17', 6)": 0, + "(7877, '2024-11-17', 7)": 0, + "(7877, '2024-11-18', 0)": 0, + "(7877, '2024-11-18', 1)": 0, + "(7877, '2024-11-18', 2)": 1, + "(7877, '2024-11-18', 3)": 0, + "(7877, '2024-11-18', 4)": 0, + "(7877, '2024-11-18', 5)": 0, + "(7877, '2024-11-18', 6)": 0, + "(7877, '2024-11-18', 7)": 0, + "(7877, '2024-11-19', 0)": 0, + "(7877, '2024-11-19', 1)": 0, + "(7877, '2024-11-19', 2)": 1, + "(7877, '2024-11-19', 3)": 0, + "(7877, '2024-11-19', 4)": 0, + "(7877, '2024-11-19', 5)": 0, + "(7877, '2024-11-19', 6)": 0, + "(7877, '2024-11-19', 7)": 0, + "(7877, '2024-11-20', 0)": 0, + "(7877, '2024-11-20', 1)": 0, + "(7877, '2024-11-20', 2)": 0, + "(7877, '2024-11-20', 3)": 0, + "(7877, '2024-11-20', 4)": 0, + "(7877, '2024-11-20', 5)": 0, + "(7877, '2024-11-20', 6)": 0, + "(7877, '2024-11-20', 7)": 0, + "(7877, '2024-11-21', 0)": 0, + "(7877, '2024-11-21', 1)": 0, + "(7877, '2024-11-21', 2)": 0, + "(7877, '2024-11-21', 3)": 0, + "(7877, '2024-11-21', 4)": 0, + "(7877, '2024-11-21', 5)": 0, + "(7877, '2024-11-21', 6)": 0, + "(7877, '2024-11-21', 7)": 0, + "(7877, '2024-11-22', 0)": 0, + "(7877, '2024-11-22', 1)": 0, + "(7877, '2024-11-22', 2)": 0, + "(7877, '2024-11-22', 3)": 0, + "(7877, '2024-11-22', 4)": 0, + "(7877, '2024-11-22', 5)": 0, + "(7877, '2024-11-22', 6)": 0, + "(7877, '2024-11-22', 7)": 0, + "(7877, '2024-11-23', 0)": 0, + "(7877, '2024-11-23', 1)": 1, + "(7877, '2024-11-23', 2)": 0, + "(7877, '2024-11-23', 3)": 0, + "(7877, '2024-11-23', 4)": 0, + "(7877, '2024-11-23', 5)": 0, + "(7877, '2024-11-23', 6)": 0, + "(7877, '2024-11-23', 7)": 0, + "(7877, '2024-11-24', 0)": 0, + "(7877, '2024-11-24', 1)": 0, + "(7877, '2024-11-24', 2)": 1, + "(7877, '2024-11-24', 3)": 0, + "(7877, '2024-11-24', 4)": 0, + "(7877, '2024-11-24', 5)": 0, + "(7877, '2024-11-24', 6)": 0, + "(7877, '2024-11-24', 7)": 0, + "(7877, '2024-11-25', 0)": 0, + "(7877, '2024-11-25', 1)": 0, + "(7877, '2024-11-25', 2)": 0, + "(7877, '2024-11-25', 3)": 0, + "(7877, '2024-11-25', 4)": 0, + "(7877, '2024-11-25', 5)": 0, + "(7877, '2024-11-25', 6)": 0, + "(7877, '2024-11-25', 7)": 0, + "(7877, '2024-11-26', 0)": 0, + "(7877, '2024-11-26', 1)": 1, + "(7877, '2024-11-26', 2)": 0, + "(7877, '2024-11-26', 3)": 0, + "(7877, '2024-11-26', 4)": 0, + "(7877, '2024-11-26', 5)": 0, + "(7877, '2024-11-26', 6)": 0, + "(7877, '2024-11-26', 7)": 0, + "(7877, '2024-11-27', 0)": 0, + "(7877, '2024-11-27', 1)": 0, + "(7877, '2024-11-27', 2)": 0, + "(7877, '2024-11-27', 3)": 0, + "(7877, '2024-11-27', 4)": 0, + "(7877, '2024-11-27', 5)": 0, + "(7877, '2024-11-27', 6)": 0, + "(7877, '2024-11-27', 7)": 0, + "(7877, '2024-11-28', 0)": 0, + "(7877, '2024-11-28', 1)": 0, + "(7877, '2024-11-28', 2)": 1, + "(7877, '2024-11-28', 3)": 0, + "(7877, '2024-11-28', 4)": 0, + "(7877, '2024-11-28', 5)": 0, + "(7877, '2024-11-28', 6)": 0, + "(7877, '2024-11-28', 7)": 0, + "(7877, '2024-11-29', 0)": 0, + "(7877, '2024-11-29', 1)": 0, + "(7877, '2024-11-29', 2)": 0, + "(7877, '2024-11-29', 3)": 0, + "(7877, '2024-11-29', 4)": 0, + "(7877, '2024-11-29', 5)": 0, + "(7877, '2024-11-29', 6)": 0, + "(7877, '2024-11-29', 7)": 0, + "(7877, '2024-11-30', 0)": 0, + "(7877, '2024-11-30', 1)": 0, + "(7877, '2024-11-30', 2)": 0, + "(7877, '2024-11-30', 3)": 0, + "(7877, '2024-11-30', 4)": 0, + "(7877, '2024-11-30', 5)": 0, + "(7877, '2024-11-30', 6)": 0, + "(7877, '2024-11-30', 7)": 0, + "(790, '2024-11-01', 0)": 0, + "(790, '2024-11-01', 1)": 0, + "(790, '2024-11-01', 2)": 0, + "(790, '2024-11-01', 3)": 0, + "(790, '2024-11-01', 4)": 0, + "(790, '2024-11-01', 5)": 0, + "(790, '2024-11-01', 6)": 0, + "(790, '2024-11-01', 7)": 0, + "(790, '2024-11-02', 0)": 0, + "(790, '2024-11-02', 1)": 0, + "(790, '2024-11-02', 2)": 0, + "(790, '2024-11-02', 3)": 0, + "(790, '2024-11-02', 4)": 0, + "(790, '2024-11-02', 5)": 0, + "(790, '2024-11-02', 6)": 0, + "(790, '2024-11-02', 7)": 0, + "(790, '2024-11-03', 0)": 0, + "(790, '2024-11-03', 1)": 0, + "(790, '2024-11-03', 2)": 0, + "(790, '2024-11-03', 3)": 0, + "(790, '2024-11-03', 4)": 0, + "(790, '2024-11-03', 5)": 0, + "(790, '2024-11-03', 6)": 0, + "(790, '2024-11-03', 7)": 0, + "(790, '2024-11-04', 0)": 0, + "(790, '2024-11-04', 1)": 0, + "(790, '2024-11-04', 2)": 0, + "(790, '2024-11-04', 3)": 0, + "(790, '2024-11-04', 4)": 0, + "(790, '2024-11-04', 5)": 0, + "(790, '2024-11-04', 6)": 0, + "(790, '2024-11-04', 7)": 0, + "(790, '2024-11-05', 0)": 0, + "(790, '2024-11-05', 1)": 0, + "(790, '2024-11-05', 2)": 0, + "(790, '2024-11-05', 3)": 0, + "(790, '2024-11-05', 4)": 0, + "(790, '2024-11-05', 5)": 0, + "(790, '2024-11-05', 6)": 0, + "(790, '2024-11-05', 7)": 0, + "(790, '2024-11-06', 0)": 0, + "(790, '2024-11-06', 1)": 0, + "(790, '2024-11-06', 2)": 0, + "(790, '2024-11-06', 3)": 0, + "(790, '2024-11-06', 4)": 0, + "(790, '2024-11-06', 5)": 0, + "(790, '2024-11-06', 6)": 0, + "(790, '2024-11-06', 7)": 0, + "(790, '2024-11-07', 0)": 0, + "(790, '2024-11-07', 1)": 0, + "(790, '2024-11-07', 2)": 0, + "(790, '2024-11-07', 3)": 0, + "(790, '2024-11-07', 4)": 0, + "(790, '2024-11-07', 5)": 0, + "(790, '2024-11-07', 6)": 0, + "(790, '2024-11-07', 7)": 0, + "(790, '2024-11-08', 0)": 0, + "(790, '2024-11-08', 1)": 0, + "(790, '2024-11-08', 2)": 0, + "(790, '2024-11-08', 3)": 0, + "(790, '2024-11-08', 4)": 0, + "(790, '2024-11-08', 5)": 0, + "(790, '2024-11-08', 6)": 0, + "(790, '2024-11-08', 7)": 0, + "(790, '2024-11-09', 0)": 0, + "(790, '2024-11-09', 1)": 0, + "(790, '2024-11-09', 2)": 0, + "(790, '2024-11-09', 3)": 0, + "(790, '2024-11-09', 4)": 0, + "(790, '2024-11-09', 5)": 0, + "(790, '2024-11-09', 6)": 0, + "(790, '2024-11-09', 7)": 0, + "(790, '2024-11-10', 0)": 0, + "(790, '2024-11-10', 1)": 0, + "(790, '2024-11-10', 2)": 0, + "(790, '2024-11-10', 3)": 0, + "(790, '2024-11-10', 4)": 0, + "(790, '2024-11-10', 5)": 0, + "(790, '2024-11-10', 6)": 0, + "(790, '2024-11-10', 7)": 0, + "(790, '2024-11-11', 0)": 0, + "(790, '2024-11-11', 1)": 0, + "(790, '2024-11-11', 2)": 0, + "(790, '2024-11-11', 3)": 0, + "(790, '2024-11-11', 4)": 0, + "(790, '2024-11-11', 5)": 0, + "(790, '2024-11-11', 6)": 0, + "(790, '2024-11-11', 7)": 0, + "(790, '2024-11-12', 0)": 0, + "(790, '2024-11-12', 1)": 0, + "(790, '2024-11-12', 2)": 0, + "(790, '2024-11-12', 3)": 0, + "(790, '2024-11-12', 4)": 0, + "(790, '2024-11-12', 5)": 0, + "(790, '2024-11-12', 6)": 0, + "(790, '2024-11-12', 7)": 0, + "(790, '2024-11-13', 0)": 0, + "(790, '2024-11-13', 1)": 0, + "(790, '2024-11-13', 2)": 0, + "(790, '2024-11-13', 3)": 0, + "(790, '2024-11-13', 4)": 0, + "(790, '2024-11-13', 5)": 0, + "(790, '2024-11-13', 6)": 0, + "(790, '2024-11-13', 7)": 0, + "(790, '2024-11-14', 0)": 0, + "(790, '2024-11-14', 1)": 0, + "(790, '2024-11-14', 2)": 0, + "(790, '2024-11-14', 3)": 0, + "(790, '2024-11-14', 4)": 0, + "(790, '2024-11-14', 5)": 0, + "(790, '2024-11-14', 6)": 0, + "(790, '2024-11-14', 7)": 0, + "(790, '2024-11-15', 0)": 0, + "(790, '2024-11-15', 1)": 0, + "(790, '2024-11-15', 2)": 0, + "(790, '2024-11-15', 3)": 0, + "(790, '2024-11-15', 4)": 0, + "(790, '2024-11-15', 5)": 0, + "(790, '2024-11-15', 6)": 0, + "(790, '2024-11-15', 7)": 0, + "(790, '2024-11-16', 0)": 0, + "(790, '2024-11-16', 1)": 0, + "(790, '2024-11-16', 2)": 0, + "(790, '2024-11-16', 3)": 0, + "(790, '2024-11-16', 4)": 0, + "(790, '2024-11-16', 5)": 0, + "(790, '2024-11-16', 6)": 0, + "(790, '2024-11-16', 7)": 0, + "(790, '2024-11-17', 0)": 0, + "(790, '2024-11-17', 1)": 0, + "(790, '2024-11-17', 2)": 0, + "(790, '2024-11-17', 3)": 0, + "(790, '2024-11-17', 4)": 0, + "(790, '2024-11-17', 5)": 0, + "(790, '2024-11-17', 6)": 0, + "(790, '2024-11-17', 7)": 0, + "(790, '2024-11-18', 0)": 0, + "(790, '2024-11-18', 1)": 0, + "(790, '2024-11-18', 2)": 0, + "(790, '2024-11-18', 3)": 0, + "(790, '2024-11-18', 4)": 0, + "(790, '2024-11-18', 5)": 0, + "(790, '2024-11-18', 6)": 0, + "(790, '2024-11-18', 7)": 0, + "(790, '2024-11-19', 0)": 0, + "(790, '2024-11-19', 1)": 0, + "(790, '2024-11-19', 2)": 0, + "(790, '2024-11-19', 3)": 0, + "(790, '2024-11-19', 4)": 0, + "(790, '2024-11-19', 5)": 0, + "(790, '2024-11-19', 6)": 0, + "(790, '2024-11-19', 7)": 0, + "(790, '2024-11-20', 0)": 0, + "(790, '2024-11-20', 1)": 0, + "(790, '2024-11-20', 2)": 0, + "(790, '2024-11-20', 3)": 0, + "(790, '2024-11-20', 4)": 0, + "(790, '2024-11-20', 5)": 0, + "(790, '2024-11-20', 6)": 0, + "(790, '2024-11-20', 7)": 0, + "(790, '2024-11-21', 0)": 0, + "(790, '2024-11-21', 1)": 0, + "(790, '2024-11-21', 2)": 0, + "(790, '2024-11-21', 3)": 0, + "(790, '2024-11-21', 4)": 0, + "(790, '2024-11-21', 5)": 0, + "(790, '2024-11-21', 6)": 0, + "(790, '2024-11-21', 7)": 0, + "(790, '2024-11-22', 0)": 0, + "(790, '2024-11-22', 1)": 0, + "(790, '2024-11-22', 2)": 0, + "(790, '2024-11-22', 3)": 0, + "(790, '2024-11-22', 4)": 0, + "(790, '2024-11-22', 5)": 0, + "(790, '2024-11-22', 6)": 0, + "(790, '2024-11-22', 7)": 0, + "(790, '2024-11-23', 0)": 0, + "(790, '2024-11-23', 1)": 0, + "(790, '2024-11-23', 2)": 0, + "(790, '2024-11-23', 3)": 0, + "(790, '2024-11-23', 4)": 0, + "(790, '2024-11-23', 5)": 0, + "(790, '2024-11-23', 6)": 0, + "(790, '2024-11-23', 7)": 0, + "(790, '2024-11-24', 0)": 0, + "(790, '2024-11-24', 1)": 0, + "(790, '2024-11-24', 2)": 0, + "(790, '2024-11-24', 3)": 0, + "(790, '2024-11-24', 4)": 0, + "(790, '2024-11-24', 5)": 0, + "(790, '2024-11-24', 6)": 0, + "(790, '2024-11-24', 7)": 0, + "(790, '2024-11-25', 0)": 0, + "(790, '2024-11-25', 1)": 0, + "(790, '2024-11-25', 2)": 0, + "(790, '2024-11-25', 3)": 0, + "(790, '2024-11-25', 4)": 0, + "(790, '2024-11-25', 5)": 0, + "(790, '2024-11-25', 6)": 0, + "(790, '2024-11-25', 7)": 0, + "(790, '2024-11-26', 0)": 0, + "(790, '2024-11-26', 1)": 0, + "(790, '2024-11-26', 2)": 0, + "(790, '2024-11-26', 3)": 0, + "(790, '2024-11-26', 4)": 0, + "(790, '2024-11-26', 5)": 0, + "(790, '2024-11-26', 6)": 0, + "(790, '2024-11-26', 7)": 0, + "(790, '2024-11-27', 0)": 0, + "(790, '2024-11-27', 1)": 0, + "(790, '2024-11-27', 2)": 0, + "(790, '2024-11-27', 3)": 0, + "(790, '2024-11-27', 4)": 0, + "(790, '2024-11-27', 5)": 0, + "(790, '2024-11-27', 6)": 0, + "(790, '2024-11-27', 7)": 0, + "(790, '2024-11-28', 0)": 0, + "(790, '2024-11-28', 1)": 0, + "(790, '2024-11-28', 2)": 0, + "(790, '2024-11-28', 3)": 0, + "(790, '2024-11-28', 4)": 0, + "(790, '2024-11-28', 5)": 0, + "(790, '2024-11-28', 6)": 0, + "(790, '2024-11-28', 7)": 0, + "(790, '2024-11-29', 0)": 0, + "(790, '2024-11-29', 1)": 0, + "(790, '2024-11-29', 2)": 0, + "(790, '2024-11-29', 3)": 0, + "(790, '2024-11-29', 4)": 0, + "(790, '2024-11-29', 5)": 0, + "(790, '2024-11-29', 6)": 0, + "(790, '2024-11-29', 7)": 0, + "(790, '2024-11-30', 0)": 0, + "(790, '2024-11-30', 1)": 0, + "(790, '2024-11-30', 2)": 0, + "(790, '2024-11-30', 3)": 0, + "(790, '2024-11-30', 4)": 0, + "(790, '2024-11-30', 5)": 0, + "(790, '2024-11-30', 6)": 0, + "(790, '2024-11-30', 7)": 0, + "(791, '2024-11-01', 0)": 0, + "(791, '2024-11-01', 1)": 0, + "(791, '2024-11-01', 2)": 1, + "(791, '2024-11-01', 3)": 0, + "(791, '2024-11-01', 4)": 0, + "(791, '2024-11-01', 5)": 0, + "(791, '2024-11-01', 6)": 0, + "(791, '2024-11-01', 7)": 0, + "(791, '2024-11-02', 0)": 0, + "(791, '2024-11-02', 1)": 0, + "(791, '2024-11-02', 2)": 0, + "(791, '2024-11-02', 3)": 0, + "(791, '2024-11-02', 4)": 0, + "(791, '2024-11-02', 5)": 0, + "(791, '2024-11-02', 6)": 0, + "(791, '2024-11-02', 7)": 0, + "(791, '2024-11-03', 0)": 0, + "(791, '2024-11-03', 1)": 0, + "(791, '2024-11-03', 2)": 0, + "(791, '2024-11-03', 3)": 0, + "(791, '2024-11-03', 4)": 0, + "(791, '2024-11-03', 5)": 0, + "(791, '2024-11-03', 6)": 0, + "(791, '2024-11-03', 7)": 0, + "(791, '2024-11-04', 0)": 0, + "(791, '2024-11-04', 1)": 0, + "(791, '2024-11-04', 2)": 1, + "(791, '2024-11-04', 3)": 0, + "(791, '2024-11-04', 4)": 0, + "(791, '2024-11-04', 5)": 0, + "(791, '2024-11-04', 6)": 0, + "(791, '2024-11-04', 7)": 0, + "(791, '2024-11-05', 0)": 0, + "(791, '2024-11-05', 1)": 0, + "(791, '2024-11-05', 2)": 0, + "(791, '2024-11-05', 3)": 0, + "(791, '2024-11-05', 4)": 0, + "(791, '2024-11-05', 5)": 0, + "(791, '2024-11-05', 6)": 0, + "(791, '2024-11-05', 7)": 0, + "(791, '2024-11-06', 0)": 0, + "(791, '2024-11-06', 1)": 0, + "(791, '2024-11-06', 2)": 1, + "(791, '2024-11-06', 3)": 0, + "(791, '2024-11-06', 4)": 0, + "(791, '2024-11-06', 5)": 0, + "(791, '2024-11-06', 6)": 0, + "(791, '2024-11-06', 7)": 0, + "(791, '2024-11-07', 0)": 0, + "(791, '2024-11-07', 1)": 0, + "(791, '2024-11-07', 2)": 0, + "(791, '2024-11-07', 3)": 0, + "(791, '2024-11-07', 4)": 0, + "(791, '2024-11-07', 5)": 0, + "(791, '2024-11-07', 6)": 0, + "(791, '2024-11-07', 7)": 0, + "(791, '2024-11-08', 0)": 0, + "(791, '2024-11-08', 1)": 1, + "(791, '2024-11-08', 2)": 0, + "(791, '2024-11-08', 3)": 0, + "(791, '2024-11-08', 4)": 0, + "(791, '2024-11-08', 5)": 0, + "(791, '2024-11-08', 6)": 0, + "(791, '2024-11-08', 7)": 0, + "(791, '2024-11-09', 0)": 0, + "(791, '2024-11-09', 1)": 0, + "(791, '2024-11-09', 2)": 0, + "(791, '2024-11-09', 3)": 0, + "(791, '2024-11-09', 4)": 0, + "(791, '2024-11-09', 5)": 0, + "(791, '2024-11-09', 6)": 0, + "(791, '2024-11-09', 7)": 0, + "(791, '2024-11-10', 0)": 0, + "(791, '2024-11-10', 1)": 0, + "(791, '2024-11-10', 2)": 0, + "(791, '2024-11-10', 3)": 0, + "(791, '2024-11-10', 4)": 0, + "(791, '2024-11-10', 5)": 0, + "(791, '2024-11-10', 6)": 0, + "(791, '2024-11-10', 7)": 0, + "(791, '2024-11-11', 0)": 1, + "(791, '2024-11-11', 1)": 0, + "(791, '2024-11-11', 2)": 0, + "(791, '2024-11-11', 3)": 0, + "(791, '2024-11-11', 4)": 0, + "(791, '2024-11-11', 5)": 0, + "(791, '2024-11-11', 6)": 0, + "(791, '2024-11-11', 7)": 0, + "(791, '2024-11-12', 0)": 1, + "(791, '2024-11-12', 1)": 0, + "(791, '2024-11-12', 2)": 0, + "(791, '2024-11-12', 3)": 0, + "(791, '2024-11-12', 4)": 0, + "(791, '2024-11-12', 5)": 0, + "(791, '2024-11-12', 6)": 0, + "(791, '2024-11-12', 7)": 0, + "(791, '2024-11-13', 0)": 0, + "(791, '2024-11-13', 1)": 1, + "(791, '2024-11-13', 2)": 0, + "(791, '2024-11-13', 3)": 0, + "(791, '2024-11-13', 4)": 0, + "(791, '2024-11-13', 5)": 0, + "(791, '2024-11-13', 6)": 0, + "(791, '2024-11-13', 7)": 0, + "(791, '2024-11-14', 0)": 0, + "(791, '2024-11-14', 1)": 0, + "(791, '2024-11-14', 2)": 0, + "(791, '2024-11-14', 3)": 0, + "(791, '2024-11-14', 4)": 0, + "(791, '2024-11-14', 5)": 0, + "(791, '2024-11-14', 6)": 0, + "(791, '2024-11-14', 7)": 0, + "(791, '2024-11-15', 0)": 1, + "(791, '2024-11-15', 1)": 0, + "(791, '2024-11-15', 2)": 0, + "(791, '2024-11-15', 3)": 0, + "(791, '2024-11-15', 4)": 0, + "(791, '2024-11-15', 5)": 0, + "(791, '2024-11-15', 6)": 0, + "(791, '2024-11-15', 7)": 0, + "(791, '2024-11-16', 0)": 0, + "(791, '2024-11-16', 1)": 0, + "(791, '2024-11-16', 2)": 0, + "(791, '2024-11-16', 3)": 0, + "(791, '2024-11-16', 4)": 0, + "(791, '2024-11-16', 5)": 0, + "(791, '2024-11-16', 6)": 0, + "(791, '2024-11-16', 7)": 0, + "(791, '2024-11-17', 0)": 0, + "(791, '2024-11-17', 1)": 0, + "(791, '2024-11-17', 2)": 0, + "(791, '2024-11-17', 3)": 0, + "(791, '2024-11-17', 4)": 0, + "(791, '2024-11-17', 5)": 0, + "(791, '2024-11-17', 6)": 0, + "(791, '2024-11-17', 7)": 0, + "(791, '2024-11-18', 0)": 0, + "(791, '2024-11-18', 1)": 0, + "(791, '2024-11-18', 2)": 0, + "(791, '2024-11-18', 3)": 0, + "(791, '2024-11-18', 4)": 0, + "(791, '2024-11-18', 5)": 0, + "(791, '2024-11-18', 6)": 0, + "(791, '2024-11-18', 7)": 0, + "(791, '2024-11-19', 0)": 0, + "(791, '2024-11-19', 1)": 0, + "(791, '2024-11-19', 2)": 1, + "(791, '2024-11-19', 3)": 0, + "(791, '2024-11-19', 4)": 0, + "(791, '2024-11-19', 5)": 0, + "(791, '2024-11-19', 6)": 0, + "(791, '2024-11-19', 7)": 0, + "(791, '2024-11-20', 0)": 0, + "(791, '2024-11-20', 1)": 0, + "(791, '2024-11-20', 2)": 0, + "(791, '2024-11-20', 3)": 1, + "(791, '2024-11-20', 4)": 0, + "(791, '2024-11-20', 5)": 0, + "(791, '2024-11-20', 6)": 0, + "(791, '2024-11-20', 7)": 0, + "(791, '2024-11-21', 0)": 0, + "(791, '2024-11-21', 1)": 0, + "(791, '2024-11-21', 2)": 0, + "(791, '2024-11-21', 3)": 0, + "(791, '2024-11-21', 4)": 0, + "(791, '2024-11-21', 5)": 0, + "(791, '2024-11-21', 6)": 0, + "(791, '2024-11-21', 7)": 0, + "(791, '2024-11-22', 0)": 1, + "(791, '2024-11-22', 1)": 0, + "(791, '2024-11-22', 2)": 0, + "(791, '2024-11-22', 3)": 0, + "(791, '2024-11-22', 4)": 0, + "(791, '2024-11-22', 5)": 0, + "(791, '2024-11-22', 6)": 0, + "(791, '2024-11-22', 7)": 0, + "(791, '2024-11-23', 0)": 0, + "(791, '2024-11-23', 1)": 0, + "(791, '2024-11-23', 2)": 0, + "(791, '2024-11-23', 3)": 0, + "(791, '2024-11-23', 4)": 0, + "(791, '2024-11-23', 5)": 0, + "(791, '2024-11-23', 6)": 0, + "(791, '2024-11-23', 7)": 0, + "(791, '2024-11-24', 0)": 0, + "(791, '2024-11-24', 1)": 0, + "(791, '2024-11-24', 2)": 0, + "(791, '2024-11-24', 3)": 0, + "(791, '2024-11-24', 4)": 0, + "(791, '2024-11-24', 5)": 0, + "(791, '2024-11-24', 6)": 0, + "(791, '2024-11-24', 7)": 0, + "(791, '2024-11-25', 0)": 0, + "(791, '2024-11-25', 1)": 0, + "(791, '2024-11-25', 2)": 0, + "(791, '2024-11-25', 3)": 1, + "(791, '2024-11-25', 4)": 0, + "(791, '2024-11-25', 5)": 0, + "(791, '2024-11-25', 6)": 0, + "(791, '2024-11-25', 7)": 0, + "(791, '2024-11-26', 0)": 0, + "(791, '2024-11-26', 1)": 0, + "(791, '2024-11-26', 2)": 0, + "(791, '2024-11-26', 3)": 1, + "(791, '2024-11-26', 4)": 0, + "(791, '2024-11-26', 5)": 0, + "(791, '2024-11-26', 6)": 0, + "(791, '2024-11-26', 7)": 0, + "(791, '2024-11-27', 0)": 0, + "(791, '2024-11-27', 1)": 0, + "(791, '2024-11-27', 2)": 0, + "(791, '2024-11-27', 3)": 0, + "(791, '2024-11-27', 4)": 0, + "(791, '2024-11-27', 5)": 0, + "(791, '2024-11-27', 6)": 0, + "(791, '2024-11-27', 7)": 0, + "(791, '2024-11-28', 0)": 1, + "(791, '2024-11-28', 1)": 0, + "(791, '2024-11-28', 2)": 0, + "(791, '2024-11-28', 3)": 0, + "(791, '2024-11-28', 4)": 0, + "(791, '2024-11-28', 5)": 0, + "(791, '2024-11-28', 6)": 0, + "(791, '2024-11-28', 7)": 0, + "(791, '2024-11-29', 0)": 0, + "(791, '2024-11-29', 1)": 0, + "(791, '2024-11-29', 2)": 1, + "(791, '2024-11-29', 3)": 0, + "(791, '2024-11-29', 4)": 0, + "(791, '2024-11-29', 5)": 0, + "(791, '2024-11-29', 6)": 0, + "(791, '2024-11-29', 7)": 0, + "(791, '2024-11-30', 0)": 0, + "(791, '2024-11-30', 1)": 0, + "(791, '2024-11-30', 2)": 0, + "(791, '2024-11-30', 3)": 0, + "(791, '2024-11-30', 4)": 0, + "(791, '2024-11-30', 5)": 0, + "(791, '2024-11-30', 6)": 0, + "(791, '2024-11-30', 7)": 0, + "(7919, '2024-11-01', 0)": 0, + "(7919, '2024-11-01', 1)": 0, + "(7919, '2024-11-01', 2)": 0, + "(7919, '2024-11-01', 3)": 0, + "(7919, '2024-11-01', 4)": 0, + "(7919, '2024-11-01', 5)": 0, + "(7919, '2024-11-01', 6)": 0, + "(7919, '2024-11-01', 7)": 0, + "(7919, '2024-11-02', 0)": 0, + "(7919, '2024-11-02', 1)": 0, + "(7919, '2024-11-02', 2)": 0, + "(7919, '2024-11-02', 3)": 0, + "(7919, '2024-11-02', 4)": 0, + "(7919, '2024-11-02', 5)": 0, + "(7919, '2024-11-02', 6)": 0, + "(7919, '2024-11-02', 7)": 0, + "(7919, '2024-11-03', 0)": 0, + "(7919, '2024-11-03', 1)": 0, + "(7919, '2024-11-03', 2)": 0, + "(7919, '2024-11-03', 3)": 0, + "(7919, '2024-11-03', 4)": 0, + "(7919, '2024-11-03', 5)": 0, + "(7919, '2024-11-03', 6)": 0, + "(7919, '2024-11-03', 7)": 0, + "(7919, '2024-11-04', 0)": 1, + "(7919, '2024-11-04', 1)": 0, + "(7919, '2024-11-04', 2)": 0, + "(7919, '2024-11-04', 3)": 0, + "(7919, '2024-11-04', 4)": 0, + "(7919, '2024-11-04', 5)": 0, + "(7919, '2024-11-04', 6)": 0, + "(7919, '2024-11-04', 7)": 0, + "(7919, '2024-11-05', 0)": 0, + "(7919, '2024-11-05', 1)": 0, + "(7919, '2024-11-05', 2)": 0, + "(7919, '2024-11-05', 3)": 1, + "(7919, '2024-11-05', 4)": 0, + "(7919, '2024-11-05', 5)": 0, + "(7919, '2024-11-05', 6)": 0, + "(7919, '2024-11-05', 7)": 0, + "(7919, '2024-11-06', 0)": 0, + "(7919, '2024-11-06', 1)": 0, + "(7919, '2024-11-06', 2)": 0, + "(7919, '2024-11-06', 3)": 1, + "(7919, '2024-11-06', 4)": 0, + "(7919, '2024-11-06', 5)": 0, + "(7919, '2024-11-06', 6)": 0, + "(7919, '2024-11-06', 7)": 0, + "(7919, '2024-11-07', 0)": 0, + "(7919, '2024-11-07', 1)": 0, + "(7919, '2024-11-07', 2)": 0, + "(7919, '2024-11-07', 3)": 0, + "(7919, '2024-11-07', 4)": 0, + "(7919, '2024-11-07', 5)": 0, + "(7919, '2024-11-07', 6)": 0, + "(7919, '2024-11-07', 7)": 0, + "(7919, '2024-11-08', 0)": 0, + "(7919, '2024-11-08', 1)": 0, + "(7919, '2024-11-08', 2)": 1, + "(7919, '2024-11-08', 3)": 0, + "(7919, '2024-11-08', 4)": 0, + "(7919, '2024-11-08', 5)": 0, + "(7919, '2024-11-08', 6)": 0, + "(7919, '2024-11-08', 7)": 0, + "(7919, '2024-11-09', 0)": 0, + "(7919, '2024-11-09', 1)": 0, + "(7919, '2024-11-09', 2)": 0, + "(7919, '2024-11-09', 3)": 0, + "(7919, '2024-11-09', 4)": 0, + "(7919, '2024-11-09', 5)": 0, + "(7919, '2024-11-09', 6)": 0, + "(7919, '2024-11-09', 7)": 0, + "(7919, '2024-11-10', 0)": 0, + "(7919, '2024-11-10', 1)": 0, + "(7919, '2024-11-10', 2)": 1, + "(7919, '2024-11-10', 3)": 0, + "(7919, '2024-11-10', 4)": 0, + "(7919, '2024-11-10', 5)": 0, + "(7919, '2024-11-10', 6)": 0, + "(7919, '2024-11-10', 7)": 0, + "(7919, '2024-11-11', 0)": 0, + "(7919, '2024-11-11', 1)": 0, + "(7919, '2024-11-11', 2)": 1, + "(7919, '2024-11-11', 3)": 0, + "(7919, '2024-11-11', 4)": 0, + "(7919, '2024-11-11', 5)": 0, + "(7919, '2024-11-11', 6)": 0, + "(7919, '2024-11-11', 7)": 0, + "(7919, '2024-11-12', 0)": 0, + "(7919, '2024-11-12', 1)": 1, + "(7919, '2024-11-12', 2)": 0, + "(7919, '2024-11-12', 3)": 0, + "(7919, '2024-11-12', 4)": 0, + "(7919, '2024-11-12', 5)": 0, + "(7919, '2024-11-12', 6)": 0, + "(7919, '2024-11-12', 7)": 0, + "(7919, '2024-11-13', 0)": 1, + "(7919, '2024-11-13', 1)": 0, + "(7919, '2024-11-13', 2)": 0, + "(7919, '2024-11-13', 3)": 0, + "(7919, '2024-11-13', 4)": 0, + "(7919, '2024-11-13', 5)": 0, + "(7919, '2024-11-13', 6)": 0, + "(7919, '2024-11-13', 7)": 0, + "(7919, '2024-11-14', 0)": 0, + "(7919, '2024-11-14', 1)": 1, + "(7919, '2024-11-14', 2)": 0, + "(7919, '2024-11-14', 3)": 0, + "(7919, '2024-11-14', 4)": 0, + "(7919, '2024-11-14', 5)": 0, + "(7919, '2024-11-14', 6)": 0, + "(7919, '2024-11-14', 7)": 0, + "(7919, '2024-11-15', 0)": 0, + "(7919, '2024-11-15', 1)": 0, + "(7919, '2024-11-15', 2)": 0, + "(7919, '2024-11-15', 3)": 1, + "(7919, '2024-11-15', 4)": 0, + "(7919, '2024-11-15', 5)": 0, + "(7919, '2024-11-15', 6)": 0, + "(7919, '2024-11-15', 7)": 0, + "(7919, '2024-11-16', 0)": 0, + "(7919, '2024-11-16', 1)": 0, + "(7919, '2024-11-16', 2)": 0, + "(7919, '2024-11-16', 3)": 0, + "(7919, '2024-11-16', 4)": 0, + "(7919, '2024-11-16', 5)": 0, + "(7919, '2024-11-16', 6)": 0, + "(7919, '2024-11-16', 7)": 0, + "(7919, '2024-11-17', 0)": 1, + "(7919, '2024-11-17', 1)": 0, + "(7919, '2024-11-17', 2)": 0, + "(7919, '2024-11-17', 3)": 0, + "(7919, '2024-11-17', 4)": 0, + "(7919, '2024-11-17', 5)": 0, + "(7919, '2024-11-17', 6)": 0, + "(7919, '2024-11-17', 7)": 0, + "(7919, '2024-11-18', 0)": 0, + "(7919, '2024-11-18', 1)": 0, + "(7919, '2024-11-18', 2)": 0, + "(7919, '2024-11-18', 3)": 0, + "(7919, '2024-11-18', 4)": 0, + "(7919, '2024-11-18', 5)": 0, + "(7919, '2024-11-18', 6)": 0, + "(7919, '2024-11-18', 7)": 0, + "(7919, '2024-11-19', 0)": 0, + "(7919, '2024-11-19', 1)": 0, + "(7919, '2024-11-19', 2)": 1, + "(7919, '2024-11-19', 3)": 0, + "(7919, '2024-11-19', 4)": 0, + "(7919, '2024-11-19', 5)": 0, + "(7919, '2024-11-19', 6)": 0, + "(7919, '2024-11-19', 7)": 0, + "(7919, '2024-11-20', 0)": 0, + "(7919, '2024-11-20', 1)": 0, + "(7919, '2024-11-20', 2)": 0, + "(7919, '2024-11-20', 3)": 0, + "(7919, '2024-11-20', 4)": 0, + "(7919, '2024-11-20', 5)": 0, + "(7919, '2024-11-20', 6)": 0, + "(7919, '2024-11-20', 7)": 0, + "(7919, '2024-11-21', 0)": 1, + "(7919, '2024-11-21', 1)": 0, + "(7919, '2024-11-21', 2)": 0, + "(7919, '2024-11-21', 3)": 0, + "(7919, '2024-11-21', 4)": 0, + "(7919, '2024-11-21', 5)": 0, + "(7919, '2024-11-21', 6)": 0, + "(7919, '2024-11-21', 7)": 0, + "(7919, '2024-11-22', 0)": 0, + "(7919, '2024-11-22', 1)": 0, + "(7919, '2024-11-22', 2)": 0, + "(7919, '2024-11-22', 3)": 1, + "(7919, '2024-11-22', 4)": 0, + "(7919, '2024-11-22', 5)": 0, + "(7919, '2024-11-22', 6)": 0, + "(7919, '2024-11-22', 7)": 0, + "(7919, '2024-11-23', 0)": 0, + "(7919, '2024-11-23', 1)": 0, + "(7919, '2024-11-23', 2)": 0, + "(7919, '2024-11-23', 3)": 0, + "(7919, '2024-11-23', 4)": 0, + "(7919, '2024-11-23', 5)": 0, + "(7919, '2024-11-23', 6)": 0, + "(7919, '2024-11-23', 7)": 0, + "(7919, '2024-11-24', 0)": 1, + "(7919, '2024-11-24', 1)": 0, + "(7919, '2024-11-24', 2)": 0, + "(7919, '2024-11-24', 3)": 0, + "(7919, '2024-11-24', 4)": 0, + "(7919, '2024-11-24', 5)": 0, + "(7919, '2024-11-24', 6)": 0, + "(7919, '2024-11-24', 7)": 0, + "(7919, '2024-11-25', 0)": 0, + "(7919, '2024-11-25', 1)": 1, + "(7919, '2024-11-25', 2)": 0, + "(7919, '2024-11-25', 3)": 0, + "(7919, '2024-11-25', 4)": 0, + "(7919, '2024-11-25', 5)": 0, + "(7919, '2024-11-25', 6)": 0, + "(7919, '2024-11-25', 7)": 0, + "(7919, '2024-11-26', 0)": 0, + "(7919, '2024-11-26', 1)": 0, + "(7919, '2024-11-26', 2)": 0, + "(7919, '2024-11-26', 3)": 0, + "(7919, '2024-11-26', 4)": 0, + "(7919, '2024-11-26', 5)": 0, + "(7919, '2024-11-26', 6)": 0, + "(7919, '2024-11-26', 7)": 0, + "(7919, '2024-11-27', 0)": 1, + "(7919, '2024-11-27', 1)": 0, + "(7919, '2024-11-27', 2)": 0, + "(7919, '2024-11-27', 3)": 0, + "(7919, '2024-11-27', 4)": 0, + "(7919, '2024-11-27', 5)": 0, + "(7919, '2024-11-27', 6)": 0, + "(7919, '2024-11-27', 7)": 0, + "(7919, '2024-11-28', 0)": 0, + "(7919, '2024-11-28', 1)": 1, + "(7919, '2024-11-28', 2)": 0, + "(7919, '2024-11-28', 3)": 0, + "(7919, '2024-11-28', 4)": 0, + "(7919, '2024-11-28', 5)": 0, + "(7919, '2024-11-28', 6)": 0, + "(7919, '2024-11-28', 7)": 0, + "(7919, '2024-11-29', 0)": 0, + "(7919, '2024-11-29', 1)": 0, + "(7919, '2024-11-29', 2)": 0, + "(7919, '2024-11-29', 3)": 0, + "(7919, '2024-11-29', 4)": 0, + "(7919, '2024-11-29', 5)": 0, + "(7919, '2024-11-29', 6)": 0, + "(7919, '2024-11-29', 7)": 0, + "(7919, '2024-11-30', 0)": 0, + "(7919, '2024-11-30', 1)": 1, + "(7919, '2024-11-30', 2)": 0, + "(7919, '2024-11-30', 3)": 0, + "(7919, '2024-11-30', 4)": 0, + "(7919, '2024-11-30', 5)": 0, + "(7919, '2024-11-30', 6)": 0, + "(7919, '2024-11-30', 7)": 0, + "(7990, '2024-11-01', 0)": 0, + "(7990, '2024-11-01', 1)": 0, + "(7990, '2024-11-01', 2)": 0, + "(7990, '2024-11-01', 3)": 0, + "(7990, '2024-11-01', 4)": 0, + "(7990, '2024-11-01', 5)": 0, + "(7990, '2024-11-01', 6)": 0, + "(7990, '2024-11-01', 7)": 0, + "(7990, '2024-11-02', 0)": 0, + "(7990, '2024-11-02', 1)": 0, + "(7990, '2024-11-02', 2)": 0, + "(7990, '2024-11-02', 3)": 0, + "(7990, '2024-11-02', 4)": 0, + "(7990, '2024-11-02', 5)": 0, + "(7990, '2024-11-02', 6)": 0, + "(7990, '2024-11-02', 7)": 0, + "(7990, '2024-11-03', 0)": 0, + "(7990, '2024-11-03', 1)": 0, + "(7990, '2024-11-03', 2)": 0, + "(7990, '2024-11-03', 3)": 0, + "(7990, '2024-11-03', 4)": 0, + "(7990, '2024-11-03', 5)": 0, + "(7990, '2024-11-03', 6)": 0, + "(7990, '2024-11-03', 7)": 0, + "(7990, '2024-11-04', 0)": 0, + "(7990, '2024-11-04', 1)": 0, + "(7990, '2024-11-04', 2)": 0, + "(7990, '2024-11-04', 3)": 0, + "(7990, '2024-11-04', 4)": 0, + "(7990, '2024-11-04', 5)": 0, + "(7990, '2024-11-04', 6)": 0, + "(7990, '2024-11-04', 7)": 0, + "(7990, '2024-11-05', 0)": 0, + "(7990, '2024-11-05', 1)": 0, + "(7990, '2024-11-05', 2)": 0, + "(7990, '2024-11-05', 3)": 0, + "(7990, '2024-11-05', 4)": 0, + "(7990, '2024-11-05', 5)": 0, + "(7990, '2024-11-05', 6)": 0, + "(7990, '2024-11-05', 7)": 0, + "(7990, '2024-11-06', 0)": 0, + "(7990, '2024-11-06', 1)": 0, + "(7990, '2024-11-06', 2)": 0, + "(7990, '2024-11-06', 3)": 0, + "(7990, '2024-11-06', 4)": 0, + "(7990, '2024-11-06', 5)": 0, + "(7990, '2024-11-06', 6)": 0, + "(7990, '2024-11-06', 7)": 0, + "(7990, '2024-11-07', 0)": 0, + "(7990, '2024-11-07', 1)": 0, + "(7990, '2024-11-07', 2)": 0, + "(7990, '2024-11-07', 3)": 0, + "(7990, '2024-11-07', 4)": 0, + "(7990, '2024-11-07', 5)": 0, + "(7990, '2024-11-07', 6)": 0, + "(7990, '2024-11-07', 7)": 0, + "(7990, '2024-11-08', 0)": 0, + "(7990, '2024-11-08', 1)": 0, + "(7990, '2024-11-08', 2)": 0, + "(7990, '2024-11-08', 3)": 0, + "(7990, '2024-11-08', 4)": 0, + "(7990, '2024-11-08', 5)": 0, + "(7990, '2024-11-08', 6)": 0, + "(7990, '2024-11-08', 7)": 0, + "(7990, '2024-11-09', 0)": 0, + "(7990, '2024-11-09', 1)": 0, + "(7990, '2024-11-09', 2)": 0, + "(7990, '2024-11-09', 3)": 0, + "(7990, '2024-11-09', 4)": 0, + "(7990, '2024-11-09', 5)": 0, + "(7990, '2024-11-09', 6)": 0, + "(7990, '2024-11-09', 7)": 0, + "(7990, '2024-11-10', 0)": 0, + "(7990, '2024-11-10', 1)": 0, + "(7990, '2024-11-10', 2)": 0, + "(7990, '2024-11-10', 3)": 0, + "(7990, '2024-11-10', 4)": 0, + "(7990, '2024-11-10', 5)": 0, + "(7990, '2024-11-10', 6)": 0, + "(7990, '2024-11-10', 7)": 0, + "(7990, '2024-11-11', 0)": 0, + "(7990, '2024-11-11', 1)": 0, + "(7990, '2024-11-11', 2)": 0, + "(7990, '2024-11-11', 3)": 0, + "(7990, '2024-11-11', 4)": 0, + "(7990, '2024-11-11', 5)": 0, + "(7990, '2024-11-11', 6)": 0, + "(7990, '2024-11-11', 7)": 0, + "(7990, '2024-11-12', 0)": 0, + "(7990, '2024-11-12', 1)": 0, + "(7990, '2024-11-12', 2)": 0, + "(7990, '2024-11-12', 3)": 0, + "(7990, '2024-11-12', 4)": 0, + "(7990, '2024-11-12', 5)": 0, + "(7990, '2024-11-12', 6)": 0, + "(7990, '2024-11-12', 7)": 0, + "(7990, '2024-11-13', 0)": 0, + "(7990, '2024-11-13', 1)": 0, + "(7990, '2024-11-13', 2)": 0, + "(7990, '2024-11-13', 3)": 0, + "(7990, '2024-11-13', 4)": 0, + "(7990, '2024-11-13', 5)": 0, + "(7990, '2024-11-13', 6)": 0, + "(7990, '2024-11-13', 7)": 0, + "(7990, '2024-11-14', 0)": 0, + "(7990, '2024-11-14', 1)": 0, + "(7990, '2024-11-14', 2)": 0, + "(7990, '2024-11-14', 3)": 0, + "(7990, '2024-11-14', 4)": 0, + "(7990, '2024-11-14', 5)": 0, + "(7990, '2024-11-14', 6)": 0, + "(7990, '2024-11-14', 7)": 0, + "(7990, '2024-11-15', 0)": 0, + "(7990, '2024-11-15', 1)": 0, + "(7990, '2024-11-15', 2)": 0, + "(7990, '2024-11-15', 3)": 0, + "(7990, '2024-11-15', 4)": 0, + "(7990, '2024-11-15', 5)": 0, + "(7990, '2024-11-15', 6)": 0, + "(7990, '2024-11-15', 7)": 0, + "(7990, '2024-11-16', 0)": 0, + "(7990, '2024-11-16', 1)": 0, + "(7990, '2024-11-16', 2)": 0, + "(7990, '2024-11-16', 3)": 0, + "(7990, '2024-11-16', 4)": 0, + "(7990, '2024-11-16', 5)": 0, + "(7990, '2024-11-16', 6)": 0, + "(7990, '2024-11-16', 7)": 0, + "(7990, '2024-11-17', 0)": 0, + "(7990, '2024-11-17', 1)": 0, + "(7990, '2024-11-17', 2)": 0, + "(7990, '2024-11-17', 3)": 0, + "(7990, '2024-11-17', 4)": 0, + "(7990, '2024-11-17', 5)": 0, + "(7990, '2024-11-17', 6)": 0, + "(7990, '2024-11-17', 7)": 0, + "(7990, '2024-11-18', 0)": 0, + "(7990, '2024-11-18', 1)": 0, + "(7990, '2024-11-18', 2)": 0, + "(7990, '2024-11-18', 3)": 0, + "(7990, '2024-11-18', 4)": 0, + "(7990, '2024-11-18', 5)": 0, + "(7990, '2024-11-18', 6)": 0, + "(7990, '2024-11-18', 7)": 0, + "(7990, '2024-11-19', 0)": 0, + "(7990, '2024-11-19', 1)": 0, + "(7990, '2024-11-19', 2)": 0, + "(7990, '2024-11-19', 3)": 0, + "(7990, '2024-11-19', 4)": 0, + "(7990, '2024-11-19', 5)": 0, + "(7990, '2024-11-19', 6)": 0, + "(7990, '2024-11-19', 7)": 0, + "(7990, '2024-11-20', 0)": 0, + "(7990, '2024-11-20', 1)": 0, + "(7990, '2024-11-20', 2)": 0, + "(7990, '2024-11-20', 3)": 0, + "(7990, '2024-11-20', 4)": 0, + "(7990, '2024-11-20', 5)": 0, + "(7990, '2024-11-20', 6)": 0, + "(7990, '2024-11-20', 7)": 0, + "(7990, '2024-11-21', 0)": 0, + "(7990, '2024-11-21', 1)": 0, + "(7990, '2024-11-21', 2)": 0, + "(7990, '2024-11-21', 3)": 0, + "(7990, '2024-11-21', 4)": 0, + "(7990, '2024-11-21', 5)": 0, + "(7990, '2024-11-21', 6)": 0, + "(7990, '2024-11-21', 7)": 0, + "(7990, '2024-11-22', 0)": 0, + "(7990, '2024-11-22', 1)": 0, + "(7990, '2024-11-22', 2)": 0, + "(7990, '2024-11-22', 3)": 0, + "(7990, '2024-11-22', 4)": 0, + "(7990, '2024-11-22', 5)": 0, + "(7990, '2024-11-22', 6)": 0, + "(7990, '2024-11-22', 7)": 0, + "(7990, '2024-11-23', 0)": 0, + "(7990, '2024-11-23', 1)": 0, + "(7990, '2024-11-23', 2)": 0, + "(7990, '2024-11-23', 3)": 0, + "(7990, '2024-11-23', 4)": 0, + "(7990, '2024-11-23', 5)": 0, + "(7990, '2024-11-23', 6)": 0, + "(7990, '2024-11-23', 7)": 0, + "(7990, '2024-11-24', 0)": 0, + "(7990, '2024-11-24', 1)": 0, + "(7990, '2024-11-24', 2)": 0, + "(7990, '2024-11-24', 3)": 0, + "(7990, '2024-11-24', 4)": 0, + "(7990, '2024-11-24', 5)": 0, + "(7990, '2024-11-24', 6)": 0, + "(7990, '2024-11-24', 7)": 0, + "(7990, '2024-11-25', 0)": 0, + "(7990, '2024-11-25', 1)": 0, + "(7990, '2024-11-25', 2)": 0, + "(7990, '2024-11-25', 3)": 0, + "(7990, '2024-11-25', 4)": 0, + "(7990, '2024-11-25', 5)": 0, + "(7990, '2024-11-25', 6)": 0, + "(7990, '2024-11-25', 7)": 0, + "(7990, '2024-11-26', 0)": 0, + "(7990, '2024-11-26', 1)": 0, + "(7990, '2024-11-26', 2)": 0, + "(7990, '2024-11-26', 3)": 0, + "(7990, '2024-11-26', 4)": 0, + "(7990, '2024-11-26', 5)": 0, + "(7990, '2024-11-26', 6)": 0, + "(7990, '2024-11-26', 7)": 0, + "(7990, '2024-11-27', 0)": 0, + "(7990, '2024-11-27', 1)": 0, + "(7990, '2024-11-27', 2)": 0, + "(7990, '2024-11-27', 3)": 0, + "(7990, '2024-11-27', 4)": 0, + "(7990, '2024-11-27', 5)": 0, + "(7990, '2024-11-27', 6)": 0, + "(7990, '2024-11-27', 7)": 0, + "(7990, '2024-11-28', 0)": 1, + "(7990, '2024-11-28', 1)": 0, + "(7990, '2024-11-28', 2)": 0, + "(7990, '2024-11-28', 3)": 0, + "(7990, '2024-11-28', 4)": 0, + "(7990, '2024-11-28', 5)": 0, + "(7990, '2024-11-28', 6)": 0, + "(7990, '2024-11-28', 7)": 0, + "(7990, '2024-11-29', 0)": 0, + "(7990, '2024-11-29', 1)": 0, + "(7990, '2024-11-29', 2)": 0, + "(7990, '2024-11-29', 3)": 0, + "(7990, '2024-11-29', 4)": 0, + "(7990, '2024-11-29', 5)": 0, + "(7990, '2024-11-29', 6)": 0, + "(7990, '2024-11-29', 7)": 0, + "(7990, '2024-11-30', 0)": 0, + "(7990, '2024-11-30', 1)": 0, + "(7990, '2024-11-30', 2)": 0, + "(7990, '2024-11-30', 3)": 0, + "(7990, '2024-11-30', 4)": 0, + "(7990, '2024-11-30', 5)": 0, + "(7990, '2024-11-30', 6)": 0, + "(7990, '2024-11-30', 7)": 0, + "(822, '2024-11-01', 0)": 0, + "(822, '2024-11-01', 1)": 0, + "(822, '2024-11-01', 2)": 0, + "(822, '2024-11-01', 3)": 0, + "(822, '2024-11-01', 4)": 0, + "(822, '2024-11-01', 5)": 0, + "(822, '2024-11-01', 6)": 0, + "(822, '2024-11-01', 7)": 1, + "(822, '2024-11-02', 0)": 0, + "(822, '2024-11-02', 1)": 0, + "(822, '2024-11-02', 2)": 0, + "(822, '2024-11-02', 3)": 0, + "(822, '2024-11-02', 4)": 0, + "(822, '2024-11-02', 5)": 0, + "(822, '2024-11-02', 6)": 0, + "(822, '2024-11-02', 7)": 1, + "(822, '2024-11-03', 0)": 0, + "(822, '2024-11-03', 1)": 0, + "(822, '2024-11-03', 2)": 0, + "(822, '2024-11-03', 3)": 0, + "(822, '2024-11-03', 4)": 0, + "(822, '2024-11-03', 5)": 0, + "(822, '2024-11-03', 6)": 0, + "(822, '2024-11-03', 7)": 1, + "(822, '2024-11-04', 0)": 0, + "(822, '2024-11-04', 1)": 0, + "(822, '2024-11-04', 2)": 0, + "(822, '2024-11-04', 3)": 0, + "(822, '2024-11-04', 4)": 0, + "(822, '2024-11-04', 5)": 0, + "(822, '2024-11-04', 6)": 0, + "(822, '2024-11-04', 7)": 0, + "(822, '2024-11-05', 0)": 0, + "(822, '2024-11-05', 1)": 0, + "(822, '2024-11-05', 2)": 0, + "(822, '2024-11-05', 3)": 0, + "(822, '2024-11-05', 4)": 0, + "(822, '2024-11-05', 5)": 0, + "(822, '2024-11-05', 6)": 0, + "(822, '2024-11-05', 7)": 0, + "(822, '2024-11-06', 0)": 0, + "(822, '2024-11-06', 1)": 0, + "(822, '2024-11-06', 2)": 0, + "(822, '2024-11-06', 3)": 0, + "(822, '2024-11-06', 4)": 0, + "(822, '2024-11-06', 5)": 0, + "(822, '2024-11-06', 6)": 1, + "(822, '2024-11-06', 7)": 0, + "(822, '2024-11-07', 0)": 0, + "(822, '2024-11-07', 1)": 0, + "(822, '2024-11-07', 2)": 0, + "(822, '2024-11-07', 3)": 0, + "(822, '2024-11-07', 4)": 0, + "(822, '2024-11-07', 5)": 0, + "(822, '2024-11-07', 6)": 1, + "(822, '2024-11-07', 7)": 0, + "(822, '2024-11-08', 0)": 0, + "(822, '2024-11-08', 1)": 0, + "(822, '2024-11-08', 2)": 0, + "(822, '2024-11-08', 3)": 0, + "(822, '2024-11-08', 4)": 0, + "(822, '2024-11-08', 5)": 0, + "(822, '2024-11-08', 6)": 1, + "(822, '2024-11-08', 7)": 0, + "(822, '2024-11-09', 0)": 0, + "(822, '2024-11-09', 1)": 0, + "(822, '2024-11-09', 2)": 0, + "(822, '2024-11-09', 3)": 0, + "(822, '2024-11-09', 4)": 0, + "(822, '2024-11-09', 5)": 0, + "(822, '2024-11-09', 6)": 0, + "(822, '2024-11-09', 7)": 0, + "(822, '2024-11-10', 0)": 0, + "(822, '2024-11-10', 1)": 0, + "(822, '2024-11-10', 2)": 0, + "(822, '2024-11-10', 3)": 0, + "(822, '2024-11-10', 4)": 0, + "(822, '2024-11-10', 5)": 0, + "(822, '2024-11-10', 6)": 0, + "(822, '2024-11-10', 7)": 0, + "(822, '2024-11-11', 0)": 0, + "(822, '2024-11-11', 1)": 0, + "(822, '2024-11-11', 2)": 0, + "(822, '2024-11-11', 3)": 0, + "(822, '2024-11-11', 4)": 0, + "(822, '2024-11-11', 5)": 1, + "(822, '2024-11-11', 6)": 0, + "(822, '2024-11-11', 7)": 0, + "(822, '2024-11-12', 0)": 0, + "(822, '2024-11-12', 1)": 0, + "(822, '2024-11-12', 2)": 0, + "(822, '2024-11-12', 3)": 0, + "(822, '2024-11-12', 4)": 0, + "(822, '2024-11-12', 5)": 1, + "(822, '2024-11-12', 6)": 0, + "(822, '2024-11-12', 7)": 0, + "(822, '2024-11-13', 0)": 0, + "(822, '2024-11-13', 1)": 0, + "(822, '2024-11-13', 2)": 0, + "(822, '2024-11-13', 3)": 0, + "(822, '2024-11-13', 4)": 0, + "(822, '2024-11-13', 5)": 1, + "(822, '2024-11-13', 6)": 0, + "(822, '2024-11-13', 7)": 0, + "(822, '2024-11-14', 0)": 0, + "(822, '2024-11-14', 1)": 0, + "(822, '2024-11-14', 2)": 0, + "(822, '2024-11-14', 3)": 0, + "(822, '2024-11-14', 4)": 0, + "(822, '2024-11-14', 5)": 1, + "(822, '2024-11-14', 6)": 0, + "(822, '2024-11-14', 7)": 0, + "(822, '2024-11-15', 0)": 0, + "(822, '2024-11-15', 1)": 0, + "(822, '2024-11-15', 2)": 0, + "(822, '2024-11-15', 3)": 0, + "(822, '2024-11-15', 4)": 0, + "(822, '2024-11-15', 5)": 0, + "(822, '2024-11-15', 6)": 0, + "(822, '2024-11-15', 7)": 0, + "(822, '2024-11-16', 0)": 0, + "(822, '2024-11-16', 1)": 0, + "(822, '2024-11-16', 2)": 0, + "(822, '2024-11-16', 3)": 0, + "(822, '2024-11-16', 4)": 0, + "(822, '2024-11-16', 5)": 0, + "(822, '2024-11-16', 6)": 0, + "(822, '2024-11-16', 7)": 0, + "(822, '2024-11-17', 0)": 0, + "(822, '2024-11-17', 1)": 0, + "(822, '2024-11-17', 2)": 0, + "(822, '2024-11-17', 3)": 0, + "(822, '2024-11-17', 4)": 0, + "(822, '2024-11-17', 5)": 0, + "(822, '2024-11-17', 6)": 0, + "(822, '2024-11-17', 7)": 0, + "(822, '2024-11-18', 0)": 0, + "(822, '2024-11-18', 1)": 0, + "(822, '2024-11-18', 2)": 0, + "(822, '2024-11-18', 3)": 0, + "(822, '2024-11-18', 4)": 0, + "(822, '2024-11-18', 5)": 0, + "(822, '2024-11-18', 6)": 0, + "(822, '2024-11-18', 7)": 0, + "(822, '2024-11-19', 0)": 0, + "(822, '2024-11-19', 1)": 0, + "(822, '2024-11-19', 2)": 0, + "(822, '2024-11-19', 3)": 0, + "(822, '2024-11-19', 4)": 0, + "(822, '2024-11-19', 5)": 0, + "(822, '2024-11-19', 6)": 0, + "(822, '2024-11-19', 7)": 0, + "(822, '2024-11-20', 0)": 0, + "(822, '2024-11-20', 1)": 0, + "(822, '2024-11-20', 2)": 0, + "(822, '2024-11-20', 3)": 0, + "(822, '2024-11-20', 4)": 0, + "(822, '2024-11-20', 5)": 0, + "(822, '2024-11-20', 6)": 1, + "(822, '2024-11-20', 7)": 0, + "(822, '2024-11-21', 0)": 0, + "(822, '2024-11-21', 1)": 0, + "(822, '2024-11-21', 2)": 0, + "(822, '2024-11-21', 3)": 0, + "(822, '2024-11-21', 4)": 0, + "(822, '2024-11-21', 5)": 0, + "(822, '2024-11-21', 6)": 0, + "(822, '2024-11-21', 7)": 1, + "(822, '2024-11-22', 0)": 0, + "(822, '2024-11-22', 1)": 0, + "(822, '2024-11-22', 2)": 0, + "(822, '2024-11-22', 3)": 0, + "(822, '2024-11-22', 4)": 0, + "(822, '2024-11-22', 5)": 0, + "(822, '2024-11-22', 6)": 0, + "(822, '2024-11-22', 7)": 1, + "(822, '2024-11-23', 0)": 0, + "(822, '2024-11-23', 1)": 0, + "(822, '2024-11-23', 2)": 0, + "(822, '2024-11-23', 3)": 0, + "(822, '2024-11-23', 4)": 0, + "(822, '2024-11-23', 5)": 0, + "(822, '2024-11-23', 6)": 0, + "(822, '2024-11-23', 7)": 1, + "(822, '2024-11-24', 0)": 0, + "(822, '2024-11-24', 1)": 0, + "(822, '2024-11-24', 2)": 0, + "(822, '2024-11-24', 3)": 0, + "(822, '2024-11-24', 4)": 0, + "(822, '2024-11-24', 5)": 0, + "(822, '2024-11-24', 6)": 0, + "(822, '2024-11-24', 7)": 1, + "(822, '2024-11-25', 0)": 0, + "(822, '2024-11-25', 1)": 0, + "(822, '2024-11-25', 2)": 0, + "(822, '2024-11-25', 3)": 0, + "(822, '2024-11-25', 4)": 0, + "(822, '2024-11-25', 5)": 0, + "(822, '2024-11-25', 6)": 0, + "(822, '2024-11-25', 7)": 0, + "(822, '2024-11-26', 0)": 0, + "(822, '2024-11-26', 1)": 0, + "(822, '2024-11-26', 2)": 0, + "(822, '2024-11-26', 3)": 0, + "(822, '2024-11-26', 4)": 0, + "(822, '2024-11-26', 5)": 0, + "(822, '2024-11-26', 6)": 0, + "(822, '2024-11-26', 7)": 0, + "(822, '2024-11-27', 0)": 0, + "(822, '2024-11-27', 1)": 0, + "(822, '2024-11-27', 2)": 0, + "(822, '2024-11-27', 3)": 0, + "(822, '2024-11-27', 4)": 0, + "(822, '2024-11-27', 5)": 0, + "(822, '2024-11-27', 6)": 0, + "(822, '2024-11-27', 7)": 0, + "(822, '2024-11-28', 0)": 0, + "(822, '2024-11-28', 1)": 0, + "(822, '2024-11-28', 2)": 1, + "(822, '2024-11-28', 3)": 0, + "(822, '2024-11-28', 4)": 0, + "(822, '2024-11-28', 5)": 0, + "(822, '2024-11-28', 6)": 0, + "(822, '2024-11-28', 7)": 0, + "(822, '2024-11-29', 0)": 0, + "(822, '2024-11-29', 1)": 0, + "(822, '2024-11-29', 2)": 0, + "(822, '2024-11-29', 3)": 0, + "(822, '2024-11-29', 4)": 0, + "(822, '2024-11-29', 5)": 0, + "(822, '2024-11-29', 6)": 0, + "(822, '2024-11-29', 7)": 1, + "(822, '2024-11-30', 0)": 0, + "(822, '2024-11-30', 1)": 0, + "(822, '2024-11-30', 2)": 0, + "(822, '2024-11-30', 3)": 0, + "(822, '2024-11-30', 4)": 0, + "(822, '2024-11-30', 5)": 0, + "(822, '2024-11-30', 6)": 0, + "(822, '2024-11-30', 7)": 1, + "(839, '2024-11-01', 0)": 0, + "(839, '2024-11-01', 1)": 0, + "(839, '2024-11-01', 2)": 0, + "(839, '2024-11-01', 3)": 0, + "(839, '2024-11-01', 4)": 0, + "(839, '2024-11-01', 5)": 0, + "(839, '2024-11-01', 6)": 0, + "(839, '2024-11-01', 7)": 0, + "(839, '2024-11-02', 0)": 0, + "(839, '2024-11-02', 1)": 0, + "(839, '2024-11-02', 2)": 0, + "(839, '2024-11-02', 3)": 0, + "(839, '2024-11-02', 4)": 0, + "(839, '2024-11-02', 5)": 0, + "(839, '2024-11-02', 6)": 0, + "(839, '2024-11-02', 7)": 0, + "(839, '2024-11-03', 0)": 0, + "(839, '2024-11-03', 1)": 0, + "(839, '2024-11-03', 2)": 0, + "(839, '2024-11-03', 3)": 0, + "(839, '2024-11-03', 4)": 0, + "(839, '2024-11-03', 5)": 0, + "(839, '2024-11-03', 6)": 0, + "(839, '2024-11-03', 7)": 0, + "(839, '2024-11-04', 0)": 1, + "(839, '2024-11-04', 1)": 0, + "(839, '2024-11-04', 2)": 0, + "(839, '2024-11-04', 3)": 0, + "(839, '2024-11-04', 4)": 0, + "(839, '2024-11-04', 5)": 0, + "(839, '2024-11-04', 6)": 0, + "(839, '2024-11-04', 7)": 0, + "(839, '2024-11-05', 0)": 0, + "(839, '2024-11-05', 1)": 0, + "(839, '2024-11-05', 2)": 0, + "(839, '2024-11-05', 3)": 0, + "(839, '2024-11-05', 4)": 0, + "(839, '2024-11-05', 5)": 0, + "(839, '2024-11-05', 6)": 0, + "(839, '2024-11-05', 7)": 0, + "(839, '2024-11-06', 0)": 0, + "(839, '2024-11-06', 1)": 0, + "(839, '2024-11-06', 2)": 0, + "(839, '2024-11-06', 3)": 0, + "(839, '2024-11-06', 4)": 0, + "(839, '2024-11-06', 5)": 0, + "(839, '2024-11-06', 6)": 0, + "(839, '2024-11-06', 7)": 0, + "(839, '2024-11-07', 0)": 0, + "(839, '2024-11-07', 1)": 0, + "(839, '2024-11-07', 2)": 0, + "(839, '2024-11-07', 3)": 0, + "(839, '2024-11-07', 4)": 0, + "(839, '2024-11-07', 5)": 0, + "(839, '2024-11-07', 6)": 0, + "(839, '2024-11-07', 7)": 0, + "(839, '2024-11-08', 0)": 0, + "(839, '2024-11-08', 1)": 0, + "(839, '2024-11-08', 2)": 0, + "(839, '2024-11-08', 3)": 0, + "(839, '2024-11-08', 4)": 0, + "(839, '2024-11-08', 5)": 0, + "(839, '2024-11-08', 6)": 0, + "(839, '2024-11-08', 7)": 0, + "(839, '2024-11-09', 0)": 0, + "(839, '2024-11-09', 1)": 0, + "(839, '2024-11-09', 2)": 0, + "(839, '2024-11-09', 3)": 0, + "(839, '2024-11-09', 4)": 0, + "(839, '2024-11-09', 5)": 0, + "(839, '2024-11-09', 6)": 0, + "(839, '2024-11-09', 7)": 0, + "(839, '2024-11-10', 0)": 0, + "(839, '2024-11-10', 1)": 0, + "(839, '2024-11-10', 2)": 0, + "(839, '2024-11-10', 3)": 0, + "(839, '2024-11-10', 4)": 0, + "(839, '2024-11-10', 5)": 0, + "(839, '2024-11-10', 6)": 0, + "(839, '2024-11-10', 7)": 1, + "(839, '2024-11-11', 0)": 0, + "(839, '2024-11-11', 1)": 0, + "(839, '2024-11-11', 2)": 0, + "(839, '2024-11-11', 3)": 0, + "(839, '2024-11-11', 4)": 0, + "(839, '2024-11-11', 5)": 0, + "(839, '2024-11-11', 6)": 0, + "(839, '2024-11-11', 7)": 1, + "(839, '2024-11-12', 0)": 0, + "(839, '2024-11-12', 1)": 0, + "(839, '2024-11-12', 2)": 0, + "(839, '2024-11-12', 3)": 0, + "(839, '2024-11-12', 4)": 0, + "(839, '2024-11-12', 5)": 0, + "(839, '2024-11-12', 6)": 0, + "(839, '2024-11-12', 7)": 0, + "(839, '2024-11-13', 0)": 0, + "(839, '2024-11-13', 1)": 0, + "(839, '2024-11-13', 2)": 0, + "(839, '2024-11-13', 3)": 0, + "(839, '2024-11-13', 4)": 0, + "(839, '2024-11-13', 5)": 0, + "(839, '2024-11-13', 6)": 0, + "(839, '2024-11-13', 7)": 0, + "(839, '2024-11-14', 0)": 0, + "(839, '2024-11-14', 1)": 0, + "(839, '2024-11-14', 2)": 0, + "(839, '2024-11-14', 3)": 0, + "(839, '2024-11-14', 4)": 0, + "(839, '2024-11-14', 5)": 0, + "(839, '2024-11-14', 6)": 0, + "(839, '2024-11-14', 7)": 0, + "(839, '2024-11-15', 0)": 0, + "(839, '2024-11-15', 1)": 0, + "(839, '2024-11-15', 2)": 0, + "(839, '2024-11-15', 3)": 0, + "(839, '2024-11-15', 4)": 0, + "(839, '2024-11-15', 5)": 0, + "(839, '2024-11-15', 6)": 0, + "(839, '2024-11-15', 7)": 1, + "(839, '2024-11-16', 0)": 0, + "(839, '2024-11-16', 1)": 0, + "(839, '2024-11-16', 2)": 0, + "(839, '2024-11-16', 3)": 0, + "(839, '2024-11-16', 4)": 0, + "(839, '2024-11-16', 5)": 0, + "(839, '2024-11-16', 6)": 0, + "(839, '2024-11-16', 7)": 0, + "(839, '2024-11-17', 0)": 0, + "(839, '2024-11-17', 1)": 0, + "(839, '2024-11-17', 2)": 0, + "(839, '2024-11-17', 3)": 0, + "(839, '2024-11-17', 4)": 0, + "(839, '2024-11-17', 5)": 0, + "(839, '2024-11-17', 6)": 0, + "(839, '2024-11-17', 7)": 0, + "(839, '2024-11-18', 0)": 0, + "(839, '2024-11-18', 1)": 0, + "(839, '2024-11-18', 2)": 0, + "(839, '2024-11-18', 3)": 0, + "(839, '2024-11-18', 4)": 0, + "(839, '2024-11-18', 5)": 0, + "(839, '2024-11-18', 6)": 0, + "(839, '2024-11-18', 7)": 0, + "(839, '2024-11-19', 0)": 0, + "(839, '2024-11-19', 1)": 0, + "(839, '2024-11-19', 2)": 0, + "(839, '2024-11-19', 3)": 0, + "(839, '2024-11-19', 4)": 0, + "(839, '2024-11-19', 5)": 0, + "(839, '2024-11-19', 6)": 0, + "(839, '2024-11-19', 7)": 1, + "(839, '2024-11-20', 0)": 0, + "(839, '2024-11-20', 1)": 0, + "(839, '2024-11-20', 2)": 0, + "(839, '2024-11-20', 3)": 0, + "(839, '2024-11-20', 4)": 0, + "(839, '2024-11-20', 5)": 0, + "(839, '2024-11-20', 6)": 0, + "(839, '2024-11-20', 7)": 1, + "(839, '2024-11-21', 0)": 0, + "(839, '2024-11-21', 1)": 0, + "(839, '2024-11-21', 2)": 0, + "(839, '2024-11-21', 3)": 0, + "(839, '2024-11-21', 4)": 0, + "(839, '2024-11-21', 5)": 0, + "(839, '2024-11-21', 6)": 0, + "(839, '2024-11-21', 7)": 0, + "(839, '2024-11-22', 0)": 0, + "(839, '2024-11-22', 1)": 0, + "(839, '2024-11-22', 2)": 0, + "(839, '2024-11-22', 3)": 0, + "(839, '2024-11-22', 4)": 0, + "(839, '2024-11-22', 5)": 0, + "(839, '2024-11-22', 6)": 0, + "(839, '2024-11-22', 7)": 0, + "(839, '2024-11-23', 0)": 0, + "(839, '2024-11-23', 1)": 0, + "(839, '2024-11-23', 2)": 0, + "(839, '2024-11-23', 3)": 0, + "(839, '2024-11-23', 4)": 0, + "(839, '2024-11-23', 5)": 0, + "(839, '2024-11-23', 6)": 0, + "(839, '2024-11-23', 7)": 0, + "(839, '2024-11-24', 0)": 0, + "(839, '2024-11-24', 1)": 0, + "(839, '2024-11-24', 2)": 0, + "(839, '2024-11-24', 3)": 0, + "(839, '2024-11-24', 4)": 0, + "(839, '2024-11-24', 5)": 0, + "(839, '2024-11-24', 6)": 0, + "(839, '2024-11-24', 7)": 0, + "(839, '2024-11-25', 0)": 0, + "(839, '2024-11-25', 1)": 0, + "(839, '2024-11-25', 2)": 0, + "(839, '2024-11-25', 3)": 0, + "(839, '2024-11-25', 4)": 0, + "(839, '2024-11-25', 5)": 0, + "(839, '2024-11-25', 6)": 0, + "(839, '2024-11-25', 7)": 0, + "(839, '2024-11-26', 0)": 0, + "(839, '2024-11-26', 1)": 0, + "(839, '2024-11-26', 2)": 0, + "(839, '2024-11-26', 3)": 0, + "(839, '2024-11-26', 4)": 0, + "(839, '2024-11-26', 5)": 0, + "(839, '2024-11-26', 6)": 0, + "(839, '2024-11-26', 7)": 1, + "(839, '2024-11-27', 0)": 0, + "(839, '2024-11-27', 1)": 0, + "(839, '2024-11-27', 2)": 0, + "(839, '2024-11-27', 3)": 0, + "(839, '2024-11-27', 4)": 0, + "(839, '2024-11-27', 5)": 0, + "(839, '2024-11-27', 6)": 0, + "(839, '2024-11-27', 7)": 1, + "(839, '2024-11-28', 0)": 0, + "(839, '2024-11-28', 1)": 0, + "(839, '2024-11-28', 2)": 0, + "(839, '2024-11-28', 3)": 0, + "(839, '2024-11-28', 4)": 0, + "(839, '2024-11-28', 5)": 0, + "(839, '2024-11-28', 6)": 0, + "(839, '2024-11-28', 7)": 0, + "(839, '2024-11-29', 0)": 0, + "(839, '2024-11-29', 1)": 0, + "(839, '2024-11-29', 2)": 0, + "(839, '2024-11-29', 3)": 0, + "(839, '2024-11-29', 4)": 0, + "(839, '2024-11-29', 5)": 0, + "(839, '2024-11-29', 6)": 0, + "(839, '2024-11-29', 7)": 0, + "(839, '2024-11-30', 0)": 0, + "(839, '2024-11-30', 1)": 0, + "(839, '2024-11-30', 2)": 0, + "(839, '2024-11-30', 3)": 0, + "(839, '2024-11-30', 4)": 0, + "(839, '2024-11-30', 5)": 0, + "(839, '2024-11-30', 6)": 0, + "(839, '2024-11-30', 7)": 0, + "(914, '2024-11-01', 0)": 0, + "(914, '2024-11-01', 1)": 0, + "(914, '2024-11-01', 2)": 1, + "(914, '2024-11-01', 3)": 0, + "(914, '2024-11-01', 4)": 0, + "(914, '2024-11-01', 5)": 0, + "(914, '2024-11-01', 6)": 0, + "(914, '2024-11-01', 7)": 0, + "(914, '2024-11-02', 0)": 0, + "(914, '2024-11-02', 1)": 0, + "(914, '2024-11-02', 2)": 0, + "(914, '2024-11-02', 3)": 0, + "(914, '2024-11-02', 4)": 0, + "(914, '2024-11-02', 5)": 0, + "(914, '2024-11-02', 6)": 0, + "(914, '2024-11-02', 7)": 0, + "(914, '2024-11-03', 0)": 0, + "(914, '2024-11-03', 1)": 0, + "(914, '2024-11-03', 2)": 0, + "(914, '2024-11-03', 3)": 0, + "(914, '2024-11-03', 4)": 0, + "(914, '2024-11-03', 5)": 0, + "(914, '2024-11-03', 6)": 0, + "(914, '2024-11-03', 7)": 0, + "(914, '2024-11-04', 0)": 1, + "(914, '2024-11-04', 1)": 0, + "(914, '2024-11-04', 2)": 0, + "(914, '2024-11-04', 3)": 0, + "(914, '2024-11-04', 4)": 0, + "(914, '2024-11-04', 5)": 0, + "(914, '2024-11-04', 6)": 0, + "(914, '2024-11-04', 7)": 0, + "(914, '2024-11-05', 0)": 0, + "(914, '2024-11-05', 1)": 0, + "(914, '2024-11-05', 2)": 0, + "(914, '2024-11-05', 3)": 0, + "(914, '2024-11-05', 4)": 0, + "(914, '2024-11-05', 5)": 0, + "(914, '2024-11-05', 6)": 0, + "(914, '2024-11-05', 7)": 0, + "(914, '2024-11-06', 0)": 1, + "(914, '2024-11-06', 1)": 0, + "(914, '2024-11-06', 2)": 0, + "(914, '2024-11-06', 3)": 0, + "(914, '2024-11-06', 4)": 0, + "(914, '2024-11-06', 5)": 0, + "(914, '2024-11-06', 6)": 0, + "(914, '2024-11-06', 7)": 0, + "(914, '2024-11-07', 0)": 0, + "(914, '2024-11-07', 1)": 0, + "(914, '2024-11-07', 2)": 0, + "(914, '2024-11-07', 3)": 0, + "(914, '2024-11-07', 4)": 0, + "(914, '2024-11-07', 5)": 0, + "(914, '2024-11-07', 6)": 0, + "(914, '2024-11-07', 7)": 0, + "(914, '2024-11-08', 0)": 1, + "(914, '2024-11-08', 1)": 0, + "(914, '2024-11-08', 2)": 0, + "(914, '2024-11-08', 3)": 0, + "(914, '2024-11-08', 4)": 0, + "(914, '2024-11-08', 5)": 0, + "(914, '2024-11-08', 6)": 0, + "(914, '2024-11-08', 7)": 0, + "(914, '2024-11-09', 0)": 0, + "(914, '2024-11-09', 1)": 0, + "(914, '2024-11-09', 2)": 1, + "(914, '2024-11-09', 3)": 0, + "(914, '2024-11-09', 4)": 0, + "(914, '2024-11-09', 5)": 0, + "(914, '2024-11-09', 6)": 0, + "(914, '2024-11-09', 7)": 0, + "(914, '2024-11-10', 0)": 0, + "(914, '2024-11-10', 1)": 0, + "(914, '2024-11-10', 2)": 0, + "(914, '2024-11-10', 3)": 1, + "(914, '2024-11-10', 4)": 0, + "(914, '2024-11-10', 5)": 0, + "(914, '2024-11-10', 6)": 0, + "(914, '2024-11-10', 7)": 0, + "(914, '2024-11-11', 0)": 0, + "(914, '2024-11-11', 1)": 0, + "(914, '2024-11-11', 2)": 0, + "(914, '2024-11-11', 3)": 0, + "(914, '2024-11-11', 4)": 0, + "(914, '2024-11-11', 5)": 0, + "(914, '2024-11-11', 6)": 0, + "(914, '2024-11-11', 7)": 0, + "(914, '2024-11-12', 0)": 0, + "(914, '2024-11-12', 1)": 0, + "(914, '2024-11-12', 2)": 0, + "(914, '2024-11-12', 3)": 0, + "(914, '2024-11-12', 4)": 0, + "(914, '2024-11-12', 5)": 0, + "(914, '2024-11-12', 6)": 0, + "(914, '2024-11-12', 7)": 0, + "(914, '2024-11-13', 0)": 1, + "(914, '2024-11-13', 1)": 0, + "(914, '2024-11-13', 2)": 0, + "(914, '2024-11-13', 3)": 0, + "(914, '2024-11-13', 4)": 0, + "(914, '2024-11-13', 5)": 0, + "(914, '2024-11-13', 6)": 0, + "(914, '2024-11-13', 7)": 0, + "(914, '2024-11-14', 0)": 0, + "(914, '2024-11-14', 1)": 0, + "(914, '2024-11-14', 2)": 0, + "(914, '2024-11-14', 3)": 0, + "(914, '2024-11-14', 4)": 0, + "(914, '2024-11-14', 5)": 0, + "(914, '2024-11-14', 6)": 0, + "(914, '2024-11-14', 7)": 0, + "(914, '2024-11-15', 0)": 0, + "(914, '2024-11-15', 1)": 0, + "(914, '2024-11-15', 2)": 0, + "(914, '2024-11-15', 3)": 0, + "(914, '2024-11-15', 4)": 0, + "(914, '2024-11-15', 5)": 0, + "(914, '2024-11-15', 6)": 0, + "(914, '2024-11-15', 7)": 0, + "(914, '2024-11-16', 0)": 0, + "(914, '2024-11-16', 1)": 1, + "(914, '2024-11-16', 2)": 0, + "(914, '2024-11-16', 3)": 0, + "(914, '2024-11-16', 4)": 0, + "(914, '2024-11-16', 5)": 0, + "(914, '2024-11-16', 6)": 0, + "(914, '2024-11-16', 7)": 0, + "(914, '2024-11-17', 0)": 0, + "(914, '2024-11-17', 1)": 0, + "(914, '2024-11-17', 2)": 0, + "(914, '2024-11-17', 3)": 0, + "(914, '2024-11-17', 4)": 0, + "(914, '2024-11-17', 5)": 0, + "(914, '2024-11-17', 6)": 0, + "(914, '2024-11-17', 7)": 0, + "(914, '2024-11-18', 0)": 0, + "(914, '2024-11-18', 1)": 0, + "(914, '2024-11-18', 2)": 0, + "(914, '2024-11-18', 3)": 0, + "(914, '2024-11-18', 4)": 0, + "(914, '2024-11-18', 5)": 0, + "(914, '2024-11-18', 6)": 0, + "(914, '2024-11-18', 7)": 0, + "(914, '2024-11-19', 0)": 0, + "(914, '2024-11-19', 1)": 0, + "(914, '2024-11-19', 2)": 0, + "(914, '2024-11-19', 3)": 0, + "(914, '2024-11-19', 4)": 0, + "(914, '2024-11-19', 5)": 0, + "(914, '2024-11-19', 6)": 0, + "(914, '2024-11-19', 7)": 0, + "(914, '2024-11-20', 0)": 0, + "(914, '2024-11-20', 1)": 0, + "(914, '2024-11-20', 2)": 0, + "(914, '2024-11-20', 3)": 0, + "(914, '2024-11-20', 4)": 0, + "(914, '2024-11-20', 5)": 0, + "(914, '2024-11-20', 6)": 0, + "(914, '2024-11-20', 7)": 0, + "(914, '2024-11-21', 0)": 0, + "(914, '2024-11-21', 1)": 0, + "(914, '2024-11-21', 2)": 0, + "(914, '2024-11-21', 3)": 0, + "(914, '2024-11-21', 4)": 0, + "(914, '2024-11-21', 5)": 0, + "(914, '2024-11-21', 6)": 0, + "(914, '2024-11-21', 7)": 0, + "(914, '2024-11-22', 0)": 0, + "(914, '2024-11-22', 1)": 0, + "(914, '2024-11-22', 2)": 0, + "(914, '2024-11-22', 3)": 0, + "(914, '2024-11-22', 4)": 0, + "(914, '2024-11-22', 5)": 0, + "(914, '2024-11-22', 6)": 0, + "(914, '2024-11-22', 7)": 0, + "(914, '2024-11-23', 0)": 0, + "(914, '2024-11-23', 1)": 0, + "(914, '2024-11-23', 2)": 0, + "(914, '2024-11-23', 3)": 0, + "(914, '2024-11-23', 4)": 0, + "(914, '2024-11-23', 5)": 0, + "(914, '2024-11-23', 6)": 0, + "(914, '2024-11-23', 7)": 0, + "(914, '2024-11-24', 0)": 0, + "(914, '2024-11-24', 1)": 0, + "(914, '2024-11-24', 2)": 0, + "(914, '2024-11-24', 3)": 0, + "(914, '2024-11-24', 4)": 0, + "(914, '2024-11-24', 5)": 0, + "(914, '2024-11-24', 6)": 0, + "(914, '2024-11-24', 7)": 0, + "(914, '2024-11-25', 0)": 0, + "(914, '2024-11-25', 1)": 0, + "(914, '2024-11-25', 2)": 0, + "(914, '2024-11-25', 3)": 0, + "(914, '2024-11-25', 4)": 0, + "(914, '2024-11-25', 5)": 0, + "(914, '2024-11-25', 6)": 0, + "(914, '2024-11-25', 7)": 0, + "(914, '2024-11-26', 0)": 0, + "(914, '2024-11-26', 1)": 0, + "(914, '2024-11-26', 2)": 0, + "(914, '2024-11-26', 3)": 0, + "(914, '2024-11-26', 4)": 0, + "(914, '2024-11-26', 5)": 0, + "(914, '2024-11-26', 6)": 0, + "(914, '2024-11-26', 7)": 0, + "(914, '2024-11-27', 0)": 0, + "(914, '2024-11-27', 1)": 0, + "(914, '2024-11-27', 2)": 0, + "(914, '2024-11-27', 3)": 0, + "(914, '2024-11-27', 4)": 0, + "(914, '2024-11-27', 5)": 0, + "(914, '2024-11-27', 6)": 0, + "(914, '2024-11-27', 7)": 0, + "(914, '2024-11-28', 0)": 0, + "(914, '2024-11-28', 1)": 0, + "(914, '2024-11-28', 2)": 0, + "(914, '2024-11-28', 3)": 0, + "(914, '2024-11-28', 4)": 0, + "(914, '2024-11-28', 5)": 0, + "(914, '2024-11-28', 6)": 0, + "(914, '2024-11-28', 7)": 0, + "(914, '2024-11-29', 0)": 0, + "(914, '2024-11-29', 1)": 0, + "(914, '2024-11-29', 2)": 0, + "(914, '2024-11-29', 3)": 0, + "(914, '2024-11-29', 4)": 0, + "(914, '2024-11-29', 5)": 0, + "(914, '2024-11-29', 6)": 0, + "(914, '2024-11-29', 7)": 0, + "(914, '2024-11-30', 0)": 0, + "(914, '2024-11-30', 1)": 0, + "(914, '2024-11-30', 2)": 0, + "(914, '2024-11-30', 3)": 0, + "(914, '2024-11-30', 4)": 0, + "(914, '2024-11-30', 5)": 0, + "(914, '2024-11-30', 6)": 0, + "(914, '2024-11-30', 7)": 0, + "(917, '2024-11-01', 0)": 1, + "(917, '2024-11-01', 1)": 0, + "(917, '2024-11-01', 2)": 0, + "(917, '2024-11-01', 3)": 0, + "(917, '2024-11-01', 4)": 0, + "(917, '2024-11-01', 5)": 0, + "(917, '2024-11-01', 6)": 0, + "(917, '2024-11-01', 7)": 0, + "(917, '2024-11-02', 0)": 0, + "(917, '2024-11-02', 1)": 0, + "(917, '2024-11-02', 2)": 0, + "(917, '2024-11-02', 3)": 0, + "(917, '2024-11-02', 4)": 0, + "(917, '2024-11-02', 5)": 0, + "(917, '2024-11-02', 6)": 1, + "(917, '2024-11-02', 7)": 0, + "(917, '2024-11-03', 0)": 0, + "(917, '2024-11-03', 1)": 1, + "(917, '2024-11-03', 2)": 0, + "(917, '2024-11-03', 3)": 0, + "(917, '2024-11-03', 4)": 0, + "(917, '2024-11-03', 5)": 0, + "(917, '2024-11-03', 6)": 0, + "(917, '2024-11-03', 7)": 0, + "(917, '2024-11-04', 0)": 0, + "(917, '2024-11-04', 1)": 0, + "(917, '2024-11-04', 2)": 0, + "(917, '2024-11-04', 3)": 0, + "(917, '2024-11-04', 4)": 0, + "(917, '2024-11-04', 5)": 0, + "(917, '2024-11-04', 6)": 0, + "(917, '2024-11-04', 7)": 0, + "(917, '2024-11-05', 0)": 0, + "(917, '2024-11-05', 1)": 0, + "(917, '2024-11-05', 2)": 1, + "(917, '2024-11-05', 3)": 0, + "(917, '2024-11-05', 4)": 0, + "(917, '2024-11-05', 5)": 0, + "(917, '2024-11-05', 6)": 0, + "(917, '2024-11-05', 7)": 0, + "(917, '2024-11-06', 0)": 0, + "(917, '2024-11-06', 1)": 0, + "(917, '2024-11-06', 2)": 0, + "(917, '2024-11-06', 3)": 0, + "(917, '2024-11-06', 4)": 0, + "(917, '2024-11-06', 5)": 0, + "(917, '2024-11-06', 6)": 0, + "(917, '2024-11-06', 7)": 0, + "(917, '2024-11-07', 0)": 0, + "(917, '2024-11-07', 1)": 0, + "(917, '2024-11-07', 2)": 0, + "(917, '2024-11-07', 3)": 0, + "(917, '2024-11-07', 4)": 0, + "(917, '2024-11-07', 5)": 0, + "(917, '2024-11-07', 6)": 0, + "(917, '2024-11-07', 7)": 0, + "(917, '2024-11-08', 0)": 1, + "(917, '2024-11-08', 1)": 0, + "(917, '2024-11-08', 2)": 0, + "(917, '2024-11-08', 3)": 0, + "(917, '2024-11-08', 4)": 0, + "(917, '2024-11-08', 5)": 0, + "(917, '2024-11-08', 6)": 0, + "(917, '2024-11-08', 7)": 0, + "(917, '2024-11-09', 0)": 0, + "(917, '2024-11-09', 1)": 0, + "(917, '2024-11-09', 2)": 0, + "(917, '2024-11-09', 3)": 0, + "(917, '2024-11-09', 4)": 0, + "(917, '2024-11-09', 5)": 0, + "(917, '2024-11-09', 6)": 0, + "(917, '2024-11-09', 7)": 0, + "(917, '2024-11-10', 0)": 1, + "(917, '2024-11-10', 1)": 0, + "(917, '2024-11-10', 2)": 0, + "(917, '2024-11-10', 3)": 0, + "(917, '2024-11-10', 4)": 0, + "(917, '2024-11-10', 5)": 0, + "(917, '2024-11-10', 6)": 0, + "(917, '2024-11-10', 7)": 0, + "(917, '2024-11-11', 0)": 0, + "(917, '2024-11-11', 1)": 0, + "(917, '2024-11-11', 2)": 0, + "(917, '2024-11-11', 3)": 1, + "(917, '2024-11-11', 4)": 0, + "(917, '2024-11-11', 5)": 0, + "(917, '2024-11-11', 6)": 0, + "(917, '2024-11-11', 7)": 0, + "(917, '2024-11-12', 0)": 0, + "(917, '2024-11-12', 1)": 0, + "(917, '2024-11-12', 2)": 0, + "(917, '2024-11-12', 3)": 0, + "(917, '2024-11-12', 4)": 0, + "(917, '2024-11-12', 5)": 0, + "(917, '2024-11-12', 6)": 0, + "(917, '2024-11-12', 7)": 0, + "(917, '2024-11-13', 0)": 1, + "(917, '2024-11-13', 1)": 0, + "(917, '2024-11-13', 2)": 0, + "(917, '2024-11-13', 3)": 0, + "(917, '2024-11-13', 4)": 0, + "(917, '2024-11-13', 5)": 0, + "(917, '2024-11-13', 6)": 0, + "(917, '2024-11-13', 7)": 0, + "(917, '2024-11-14', 0)": 0, + "(917, '2024-11-14', 1)": 0, + "(917, '2024-11-14', 2)": 1, + "(917, '2024-11-14', 3)": 0, + "(917, '2024-11-14', 4)": 0, + "(917, '2024-11-14', 5)": 0, + "(917, '2024-11-14', 6)": 0, + "(917, '2024-11-14', 7)": 0, + "(917, '2024-11-15', 0)": 0, + "(917, '2024-11-15', 1)": 0, + "(917, '2024-11-15', 2)": 0, + "(917, '2024-11-15', 3)": 0, + "(917, '2024-11-15', 4)": 0, + "(917, '2024-11-15', 5)": 0, + "(917, '2024-11-15', 6)": 1, + "(917, '2024-11-15', 7)": 0, + "(917, '2024-11-16', 0)": 0, + "(917, '2024-11-16', 1)": 0, + "(917, '2024-11-16', 2)": 0, + "(917, '2024-11-16', 3)": 0, + "(917, '2024-11-16', 4)": 0, + "(917, '2024-11-16', 5)": 0, + "(917, '2024-11-16', 6)": 1, + "(917, '2024-11-16', 7)": 0, + "(917, '2024-11-17', 0)": 0, + "(917, '2024-11-17', 1)": 0, + "(917, '2024-11-17', 2)": 0, + "(917, '2024-11-17', 3)": 0, + "(917, '2024-11-17', 4)": 0, + "(917, '2024-11-17', 5)": 0, + "(917, '2024-11-17', 6)": 0, + "(917, '2024-11-17', 7)": 0, + "(917, '2024-11-18', 0)": 0, + "(917, '2024-11-18', 1)": 1, + "(917, '2024-11-18', 2)": 0, + "(917, '2024-11-18', 3)": 0, + "(917, '2024-11-18', 4)": 0, + "(917, '2024-11-18', 5)": 0, + "(917, '2024-11-18', 6)": 0, + "(917, '2024-11-18', 7)": 0, + "(917, '2024-11-19', 0)": 0, + "(917, '2024-11-19', 1)": 0, + "(917, '2024-11-19', 2)": 0, + "(917, '2024-11-19', 3)": 0, + "(917, '2024-11-19', 4)": 0, + "(917, '2024-11-19', 5)": 0, + "(917, '2024-11-19', 6)": 0, + "(917, '2024-11-19', 7)": 0, + "(917, '2024-11-20', 0)": 1, + "(917, '2024-11-20', 1)": 0, + "(917, '2024-11-20', 2)": 0, + "(917, '2024-11-20', 3)": 0, + "(917, '2024-11-20', 4)": 0, + "(917, '2024-11-20', 5)": 0, + "(917, '2024-11-20', 6)": 0, + "(917, '2024-11-20', 7)": 0, + "(917, '2024-11-21', 0)": 0, + "(917, '2024-11-21', 1)": 1, + "(917, '2024-11-21', 2)": 0, + "(917, '2024-11-21', 3)": 0, + "(917, '2024-11-21', 4)": 0, + "(917, '2024-11-21', 5)": 0, + "(917, '2024-11-21', 6)": 0, + "(917, '2024-11-21', 7)": 0, + "(917, '2024-11-22', 0)": 0, + "(917, '2024-11-22', 1)": 1, + "(917, '2024-11-22', 2)": 0, + "(917, '2024-11-22', 3)": 0, + "(917, '2024-11-22', 4)": 0, + "(917, '2024-11-22', 5)": 0, + "(917, '2024-11-22', 6)": 0, + "(917, '2024-11-22', 7)": 0, + "(917, '2024-11-23', 0)": 0, + "(917, '2024-11-23', 1)": 0, + "(917, '2024-11-23', 2)": 0, + "(917, '2024-11-23', 3)": 0, + "(917, '2024-11-23', 4)": 0, + "(917, '2024-11-23', 5)": 0, + "(917, '2024-11-23', 6)": 1, + "(917, '2024-11-23', 7)": 0, + "(917, '2024-11-24', 0)": 0, + "(917, '2024-11-24', 1)": 0, + "(917, '2024-11-24', 2)": 0, + "(917, '2024-11-24', 3)": 0, + "(917, '2024-11-24', 4)": 0, + "(917, '2024-11-24', 5)": 0, + "(917, '2024-11-24', 6)": 0, + "(917, '2024-11-24', 7)": 0, + "(917, '2024-11-25', 0)": 1, + "(917, '2024-11-25', 1)": 0, + "(917, '2024-11-25', 2)": 0, + "(917, '2024-11-25', 3)": 0, + "(917, '2024-11-25', 4)": 0, + "(917, '2024-11-25', 5)": 0, + "(917, '2024-11-25', 6)": 0, + "(917, '2024-11-25', 7)": 0, + "(917, '2024-11-26', 0)": 0, + "(917, '2024-11-26', 1)": 0, + "(917, '2024-11-26', 2)": 1, + "(917, '2024-11-26', 3)": 0, + "(917, '2024-11-26', 4)": 0, + "(917, '2024-11-26', 5)": 0, + "(917, '2024-11-26', 6)": 0, + "(917, '2024-11-26', 7)": 0, + "(917, '2024-11-27', 0)": 0, + "(917, '2024-11-27', 1)": 0, + "(917, '2024-11-27', 2)": 0, + "(917, '2024-11-27', 3)": 1, + "(917, '2024-11-27', 4)": 0, + "(917, '2024-11-27', 5)": 0, + "(917, '2024-11-27', 6)": 0, + "(917, '2024-11-27', 7)": 0, + "(917, '2024-11-28', 0)": 0, + "(917, '2024-11-28', 1)": 0, + "(917, '2024-11-28', 2)": 0, + "(917, '2024-11-28', 3)": 0, + "(917, '2024-11-28', 4)": 0, + "(917, '2024-11-28', 5)": 0, + "(917, '2024-11-28', 6)": 0, + "(917, '2024-11-28', 7)": 0, + "(917, '2024-11-29', 0)": 0, + "(917, '2024-11-29', 1)": 0, + "(917, '2024-11-29', 2)": 0, + "(917, '2024-11-29', 3)": 0, + "(917, '2024-11-29', 4)": 0, + "(917, '2024-11-29', 5)": 0, + "(917, '2024-11-29', 6)": 1, + "(917, '2024-11-29', 7)": 0, + "(917, '2024-11-30', 0)": 0, + "(917, '2024-11-30', 1)": 0, + "(917, '2024-11-30', 2)": 0, + "(917, '2024-11-30', 3)": 0, + "(917, '2024-11-30', 4)": 0, + "(917, '2024-11-30', 5)": 0, + "(917, '2024-11-30', 6)": 0, + "(917, '2024-11-30', 7)": 0, + "(921, '2024-11-01', 0)": 0, + "(921, '2024-11-01', 1)": 0, + "(921, '2024-11-01', 2)": 0, + "(921, '2024-11-01', 3)": 0, + "(921, '2024-11-01', 4)": 0, + "(921, '2024-11-01', 5)": 0, + "(921, '2024-11-01', 6)": 0, + "(921, '2024-11-01', 7)": 0, + "(921, '2024-11-02', 0)": 0, + "(921, '2024-11-02', 1)": 0, + "(921, '2024-11-02', 2)": 0, + "(921, '2024-11-02', 3)": 0, + "(921, '2024-11-02', 4)": 0, + "(921, '2024-11-02', 5)": 0, + "(921, '2024-11-02', 6)": 0, + "(921, '2024-11-02', 7)": 0, + "(921, '2024-11-03', 0)": 0, + "(921, '2024-11-03', 1)": 0, + "(921, '2024-11-03', 2)": 0, + "(921, '2024-11-03', 3)": 0, + "(921, '2024-11-03', 4)": 0, + "(921, '2024-11-03', 5)": 0, + "(921, '2024-11-03', 6)": 0, + "(921, '2024-11-03', 7)": 0, + "(921, '2024-11-04', 0)": 0, + "(921, '2024-11-04', 1)": 0, + "(921, '2024-11-04', 2)": 0, + "(921, '2024-11-04', 3)": 0, + "(921, '2024-11-04', 4)": 0, + "(921, '2024-11-04', 5)": 0, + "(921, '2024-11-04', 6)": 0, + "(921, '2024-11-04', 7)": 0, + "(921, '2024-11-05', 0)": 0, + "(921, '2024-11-05', 1)": 0, + "(921, '2024-11-05', 2)": 0, + "(921, '2024-11-05', 3)": 0, + "(921, '2024-11-05', 4)": 0, + "(921, '2024-11-05', 5)": 0, + "(921, '2024-11-05', 6)": 0, + "(921, '2024-11-05', 7)": 0, + "(921, '2024-11-06', 0)": 0, + "(921, '2024-11-06', 1)": 0, + "(921, '2024-11-06', 2)": 0, + "(921, '2024-11-06', 3)": 0, + "(921, '2024-11-06', 4)": 0, + "(921, '2024-11-06', 5)": 0, + "(921, '2024-11-06', 6)": 0, + "(921, '2024-11-06', 7)": 0, + "(921, '2024-11-07', 0)": 0, + "(921, '2024-11-07', 1)": 0, + "(921, '2024-11-07', 2)": 0, + "(921, '2024-11-07', 3)": 0, + "(921, '2024-11-07', 4)": 0, + "(921, '2024-11-07', 5)": 0, + "(921, '2024-11-07', 6)": 0, + "(921, '2024-11-07', 7)": 0, + "(921, '2024-11-08', 0)": 0, + "(921, '2024-11-08', 1)": 0, + "(921, '2024-11-08', 2)": 0, + "(921, '2024-11-08', 3)": 0, + "(921, '2024-11-08', 4)": 0, + "(921, '2024-11-08', 5)": 0, + "(921, '2024-11-08', 6)": 0, + "(921, '2024-11-08', 7)": 0, + "(921, '2024-11-09', 0)": 0, + "(921, '2024-11-09', 1)": 0, + "(921, '2024-11-09', 2)": 0, + "(921, '2024-11-09', 3)": 0, + "(921, '2024-11-09', 4)": 0, + "(921, '2024-11-09', 5)": 0, + "(921, '2024-11-09', 6)": 0, + "(921, '2024-11-09', 7)": 0, + "(921, '2024-11-10', 0)": 0, + "(921, '2024-11-10', 1)": 0, + "(921, '2024-11-10', 2)": 0, + "(921, '2024-11-10', 3)": 0, + "(921, '2024-11-10', 4)": 0, + "(921, '2024-11-10', 5)": 0, + "(921, '2024-11-10', 6)": 0, + "(921, '2024-11-10', 7)": 0, + "(921, '2024-11-11', 0)": 0, + "(921, '2024-11-11', 1)": 0, + "(921, '2024-11-11', 2)": 0, + "(921, '2024-11-11', 3)": 0, + "(921, '2024-11-11', 4)": 0, + "(921, '2024-11-11', 5)": 0, + "(921, '2024-11-11', 6)": 0, + "(921, '2024-11-11', 7)": 0, + "(921, '2024-11-12', 0)": 0, + "(921, '2024-11-12', 1)": 0, + "(921, '2024-11-12', 2)": 0, + "(921, '2024-11-12', 3)": 0, + "(921, '2024-11-12', 4)": 0, + "(921, '2024-11-12', 5)": 0, + "(921, '2024-11-12', 6)": 0, + "(921, '2024-11-12', 7)": 0, + "(921, '2024-11-13', 0)": 0, + "(921, '2024-11-13', 1)": 0, + "(921, '2024-11-13', 2)": 0, + "(921, '2024-11-13', 3)": 0, + "(921, '2024-11-13', 4)": 0, + "(921, '2024-11-13', 5)": 0, + "(921, '2024-11-13', 6)": 0, + "(921, '2024-11-13', 7)": 0, + "(921, '2024-11-14', 0)": 0, + "(921, '2024-11-14', 1)": 0, + "(921, '2024-11-14', 2)": 0, + "(921, '2024-11-14', 3)": 0, + "(921, '2024-11-14', 4)": 0, + "(921, '2024-11-14', 5)": 0, + "(921, '2024-11-14', 6)": 0, + "(921, '2024-11-14', 7)": 0, + "(921, '2024-11-15', 0)": 0, + "(921, '2024-11-15', 1)": 0, + "(921, '2024-11-15', 2)": 0, + "(921, '2024-11-15', 3)": 0, + "(921, '2024-11-15', 4)": 0, + "(921, '2024-11-15', 5)": 0, + "(921, '2024-11-15', 6)": 0, + "(921, '2024-11-15', 7)": 0, + "(921, '2024-11-16', 0)": 0, + "(921, '2024-11-16', 1)": 0, + "(921, '2024-11-16', 2)": 0, + "(921, '2024-11-16', 3)": 0, + "(921, '2024-11-16', 4)": 0, + "(921, '2024-11-16', 5)": 0, + "(921, '2024-11-16', 6)": 0, + "(921, '2024-11-16', 7)": 0, + "(921, '2024-11-17', 0)": 0, + "(921, '2024-11-17', 1)": 0, + "(921, '2024-11-17', 2)": 0, + "(921, '2024-11-17', 3)": 0, + "(921, '2024-11-17', 4)": 0, + "(921, '2024-11-17', 5)": 0, + "(921, '2024-11-17', 6)": 0, + "(921, '2024-11-17', 7)": 0, + "(921, '2024-11-18', 0)": 0, + "(921, '2024-11-18', 1)": 0, + "(921, '2024-11-18', 2)": 0, + "(921, '2024-11-18', 3)": 0, + "(921, '2024-11-18', 4)": 0, + "(921, '2024-11-18', 5)": 0, + "(921, '2024-11-18', 6)": 0, + "(921, '2024-11-18', 7)": 0, + "(921, '2024-11-19', 0)": 0, + "(921, '2024-11-19', 1)": 0, + "(921, '2024-11-19', 2)": 0, + "(921, '2024-11-19', 3)": 0, + "(921, '2024-11-19', 4)": 0, + "(921, '2024-11-19', 5)": 0, + "(921, '2024-11-19', 6)": 0, + "(921, '2024-11-19', 7)": 0, + "(921, '2024-11-20', 0)": 0, + "(921, '2024-11-20', 1)": 0, + "(921, '2024-11-20', 2)": 0, + "(921, '2024-11-20', 3)": 0, + "(921, '2024-11-20', 4)": 0, + "(921, '2024-11-20', 5)": 0, + "(921, '2024-11-20', 6)": 0, + "(921, '2024-11-20', 7)": 0, + "(921, '2024-11-21', 0)": 0, + "(921, '2024-11-21', 1)": 0, + "(921, '2024-11-21', 2)": 0, + "(921, '2024-11-21', 3)": 0, + "(921, '2024-11-21', 4)": 0, + "(921, '2024-11-21', 5)": 0, + "(921, '2024-11-21', 6)": 0, + "(921, '2024-11-21', 7)": 0, + "(921, '2024-11-22', 0)": 0, + "(921, '2024-11-22', 1)": 0, + "(921, '2024-11-22', 2)": 0, + "(921, '2024-11-22', 3)": 0, + "(921, '2024-11-22', 4)": 0, + "(921, '2024-11-22', 5)": 0, + "(921, '2024-11-22', 6)": 0, + "(921, '2024-11-22', 7)": 0, + "(921, '2024-11-23', 0)": 0, + "(921, '2024-11-23', 1)": 0, + "(921, '2024-11-23', 2)": 0, + "(921, '2024-11-23', 3)": 0, + "(921, '2024-11-23', 4)": 0, + "(921, '2024-11-23', 5)": 0, + "(921, '2024-11-23', 6)": 0, + "(921, '2024-11-23', 7)": 0, + "(921, '2024-11-24', 0)": 0, + "(921, '2024-11-24', 1)": 0, + "(921, '2024-11-24', 2)": 0, + "(921, '2024-11-24', 3)": 0, + "(921, '2024-11-24', 4)": 0, + "(921, '2024-11-24', 5)": 0, + "(921, '2024-11-24', 6)": 0, + "(921, '2024-11-24', 7)": 0, + "(921, '2024-11-25', 0)": 0, + "(921, '2024-11-25', 1)": 0, + "(921, '2024-11-25', 2)": 0, + "(921, '2024-11-25', 3)": 0, + "(921, '2024-11-25', 4)": 0, + "(921, '2024-11-25', 5)": 0, + "(921, '2024-11-25', 6)": 0, + "(921, '2024-11-25', 7)": 0, + "(921, '2024-11-26', 0)": 0, + "(921, '2024-11-26', 1)": 0, + "(921, '2024-11-26', 2)": 0, + "(921, '2024-11-26', 3)": 0, + "(921, '2024-11-26', 4)": 0, + "(921, '2024-11-26', 5)": 0, + "(921, '2024-11-26', 6)": 0, + "(921, '2024-11-26', 7)": 0, + "(921, '2024-11-27', 0)": 0, + "(921, '2024-11-27', 1)": 0, + "(921, '2024-11-27', 2)": 0, + "(921, '2024-11-27', 3)": 0, + "(921, '2024-11-27', 4)": 0, + "(921, '2024-11-27', 5)": 0, + "(921, '2024-11-27', 6)": 0, + "(921, '2024-11-27', 7)": 0, + "(921, '2024-11-28', 0)": 0, + "(921, '2024-11-28', 1)": 0, + "(921, '2024-11-28', 2)": 0, + "(921, '2024-11-28', 3)": 0, + "(921, '2024-11-28', 4)": 0, + "(921, '2024-11-28', 5)": 0, + "(921, '2024-11-28', 6)": 0, + "(921, '2024-11-28', 7)": 0, + "(921, '2024-11-29', 0)": 0, + "(921, '2024-11-29', 1)": 0, + "(921, '2024-11-29', 2)": 0, + "(921, '2024-11-29', 3)": 0, + "(921, '2024-11-29', 4)": 0, + "(921, '2024-11-29', 5)": 0, + "(921, '2024-11-29', 6)": 0, + "(921, '2024-11-29', 7)": 0, + "(921, '2024-11-30', 0)": 0, + "(921, '2024-11-30', 1)": 0, + "(921, '2024-11-30', 2)": 0, + "(921, '2024-11-30', 3)": 0, + "(921, '2024-11-30', 4)": 0, + "(921, '2024-11-30', 5)": 0, + "(921, '2024-11-30', 6)": 0, + "(921, '2024-11-30', 7)": 0, + "(924, '2024-11-01', 0)": 0, + "(924, '2024-11-01', 1)": 0, + "(924, '2024-11-01', 2)": 0, + "(924, '2024-11-01', 3)": 0, + "(924, '2024-11-01', 4)": 0, + "(924, '2024-11-01', 5)": 0, + "(924, '2024-11-01', 6)": 0, + "(924, '2024-11-01', 7)": 0, + "(924, '2024-11-02', 0)": 0, + "(924, '2024-11-02', 1)": 0, + "(924, '2024-11-02', 2)": 0, + "(924, '2024-11-02', 3)": 0, + "(924, '2024-11-02', 4)": 0, + "(924, '2024-11-02', 5)": 0, + "(924, '2024-11-02', 6)": 0, + "(924, '2024-11-02', 7)": 0, + "(924, '2024-11-03', 0)": 0, + "(924, '2024-11-03', 1)": 0, + "(924, '2024-11-03', 2)": 0, + "(924, '2024-11-03', 3)": 0, + "(924, '2024-11-03', 4)": 0, + "(924, '2024-11-03', 5)": 0, + "(924, '2024-11-03', 6)": 0, + "(924, '2024-11-03', 7)": 0, + "(924, '2024-11-04', 0)": 0, + "(924, '2024-11-04', 1)": 0, + "(924, '2024-11-04', 2)": 0, + "(924, '2024-11-04', 3)": 0, + "(924, '2024-11-04', 4)": 0, + "(924, '2024-11-04', 5)": 0, + "(924, '2024-11-04', 6)": 0, + "(924, '2024-11-04', 7)": 0, + "(924, '2024-11-05', 0)": 0, + "(924, '2024-11-05', 1)": 0, + "(924, '2024-11-05', 2)": 0, + "(924, '2024-11-05', 3)": 0, + "(924, '2024-11-05', 4)": 0, + "(924, '2024-11-05', 5)": 0, + "(924, '2024-11-05', 6)": 0, + "(924, '2024-11-05', 7)": 0, + "(924, '2024-11-06', 0)": 0, + "(924, '2024-11-06', 1)": 0, + "(924, '2024-11-06', 2)": 0, + "(924, '2024-11-06', 3)": 0, + "(924, '2024-11-06', 4)": 0, + "(924, '2024-11-06', 5)": 0, + "(924, '2024-11-06', 6)": 0, + "(924, '2024-11-06', 7)": 0, + "(924, '2024-11-07', 0)": 0, + "(924, '2024-11-07', 1)": 0, + "(924, '2024-11-07', 2)": 0, + "(924, '2024-11-07', 3)": 0, + "(924, '2024-11-07', 4)": 0, + "(924, '2024-11-07', 5)": 0, + "(924, '2024-11-07', 6)": 0, + "(924, '2024-11-07', 7)": 0, + "(924, '2024-11-08', 0)": 0, + "(924, '2024-11-08', 1)": 0, + "(924, '2024-11-08', 2)": 0, + "(924, '2024-11-08', 3)": 0, + "(924, '2024-11-08', 4)": 0, + "(924, '2024-11-08', 5)": 0, + "(924, '2024-11-08', 6)": 0, + "(924, '2024-11-08', 7)": 0, + "(924, '2024-11-09', 0)": 0, + "(924, '2024-11-09', 1)": 0, + "(924, '2024-11-09', 2)": 0, + "(924, '2024-11-09', 3)": 0, + "(924, '2024-11-09', 4)": 0, + "(924, '2024-11-09', 5)": 0, + "(924, '2024-11-09', 6)": 0, + "(924, '2024-11-09', 7)": 0, + "(924, '2024-11-10', 0)": 1, + "(924, '2024-11-10', 1)": 0, + "(924, '2024-11-10', 2)": 0, + "(924, '2024-11-10', 3)": 0, + "(924, '2024-11-10', 4)": 0, + "(924, '2024-11-10', 5)": 0, + "(924, '2024-11-10', 6)": 0, + "(924, '2024-11-10', 7)": 0, + "(924, '2024-11-11', 0)": 1, + "(924, '2024-11-11', 1)": 0, + "(924, '2024-11-11', 2)": 0, + "(924, '2024-11-11', 3)": 0, + "(924, '2024-11-11', 4)": 0, + "(924, '2024-11-11', 5)": 0, + "(924, '2024-11-11', 6)": 0, + "(924, '2024-11-11', 7)": 0, + "(924, '2024-11-12', 0)": 0, + "(924, '2024-11-12', 1)": 0, + "(924, '2024-11-12', 2)": 0, + "(924, '2024-11-12', 3)": 0, + "(924, '2024-11-12', 4)": 0, + "(924, '2024-11-12', 5)": 0, + "(924, '2024-11-12', 6)": 0, + "(924, '2024-11-12', 7)": 0, + "(924, '2024-11-13', 0)": 0, + "(924, '2024-11-13', 1)": 0, + "(924, '2024-11-13', 2)": 0, + "(924, '2024-11-13', 3)": 0, + "(924, '2024-11-13', 4)": 0, + "(924, '2024-11-13', 5)": 0, + "(924, '2024-11-13', 6)": 0, + "(924, '2024-11-13', 7)": 0, + "(924, '2024-11-14', 0)": 0, + "(924, '2024-11-14', 1)": 0, + "(924, '2024-11-14', 2)": 0, + "(924, '2024-11-14', 3)": 0, + "(924, '2024-11-14', 4)": 0, + "(924, '2024-11-14', 5)": 0, + "(924, '2024-11-14', 6)": 0, + "(924, '2024-11-14', 7)": 0, + "(924, '2024-11-15', 0)": 0, + "(924, '2024-11-15', 1)": 0, + "(924, '2024-11-15', 2)": 0, + "(924, '2024-11-15', 3)": 0, + "(924, '2024-11-15', 4)": 0, + "(924, '2024-11-15', 5)": 0, + "(924, '2024-11-15', 6)": 0, + "(924, '2024-11-15', 7)": 0, + "(924, '2024-11-16', 0)": 0, + "(924, '2024-11-16', 1)": 0, + "(924, '2024-11-16', 2)": 0, + "(924, '2024-11-16', 3)": 0, + "(924, '2024-11-16', 4)": 0, + "(924, '2024-11-16', 5)": 0, + "(924, '2024-11-16', 6)": 0, + "(924, '2024-11-16', 7)": 0, + "(924, '2024-11-17', 0)": 0, + "(924, '2024-11-17', 1)": 0, + "(924, '2024-11-17', 2)": 0, + "(924, '2024-11-17', 3)": 0, + "(924, '2024-11-17', 4)": 0, + "(924, '2024-11-17', 5)": 0, + "(924, '2024-11-17', 6)": 0, + "(924, '2024-11-17', 7)": 0, + "(924, '2024-11-18', 0)": 0, + "(924, '2024-11-18', 1)": 0, + "(924, '2024-11-18', 2)": 0, + "(924, '2024-11-18', 3)": 0, + "(924, '2024-11-18', 4)": 0, + "(924, '2024-11-18', 5)": 0, + "(924, '2024-11-18', 6)": 0, + "(924, '2024-11-18', 7)": 0, + "(924, '2024-11-19', 0)": 0, + "(924, '2024-11-19', 1)": 0, + "(924, '2024-11-19', 2)": 0, + "(924, '2024-11-19', 3)": 0, + "(924, '2024-11-19', 4)": 0, + "(924, '2024-11-19', 5)": 0, + "(924, '2024-11-19', 6)": 0, + "(924, '2024-11-19', 7)": 0, + "(924, '2024-11-20', 0)": 0, + "(924, '2024-11-20', 1)": 0, + "(924, '2024-11-20', 2)": 0, + "(924, '2024-11-20', 3)": 0, + "(924, '2024-11-20', 4)": 0, + "(924, '2024-11-20', 5)": 0, + "(924, '2024-11-20', 6)": 0, + "(924, '2024-11-20', 7)": 0, + "(924, '2024-11-21', 0)": 0, + "(924, '2024-11-21', 1)": 0, + "(924, '2024-11-21', 2)": 0, + "(924, '2024-11-21', 3)": 0, + "(924, '2024-11-21', 4)": 0, + "(924, '2024-11-21', 5)": 0, + "(924, '2024-11-21', 6)": 0, + "(924, '2024-11-21', 7)": 0, + "(924, '2024-11-22', 0)": 0, + "(924, '2024-11-22', 1)": 0, + "(924, '2024-11-22', 2)": 0, + "(924, '2024-11-22', 3)": 0, + "(924, '2024-11-22', 4)": 0, + "(924, '2024-11-22', 5)": 0, + "(924, '2024-11-22', 6)": 0, + "(924, '2024-11-22', 7)": 0, + "(924, '2024-11-23', 0)": 0, + "(924, '2024-11-23', 1)": 0, + "(924, '2024-11-23', 2)": 0, + "(924, '2024-11-23', 3)": 0, + "(924, '2024-11-23', 4)": 0, + "(924, '2024-11-23', 5)": 0, + "(924, '2024-11-23', 6)": 0, + "(924, '2024-11-23', 7)": 0, + "(924, '2024-11-24', 0)": 0, + "(924, '2024-11-24', 1)": 1, + "(924, '2024-11-24', 2)": 0, + "(924, '2024-11-24', 3)": 0, + "(924, '2024-11-24', 4)": 0, + "(924, '2024-11-24', 5)": 0, + "(924, '2024-11-24', 6)": 0, + "(924, '2024-11-24', 7)": 0, + "(924, '2024-11-25', 0)": 1, + "(924, '2024-11-25', 1)": 0, + "(924, '2024-11-25', 2)": 0, + "(924, '2024-11-25', 3)": 0, + "(924, '2024-11-25', 4)": 0, + "(924, '2024-11-25', 5)": 0, + "(924, '2024-11-25', 6)": 0, + "(924, '2024-11-25', 7)": 0, + "(924, '2024-11-26', 0)": 0, + "(924, '2024-11-26', 1)": 0, + "(924, '2024-11-26', 2)": 0, + "(924, '2024-11-26', 3)": 0, + "(924, '2024-11-26', 4)": 0, + "(924, '2024-11-26', 5)": 0, + "(924, '2024-11-26', 6)": 0, + "(924, '2024-11-26', 7)": 0, + "(924, '2024-11-27', 0)": 0, + "(924, '2024-11-27', 1)": 0, + "(924, '2024-11-27', 2)": 1, + "(924, '2024-11-27', 3)": 0, + "(924, '2024-11-27', 4)": 0, + "(924, '2024-11-27', 5)": 0, + "(924, '2024-11-27', 6)": 0, + "(924, '2024-11-27', 7)": 0, + "(924, '2024-11-28', 0)": 0, + "(924, '2024-11-28', 1)": 0, + "(924, '2024-11-28', 2)": 0, + "(924, '2024-11-28', 3)": 0, + "(924, '2024-11-28', 4)": 0, + "(924, '2024-11-28', 5)": 0, + "(924, '2024-11-28', 6)": 0, + "(924, '2024-11-28', 7)": 0, + "(924, '2024-11-29', 0)": 0, + "(924, '2024-11-29', 1)": 0, + "(924, '2024-11-29', 2)": 0, + "(924, '2024-11-29', 3)": 0, + "(924, '2024-11-29', 4)": 0, + "(924, '2024-11-29', 5)": 0, + "(924, '2024-11-29', 6)": 0, + "(924, '2024-11-29', 7)": 0, + "(924, '2024-11-30', 0)": 0, + "(924, '2024-11-30', 1)": 1, + "(924, '2024-11-30', 2)": 0, + "(924, '2024-11-30', 3)": 0, + "(924, '2024-11-30', 4)": 0, + "(924, '2024-11-30', 5)": 0, + "(924, '2024-11-30', 6)": 0, + "(924, '2024-11-30', 7)": 0, + "(925, '2024-11-01', 0)": 0, + "(925, '2024-11-01', 1)": 0, + "(925, '2024-11-01', 2)": 0, + "(925, '2024-11-01', 3)": 0, + "(925, '2024-11-01', 4)": 0, + "(925, '2024-11-01', 5)": 0, + "(925, '2024-11-01', 6)": 0, + "(925, '2024-11-01', 7)": 0, + "(925, '2024-11-02', 0)": 0, + "(925, '2024-11-02', 1)": 0, + "(925, '2024-11-02', 2)": 0, + "(925, '2024-11-02', 3)": 0, + "(925, '2024-11-02', 4)": 0, + "(925, '2024-11-02', 5)": 0, + "(925, '2024-11-02', 6)": 0, + "(925, '2024-11-02', 7)": 0, + "(925, '2024-11-03', 0)": 0, + "(925, '2024-11-03', 1)": 0, + "(925, '2024-11-03', 2)": 0, + "(925, '2024-11-03', 3)": 1, + "(925, '2024-11-03', 4)": 0, + "(925, '2024-11-03', 5)": 0, + "(925, '2024-11-03', 6)": 0, + "(925, '2024-11-03', 7)": 0, + "(925, '2024-11-04', 0)": 0, + "(925, '2024-11-04', 1)": 0, + "(925, '2024-11-04', 2)": 0, + "(925, '2024-11-04', 3)": 1, + "(925, '2024-11-04', 4)": 0, + "(925, '2024-11-04', 5)": 0, + "(925, '2024-11-04', 6)": 0, + "(925, '2024-11-04', 7)": 0, + "(925, '2024-11-05', 0)": 0, + "(925, '2024-11-05', 1)": 0, + "(925, '2024-11-05', 2)": 0, + "(925, '2024-11-05', 3)": 0, + "(925, '2024-11-05', 4)": 0, + "(925, '2024-11-05', 5)": 0, + "(925, '2024-11-05', 6)": 0, + "(925, '2024-11-05', 7)": 0, + "(925, '2024-11-06', 0)": 0, + "(925, '2024-11-06', 1)": 0, + "(925, '2024-11-06', 2)": 0, + "(925, '2024-11-06', 3)": 0, + "(925, '2024-11-06', 4)": 0, + "(925, '2024-11-06', 5)": 0, + "(925, '2024-11-06', 6)": 0, + "(925, '2024-11-06', 7)": 0, + "(925, '2024-11-07', 0)": 0, + "(925, '2024-11-07', 1)": 0, + "(925, '2024-11-07', 2)": 0, + "(925, '2024-11-07', 3)": 1, + "(925, '2024-11-07', 4)": 0, + "(925, '2024-11-07', 5)": 0, + "(925, '2024-11-07', 6)": 0, + "(925, '2024-11-07', 7)": 0, + "(925, '2024-11-08', 0)": 0, + "(925, '2024-11-08', 1)": 0, + "(925, '2024-11-08', 2)": 0, + "(925, '2024-11-08', 3)": 0, + "(925, '2024-11-08', 4)": 0, + "(925, '2024-11-08', 5)": 0, + "(925, '2024-11-08', 6)": 0, + "(925, '2024-11-08', 7)": 0, + "(925, '2024-11-09', 0)": 0, + "(925, '2024-11-09', 1)": 0, + "(925, '2024-11-09', 2)": 0, + "(925, '2024-11-09', 3)": 1, + "(925, '2024-11-09', 4)": 0, + "(925, '2024-11-09', 5)": 0, + "(925, '2024-11-09', 6)": 0, + "(925, '2024-11-09', 7)": 0, + "(925, '2024-11-10', 0)": 0, + "(925, '2024-11-10', 1)": 0, + "(925, '2024-11-10', 2)": 0, + "(925, '2024-11-10', 3)": 0, + "(925, '2024-11-10', 4)": 0, + "(925, '2024-11-10', 5)": 0, + "(925, '2024-11-10', 6)": 0, + "(925, '2024-11-10', 7)": 0, + "(925, '2024-11-11', 0)": 0, + "(925, '2024-11-11', 1)": 0, + "(925, '2024-11-11', 2)": 0, + "(925, '2024-11-11', 3)": 0, + "(925, '2024-11-11', 4)": 0, + "(925, '2024-11-11', 5)": 0, + "(925, '2024-11-11', 6)": 0, + "(925, '2024-11-11', 7)": 0, + "(925, '2024-11-12', 0)": 0, + "(925, '2024-11-12', 1)": 0, + "(925, '2024-11-12', 2)": 0, + "(925, '2024-11-12', 3)": 1, + "(925, '2024-11-12', 4)": 0, + "(925, '2024-11-12', 5)": 0, + "(925, '2024-11-12', 6)": 0, + "(925, '2024-11-12', 7)": 0, + "(925, '2024-11-13', 0)": 0, + "(925, '2024-11-13', 1)": 0, + "(925, '2024-11-13', 2)": 0, + "(925, '2024-11-13', 3)": 1, + "(925, '2024-11-13', 4)": 0, + "(925, '2024-11-13', 5)": 0, + "(925, '2024-11-13', 6)": 0, + "(925, '2024-11-13', 7)": 0, + "(925, '2024-11-14', 0)": 0, + "(925, '2024-11-14', 1)": 0, + "(925, '2024-11-14', 2)": 0, + "(925, '2024-11-14', 3)": 0, + "(925, '2024-11-14', 4)": 0, + "(925, '2024-11-14', 5)": 0, + "(925, '2024-11-14', 6)": 0, + "(925, '2024-11-14', 7)": 0, + "(925, '2024-11-15', 0)": 0, + "(925, '2024-11-15', 1)": 0, + "(925, '2024-11-15', 2)": 0, + "(925, '2024-11-15', 3)": 0, + "(925, '2024-11-15', 4)": 0, + "(925, '2024-11-15', 5)": 0, + "(925, '2024-11-15', 6)": 0, + "(925, '2024-11-15', 7)": 0, + "(925, '2024-11-16', 0)": 0, + "(925, '2024-11-16', 1)": 0, + "(925, '2024-11-16', 2)": 0, + "(925, '2024-11-16', 3)": 0, + "(925, '2024-11-16', 4)": 0, + "(925, '2024-11-16', 5)": 0, + "(925, '2024-11-16', 6)": 0, + "(925, '2024-11-16', 7)": 0, + "(925, '2024-11-17', 0)": 0, + "(925, '2024-11-17', 1)": 0, + "(925, '2024-11-17', 2)": 0, + "(925, '2024-11-17', 3)": 0, + "(925, '2024-11-17', 4)": 0, + "(925, '2024-11-17', 5)": 0, + "(925, '2024-11-17', 6)": 0, + "(925, '2024-11-17', 7)": 0, + "(925, '2024-11-18', 0)": 0, + "(925, '2024-11-18', 1)": 0, + "(925, '2024-11-18', 2)": 0, + "(925, '2024-11-18', 3)": 0, + "(925, '2024-11-18', 4)": 0, + "(925, '2024-11-18', 5)": 0, + "(925, '2024-11-18', 6)": 0, + "(925, '2024-11-18', 7)": 0, + "(925, '2024-11-19', 0)": 0, + "(925, '2024-11-19', 1)": 0, + "(925, '2024-11-19', 2)": 0, + "(925, '2024-11-19', 3)": 0, + "(925, '2024-11-19', 4)": 0, + "(925, '2024-11-19', 5)": 0, + "(925, '2024-11-19', 6)": 0, + "(925, '2024-11-19', 7)": 0, + "(925, '2024-11-20', 0)": 0, + "(925, '2024-11-20', 1)": 0, + "(925, '2024-11-20', 2)": 0, + "(925, '2024-11-20', 3)": 0, + "(925, '2024-11-20', 4)": 0, + "(925, '2024-11-20', 5)": 0, + "(925, '2024-11-20', 6)": 0, + "(925, '2024-11-20', 7)": 0, + "(925, '2024-11-21', 0)": 0, + "(925, '2024-11-21', 1)": 0, + "(925, '2024-11-21', 2)": 0, + "(925, '2024-11-21', 3)": 0, + "(925, '2024-11-21', 4)": 0, + "(925, '2024-11-21', 5)": 0, + "(925, '2024-11-21', 6)": 0, + "(925, '2024-11-21', 7)": 0, + "(925, '2024-11-22', 0)": 0, + "(925, '2024-11-22', 1)": 0, + "(925, '2024-11-22', 2)": 0, + "(925, '2024-11-22', 3)": 0, + "(925, '2024-11-22', 4)": 0, + "(925, '2024-11-22', 5)": 0, + "(925, '2024-11-22', 6)": 0, + "(925, '2024-11-22', 7)": 0, + "(925, '2024-11-23', 0)": 0, + "(925, '2024-11-23', 1)": 0, + "(925, '2024-11-23', 2)": 0, + "(925, '2024-11-23', 3)": 1, + "(925, '2024-11-23', 4)": 0, + "(925, '2024-11-23', 5)": 0, + "(925, '2024-11-23', 6)": 0, + "(925, '2024-11-23', 7)": 0, + "(925, '2024-11-24', 0)": 0, + "(925, '2024-11-24', 1)": 0, + "(925, '2024-11-24', 2)": 0, + "(925, '2024-11-24', 3)": 1, + "(925, '2024-11-24', 4)": 0, + "(925, '2024-11-24', 5)": 0, + "(925, '2024-11-24', 6)": 0, + "(925, '2024-11-24', 7)": 0, + "(925, '2024-11-25', 0)": 0, + "(925, '2024-11-25', 1)": 0, + "(925, '2024-11-25', 2)": 0, + "(925, '2024-11-25', 3)": 0, + "(925, '2024-11-25', 4)": 0, + "(925, '2024-11-25', 5)": 0, + "(925, '2024-11-25', 6)": 0, + "(925, '2024-11-25', 7)": 0, + "(925, '2024-11-26', 0)": 0, + "(925, '2024-11-26', 1)": 0, + "(925, '2024-11-26', 2)": 0, + "(925, '2024-11-26', 3)": 0, + "(925, '2024-11-26', 4)": 0, + "(925, '2024-11-26', 5)": 0, + "(925, '2024-11-26', 6)": 0, + "(925, '2024-11-26', 7)": 0, + "(925, '2024-11-27', 0)": 0, + "(925, '2024-11-27', 1)": 0, + "(925, '2024-11-27', 2)": 0, + "(925, '2024-11-27', 3)": 0, + "(925, '2024-11-27', 4)": 0, + "(925, '2024-11-27', 5)": 0, + "(925, '2024-11-27', 6)": 0, + "(925, '2024-11-27', 7)": 0, + "(925, '2024-11-28', 0)": 0, + "(925, '2024-11-28', 1)": 0, + "(925, '2024-11-28', 2)": 0, + "(925, '2024-11-28', 3)": 1, + "(925, '2024-11-28', 4)": 0, + "(925, '2024-11-28', 5)": 0, + "(925, '2024-11-28', 6)": 0, + "(925, '2024-11-28', 7)": 0, + "(925, '2024-11-29', 0)": 0, + "(925, '2024-11-29', 1)": 0, + "(925, '2024-11-29', 2)": 0, + "(925, '2024-11-29', 3)": 1, + "(925, '2024-11-29', 4)": 0, + "(925, '2024-11-29', 5)": 0, + "(925, '2024-11-29', 6)": 0, + "(925, '2024-11-29', 7)": 0, + "(925, '2024-11-30', 0)": 0, + "(925, '2024-11-30', 1)": 0, + "(925, '2024-11-30', 2)": 0, + "(925, '2024-11-30', 3)": 0, + "(925, '2024-11-30', 4)": 0, + "(925, '2024-11-30', 5)": 0, + "(925, '2024-11-30', 6)": 0, + "(925, '2024-11-30', 7)": 0, + "(927, '2024-11-01', 0)": 0, + "(927, '2024-11-01', 1)": 0, + "(927, '2024-11-01', 2)": 0, + "(927, '2024-11-01', 3)": 0, + "(927, '2024-11-01', 4)": 0, + "(927, '2024-11-01', 5)": 0, + "(927, '2024-11-01', 6)": 0, + "(927, '2024-11-01', 7)": 0, + "(927, '2024-11-02', 0)": 0, + "(927, '2024-11-02', 1)": 0, + "(927, '2024-11-02', 2)": 0, + "(927, '2024-11-02', 3)": 1, + "(927, '2024-11-02', 4)": 0, + "(927, '2024-11-02', 5)": 0, + "(927, '2024-11-02', 6)": 0, + "(927, '2024-11-02', 7)": 0, + "(927, '2024-11-03', 0)": 0, + "(927, '2024-11-03', 1)": 0, + "(927, '2024-11-03', 2)": 0, + "(927, '2024-11-03', 3)": 0, + "(927, '2024-11-03', 4)": 0, + "(927, '2024-11-03', 5)": 0, + "(927, '2024-11-03', 6)": 0, + "(927, '2024-11-03', 7)": 0, + "(927, '2024-11-04', 0)": 0, + "(927, '2024-11-04', 1)": 0, + "(927, '2024-11-04', 2)": 0, + "(927, '2024-11-04', 3)": 0, + "(927, '2024-11-04', 4)": 0, + "(927, '2024-11-04', 5)": 0, + "(927, '2024-11-04', 6)": 0, + "(927, '2024-11-04', 7)": 0, + "(927, '2024-11-05', 0)": 1, + "(927, '2024-11-05', 1)": 0, + "(927, '2024-11-05', 2)": 0, + "(927, '2024-11-05', 3)": 0, + "(927, '2024-11-05', 4)": 0, + "(927, '2024-11-05', 5)": 0, + "(927, '2024-11-05', 6)": 0, + "(927, '2024-11-05', 7)": 0, + "(927, '2024-11-06', 0)": 0, + "(927, '2024-11-06', 1)": 1, + "(927, '2024-11-06', 2)": 0, + "(927, '2024-11-06', 3)": 0, + "(927, '2024-11-06', 4)": 0, + "(927, '2024-11-06', 5)": 0, + "(927, '2024-11-06', 6)": 0, + "(927, '2024-11-06', 7)": 0, + "(927, '2024-11-07', 0)": 0, + "(927, '2024-11-07', 1)": 0, + "(927, '2024-11-07', 2)": 1, + "(927, '2024-11-07', 3)": 0, + "(927, '2024-11-07', 4)": 0, + "(927, '2024-11-07', 5)": 0, + "(927, '2024-11-07', 6)": 0, + "(927, '2024-11-07', 7)": 0, + "(927, '2024-11-08', 0)": 0, + "(927, '2024-11-08', 1)": 0, + "(927, '2024-11-08', 2)": 1, + "(927, '2024-11-08', 3)": 0, + "(927, '2024-11-08', 4)": 0, + "(927, '2024-11-08', 5)": 0, + "(927, '2024-11-08', 6)": 0, + "(927, '2024-11-08', 7)": 0, + "(927, '2024-11-09', 0)": 0, + "(927, '2024-11-09', 1)": 0, + "(927, '2024-11-09', 2)": 0, + "(927, '2024-11-09', 3)": 1, + "(927, '2024-11-09', 4)": 0, + "(927, '2024-11-09', 5)": 0, + "(927, '2024-11-09', 6)": 0, + "(927, '2024-11-09', 7)": 0, + "(927, '2024-11-10', 0)": 0, + "(927, '2024-11-10', 1)": 0, + "(927, '2024-11-10', 2)": 0, + "(927, '2024-11-10', 3)": 0, + "(927, '2024-11-10', 4)": 0, + "(927, '2024-11-10', 5)": 0, + "(927, '2024-11-10', 6)": 0, + "(927, '2024-11-10', 7)": 0, + "(927, '2024-11-11', 0)": 1, + "(927, '2024-11-11', 1)": 0, + "(927, '2024-11-11', 2)": 0, + "(927, '2024-11-11', 3)": 0, + "(927, '2024-11-11', 4)": 0, + "(927, '2024-11-11', 5)": 0, + "(927, '2024-11-11', 6)": 0, + "(927, '2024-11-11', 7)": 0, + "(927, '2024-11-12', 0)": 0, + "(927, '2024-11-12', 1)": 0, + "(927, '2024-11-12', 2)": 0, + "(927, '2024-11-12', 3)": 0, + "(927, '2024-11-12', 4)": 0, + "(927, '2024-11-12', 5)": 0, + "(927, '2024-11-12', 6)": 0, + "(927, '2024-11-12', 7)": 0, + "(927, '2024-11-13', 0)": 1, + "(927, '2024-11-13', 1)": 0, + "(927, '2024-11-13', 2)": 0, + "(927, '2024-11-13', 3)": 0, + "(927, '2024-11-13', 4)": 0, + "(927, '2024-11-13', 5)": 0, + "(927, '2024-11-13', 6)": 0, + "(927, '2024-11-13', 7)": 0, + "(927, '2024-11-14', 0)": 0, + "(927, '2024-11-14', 1)": 0, + "(927, '2024-11-14', 2)": 1, + "(927, '2024-11-14', 3)": 0, + "(927, '2024-11-14', 4)": 0, + "(927, '2024-11-14', 5)": 0, + "(927, '2024-11-14', 6)": 0, + "(927, '2024-11-14', 7)": 0, + "(927, '2024-11-15', 0)": 0, + "(927, '2024-11-15', 1)": 0, + "(927, '2024-11-15', 2)": 1, + "(927, '2024-11-15', 3)": 0, + "(927, '2024-11-15', 4)": 0, + "(927, '2024-11-15', 5)": 0, + "(927, '2024-11-15', 6)": 0, + "(927, '2024-11-15', 7)": 0, + "(927, '2024-11-16', 0)": 0, + "(927, '2024-11-16', 1)": 0, + "(927, '2024-11-16', 2)": 1, + "(927, '2024-11-16', 3)": 0, + "(927, '2024-11-16', 4)": 0, + "(927, '2024-11-16', 5)": 0, + "(927, '2024-11-16', 6)": 0, + "(927, '2024-11-16', 7)": 0, + "(927, '2024-11-17', 0)": 0, + "(927, '2024-11-17', 1)": 0, + "(927, '2024-11-17', 2)": 1, + "(927, '2024-11-17', 3)": 0, + "(927, '2024-11-17', 4)": 0, + "(927, '2024-11-17', 5)": 0, + "(927, '2024-11-17', 6)": 0, + "(927, '2024-11-17', 7)": 0, + "(927, '2024-11-18', 0)": 0, + "(927, '2024-11-18', 1)": 0, + "(927, '2024-11-18', 2)": 0, + "(927, '2024-11-18', 3)": 0, + "(927, '2024-11-18', 4)": 0, + "(927, '2024-11-18', 5)": 0, + "(927, '2024-11-18', 6)": 0, + "(927, '2024-11-18', 7)": 0, + "(927, '2024-11-19', 0)": 0, + "(927, '2024-11-19', 1)": 0, + "(927, '2024-11-19', 2)": 0, + "(927, '2024-11-19', 3)": 0, + "(927, '2024-11-19', 4)": 0, + "(927, '2024-11-19', 5)": 0, + "(927, '2024-11-19', 6)": 0, + "(927, '2024-11-19', 7)": 0, + "(927, '2024-11-20', 0)": 0, + "(927, '2024-11-20', 1)": 1, + "(927, '2024-11-20', 2)": 0, + "(927, '2024-11-20', 3)": 0, + "(927, '2024-11-20', 4)": 0, + "(927, '2024-11-20', 5)": 0, + "(927, '2024-11-20', 6)": 0, + "(927, '2024-11-20', 7)": 0, + "(927, '2024-11-21', 0)": 0, + "(927, '2024-11-21', 1)": 0, + "(927, '2024-11-21', 2)": 0, + "(927, '2024-11-21', 3)": 0, + "(927, '2024-11-21', 4)": 0, + "(927, '2024-11-21', 5)": 0, + "(927, '2024-11-21', 6)": 0, + "(927, '2024-11-21', 7)": 0, + "(927, '2024-11-22', 0)": 0, + "(927, '2024-11-22', 1)": 1, + "(927, '2024-11-22', 2)": 0, + "(927, '2024-11-22', 3)": 0, + "(927, '2024-11-22', 4)": 0, + "(927, '2024-11-22', 5)": 0, + "(927, '2024-11-22', 6)": 0, + "(927, '2024-11-22', 7)": 0, + "(927, '2024-11-23', 0)": 0, + "(927, '2024-11-23', 1)": 0, + "(927, '2024-11-23', 2)": 0, + "(927, '2024-11-23', 3)": 0, + "(927, '2024-11-23', 4)": 0, + "(927, '2024-11-23', 5)": 0, + "(927, '2024-11-23', 6)": 0, + "(927, '2024-11-23', 7)": 0, + "(927, '2024-11-24', 0)": 0, + "(927, '2024-11-24', 1)": 0, + "(927, '2024-11-24', 2)": 0, + "(927, '2024-11-24', 3)": 1, + "(927, '2024-11-24', 4)": 0, + "(927, '2024-11-24', 5)": 0, + "(927, '2024-11-24', 6)": 0, + "(927, '2024-11-24', 7)": 0, + "(927, '2024-11-25', 0)": 0, + "(927, '2024-11-25', 1)": 0, + "(927, '2024-11-25', 2)": 0, + "(927, '2024-11-25', 3)": 0, + "(927, '2024-11-25', 4)": 0, + "(927, '2024-11-25', 5)": 0, + "(927, '2024-11-25', 6)": 0, + "(927, '2024-11-25', 7)": 0, + "(927, '2024-11-26', 0)": 1, + "(927, '2024-11-26', 1)": 0, + "(927, '2024-11-26', 2)": 0, + "(927, '2024-11-26', 3)": 0, + "(927, '2024-11-26', 4)": 0, + "(927, '2024-11-26', 5)": 0, + "(927, '2024-11-26', 6)": 0, + "(927, '2024-11-26', 7)": 0, + "(927, '2024-11-27', 0)": 0, + "(927, '2024-11-27', 1)": 0, + "(927, '2024-11-27', 2)": 0, + "(927, '2024-11-27', 3)": 0, + "(927, '2024-11-27', 4)": 0, + "(927, '2024-11-27', 5)": 0, + "(927, '2024-11-27', 6)": 0, + "(927, '2024-11-27', 7)": 0, + "(927, '2024-11-28', 0)": 0, + "(927, '2024-11-28', 1)": 0, + "(927, '2024-11-28', 2)": 0, + "(927, '2024-11-28', 3)": 0, + "(927, '2024-11-28', 4)": 0, + "(927, '2024-11-28', 5)": 0, + "(927, '2024-11-28', 6)": 0, + "(927, '2024-11-28', 7)": 0, + "(927, '2024-11-29', 0)": 1, + "(927, '2024-11-29', 1)": 0, + "(927, '2024-11-29', 2)": 0, + "(927, '2024-11-29', 3)": 0, + "(927, '2024-11-29', 4)": 0, + "(927, '2024-11-29', 5)": 0, + "(927, '2024-11-29', 6)": 0, + "(927, '2024-11-29', 7)": 0, + "(927, '2024-11-30', 0)": 0, + "(927, '2024-11-30', 1)": 0, + "(927, '2024-11-30', 2)": 1, + "(927, '2024-11-30', 3)": 0, + "(927, '2024-11-30', 4)": 0, + "(927, '2024-11-30', 5)": 0, + "(927, '2024-11-30', 6)": 0, + "(927, '2024-11-30', 7)": 0, + "(928, '2024-11-01', 0)": 0, + "(928, '2024-11-01', 1)": 0, + "(928, '2024-11-01', 2)": 0, + "(928, '2024-11-01', 3)": 0, + "(928, '2024-11-01', 4)": 0, + "(928, '2024-11-01', 5)": 0, + "(928, '2024-11-01', 6)": 0, + "(928, '2024-11-01', 7)": 0, + "(928, '2024-11-02', 0)": 0, + "(928, '2024-11-02', 1)": 0, + "(928, '2024-11-02', 2)": 0, + "(928, '2024-11-02', 3)": 0, + "(928, '2024-11-02', 4)": 0, + "(928, '2024-11-02', 5)": 0, + "(928, '2024-11-02', 6)": 0, + "(928, '2024-11-02', 7)": 0, + "(928, '2024-11-03', 0)": 0, + "(928, '2024-11-03', 1)": 0, + "(928, '2024-11-03', 2)": 0, + "(928, '2024-11-03', 3)": 0, + "(928, '2024-11-03', 4)": 0, + "(928, '2024-11-03', 5)": 0, + "(928, '2024-11-03', 6)": 0, + "(928, '2024-11-03', 7)": 0, + "(928, '2024-11-04', 0)": 0, + "(928, '2024-11-04', 1)": 0, + "(928, '2024-11-04', 2)": 0, + "(928, '2024-11-04', 3)": 1, + "(928, '2024-11-04', 4)": 0, + "(928, '2024-11-04', 5)": 0, + "(928, '2024-11-04', 6)": 0, + "(928, '2024-11-04', 7)": 0, + "(928, '2024-11-05', 0)": 0, + "(928, '2024-11-05', 1)": 0, + "(928, '2024-11-05', 2)": 0, + "(928, '2024-11-05', 3)": 0, + "(928, '2024-11-05', 4)": 0, + "(928, '2024-11-05', 5)": 0, + "(928, '2024-11-05', 6)": 0, + "(928, '2024-11-05', 7)": 0, + "(928, '2024-11-06', 0)": 0, + "(928, '2024-11-06', 1)": 0, + "(928, '2024-11-06', 2)": 0, + "(928, '2024-11-06', 3)": 0, + "(928, '2024-11-06', 4)": 0, + "(928, '2024-11-06', 5)": 0, + "(928, '2024-11-06', 6)": 0, + "(928, '2024-11-06', 7)": 0, + "(928, '2024-11-07', 0)": 0, + "(928, '2024-11-07', 1)": 0, + "(928, '2024-11-07', 2)": 0, + "(928, '2024-11-07', 3)": 0, + "(928, '2024-11-07', 4)": 0, + "(928, '2024-11-07', 5)": 0, + "(928, '2024-11-07', 6)": 0, + "(928, '2024-11-07', 7)": 0, + "(928, '2024-11-08', 0)": 0, + "(928, '2024-11-08', 1)": 0, + "(928, '2024-11-08', 2)": 0, + "(928, '2024-11-08', 3)": 0, + "(928, '2024-11-08', 4)": 0, + "(928, '2024-11-08', 5)": 0, + "(928, '2024-11-08', 6)": 0, + "(928, '2024-11-08', 7)": 0, + "(928, '2024-11-09', 0)": 0, + "(928, '2024-11-09', 1)": 0, + "(928, '2024-11-09', 2)": 0, + "(928, '2024-11-09', 3)": 0, + "(928, '2024-11-09', 4)": 0, + "(928, '2024-11-09', 5)": 0, + "(928, '2024-11-09', 6)": 0, + "(928, '2024-11-09', 7)": 0, + "(928, '2024-11-10', 0)": 0, + "(928, '2024-11-10', 1)": 0, + "(928, '2024-11-10', 2)": 0, + "(928, '2024-11-10', 3)": 0, + "(928, '2024-11-10', 4)": 0, + "(928, '2024-11-10', 5)": 0, + "(928, '2024-11-10', 6)": 0, + "(928, '2024-11-10', 7)": 0, + "(928, '2024-11-11', 0)": 0, + "(928, '2024-11-11', 1)": 0, + "(928, '2024-11-11', 2)": 0, + "(928, '2024-11-11', 3)": 0, + "(928, '2024-11-11', 4)": 0, + "(928, '2024-11-11', 5)": 0, + "(928, '2024-11-11', 6)": 0, + "(928, '2024-11-11', 7)": 0, + "(928, '2024-11-12', 0)": 0, + "(928, '2024-11-12', 1)": 0, + "(928, '2024-11-12', 2)": 0, + "(928, '2024-11-12', 3)": 0, + "(928, '2024-11-12', 4)": 0, + "(928, '2024-11-12', 5)": 0, + "(928, '2024-11-12', 6)": 0, + "(928, '2024-11-12', 7)": 0, + "(928, '2024-11-13', 0)": 0, + "(928, '2024-11-13', 1)": 0, + "(928, '2024-11-13', 2)": 0, + "(928, '2024-11-13', 3)": 1, + "(928, '2024-11-13', 4)": 0, + "(928, '2024-11-13', 5)": 0, + "(928, '2024-11-13', 6)": 0, + "(928, '2024-11-13', 7)": 0, + "(928, '2024-11-14', 0)": 0, + "(928, '2024-11-14', 1)": 0, + "(928, '2024-11-14', 2)": 0, + "(928, '2024-11-14', 3)": 1, + "(928, '2024-11-14', 4)": 0, + "(928, '2024-11-14', 5)": 0, + "(928, '2024-11-14', 6)": 0, + "(928, '2024-11-14', 7)": 0, + "(928, '2024-11-15', 0)": 0, + "(928, '2024-11-15', 1)": 0, + "(928, '2024-11-15', 2)": 0, + "(928, '2024-11-15', 3)": 0, + "(928, '2024-11-15', 4)": 0, + "(928, '2024-11-15', 5)": 0, + "(928, '2024-11-15', 6)": 0, + "(928, '2024-11-15', 7)": 0, + "(928, '2024-11-16', 0)": 0, + "(928, '2024-11-16', 1)": 0, + "(928, '2024-11-16', 2)": 0, + "(928, '2024-11-16', 3)": 0, + "(928, '2024-11-16', 4)": 0, + "(928, '2024-11-16', 5)": 0, + "(928, '2024-11-16', 6)": 0, + "(928, '2024-11-16', 7)": 0, + "(928, '2024-11-17', 0)": 0, + "(928, '2024-11-17', 1)": 0, + "(928, '2024-11-17', 2)": 0, + "(928, '2024-11-17', 3)": 0, + "(928, '2024-11-17', 4)": 0, + "(928, '2024-11-17', 5)": 0, + "(928, '2024-11-17', 6)": 0, + "(928, '2024-11-17', 7)": 0, + "(928, '2024-11-18', 0)": 0, + "(928, '2024-11-18', 1)": 0, + "(928, '2024-11-18', 2)": 0, + "(928, '2024-11-18', 3)": 0, + "(928, '2024-11-18', 4)": 0, + "(928, '2024-11-18', 5)": 0, + "(928, '2024-11-18', 6)": 0, + "(928, '2024-11-18', 7)": 0, + "(928, '2024-11-19', 0)": 0, + "(928, '2024-11-19', 1)": 0, + "(928, '2024-11-19', 2)": 0, + "(928, '2024-11-19', 3)": 0, + "(928, '2024-11-19', 4)": 0, + "(928, '2024-11-19', 5)": 0, + "(928, '2024-11-19', 6)": 0, + "(928, '2024-11-19', 7)": 0, + "(928, '2024-11-20', 0)": 0, + "(928, '2024-11-20', 1)": 0, + "(928, '2024-11-20', 2)": 0, + "(928, '2024-11-20', 3)": 0, + "(928, '2024-11-20', 4)": 0, + "(928, '2024-11-20', 5)": 0, + "(928, '2024-11-20', 6)": 0, + "(928, '2024-11-20', 7)": 0, + "(928, '2024-11-21', 0)": 0, + "(928, '2024-11-21', 1)": 0, + "(928, '2024-11-21', 2)": 0, + "(928, '2024-11-21', 3)": 1, + "(928, '2024-11-21', 4)": 0, + "(928, '2024-11-21', 5)": 0, + "(928, '2024-11-21', 6)": 0, + "(928, '2024-11-21', 7)": 0, + "(928, '2024-11-22', 0)": 0, + "(928, '2024-11-22', 1)": 0, + "(928, '2024-11-22', 2)": 0, + "(928, '2024-11-22', 3)": 0, + "(928, '2024-11-22', 4)": 0, + "(928, '2024-11-22', 5)": 0, + "(928, '2024-11-22', 6)": 0, + "(928, '2024-11-22', 7)": 0, + "(928, '2024-11-23', 0)": 0, + "(928, '2024-11-23', 1)": 0, + "(928, '2024-11-23', 2)": 0, + "(928, '2024-11-23', 3)": 0, + "(928, '2024-11-23', 4)": 0, + "(928, '2024-11-23', 5)": 0, + "(928, '2024-11-23', 6)": 0, + "(928, '2024-11-23', 7)": 0, + "(928, '2024-11-24', 0)": 0, + "(928, '2024-11-24', 1)": 0, + "(928, '2024-11-24', 2)": 0, + "(928, '2024-11-24', 3)": 0, + "(928, '2024-11-24', 4)": 0, + "(928, '2024-11-24', 5)": 0, + "(928, '2024-11-24', 6)": 0, + "(928, '2024-11-24', 7)": 0, + "(928, '2024-11-25', 0)": 0, + "(928, '2024-11-25', 1)": 0, + "(928, '2024-11-25', 2)": 0, + "(928, '2024-11-25', 3)": 0, + "(928, '2024-11-25', 4)": 0, + "(928, '2024-11-25', 5)": 0, + "(928, '2024-11-25', 6)": 0, + "(928, '2024-11-25', 7)": 0, + "(928, '2024-11-26', 0)": 0, + "(928, '2024-11-26', 1)": 0, + "(928, '2024-11-26', 2)": 0, + "(928, '2024-11-26', 3)": 0, + "(928, '2024-11-26', 4)": 0, + "(928, '2024-11-26', 5)": 0, + "(928, '2024-11-26', 6)": 0, + "(928, '2024-11-26', 7)": 0, + "(928, '2024-11-27', 0)": 0, + "(928, '2024-11-27', 1)": 0, + "(928, '2024-11-27', 2)": 0, + "(928, '2024-11-27', 3)": 0, + "(928, '2024-11-27', 4)": 0, + "(928, '2024-11-27', 5)": 0, + "(928, '2024-11-27', 6)": 0, + "(928, '2024-11-27', 7)": 0, + "(928, '2024-11-28', 0)": 0, + "(928, '2024-11-28', 1)": 0, + "(928, '2024-11-28', 2)": 0, + "(928, '2024-11-28', 3)": 0, + "(928, '2024-11-28', 4)": 0, + "(928, '2024-11-28', 5)": 0, + "(928, '2024-11-28', 6)": 0, + "(928, '2024-11-28', 7)": 0, + "(928, '2024-11-29', 0)": 0, + "(928, '2024-11-29', 1)": 0, + "(928, '2024-11-29', 2)": 0, + "(928, '2024-11-29', 3)": 0, + "(928, '2024-11-29', 4)": 0, + "(928, '2024-11-29', 5)": 0, + "(928, '2024-11-29', 6)": 0, + "(928, '2024-11-29', 7)": 0, + "(928, '2024-11-30', 0)": 0, + "(928, '2024-11-30', 1)": 0, + "(928, '2024-11-30', 2)": 0, + "(928, '2024-11-30', 3)": 0, + "(928, '2024-11-30', 4)": 0, + "(928, '2024-11-30', 5)": 0, + "(928, '2024-11-30', 6)": 0, + "(928, '2024-11-30', 7)": 0, + "e:0_d:2024-11-01": 0, + "e:0_d:2024-11-02": 1, + "e:0_d:2024-11-03": 1, + "e:0_d:2024-11-04": 1, + "e:0_d:2024-11-05": 1, + "e:0_d:2024-11-06": 0, + "e:0_d:2024-11-07": 0, + "e:0_d:2024-11-08": 0, + "e:0_d:2024-11-09": 1, + "e:0_d:2024-11-10": 1, + "e:0_d:2024-11-11": 1, + "e:0_d:2024-11-12": 1, + "e:0_d:2024-11-13": 1, + "e:0_d:2024-11-14": 0, + "e:0_d:2024-11-15": 1, + "e:0_d:2024-11-16": 1, + "e:0_d:2024-11-17": 1, + "e:0_d:2024-11-18": 0, + "e:0_d:2024-11-19": 1, + "e:0_d:2024-11-20": 1, + "e:0_d:2024-11-21": 1, + "e:0_d:2024-11-22": 1, + "e:0_d:2024-11-23": 1, + "e:0_d:2024-11-24": 1, + "e:0_d:2024-11-25": 1, + "e:0_d:2024-11-26": 0, + "e:0_d:2024-11-27": 1, + "e:0_d:2024-11-28": 0, + "e:0_d:2024-11-29": 0, + "e:0_d:2024-11-30": 1, + "e:1230_d:2024-11-01": 0, + "e:1230_d:2024-11-02": 1, + "e:1230_d:2024-11-03": 0, + "e:1230_d:2024-11-04": 1, + "e:1230_d:2024-11-05": 0, + "e:1230_d:2024-11-06": 1, + "e:1230_d:2024-11-07": 1, + "e:1230_d:2024-11-08": 1, + "e:1230_d:2024-11-09": 0, + "e:1230_d:2024-11-10": 1, + "e:1230_d:2024-11-11": 0, + "e:1230_d:2024-11-12": 1, + "e:1230_d:2024-11-13": 1, + "e:1230_d:2024-11-14": 0, + "e:1230_d:2024-11-15": 1, + "e:1230_d:2024-11-16": 0, + "e:1230_d:2024-11-17": 0, + "e:1230_d:2024-11-18": 1, + "e:1230_d:2024-11-19": 1, + "e:1230_d:2024-11-20": 1, + "e:1230_d:2024-11-21": 0, + "e:1230_d:2024-11-22": 1, + "e:1230_d:2024-11-23": 0, + "e:1230_d:2024-11-24": 0, + "e:1230_d:2024-11-25": 1, + "e:1230_d:2024-11-26": 1, + "e:1230_d:2024-11-27": 1, + "e:1230_d:2024-11-28": 1, + "e:1230_d:2024-11-29": 1, + "e:1230_d:2024-11-30": 0, + "e:1_d:2024-11-01": 1, + "e:1_d:2024-11-02": 1, + "e:1_d:2024-11-03": 1, + "e:1_d:2024-11-04": 0, + "e:1_d:2024-11-05": 0, + "e:1_d:2024-11-06": 0, + "e:1_d:2024-11-07": 1, + "e:1_d:2024-11-08": 1, + "e:1_d:2024-11-09": 1, + "e:1_d:2024-11-10": 1, + "e:1_d:2024-11-11": 0, + "e:1_d:2024-11-12": 0, + "e:1_d:2024-11-13": 0, + "e:1_d:2024-11-14": 1, + "e:1_d:2024-11-15": 0, + "e:1_d:2024-11-16": 1, + "e:1_d:2024-11-17": 1, + "e:1_d:2024-11-18": 1, + "e:1_d:2024-11-19": 1, + "e:1_d:2024-11-20": 1, + "e:1_d:2024-11-21": 1, + "e:1_d:2024-11-22": 0, + "e:1_d:2024-11-23": 1, + "e:1_d:2024-11-24": 1, + "e:1_d:2024-11-25": 0, + "e:1_d:2024-11-26": 1, + "e:1_d:2024-11-27": 0, + "e:1_d:2024-11-28": 0, + "e:1_d:2024-11-29": 0, + "e:1_d:2024-11-30": 1, + "e:2932_d:2024-11-01": 0, + "e:2932_d:2024-11-02": 1, + "e:2932_d:2024-11-03": 1, + "e:2932_d:2024-11-04": 1, + "e:2932_d:2024-11-05": 0, + "e:2932_d:2024-11-06": 0, + "e:2932_d:2024-11-07": 1, + "e:2932_d:2024-11-08": 1, + "e:2932_d:2024-11-09": 0, + "e:2932_d:2024-11-10": 1, + "e:2932_d:2024-11-11": 0, + "e:2932_d:2024-11-12": 0, + "e:2932_d:2024-11-13": 0, + "e:2932_d:2024-11-14": 1, + "e:2932_d:2024-11-15": 1, + "e:2932_d:2024-11-16": 0, + "e:2932_d:2024-11-17": 1, + "e:2932_d:2024-11-18": 1, + "e:2932_d:2024-11-19": 1, + "e:2932_d:2024-11-20": 0, + "e:2932_d:2024-11-21": 0, + "e:2932_d:2024-11-22": 1, + "e:2932_d:2024-11-23": 0, + "e:2932_d:2024-11-24": 0, + "e:2932_d:2024-11-25": 1, + "e:2932_d:2024-11-26": 1, + "e:2932_d:2024-11-27": 1, + "e:2932_d:2024-11-28": 1, + "e:2932_d:2024-11-29": 1, + "e:2932_d:2024-11-30": 1, + "e:2963_d:2024-11-01": 0, + "e:2963_d:2024-11-02": 1, + "e:2963_d:2024-11-03": 1, + "e:2963_d:2024-11-04": 0, + "e:2963_d:2024-11-05": 1, + "e:2963_d:2024-11-06": 1, + "e:2963_d:2024-11-07": 1, + "e:2963_d:2024-11-08": 0, + "e:2963_d:2024-11-09": 1, + "e:2963_d:2024-11-10": 1, + "e:2963_d:2024-11-11": 0, + "e:2963_d:2024-11-12": 0, + "e:2963_d:2024-11-13": 1, + "e:2963_d:2024-11-14": 1, + "e:2963_d:2024-11-15": 0, + "e:2963_d:2024-11-16": 1, + "e:2963_d:2024-11-17": 0, + "e:2963_d:2024-11-18": 1, + "e:2963_d:2024-11-19": 1, + "e:2963_d:2024-11-20": 1, + "e:2963_d:2024-11-21": 1, + "e:2963_d:2024-11-22": 1, + "e:2963_d:2024-11-23": 1, + "e:2963_d:2024-11-24": 0, + "e:2963_d:2024-11-25": 1, + "e:2963_d:2024-11-26": 1, + "e:2963_d:2024-11-27": 0, + "e:2963_d:2024-11-28": 0, + "e:2963_d:2024-11-29": 1, + "e:2963_d:2024-11-30": 1, + "e:3566_d:2024-11-01": 0, + "e:3566_d:2024-11-02": 0, + "e:3566_d:2024-11-03": 0, + "e:3566_d:2024-11-04": 0, + "e:3566_d:2024-11-05": 0, + "e:3566_d:2024-11-06": 0, + "e:3566_d:2024-11-07": 0, + "e:3566_d:2024-11-08": 0, + "e:3566_d:2024-11-09": 0, + "e:3566_d:2024-11-10": 0, + "e:3566_d:2024-11-11": 0, + "e:3566_d:2024-11-12": 0, + "e:3566_d:2024-11-13": 0, + "e:3566_d:2024-11-14": 0, + "e:3566_d:2024-11-15": 0, + "e:3566_d:2024-11-16": 0, + "e:3566_d:2024-11-17": 0, + "e:3566_d:2024-11-18": 0, + "e:3566_d:2024-11-19": 0, + "e:3566_d:2024-11-20": 0, + "e:3566_d:2024-11-21": 0, + "e:3566_d:2024-11-22": 0, + "e:3566_d:2024-11-23": 0, + "e:3566_d:2024-11-24": 0, + "e:3566_d:2024-11-25": 0, + "e:3566_d:2024-11-26": 0, + "e:3566_d:2024-11-27": 0, + "e:3566_d:2024-11-28": 0, + "e:3566_d:2024-11-29": 0, + "e:3566_d:2024-11-30": 0, + "e:3868_d:2024-11-01": 1, + "e:3868_d:2024-11-02": 1, + "e:3868_d:2024-11-03": 1, + "e:3868_d:2024-11-04": 1, + "e:3868_d:2024-11-05": 0, + "e:3868_d:2024-11-06": 1, + "e:3868_d:2024-11-07": 0, + "e:3868_d:2024-11-08": 1, + "e:3868_d:2024-11-09": 1, + "e:3868_d:2024-11-10": 0, + "e:3868_d:2024-11-11": 1, + "e:3868_d:2024-11-12": 0, + "e:3868_d:2024-11-13": 0, + "e:3868_d:2024-11-14": 0, + "e:3868_d:2024-11-15": 0, + "e:3868_d:2024-11-16": 1, + "e:3868_d:2024-11-17": 1, + "e:3868_d:2024-11-18": 0, + "e:3868_d:2024-11-19": 0, + "e:3868_d:2024-11-20": 0, + "e:3868_d:2024-11-21": 1, + "e:3868_d:2024-11-22": 1, + "e:3868_d:2024-11-23": 1, + "e:3868_d:2024-11-24": 1, + "e:3868_d:2024-11-25": 1, + "e:3868_d:2024-11-26": 0, + "e:3868_d:2024-11-27": 1, + "e:3868_d:2024-11-28": 1, + "e:3868_d:2024-11-29": 1, + "e:3868_d:2024-11-30": 1, + "e:4566_d:2024-11-01": 1, + "e:4566_d:2024-11-02": 1, + "e:4566_d:2024-11-03": 1, + "e:4566_d:2024-11-04": 1, + "e:4566_d:2024-11-05": 1, + "e:4566_d:2024-11-06": 1, + "e:4566_d:2024-11-07": 1, + "e:4566_d:2024-11-08": 1, + "e:4566_d:2024-11-09": 0, + "e:4566_d:2024-11-10": 0, + "e:4566_d:2024-11-11": 0, + "e:4566_d:2024-11-12": 0, + "e:4566_d:2024-11-13": 0, + "e:4566_d:2024-11-14": 0, + "e:4566_d:2024-11-15": 1, + "e:4566_d:2024-11-16": 0, + "e:4566_d:2024-11-17": 1, + "e:4566_d:2024-11-18": 0, + "e:4566_d:2024-11-19": 1, + "e:4566_d:2024-11-20": 1, + "e:4566_d:2024-11-21": 1, + "e:4566_d:2024-11-22": 1, + "e:4566_d:2024-11-23": 1, + "e:4566_d:2024-11-24": 0, + "e:4566_d:2024-11-25": 1, + "e:4566_d:2024-11-26": 1, + "e:4566_d:2024-11-27": 1, + "e:4566_d:2024-11-28": 0, + "e:4566_d:2024-11-29": 1, + "e:4566_d:2024-11-30": 0, + "e:459_d:2024-11-01": 0, + "e:459_d:2024-11-02": 0, + "e:459_d:2024-11-03": 0, + "e:459_d:2024-11-04": 0, + "e:459_d:2024-11-05": 0, + "e:459_d:2024-11-06": 0, + "e:459_d:2024-11-07": 0, + "e:459_d:2024-11-08": 0, + "e:459_d:2024-11-09": 0, + "e:459_d:2024-11-10": 0, + "e:459_d:2024-11-11": 0, + "e:459_d:2024-11-12": 0, + "e:459_d:2024-11-13": 1, + "e:459_d:2024-11-14": 0, + "e:459_d:2024-11-15": 1, + "e:459_d:2024-11-16": 0, + "e:459_d:2024-11-17": 0, + "e:459_d:2024-11-18": 0, + "e:459_d:2024-11-19": 1, + "e:459_d:2024-11-20": 1, + "e:459_d:2024-11-21": 0, + "e:459_d:2024-11-22": 0, + "e:459_d:2024-11-23": 0, + "e:459_d:2024-11-24": 0, + "e:459_d:2024-11-25": 0, + "e:459_d:2024-11-26": 0, + "e:459_d:2024-11-27": 1, + "e:459_d:2024-11-28": 0, + "e:459_d:2024-11-29": 0, + "e:459_d:2024-11-30": 0, + "e:5367_d:2024-11-01": 1, + "e:5367_d:2024-11-02": 0, + "e:5367_d:2024-11-03": 1, + "e:5367_d:2024-11-04": 0, + "e:5367_d:2024-11-05": 1, + "e:5367_d:2024-11-06": 1, + "e:5367_d:2024-11-07": 0, + "e:5367_d:2024-11-08": 0, + "e:5367_d:2024-11-09": 0, + "e:5367_d:2024-11-10": 1, + "e:5367_d:2024-11-11": 1, + "e:5367_d:2024-11-12": 1, + "e:5367_d:2024-11-13": 0, + "e:5367_d:2024-11-14": 0, + "e:5367_d:2024-11-15": 1, + "e:5367_d:2024-11-16": 0, + "e:5367_d:2024-11-17": 1, + "e:5367_d:2024-11-18": 1, + "e:5367_d:2024-11-19": 1, + "e:5367_d:2024-11-20": 0, + "e:5367_d:2024-11-21": 0, + "e:5367_d:2024-11-22": 1, + "e:5367_d:2024-11-23": 1, + "e:5367_d:2024-11-24": 1, + "e:5367_d:2024-11-25": 1, + "e:5367_d:2024-11-26": 1, + "e:5367_d:2024-11-27": 1, + "e:5367_d:2024-11-28": 1, + "e:5367_d:2024-11-29": 0, + "e:5367_d:2024-11-30": 1, + "e:5920_d:2024-11-01": 1, + "e:5920_d:2024-11-02": 0, + "e:5920_d:2024-11-03": 1, + "e:5920_d:2024-11-04": 1, + "e:5920_d:2024-11-05": 1, + "e:5920_d:2024-11-06": 0, + "e:5920_d:2024-11-07": 0, + "e:5920_d:2024-11-08": 1, + "e:5920_d:2024-11-09": 0, + "e:5920_d:2024-11-10": 0, + "e:5920_d:2024-11-11": 1, + "e:5920_d:2024-11-12": 1, + "e:5920_d:2024-11-13": 0, + "e:5920_d:2024-11-14": 0, + "e:5920_d:2024-11-15": 1, + "e:5920_d:2024-11-16": 1, + "e:5920_d:2024-11-17": 1, + "e:5920_d:2024-11-18": 1, + "e:5920_d:2024-11-19": 0, + "e:5920_d:2024-11-20": 0, + "e:5920_d:2024-11-21": 1, + "e:5920_d:2024-11-22": 1, + "e:5920_d:2024-11-23": 1, + "e:5920_d:2024-11-24": 0, + "e:5920_d:2024-11-25": 1, + "e:5920_d:2024-11-26": 1, + "e:5920_d:2024-11-27": 0, + "e:5920_d:2024-11-28": 1, + "e:5920_d:2024-11-29": 1, + "e:5920_d:2024-11-30": 1, + "e:6475_d:2024-11-01": 1, + "e:6475_d:2024-11-02": 0, + "e:6475_d:2024-11-03": 0, + "e:6475_d:2024-11-04": 0, + "e:6475_d:2024-11-05": 0, + "e:6475_d:2024-11-06": 0, + "e:6475_d:2024-11-07": 0, + "e:6475_d:2024-11-08": 0, + "e:6475_d:2024-11-09": 0, + "e:6475_d:2024-11-10": 0, + "e:6475_d:2024-11-11": 0, + "e:6475_d:2024-11-12": 0, + "e:6475_d:2024-11-13": 0, + "e:6475_d:2024-11-14": 0, + "e:6475_d:2024-11-15": 0, + "e:6475_d:2024-11-16": 0, + "e:6475_d:2024-11-17": 0, + "e:6475_d:2024-11-18": 0, + "e:6475_d:2024-11-19": 0, + "e:6475_d:2024-11-20": 0, + "e:6475_d:2024-11-21": 0, + "e:6475_d:2024-11-22": 0, + "e:6475_d:2024-11-23": 0, + "e:6475_d:2024-11-24": 0, + "e:6475_d:2024-11-25": 0, + "e:6475_d:2024-11-26": 0, + "e:6475_d:2024-11-27": 0, + "e:6475_d:2024-11-28": 0, + "e:6475_d:2024-11-29": 0, + "e:6475_d:2024-11-30": 0, + "e:6507_d:2024-11-01": 1, + "e:6507_d:2024-11-02": 1, + "e:6507_d:2024-11-03": 0, + "e:6507_d:2024-11-04": 0, + "e:6507_d:2024-11-05": 1, + "e:6507_d:2024-11-06": 1, + "e:6507_d:2024-11-07": 1, + "e:6507_d:2024-11-08": 0, + "e:6507_d:2024-11-09": 1, + "e:6507_d:2024-11-10": 0, + "e:6507_d:2024-11-11": 1, + "e:6507_d:2024-11-12": 1, + "e:6507_d:2024-11-13": 1, + "e:6507_d:2024-11-14": 1, + "e:6507_d:2024-11-15": 1, + "e:6507_d:2024-11-16": 0, + "e:6507_d:2024-11-17": 0, + "e:6507_d:2024-11-18": 1, + "e:6507_d:2024-11-19": 1, + "e:6507_d:2024-11-20": 1, + "e:6507_d:2024-11-21": 1, + "e:6507_d:2024-11-22": 0, + "e:6507_d:2024-11-23": 0, + "e:6507_d:2024-11-24": 0, + "e:6507_d:2024-11-25": 0, + "e:6507_d:2024-11-26": 1, + "e:6507_d:2024-11-27": 1, + "e:6507_d:2024-11-28": 1, + "e:6507_d:2024-11-29": 1, + "e:6507_d:2024-11-30": 1, + "e:6677_d:2024-11-01": 0, + "e:6677_d:2024-11-02": 1, + "e:6677_d:2024-11-03": 1, + "e:6677_d:2024-11-04": 0, + "e:6677_d:2024-11-05": 1, + "e:6677_d:2024-11-06": 1, + "e:6677_d:2024-11-07": 1, + "e:6677_d:2024-11-08": 0, + "e:6677_d:2024-11-09": 0, + "e:6677_d:2024-11-10": 1, + "e:6677_d:2024-11-11": 1, + "e:6677_d:2024-11-12": 0, + "e:6677_d:2024-11-13": 0, + "e:6677_d:2024-11-14": 1, + "e:6677_d:2024-11-15": 1, + "e:6677_d:2024-11-16": 1, + "e:6677_d:2024-11-17": 1, + "e:6677_d:2024-11-18": 0, + "e:6677_d:2024-11-19": 1, + "e:6677_d:2024-11-20": 1, + "e:6677_d:2024-11-21": 1, + "e:6677_d:2024-11-22": 1, + "e:6677_d:2024-11-23": 1, + "e:6677_d:2024-11-24": 0, + "e:6677_d:2024-11-25": 1, + "e:6677_d:2024-11-26": 0, + "e:6677_d:2024-11-27": 1, + "e:6677_d:2024-11-28": 1, + "e:6677_d:2024-11-29": 1, + "e:6677_d:2024-11-30": 0, + "e:6681_d:2024-11-01": 1, + "e:6681_d:2024-11-02": 0, + "e:6681_d:2024-11-03": 0, + "e:6681_d:2024-11-04": 0, + "e:6681_d:2024-11-05": 1, + "e:6681_d:2024-11-06": 1, + "e:6681_d:2024-11-07": 0, + "e:6681_d:2024-11-08": 1, + "e:6681_d:2024-11-09": 0, + "e:6681_d:2024-11-10": 0, + "e:6681_d:2024-11-11": 0, + "e:6681_d:2024-11-12": 1, + "e:6681_d:2024-11-13": 0, + "e:6681_d:2024-11-14": 1, + "e:6681_d:2024-11-15": 0, + "e:6681_d:2024-11-16": 0, + "e:6681_d:2024-11-17": 0, + "e:6681_d:2024-11-18": 0, + "e:6681_d:2024-11-19": 0, + "e:6681_d:2024-11-20": 0, + "e:6681_d:2024-11-21": 0, + "e:6681_d:2024-11-22": 0, + "e:6681_d:2024-11-23": 0, + "e:6681_d:2024-11-24": 0, + "e:6681_d:2024-11-25": 0, + "e:6681_d:2024-11-26": 0, + "e:6681_d:2024-11-27": 0, + "e:6681_d:2024-11-28": 0, + "e:6681_d:2024-11-29": 0, + "e:6681_d:2024-11-30": 0, + "e:6715_d:2024-11-01": 1, + "e:6715_d:2024-11-02": 0, + "e:6715_d:2024-11-03": 0, + "e:6715_d:2024-11-04": 0, + "e:6715_d:2024-11-05": 0, + "e:6715_d:2024-11-06": 0, + "e:6715_d:2024-11-07": 0, + "e:6715_d:2024-11-08": 0, + "e:6715_d:2024-11-09": 0, + "e:6715_d:2024-11-10": 0, + "e:6715_d:2024-11-11": 0, + "e:6715_d:2024-11-12": 0, + "e:6715_d:2024-11-13": 0, + "e:6715_d:2024-11-14": 0, + "e:6715_d:2024-11-15": 0, + "e:6715_d:2024-11-16": 0, + "e:6715_d:2024-11-17": 0, + "e:6715_d:2024-11-18": 0, + "e:6715_d:2024-11-19": 0, + "e:6715_d:2024-11-20": 0, + "e:6715_d:2024-11-21": 0, + "e:6715_d:2024-11-22": 0, + "e:6715_d:2024-11-23": 0, + "e:6715_d:2024-11-24": 0, + "e:6715_d:2024-11-25": 0, + "e:6715_d:2024-11-26": 0, + "e:6715_d:2024-11-27": 0, + "e:6715_d:2024-11-28": 0, + "e:6715_d:2024-11-29": 0, + "e:6715_d:2024-11-30": 0, + "e:6836_d:2024-11-01": 1, + "e:6836_d:2024-11-02": 1, + "e:6836_d:2024-11-03": 1, + "e:6836_d:2024-11-04": 0, + "e:6836_d:2024-11-05": 1, + "e:6836_d:2024-11-06": 0, + "e:6836_d:2024-11-07": 1, + "e:6836_d:2024-11-08": 1, + "e:6836_d:2024-11-09": 0, + "e:6836_d:2024-11-10": 0, + "e:6836_d:2024-11-11": 1, + "e:6836_d:2024-11-12": 0, + "e:6836_d:2024-11-13": 0, + "e:6836_d:2024-11-14": 0, + "e:6836_d:2024-11-15": 1, + "e:6836_d:2024-11-16": 1, + "e:6836_d:2024-11-17": 1, + "e:6836_d:2024-11-18": 1, + "e:6836_d:2024-11-19": 0, + "e:6836_d:2024-11-20": 1, + "e:6836_d:2024-11-21": 1, + "e:6836_d:2024-11-22": 0, + "e:6836_d:2024-11-23": 1, + "e:6836_d:2024-11-24": 1, + "e:6836_d:2024-11-25": 1, + "e:6836_d:2024-11-26": 1, + "e:6836_d:2024-11-27": 1, + "e:6836_d:2024-11-28": 0, + "e:6836_d:2024-11-29": 1, + "e:6836_d:2024-11-30": 0, + "e:6928_d:2024-11-01": 1, + "e:6928_d:2024-11-02": 0, + "e:6928_d:2024-11-03": 1, + "e:6928_d:2024-11-04": 1, + "e:6928_d:2024-11-05": 0, + "e:6928_d:2024-11-06": 1, + "e:6928_d:2024-11-07": 1, + "e:6928_d:2024-11-08": 1, + "e:6928_d:2024-11-09": 1, + "e:6928_d:2024-11-10": 0, + "e:6928_d:2024-11-11": 1, + "e:6928_d:2024-11-12": 1, + "e:6928_d:2024-11-13": 0, + "e:6928_d:2024-11-14": 1, + "e:6928_d:2024-11-15": 0, + "e:6928_d:2024-11-16": 1, + "e:6928_d:2024-11-17": 0, + "e:6928_d:2024-11-18": 1, + "e:6928_d:2024-11-19": 1, + "e:6928_d:2024-11-20": 0, + "e:6928_d:2024-11-21": 1, + "e:6928_d:2024-11-22": 0, + "e:6928_d:2024-11-23": 0, + "e:6928_d:2024-11-24": 0, + "e:6928_d:2024-11-25": 1, + "e:6928_d:2024-11-26": 1, + "e:6928_d:2024-11-27": 1, + "e:6928_d:2024-11-28": 1, + "e:6928_d:2024-11-29": 1, + "e:6928_d:2024-11-30": 1, + "e:7496_d:2024-11-01": 0, + "e:7496_d:2024-11-02": 0, + "e:7496_d:2024-11-03": 0, + "e:7496_d:2024-11-04": 0, + "e:7496_d:2024-11-05": 0, + "e:7496_d:2024-11-06": 0, + "e:7496_d:2024-11-07": 0, + "e:7496_d:2024-11-08": 0, + "e:7496_d:2024-11-09": 0, + "e:7496_d:2024-11-10": 0, + "e:7496_d:2024-11-11": 0, + "e:7496_d:2024-11-12": 0, + "e:7496_d:2024-11-13": 0, + "e:7496_d:2024-11-14": 0, + "e:7496_d:2024-11-15": 1, + "e:7496_d:2024-11-16": 0, + "e:7496_d:2024-11-17": 1, + "e:7496_d:2024-11-18": 1, + "e:7496_d:2024-11-19": 1, + "e:7496_d:2024-11-20": 1, + "e:7496_d:2024-11-21": 0, + "e:7496_d:2024-11-22": 0, + "e:7496_d:2024-11-23": 0, + "e:7496_d:2024-11-24": 0, + "e:7496_d:2024-11-25": 1, + "e:7496_d:2024-11-26": 1, + "e:7496_d:2024-11-27": 1, + "e:7496_d:2024-11-28": 1, + "e:7496_d:2024-11-29": 1, + "e:7496_d:2024-11-30": 0, + "e:7603_d:2024-11-01": 1, + "e:7603_d:2024-11-02": 1, + "e:7603_d:2024-11-03": 1, + "e:7603_d:2024-11-04": 1, + "e:7603_d:2024-11-05": 1, + "e:7603_d:2024-11-06": 1, + "e:7603_d:2024-11-07": 1, + "e:7603_d:2024-11-08": 0, + "e:7603_d:2024-11-09": 1, + "e:7603_d:2024-11-10": 0, + "e:7603_d:2024-11-11": 0, + "e:7603_d:2024-11-12": 1, + "e:7603_d:2024-11-13": 0, + "e:7603_d:2024-11-14": 1, + "e:7603_d:2024-11-15": 0, + "e:7603_d:2024-11-16": 1, + "e:7603_d:2024-11-17": 1, + "e:7603_d:2024-11-18": 1, + "e:7603_d:2024-11-19": 0, + "e:7603_d:2024-11-20": 1, + "e:7603_d:2024-11-21": 0, + "e:7603_d:2024-11-22": 0, + "e:7603_d:2024-11-23": 1, + "e:7603_d:2024-11-24": 1, + "e:7603_d:2024-11-25": 1, + "e:7603_d:2024-11-26": 1, + "e:7603_d:2024-11-27": 1, + "e:7603_d:2024-11-28": 1, + "e:7603_d:2024-11-29": 0, + "e:7603_d:2024-11-30": 1, + "e:7741_d:2024-11-01": 0, + "e:7741_d:2024-11-02": 0, + "e:7741_d:2024-11-03": 0, + "e:7741_d:2024-11-04": 0, + "e:7741_d:2024-11-05": 0, + "e:7741_d:2024-11-06": 0, + "e:7741_d:2024-11-07": 0, + "e:7741_d:2024-11-08": 0, + "e:7741_d:2024-11-09": 0, + "e:7741_d:2024-11-10": 0, + "e:7741_d:2024-11-11": 0, + "e:7741_d:2024-11-12": 0, + "e:7741_d:2024-11-13": 0, + "e:7741_d:2024-11-14": 0, + "e:7741_d:2024-11-15": 0, + "e:7741_d:2024-11-16": 0, + "e:7741_d:2024-11-17": 0, + "e:7741_d:2024-11-18": 0, + "e:7741_d:2024-11-19": 0, + "e:7741_d:2024-11-20": 0, + "e:7741_d:2024-11-21": 0, + "e:7741_d:2024-11-22": 0, + "e:7741_d:2024-11-23": 0, + "e:7741_d:2024-11-24": 0, + "e:7741_d:2024-11-25": 0, + "e:7741_d:2024-11-26": 0, + "e:7741_d:2024-11-27": 1, + "e:7741_d:2024-11-28": 1, + "e:7741_d:2024-11-29": 0, + "e:7741_d:2024-11-30": 0, + "e:7752_d:2024-11-01": 0, + "e:7752_d:2024-11-02": 1, + "e:7752_d:2024-11-03": 0, + "e:7752_d:2024-11-04": 0, + "e:7752_d:2024-11-05": 1, + "e:7752_d:2024-11-06": 1, + "e:7752_d:2024-11-07": 1, + "e:7752_d:2024-11-08": 0, + "e:7752_d:2024-11-09": 0, + "e:7752_d:2024-11-10": 0, + "e:7752_d:2024-11-11": 0, + "e:7752_d:2024-11-12": 1, + "e:7752_d:2024-11-13": 1, + "e:7752_d:2024-11-14": 0, + "e:7752_d:2024-11-15": 0, + "e:7752_d:2024-11-16": 1, + "e:7752_d:2024-11-17": 0, + "e:7752_d:2024-11-18": 1, + "e:7752_d:2024-11-19": 0, + "e:7752_d:2024-11-20": 1, + "e:7752_d:2024-11-21": 0, + "e:7752_d:2024-11-22": 1, + "e:7752_d:2024-11-23": 1, + "e:7752_d:2024-11-24": 1, + "e:7752_d:2024-11-25": 1, + "e:7752_d:2024-11-26": 1, + "e:7752_d:2024-11-27": 0, + "e:7752_d:2024-11-28": 1, + "e:7752_d:2024-11-29": 1, + "e:7752_d:2024-11-30": 0, + "e:7770_d:2024-11-01": 0, + "e:7770_d:2024-11-02": 0, + "e:7770_d:2024-11-03": 0, + "e:7770_d:2024-11-04": 0, + "e:7770_d:2024-11-05": 0, + "e:7770_d:2024-11-06": 0, + "e:7770_d:2024-11-07": 1, + "e:7770_d:2024-11-08": 0, + "e:7770_d:2024-11-09": 0, + "e:7770_d:2024-11-10": 0, + "e:7770_d:2024-11-11": 0, + "e:7770_d:2024-11-12": 1, + "e:7770_d:2024-11-13": 1, + "e:7770_d:2024-11-14": 1, + "e:7770_d:2024-11-15": 0, + "e:7770_d:2024-11-16": 0, + "e:7770_d:2024-11-17": 0, + "e:7770_d:2024-11-18": 0, + "e:7770_d:2024-11-19": 1, + "e:7770_d:2024-11-20": 0, + "e:7770_d:2024-11-21": 0, + "e:7770_d:2024-11-22": 0, + "e:7770_d:2024-11-23": 1, + "e:7770_d:2024-11-24": 0, + "e:7770_d:2024-11-25": 0, + "e:7770_d:2024-11-26": 1, + "e:7770_d:2024-11-27": 1, + "e:7770_d:2024-11-28": 0, + "e:7770_d:2024-11-29": 1, + "e:7770_d:2024-11-30": 1, + "e:7796_d:2024-11-01": 0, + "e:7796_d:2024-11-02": 0, + "e:7796_d:2024-11-03": 0, + "e:7796_d:2024-11-04": 0, + "e:7796_d:2024-11-05": 0, + "e:7796_d:2024-11-06": 0, + "e:7796_d:2024-11-07": 1, + "e:7796_d:2024-11-08": 0, + "e:7796_d:2024-11-09": 0, + "e:7796_d:2024-11-10": 0, + "e:7796_d:2024-11-11": 0, + "e:7796_d:2024-11-12": 1, + "e:7796_d:2024-11-13": 1, + "e:7796_d:2024-11-14": 0, + "e:7796_d:2024-11-15": 1, + "e:7796_d:2024-11-16": 0, + "e:7796_d:2024-11-17": 1, + "e:7796_d:2024-11-18": 0, + "e:7796_d:2024-11-19": 1, + "e:7796_d:2024-11-20": 1, + "e:7796_d:2024-11-21": 1, + "e:7796_d:2024-11-22": 1, + "e:7796_d:2024-11-23": 0, + "e:7796_d:2024-11-24": 0, + "e:7796_d:2024-11-25": 0, + "e:7796_d:2024-11-26": 0, + "e:7796_d:2024-11-27": 1, + "e:7796_d:2024-11-28": 0, + "e:7796_d:2024-11-29": 0, + "e:7796_d:2024-11-30": 0, + "e:7835_d:2024-11-01": 0, + "e:7835_d:2024-11-02": 0, + "e:7835_d:2024-11-03": 0, + "e:7835_d:2024-11-04": 0, + "e:7835_d:2024-11-05": 0, + "e:7835_d:2024-11-06": 0, + "e:7835_d:2024-11-07": 0, + "e:7835_d:2024-11-08": 0, + "e:7835_d:2024-11-09": 1, + "e:7835_d:2024-11-10": 1, + "e:7835_d:2024-11-11": 0, + "e:7835_d:2024-11-12": 1, + "e:7835_d:2024-11-13": 1, + "e:7835_d:2024-11-14": 1, + "e:7835_d:2024-11-15": 0, + "e:7835_d:2024-11-16": 0, + "e:7835_d:2024-11-17": 0, + "e:7835_d:2024-11-18": 1, + "e:7835_d:2024-11-19": 0, + "e:7835_d:2024-11-20": 1, + "e:7835_d:2024-11-21": 1, + "e:7835_d:2024-11-22": 0, + "e:7835_d:2024-11-23": 0, + "e:7835_d:2024-11-24": 1, + "e:7835_d:2024-11-25": 0, + "e:7835_d:2024-11-26": 0, + "e:7835_d:2024-11-27": 0, + "e:7835_d:2024-11-28": 0, + "e:7835_d:2024-11-29": 1, + "e:7835_d:2024-11-30": 0, + "e:7848_d:2024-11-01": 1, + "e:7848_d:2024-11-02": 1, + "e:7848_d:2024-11-03": 1, + "e:7848_d:2024-11-04": 1, + "e:7848_d:2024-11-05": 1, + "e:7848_d:2024-11-06": 1, + "e:7848_d:2024-11-07": 0, + "e:7848_d:2024-11-08": 1, + "e:7848_d:2024-11-09": 1, + "e:7848_d:2024-11-10": 0, + "e:7848_d:2024-11-11": 1, + "e:7848_d:2024-11-12": 0, + "e:7848_d:2024-11-13": 0, + "e:7848_d:2024-11-14": 1, + "e:7848_d:2024-11-15": 0, + "e:7848_d:2024-11-16": 1, + "e:7848_d:2024-11-17": 1, + "e:7848_d:2024-11-18": 1, + "e:7848_d:2024-11-19": 0, + "e:7848_d:2024-11-20": 1, + "e:7848_d:2024-11-21": 1, + "e:7848_d:2024-11-22": 0, + "e:7848_d:2024-11-23": 0, + "e:7848_d:2024-11-24": 1, + "e:7848_d:2024-11-25": 1, + "e:7848_d:2024-11-26": 0, + "e:7848_d:2024-11-27": 0, + "e:7848_d:2024-11-28": 1, + "e:7848_d:2024-11-29": 0, + "e:7848_d:2024-11-30": 1, + "e:7877_d:2024-11-01": 0, + "e:7877_d:2024-11-02": 0, + "e:7877_d:2024-11-03": 0, + "e:7877_d:2024-11-04": 0, + "e:7877_d:2024-11-05": 0, + "e:7877_d:2024-11-06": 0, + "e:7877_d:2024-11-07": 0, + "e:7877_d:2024-11-08": 0, + "e:7877_d:2024-11-09": 1, + "e:7877_d:2024-11-10": 1, + "e:7877_d:2024-11-11": 1, + "e:7877_d:2024-11-12": 0, + "e:7877_d:2024-11-13": 1, + "e:7877_d:2024-11-14": 1, + "e:7877_d:2024-11-15": 1, + "e:7877_d:2024-11-16": 1, + "e:7877_d:2024-11-17": 1, + "e:7877_d:2024-11-18": 1, + "e:7877_d:2024-11-19": 1, + "e:7877_d:2024-11-20": 0, + "e:7877_d:2024-11-21": 0, + "e:7877_d:2024-11-22": 0, + "e:7877_d:2024-11-23": 1, + "e:7877_d:2024-11-24": 1, + "e:7877_d:2024-11-25": 0, + "e:7877_d:2024-11-26": 1, + "e:7877_d:2024-11-27": 0, + "e:7877_d:2024-11-28": 1, + "e:7877_d:2024-11-29": 0, + "e:7877_d:2024-11-30": 0, + "e:790_d:2024-11-01": 0, + "e:790_d:2024-11-02": 0, + "e:790_d:2024-11-03": 0, + "e:790_d:2024-11-04": 0, + "e:790_d:2024-11-05": 0, + "e:790_d:2024-11-06": 0, + "e:790_d:2024-11-07": 0, + "e:790_d:2024-11-08": 0, + "e:790_d:2024-11-09": 0, + "e:790_d:2024-11-10": 0, + "e:790_d:2024-11-11": 0, + "e:790_d:2024-11-12": 0, + "e:790_d:2024-11-13": 0, + "e:790_d:2024-11-14": 0, + "e:790_d:2024-11-15": 0, + "e:790_d:2024-11-16": 0, + "e:790_d:2024-11-17": 0, + "e:790_d:2024-11-18": 0, + "e:790_d:2024-11-19": 0, + "e:790_d:2024-11-20": 0, + "e:790_d:2024-11-21": 0, + "e:790_d:2024-11-22": 0, + "e:790_d:2024-11-23": 0, + "e:790_d:2024-11-24": 0, + "e:790_d:2024-11-25": 0, + "e:790_d:2024-11-26": 0, + "e:790_d:2024-11-27": 0, + "e:790_d:2024-11-28": 0, + "e:790_d:2024-11-29": 0, + "e:790_d:2024-11-30": 0, + "e:7919_d:2024-11-01": 0, + "e:7919_d:2024-11-02": 0, + "e:7919_d:2024-11-03": 0, + "e:7919_d:2024-11-04": 1, + "e:7919_d:2024-11-05": 1, + "e:7919_d:2024-11-06": 1, + "e:7919_d:2024-11-07": 0, + "e:7919_d:2024-11-08": 1, + "e:7919_d:2024-11-09": 0, + "e:7919_d:2024-11-10": 1, + "e:7919_d:2024-11-11": 1, + "e:7919_d:2024-11-12": 1, + "e:7919_d:2024-11-13": 1, + "e:7919_d:2024-11-14": 1, + "e:7919_d:2024-11-15": 1, + "e:7919_d:2024-11-16": 0, + "e:7919_d:2024-11-17": 1, + "e:7919_d:2024-11-18": 0, + "e:7919_d:2024-11-19": 1, + "e:7919_d:2024-11-20": 0, + "e:7919_d:2024-11-21": 1, + "e:7919_d:2024-11-22": 1, + "e:7919_d:2024-11-23": 0, + "e:7919_d:2024-11-24": 1, + "e:7919_d:2024-11-25": 1, + "e:7919_d:2024-11-26": 0, + "e:7919_d:2024-11-27": 1, + "e:7919_d:2024-11-28": 1, + "e:7919_d:2024-11-29": 0, + "e:7919_d:2024-11-30": 1, + "e:791_d:2024-11-01": 1, + "e:791_d:2024-11-02": 0, + "e:791_d:2024-11-03": 0, + "e:791_d:2024-11-04": 1, + "e:791_d:2024-11-05": 0, + "e:791_d:2024-11-06": 1, + "e:791_d:2024-11-07": 0, + "e:791_d:2024-11-08": 1, + "e:791_d:2024-11-09": 0, + "e:791_d:2024-11-10": 0, + "e:791_d:2024-11-11": 1, + "e:791_d:2024-11-12": 1, + "e:791_d:2024-11-13": 1, + "e:791_d:2024-11-14": 0, + "e:791_d:2024-11-15": 1, + "e:791_d:2024-11-16": 0, + "e:791_d:2024-11-17": 0, + "e:791_d:2024-11-18": 0, + "e:791_d:2024-11-19": 1, + "e:791_d:2024-11-20": 1, + "e:791_d:2024-11-21": 0, + "e:791_d:2024-11-22": 1, + "e:791_d:2024-11-23": 0, + "e:791_d:2024-11-24": 0, + "e:791_d:2024-11-25": 1, + "e:791_d:2024-11-26": 1, + "e:791_d:2024-11-27": 0, + "e:791_d:2024-11-28": 1, + "e:791_d:2024-11-29": 1, + "e:791_d:2024-11-30": 0, + "e:7990_d:2024-11-01": 0, + "e:7990_d:2024-11-02": 0, + "e:7990_d:2024-11-03": 0, + "e:7990_d:2024-11-04": 0, + "e:7990_d:2024-11-05": 0, + "e:7990_d:2024-11-06": 0, + "e:7990_d:2024-11-07": 0, + "e:7990_d:2024-11-08": 0, + "e:7990_d:2024-11-09": 0, + "e:7990_d:2024-11-10": 0, + "e:7990_d:2024-11-11": 0, + "e:7990_d:2024-11-12": 0, + "e:7990_d:2024-11-13": 0, + "e:7990_d:2024-11-14": 0, + "e:7990_d:2024-11-15": 0, + "e:7990_d:2024-11-16": 0, + "e:7990_d:2024-11-17": 0, + "e:7990_d:2024-11-18": 0, + "e:7990_d:2024-11-19": 0, + "e:7990_d:2024-11-20": 0, + "e:7990_d:2024-11-21": 0, + "e:7990_d:2024-11-22": 0, + "e:7990_d:2024-11-23": 0, + "e:7990_d:2024-11-24": 0, + "e:7990_d:2024-11-25": 0, + "e:7990_d:2024-11-26": 0, + "e:7990_d:2024-11-27": 0, + "e:7990_d:2024-11-28": 1, + "e:7990_d:2024-11-29": 0, + "e:7990_d:2024-11-30": 0, + "e:822_d:2024-11-01": 1, + "e:822_d:2024-11-02": 1, + "e:822_d:2024-11-03": 1, + "e:822_d:2024-11-04": 0, + "e:822_d:2024-11-05": 0, + "e:822_d:2024-11-06": 1, + "e:822_d:2024-11-07": 1, + "e:822_d:2024-11-08": 1, + "e:822_d:2024-11-09": 0, + "e:822_d:2024-11-10": 0, + "e:822_d:2024-11-11": 1, + "e:822_d:2024-11-12": 1, + "e:822_d:2024-11-13": 1, + "e:822_d:2024-11-14": 1, + "e:822_d:2024-11-15": 0, + "e:822_d:2024-11-16": 0, + "e:822_d:2024-11-17": 0, + "e:822_d:2024-11-18": 0, + "e:822_d:2024-11-19": 0, + "e:822_d:2024-11-20": 1, + "e:822_d:2024-11-21": 1, + "e:822_d:2024-11-22": 1, + "e:822_d:2024-11-23": 1, + "e:822_d:2024-11-24": 1, + "e:822_d:2024-11-25": 0, + "e:822_d:2024-11-26": 0, + "e:822_d:2024-11-27": 0, + "e:822_d:2024-11-28": 1, + "e:822_d:2024-11-29": 1, + "e:822_d:2024-11-30": 1, + "e:839_d:2024-11-01": 0, + "e:839_d:2024-11-02": 0, + "e:839_d:2024-11-03": 0, + "e:839_d:2024-11-04": 1, + "e:839_d:2024-11-05": 0, + "e:839_d:2024-11-06": 0, + "e:839_d:2024-11-07": 0, + "e:839_d:2024-11-08": 0, + "e:839_d:2024-11-09": 0, + "e:839_d:2024-11-10": 1, + "e:839_d:2024-11-11": 1, + "e:839_d:2024-11-12": 0, + "e:839_d:2024-11-13": 0, + "e:839_d:2024-11-14": 0, + "e:839_d:2024-11-15": 1, + "e:839_d:2024-11-16": 0, + "e:839_d:2024-11-17": 0, + "e:839_d:2024-11-18": 0, + "e:839_d:2024-11-19": 1, + "e:839_d:2024-11-20": 1, + "e:839_d:2024-11-21": 0, + "e:839_d:2024-11-22": 0, + "e:839_d:2024-11-23": 0, + "e:839_d:2024-11-24": 0, + "e:839_d:2024-11-25": 0, + "e:839_d:2024-11-26": 1, + "e:839_d:2024-11-27": 1, + "e:839_d:2024-11-28": 0, + "e:839_d:2024-11-29": 0, + "e:839_d:2024-11-30": 0, + "e:914_d:2024-11-01": 1, + "e:914_d:2024-11-02": 0, + "e:914_d:2024-11-03": 0, + "e:914_d:2024-11-04": 1, + "e:914_d:2024-11-05": 0, + "e:914_d:2024-11-06": 1, + "e:914_d:2024-11-07": 0, + "e:914_d:2024-11-08": 1, + "e:914_d:2024-11-09": 1, + "e:914_d:2024-11-10": 1, + "e:914_d:2024-11-11": 0, + "e:914_d:2024-11-12": 0, + "e:914_d:2024-11-13": 1, + "e:914_d:2024-11-14": 0, + "e:914_d:2024-11-15": 0, + "e:914_d:2024-11-16": 1, + "e:914_d:2024-11-17": 0, + "e:914_d:2024-11-18": 0, + "e:914_d:2024-11-19": 0, + "e:914_d:2024-11-20": 0, + "e:914_d:2024-11-21": 0, + "e:914_d:2024-11-22": 0, + "e:914_d:2024-11-23": 0, + "e:914_d:2024-11-24": 0, + "e:914_d:2024-11-25": 0, + "e:914_d:2024-11-26": 0, + "e:914_d:2024-11-27": 0, + "e:914_d:2024-11-28": 0, + "e:914_d:2024-11-29": 0, + "e:914_d:2024-11-30": 0, + "e:917_d:2024-11-01": 1, + "e:917_d:2024-11-02": 1, + "e:917_d:2024-11-03": 1, + "e:917_d:2024-11-04": 0, + "e:917_d:2024-11-05": 1, + "e:917_d:2024-11-06": 0, + "e:917_d:2024-11-07": 0, + "e:917_d:2024-11-08": 1, + "e:917_d:2024-11-09": 0, + "e:917_d:2024-11-10": 1, + "e:917_d:2024-11-11": 1, + "e:917_d:2024-11-12": 0, + "e:917_d:2024-11-13": 1, + "e:917_d:2024-11-14": 1, + "e:917_d:2024-11-15": 1, + "e:917_d:2024-11-16": 1, + "e:917_d:2024-11-17": 0, + "e:917_d:2024-11-18": 1, + "e:917_d:2024-11-19": 0, + "e:917_d:2024-11-20": 1, + "e:917_d:2024-11-21": 1, + "e:917_d:2024-11-22": 1, + "e:917_d:2024-11-23": 1, + "e:917_d:2024-11-24": 0, + "e:917_d:2024-11-25": 1, + "e:917_d:2024-11-26": 1, + "e:917_d:2024-11-27": 1, + "e:917_d:2024-11-28": 0, + "e:917_d:2024-11-29": 1, + "e:917_d:2024-11-30": 0, + "e:921_d:2024-11-01": 0, + "e:921_d:2024-11-02": 0, + "e:921_d:2024-11-03": 0, + "e:921_d:2024-11-04": 0, + "e:921_d:2024-11-05": 0, + "e:921_d:2024-11-06": 0, + "e:921_d:2024-11-07": 0, + "e:921_d:2024-11-08": 0, + "e:921_d:2024-11-09": 0, + "e:921_d:2024-11-10": 0, + "e:921_d:2024-11-11": 0, + "e:921_d:2024-11-12": 0, + "e:921_d:2024-11-13": 0, + "e:921_d:2024-11-14": 0, + "e:921_d:2024-11-15": 0, + "e:921_d:2024-11-16": 0, + "e:921_d:2024-11-17": 0, + "e:921_d:2024-11-18": 0, + "e:921_d:2024-11-19": 0, + "e:921_d:2024-11-20": 0, + "e:921_d:2024-11-21": 0, + "e:921_d:2024-11-22": 0, + "e:921_d:2024-11-23": 0, + "e:921_d:2024-11-24": 0, + "e:921_d:2024-11-25": 0, + "e:921_d:2024-11-26": 0, + "e:921_d:2024-11-27": 0, + "e:921_d:2024-11-28": 0, + "e:921_d:2024-11-29": 0, + "e:921_d:2024-11-30": 0, + "e:924_d:2024-11-01": 0, + "e:924_d:2024-11-02": 0, + "e:924_d:2024-11-03": 0, + "e:924_d:2024-11-04": 0, + "e:924_d:2024-11-05": 0, + "e:924_d:2024-11-06": 0, + "e:924_d:2024-11-07": 0, + "e:924_d:2024-11-08": 0, + "e:924_d:2024-11-09": 0, + "e:924_d:2024-11-10": 1, + "e:924_d:2024-11-11": 1, + "e:924_d:2024-11-12": 0, + "e:924_d:2024-11-13": 0, + "e:924_d:2024-11-14": 0, + "e:924_d:2024-11-15": 0, + "e:924_d:2024-11-16": 0, + "e:924_d:2024-11-17": 0, + "e:924_d:2024-11-18": 0, + "e:924_d:2024-11-19": 0, + "e:924_d:2024-11-20": 0, + "e:924_d:2024-11-21": 0, + "e:924_d:2024-11-22": 0, + "e:924_d:2024-11-23": 0, + "e:924_d:2024-11-24": 1, + "e:924_d:2024-11-25": 1, + "e:924_d:2024-11-26": 0, + "e:924_d:2024-11-27": 1, + "e:924_d:2024-11-28": 0, + "e:924_d:2024-11-29": 0, + "e:924_d:2024-11-30": 1, + "e:925_d:2024-11-01": 0, + "e:925_d:2024-11-02": 0, + "e:925_d:2024-11-03": 1, + "e:925_d:2024-11-04": 1, + "e:925_d:2024-11-05": 0, + "e:925_d:2024-11-06": 0, + "e:925_d:2024-11-07": 1, + "e:925_d:2024-11-08": 0, + "e:925_d:2024-11-09": 1, + "e:925_d:2024-11-10": 0, + "e:925_d:2024-11-11": 0, + "e:925_d:2024-11-12": 1, + "e:925_d:2024-11-13": 1, + "e:925_d:2024-11-14": 0, + "e:925_d:2024-11-15": 0, + "e:925_d:2024-11-16": 0, + "e:925_d:2024-11-17": 0, + "e:925_d:2024-11-18": 0, + "e:925_d:2024-11-19": 0, + "e:925_d:2024-11-20": 0, + "e:925_d:2024-11-21": 0, + "e:925_d:2024-11-22": 0, + "e:925_d:2024-11-23": 1, + "e:925_d:2024-11-24": 1, + "e:925_d:2024-11-25": 0, + "e:925_d:2024-11-26": 0, + "e:925_d:2024-11-27": 0, + "e:925_d:2024-11-28": 1, + "e:925_d:2024-11-29": 1, + "e:925_d:2024-11-30": 0, + "e:927_d:2024-11-01": 0, + "e:927_d:2024-11-02": 1, + "e:927_d:2024-11-03": 0, + "e:927_d:2024-11-04": 0, + "e:927_d:2024-11-05": 1, + "e:927_d:2024-11-06": 1, + "e:927_d:2024-11-07": 1, + "e:927_d:2024-11-08": 1, + "e:927_d:2024-11-09": 1, + "e:927_d:2024-11-10": 0, + "e:927_d:2024-11-11": 1, + "e:927_d:2024-11-12": 0, + "e:927_d:2024-11-13": 1, + "e:927_d:2024-11-14": 1, + "e:927_d:2024-11-15": 1, + "e:927_d:2024-11-16": 1, + "e:927_d:2024-11-17": 1, + "e:927_d:2024-11-18": 0, + "e:927_d:2024-11-19": 0, + "e:927_d:2024-11-20": 1, + "e:927_d:2024-11-21": 0, + "e:927_d:2024-11-22": 1, + "e:927_d:2024-11-23": 0, + "e:927_d:2024-11-24": 1, + "e:927_d:2024-11-25": 0, + "e:927_d:2024-11-26": 1, + "e:927_d:2024-11-27": 0, + "e:927_d:2024-11-28": 0, + "e:927_d:2024-11-29": 1, + "e:927_d:2024-11-30": 1, + "e:928_d:2024-11-01": 0, + "e:928_d:2024-11-02": 0, + "e:928_d:2024-11-03": 0, + "e:928_d:2024-11-04": 1, + "e:928_d:2024-11-05": 0, + "e:928_d:2024-11-06": 0, + "e:928_d:2024-11-07": 0, + "e:928_d:2024-11-08": 0, + "e:928_d:2024-11-09": 0, + "e:928_d:2024-11-10": 0, + "e:928_d:2024-11-11": 0, + "e:928_d:2024-11-12": 0, + "e:928_d:2024-11-13": 1, + "e:928_d:2024-11-14": 1, + "e:928_d:2024-11-15": 0, + "e:928_d:2024-11-16": 0, + "e:928_d:2024-11-17": 0, + "e:928_d:2024-11-18": 0, + "e:928_d:2024-11-19": 0, + "e:928_d:2024-11-20": 0, + "e:928_d:2024-11-21": 1, + "e:928_d:2024-11-22": 0, + "e:928_d:2024-11-23": 0, + "e:928_d:2024-11-24": 0, + "e:928_d:2024-11-25": 0, + "e:928_d:2024-11-26": 0, + "e:928_d:2024-11-27": 0, + "e:928_d:2024-11-28": 0, + "e:928_d:2024-11-29": 0, + "e:928_d:2024-11-30": 0 + } +} diff --git a/src/api/__init__.py b/legacy/src/api/__init__.py similarity index 100% rename from src/api/__init__.py rename to legacy/src/api/__init__.py diff --git a/src/api/main.py b/legacy/src/api/main.py similarity index 98% rename from src/api/main.py rename to legacy/src/api/main.py index df78cf3a..5c6edf6e 100644 --- a/src/api/main.py +++ b/legacy/src/api/main.py @@ -9,9 +9,9 @@ from fastapi import FastAPI, HTTPException from pydantic import BaseModel -from src.db.export_main import main as fetcher -from src.db.import_main import main as inserter -from src.services.solve_service import execute_solve, execute_solve_multiple +from legacy.src.db.export_main import main as fetcher +from legacy.src.db.import_main import main as inserter +from legacy.src.services.solve_service import execute_solve, execute_solve_multiple load_dotenv() diff --git a/src/cp/__init__.py b/legacy/src/cp/__init__.py similarity index 100% rename from src/cp/__init__.py rename to legacy/src/cp/__init__.py diff --git a/src/cp/constants.py b/legacy/src/cp/constants.py similarity index 100% rename from src/cp/constants.py rename to legacy/src/cp/constants.py diff --git a/src/cp/constraints/__init__.py b/legacy/src/cp/constraints/__init__.py similarity index 100% rename from src/cp/constraints/__init__.py rename to legacy/src/cp/constraints/__init__.py diff --git a/src/cp/constraints/constraint.py b/legacy/src/cp/constraints/constraint.py similarity index 92% rename from src/cp/constraints/constraint.py rename to legacy/src/cp/constraints/constraint.py index 80fff929..cab58ed9 100644 --- a/src/cp/constraints/constraint.py +++ b/legacy/src/cp/constraints/constraint.py @@ -2,9 +2,9 @@ from ortools.sat.python.cp_model import CpModel, LinearExpr -from src.day import Day -from src.employee import Employee -from src.shift import Shift +from legacy.src.day import Day +from legacy.src.employee import Employee +from legacy.src.shift import Shift from ..variables import EmployeeWorksOnDayVariables, ShiftAssignmentVariables diff --git a/src/cp/constraints/free_day_after_night_shift_phase.py b/legacy/src/cp/constraints/free_day_after_night_shift_phase.py similarity index 96% rename from src/cp/constraints/free_day_after_night_shift_phase.py rename to legacy/src/cp/constraints/free_day_after_night_shift_phase.py index af45fffb..0a0a80b6 100644 --- a/src/cp/constraints/free_day_after_night_shift_phase.py +++ b/legacy/src/cp/constraints/free_day_after_night_shift_phase.py @@ -2,9 +2,9 @@ from ortools.sat.python.cp_model import CpModel -from src.day import Day -from src.employee import Employee -from src.shift import Shift +from legacy.src.day import Day +from legacy.src.employee import Employee +from legacy.src.shift import Shift from ..constants import SPECIAL_NIGHT_SHIFT_INDEX from ..variables import EmployeeWorksOnDayVariables, ShiftAssignmentVariables diff --git a/src/cp/constraints/hierarchy_of_intermediate_shifts.py b/legacy/src/cp/constraints/hierarchy_of_intermediate_shifts.py similarity index 97% rename from src/cp/constraints/hierarchy_of_intermediate_shifts.py rename to legacy/src/cp/constraints/hierarchy_of_intermediate_shifts.py index 4c3d9eff..e194c9df 100644 --- a/src/cp/constraints/hierarchy_of_intermediate_shifts.py +++ b/legacy/src/cp/constraints/hierarchy_of_intermediate_shifts.py @@ -1,8 +1,8 @@ from ortools.sat.python.cp_model import CpModel, LinearExpr -from src.day import Day -from src.employee import Employee -from src.shift import Shift +from legacy.src.day import Day +from legacy.src.employee import Employee +from legacy.src.shift import Shift from ..constants import WEEKEND_DAYS from ..variables import EmployeeWorksOnDayVariables, ShiftAssignmentVariables, Variable diff --git a/src/cp/constraints/max_one_shift_per_day.py b/legacy/src/cp/constraints/max_one_shift_per_day.py similarity index 89% rename from src/cp/constraints/max_one_shift_per_day.py rename to legacy/src/cp/constraints/max_one_shift_per_day.py index cb182c09..c4636e73 100644 --- a/src/cp/constraints/max_one_shift_per_day.py +++ b/legacy/src/cp/constraints/max_one_shift_per_day.py @@ -1,8 +1,8 @@ from ortools.sat.python.cp_model import CpModel -from src.day import Day -from src.employee import Employee -from src.shift import Shift +from legacy.src.day import Day +from legacy.src.employee import Employee +from legacy.src.shift import Shift from ..variables import EmployeeWorksOnDayVariables, ShiftAssignmentVariables from .constraint import Constraint diff --git a/src/cp/constraints/min_rest_time.py b/legacy/src/cp/constraints/min_rest_time.py similarity index 92% rename from src/cp/constraints/min_rest_time.py rename to legacy/src/cp/constraints/min_rest_time.py index 811036f5..ae25976a 100644 --- a/src/cp/constraints/min_rest_time.py +++ b/legacy/src/cp/constraints/min_rest_time.py @@ -2,9 +2,9 @@ from ortools.sat.python.cp_model import CpModel -from src.day import Day -from src.employee import Employee -from src.shift import Shift +from legacy.src.day import Day +from legacy.src.employee import Employee +from legacy.src.shift import Shift from ..variables import EmployeeWorksOnDayVariables, ShiftAssignmentVariables from .constraint import Constraint diff --git a/src/cp/constraints/min_staffing.py b/legacy/src/cp/constraints/min_staffing.py similarity index 95% rename from src/cp/constraints/min_staffing.py rename to legacy/src/cp/constraints/min_staffing.py index 1468b867..f1941378 100644 --- a/src/cp/constraints/min_staffing.py +++ b/legacy/src/cp/constraints/min_staffing.py @@ -1,8 +1,8 @@ from ortools.sat.python.cp_model import CpModel -from src.day import Day -from src.employee import Employee -from src.shift import Shift +from legacy.src.day import Day +from legacy.src.employee import Employee +from legacy.src.shift import Shift from ..variables import EmployeeWorksOnDayVariables, ShiftAssignmentVariables, Variable from .constraint import Constraint diff --git a/src/cp/constraints/planned_shifts.py b/legacy/src/cp/constraints/planned_shifts.py similarity index 97% rename from src/cp/constraints/planned_shifts.py rename to legacy/src/cp/constraints/planned_shifts.py index d35d3ee5..fab105d1 100644 --- a/src/cp/constraints/planned_shifts.py +++ b/legacy/src/cp/constraints/planned_shifts.py @@ -2,9 +2,9 @@ from ortools.sat.python.cp_model import CpModel -from src.day import Day -from src.employee import Employee -from src.shift import Shift +from legacy.src.day import Day +from legacy.src.employee import Employee +from legacy.src.shift import Shift from ..variables import EmployeeWorksOnDayVariables, ShiftAssignmentVariables from .constraint import Constraint diff --git a/src/cp/constraints/rounds_in_early_shift.py b/legacy/src/cp/constraints/rounds_in_early_shift.py similarity index 91% rename from src/cp/constraints/rounds_in_early_shift.py rename to legacy/src/cp/constraints/rounds_in_early_shift.py index 1587e86a..ce0ca6fe 100644 --- a/src/cp/constraints/rounds_in_early_shift.py +++ b/legacy/src/cp/constraints/rounds_in_early_shift.py @@ -1,8 +1,8 @@ from ortools.sat.python.cp_model import CpModel -from src.day import Day -from src.employee import Employee -from src.shift import Shift +from legacy.src.day import Day +from legacy.src.employee import Employee +from legacy.src.shift import Shift from ..constants import WEEKDAYS from ..variables import EmployeeWorksOnDayVariables, ShiftAssignmentVariables diff --git a/src/cp/constraints/target_working_time.py b/legacy/src/cp/constraints/target_working_time.py similarity index 97% rename from src/cp/constraints/target_working_time.py rename to legacy/src/cp/constraints/target_working_time.py index e3b967cf..c5f2eaee 100644 --- a/src/cp/constraints/target_working_time.py +++ b/legacy/src/cp/constraints/target_working_time.py @@ -1,8 +1,8 @@ from ortools.sat.python.cp_model import CpModel, Domain, LinearExpr -from src.day import Day -from src.employee import Employee -from src.shift import Shift +from legacy.src.day import Day +from legacy.src.employee import Employee +from legacy.src.shift import Shift from ..constants import DEFAULT_MONTHLY_TARGET_MINUTES, TOLERANCE_LESS, TOLERANCE_MORE from ..variables import EmployeeWorksOnDayVariables, ShiftAssignmentVariables diff --git a/src/cp/constraints/vacation_days_and_shifts.py b/legacy/src/cp/constraints/vacation_days_and_shifts.py similarity index 94% rename from src/cp/constraints/vacation_days_and_shifts.py rename to legacy/src/cp/constraints/vacation_days_and_shifts.py index d915b213..674003d8 100644 --- a/src/cp/constraints/vacation_days_and_shifts.py +++ b/legacy/src/cp/constraints/vacation_days_and_shifts.py @@ -2,9 +2,9 @@ from ortools.sat.python.cp_model import CpModel -from src.day import Day -from src.employee import Employee -from src.shift import Shift +from legacy.src.day import Day +from legacy.src.employee import Employee +from legacy.src.shift import Shift from ..variables import EmployeeWorksOnDayVariables, ShiftAssignmentVariables from .constraint import Constraint diff --git a/src/cp/model.py b/legacy/src/cp/model.py similarity index 97% rename from src/cp/model.py rename to legacy/src/cp/model.py index 67bbe3e3..f1e54ef6 100644 --- a/src/cp/model.py +++ b/legacy/src/cp/model.py @@ -9,10 +9,10 @@ LinearExpr, ) -from src.day import Day -from src.employee import Employee -from src.shift import Shift -from src.solution import Solution +from legacy.src.day import Day +from legacy.src.employee import Employee +from legacy.src.shift import Shift +from legacy.src.solution import Solution from .constraints import Constraint from .objectives import Objective diff --git a/src/cp/objectives/__init__.py b/legacy/src/cp/objectives/__init__.py similarity index 100% rename from src/cp/objectives/__init__.py rename to legacy/src/cp/objectives/__init__.py diff --git a/src/cp/objectives/every_second_weekend_free.py b/legacy/src/cp/objectives/every_second_weekend_free.py similarity index 98% rename from src/cp/objectives/every_second_weekend_free.py rename to legacy/src/cp/objectives/every_second_weekend_free.py index b7bd401c..3062e25a 100644 --- a/src/cp/objectives/every_second_weekend_free.py +++ b/legacy/src/cp/objectives/every_second_weekend_free.py @@ -4,8 +4,8 @@ from ortools.sat.python.cp_model import CpModel, IntVar, LinearExpr -from src.day import Day -from src.employee import Employee +from legacy.src.day import Day +from legacy.src.employee import Employee from ..variables import EmployeeWorksOnDayVariables, ShiftAssignmentVariables from .objective import Objective diff --git a/src/cp/objectives/free_days_after_night_shift_phase.py b/legacy/src/cp/objectives/free_days_after_night_shift_phase.py similarity index 93% rename from src/cp/objectives/free_days_after_night_shift_phase.py rename to legacy/src/cp/objectives/free_days_after_night_shift_phase.py index 2ebe5c54..070d0f95 100644 --- a/src/cp/objectives/free_days_after_night_shift_phase.py +++ b/legacy/src/cp/objectives/free_days_after_night_shift_phase.py @@ -3,9 +3,9 @@ from ortools.sat.python.cp_model import CpModel, IntVar, LinearExpr -from src.day import Day -from src.employee import Employee -from src.shift import Shift +from legacy.src.day import Day +from legacy.src.employee import Employee +from legacy.src.shift import Shift from ..variables import EmployeeWorksOnDayVariables, ShiftAssignmentVariables from .objective import Objective diff --git a/src/cp/objectives/free_days_near_weekend.py b/legacy/src/cp/objectives/free_days_near_weekend.py similarity index 98% rename from src/cp/objectives/free_days_near_weekend.py rename to legacy/src/cp/objectives/free_days_near_weekend.py index 8a8bef9d..67f9165e 100644 --- a/src/cp/objectives/free_days_near_weekend.py +++ b/legacy/src/cp/objectives/free_days_near_weekend.py @@ -3,8 +3,8 @@ from ortools.sat.python.cp_model import CpModel, IntVar, LinearExpr -from src.day import Day -from src.employee import Employee +from legacy.src.day import Day +from legacy.src.employee import Employee from ..constants import NEAR_WEEKEND_DAYS from ..variables import EmployeeWorksOnDayVariables, ShiftAssignmentVariables diff --git a/src/cp/objectives/maximize_wishes.py b/legacy/src/cp/objectives/maximize_wishes.py similarity index 100% rename from src/cp/objectives/maximize_wishes.py rename to legacy/src/cp/objectives/maximize_wishes.py diff --git a/src/cp/objectives/minimize_consecutive_night_shifts.py b/legacy/src/cp/objectives/minimize_consecutive_night_shifts.py similarity index 95% rename from src/cp/objectives/minimize_consecutive_night_shifts.py rename to legacy/src/cp/objectives/minimize_consecutive_night_shifts.py index ec88e4c4..3f82b5a8 100644 --- a/src/cp/objectives/minimize_consecutive_night_shifts.py +++ b/legacy/src/cp/objectives/minimize_consecutive_night_shifts.py @@ -3,9 +3,9 @@ from ortools.sat.python.cp_model import CpModel, IntVar, LinearExpr -from src.day import Day -from src.employee import Employee -from src.shift import Shift +from legacy.src.day import Day +from legacy.src.employee import Employee +from legacy.src.shift import Shift from ..variables import EmployeeWorksOnDayVariables, ShiftAssignmentVariables from .objective import Objective diff --git a/src/cp/objectives/minimize_hidden_employee_count.py b/legacy/src/cp/objectives/minimize_hidden_employee_count.py similarity index 93% rename from src/cp/objectives/minimize_hidden_employee_count.py rename to legacy/src/cp/objectives/minimize_hidden_employee_count.py index 62d4327f..ea09abcc 100644 --- a/src/cp/objectives/minimize_hidden_employee_count.py +++ b/legacy/src/cp/objectives/minimize_hidden_employee_count.py @@ -2,9 +2,9 @@ from ortools.sat.python.cp_model import CpModel, IntVar, LinearExpr -from src.day import Day -from src.employee import Employee -from src.shift import Shift +from legacy.src.day import Day +from legacy.src.employee import Employee +from legacy.src.shift import Shift from ..variables import EmployeeWorksOnDayVariables, ShiftAssignmentVariables from .objective import Objective diff --git a/src/cp/objectives/minimize_hidden_employees.py b/legacy/src/cp/objectives/minimize_hidden_employees.py similarity index 94% rename from src/cp/objectives/minimize_hidden_employees.py rename to legacy/src/cp/objectives/minimize_hidden_employees.py index d9315142..34240215 100644 --- a/src/cp/objectives/minimize_hidden_employees.py +++ b/legacy/src/cp/objectives/minimize_hidden_employees.py @@ -2,9 +2,9 @@ from ortools.sat.python.cp_model import CpModel, IntVar, LinearExpr -from src.day import Day -from src.employee import Employee -from src.shift import Shift +from legacy.src.day import Day +from legacy.src.employee import Employee +from legacy.src.shift import Shift from ..constants import MAX_DURATION_MINUTES from ..variables import EmployeeWorksOnDayVariables, ShiftAssignmentVariables diff --git a/src/cp/objectives/minimize_overtime.py b/legacy/src/cp/objectives/minimize_overtime.py similarity index 95% rename from src/cp/objectives/minimize_overtime.py rename to legacy/src/cp/objectives/minimize_overtime.py index 3a11fddb..6d857e4c 100644 --- a/src/cp/objectives/minimize_overtime.py +++ b/legacy/src/cp/objectives/minimize_overtime.py @@ -2,9 +2,9 @@ from ortools.sat.python.cp_model import CpModel, IntVar, LinearExpr -from src.day import Day -from src.employee import Employee -from src.shift import Shift +from legacy.src.day import Day +from legacy.src.employee import Employee +from legacy.src.shift import Shift from ..constants import MAX_DURATION_MINUTES from ..variables import EmployeeWorksOnDayVariables, ShiftAssignmentVariables diff --git a/src/cp/objectives/not_too_many_consecutive_days.py b/legacy/src/cp/objectives/not_too_many_consecutive_days.py similarity index 96% rename from src/cp/objectives/not_too_many_consecutive_days.py rename to legacy/src/cp/objectives/not_too_many_consecutive_days.py index 19cb5c8f..bdacb064 100644 --- a/src/cp/objectives/not_too_many_consecutive_days.py +++ b/legacy/src/cp/objectives/not_too_many_consecutive_days.py @@ -3,8 +3,8 @@ from ortools.sat.python.cp_model import CpModel, IntVar, LinearExpr -from src.day import Day -from src.employee import Employee +from legacy.src.day import Day +from legacy.src.employee import Employee from ..variables import EmployeeWorksOnDayVariables, ShiftAssignmentVariables from .objective import Objective diff --git a/src/cp/objectives/objective.py b/legacy/src/cp/objectives/objective.py similarity index 91% rename from src/cp/objectives/objective.py rename to legacy/src/cp/objectives/objective.py index defd84f0..ccaf52b2 100644 --- a/src/cp/objectives/objective.py +++ b/legacy/src/cp/objectives/objective.py @@ -2,9 +2,9 @@ from ortools.sat.python.cp_model import CpModel, LinearExpr -from src.day import Day -from src.employee import Employee -from src.shift import Shift +from legacy.src.day import Day +from legacy.src.employee import Employee +from legacy.src.shift import Shift from ..constraints import Constraint from ..variables import EmployeeWorksOnDayVariables, ShiftAssignmentVariables diff --git a/src/cp/objectives/preferred_block_length.py b/legacy/src/cp/objectives/preferred_block_length.py similarity index 98% rename from src/cp/objectives/preferred_block_length.py rename to legacy/src/cp/objectives/preferred_block_length.py index 663bcf7c..3c6cc7dc 100644 --- a/src/cp/objectives/preferred_block_length.py +++ b/legacy/src/cp/objectives/preferred_block_length.py @@ -3,8 +3,8 @@ from ortools.sat.python.cp_model import CpModel, IntVar, LinearExpr, LiteralT -from src.day import Day -from src.employee import Employee +from legacy.src.day import Day +from legacy.src.employee import Employee from ..variables import EmployeeWorksOnDayVariables, ShiftAssignmentVariables from .objective import Objective diff --git a/src/cp/objectives/rotate_shifts_forward.py b/legacy/src/cp/objectives/rotate_shifts_forward.py similarity index 97% rename from src/cp/objectives/rotate_shifts_forward.py rename to legacy/src/cp/objectives/rotate_shifts_forward.py index a113d9b5..2f05bfc2 100644 --- a/src/cp/objectives/rotate_shifts_forward.py +++ b/legacy/src/cp/objectives/rotate_shifts_forward.py @@ -2,9 +2,9 @@ from ortools.sat.python.cp_model import CpModel, IntVar, LinearExpr -from src.day import Day -from src.employee import Employee -from src.shift import Shift +from legacy.src.day import Day +from legacy.src.employee import Employee +from legacy.src.shift import Shift from ..variables import EmployeeWorksOnDayVariables, ShiftAssignmentVariables from .objective import Objective diff --git a/src/cp/variables/__init__.py b/legacy/src/cp/variables/__init__.py similarity index 100% rename from src/cp/variables/__init__.py rename to legacy/src/cp/variables/__init__.py diff --git a/src/cp/variables/variable.py b/legacy/src/cp/variables/variable.py similarity index 97% rename from src/cp/variables/variable.py rename to legacy/src/cp/variables/variable.py index 86e8e196..122c88bc 100644 --- a/src/cp/variables/variable.py +++ b/legacy/src/cp/variables/variable.py @@ -1,8 +1,8 @@ from ortools.sat.python.cp_model import BoolVarT, CpModel -from src.day import Day -from src.employee import Employee -from src.shift import Shift +from legacy.src.day import Day +from legacy.src.employee import Employee +from legacy.src.shift import Shift type Variable = BoolVarT diff --git a/src/day.py b/legacy/src/day.py similarity index 100% rename from src/day.py rename to legacy/src/day.py diff --git a/src/db/connection_setup.py b/legacy/src/db/connection_setup.py similarity index 100% rename from src/db/connection_setup.py rename to legacy/src/db/connection_setup.py diff --git a/src/db/export_data.py b/legacy/src/db/export_data.py similarity index 100% rename from src/db/export_data.py rename to legacy/src/db/export_data.py diff --git a/src/db/export_main.py b/legacy/src/db/export_main.py similarity index 97% rename from src/db/export_main.py rename to legacy/src/db/export_main.py index e90f07ec..6163037c 100644 --- a/src/db/export_main.py +++ b/legacy/src/db/export_main.py @@ -1,6 +1,6 @@ from datetime import date -from . import export_data +from ....src import export_data from .connection_setup import get_db_engine diff --git a/src/db/import_main.py b/legacy/src/db/import_main.py similarity index 97% rename from src/db/import_main.py rename to legacy/src/db/import_main.py index fb3f0018..f9c12748 100644 --- a/src/db/import_main.py +++ b/legacy/src/db/import_main.py @@ -3,7 +3,8 @@ from datetime import date from typing import Any -from . import export_data, import_solution +from ....legacy.db import import_solution +from ....src import export_data from .connection_setup import get_db_engine diff --git a/src/db/import_solution.py b/legacy/src/db/import_solution.py similarity index 100% rename from src/db/import_solution.py rename to legacy/src/db/import_solution.py diff --git a/src/employee.py b/legacy/src/employee.py similarity index 98% rename from src/employee.py rename to legacy/src/employee.py index 3e7abace..31de3dfa 100644 --- a/src/employee.py +++ b/legacy/src/employee.py @@ -1,5 +1,5 @@ -from src.day import Day -from src.shift import Shift +from legacy.src.day import Day +from legacy.src.shift import Shift class Employee: diff --git a/src/loader/__init__.py b/legacy/src/loader/__init__.py similarity index 100% rename from src/loader/__init__.py rename to legacy/src/loader/__init__.py diff --git a/src/loader/filesystem_loader.py b/legacy/src/loader/filesystem_loader.py similarity index 99% rename from src/loader/filesystem_loader.py rename to legacy/src/loader/filesystem_loader.py index 27c529cd..d668536a 100644 --- a/src/loader/filesystem_loader.py +++ b/legacy/src/loader/filesystem_loader.py @@ -6,9 +6,9 @@ from os import listdir from typing import Any -from src.employee import Employee -from src.shift import Shift -from src.solution import Solution +from legacy.src.employee import Employee +from legacy.src.shift import Shift +from legacy.src.solution import Solution from .loader import Loader diff --git a/src/loader/loader.py b/legacy/src/loader/loader.py similarity index 94% rename from src/loader/loader.py rename to legacy/src/loader/loader.py index 794add1c..58201755 100644 --- a/src/loader/loader.py +++ b/legacy/src/loader/loader.py @@ -1,9 +1,9 @@ from abc import ABC, abstractmethod from datetime import date -from src.employee import Employee -from src.shift import Shift -from src.solution import Solution +from legacy.src.employee import Employee +from legacy.src.shift import Shift +from legacy.src.solution import Solution class Loader(ABC): diff --git a/src/main.py b/legacy/src/main.py similarity index 83% rename from src/main.py rename to legacy/src/main.py index b281282b..bd560f1a 100644 --- a/src/main.py +++ b/legacy/src/main.py @@ -2,12 +2,12 @@ import click -from src.db.import_main import main as inserter -from src.loader import FSLoader -from src.scheduling.timeoffice.models import FetchStationsRequest, PlanningPeriod -from src.scheduling.timeoffice.service import create_timeoffice_service -from src.services.solve_service import execute_solve, execute_solve_multiple -from src.web import App +from legacy.src.db.import_main import main as inserter +from legacy.src.loader import FSLoader +from legacy.src.services.solve_service import execute_solve, execute_solve_multiple +from legacy.src.web import App +from src.scheduling.timeoffice.models import PlanningPeriod +from src.scheduling.timeoffice.service import get_timeoffice_service @click.group() @@ -25,39 +25,33 @@ def cli(ctx: click.Context): required=True, help="Planning unit/station to fetch. Can be passed multiple times.", ) -@click.option( - "--use-cache", - is_flag=True, - help="Try loading cached TimeOffice data before reading the database.", -) @click.argument("start", type=click.DateTime(formats=["%d.%m.%Y"])) @click.argument("end", type=click.DateTime(formats=["%d.%m.%Y"])) def fetch( stations: tuple[int, ...], - use_cache: bool, start: datetime, end: datetime, ): """Fetch TimeOffice data and update the local cache.""" - request = FetchStationsRequest( - station_ids=stations, + + timeoffice = get_timeoffice_service() + dataset = timeoffice.fetch_dataset( + selected_station_ids=stations, period=PlanningPeriod( start=start.date(), end=end.date(), ), ) - timeoffice = create_timeoffice_service() - dataset = timeoffice.fetch(request) - - click.echo( - "Prepared scheduling dataset: " - f"stations={len(dataset.stations)}, " - f"regular={list(dataset.regular_station_ids)}, " - f"jump_pool={list(dataset.jump_pool_station_ids)}, " - f"employees={len(dataset.employees)}, " - f"shifts={len(dataset.shifts)}, " - f"demand={len(dataset.demand)}" + print( + "[timeoffice] dataset " + f"stations={len(dataset.stations)} " + f"pools={len(dataset.pools)} " + f"pool_memberships={len(dataset.pool_memberships)} " + f"employees={len(dataset.employees)} " + f"shifts={len(dataset.shifts)} " + f"assignments={len(dataset.assignments)} " + f"availability={len(dataset.availability)}" ) diff --git a/src/run.bat b/legacy/src/run.bat similarity index 100% rename from src/run.bat rename to legacy/src/run.bat diff --git a/src/services/__init__.py b/legacy/src/services/__init__.py similarity index 100% rename from src/services/__init__.py rename to legacy/src/services/__init__.py diff --git a/src/services/solve_service.py b/legacy/src/services/solve_service.py similarity index 97% rename from src/services/solve_service.py rename to legacy/src/services/solve_service.py index ecf28256..bc42aaf3 100644 --- a/src/services/solve_service.py +++ b/legacy/src/services/solve_service.py @@ -5,9 +5,9 @@ from pathlib import Path from typing import Any, TypedDict -from src.loader import FSLoader -from src.solve import main as run_solver -from src.web.process_solution import process_solution +from legacy.src.loader import FSLoader +from legacy.src.solve import main as run_solver +from legacy.src.web.process_solution import process_solution DEFAULT_WEIGHTS = { "free_weekend": 2, diff --git a/src/shift.py b/legacy/src/shift.py similarity index 100% rename from src/shift.py rename to legacy/src/shift.py diff --git a/src/solution.py b/legacy/src/solution.py similarity index 100% rename from src/solution.py rename to legacy/src/solution.py diff --git a/src/solve.py b/legacy/src/solve.py similarity index 97% rename from src/solve.py rename to legacy/src/solve.py index 0c215807..f11804fa 100644 --- a/src/solve.py +++ b/legacy/src/solve.py @@ -5,7 +5,7 @@ from ortools.sat.python.cp_model import CpSolver -from src.cp import ( +from legacy.src.cp import ( EverySecondWeekendFreeObjective, FreeDayAfterNightShiftPhaseConstraint, FreeDaysAfterNightShiftPhaseObjective, @@ -28,12 +28,12 @@ TargetWorkingTimeConstraint, VacationDaysAndShiftsConstraint, ) -from src.cp.constants import MAX_CONSECUTIVE_DAYS -from src.day import Day -from src.employee import Employee -from src.loader import FSLoader -from src.shift import Shift -from src.solution import Solution +from legacy.src.cp.constants import MAX_CONSECUTIVE_DAYS +from legacy.src.day import Day +from legacy.src.employee import Employee +from legacy.src.loader import FSLoader +from legacy.src.shift import Shift +from legacy.src.solution import Solution logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s") diff --git a/src/web/__init__.py b/legacy/src/web/__init__.py similarity index 100% rename from src/web/__init__.py rename to legacy/src/web/__init__.py diff --git a/src/web/analyze_solution.py b/legacy/src/web/analyze_solution.py similarity index 98% rename from src/web/analyze_solution.py rename to legacy/src/web/analyze_solution.py index 0d167878..9d34805c 100644 --- a/src/web/analyze_solution.py +++ b/legacy/src/web/analyze_solution.py @@ -2,8 +2,8 @@ from collections import defaultdict from datetime import date, datetime, timedelta -from src.employee import Employee -from src.shift import Shift +from legacy.src.employee import Employee +from legacy.src.shift import Shift # analyze_solution and its helper functions calculate various metrics # and violations based on the assigned shifts for each employee. diff --git a/src/web/app.py b/legacy/src/web/app.py similarity index 100% rename from src/web/app.py rename to legacy/src/web/app.py diff --git a/src/web/process_solution.py b/legacy/src/web/process_solution.py similarity index 100% rename from src/web/process_solution.py rename to legacy/src/web/process_solution.py diff --git a/src/web/templates/index.html b/legacy/src/web/templates/index.html similarity index 100% rename from src/web/templates/index.html rename to legacy/src/web/templates/index.html diff --git a/pyproject.toml b/pyproject.toml index 490ac1bd..74465cad 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,24 +5,29 @@ description = "" readme = "README.md" requires-python = ">=3.12" dependencies = [ + "fastapi[standard]>=0.137.0", + "SQLAlchemy>=2.0.50", + "pydantic>=2.13.4", + "pydantic-settings>=2.14.1", + "ortools>=9.15.6755", + + # Legacy -> Should be removed after refactoring "click>=8.2.0", - "fastapi>=0.115.0", "flask>=3.1.0", + "pyodbc>=5.2.0", "matplotlib>=3.10.1", - "numpy>=2.2.5", - "ortools>=9.12.4544", "pandas>=2.2.3", - "pyodbc>=5.2.0", - "SQLAlchemy>=2.0.41", - "pytest>=9.0.1", - "uvicorn>=0.34.0", + "numpy>=2.2.5", "dotenv>=0.9.9", - "pydantic>=2.12.5", - "pydantic-settings>=2.14.1", ] [dependency-groups] -dev = ["pre-commit>=4.2.0", "pyright>=1.1.407", "ruff>=0.14.4"] +dev = [ + "pre-commit>=4.6.0", + "pyright>=1.1.410", + "ruff>=0.15.17", + "pytest>=9.1.0", +] [project.optional-dependencies] docs = [ diff --git a/src/SELECT.sql b/src/SELECT.sql new file mode 100644 index 00000000..1f084063 --- /dev/null +++ b/src/SELECT.sql @@ -0,0 +1,18 @@ +WITH selected_employees AS ( + SELECT DISTINCT pp.RefPersonal AS employee_id + FROM TPlanPersonal pp + WHERE pp.RefPlan IN (17916, 17045) +) +SELECT + se.employee_id, + COUNT(DISTINCT CAST(pkt.Datum AS date)) AS worked_sundays +FROM selected_employees se +LEFT JOIN TPersonalKontenJeTag pkt + ON pkt.RefPersonal = se.employee_id + AND pkt.RefKonten = 40 + AND pkt.Datum BETWEEN CONVERT(date, '2023-11-30', 23) + AND CONVERT(date, '2024-11-30', 23) + AND DATEDIFF(day, CONVERT(date, '1900-01-07', 23), CAST(pkt.Datum AS date)) % 7 = 0 + AND ISNULL(pkt.Wert, 0) > 0 +GROUP BY se.employee_id +ORDER BY worked_sundays DESC, se.employee_id; diff --git a/src/scheduling/api/__init__.py b/src/scheduling/api/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/scheduling/api/app.py b/src/scheduling/api/app.py new file mode 100644 index 00000000..dfa63c06 --- /dev/null +++ b/src/scheduling/api/app.py @@ -0,0 +1,22 @@ +import logging + +from fastapi import FastAPI + +from src.scheduling.api.data_router import data_router +from src.scheduling.api.solver_router import solver_router +from src.scheduling.logging import configure_logging +from src.scheduling.settings import get_settings + +settings = get_settings() +configure_logging(level=settings.log_level) + +logger = logging.getLogger(__name__) + +app = FastAPI(title="Staff Scheduling API") +app.include_router(solver_router, prefix="/solver") +app.include_router(data_router, prefix="/data") + + +@app.get("/health") +async def healthcheck(): + return {"status": "healthy"} diff --git a/src/scheduling/api/data_router.py b/src/scheduling/api/data_router.py new file mode 100644 index 00000000..2f35c0bc --- /dev/null +++ b/src/scheduling/api/data_router.py @@ -0,0 +1,48 @@ +import logging +from typing import Annotated + +from fastapi import APIRouter, Depends, Query + +from src.scheduling.api.dependencies import get_timeoffice_service +from src.scheduling.api.types import ApiDate +from src.scheduling.models import PlanningPeriod +from src.scheduling.models.assignment import AssignmentType +from src.scheduling.timeoffice.service import TimeOfficeService + +logger = logging.getLogger(__name__) + +data_router = APIRouter() + + +@data_router.get("/fetch") +def fetch_timeoffice_dataset( + station_ids: Annotated[list[int], Query(alias="station")], + start: Annotated[ApiDate, Query()], + end: Annotated[ApiDate, Query()], + timeoffice: Annotated[TimeOfficeService, Depends(get_timeoffice_service)], +) -> dict[str, int]: + period = PlanningPeriod(start=start, end=end) + + dataset = timeoffice.fetch_dataset( + planning_unit_ids=tuple(station_ids), + period=period, + ) + + return { + "planning_units": len(dataset.planning_units), + "plans": len(dataset.plans), + "employees": len(dataset.employees), + "plan_participants": len(dataset.plan_participants), + "planning_unit_memberships": len(dataset.planning_unit_memberships), + "shifts": len(dataset.shifts), + "assignments": len(dataset.assignments), + "planned_assignments": sum( + assignment.assignment_type == AssignmentType.PLANNED for assignment in dataset.assignments + ), + "external_assignments": sum( + assignment.assignment_type == AssignmentType.EXTERNAL for assignment in dataset.assignments + ), + "availability": len(dataset.availability), + "demand_requirements": len(dataset.demand_requirements), + "sunday_work_history": len(dataset.sunday_work_history), + } diff --git a/src/scheduling/api/dependencies.py b/src/scheduling/api/dependencies.py new file mode 100644 index 00000000..5a0c9d0c --- /dev/null +++ b/src/scheduling/api/dependencies.py @@ -0,0 +1,46 @@ +from functools import lru_cache +from typing import Annotated + +from fastapi import Depends +from sqlalchemy import Engine + +from src.scheduling.settings import get_settings +from src.scheduling.timeoffice.database import TimeOfficeDatabase, create_db_engine +from src.scheduling.timeoffice.facts import TIMEOFFICE_FACTS, TimeOfficeFacts +from src.scheduling.timeoffice.repositories import TimeOfficeRepositories +from src.scheduling.timeoffice.service import TimeOfficeService + + +@lru_cache(maxsize=1) +def get_db_engine() -> Engine: + """Create and cache the SQLAlchemy engine for the app process.""" + settings = get_settings() + return create_db_engine(settings=settings) + + +def get_timeoffice_facts() -> TimeOfficeFacts: + """Return static TimeOffice source facts.""" + return TIMEOFFICE_FACTS + + +@lru_cache(maxsize=1) +def get_timeoffice_repositories() -> TimeOfficeRepositories: + """Create stateless TimeOffice repositories.""" + return TimeOfficeRepositories.create(facts=TIMEOFFICE_FACTS) + + +def get_timeoffice_database( + engine: Annotated[Engine, Depends(get_db_engine)], + repositories: Annotated[TimeOfficeRepositories, Depends(get_timeoffice_repositories)], + facts: Annotated[TimeOfficeFacts, Depends(get_timeoffice_facts)], +) -> TimeOfficeDatabase: + """Create the TimeOffice database gateway.""" + return TimeOfficeDatabase(engine=engine, repositories=repositories, facts=facts) + + +def get_timeoffice_service( + database: Annotated[TimeOfficeDatabase, Depends(get_timeoffice_database)], + facts: Annotated[TimeOfficeFacts, Depends(get_timeoffice_facts)], +) -> TimeOfficeService: + """Create the high-level TimeOffice service.""" + return TimeOfficeService(database=database, facts=facts) diff --git a/src/scheduling/api/solver_router.py b/src/scheduling/api/solver_router.py new file mode 100644 index 00000000..f9eb266a --- /dev/null +++ b/src/scheduling/api/solver_router.py @@ -0,0 +1,65 @@ +import asyncio +import logging +import uuid +from dataclasses import dataclass +from typing import Annotated + +from fastapi import APIRouter, BackgroundTasks, Query, status +from fastapi.responses import JSONResponse + +logger = logging.getLogger(__name__) + + +solver_router = APIRouter() + + +@dataclass +class FakeSolution: + id: str + value: str + + +solutions: list[FakeSolution] = [] +lock = asyncio.Lock() + + +async def fake_solve(id: uuid.UUID) -> str: + logger.info("Started processing") + await asyncio.sleep(5) + logger.info("Finished processing") + + return f"{id} processed!" + + +@solver_router.post("/solve") +async def solve_schedule(background_tasks: BackgroundTasks): + try: + async with asyncio.timeout(2): + await lock.acquire() + except TimeoutError: + return JSONResponse( + status_code=status.HTTP_423_LOCKED, + content={ + "status": "blocked", + "message": "Solver already working on a different solution. Try again later!", + }, + ) + + unique_id = uuid.uuid4() + background_tasks.add_task(fake_solve, unique_id) + + +@solver_router.get("/solution") +def get_solution(id: Annotated[uuid.UUID, Query()]): + solution = next(filter(lambda s: s.id == id, solutions), None) + + if solution is None: + return JSONResponse( + status_code=status.HTTP_404_NOT_FOUND, + content={ + "status": "failed", + "message": "Solution not found!", + }, + ) + + return solution diff --git a/src/scheduling/api/types.py b/src/scheduling/api/types.py new file mode 100644 index 00000000..7b89535a --- /dev/null +++ b/src/scheduling/api/types.py @@ -0,0 +1,34 @@ +from datetime import date as Date +from datetime import datetime as DateTime +from typing import Annotated, Any + +from pydantic import BeforeValidator + + +def parse_api_date(value: Any) -> Date: + """Parse API date values. + + Primary API format is ISO: YYYY-MM-DD. + DD.MM.YYYY is accepted for compatibility with previous CLI usage. + """ + if isinstance(value, Date) and not isinstance(value, DateTime): + return value + + if isinstance(value, DateTime): + return value.date() + + if not isinstance(value, str): + raise ValueError("Date must be a string in YYYY-MM-DD format.") + + cleaned = value.strip() + + for date_format in ("%Y-%m-%d", "%d.%m.%Y"): + try: + return DateTime.strptime(cleaned, date_format).date() + except ValueError: + pass + + raise ValueError("Date must use YYYY-MM-DD, for example 2024-11-01.") + + +ApiDate = Annotated[Date, BeforeValidator(parse_api_date)] diff --git a/src/scheduling/logging.py b/src/scheduling/logging.py new file mode 100644 index 00000000..7145b7a5 --- /dev/null +++ b/src/scheduling/logging.py @@ -0,0 +1,21 @@ +import logging +import sys + + +def configure_logging(*, level: str = "INFO") -> None: + """Configure application logging once at process startup.""" + normalized_level = level.strip().upper() + numeric_level = logging.getLevelNamesMapping().get(normalized_level) + + if numeric_level is None: + raise ValueError(f"Invalid log level: {level!r}") + + root_logger = logging.getLogger() + root_logger.setLevel(numeric_level) + + if not root_logger.handlers: + handler = logging.StreamHandler(sys.stdout) + handler.setFormatter(logging.Formatter("%(asctime)s %(levelname)s [%(name)s] %(message)s")) + root_logger.addHandler(handler) + + logging.getLogger("sqlalchemy.engine").setLevel(logging.WARNING) diff --git a/src/scheduling/models/__init__.py b/src/scheduling/models/__init__.py index 9c667243..8b92090d 100644 --- a/src/scheduling/models/__init__.py +++ b/src/scheduling/models/__init__.py @@ -1,39 +1,41 @@ -from src.scheduling.models.core import PlanningPeriod -from src.scheduling.models.dataset import SchedulingDataset -from src.scheduling.models.demand import Demand, DemandType -from src.scheduling.models.employee import Employee -from src.scheduling.models.relations import ( - Assignment, - AssignmentType, - Availability, - AvailabilityType, - Membership, - MembershipType, - Preference, - PreferenceType, - Rule, - RuleType, -) -from src.scheduling.models.shift import Shift, ShiftKind -from src.scheduling.models.station import Station +from src.scheduling.models.assignment import Assignment, AssignmentType +from src.scheduling.models.availability import Availability, AvailabilityType +from src.scheduling.models.core import MinuteOfDay, NonEmptyStr, NonNegativeInt, PositiveId, SchedulingBaseModel +from src.scheduling.models.dataset import PlanningPeriod, SchedulingDataset +from src.scheduling.models.demand import DemandRequirement +from src.scheduling.models.employee import Capability, Employee, EmployeeId, StaffLevel +from src.scheduling.models.plan import Plan, PlanId, PlanParticipant +from src.scheduling.models.planning_unit import PlanningUnit, PlanningUnitId, PlanningUnitKind, PlanningUnitMembership +from src.scheduling.models.shift import Shift, ShiftId, ShiftKind, StaffingDemandRole +from src.scheduling.models.sunday_work_history import EmployeeSundayWorkHistory __all__ = [ + "PositiveId", + "NonEmptyStr", + "NonNegativeInt", + "MinuteOfDay", + "SchedulingBaseModel", + "SchedulingDataset", + "PlanningPeriod", + "Plan", + "PlanId", + "PlanParticipant", + "PlanningUnit", + "PlanningUnitId", + "PlanningUnitKind", + "PlanningUnitMembership", + "EmployeeId", + "Employee", + "StaffLevel", + "Capability", "Assignment", "AssignmentType", "Availability", "AvailabilityType", - "Demand", - "DemandType", - "Employee", - "Membership", - "MembershipType", - "PlanningPeriod", - "Preference", - "PreferenceType", - "Rule", - "RuleType", - "SchedulingDataset", "Shift", + "ShiftId", "ShiftKind", - "Station", + "StaffingDemandRole", + "DemandRequirement", + "EmployeeSundayWorkHistory", ] diff --git a/src/scheduling/models/assignment.py b/src/scheduling/models/assignment.py new file mode 100644 index 00000000..2e73f2bd --- /dev/null +++ b/src/scheduling/models/assignment.py @@ -0,0 +1,48 @@ +from datetime import date as Date +from enum import StrEnum +from typing import Self + +from pydantic import model_validator + +from src.scheduling.models.core import SchedulingBaseModel +from src.scheduling.models.employee import EmployeeId +from src.scheduling.models.planning_unit import PlanningUnitId +from src.scheduling.models.shift import ShiftId + + +class AssignmentType(StrEnum): + """Type of existing TimeOffice assignment in the imported dataset.""" + + PLANNED = "planned" + EXTERNAL = "external" + + +class Assignment(SchedulingBaseModel): + """Imported existing TimeOffice work assignment. + + PLANNED assignments belong to one of the selected planning units. + + EXTERNAL assignments are work assignments of selected employees outside the + selected scheduling context. They block the employee via the referenced + shift, but they do not satisfy selected planning-unit demand. + """ + + employee_id: EmployeeId + date: Date + shift_id: ShiftId + + assignment_type: AssignmentType + + # Required for PLANNED assignments. External work only blocks the employee, + # so its original TimeOffice planning unit is intentionally not exposed. + planning_unit_id: PlanningUnitId | None = None + + @model_validator(mode="after") + def validate_assignment(self) -> Self: + if self.assignment_type == AssignmentType.PLANNED and self.planning_unit_id is None: + raise ValueError("PLANNED assignments must reference planning_unit_id.") + + if self.assignment_type == AssignmentType.EXTERNAL and self.planning_unit_id is not None: + raise ValueError("EXTERNAL assignments must not reference planning_unit_id.") + + return self diff --git a/src/scheduling/models/availability.py b/src/scheduling/models/availability.py new file mode 100644 index 00000000..190b7108 --- /dev/null +++ b/src/scheduling/models/availability.py @@ -0,0 +1,45 @@ +from datetime import date as Date +from enum import StrEnum +from typing import Self + +from pydantic import model_validator + +from src.scheduling.models.core import SchedulingBaseModel +from src.scheduling.models.employee import EmployeeId +from src.scheduling.models.shift import ShiftId + + +class AvailabilityType(StrEnum): + """Hard employee availability restriction.""" + + UNAVAILABLE = "unavailable" + VACATION = "vacation" + TRAINING = "training" + FREE_DAY = "free_day" + AVAILABLE_ONLY = "available_only" + + +class Availability(SchedulingBaseModel): + """Hard employee availability restriction for a date. + + Wishes/preferences must not be represented here. They should become a + separate soft-preference model later. + """ + + employee_id: EmployeeId + date: Date + availability_type: AvailabilityType + + # Only used for AVAILABLE_ONLY. For absences/blockers this stays None. + shift_ids: tuple[ShiftId, ...] | None = None + + @model_validator(mode="after") + def validate_availability(self) -> Self: + if self.availability_type == AvailabilityType.AVAILABLE_ONLY: + if not self.shift_ids: + raise ValueError("AVAILABLE_ONLY availability must define shift_ids.") + + elif self.shift_ids is not None: + raise ValueError(f"{self.availability_type} availability must not define shift_ids.") + + return self diff --git a/src/scheduling/models/core.py b/src/scheduling/models/core.py index 78679272..a3e1a6cd 100644 --- a/src/scheduling/models/core.py +++ b/src/scheduling/models/core.py @@ -1,22 +1,19 @@ -from datetime import date -from typing import Self +from typing import Annotated -from pydantic import BaseModel, model_validator +from pydantic import BaseModel, ConfigDict, Field, StringConstraints -class PlanningPeriod(BaseModel): - """Inclusive planning period for a scheduling run.""" +class SchedulingBaseModel(BaseModel): + """Base model for canonical scheduling data.""" - start: date - end: date + model_config = ConfigDict( + frozen=True, + extra="forbid", + str_strip_whitespace=True, + ) - @model_validator(mode="after") - def end_must_not_be_before_start(self) -> Self: - if self.end < self.start: - raise ValueError("Planning period end date must not be before start date.") - return self - @property - def month_folder(self) -> str: - """Return the cache month folder for this period.""" - return f"{self.start.month:02d}_{self.start.year}" +NonEmptyStr = Annotated[str, StringConstraints(strip_whitespace=True, min_length=1)] +PositiveId = Annotated[int, Field(gt=0)] +NonNegativeInt = Annotated[int, Field(ge=0)] +MinuteOfDay = Annotated[int, Field(ge=0, lt=24 * 60)] diff --git a/src/scheduling/models/dataset.py b/src/scheduling/models/dataset.py index f775faf3..a3795f7a 100644 --- a/src/scheduling/models/dataset.py +++ b/src/scheduling/models/dataset.py @@ -1,31 +1,378 @@ -from pydantic import BaseModel +from collections.abc import Sequence +from datetime import date as Date +from typing import Self -from src.scheduling.models.core import PlanningPeriod -from src.scheduling.models.demand import Demand -from src.scheduling.models.employee import Employee -from src.scheduling.models.relations import Assignment, Availability, Membership, Preference, Rule -from src.scheduling.models.shift import Shift -from src.scheduling.models.station import Station +from pydantic import model_validator +from src.scheduling.models.assignment import Assignment, AssignmentType +from src.scheduling.models.availability import Availability +from src.scheduling.models.core import SchedulingBaseModel +from src.scheduling.models.demand import DemandRequirement +from src.scheduling.models.employee import Employee, EmployeeId, StaffLevel +from src.scheduling.models.plan import Plan, PlanId, PlanParticipant +from src.scheduling.models.planning_unit import PlanningUnit, PlanningUnitId, PlanningUnitKind, PlanningUnitMembership +from src.scheduling.models.shift import Shift, ShiftId, StaffingDemandRole +from src.scheduling.models.sunday_work_history import EmployeeSundayWorkHistory -class SchedulingDataset(BaseModel): - """Combined scheduling data for one solver run. - This is the main application-facing data model. It represents the scheduling - problem for one period and a selected set of stations. +class PlanningPeriod(SchedulingBaseModel): + """Inclusive planning period for one scheduling dataset.""" + + start: Date + end: Date + + @model_validator(mode="after") + def validate_period(self) -> Self: + if self.start > self.end: + raise ValueError(f"PlanningPeriod.start must be before or equal to end: {self.start} > {self.end}") + + return self + + def contains(self, date: Date) -> bool: + return self.start <= date <= self.end + + +class SchedulingDataset(SchedulingBaseModel): + """Clean scheduling dataset aligned with TimeOffice planning concepts. + + This is not solver input yet. Repositories and small transformation functions + build this reduced model from TimeOffice. Solver-specific indexes and + OR-Tools variables are derived later. """ period: PlanningPeriod - stations: tuple[Station, ...] - regular_station_ids: tuple[int, ...] - jump_pool_station_ids: tuple[int, ...] = () + planning_units: tuple[PlanningUnit, ...] + plans: tuple[Plan, ...] employees: tuple[Employee, ...] = () + plan_participants: tuple[PlanParticipant, ...] = () + planning_unit_memberships: tuple[PlanningUnitMembership, ...] = () + shifts: tuple[Shift, ...] = () - demand: tuple[Demand, ...] = () - memberships: tuple[Membership, ...] = () assignments: tuple[Assignment, ...] = () availability: tuple[Availability, ...] = () - rules: tuple[Rule, ...] = () - preferences: tuple[Preference, ...] = () + + demand_requirements: tuple[DemandRequirement, ...] = () + + sunday_work_history: tuple[EmployeeSundayWorkHistory, ...] = () + + @model_validator(mode="after") + def validate_dataset(self) -> Self: + employee_ids = self._unique_employee_ids() + planning_unit_ids = self._unique_planning_unit_ids() + plan_ids = self._unique_plan_ids() + shift_ids = self._unique_shift_ids() + + self._validate_plans( + planning_unit_ids=planning_unit_ids, + ) + self._validate_plan_participants( + employee_ids=employee_ids, + plan_ids=plan_ids, + planning_unit_ids=planning_unit_ids, + ) + self._validate_planning_unit_memberships( + employee_ids=employee_ids, + planning_unit_ids=planning_unit_ids, + ) + self._validate_assignments( + employee_ids=employee_ids, + planning_unit_ids=planning_unit_ids, + shift_ids=shift_ids, + ) + self._validate_availability( + employee_ids=employee_ids, + shift_ids=shift_ids, + ) + self._validate_demand_requirements( + planning_unit_ids=planning_unit_ids, + shift_ids=shift_ids, + ) + self._validate_sunday_work_history( + employee_ids=employee_ids, + ) + + return self + + def _unique_employee_ids(self) -> set[EmployeeId]: + employee_ids = [employee.employee_id for employee in self.employees] + self._ensure_unique(employee_ids, "employee_id") + return set(employee_ids) + + def _unique_planning_unit_ids(self) -> set[PlanningUnitId]: + planning_unit_ids = [unit.planning_unit_id for unit in self.planning_units] + self._ensure_unique(planning_unit_ids, "planning_unit_id") + return set(planning_unit_ids) + + def _unique_plan_ids(self) -> set[PlanId]: + plan_ids = [plan.plan_id for plan in self.plans] + self._ensure_unique(plan_ids, "plan_id") + return set(plan_ids) + + def _unique_shift_ids(self) -> set[ShiftId]: + shift_ids = [shift.shift_id for shift in self.shifts] + self._ensure_unique(shift_ids, "shift_id") + return set(shift_ids) + + def _validate_plans( + self, + *, + planning_unit_ids: set[PlanningUnitId], + ) -> None: + seen_planning_units: set[PlanningUnitId] = set() + + for plan in self.plans: + if plan.planning_unit_id not in planning_unit_ids: + raise ValueError(f"Plan references unknown planning_unit_id={plan.planning_unit_id}.") + + if plan.planning_unit_id in seen_planning_units: + raise ValueError( + f"Multiple selected plans reference the same planning_unit_id={plan.planning_unit_id}." + ) + + seen_planning_units.add(plan.planning_unit_id) + + def _validate_plan_participants( + self, + *, + employee_ids: set[EmployeeId], + plan_ids: set[PlanId], + planning_unit_ids: set[PlanningUnitId], + ) -> None: + plans_by_id = {plan.plan_id: plan for plan in self.plans} + seen: set[tuple[PlanId, EmployeeId]] = set() + + for participant in self.plan_participants: + if participant.plan_id not in plan_ids: + raise ValueError(f"PlanParticipant references unknown plan_id={participant.plan_id}.") + + if participant.planning_unit_id not in planning_unit_ids: + raise ValueError(f"PlanParticipant references unknown planning_unit_id={participant.planning_unit_id}.") + + if participant.employee_id not in employee_ids: + raise ValueError(f"PlanParticipant references unknown employee_id={participant.employee_id}.") + + plan = plans_by_id[participant.plan_id] + if participant.planning_unit_id != plan.planning_unit_id: + raise ValueError( + "PlanParticipant planning_unit_id does not match its Plan: " + f"plan_id={participant.plan_id} " + f"participant_planning_unit_id={participant.planning_unit_id} " + f"plan_planning_unit_id={plan.planning_unit_id}." + ) + + key = (participant.plan_id, participant.employee_id) + if key in seen: + raise ValueError( + f"Duplicate PlanParticipant plan_id={participant.plan_id} employee_id={participant.employee_id}." + ) + + seen.add(key) + + def _validate_planning_unit_memberships( + self, + *, + employee_ids: set[EmployeeId], + planning_unit_ids: set[PlanningUnitId], + ) -> None: + seen: set[tuple[PlanningUnitId, EmployeeId, Date, Date | None]] = set() + + for membership in self.planning_unit_memberships: + if membership.planning_unit_id not in planning_unit_ids: + raise ValueError( + f"PlanningUnitMembership references unknown planning_unit_id={membership.planning_unit_id}." + ) + + if membership.employee_id not in employee_ids: + raise ValueError(f"PlanningUnitMembership references unknown employee_id={membership.employee_id}.") + + key = ( + membership.planning_unit_id, + membership.employee_id, + membership.valid_from, + membership.valid_until, + ) + if key in seen: + raise ValueError( + "Duplicate PlanningUnitMembership " + f"planning_unit_id={membership.planning_unit_id} " + f"employee_id={membership.employee_id} " + f"valid_from={membership.valid_from} " + f"valid_until={membership.valid_until}." + ) + + seen.add(key) + + def _validate_assignments( + self, + *, + employee_ids: set[EmployeeId], + planning_unit_ids: set[PlanningUnitId], + shift_ids: set[ShiftId], + ) -> None: + seen: set[tuple[EmployeeId, Date, ShiftId, AssignmentType, PlanningUnitId | None]] = set() + + for assignment in self.assignments: + if not self.period.contains(assignment.date): + raise ValueError( + f"Assignment outside planning period: employee_id={assignment.employee_id} date={assignment.date}." + ) + + if assignment.employee_id not in employee_ids: + raise ValueError(f"Assignment references unknown employee_id={assignment.employee_id}.") + + if assignment.shift_id not in shift_ids: + raise ValueError(f"Assignment references unknown shift_id={assignment.shift_id}.") + + if assignment.assignment_type == AssignmentType.PLANNED: + if assignment.planning_unit_id is None: + raise ValueError("Planned assignment must reference planning_unit_id.") + + if assignment.planning_unit_id not in planning_unit_ids: + raise ValueError( + f"Planned assignment references unknown planning_unit_id={assignment.planning_unit_id}." + ) + + elif assignment.assignment_type == AssignmentType.EXTERNAL: + if assignment.planning_unit_id is not None: + raise ValueError("External assignment must not reference planning_unit_id.") + + key = ( + assignment.employee_id, + assignment.date, + assignment.shift_id, + assignment.assignment_type, + assignment.planning_unit_id, + ) + if key in seen: + raise ValueError( + f"Duplicate assignment employee_id={assignment.employee_id} " + f"date={assignment.date} shift_id={assignment.shift_id} " + f"type={assignment.assignment_type} " + f"planning_unit_id={assignment.planning_unit_id}." + ) + + seen.add(key) + + def _validate_availability( + self, + *, + employee_ids: set[EmployeeId], + shift_ids: set[ShiftId], + ) -> None: + seen: set[tuple[EmployeeId, Date, str, tuple[ShiftId, ...] | None]] = set() + + for availability in self.availability: + if not self.period.contains(availability.date): + raise ValueError( + "Availability outside planning period: " + f"employee_id={availability.employee_id} date={availability.date}." + ) + + if availability.employee_id not in employee_ids: + raise ValueError(f"Availability references unknown employee_id={availability.employee_id}.") + + if availability.shift_ids is not None: + unknown_shift_ids = sorted(set(availability.shift_ids) - shift_ids) + if unknown_shift_ids: + raise ValueError(f"Availability references unknown shift_ids={unknown_shift_ids}.") + + key = ( + availability.employee_id, + availability.date, + str(availability.availability_type), + availability.shift_ids, + ) + if key in seen: + raise ValueError( + f"Duplicate availability employee_id={availability.employee_id} " + f"date={availability.date} " + f"type={availability.availability_type} " + f"shift_ids={availability.shift_ids}." + ) + + seen.add(key) + + def _validate_demand_requirements( + self, + *, + planning_unit_ids: set[PlanningUnitId], + shift_ids: set[ShiftId], + ) -> None: + planning_unit_kind_by_id = {unit.planning_unit_id: unit.kind for unit in self.planning_units} + shift_by_id = {shift.shift_id: shift for shift in self.shifts} + + seen: set[tuple[PlanningUnitId, Date, ShiftId, StaffLevel]] = set() + + for demand in self.demand_requirements: + if not self.period.contains(demand.date): + raise ValueError( + "DemandRequirement outside planning period: " + f"planning_unit_id={demand.planning_unit_id} date={demand.date}." + ) + + if demand.planning_unit_id not in planning_unit_ids: + raise ValueError(f"DemandRequirement references unknown planning_unit_id={demand.planning_unit_id}.") + + if planning_unit_kind_by_id[demand.planning_unit_id] != PlanningUnitKind.STATION: + raise ValueError( + "DemandRequirement must target a station planning unit: " + f"planning_unit_id={demand.planning_unit_id}." + ) + + if demand.shift_id not in shift_ids: + raise ValueError(f"DemandRequirement references unknown shift_id={demand.shift_id}.") + + shift = shift_by_id[demand.shift_id] + if shift.staffing_role != StaffingDemandRole.REQUIRED_MINIMUM: + raise ValueError( + f"DemandRequirement must reference a REQUIRED_MINIMUM shift: shift_id={demand.shift_id}." + ) + + key = ( + demand.planning_unit_id, + demand.date, + demand.shift_id, + demand.staff_level, + ) + if key in seen: + raise ValueError( + "Duplicate DemandRequirement " + f"planning_unit_id={demand.planning_unit_id} " + f"date={demand.date} " + f"shift_id={demand.shift_id} " + f"staff_level={demand.staff_level}." + ) + + seen.add(key) + + def _validate_sunday_work_history( + self, + *, + employee_ids: set[EmployeeId], + ) -> None: + seen: set[EmployeeId] = set() + + for history in self.sunday_work_history: + if history.employee_id not in employee_ids: + raise ValueError(f"EmployeeSundayWorkHistory references unknown employee_id={history.employee_id}.") + + if history.employee_id in seen: + raise ValueError(f"Duplicate EmployeeSundayWorkHistory employee_id={history.employee_id}.") + + seen.add(history.employee_id) + + def _ensure_unique(self, values: Sequence[object], field_name: str) -> None: + seen: set[object] = set() + duplicates: set[object] = set() + + for value in values: + if value in seen: + duplicates.add(value) + + seen.add(value) + + if duplicates: + duplicate_values = ", ".join(sorted(str(value) for value in duplicates)) + raise ValueError(f"Duplicate {field_name} values: {duplicate_values}.") diff --git a/src/scheduling/models/demand.py b/src/scheduling/models/demand.py index cce7b58f..a6dd92d2 100644 --- a/src/scheduling/models/demand.py +++ b/src/scheduling/models/demand.py @@ -1,26 +1,18 @@ -from datetime import date -from enum import StrEnum +from datetime import date as Date -from pydantic import BaseModel, Field +from pydantic import Field +from src.scheduling.models.core import SchedulingBaseModel +from src.scheduling.models.employee import StaffLevel +from src.scheduling.models.planning_unit import PlanningUnitId +from src.scheduling.models.shift import ShiftId -class DemandType(StrEnum): - MINIMUM = "minimum" - OPTIONAL = "optional" +class DemandRequirement(SchedulingBaseModel): + """Hard minimum staffing demand for one planning unit, date, shift and staff level.""" -class Demand(BaseModel): - """Staffing need or optional coverage goal for a station/date/shift.""" - - station_id: int = Field(gt=0) - date: date - shift_id: str - - required_count: int = Field(ge=0) - - required_group_id: str | None = None - required_qualification_id: str | None = None - - demand_type: DemandType = DemandType.MINIMUM - priority: int = Field(default=0, ge=0) - weight: int = Field(default=1, ge=0) + planning_unit_id: PlanningUnitId + date: Date + shift_id: ShiftId + staff_level: StaffLevel + required_count: int = Field(gt=0) diff --git a/src/scheduling/models/employee.py b/src/scheduling/models/employee.py index fde42a9c..7db36c9c 100644 --- a/src/scheduling/models/employee.py +++ b/src/scheduling/models/employee.py @@ -1,19 +1,44 @@ -from pydantic import BaseModel, Field +from enum import StrEnum +from src.scheduling.models.core import NonEmptyStr, PositiveId, SchedulingBaseModel -class Employee(BaseModel): - """Employee identity and stable staffing classification. +EmployeeId = PositiveId - Period-specific facts like availability, assignments, wishes, and station - memberships are represented as separate relationship records. + +class StaffLevel(StrEnum): + """Reduced staffing level used by demand and solver logic. + + This is mapped from TimeOffice source data such as Berufe/Qualis. + The solver should depend on this enum, not on raw TimeOffice IDs. """ - employee_id: int = Field(gt=0) - personnel_number: str | None = None + PROFESSIONAL = "professional" # Fachkraft + ASSISTANT = "assistant" # Hilfskraft + TRAINEE = "trainee" # Azubi + + +class Capability(StrEnum): + """Special employee capability used by solver rules. + + Capabilities should come from validated TimeOffice data where possible. + If a project requirement is not represented in TimeOffice, it should be + added later through an explicit scenario input, not hidden in TimeOffice facts. + """ + + NIGHT_WATCH = "night_watch" + ROUNDS = "rounds" + + +class Employee(SchedulingBaseModel): + """Employee known to the scheduling dataset. + + The domain model intentionally does not expose raw TimeOffice profession or + qualification IDs. The TimeOffice adapter maps those source details into the + reduced solver-facing fields below. + """ - first_name: str | None = None - last_name: str | None = None - display_name: str + employee_id: EmployeeId + display_name: NonEmptyStr - group_id: str | None = None - active: bool = True + staff_level: StaffLevel + capabilities: tuple[Capability, ...] = () diff --git a/src/scheduling/models/plan.py b/src/scheduling/models/plan.py new file mode 100644 index 00000000..0fffc447 --- /dev/null +++ b/src/scheduling/models/plan.py @@ -0,0 +1,28 @@ +from src.scheduling.models.core import PositiveId, SchedulingBaseModel +from src.scheduling.models.employee import EmployeeId +from src.scheduling.models.planning_unit import PlanningUnitId + +PlanId = PositiveId + + +class Plan(SchedulingBaseModel): + """Concrete selected TimeOffice plan for one PlanningUnit. + + Plan is kept as write-back context. It should stay minimal: the repository + already selected the correct interval/status using TimeOfficeFacts. + """ + + plan_id: PlanId + planning_unit_id: PlanningUnitId + + +class PlanParticipant(SchedulingBaseModel): + """Employee included in a concrete selected Plan. + + This comes from TimeOffice `TPlanPersonal` and defines the concrete employee + set for the imported planning context. + """ + + plan_id: PlanId + planning_unit_id: PlanningUnitId + employee_id: EmployeeId diff --git a/src/scheduling/models/planning_unit.py b/src/scheduling/models/planning_unit.py new file mode 100644 index 00000000..f2f5d38f --- /dev/null +++ b/src/scheduling/models/planning_unit.py @@ -0,0 +1,68 @@ +from datetime import date as Date +from enum import StrEnum +from typing import Self + +from pydantic import model_validator + +from src.scheduling.models.core import NonEmptyStr, PositiveId, SchedulingBaseModel +from src.scheduling.models.employee import EmployeeId, StaffLevel + +PlanningUnitId = PositiveId + + +class PlanningUnitKind(StrEnum): + """Kind of planning unit used by the scheduling pipeline. + + STATION: + Planning unit with staffing demand. The solver may assign employees + into this unit. + + SHARED_POOL: + Planning unit used as a possible cross-unit employee source. Marking a + unit as SHARED_POOL never creates eligibility by itself. Eligibility + still requires real membership rows. + """ + + STATION = "station" + SHARED_POOL = "shared_pool" + + +class PlanningUnit(SchedulingBaseModel): + """Stable organizational scheduling unit. + + This mirrors the TimeOffice concept "Planungseinheit". A planning unit can + represent a station/ward or, if explicitly configured and backed by data, a + shared/jump pool. + """ + + planning_unit_id: PlanningUnitId + display_name: NonEmptyStr + kind: PlanningUnitKind + + +class PlanningUnitMembership(SchedulingBaseModel): + """Active employee membership interval in a PlanningUnit. + + This comes from TimeOffice `TPlanungseinheitenPersonal`. + + Multiple intervals for the same employee and planning unit are valid because + eligibility can change inside the planning period. + """ + + planning_unit_id: PlanningUnitId + employee_id: EmployeeId + + valid_from: Date + valid_until: Date | None = None + + staff_level: StaffLevel + + is_home: bool + is_replacement: bool + + @model_validator(mode="after") + def validate_membership(self) -> Self: + if self.valid_until is not None and self.valid_from > self.valid_until: + raise ValueError("PlanningUnitMembership.valid_from must be before or equal to valid_until.") + + return self diff --git a/src/scheduling/models/relations.py b/src/scheduling/models/relations.py deleted file mode 100644 index d61f721e..00000000 --- a/src/scheduling/models/relations.py +++ /dev/null @@ -1,134 +0,0 @@ -from datetime import date as Date -from enum import StrEnum - -from pydantic import BaseModel, Field - - -class MembershipType(StrEnum): - LOCAL = "local" - JUMP_POOL = "jump_pool" - EXTERNAL = "external" - UNKNOWN = "unknown" - - -class AssignmentType(StrEnum): - PLANNED = "planned" - FIXED = "fixed" - EXTERNAL = "external" - MANAGEMENT = "management" - - -class AvailabilityType(StrEnum): - UNAVAILABLE = "unavailable" - VACATION = "vacation" - TRAINING = "training" - FREE_WEEKEND = "free_weekend" - AVAILABLE_ONLY = "available_only" - - -class RuleType(StrEnum): - MEDICAL_NIGHT_BAN = "medical_night_ban" - NIGHT_WATCH_ONLY = "night_watch_only" - WEEKDAY_EARLY_ONLY = "weekday_early_only" - FIXED_WEEKDAY_FREE = "fixed_weekday_free" - DOES_NOT_COUNT_FOR_MINIMUM_STAFFING = "does_not_count_for_minimum_staffing" - NO_NIGHT_BEFORE_PROTECTED_FREE_TIME = "no_night_before_protected_free_time" - MAX_CONSECUTIVE_DAYS = "max_consecutive_days" - MIN_REST_TIME = "min_rest_time" - OTHER = "other" - - -class PreferenceType(StrEnum): - DAY_OFF = "day_off" - SHIFT_OFF = "shift_off" - SHIFT_ON = "shift_on" - WORK_ANY = "work_any" - AVOID_SHIFT = "avoid_shift" - PREFER_SHIFT = "prefer_shift" - - -class Membership(BaseModel): - """Employee membership in a station-local or jump-pool staffing pool.""" - - employee_id: int = Field(gt=0) - station_id: int = Field(gt=0) - - membership_type: MembershipType = MembershipType.LOCAL - - valid_from: Date | None = None - valid_until: Date | None = None - - is_home_station: bool | None = None - is_substitute: bool | None = None - - -class Assignment(BaseModel): - """Known, planned, fixed, or externally blocking assignment.""" - - employee_id: int = Field(gt=0) - date: Date - shift_id: str - station_id: int | None = None - - assignment_type: AssignmentType = AssignmentType.PLANNED - - counts_as_work: bool = True - counts_for_minimum_staffing: bool | None = None - - source: str | None = None - source_assignment_id: str | None = None - source_shift_id: int | None = None - source_code: str | None = None - - -class Availability(BaseModel): - """Employee availability or unavailability information.""" - - employee_id: int = Field(gt=0) - date: Date - - availability_type: AvailabilityType - - shift_ids: tuple[str, ...] | None = None - is_hard: bool = True - - source: str | None = None - source_code: str | None = None - source_id: str | None = None - - -class Rule(BaseModel): - """Generic scheduling rule or special-case restriction. - - Keep this typed by rule_type. If one rule type becomes complex, extract a - dedicated model later. - """ - - rule_id: str - rule_type: RuleType - - employee_id: int | None = None - station_id: int | None = None - - date: Date | None = None - weekdays: tuple[int, ...] | None = None - shift_ids: tuple[str, ...] | None = None - qualification_id: str | None = None - - is_hard: bool = True - description: str | None = None - - -class Preference(BaseModel): - """Soft employee wish or recurring preference.""" - - employee_id: int = Field(gt=0) - - preference_type: PreferenceType - - date: Date | None = None - weekdays: tuple[int, ...] | None = None - shift_ids: tuple[str, ...] | None = None - - weight: int = Field(default=1, ge=0) - source: str | None = None diff --git a/src/scheduling/models/shift.py b/src/scheduling/models/shift.py index a6e1f26c..16a6684c 100644 --- a/src/scheduling/models/shift.py +++ b/src/scheduling/models/shift.py @@ -1,44 +1,63 @@ from enum import StrEnum +from typing import Self -from pydantic import BaseModel, Field +from pydantic import Field, model_validator + +from src.scheduling.models.core import ( + MinuteOfDay, + NonEmptyStr, + PositiveId, + SchedulingBaseModel, +) + +ShiftId = PositiveId class ShiftKind(StrEnum): - """Canonical solver-facing shift category.""" + """Reduced shift kind used by scheduling rules. + + This is not a full TimeOffice shift taxonomy. It only contains categories + relevant for demand, rest rules, night rules, and project-specific work. + """ EARLY = "early" - INTERMEDIATE = "intermediate" LATE = "late" NIGHT = "night" + INTERMEDIATE = "intermediate" MANAGEMENT = "management" OTHER = "other" -class Shift(BaseModel): - """Canonical assignable shift definition. +class StaffingDemandRole(StrEnum): + """How a shift relates to staffing demand.""" + + REQUIRED_MINIMUM = "required_minimum" + OPTIONAL_COVERAGE = "optional_coverage" + NON_MINIMUM_WORK = "non_minimum_work" - One Shift represents one assignable shift variant. Similar shifts can be - grouped through shift_group_id, e.g. several TimeOffice night shifts can all - belong to group "night" while keeping their own shift_id/source metadata. - """ - shift_id: str - shift_group_id: str | None = None - name: str +class Shift(SchedulingBaseModel): + """Scheduling-relevant view of a TimeOffice shift. - source_shift_id: int | None = None - source_code: str | None = None + The TimeOffice adapter maps raw shift IDs/types/codes into this reduced + model. The solver should use this model, not raw TimeOffice catalog fields. + """ + + shift_id: ShiftId + code: NonEmptyStr kind: ShiftKind + staffing_role: StaffingDemandRole + + start_minute: MinuteOfDay + end_minute: MinuteOfDay - start_minute: int = Field(ge=0, lt=24 * 60) - end_minute: int = Field(ge=0, lt=24 * 60) - ends_next_day: bool = False + # Net paid/planned work time used for monthly target-hour balancing. + net_work_minutes: int = Field(gt=0) - break_minutes: int = Field(default=0, ge=0) - net_work_minutes: int = Field(ge=0) + @model_validator(mode="after") + def validate_shift(self) -> Self: + if self.start_minute == self.end_minute: + raise ValueError("Shift start_minute and end_minute must differ.") - assignable: bool = True - counts_as_work: bool = True - counts_for_minimum_staffing: bool = True - is_night: bool = False + return self diff --git a/src/scheduling/models/station.py b/src/scheduling/models/station.py deleted file mode 100644 index 0c306908..00000000 --- a/src/scheduling/models/station.py +++ /dev/null @@ -1,9 +0,0 @@ -from pydantic import BaseModel, Field - - -class Station(BaseModel): - """Hospital station or source planning unit.""" - - station_id: int = Field(gt=0) - name: str | None = None - source_planning_unit_id: int | None = None diff --git a/src/scheduling/models/sunday_work_history.py b/src/scheduling/models/sunday_work_history.py new file mode 100644 index 00000000..5de7b8d9 --- /dev/null +++ b/src/scheduling/models/sunday_work_history.py @@ -0,0 +1,11 @@ +from pydantic import Field + +from src.scheduling.models.core import SchedulingBaseModel +from src.scheduling.models.employee import EmployeeId + + +class EmployeeSundayWorkHistory(SchedulingBaseModel): + """Historical Sunday workload for one employee in the configured lookback window.""" + + employee_id: EmployeeId + worked_sundays: int = Field(ge=0) diff --git a/src/scheduling/settings.py b/src/scheduling/settings.py new file mode 100644 index 00000000..663bb99f --- /dev/null +++ b/src/scheduling/settings.py @@ -0,0 +1,26 @@ +from functools import lru_cache + +from pydantic import Field, SecretStr +from pydantic_settings import BaseSettings, SettingsConfigDict + + +class Settings(BaseSettings): + model_config = SettingsConfigDict( + env_file=".env", + extra="ignore", + ) + + log_level: str = Field(default="INFO") + + # TimeOffice Database + db_driver: str = "ODBC Driver 18 for SQL Server" + db_server: str + db_name: str + db_user: str + db_password: SecretStr + + +@lru_cache(maxsize=1) +def get_settings() -> Settings: + """Load application settings from environment variables and .env files.""" + return Settings() # type: ignore[call-arg] diff --git a/src/scheduling/timeoffice/config.py b/src/scheduling/timeoffice/config.py deleted file mode 100644 index 621a8818..00000000 --- a/src/scheduling/timeoffice/config.py +++ /dev/null @@ -1,229 +0,0 @@ -from enum import IntEnum, StrEnum - -from pydantic import BaseModel, Field - -from src.scheduling.models.shift import ShiftKind - - -class TimeOfficePlanStatus(IntEnum): - """Known TimeOffice plan status ids.""" - - TARGET_PLANNING = 20 - ACTUAL = 50 - COMPLETED = 70 - SETTLED = 80 - - -class TimeOfficePlanningInterval(IntEnum): - """Known TimeOffice planning interval ids.""" - - MONTHLY = 1 - ANNUAL = 3 - - -class TimeOfficeShiftType(IntEnum): - """Known TimeOffice shift type ids.""" - - WORK = 1 - - -class StationType(StrEnum): - """Configured station role for the TimeOffice import.""" - - REGULAR = "regular" - JUMP_POOL = "jump_pool" - - -class TimeOfficePlanSelection(BaseModel): - """Which TimeOffice plans are used as source for scheduling.""" - - planning_interval_id: TimeOfficePlanningInterval - plan_status_id: TimeOfficePlanStatus - - -class TimeOfficeStationConfig(BaseModel): - """Configured TimeOffice station relevant for the project.""" - - station_id: int = Field(gt=0) - label: str - station_type: StationType = StationType.REGULAR - area_hint: str | None = None - notes: str | None = None - - -class TimeOfficeShiftConfig(BaseModel): - """Configured TimeOffice shift relevant for the solver.""" - - source_shift_id: int = Field(gt=0) - expected_code: str - - kind: ShiftKind - group_id: str - - assignable: bool = True - counts_as_work: bool = True - counts_for_minimum_staffing: bool = True - - description: str | None = None - - -class TimeOfficeConfig(BaseModel): - """Source of truth for TimeOffice IDs and project-specific import semantics. - - Keep external TimeOffice IDs and project/domain decisions here instead of - scattering them through repositories, SQL queries, or solver code. - """ - - plan_selection: TimeOfficePlanSelection - stations: tuple[TimeOfficeStationConfig, ...] - solver_shifts: tuple[TimeOfficeShiftConfig, ...] - assignable_shift_type_ids: tuple[TimeOfficeShiftType, ...] - - @property - def station_ids(self) -> tuple[int, ...]: - """Return all configured TimeOffice station ids.""" - return tuple(station.station_id for station in self.stations) - - @property - def solver_shift_ids(self) -> tuple[int, ...]: - """Return all configured TimeOffice shift ids used by the solver.""" - return tuple(shift.source_shift_id for shift in self.solver_shifts) - - @property - def stations_by_id(self) -> dict[int, TimeOfficeStationConfig]: - """Return configured stations keyed by TimeOffice station id.""" - return {station.station_id: station for station in self.stations} - - @property - def shifts_by_id(self) -> dict[int, TimeOfficeShiftConfig]: - """Return configured solver shifts keyed by TimeOffice shift id.""" - return {shift.source_shift_id: shift for shift in self.solver_shifts} - - def regular_station_ids_for(self, station_ids: tuple[int, ...]) -> tuple[int, ...]: - """Return requested station ids that are configured as regular stations.""" - return tuple( - station_id for station_id in station_ids if self.station_type_for(station_id) == StationType.REGULAR - ) - - def jump_pool_station_ids_for(self, station_ids: tuple[int, ...]) -> tuple[int, ...]: - """Return requested station ids that are configured as jump-pool stations.""" - return tuple( - station_id for station_id in station_ids if self.station_type_for(station_id) == StationType.JUMP_POOL - ) - - def station_type_for(self, station_id: int) -> StationType: - """Return configured station type. - - Unknown stations default to regular to keep exploratory database reads - possible while we are still inspecting TimeOffice data. - """ - station = self.stations_by_id.get(station_id) - - if station is None: - return StationType.REGULAR - - return station.station_type - - -STATION_77 = 77 -STATION_79_LEGACY = 79 -STATION_337 = 337 -STATION_85 = 85 -STATION_239 = 239 -STATION_78 = 78 -SPRINGERPOOL_408 = 408 - -SHIFT_Z60 = 1406 -SHIFT_T75 = 2906 -SHIFT_F2 = 2939 -SHIFT_S2 = 2947 -SHIFT_N2 = 2953 - -TIMEOFFICE_CONFIG = TimeOfficeConfig( - plan_selection=TimeOfficePlanSelection( - planning_interval_id=TimeOfficePlanningInterval.MONTHLY, - plan_status_id=TimeOfficePlanStatus.TARGET_PLANNING, - ), - assignable_shift_type_ids=(TimeOfficeShiftType.WORK,), - stations=( - TimeOfficeStationConfig( - station_id=STATION_77, - label="Station 77", - area_hint="Bereich 5 / Bereich 32", - notes="Previously used station.", - ), - TimeOfficeStationConfig( - station_id=STATION_79_LEGACY, - label="Station 79", - notes="Legacy/development station used during refactoring; not listed as long-term station.", - ), - TimeOfficeStationConfig( - station_id=STATION_337, - label="Station 337", - area_hint="Bereich 5 / Bereich 32", - ), - TimeOfficeStationConfig( - station_id=STATION_85, - label="Station 85", - area_hint="Bereich 5 / Bereich 32", - ), - TimeOfficeStationConfig( - station_id=STATION_239, - label="Station 239", - area_hint="Bereich 17", - ), - TimeOfficeStationConfig( - station_id=STATION_78, - label="Station 78", - area_hint="Bereich 5 / Bereich 32", - ), - TimeOfficeStationConfig( - station_id=SPRINGERPOOL_408, - label="Springerpool", - station_type=StationType.JUMP_POOL, - area_hint="Bereich 546", - ), - ), - solver_shifts=( - TimeOfficeShiftConfig( - source_shift_id=SHIFT_F2, - expected_code="F2_", - kind=ShiftKind.EARLY, - group_id="early", - counts_for_minimum_staffing=True, - description="Required early shift F2.", - ), - TimeOfficeShiftConfig( - source_shift_id=SHIFT_S2, - expected_code="S2_", - kind=ShiftKind.LATE, - group_id="late", - counts_for_minimum_staffing=True, - description="Required late shift S2.", - ), - TimeOfficeShiftConfig( - source_shift_id=SHIFT_N2, - expected_code="N2_", - kind=ShiftKind.NIGHT, - group_id="night", - counts_for_minimum_staffing=True, - description="Required night shift N2.", - ), - TimeOfficeShiftConfig( - source_shift_id=SHIFT_T75, - expected_code="T75_", - kind=ShiftKind.INTERMEDIATE, - group_id="intermediate", - counts_for_minimum_staffing=True, - description="Optional/intermediate Zwischendienst T75.", - ), - TimeOfficeShiftConfig( - source_shift_id=SHIFT_Z60, - expected_code="Z60", - kind=ShiftKind.MANAGEMENT, - group_id="management", - counts_for_minimum_staffing=False, - description="Management shift Z60. Counts as work, not minimum staffing.", - ), - ), -) diff --git a/src/scheduling/timeoffice/database.py b/src/scheduling/timeoffice/database.py index a521074e..5702453c 100644 --- a/src/scheduling/timeoffice/database.py +++ b/src/scheduling/timeoffice/database.py @@ -1,76 +1,111 @@ -from sqlalchemy import URL, create_engine -from sqlalchemy.engine import Engine +import logging -from src.scheduling.models.dataset import SchedulingDataset -from src.scheduling.timeoffice.config import TIMEOFFICE_CONFIG, TimeOfficeConfig -from src.scheduling.timeoffice.models import FetchStationsRequest -from src.scheduling.timeoffice.repositories.employees import TimeOfficeEmployeeRepository -from src.scheduling.timeoffice.repositories.plans import TimeOfficePlanRepository -from src.scheduling.timeoffice.repositories.shifts import TimeOfficeShiftRepository -from src.scheduling.timeoffice.settings import TimeOfficeSettings +from sqlalchemy import URL, Engine, create_engine + +from src.scheduling.models import PlanningPeriod, SchedulingDataset +from src.scheduling.settings import Settings +from src.scheduling.timeoffice.facts import TimeOfficeFacts +from src.scheduling.timeoffice.repositories import TimeOfficeRepositories + +logger = logging.getLogger(__name__) + + +def create_db_engine(settings: Settings) -> Engine: + """Build the SQLAlchemy Engine for the TimeOffice SQL Server database.""" + url = URL.create( + drivername="mssql+pyodbc", + username=settings.db_user, + password=settings.db_password.get_secret_value(), + host=settings.db_server, + database=settings.db_name, + query={ + "driver": settings.db_driver, + "TrustServerCertificate": "yes", + }, + ) + + return create_engine(url) class TimeOfficeDatabase: - """Read TimeOffice data and build the canonical scheduling dataset. + """Loads reduced scheduling data from TimeOffice. - This class owns the database connection and explicit repository orchestration. - Repositories own SQL access, source-local validation, and mapping to canonical - scheduling models. + This class owns the database connection boundary and repository call order. + It does not contain solver logic or TimeOffice row mapping details. """ def __init__( self, - settings: TimeOfficeSettings, - config: TimeOfficeConfig = TIMEOFFICE_CONFIG, - ): - self._settings = settings - self._config = config - self._engine: Engine = create_engine(self._database_url()) - - self._plans = TimeOfficePlanRepository(config) - self._employees = TimeOfficeEmployeeRepository() - self._shifts = TimeOfficeShiftRepository(config) - - def _database_url(self) -> URL: - """Build the SQLAlchemy URL for the TimeOffice SQL Server database.""" - query: dict[str, str] = { - "driver": self._settings.db_driver, - "TrustServerCertificate": "yes", - } - - return URL.create( - drivername="mssql+pyodbc", - username=self._settings.db_user, - password=self._settings.db_password.get_secret_value(), - host=self._settings.db_server, - database=self._settings.db_name, - query=query, - ) + *, + engine: Engine, + repositories: TimeOfficeRepositories, + facts: TimeOfficeFacts, + ) -> None: + self._engine = engine + self._repositories = repositories + self._facts = facts - def read(self, request: FetchStationsRequest) -> SchedulingDataset: - """Read and map TimeOffice data into the canonical scheduling dataset.""" - print( - "[timeoffice] database.read " - f"stations={list(request.station_ids)} " - f"period={request.period.start.isoformat()}..{request.period.end.isoformat()}" - ) + def fetch_dataset( + self, + *, + selected_planning_unit_ids: tuple[int, ...], + period: PlanningPeriod, + ) -> SchedulingDataset: + if not selected_planning_unit_ids: + raise ValueError("At least one planning unit must be selected.") with self._engine.connect() as connection: - plan_result = self._plans.fetch(connection, request) - employee_result = self._employees.fetch(connection, plan_result.plans) - shift_result = self._shifts.fetch(connection) + planning_unit_result = self._repositories.planning_units.fetch( + connection=connection, + selected_planning_unit_ids=selected_planning_unit_ids, + period=period, + ) + + planning_unit_ids = tuple( + planning_unit.planning_unit_id for planning_unit in planning_unit_result.planning_units + ) + + personnel_result = self._repositories.personnel.fetch( + connection=connection, + plans=planning_unit_result.plans, + planning_unit_ids=planning_unit_ids, + period=period, + ) + + shift_result = self._repositories.shifts.fetch( + connection=connection, + ) + + roster_result = self._repositories.roster.fetch( + connection=connection, + plans=planning_unit_result.plans, + employees=personnel_result.employees, + period=period, + ) + + demand_result = self._repositories.demand.fetch( + connection=connection, + period=period, + planning_units=planning_unit_result.planning_units, + shifts=shift_result.shifts, + ) + + sunday_work_history_result = self._repositories.sunday_work_history.fetch( + connection=connection, + period=period, + employees=personnel_result.employees, + ) return SchedulingDataset( - period=request.period, - stations=plan_result.stations, - regular_station_ids=self._config.regular_station_ids_for(request.station_ids), - jump_pool_station_ids=self._config.jump_pool_station_ids_for(request.station_ids), - employees=employee_result.employees, + period=period, + planning_units=planning_unit_result.planning_units, + plans=planning_unit_result.plans, + employees=personnel_result.employees, + plan_participants=personnel_result.plan_participants, + planning_unit_memberships=personnel_result.planning_unit_memberships, shifts=shift_result.shifts, - demand=(), - memberships=employee_result.memberships, - assignments=(), - availability=(), - rules=(), - preferences=(), + assignments=roster_result.assignments, + availability=roster_result.availability, + demand_requirements=demand_result.demand_requirements, + sunday_work_history=sunday_work_history_result.sunday_work_history, ) diff --git a/src/scheduling/timeoffice/facts.py b/src/scheduling/timeoffice/facts.py new file mode 100644 index 00000000..ac6bdc15 --- /dev/null +++ b/src/scheduling/timeoffice/facts.py @@ -0,0 +1,393 @@ +from collections.abc import Mapping +from dataclasses import dataclass +from enum import IntEnum +from types import MappingProxyType + +from src.scheduling.models.availability import AvailabilityType +from src.scheduling.models.employee import Capability, StaffLevel +from src.scheduling.models.planning_unit import PlanningUnitId, PlanningUnitKind +from src.scheduling.models.shift import ShiftId, ShiftKind, StaffingDemandRole + + +class TimeOfficePlanStatusId(IntEnum): + """Known TimeOffice RefStati values used around plan selection. + + These are source-system IDs from TimeOffice. + + TARGET_PLANNING is the currently used plan status for reading the editable + target roster that we want to repair/optimize. + + The other values are kept because they were already known in the inherited + implementation and make the meaning of TARGET_PLANNING reviewable. They are + not used by the current read pipeline. + """ + + TARGET_PLANNING = 20 + ACTUAL = 50 + COMPLETED = 70 + SETTLED = 80 + + +# TimeOffice TDienste.Prim values used by the reduced scheduling model. +# Keep these source IDs local to TimeOffice facts. +EARLY_F2_SHIFT_ID = 2939 +LATE_S2_SHIFT_ID = 2947 +NIGHT_N2_SHIFT_ID = 2953 +INTERMEDIATE_T75_SHIFT_ID = 2906 +MANAGEMENT_Z60_SHIFT_ID = 1406 + +# Additional TimeOffice work shifts observed in roster rows. +NIGHT_N5_SHIFT_ID = 2889 +NIGHT_N15_SHIFT_ID = 1692 +OTHER_T8X_SHIFT_ID = 2994 +OTHER_Z52_SHIFT_ID = 3066 + + +@dataclass(frozen=True, slots=True) +class TimeOfficeShiftFact: + """Validated meaning of one known TimeOffice shift.""" + + source_shift_id: int + expected_code: str + kind: ShiftKind + staffing_role: StaffingDemandRole + + +@dataclass(frozen=True, slots=True) +class TimeOfficeAvailabilityFact: + source_shift_id: int + expected_code: str + availability_type: AvailabilityType + + +IsoWeekday = int # Monday=1 ... Sunday=7 + + +@dataclass(frozen=True, slots=True) +class TimeOfficeDemandFact: + """Fallback TimeOffice demand fact. + + This represents the same reduced information that should later come from + TimeOffice `TBenutzerBedarf*` tables. + + If planning_unit_ids is None, the demand applies to all selected station-like + planning units. + """ + + source_shift_id: ShiftId + staff_level: StaffLevel + required_by_iso_weekday: Mapping[IsoWeekday, int] + planning_unit_ids: tuple[PlanningUnitId, ...] | None = None + + +@dataclass(frozen=True, slots=True) +class TimeOfficeFacts: + """Flat source assumptions for the TimeOffice adapter. + + This object carries constants only. It must not contain behavior. + Repositories and validation code consume these facts for fetching, mapping, + and source-drift checks. + """ + + monthly_planning_interval_id: int + target_planning_status_id: int + + planning_unit_kind_map: dict[int, PlanningUnitKind] + + real_work_shift_type_ids: tuple[int, ...] + shift_facts: tuple[TimeOfficeShiftFact, ...] + + profession_staff_level_map: dict[int, StaffLevel] + # Temporary project/problem assumptions. Not DB-backed. + employee_capabilities_map: dict[int, tuple[Capability, ...]] + + availability_facts: tuple[TimeOfficeAvailabilityFact, ...] + + demand_facts: tuple[TimeOfficeDemandFact, ...] + + +TIMEOFFICE_FACTS = TimeOfficeFacts( + monthly_planning_interval_id=1, # Known TimeOffice RefPlanungsIntervalle value + target_planning_status_id=int(TimeOfficePlanStatusId.TARGET_PLANNING), + planning_unit_kind_map={ + 77: PlanningUnitKind.STATION, + 78: PlanningUnitKind.STATION, + 79: PlanningUnitKind.STATION, + 85: PlanningUnitKind.STATION, + 239: PlanningUnitKind.STATION, + 337: PlanningUnitKind.STATION, + 408: PlanningUnitKind.SHARED_POOL, + }, + real_work_shift_type_ids=(1,), + shift_facts=( + TimeOfficeShiftFact( + source_shift_id=EARLY_F2_SHIFT_ID, + expected_code="F2_", + kind=ShiftKind.EARLY, + staffing_role=StaffingDemandRole.REQUIRED_MINIMUM, + ), + TimeOfficeShiftFact( + source_shift_id=LATE_S2_SHIFT_ID, + expected_code="S2_", + kind=ShiftKind.LATE, + staffing_role=StaffingDemandRole.REQUIRED_MINIMUM, + ), + TimeOfficeShiftFact( + source_shift_id=NIGHT_N2_SHIFT_ID, + expected_code="N2_", + kind=ShiftKind.NIGHT, + staffing_role=StaffingDemandRole.REQUIRED_MINIMUM, + ), + TimeOfficeShiftFact( + source_shift_id=INTERMEDIATE_T75_SHIFT_ID, + expected_code="T75_", + kind=ShiftKind.INTERMEDIATE, + staffing_role=StaffingDemandRole.OPTIONAL_COVERAGE, + ), + TimeOfficeShiftFact( + source_shift_id=MANAGEMENT_Z60_SHIFT_ID, + expected_code="Z60", + kind=ShiftKind.MANAGEMENT, + staffing_role=StaffingDemandRole.NON_MINIMUM_WORK, + ), + TimeOfficeShiftFact( + source_shift_id=NIGHT_N5_SHIFT_ID, + expected_code="N5", + kind=ShiftKind.NIGHT, + staffing_role=StaffingDemandRole.NON_MINIMUM_WORK, + ), + TimeOfficeShiftFact( + source_shift_id=NIGHT_N15_SHIFT_ID, + expected_code="N15", + kind=ShiftKind.NIGHT, + staffing_role=StaffingDemandRole.NON_MINIMUM_WORK, + ), + TimeOfficeShiftFact( + source_shift_id=OTHER_T8X_SHIFT_ID, + expected_code="T8x", + kind=ShiftKind.OTHER, + staffing_role=StaffingDemandRole.NON_MINIMUM_WORK, + ), + TimeOfficeShiftFact( + source_shift_id=OTHER_Z52_SHIFT_ID, + expected_code="Z52", + kind=ShiftKind.OTHER, + staffing_role=StaffingDemandRole.NON_MINIMUM_WORK, + ), + ), + profession_staff_level_map={ + # Fachkraft + 803: StaffLevel.PROFESSIONAL, # Gesundheits- und Krankenpfleger/in + 110: StaffLevel.PROFESSIONAL, # Pflegefachkraft (Krankenpflege) + 129: StaffLevel.PROFESSIONAL, # Altenpfleger/in + 651: StaffLevel.PROFESSIONAL, # Pflegefachmann/-frau + 736: StaffLevel.PROFESSIONAL, # Krankenschwester/-pfleger + 987: StaffLevel.PROFESSIONAL, # Pflegefachkraft - Kinderkrankenpflege + # Hilfskraft + 90: StaffLevel.ASSISTANT, # Krankenpflegehelfer/in + 124: StaffLevel.ASSISTANT, # Medizinische/r Fachangestellte/r + 326: StaffLevel.ASSISTANT, # Helfer/in - stationäre Krankenpflege + 334: StaffLevel.ASSISTANT, # Pflegehelfer/in - stationäre Pflege + 793: StaffLevel.ASSISTANT, # Stationshilfe + 1245: StaffLevel.ASSISTANT, # Bundesfreiwilligendienst + # Azubi / Ausbildung + 835: StaffLevel.TRAINEE, # A-Pflegeassistent/in + 837: StaffLevel.TRAINEE, # A-Pflegefachkraft (Krankenpflege) + 1478: StaffLevel.TRAINEE, # A-Pflegefachkraft (Altenpflege) + }, + employee_capabilities_map={ + # Problem/legacy assumption: FWB employees for weekday early rounds. + # Not DB-backed yet. + 791: (Capability.ROUNDS,), # Branz, Janett + 2963: (Capability.ROUNDS,), # Hoots, Renilde + 3868: (Capability.ROUNDS,), # Vanfleet, Eike + # Problem assumption: night-watch employees. + # TPersonalVertraege.IstReineNachtwache did not confirm this, so keep it + # explicitly marked as temporary/problem-derived. + 925: (Capability.NIGHT_WATCH,), # Farniok, Lina + 6681: (Capability.NIGHT_WATCH,), # Labelle, Saskia + 928: (Capability.NIGHT_WATCH,), # Wunderlich, Daniele + }, + availability_facts=( + TimeOfficeAvailabilityFact( + source_shift_id=2434, + expected_code="U", + availability_type=AvailabilityType.VACATION, + ), + TimeOfficeAvailabilityFact( + source_shift_id=2091, + expected_code="ZU", + availability_type=AvailabilityType.VACATION, + ), + TimeOfficeAvailabilityFact( + source_shift_id=1089, + expected_code="FR", + availability_type=AvailabilityType.FREE_DAY, + ), + TimeOfficeAvailabilityFact( + source_shift_id=26, + expected_code="SC", + availability_type=AvailabilityType.TRAINING, + ), + TimeOfficeAvailabilityFact( + source_shift_id=739, + expected_code="FI", + availability_type=AvailabilityType.TRAINING, + ), + TimeOfficeAvailabilityFact( + source_shift_id=1078, + expected_code="EZ", + availability_type=AvailabilityType.UNAVAILABLE, + ), + TimeOfficeAvailabilityFact( + source_shift_id=1086, + expected_code="RE", + availability_type=AvailabilityType.UNAVAILABLE, + ), + TimeOfficeAvailabilityFact( + source_shift_id=1092, + expected_code="AZV", + availability_type=AvailabilityType.FREE_DAY, + ), + ), + demand_facts=( + # Fachkraft + TimeOfficeDemandFact( + source_shift_id=EARLY_F2_SHIFT_ID, + staff_level=StaffLevel.PROFESSIONAL, + required_by_iso_weekday=MappingProxyType( + { + 1: 3, # Mo + 2: 3, # Di + 3: 4, # Mi + 4: 3, # Do + 5: 3, # Fr + 6: 2, # Sa + 7: 2, # So + } + ), + ), + TimeOfficeDemandFact( + source_shift_id=LATE_S2_SHIFT_ID, + staff_level=StaffLevel.PROFESSIONAL, + required_by_iso_weekday=MappingProxyType( + { + 1: 2, # Mo + 2: 2, # Di + 3: 2, # Mi + 4: 2, # Do + 5: 2, # Fr + 6: 2, # Sa + 7: 2, # So + } + ), + ), + TimeOfficeDemandFact( + source_shift_id=NIGHT_N2_SHIFT_ID, + staff_level=StaffLevel.PROFESSIONAL, + required_by_iso_weekday=MappingProxyType( + { + 1: 2, # Mo + 2: 2, # Di + 3: 2, # Mi + 4: 2, # Do + 5: 2, # Fr + 6: 1, # Sa + 7: 1, # So + } + ), + ), + # Hilfskraft + TimeOfficeDemandFact( + source_shift_id=EARLY_F2_SHIFT_ID, + staff_level=StaffLevel.ASSISTANT, + required_by_iso_weekday=MappingProxyType( + { + 1: 2, # Mo + 2: 2, # Di + 3: 2, # Mi + 4: 2, # Do + 5: 2, # Fr + 6: 2, # Sa + 7: 2, # So + } + ), + ), + TimeOfficeDemandFact( + source_shift_id=LATE_S2_SHIFT_ID, + staff_level=StaffLevel.ASSISTANT, + required_by_iso_weekday=MappingProxyType( + { + 1: 2, # Mo + 2: 2, # Di + 3: 2, # Mi + 4: 2, # Do + 5: 2, # Fr + 6: 2, # Sa + 7: 2, # So + } + ), + ), + TimeOfficeDemandFact( + source_shift_id=NIGHT_N2_SHIFT_ID, + staff_level=StaffLevel.ASSISTANT, + required_by_iso_weekday=MappingProxyType( + { + 1: 0, # Mo + 2: 0, # Di + 3: 0, # Mi + 4: 0, # Do + 5: 0, # Fr + 6: 1, # Sa + 7: 1, # So + } + ), + ), + # Azubi + TimeOfficeDemandFact( + source_shift_id=EARLY_F2_SHIFT_ID, + staff_level=StaffLevel.TRAINEE, + required_by_iso_weekday=MappingProxyType( + { + 1: 1, # Mo + 2: 1, # Di + 3: 1, # Mi + 4: 1, # Do + 5: 1, # Fr + 6: 1, # Sa + 7: 1, # So + } + ), + ), + TimeOfficeDemandFact( + source_shift_id=LATE_S2_SHIFT_ID, + staff_level=StaffLevel.TRAINEE, + required_by_iso_weekday=MappingProxyType( + { + 1: 1, # Mo + 2: 1, # Di + 3: 1, # Mi + 4: 1, # Do + 5: 1, # Fr + 6: 1, # Sa + 7: 1, # So + } + ), + ), + TimeOfficeDemandFact( + source_shift_id=NIGHT_N2_SHIFT_ID, + staff_level=StaffLevel.TRAINEE, + required_by_iso_weekday=MappingProxyType( + { + 1: 0, # Mo + 2: 0, # Di + 3: 0, # Mi + 4: 0, # Do + 5: 0, # Fr + 6: 0, # Sa + 7: 0, # So + } + ), + ), + ), +) diff --git a/src/scheduling/timeoffice/models.py b/src/scheduling/timeoffice/models.py deleted file mode 100644 index 5c92bb41..00000000 --- a/src/scheduling/timeoffice/models.py +++ /dev/null @@ -1,28 +0,0 @@ -from pydantic import BaseModel, field_validator - -from src.scheduling.models.core import PlanningPeriod - - -class FetchStationsRequest(BaseModel): - """Request to provide scheduling data for one or more TimeOffice stations.""" - - station_ids: tuple[int, ...] - period: PlanningPeriod - - @field_validator("station_ids") - @classmethod - def station_ids_must_not_be_empty(cls, station_ids: tuple[int, ...]) -> tuple[int, ...]: - if not station_ids: - raise ValueError("At least one station id is required.") - - return station_ids - - @field_validator("station_ids") - @classmethod - def station_ids_must_be_positive(cls, station_ids: tuple[int, ...]) -> tuple[int, ...]: - invalid_station_ids = [station_id for station_id in station_ids if station_id <= 0] - - if invalid_station_ids: - raise ValueError(f"Station ids must be positive: {invalid_station_ids}") - - return station_ids diff --git a/src/scheduling/timeoffice/repositories/__init__.py b/src/scheduling/timeoffice/repositories/__init__.py index 5d4bbe61..ba3fa426 100644 --- a/src/scheduling/timeoffice/repositories/__init__.py +++ b/src/scheduling/timeoffice/repositories/__init__.py @@ -1,13 +1,29 @@ -from src.scheduling.timeoffice.repositories.employees import EmployeeRepositoryResult, TimeOfficeEmployeeRepository -from src.scheduling.timeoffice.repositories.plans import PlanRepositoryResult, TimeOfficePlan, TimeOfficePlanRepository +from src.scheduling.timeoffice.repositories.container import TimeOfficeRepositories +from src.scheduling.timeoffice.repositories.demand import DemandRepositoryResult, TimeOfficeDemandRepository +from src.scheduling.timeoffice.repositories.personnel import PersonnelRepositoryResult, TimeOfficePersonnelRepository +from src.scheduling.timeoffice.repositories.planning_units import ( + PlanningUnitRepositoryResult, + TimeOfficePlanningUnitRepository, +) +from src.scheduling.timeoffice.repositories.roster import RosterRepositoryResult, TimeOfficeRosterRepository from src.scheduling.timeoffice.repositories.shifts import ShiftRepositoryResult, TimeOfficeShiftRepository +from src.scheduling.timeoffice.repositories.sunday_work_history import ( + SundayWorkHistoryRepositoryResult, + TimeOfficeSundayWorkHistoryRepository, +) __all__ = [ - "EmployeeRepositoryResult", - "PlanRepositoryResult", + "PersonnelRepositoryResult", + "PlanningUnitRepositoryResult", + "RosterRepositoryResult", "ShiftRepositoryResult", - "TimeOfficeEmployeeRepository", - "TimeOfficePlan", - "TimeOfficePlanRepository", + "TimeOfficePersonnelRepository", + "TimeOfficePlanningUnitRepository", + "TimeOfficeRepositories", + "TimeOfficeRosterRepository", "TimeOfficeShiftRepository", + "DemandRepositoryResult", + "TimeOfficeDemandRepository", + "SundayWorkHistoryRepositoryResult", + "TimeOfficeSundayWorkHistoryRepository", ] diff --git a/src/scheduling/timeoffice/repositories/container.py b/src/scheduling/timeoffice/repositories/container.py new file mode 100644 index 00000000..c0ce52d0 --- /dev/null +++ b/src/scheduling/timeoffice/repositories/container.py @@ -0,0 +1,30 @@ +from dataclasses import dataclass + +from src.scheduling.timeoffice.facts import TimeOfficeFacts +from src.scheduling.timeoffice.repositories.demand import TimeOfficeDemandRepository +from src.scheduling.timeoffice.repositories.personnel import TimeOfficePersonnelRepository +from src.scheduling.timeoffice.repositories.planning_units import TimeOfficePlanningUnitRepository +from src.scheduling.timeoffice.repositories.roster import TimeOfficeRosterRepository +from src.scheduling.timeoffice.repositories.shifts import TimeOfficeShiftRepository +from src.scheduling.timeoffice.repositories.sunday_work_history import TimeOfficeSundayWorkHistoryRepository + + +@dataclass(frozen=True, slots=True) +class TimeOfficeRepositories: + planning_units: TimeOfficePlanningUnitRepository + personnel: TimeOfficePersonnelRepository + shifts: TimeOfficeShiftRepository + roster: TimeOfficeRosterRepository + demand: TimeOfficeDemandRepository + sunday_work_history: TimeOfficeSundayWorkHistoryRepository + + @classmethod + def create(cls, *, facts: TimeOfficeFacts) -> "TimeOfficeRepositories": + return cls( + planning_units=TimeOfficePlanningUnitRepository(facts=facts), + personnel=TimeOfficePersonnelRepository(facts=facts), + shifts=TimeOfficeShiftRepository(facts=facts), + roster=TimeOfficeRosterRepository(facts=facts), + demand=TimeOfficeDemandRepository(facts=facts), + sunday_work_history=TimeOfficeSundayWorkHistoryRepository(), + ) diff --git a/src/scheduling/timeoffice/repositories/demand.py b/src/scheduling/timeoffice/repositories/demand.py new file mode 100644 index 00000000..c0bf10a7 --- /dev/null +++ b/src/scheduling/timeoffice/repositories/demand.py @@ -0,0 +1,158 @@ +from datetime import timedelta + +from sqlalchemy import Connection + +from src.scheduling.models import ( + DemandRequirement, + PlanningPeriod, + PlanningUnit, + PlanningUnitKind, + SchedulingBaseModel, + Shift, + StaffingDemandRole, +) +from src.scheduling.models.employee import StaffLevel +from src.scheduling.timeoffice.facts import TimeOfficeDemandFact, TimeOfficeFacts + + +class DemandRepositoryResult(SchedulingBaseModel): + demand_requirements: tuple[DemandRequirement, ...] + + +class TimeOfficeDemandRepository: + """Reads TimeOffice demand/Bedarf. + + The test database currently has no usable `TBenutzerBedarf*` rows for the + selected planning units. Therefore this repository is facts-backed for now. + + Later, the internals can be replaced or extended with DB-backed reads from: + - TBenutzerBedarfsGruppen + - TBenutzerBedarf + - TBenutzerBedarfTagTypGruppe + + The output contract stays `DemandRequirement`. + """ + + def __init__(self, *, facts: TimeOfficeFacts) -> None: + self._facts = facts + + def fetch( + self, + *, + connection: Connection, + period: PlanningPeriod, + planning_units: tuple[PlanningUnit, ...], + shifts: tuple[Shift, ...], + ) -> DemandRepositoryResult: + # Kept in the signature because demand is architecturally source-backed. + # The current fallback implementation does not need DB rows yet. + _ = connection + + return DemandRepositoryResult( + demand_requirements=self._build_from_facts( + period=period, + planning_units=planning_units, + shifts=shifts, + ) + ) + + def _build_from_facts( + self, + *, + period: PlanningPeriod, + planning_units: tuple[PlanningUnit, ...], + shifts: tuple[Shift, ...], + ) -> tuple[DemandRequirement, ...]: + shifts_by_id = {shift.shift_id: shift for shift in shifts} + + station_planning_units = tuple( + planning_unit for planning_unit in planning_units if planning_unit.kind == PlanningUnitKind.STATION + ) + + requirements_by_key: dict[ + tuple[int, object, int, StaffLevel], + DemandRequirement, + ] = {} + + current_date = period.start + while current_date <= period.end: + iso_weekday = current_date.isoweekday() + + for planning_unit in station_planning_units: + for fact in self._facts.demand_facts: + if not self._applies_to_planning_unit( + fact=fact, + planning_unit_id=planning_unit.planning_unit_id, + ): + continue + + shift = shifts_by_id.get(fact.source_shift_id) + if shift is None: + raise ValueError(f"TimeOffice demand fact references unknown shift_id={fact.source_shift_id}.") + + if shift.staffing_role != StaffingDemandRole.REQUIRED_MINIMUM: + raise ValueError( + "TimeOffice demand fact must reference a REQUIRED_MINIMUM shift: " + f"shift_id={shift.shift_id} staffing_role={shift.staffing_role}." + ) + + required_count = fact.required_by_iso_weekday.get(iso_weekday) + if required_count is None: + raise ValueError( + "TimeOffice demand fact missing ISO weekday " + f"{iso_weekday}: shift_id={fact.source_shift_id} " + f"staff_level={fact.staff_level}." + ) + + if required_count <= 0: + continue + + requirement = DemandRequirement( + planning_unit_id=planning_unit.planning_unit_id, + date=current_date, + shift_id=fact.source_shift_id, + staff_level=fact.staff_level, + required_count=required_count, + ) + + key = ( + requirement.planning_unit_id, + requirement.date, + requirement.shift_id, + requirement.staff_level, + ) + + existing = requirements_by_key.get(key) + if existing is not None: + raise ValueError( + "Duplicate TimeOffice demand fact expansion: " + f"planning_unit_id={requirement.planning_unit_id} " + f"date={requirement.date} " + f"shift_id={requirement.shift_id} " + f"staff_level={requirement.staff_level}." + ) + + requirements_by_key[key] = requirement + + current_date += timedelta(days=1) + + return tuple( + requirements_by_key[key] + for key in sorted( + requirements_by_key, + key=lambda item: ( + item[0], + item[1], + item[2], + str(item[3]), + ), + ) + ) + + def _applies_to_planning_unit( + self, + *, + fact: TimeOfficeDemandFact, + planning_unit_id: int, + ) -> bool: + return fact.planning_unit_ids is None or planning_unit_id in fact.planning_unit_ids diff --git a/src/scheduling/timeoffice/repositories/employees.py b/src/scheduling/timeoffice/repositories/employees.py deleted file mode 100644 index c8b58467..00000000 --- a/src/scheduling/timeoffice/repositories/employees.py +++ /dev/null @@ -1,191 +0,0 @@ -from datetime import date as Date - -from pydantic import BaseModel, Field -from sqlalchemy import bindparam, text -from sqlalchemy.engine import Connection - -from src.scheduling.models.employee import Employee -from src.scheduling.models.relations import Membership, MembershipType -from src.scheduling.timeoffice.repositories.helpers import to_date -from src.scheduling.timeoffice.repositories.plans import TimeOfficePlan - - -class TimeOfficePlanEmployee(BaseModel): - """Employee assigned to a concrete TimeOffice monthly plan.""" - - source_plan_employee_id: int = Field(gt=0) - source_plan_id: int = Field(gt=0) - - station_id: int = Field(gt=0) - employee_id: int = Field(gt=0) - - personnel_number: str | None = None - first_name: str | None = None - last_name: str | None = None - short_name: str | None = None - - source_profession_id: int | None = None - - valid_from: Date | None = None - valid_until: Date | None = None - - is_substitute: bool | None = None - - -class EmployeeRepositoryResult(BaseModel): - """Canonical output of reading TimeOffice plan employees.""" - - employees: tuple[Employee, ...] - memberships: tuple[Membership, ...] - - -class TimeOfficeEmployeeRepository: - """Read TimeOffice plan employees and map them to employees/memberships.""" - - def fetch( - self, - connection: Connection, - plans: tuple[TimeOfficePlan, ...], - ) -> EmployeeRepositoryResult: - """Read employees assigned to selected monthly TimeOffice plans.""" - source_plan_ids = tuple(plan.source_plan_id for plan in plans) - - if not source_plan_ids: - raise ValueError("Cannot read TimeOffice employees without source plan ids.") - - query = text( - """ - SELECT - pp.Prim AS source_plan_employee_id, - pp.RefPlan AS source_plan_id, - tp.RefPlanungseinheiten AS station_id, - pp.RefPersonal AS employee_id, - - p.PersNr AS personnel_number, - p.Vorname AS first_name, - p.Name AS last_name, - p.KurzName AS short_name, - - pp.RefBerufe AS source_profession_id, - pp.VonDat AS valid_from, - pp.BisDat AS valid_until, - pp.IstVonErsatz AS is_substitute - FROM TPlanPersonal pp - JOIN TPlan tp - ON tp.Prim = pp.RefPlan - JOIN TPersonal p - ON p.Prim = pp.RefPersonal - WHERE pp.RefPlan IN :source_plan_ids - """ - ).bindparams(bindparam("source_plan_ids", expanding=True)) - - rows = ( - connection.execute( - query, - { - "source_plan_ids": source_plan_ids, - }, - ) - .mappings() - .all() - ) - - source_employees = tuple( - TimeOfficePlanEmployee( - source_plan_employee_id=row["source_plan_employee_id"], - source_plan_id=row["source_plan_id"], - station_id=row["station_id"], - employee_id=row["employee_id"], - personnel_number=row["personnel_number"], - first_name=row["first_name"], - last_name=row["last_name"], - short_name=row["short_name"], - source_profession_id=row["source_profession_id"], - valid_from=to_date(row["valid_from"]), - valid_until=to_date(row["valid_until"]), - is_substitute=None if row["is_substitute"] is None else bool(row["is_substitute"]), - ) - for row in rows - ) - - self._ensure_rows_reference_known_plans(source_plan_ids, source_employees) - - employees_by_id: dict[int, Employee] = {} - memberships_by_key: dict[tuple[int, int], Membership] = {} - - for source_employee in source_employees: - employees_by_id[source_employee.employee_id] = self._map_employee(source_employee) - - membership = self._map_membership(source_employee) - memberships_by_key[(membership.employee_id, membership.station_id)] = membership - - print(f"[timeoffice] database.repository.employees rows={len(source_employees)}") - - return EmployeeRepositoryResult( - employees=tuple(employees_by_id.values()), - memberships=tuple(memberships_by_key.values()), - ) - - def _map_employee(self, source_employee: TimeOfficePlanEmployee) -> Employee: - """Map a TimeOffice plan employee row to a canonical Employee.""" - return Employee( - employee_id=source_employee.employee_id, - personnel_number=source_employee.personnel_number, - first_name=source_employee.first_name, - last_name=source_employee.last_name, - display_name=self._display_name(source_employee), - group_id=None, - active=True, - ) - - def _map_membership(self, source_employee: TimeOfficePlanEmployee) -> Membership: - """Map a TimeOffice plan employee row to a local station membership.""" - return Membership( - employee_id=source_employee.employee_id, - station_id=source_employee.station_id, - membership_type=MembershipType.LOCAL, - valid_from=source_employee.valid_from, - valid_until=source_employee.valid_until, - is_substitute=source_employee.is_substitute, - ) - - def _display_name(self, source_employee: TimeOfficePlanEmployee) -> str: - """Build a readable employee display name.""" - display_name = " ".join( - part - for part in ( - source_employee.first_name, - source_employee.last_name, - ) - if part - ) - - if display_name: - return display_name - - if source_employee.short_name: - return source_employee.short_name - - if source_employee.personnel_number: - return source_employee.personnel_number - - return f"Employee {source_employee.employee_id}" - - def _ensure_rows_reference_known_plans( - self, - known_source_plan_ids: tuple[int, ...], - source_employees: tuple[TimeOfficePlanEmployee, ...], - ) -> None: - """Ensure employee rows only reference selected plans.""" - known_source_plan_id_set = set(known_source_plan_ids) - - unknown_source_plan_ids = sorted( - { - source_employee.source_plan_id - for source_employee in source_employees - if source_employee.source_plan_id not in known_source_plan_id_set - } - ) - - if unknown_source_plan_ids: - raise ValueError(f"Employee rows reference unknown TimeOffice plan ids: {unknown_source_plan_ids}") diff --git a/src/scheduling/timeoffice/repositories/helpers.py b/src/scheduling/timeoffice/repositories/helpers.py index 77ab3049..fad07338 100644 --- a/src/scheduling/timeoffice/repositories/helpers.py +++ b/src/scheduling/timeoffice/repositories/helpers.py @@ -4,6 +4,14 @@ from typing import Any +def required[T](value: T | None, *, field_name: str, context: str) -> T: + """Return a required TimeOffice value or fail with source context.""" + if value is None: + raise ValueError(f"Missing required TimeOffice field {field_name} for {context}.") + + return value + + def clean_text(value: Any) -> str | None: """Normalize empty source text values to None.""" if value is None: @@ -17,16 +25,6 @@ def clean_text(value: Any) -> str | None: return cleaned -def required_text(value: Any, *, field_name: str, context: str) -> str: - """Return required non-empty text or raise a useful source error.""" - cleaned = clean_text(value) - - if cleaned is None: - raise ValueError(f"Missing required TimeOffice field {field_name} for {context}.") - - return cleaned - - def normalize_code(value: str) -> str: """Normalize TimeOffice code values for stable comparison.""" return value.strip().upper() @@ -73,6 +71,6 @@ def to_non_negative_int(value: Any) -> int: result = int(value) if result < 0: - return 0 + raise ValueError(f"Expected non-negative TimeOffice integer, got {result}.") return result diff --git a/src/scheduling/timeoffice/repositories/personnel.py b/src/scheduling/timeoffice/repositories/personnel.py new file mode 100644 index 00000000..3391ecb5 --- /dev/null +++ b/src/scheduling/timeoffice/repositories/personnel.py @@ -0,0 +1,338 @@ +from sqlalchemy import Connection, bindparam, text +from sqlalchemy.engine import RowMapping + +from src.scheduling.models import ( + Capability, + Employee, + Plan, + PlanningPeriod, + PlanningUnitMembership, + PlanParticipant, + SchedulingBaseModel, + StaffLevel, +) +from src.scheduling.timeoffice.facts import TimeOfficeFacts +from src.scheduling.timeoffice.repositories.helpers import clean_text, required, to_datetime + + +class PersonnelRepositoryResult(SchedulingBaseModel): + employees: tuple[Employee, ...] + plan_participants: tuple[PlanParticipant, ...] + planning_unit_memberships: tuple[PlanningUnitMembership, ...] + + +class TimeOfficePersonnelRepository: + """Reads selected plan participants and active planning-unit memberships.""" + + def __init__(self, *, facts: TimeOfficeFacts) -> None: + self._facts = facts + + def fetch( + self, + *, + connection: Connection, + plans: tuple[Plan, ...], + planning_unit_ids: tuple[int, ...], + period: PlanningPeriod, + ) -> PersonnelRepositoryResult: + if not plans: + return PersonnelRepositoryResult( + employees=(), + plan_participants=(), + planning_unit_memberships=(), + ) + + plan_personnel_rows = self._fetch_plan_personnel_rows( + connection=connection, + plans=plans, + ) + + employees = self._map_employees(plan_personnel_rows) + plan_participants = tuple(self._map_plan_participant(row) for row in plan_personnel_rows) + + employee_ids = tuple(employee.employee_id for employee in employees) + + memberships = self._fetch_memberships( + connection=connection, + planning_unit_ids=planning_unit_ids, + employee_ids=employee_ids, + period=period, + ) + + return PersonnelRepositoryResult( + employees=employees, + plan_participants=plan_participants, + planning_unit_memberships=memberships, + ) + + def _fetch_plan_personnel_rows( + self, + *, + connection: Connection, + plans: tuple[Plan, ...], + ) -> tuple[RowMapping, ...]: + plan_ids = tuple(plan.plan_id for plan in plans) + + return tuple( + connection.execute( + self._plan_personnel_query(), + {"plan_ids": plan_ids}, + ) + .mappings() + .all() + ) + + def _fetch_memberships( + self, + *, + connection: Connection, + planning_unit_ids: tuple[int, ...], + employee_ids: tuple[int, ...], + period: PlanningPeriod, + ) -> tuple[PlanningUnitMembership, ...]: + if not planning_unit_ids or not employee_ids: + return () + + rows = tuple( + connection.execute( + self._membership_query(), + { + "planning_unit_ids": planning_unit_ids, + "employee_ids": employee_ids, + "period_start": period.start, + "period_end": period.end, + }, + ) + .mappings() + .all() + ) + + return tuple(self._map_membership(row) for row in rows) + + def _plan_personnel_query(self): + return text( + """ + SELECT DISTINCT + pp.RefPlan AS plan_id, + p.RefPlanungseinheiten AS planning_unit_id, + pp.RefPersonal AS employee_id, + per.RefBerufe AS employee_profession_id, + per.Vorname AS first_name, + per.Name AS last_name + FROM TPlanPersonal pp + JOIN TPlan p + ON p.Prim = pp.RefPlan + JOIN TPersonal per + ON per.Prim = pp.RefPersonal + WHERE pp.RefPlan IN :plan_ids + ORDER BY + p.RefPlanungseinheiten, + per.Name, + per.Vorname, + pp.RefPersonal + """ + ).bindparams(bindparam("plan_ids", expanding=True)) + + def _membership_query(self): + return text( + """ + SELECT DISTINCT + pep.RefPlanungseinheiten AS planning_unit_id, + pep.RefPersonal AS employee_id, + pep.RefBerufe AS membership_profession_id, + pep.VonDat AS valid_from, + pep.BisDat AS valid_until, + pep.IstHeimat AS is_home, + pep.IstVonErsatz AS is_replacement + FROM TPlanungseinheitenPersonal pep + WHERE pep.RefPlanungseinheiten IN :planning_unit_ids + AND pep.RefPersonal IN :employee_ids + AND CONVERT(date, pep.VonDat) <= :period_end + AND ( + pep.BisDat IS NULL + OR CONVERT(date, pep.BisDat) >= :period_start + ) + AND ISNULL(pep.KeinEPlan, 0) = 0 + ORDER BY + planning_unit_id, + employee_id, + valid_from, + valid_until + """ + ).bindparams( + bindparam("planning_unit_ids", expanding=True), + bindparam("employee_ids", expanding=True), + ) + + def _map_employees(self, rows: tuple[RowMapping, ...]) -> tuple[Employee, ...]: + employees_by_id: dict[int, Employee] = {} + + for row in rows: + employee_id = int( + required( + row["employee_id"], + field_name="employee_id", + context="TPlanPersonal", + ) + ) + + employee_profession_id = int( + required( + row["employee_profession_id"], + field_name="employee_profession_id", + context=f"TPersonal employee_id={employee_id}", + ) + ) + + employee = Employee( + employee_id=employee_id, + display_name=self._display_name( + employee_id=employee_id, + first_name=row["first_name"], + last_name=row["last_name"], + ), + staff_level=self._staff_level_from_profession( + employee_profession_id, + context=f"TPersonal employee_id={employee_id}", + ), + capabilities=self._capabilities_for_employee(employee_id), + ) + + existing = employees_by_id.get(employee_id) + if existing is not None: + if ( + existing.display_name != employee.display_name + or existing.staff_level != employee.staff_level + or existing.capabilities != employee.capabilities + ): + raise ValueError( + "Conflicting duplicate employee rows from TPlanPersonal/TPersonal: " + f"employee_id={employee_id} " + f"existing={existing!r} new={employee!r}." + ) + + continue + + employees_by_id[employee_id] = employee + + return tuple( + sorted( + employees_by_id.values(), + key=lambda employee: employee.employee_id, + ) + ) + + def _map_plan_participant(self, row: RowMapping) -> PlanParticipant: + return PlanParticipant( + plan_id=int( + required( + row["plan_id"], + field_name="plan_id", + context="TPlanPersonal", + ) + ), + planning_unit_id=int( + required( + row["planning_unit_id"], + field_name="planning_unit_id", + context="TPlanPersonal", + ) + ), + employee_id=int( + required( + row["employee_id"], + field_name="employee_id", + context="TPlanPersonal", + ) + ), + ) + + def _map_membership(self, row: RowMapping) -> PlanningUnitMembership: + planning_unit_id = int( + required( + row["planning_unit_id"], + field_name="planning_unit_id", + context="TPlanungseinheitenPersonal", + ) + ) + employee_id = int( + required( + row["employee_id"], + field_name="employee_id", + context="TPlanungseinheitenPersonal", + ) + ) + + membership_profession_id = int( + required( + row["membership_profession_id"], + field_name="membership_profession_id", + context=(f"TPlanungseinheitenPersonal planning_unit_id={planning_unit_id} employee_id={employee_id}"), + ) + ) + + valid_from = required( + to_datetime(row["valid_from"]), + field_name="valid_from", + context=(f"TPlanungseinheitenPersonal planning_unit_id={planning_unit_id} employee_id={employee_id}"), + ).date() + + valid_until = to_datetime(row["valid_until"]).date() if row["valid_until"] is not None else None + + return PlanningUnitMembership( + planning_unit_id=planning_unit_id, + employee_id=employee_id, + valid_from=valid_from, + valid_until=valid_until, + staff_level=self._staff_level_from_profession( + membership_profession_id, + context=(f"TPlanungseinheitenPersonal planning_unit_id={planning_unit_id} employee_id={employee_id}"), + ), + is_home=bool( + required( + row["is_home"], + field_name="is_home", + context="TPlanungseinheitenPersonal", + ) + ), + is_replacement=bool( + required( + row["is_replacement"], + field_name="is_replacement", + context="TPlanungseinheitenPersonal", + ) + ), + ) + + def _staff_level_from_profession( + self, + profession_id: int, + *, + context: str, + ) -> StaffLevel: + staff_level = self._facts.profession_staff_level_map.get(profession_id) + if staff_level is None: + raise ValueError( + f"No StaffLevel mapping configured for TimeOffice profession_id={profession_id} in {context}." + ) + + return staff_level + + def _capabilities_for_employee(self, employee_id: int) -> tuple[Capability, ...]: + return tuple(self._facts.employee_capabilities_map.get(employee_id, ())) + + def _display_name( + self, + *, + employee_id: int, + first_name: object, + last_name: object, + ) -> str: + first = clean_text(first_name) + last = clean_text(last_name) + + display_name = " ".join(part for part in (last, first) if part) + if display_name: + return display_name + + return f"Employee {employee_id}" diff --git a/src/scheduling/timeoffice/repositories/planning_units.py b/src/scheduling/timeoffice/repositories/planning_units.py new file mode 100644 index 00000000..a26e3a0c --- /dev/null +++ b/src/scheduling/timeoffice/repositories/planning_units.py @@ -0,0 +1,140 @@ +from sqlalchemy import Connection, bindparam, text +from sqlalchemy.engine import RowMapping + +from src.scheduling.models import ( + Plan, + PlanningPeriod, + PlanningUnit, + PlanningUnitKind, + SchedulingBaseModel, +) +from src.scheduling.timeoffice.facts import TimeOfficeFacts +from src.scheduling.timeoffice.repositories.helpers import required + + +class PlanningUnitRepositoryResult(SchedulingBaseModel): + planning_units: tuple[PlanningUnit, ...] + plans: tuple[Plan, ...] + + +class TimeOfficePlanningUnitRepository: + """Reads selected TimeOffice planning units and their concrete plans.""" + + def __init__(self, *, facts: TimeOfficeFacts) -> None: + self._facts = facts + + def fetch( + self, + *, + connection: Connection, + selected_planning_unit_ids: tuple[int, ...], + period: PlanningPeriod, + ) -> PlanningUnitRepositoryResult: + if not selected_planning_unit_ids: + return PlanningUnitRepositoryResult(planning_units=(), plans=()) + + rows = tuple( + connection.execute( + self._query(), + { + "planning_unit_ids": selected_planning_unit_ids, + "period_start": period.start, + "period_end": period.end, + "planning_interval_id": self._facts.monthly_planning_interval_id, + "planning_status_id": self._facts.target_planning_status_id, + }, + ) + .mappings() + .all() + ) + + planning_units = tuple(self._map_planning_unit(row) for row in rows) + plans = tuple(self._map_plan(row) for row in rows) + + self._validate_result( + requested_ids=selected_planning_unit_ids, + planning_units=planning_units, + plans=plans, + ) + + return PlanningUnitRepositoryResult( + planning_units=planning_units, + plans=plans, + ) + + def _query(self): + return text( + """ + SELECT + pe.Prim AS planning_unit_id, + p.Prim AS plan_id, + p.RefPlanungseinheiten AS plan_planning_unit_id + FROM TPlanungseinheiten pe + JOIN TPlan p + ON p.RefPlanungseinheiten = pe.Prim + WHERE pe.Prim IN :planning_unit_ids + AND p.RefPlanungsIntervalle = :planning_interval_id + AND p.RefStati = :planning_status_id + AND CONVERT(date, p.VonDat) = :period_start + AND CONVERT(date, p.BisDat) = :period_end + ORDER BY pe.Prim + """ + ).bindparams(bindparam("planning_unit_ids", expanding=True)) + + def _map_planning_unit(self, row: RowMapping) -> PlanningUnit: + planning_unit_id = int( + required( + row["planning_unit_id"], + field_name="planning_unit_id", + context="TPlanungseinheiten", + ) + ) + + return PlanningUnit( + planning_unit_id=planning_unit_id, + display_name=f"Planning Unit {planning_unit_id}", + kind=self._facts.planning_unit_kind_map.get( + planning_unit_id, + PlanningUnitKind.STATION, + ), + ) + + def _map_plan(self, row: RowMapping) -> Plan: + return Plan( + plan_id=int(required(row["plan_id"], field_name="plan_id", context="TPlan")), + planning_unit_id=int( + required( + row["plan_planning_unit_id"], + field_name="plan_planning_unit_id", + context="TPlan", + ) + ), + ) + + def _validate_result( + self, + *, + requested_ids: tuple[int, ...], + planning_units: tuple[PlanningUnit, ...], + plans: tuple[Plan, ...], + ) -> None: + requested = set(requested_ids) + returned_units = [unit.planning_unit_id for unit in planning_units] + returned_plans = [plan.planning_unit_id for plan in plans] + + missing = sorted(requested - set(returned_units)) + if missing: + raise ValueError(f"No selected TimeOffice target plan found for planning_unit_ids={missing}.") + + duplicates = sorted( + {planning_unit_id for planning_unit_id in returned_units if returned_units.count(planning_unit_id) > 1} + ) + if duplicates: + raise ValueError(f"Multiple selected TimeOffice target plans found for planning_unit_ids={duplicates}.") + + if set(returned_units) != set(returned_plans): + raise ValueError( + "Planning units and plans do not match: " + f"planning_units={sorted(returned_units)} " + f"plans={sorted(returned_plans)}." + ) diff --git a/src/scheduling/timeoffice/repositories/plans.py b/src/scheduling/timeoffice/repositories/plans.py deleted file mode 100644 index 9c8272d8..00000000 --- a/src/scheduling/timeoffice/repositories/plans.py +++ /dev/null @@ -1,144 +0,0 @@ -from pydantic import BaseModel, Field -from sqlalchemy import bindparam, text -from sqlalchemy.engine import Connection - -from src.scheduling.models.core import PlanningPeriod -from src.scheduling.models.station import Station -from src.scheduling.timeoffice.config import TimeOfficeConfig -from src.scheduling.timeoffice.models import FetchStationsRequest - - -class TimeOfficePlan(BaseModel): - """TimeOffice monthly plan metadata for one station/planning unit.""" - - station_id: int = Field(gt=0) - source_plan_id: int = Field(gt=0) - source_planning_unit_id: int = Field(gt=0) - - station_name: str | None = None - - status_id: int | None = None - planning_interval_id: int | None = None - - period: PlanningPeriod - - -class PlanRepositoryResult(BaseModel): - """Canonical output of reading TimeOffice plans.""" - - plans: tuple[TimeOfficePlan, ...] - stations: tuple[Station, ...] - - -class TimeOfficePlanRepository: - """Read monthly TimeOffice plans and station metadata.""" - - def __init__(self, config: TimeOfficeConfig): - self._config = config - - def fetch( - self, - connection: Connection, - request: FetchStationsRequest, - ) -> PlanRepositoryResult: - """Read monthly target plans for all requested stations.""" - query = text( - """ - SELECT - p.Prim AS source_plan_id, - p.RefPlanungseinheiten AS source_planning_unit_id, - p.RefPlanungseinheiten AS station_id, - COALESCE(pe.Bezeichnung, pe.KurzBez) AS station_name, - p.RefStati AS status_id, - p.RefPlanungsIntervalle AS planning_interval_id - FROM TPlan p - LEFT JOIN TPlanungseinheiten pe - ON pe.Prim = p.RefPlanungseinheiten - WHERE p.RefPlanungseinheiten IN :station_ids - AND p.VonDat = :period_start - AND p.BisDat = :period_end - AND p.RefPlanungsIntervalle = :planning_interval_id - AND p.RefStati = :status_id - """ - ).bindparams(bindparam("station_ids", expanding=True)) - - rows = ( - connection.execute( - query, - { - "station_ids": request.station_ids, - "period_start": request.period.start, - "period_end": request.period.end, - "planning_interval_id": self._config.plan_selection.planning_interval_id, - "status_id": self._config.plan_selection.plan_status_id, - }, - ) - .mappings() - .all() - ) - - plans = tuple( - TimeOfficePlan( - station_id=row["station_id"], - source_plan_id=row["source_plan_id"], - source_planning_unit_id=row["source_planning_unit_id"], - station_name=row["station_name"], - status_id=row["status_id"], - planning_interval_id=row["planning_interval_id"], - period=request.period, - ) - for row in rows - ) - - self._ensure_one_plan_per_requested_station(request, plans) - - stations = tuple( - Station( - station_id=plan.station_id, - name=plan.station_name, - source_planning_unit_id=plan.source_planning_unit_id, - ) - for plan in plans - ) - - print(f"[timeoffice] database.repository.plans rows={len(plans)}") - - return PlanRepositoryResult( - plans=plans, - stations=stations, - ) - - def _ensure_one_plan_per_requested_station( - self, - request: FetchStationsRequest, - plans: tuple[TimeOfficePlan, ...], - ) -> None: - """Ensure the query returned exactly one plan per requested station.""" - plans_by_station: dict[int, list[TimeOfficePlan]] = {station_id: [] for station_id in request.station_ids} - - for plan in plans: - plans_by_station.setdefault(plan.station_id, []).append(plan) - - missing_station_ids = [ - station_id for station_id, station_plans in plans_by_station.items() if not station_plans - ] - - if missing_station_ids: - raise ValueError( - "No monthly target TimeOffice plan found for station(s) " - f"{missing_station_ids} and period " - f"{request.period.start.isoformat()}..{request.period.end.isoformat()}." - ) - - ambiguous_station_ids = [ - station_id for station_id, station_plans in plans_by_station.items() if len(station_plans) > 1 - ] - - if ambiguous_station_ids: - details = { - station_id: [plan.source_plan_id for plan in station_plans] - for station_id, station_plans in plans_by_station.items() - if len(station_plans) > 1 - } - - raise ValueError(f"Multiple monthly target TimeOffice plans found for station(s): {details}") diff --git a/src/scheduling/timeoffice/repositories/roster.py b/src/scheduling/timeoffice/repositories/roster.py new file mode 100644 index 00000000..91571bc1 --- /dev/null +++ b/src/scheduling/timeoffice/repositories/roster.py @@ -0,0 +1,389 @@ +from datetime import date as Date + +from sqlalchemy import Connection, bindparam, text +from sqlalchemy.engine import RowMapping + +from src.scheduling.models import ( + Assignment, + AssignmentType, + Availability, + AvailabilityType, + Employee, + Plan, + PlanningPeriod, + SchedulingBaseModel, +) +from src.scheduling.timeoffice.facts import ( + TimeOfficeAvailabilityFact, + TimeOfficeFacts, + TimeOfficeShiftFact, +) +from src.scheduling.timeoffice.repositories.helpers import required, to_datetime + + +class RosterRepositoryResult(SchedulingBaseModel): + assignments: tuple[Assignment, ...] + availability: tuple[Availability, ...] + + +class TimeOfficeRosterRepository: + """Reads hard roster facts from TimeOffice TPlanPersonalKommtGeht. + + This repository emits: + - work rows as Assignment + - absence rows as Availability + + Wishes/preferences are intentionally not emitted here yet, even though + TPlanPersonalKommtGeht has `Wunschdienst`. They need a separate source + analysis and a separate Preference model. + """ + + def __init__(self, *, facts: TimeOfficeFacts) -> None: + self._facts = facts + + def fetch( + self, + *, + connection: Connection, + plans: tuple[Plan, ...], + employees: tuple[Employee, ...], + period: PlanningPeriod, + ) -> RosterRepositoryResult: + if not plans or not employees: + return RosterRepositoryResult(assignments=(), availability=()) + + selected_plan_ids = tuple(plan.plan_id for plan in plans) + selected_planning_unit_ids = tuple(plan.planning_unit_id for plan in plans) + employee_ids = tuple(employee.employee_id for employee in employees) + + rows = tuple( + connection.execute( + self._query(), + { + "employee_ids": employee_ids, + "period_start": period.start, + "period_end": period.end, + }, + ) + .mappings() + .all() + ) + + return RosterRepositoryResult( + assignments=self._map_assignments( + rows=rows, + selected_plan_ids=selected_plan_ids, + selected_planning_unit_ids=selected_planning_unit_ids, + ), + availability=self._map_availability(rows=rows), + ) + + def _query(self): + return text( + """ + SELECT + pkg.RefPlan AS plan_id, + pkg.RefPersonal AS employee_id, + pkg.Datum AS roster_date, + pkg.lfdNr AS segment_number, + + pkg.RefDienste AS work_shift_id, + work_d.KurzBez AS work_shift_code, + + pkg.RefgAbw AS global_absence_shift_id, + global_absence_d.KurzBez AS global_absence_shift_code, + + pkg.RefDienstAbw AS absence_shift_id, + absence_d.KurzBez AS absence_shift_code, + + pkg.RefPlanungseinheiten AS planning_unit_id, + pkg.RefPeinheitOwner AS planning_unit_owner_id, + + pkg.Wunschdienst AS is_wish + FROM TPlanPersonalKommtGeht pkg + LEFT JOIN TDienste work_d + ON work_d.Prim = pkg.RefDienste + LEFT JOIN TDienste global_absence_d + ON global_absence_d.Prim = pkg.RefgAbw + LEFT JOIN TDienste absence_d + ON absence_d.Prim = pkg.RefDienstAbw + WHERE pkg.RefPersonal IN :employee_ids + AND CONVERT(date, pkg.Datum) BETWEEN :period_start AND :period_end + AND ( + pkg.RefDienste IS NOT NULL + OR pkg.RefgAbw IS NOT NULL + OR pkg.RefDienstAbw IS NOT NULL + ) + ORDER BY + pkg.RefPersonal, + pkg.Datum, + pkg.RefPlan, + pkg.lfdNr + """ + ).bindparams(bindparam("employee_ids", expanding=True)) + + def _map_assignments( + self, + *, + rows: tuple[RowMapping, ...], + selected_plan_ids: tuple[int, ...], + selected_planning_unit_ids: tuple[int, ...], + ) -> tuple[Assignment, ...]: + shift_facts = {int(fact.source_shift_id): fact for fact in self._facts.shift_facts} + + selected_plan_id_set = set(selected_plan_ids) + selected_planning_unit_id_set = set(selected_planning_unit_ids) + + assignments_by_key: dict[ + tuple[int, Date, int, AssignmentType, int | None], + Assignment, + ] = {} + unmapped_shift_ids: dict[int, int] = {} + + for row in rows: + raw_shift_id = row["work_shift_id"] + if raw_shift_id is None: + continue + + shift_id = int(raw_shift_id) + shift_fact = shift_facts.get(shift_id) + + if shift_fact is None: + unmapped_shift_ids[shift_id] = unmapped_shift_ids.get(shift_id, 0) + 1 + continue + + self._validate_work_shift_code(row=row, fact=shift_fact) + + employee_id = self._employee_id(row) + roster_date = required( + to_datetime(row["roster_date"]), + field_name="roster_date", + context="TPlanPersonalKommtGeht", + ).date() + + plan_id = self._optional_int( + row["plan_id"], + field_name="plan_id", + context="TPlanPersonalKommtGeht", + ) + + planning_unit_id = self._optional_int( + row["planning_unit_id"], + field_name="planning_unit_id", + context="TPlanPersonalKommtGeht", + ) + + assignment_type = self._assignment_type( + plan_id=plan_id, + planning_unit_id=planning_unit_id, + selected_plan_ids=selected_plan_id_set, + selected_planning_unit_ids=selected_planning_unit_id_set, + ) + + effective_planning_unit_id = planning_unit_id if assignment_type == AssignmentType.PLANNED else None + + assignment = Assignment( + employee_id=employee_id, + date=roster_date, + shift_id=shift_id, + assignment_type=assignment_type, + planning_unit_id=effective_planning_unit_id, + ) + + key = ( + assignment.employee_id, + assignment.date, + assignment.shift_id, + assignment.assignment_type, + assignment.planning_unit_id, + ) + assignments_by_key.setdefault(key, assignment) + + if unmapped_shift_ids: + details = ", ".join(f"{shift_id} count={count}" for shift_id, count in sorted(unmapped_shift_ids.items())) + raise ValueError( + "Unmapped TimeOffice work shift ids found in " + "TPlanPersonalKommtGeht. Add them to " + "TIMEOFFICE_FACTS.scheduling_shift_facts or explicitly decide " + f"to exclude them. Details: {details}." + ) + + return tuple( + assignments_by_key[key] + for key in sorted( + assignments_by_key, + key=lambda item: ( + item[0], + item[1], + item[2], + item[3], + item[4] or -1, + ), + ) + ) + + def _map_availability( + self, + *, + rows: tuple[RowMapping, ...], + ) -> tuple[Availability, ...]: + availability_facts = {int(fact.source_shift_id): fact for fact in self._facts.availability_facts} + + availability_by_key: dict[ + tuple[int, Date, AvailabilityType], + Availability, + ] = {} + unmapped_absence_ids: dict[int, int] = {} + + for row in rows: + absence_shift_id = self._absence_shift_id(row) + if absence_shift_id is None: + continue + + fact = availability_facts.get(absence_shift_id) + if fact is None: + unmapped_absence_ids[absence_shift_id] = unmapped_absence_ids.get(absence_shift_id, 0) + 1 + continue + + self._validate_absence_code(row=row, fact=fact) + + employee_id = self._employee_id(row) + roster_date = required( + to_datetime(row["roster_date"]), + field_name="roster_date", + context="TPlanPersonalKommtGeht", + ).date() + + availability = Availability( + employee_id=employee_id, + date=roster_date, + availability_type=fact.availability_type, + ) + + key = ( + availability.employee_id, + availability.date, + availability.availability_type, + ) + availability_by_key.setdefault(key, availability) + + if unmapped_absence_ids: + details = ", ".join( + f"{absence_id} count={count}" for absence_id, count in sorted(unmapped_absence_ids.items()) + ) + raise ValueError( + "Unmapped TimeOffice absence shift ids found in " + "TPlanPersonalKommtGeht. Add them to " + f"TIMEOFFICE_FACTS.availability_facts. Details: {details}." + ) + + return tuple( + availability_by_key[key] + for key in sorted( + availability_by_key, + key=lambda item: ( + item[0], + item[1], + item[2], + ), + ) + ) + + def _assignment_type( + self, + *, + plan_id: int | None, + planning_unit_id: int | None, + selected_plan_ids: set[int], + selected_planning_unit_ids: set[int], + ) -> AssignmentType: + if plan_id in selected_plan_ids and planning_unit_id in selected_planning_unit_ids: + return AssignmentType.PLANNED + + return AssignmentType.EXTERNAL + + def _absence_shift_id(self, row: RowMapping) -> int | None: + global_absence_shift_id = self._optional_int( + row["global_absence_shift_id"], + field_name="global_absence_shift_id", + context="TPlanPersonalKommtGeht", + ) + absence_shift_id = self._optional_int( + row["absence_shift_id"], + field_name="absence_shift_id", + context="TPlanPersonalKommtGeht", + ) + + if global_absence_shift_id is None: + return absence_shift_id + + if absence_shift_id is None: + return global_absence_shift_id + + if global_absence_shift_id != absence_shift_id: + raise ValueError( + "Conflicting TimeOffice absence references in " + "TPlanPersonalKommtGeht: " + f"RefgAbw={global_absence_shift_id} " + f"RefDienstAbw={absence_shift_id}." + ) + + return absence_shift_id + + def _validate_work_shift_code( + self, + *, + row: RowMapping, + fact: TimeOfficeShiftFact, + ) -> None: + actual_code = row["work_shift_code"] + if actual_code is None: + raise ValueError( + f"Missing TDienste.KurzBez for TimeOffice work shift source_shift_id={fact.source_shift_id}." + ) + + actual = str(actual_code).strip() + if actual != fact.expected_code: + raise ValueError( + "Unexpected TimeOffice work shift code: " + f"source_shift_id={fact.source_shift_id} " + f"expected={fact.expected_code!r} actual={actual!r}." + ) + + def _validate_absence_code( + self, + *, + row: RowMapping, + fact: TimeOfficeAvailabilityFact, + ) -> None: + actual_code = row["absence_shift_code"] or row["global_absence_shift_code"] + if actual_code is None: + raise ValueError( + f"Missing TDienste.KurzBez for TimeOffice absence shift source_shift_id={fact.source_shift_id}." + ) + + actual = str(actual_code).strip() + if actual != fact.expected_code: + raise ValueError( + "Unexpected TimeOffice absence code: " + f"source_shift_id={fact.source_shift_id} " + f"expected={fact.expected_code!r} actual={actual!r}." + ) + + def _employee_id(self, row: RowMapping) -> int: + return int( + required( + row["employee_id"], + field_name="employee_id", + context="TPlanPersonalKommtGeht", + ) + ) + + def _optional_int(self, value: object, *, field_name: str, context: str) -> int | None: + if value is None: + return None + + if isinstance(value, bool) or not isinstance(value, int): + raise ValueError(f"Expected int or NULL for {field_name} in {context}, got {value!r}.") + + return value diff --git a/src/scheduling/timeoffice/repositories/shifts.py b/src/scheduling/timeoffice/repositories/shifts.py index 096be7b4..f362e820 100644 --- a/src/scheduling/timeoffice/repositories/shifts.py +++ b/src/scheduling/timeoffice/repositories/shifts.py @@ -1,90 +1,66 @@ from collections import defaultdict from collections.abc import Sequence from datetime import datetime as DateTime -from itertools import pairwise -from typing import Any -from pydantic import BaseModel, Field -from sqlalchemy import bindparam, text -from sqlalchemy.engine import Connection +from sqlalchemy import Connection, bindparam, text +from sqlalchemy.engine import RowMapping -from src.scheduling.models.shift import Shift, ShiftKind -from src.scheduling.timeoffice.config import TimeOfficeConfig, TimeOfficeShiftConfig +from src.scheduling.models import SchedulingBaseModel, Shift +from src.scheduling.timeoffice.facts import TimeOfficeFacts, TimeOfficeShiftFact from src.scheduling.timeoffice.repositories.helpers import ( clean_text, - minute_of_day, normalize_code, - required_text, + required, to_datetime, to_non_negative_int, ) -class TimeOfficeShiftSegment(BaseModel): - """One TimeOffice timing segment for a shift.""" - - start: DateTime - end: DateTime - minutes: int = Field(ge=0) - - -class TimeOfficeShiftSource(BaseModel): - """Source data for one configured TimeOffice shift.""" - - source_shift_id: int = Field(gt=0) - source_code: str - name: str - - source_shift_type_id: int - source_statistics_group_id: int | None = None - source_facility_id: int | None = None - - ppug_relevant: bool = False - ppprl_relevant: bool = False - ppug_pause_counts: bool = False - - segments: tuple[TimeOfficeShiftSegment, ...] - - start_minute: int = Field(ge=0, lt=24 * 60) - end_minute: int = Field(ge=0, lt=24 * 60) - ends_next_day: bool = False - break_minutes: int = Field(default=0, ge=0) - net_work_minutes: int = Field(ge=0) +class ShiftRepositoryResult(SchedulingBaseModel): + shifts: tuple[Shift, ...] -class ShiftRepositoryResult(BaseModel): - """Canonical output of reading TimeOffice shifts.""" +class TimeOfficeShiftRepository: + """Reads TimeOffice shifts and maps them to reduced scheduling shifts.""" - shifts: tuple[Shift, ...] + def __init__(self, *, facts: TimeOfficeFacts) -> None: + self._facts = facts + def fetch( + self, + *, + connection: Connection, + ) -> ShiftRepositoryResult: + shift_ids = tuple(int(shift_fact.source_shift_id) for shift_fact in self._facts.shift_facts) -class TimeOfficeShiftRepository: - """Read configured TimeOffice shift definitions.""" + if not shift_ids: + return ShiftRepositoryResult(shifts=()) - def __init__(self, config: TimeOfficeConfig): - self._config = config + rows = tuple( + connection.execute( + self._query(), + {"shift_ids": shift_ids}, + ) + .mappings() + .all() + ) - def fetch(self, connection: Connection) -> ShiftRepositoryResult: - """Read configured solver-relevant shifts from TimeOffice.""" - source_shift_ids = self._config.solver_shift_ids + shifts = self._map_rows(rows) + self._validate_requested_shifts( + requested_shift_ids=shift_ids, + shifts=shifts, + ) - if not source_shift_ids: - raise ValueError("At least one TimeOffice solver shift id is required.") + return ShiftRepositoryResult(shifts=shifts) - query = text( + def _query(self): + return text( """ SELECT - d.Prim AS source_shift_id, - d.KurzBez AS source_code, - d.Bezeichnung AS name, - - d.RefDienstTypen AS source_shift_type_id, - d.RefDiensteStatistikGruppen AS source_statistics_group_id, - d.RefEinrichtungen AS source_facility_id, - - d.PpugRelevant AS ppug_relevant, - d.PpprlRelevant AS ppprl_relevant, - d.PpugPauseAnrechnen AS ppug_pause_counts, + d.Prim AS shift_id, + d.KurzBez AS shift_code, + d.Bezeichnung AS shift_name, + d.RefDienstTypen AS shift_type_id, sz.Kommt AS segment_start, sz.Geht AS segment_end, @@ -92,242 +68,177 @@ def fetch(self, connection: Connection) -> ShiftRepositoryResult: FROM TDienste d LEFT JOIN TDiensteSollzeiten sz ON sz.RefDienste = d.Prim - WHERE d.Prim IN :source_shift_ids + WHERE d.Prim IN :shift_ids ORDER BY d.Prim, sz.Kommt, sz.Geht """ - ).bindparams(bindparam("source_shift_ids", expanding=True)) - - rows = ( - connection.execute( - query, - { - "source_shift_ids": source_shift_ids, - }, - ) - .mappings() - .all() - ) - - sources = self._map_rows(rows) - - self._ensure_all_configured_shifts_found(sources) - self._warn_about_unexpected_codes(sources) - self._warn_about_unexpected_shift_types(sources) - - shifts = tuple(self._map_shift(source) for source in sources) + ).bindparams(bindparam("shift_ids", expanding=True)) - print(f"[timeoffice] database.repository.shifts rows={len(shifts)}") - - return ShiftRepositoryResult(shifts=shifts) - - def _map_rows(self, rows: Sequence[Any]) -> tuple[TimeOfficeShiftSource, ...]: - """Map flat SQL rows into one source object per TimeOffice shift.""" - rows_by_shift_id: dict[int, list[Any]] = defaultdict(list) + def _map_rows(self, rows: Sequence[RowMapping]) -> tuple[Shift, ...]: + rows_by_shift_id: dict[int, list[RowMapping]] = defaultdict(list) for row in rows: - rows_by_shift_id[row["source_shift_id"]].append(row) - - return tuple(self._map_shift_source(shift_rows) for _, shift_rows in sorted(rows_by_shift_id.items())) - - def _map_shift_source(self, rows: list[Any]) -> TimeOfficeShiftSource: - """Map all rows for one TimeOffice shift.""" - first_row = rows[0] - - segments = tuple(segment for segment in (self._map_segment(row) for row in rows) if segment is not None) - - if not segments: - raise ValueError( - "Configured TimeOffice shift has no timing segments: " - f"{first_row['source_shift_id']} / {first_row['source_code']}" + shift_id = int( + required( + row["shift_id"], + field_name="shift_id", + context="TDienste", + ) ) + rows_by_shift_id[shift_id].append(row) - ordered_segments = tuple(sorted(segments, key=lambda segment: segment.start)) + shift_facts_by_id = {int(shift_fact.source_shift_id): shift_fact for shift_fact in self._facts.shift_facts} - first_segment = ordered_segments[0] - last_segment = ordered_segments[-1] - - start_minute = minute_of_day(first_segment.start) - end_minute = minute_of_day(last_segment.end) - ends_next_day = last_segment.end.date() > first_segment.start.date() - - net_work_minutes = sum(segment.minutes for segment in ordered_segments) - break_minutes = self._break_minutes(ordered_segments) - - if net_work_minutes <= 0: - net_work_minutes = self._fallback_net_work_minutes( - start=first_segment.start, - end=last_segment.end, - break_minutes=break_minutes, + return tuple( + self._map_shift( + shift_id=shift_id, + rows=shift_rows, + shift_fact=shift_facts_by_id[shift_id], ) - - return TimeOfficeShiftSource( - source_shift_id=first_row["source_shift_id"], - source_code=required_text( - first_row["source_code"], - field_name="source_code", - context=f"shift {first_row['source_shift_id']}", - ), - name=self._name( - name=first_row["name"], - code=first_row["source_code"], - source_shift_id=first_row["source_shift_id"], - ), - source_shift_type_id=first_row["source_shift_type_id"], - source_statistics_group_id=first_row["source_statistics_group_id"], - source_facility_id=first_row["source_facility_id"], - ppug_relevant=bool(first_row["ppug_relevant"]), - ppprl_relevant=bool(first_row["ppprl_relevant"]), - ppug_pause_counts=bool(first_row["ppug_pause_counts"]), - segments=ordered_segments, - start_minute=start_minute, - end_minute=end_minute, - ends_next_day=ends_next_day, - break_minutes=break_minutes, - net_work_minutes=net_work_minutes, + for shift_id, shift_rows in sorted(rows_by_shift_id.items()) ) - def _map_segment(self, row: Any) -> TimeOfficeShiftSegment | None: - """Map one TDiensteSollzeiten row to a segment.""" - if row["segment_start"] is None or row["segment_end"] is None: - return None + def _map_shift( + self, + *, + shift_id: int, + rows: list[RowMapping], + shift_fact: TimeOfficeShiftFact, + ) -> Shift: + first_row = rows[0] + context = f"TDienste shift_id={shift_id}" - start = to_datetime(row["segment_start"]) - end = to_datetime(row["segment_end"]) - minutes = to_non_negative_int(row["segment_minutes"]) + source_code = clean_text( + required( + first_row["shift_code"], + field_name="shift_code", + context=context, + ) + ) + if source_code is None: + raise ValueError(f"Empty TimeOffice shift code in {context}.") - if minutes <= 0: - minutes = self._duration_minutes(start, end) + self._validate_expected_code( + shift_id=shift_id, + actual_code=source_code, + expected_code=shift_fact.expected_code, + ) - return TimeOfficeShiftSegment( - start=start, - end=end, - minutes=minutes, + shift_type_id = int( + required( + first_row["shift_type_id"], + field_name="shift_type_id", + context=context, + ) + ) + self._validate_real_work_shift( + shift_id=shift_id, + shift_type_id=shift_type_id, ) - def _map_shift(self, source: TimeOfficeShiftSource) -> Shift: - """Map a TimeOffice shift source to a canonical solver-facing Shift.""" - configured_shift = self._configured_shift(source.source_shift_id) + segments = self._map_segments(rows=rows, shift_id=shift_id) + start_at = segments[0][0] + end_at = segments[-1][1] + net_work_minutes = self._net_work_minutes(segments) return Shift( - shift_id=f"timeoffice:{source.source_shift_id}", - shift_group_id=configured_shift.group_id, - name=source.name, - source_shift_id=source.source_shift_id, - source_code=normalize_code(source.source_code), - kind=configured_shift.kind, - start_minute=source.start_minute, - end_minute=source.end_minute, - ends_next_day=source.ends_next_day, - break_minutes=source.break_minutes, - net_work_minutes=source.net_work_minutes, - assignable=self._is_assignable(source, configured_shift), - counts_as_work=configured_shift.counts_as_work and source.net_work_minutes > 0, - counts_for_minimum_staffing=configured_shift.counts_for_minimum_staffing, - is_night=configured_shift.kind == ShiftKind.NIGHT, + shift_id=shift_id, + code=source_code, + kind=shift_fact.kind, + staffing_role=shift_fact.staffing_role, + start_minute=self._minute_of_day(start_at), + end_minute=self._minute_of_day(end_at), + net_work_minutes=net_work_minutes, ) - def _configured_shift(self, source_shift_id: int) -> TimeOfficeShiftConfig: - """Return configured solver semantics for a TimeOffice shift id.""" - try: - return self._config.shifts_by_id[source_shift_id] - except KeyError as error: - raise KeyError(f"No TimeOffice shift configuration found for source id {source_shift_id}.") from error - - def _is_assignable( + def _map_segments( self, - source: TimeOfficeShiftSource, - configured_shift: TimeOfficeShiftConfig, - ) -> bool: - """Return whether the solver may create decision variables for this shift.""" - if not configured_shift.assignable: - return False + *, + rows: list[RowMapping], + shift_id: int, + ) -> tuple[tuple[DateTime, DateTime, int], ...]: + segments: list[tuple[DateTime, DateTime, int]] = [] + + for row in rows: + if row["segment_start"] is None and row["segment_end"] is None: + continue - if source.net_work_minutes <= 0: - return False + context = f"TDiensteSollzeiten shift_id={shift_id}" - return source.source_shift_type_id in self._config.assignable_shift_type_ids + start_at = required( + to_datetime(row["segment_start"]), + field_name="segment_start", + context=context, + ) + end_at = required( + to_datetime(row["segment_end"]), + field_name="segment_end", + context=context, + ) + minutes = to_non_negative_int(row["segment_minutes"]) - def _break_minutes(self, segments: tuple[TimeOfficeShiftSegment, ...]) -> int: - """Compute break minutes as gaps between ordered work segments.""" - break_minutes = 0 + if end_at <= start_at: + raise ValueError( + f"Invalid shift segment in {context}: segment_start={start_at!r} segment_end={end_at!r}." + ) - for previous, current in pairwise(segments): - gap = self._duration_minutes(previous.end, current.start) + segments.append((start_at, end_at, minutes)) - if gap > 0: - break_minutes += gap + if not segments: + raise ValueError(f"No timing segments found for TimeOffice shift_id={shift_id}.") - return break_minutes + return tuple(segments) - def _fallback_net_work_minutes( + def _net_work_minutes( self, - start: DateTime, - end: DateTime, - break_minutes: int, + segments: tuple[tuple[DateTime, DateTime, int], ...], ) -> int: - """Fallback net minutes if TDiensteSollzeiten.Minuten is unavailable.""" - return max(0, self._duration_minutes(start, end) - break_minutes) - - def _duration_minutes(self, start: DateTime, end: DateTime) -> int: - """Return duration in minutes.""" - duration = int((end - start).total_seconds() // 60) + source_minutes = sum(segment_minutes for _, _, segment_minutes in segments) + if source_minutes > 0: + return source_minutes - if duration < 0: - raise ValueError(f"Negative TimeOffice segment duration: {start!r} -> {end!r}") + return sum(int((end_at - start_at).total_seconds() // 60) for start_at, end_at, _ in segments) - return duration - - def _ensure_all_configured_shifts_found(self, sources: tuple[TimeOfficeShiftSource, ...]) -> None: - """Ensure configured shift ids exist in TimeOffice.""" - found_ids = {source.source_shift_id for source in sources} - missing_ids = sorted(set(self._config.solver_shift_ids) - found_ids) - - if missing_ids: - raise ValueError(f"Configured TimeOffice shift ids were not found: {missing_ids}") - - def _warn_about_unexpected_codes(self, sources: tuple[TimeOfficeShiftSource, ...]) -> None: - """Print a warning if TimeOffice code differs from configured expectation.""" - mismatches: list[str] = [] - - for source in sources: - configured_shift = self._configured_shift(source.source_shift_id) - actual_code = normalize_code(source.source_code) - expected_code = normalize_code(configured_shift.expected_code) - - if actual_code != expected_code: - mismatches.append(f"{source.source_shift_id}: expected={expected_code} actual={actual_code}") - - if not mismatches: - return - - print("[timeoffice] database.repository.shifts warning unexpected_codes=" + ", ".join(mismatches)) - - def _warn_about_unexpected_shift_types(self, sources: tuple[TimeOfficeShiftSource, ...]) -> None: - """Print a warning if a configured shift has an unexpected TimeOffice shift type.""" - unexpected: list[str] = [] - - for source in sources: - if source.source_shift_type_id not in self._config.assignable_shift_type_ids: - unexpected.append(f"{source.source_shift_id}: type={source.source_shift_type_id}") - - if not unexpected: - return - - print("[timeoffice] database.repository.shifts warning unexpected_shift_types=" + ", ".join(unexpected)) - - def _name(self, name: Any, code: Any, source_shift_id: int) -> str: - """Return a readable shift name.""" - cleaned_name = clean_text(name) - - if cleaned_name is not None: - return cleaned_name + def _validate_expected_code( + self, + *, + shift_id: int, + actual_code: str, + expected_code: str, + ) -> None: + if normalize_code(actual_code) != normalize_code(expected_code): + raise ValueError( + "Unexpected TimeOffice shift code for known scheduling shift: " + f"shift_id={shift_id} expected={expected_code!r} actual={actual_code!r}." + ) - cleaned_code = clean_text(code) + def _validate_real_work_shift( + self, + *, + shift_id: int, + shift_type_id: int, + ) -> None: + real_work_shift_type_ids = {int(shift_type_id) for shift_type_id in self._facts.real_work_shift_type_ids} - if cleaned_code is not None: - return cleaned_code + if shift_type_id not in real_work_shift_type_ids: + raise ValueError( + "Known scheduling shift is not configured as real work shift in TimeOffice: " + f"shift_id={shift_id} shift_type_id={shift_type_id}." + ) - return f"TimeOffice shift {source_shift_id}" + def _validate_requested_shifts( + self, + *, + requested_shift_ids: tuple[int, ...], + shifts: tuple[Shift, ...], + ) -> None: + returned_shift_ids = {shift.shift_id for shift in shifts} + missing_shift_ids = sorted(set(requested_shift_ids) - returned_shift_ids) + + if missing_shift_ids: + raise ValueError(f"Missing TimeOffice shift definitions for shift_ids={missing_shift_ids}.") + + def _minute_of_day(self, value: DateTime) -> int: + return value.hour * 60 + value.minute diff --git a/src/scheduling/timeoffice/repositories/sunday_work_history.py b/src/scheduling/timeoffice/repositories/sunday_work_history.py new file mode 100644 index 00000000..ffd746bb --- /dev/null +++ b/src/scheduling/timeoffice/repositories/sunday_work_history.py @@ -0,0 +1,133 @@ +from datetime import date as Date + +from sqlalchemy import Connection, bindparam, text +from sqlalchemy.engine import RowMapping + +from src.scheduling.models import ( + Employee, + EmployeeSundayWorkHistory, + PlanningPeriod, + SchedulingBaseModel, +) + + +class SundayWorkHistoryRepositoryResult(SchedulingBaseModel): + sunday_work_history: tuple[EmployeeSundayWorkHistory, ...] + + +class TimeOfficeSundayWorkHistoryRepository: + """Reads historical worked-Sunday counts from TimeOffice daily account rows. + + Source: + - TKonten identifies the Sunday account via BezProg = 'SONNTAG' + - TPersonalKontenJeTag contains daily account rows + + This repository intentionally does not use TimeOfficeFacts because the + account identity is inferable from the database. + """ + + LOOKBACK_YEARS = 1 + SUNDAY_ACCOUNT_CODE = "SONNTAG" + + def fetch( + self, + *, + connection: Connection, + period: PlanningPeriod, + employees: tuple[Employee, ...], + ) -> SundayWorkHistoryRepositoryResult: + if not employees: + return SundayWorkHistoryRepositoryResult(sunday_work_history=()) + + employee_ids = tuple(employee.employee_id for employee in employees) + sunday_account_id = self._fetch_sunday_account_id(connection) + + lookback_start = self._subtract_years(period.end, self.LOOKBACK_YEARS) + lookback_end = period.end + + rows = connection.execute( + self._history_query(), + { + "employee_ids": employee_ids, + "sunday_account_id": sunday_account_id, + "lookback_start": lookback_start, + "lookback_end": lookback_end, + }, + ).mappings() + + return SundayWorkHistoryRepositoryResult(sunday_work_history=tuple(self._map_history_row(row) for row in rows)) + + def _fetch_sunday_account_id(self, connection: Connection) -> int: + rows = ( + connection.execute( + self._sunday_account_query(), + {"sunday_account_code": self.SUNDAY_ACCOUNT_CODE}, + ) + .mappings() + .all() + ) + + if len(rows) != 1: + raise ValueError( + "Expected exactly one TimeOffice Sunday account with " + f"BezProg={self.SUNDAY_ACCOUNT_CODE!r}, found {len(rows)}." + ) + + row = rows[0] + is_daily_account = int(row["is_daily_account"] or 0) + + if is_daily_account != 1: + raise ValueError( + "TimeOffice Sunday account must be a daily account: " + f"account_id={row['account_id']} " + f"code={row['account_code']!r} " + f"name={row['account_name']!r}." + ) + + return int(row["account_id"]) + + def _sunday_account_query(self): + return text(""" + SELECT + Prim AS account_id, + BezProg AS account_code, + Bez AS account_name, + BezKurz AS account_short_name, + IstTagesKonto AS is_daily_account + FROM TKonten + WHERE BezProg = :sunday_account_code + """) + + def _history_query(self): + return text(""" + SELECT + p.Prim AS employee_id, + COUNT(DISTINCT CAST(pkt.Datum AS date)) AS worked_sundays + FROM TPersonal p + LEFT JOIN TPersonalKontenJeTag pkt + ON pkt.RefPersonal = p.Prim + AND pkt.RefKonten = :sunday_account_id + AND CAST(pkt.Datum AS date) BETWEEN :lookback_start AND :lookback_end + AND DATEDIFF( + day, + CONVERT(date, '1900-01-07', 23), + CAST(pkt.Datum AS date) + ) % 7 = 0 + AND ISNULL(pkt.Wert, 0) > 0 + WHERE p.Prim IN :employee_ids + GROUP BY p.Prim + ORDER BY p.Prim + """).bindparams(bindparam("employee_ids", expanding=True)) + + def _map_history_row(self, row: RowMapping) -> EmployeeSundayWorkHistory: + return EmployeeSundayWorkHistory( + employee_id=int(row["employee_id"]), + worked_sundays=int(row["worked_sundays"] or 0), + ) + + def _subtract_years(self, value: Date, years: int) -> Date: + try: + return value.replace(year=value.year - years) + except ValueError: + # Leap day fallback. + return value.replace(year=value.year - years, day=28) diff --git a/src/scheduling/timeoffice/service.py b/src/scheduling/timeoffice/service.py index a66ae486..14c72d3f 100644 --- a/src/scheduling/timeoffice/service.py +++ b/src/scheduling/timeoffice/service.py @@ -1,40 +1,52 @@ -from src.scheduling.models.dataset import SchedulingDataset -from src.scheduling.timeoffice.config import TIMEOFFICE_CONFIG, TimeOfficeConfig +from src.scheduling.models import PlanningPeriod, SchedulingDataset from src.scheduling.timeoffice.database import TimeOfficeDatabase -from src.scheduling.timeoffice.models import FetchStationsRequest -from src.scheduling.timeoffice.settings import TimeOfficeSettings, load_settings +from src.scheduling.timeoffice.facts import TimeOfficeFacts class TimeOfficeService: - """Public facade for TimeOffice data transfer.""" + """Application-facing service for loading scheduling data from TimeOffice.""" def __init__( self, - settings: TimeOfficeSettings, + *, + facts: TimeOfficeFacts, database: TimeOfficeDatabase, - ): - self._settings = settings + ) -> None: + self._facts = facts self._database = database - def fetch(self, request: FetchStationsRequest) -> SchedulingDataset: - """Provide scheduling data for the requested TimeOffice stations.""" - print( - "[timeoffice] service.fetch " - f"stations={list(request.station_ids)} " - f"period={request.period.start.isoformat()}..{request.period.end.isoformat()}" + def fetch_dataset( + self, + *, + planning_unit_ids: tuple[int, ...], + period: PlanningPeriod, + ) -> SchedulingDataset: + selected_planning_unit_ids = self._normalize_planning_unit_ids(planning_unit_ids) + + return self._database.fetch_dataset( + selected_planning_unit_ids=selected_planning_unit_ids, + period=period, ) - return self._database.read(request) - + def _normalize_planning_unit_ids( + self, + planning_unit_ids: tuple[int, ...], + ) -> tuple[int, ...]: + normalized = tuple(dict.fromkeys(int(value) for value in planning_unit_ids)) -def create_timeoffice_service( - settings: TimeOfficeSettings | None = None, - config: TimeOfficeConfig = TIMEOFFICE_CONFIG, -) -> TimeOfficeService: - """Create the default TimeOffice service.""" - settings = settings or load_settings() + if not normalized: + raise ValueError("At least one planning unit must be selected.") - return TimeOfficeService( - settings=settings, - database=TimeOfficeDatabase(settings, config), - ) + unknown_ids = sorted( + planning_unit_id + for planning_unit_id in normalized + if planning_unit_id not in self._facts.planning_unit_kind_map + ) + if unknown_ids: + raise ValueError( + "Unknown TimeOffice planning_unit_ids requested: " + f"{unknown_ids}. Add them to TIMEOFFICE_FACTS.planning_unit_kind_map " + "or fix the request." + ) + + return normalized diff --git a/src/scheduling/timeoffice/settings.py b/src/scheduling/timeoffice/settings.py deleted file mode 100644 index 5e35e4d4..00000000 --- a/src/scheduling/timeoffice/settings.py +++ /dev/null @@ -1,22 +0,0 @@ -from pydantic import SecretStr -from pydantic_settings import BaseSettings, SettingsConfigDict - - -class TimeOfficeSettings(BaseSettings): - """Settings for TimeOffice data transfer.""" - - model_config = SettingsConfigDict( - env_file=".env", - extra="ignore", - ) - - db_driver: str = "ODBC Driver 18 for SQL Server" - db_server: str - db_name: str - db_user: str - db_password: SecretStr - - -def load_settings() -> TimeOfficeSettings: - """Load TimeOffice settings from environment variables and .env files.""" - return TimeOfficeSettings() # type: ignore[call-arg] diff --git a/tests/cp/conftest.py b/tests/cp/conftest.py index 90f5f099..d6fdd40b 100644 --- a/tests/cp/conftest.py +++ b/tests/cp/conftest.py @@ -2,11 +2,11 @@ import pytest -from src.cp import Model -from src.day import Day -from src.employee import Employee -from src.loader import FSLoader -from src.shift import Shift +from legacy.src.cp import Model +from legacy.src.day import Day +from legacy.src.employee import Employee +from legacy.src.loader import FSLoader +from legacy.src.shift import Shift alice: Employee = Employee( key=1, diff --git a/tests/cp/constraints/test_all_constraints.py b/tests/cp/constraints/test_all_constraints.py index b91d81d8..0cd8f0ac 100644 --- a/tests/cp/constraints/test_all_constraints.py +++ b/tests/cp/constraints/test_all_constraints.py @@ -15,7 +15,7 @@ from test_target_working_time import find_target_working_time_violations from test_vaction_days_and_shifts import find_vaction_days_and_shifts_violations -from src.cp.constraints import ( +from legacy.src.cp.constraints import ( FreeDayAfterNightShiftPhaseConstraint, HierarchyOfIntermediateShiftsConstraint, MaxOneShiftPerDayConstraint, @@ -26,8 +26,8 @@ TargetWorkingTimeConstraint, VacationDaysAndShiftsConstraint, ) -from src.cp.model import Model -from src.cp.objectives import ( +from legacy.src.cp.model import Model +from legacy.src.cp.objectives import ( EverySecondWeekendFreeObjective, FreeDaysAfterNightShiftPhaseObjective, FreeDaysNearWeekendObjective, @@ -38,10 +38,10 @@ NotTooManyConsecutiveDaysObjective, RotateShiftsForwardObjective, ) -from src.cp.variables import Variable -from src.loader import FSLoader -from src.solution import Solution -from src.web.process_solution import process_solution +from legacy.src.cp.variables import Variable +from legacy.src.loader import FSLoader +from legacy.src.solution import Solution +from legacy.src.web.process_solution import process_solution def detailed_error_display( diff --git a/tests/cp/constraints/test_free_day_after_night_shift_phase.py b/tests/cp/constraints/test_free_day_after_night_shift_phase.py index 0e6e4419..7b02706a 100644 --- a/tests/cp/constraints/test_free_day_after_night_shift_phase.py +++ b/tests/cp/constraints/test_free_day_after_night_shift_phase.py @@ -4,10 +4,10 @@ from ortools.sat.python.cp_model import CpSolver, IntVar -from src.cp.constraints import FreeDayAfterNightShiftPhaseConstraint -from src.cp.model import Model -from src.cp.variables import Variable -from src.shift import Shift +from legacy.src.cp.constraints import FreeDayAfterNightShiftPhaseConstraint +from legacy.src.cp.model import Model +from legacy.src.cp.variables import Variable +from legacy.src.shift import Shift def find_free_day_after_night_shift_phase_violations( diff --git a/tests/cp/constraints/test_hierarchy_of_intermediate_shifts.py b/tests/cp/constraints/test_hierarchy_of_intermediate_shifts.py index 99b20867..26148ad3 100644 --- a/tests/cp/constraints/test_hierarchy_of_intermediate_shifts.py +++ b/tests/cp/constraints/test_hierarchy_of_intermediate_shifts.py @@ -4,11 +4,11 @@ from ortools.sat.python.cp_model import CpSolver, IntVar -from src.cp.constraints import HierarchyOfIntermediateShiftsConstraint -from src.cp.model import Model -from src.cp.variables import Variable -from src.day import Day -from src.shift import Shift +from legacy.src.cp.constraints import HierarchyOfIntermediateShiftsConstraint +from legacy.src.cp.model import Model +from legacy.src.cp.variables import Variable +from legacy.src.day import Day +from legacy.src.shift import Shift def find_hierarchy_of_intermediate_shifts_violations( diff --git a/tests/cp/constraints/test_max_one_shift_per_day.py b/tests/cp/constraints/test_max_one_shift_per_day.py index 28bd6fe2..f161bfa4 100644 --- a/tests/cp/constraints/test_max_one_shift_per_day.py +++ b/tests/cp/constraints/test_max_one_shift_per_day.py @@ -3,9 +3,9 @@ from ortools.sat.python.cp_model import CpSolver, IntVar -from src.cp.constraints import MaxOneShiftPerDayConstraint -from src.cp.model import Model -from src.cp.variables import Variable +from legacy.src.cp.constraints import MaxOneShiftPerDayConstraint +from legacy.src.cp.model import Model +from legacy.src.cp.variables import Variable def find_max_one_shift_per_day_violations(assignment: dict[Variable, int], model: Model) -> list[dict[str, int]]: diff --git a/tests/cp/constraints/test_min_rest_time.py b/tests/cp/constraints/test_min_rest_time.py index bb78d491..cc1d9e05 100644 --- a/tests/cp/constraints/test_min_rest_time.py +++ b/tests/cp/constraints/test_min_rest_time.py @@ -4,10 +4,10 @@ from ortools.sat.python.cp_model import CpSolver, IntVar -from src.cp.constraints import MinRestTimeConstraint -from src.cp.model import Model -from src.cp.variables import Variable -from src.shift import Shift +from legacy.src.cp.constraints import MinRestTimeConstraint +from legacy.src.cp.model import Model +from legacy.src.cp.variables import Variable +from legacy.src.shift import Shift def find_min_rest_time_violations(assignment: dict[Variable, int], model: Model) -> list[dict[str, int]]: diff --git a/tests/cp/constraints/test_min_staffing.py b/tests/cp/constraints/test_min_staffing.py index 56225184..fb2087bb 100644 --- a/tests/cp/constraints/test_min_staffing.py +++ b/tests/cp/constraints/test_min_staffing.py @@ -3,10 +3,10 @@ from ortools.sat.python.cp_model import CpSolver, IntVar -from src.cp.constraints import MinStaffingConstraint -from src.cp.model import Model -from src.cp.variables import Variable -from src.shift import Shift +from legacy.src.cp.constraints import MinStaffingConstraint +from legacy.src.cp.model import Model +from legacy.src.cp.variables import Variable +from legacy.src.shift import Shift def find_min_staffing_violations( diff --git a/tests/cp/constraints/test_planned_shifts.py b/tests/cp/constraints/test_planned_shifts.py index e6947865..17bca06a 100644 --- a/tests/cp/constraints/test_planned_shifts.py +++ b/tests/cp/constraints/test_planned_shifts.py @@ -4,10 +4,10 @@ from ortools.sat.python.cp_model import CpSolver, IntVar -from src.cp.constraints import PlannedShiftsConstraint -from src.cp.model import Model -from src.cp.variables import Variable -from src.shift import Shift +from legacy.src.cp.constraints import PlannedShiftsConstraint +from legacy.src.cp.model import Model +from legacy.src.cp.variables import Variable +from legacy.src.shift import Shift def find_planned_shifts_violations(assignment: dict[Variable, int], model: Model) -> list[dict[str, int]]: diff --git a/tests/cp/constraints/test_rounds_in_early_shifts.py b/tests/cp/constraints/test_rounds_in_early_shifts.py index c5aad670..dbbece82 100644 --- a/tests/cp/constraints/test_rounds_in_early_shifts.py +++ b/tests/cp/constraints/test_rounds_in_early_shifts.py @@ -3,11 +3,11 @@ from ortools.sat.python.cp_model import CpSolver, IntVar -from src.cp.constraints import RoundsInEarlyShiftConstraint -from src.cp.model import Model -from src.cp.variables import Variable -from src.employee import Employee -from src.shift import Shift +from legacy.src.cp.constraints import RoundsInEarlyShiftConstraint +from legacy.src.cp.model import Model +from legacy.src.cp.variables import Variable +from legacy.src.employee import Employee +from legacy.src.shift import Shift def find_rounds_in_early_shifts_violations(assignment: dict[Variable, int], model: Model) -> list[dict[str, int]]: diff --git a/tests/cp/constraints/test_target_working_time.py b/tests/cp/constraints/test_target_working_time.py index f50f3454..40a8b377 100644 --- a/tests/cp/constraints/test_target_working_time.py +++ b/tests/cp/constraints/test_target_working_time.py @@ -3,9 +3,9 @@ from ortools.sat.python.cp_model import CpSolver, IntVar -from src.cp.constraints import TargetWorkingTimeConstraint -from src.cp.model import Model -from src.cp.variables import Variable +from legacy.src.cp.constraints import TargetWorkingTimeConstraint +from legacy.src.cp.model import Model +from legacy.src.cp.variables import Variable def find_target_working_time_violations( diff --git a/tests/cp/constraints/test_vaction_days_and_shifts.py b/tests/cp/constraints/test_vaction_days_and_shifts.py index 26af5073..af443ccd 100644 --- a/tests/cp/constraints/test_vaction_days_and_shifts.py +++ b/tests/cp/constraints/test_vaction_days_and_shifts.py @@ -4,10 +4,10 @@ from ortools.sat.python.cp_model import CpSolver, IntVar -from src.cp.constraints import VacationDaysAndShiftsConstraint -from src.cp.model import Model -from src.cp.variables import Variable -from src.shift import Shift +from legacy.src.cp.constraints import VacationDaysAndShiftsConstraint +from legacy.src.cp.model import Model +from legacy.src.cp.variables import Variable +from legacy.src.shift import Shift def find_vaction_days_and_shifts_violations(assignment: dict[Variable, int], model: Model) -> list[dict[str, int]]: diff --git a/tests/integration/helpers/smoke_fixtures.py b/tests/integration/helpers/smoke_fixtures.py index 54daa13a..edd8a5c6 100644 --- a/tests/integration/helpers/smoke_fixtures.py +++ b/tests/integration/helpers/smoke_fixtures.py @@ -1,8 +1,8 @@ from dataclasses import dataclass from datetime import date, timedelta -from src.employee import Employee -from src.shift import Shift +from legacy.src.employee import Employee +from legacy.src.shift import Shift type WeekdayAbbreviation = str type EmployeeLevel = str diff --git a/tests/integration/smoke_test.py b/tests/integration/smoke_test.py index 6b44c97f..7ded4f32 100644 --- a/tests/integration/smoke_test.py +++ b/tests/integration/smoke_test.py @@ -2,7 +2,7 @@ import pytest -from src.services.solve_service import execute_solve +from legacy.src.services.solve_service import execute_solve from tests.integration.helpers.smoke_fixtures import SMOKE_TEST_WEIGHTS, SmokeSolveFixture, make_smoke_solve_fixture diff --git a/uv.lock b/uv.lock index 9cad1580..12c3f3db 100644 --- a/uv.lock +++ b/uv.lock @@ -1,6 +1,11 @@ version = 1 revision = 3 requires-python = ">=3.12" +resolution-markers = [ + "python_full_version >= '3.14'", + "python_full_version == '3.13.*'", + "python_full_version < '3.13'", +] [[package]] name = "absl-py" @@ -254,6 +259,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload-time = "2023-10-07T05:32:16.783Z" }, ] +[[package]] +name = "detect-installer" +version = "0.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5f/ce/6897d812825e9d4c53e3c7112726e800cc5231b013b2223bf64f653ff362/detect_installer-0.1.0.tar.gz", hash = "sha256:00ad7ba0a36e3cf7d08a40d3643011746dbc112597c7d475cc91c416710ca4e7", size = 3049, upload-time = "2026-02-23T10:40:22.567Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/34/8cc73273414405086c58852916e4031812a6a30fe04c057e37ad99397b7f/detect_installer-0.1.0-py3-none-any.whl", hash = "sha256:034fb20fd665c36e6ba52b8821525ea07fb4f7f938cac459df889fb33801528a", size = 4539, upload-time = "2026-02-23T10:40:23.807Z" }, +] + [[package]] name = "distlib" version = "0.4.0" @@ -263,6 +277,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, ] +[[package]] +name = "dnspython" +version = "2.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251, upload-time = "2025-09-07T18:58:00.022Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094, upload-time = "2025-09-07T18:57:58.071Z" }, +] + [[package]] name = "dotenv" version = "0.9.9" @@ -274,9 +297,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b2/b7/545d2c10c1fc15e48653c91efde329a790f2eecfbbf2bd16003b5db2bab0/dotenv-0.9.9-py2.py3-none-any.whl", hash = "sha256:29cf74a087b31dafdb5a446b6d7e11cbce8ed2741540e2339c69fbef92c94ce9", size = 1892, upload-time = "2025-02-19T22:15:01.647Z" }, ] +[[package]] +name = "email-validator" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dnspython" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f5/22/900cb125c76b7aaa450ce02fd727f452243f2e91a61af068b40adba60ea9/email_validator-2.3.0.tar.gz", hash = "sha256:9fc05c37f2f6cf439ff414f8fc46d917929974a82244c20eb10231ba60c54426", size = 51238, upload-time = "2025-08-26T13:09:06.831Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", size = 35604, upload-time = "2025-08-26T13:09:05.858Z" }, +] + [[package]] name = "fastapi" -version = "0.135.1" +version = "0.137.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-doc" }, @@ -285,9 +321,134 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e7/7b/f8e0211e9380f7195ba3f3d40c292594fd81ba8ec4629e3854c353aaca45/fastapi-0.135.1.tar.gz", hash = "sha256:d04115b508d936d254cea545b7312ecaa58a7b3a0f84952535b4c9afae7668cd", size = 394962, upload-time = "2026-03-01T18:18:29.369Z" } +sdist = { url = "https://files.pythonhosted.org/packages/da/fe/fb25c287ff7e0f79fc6acf2e8b812725dad28d2a1446c0410bab1422ac90/fastapi-0.137.0.tar.gz", hash = "sha256:d0565d551f65a803ecff245390840867186f456ef98971f750724eed16e1541c", size = 408023, upload-time = "2026-06-14T12:51:30.672Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e4/72/42e900510195b23a56bde950d26a51f8b723846bfcaa0286e90287f0422b/fastapi-0.135.1-py3-none-any.whl", hash = "sha256:46e2fc5745924b7c840f71ddd277382af29ce1cdb7d5eab5bf697e3fb9999c9e", size = 116999, upload-time = "2026-03-01T18:18:30.831Z" }, + { url = "https://files.pythonhosted.org/packages/e7/f1/b38481428e50131e5345b535414d11d196f14990122fe69c9020c64e5683/fastapi-0.137.0-py3-none-any.whl", hash = "sha256:6dcbde8d464f92117c1accb9e42720f8e423fa9b86cb563b1f5862f785a06498", size = 121777, upload-time = "2026-06-14T12:51:29.067Z" }, +] + +[package.optional-dependencies] +standard = [ + { name = "email-validator" }, + { name = "fastapi-cli", extra = ["standard"] }, + { name = "fastar" }, + { name = "httpx" }, + { name = "jinja2" }, + { name = "pydantic-extra-types" }, + { name = "pydantic-settings" }, + { name = "python-multipart" }, + { name = "uvicorn", extra = ["standard"] }, +] + +[[package]] +name = "fastapi-cli" +version = "0.0.24" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "rich-toolkit" }, + { name = "typer" }, + { name = "uvicorn", extra = ["standard"] }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6e/58/74797ae9e4610cfa0c6b34c8309096d3b20bb29be3b8b5fbf1004d10fa5f/fastapi_cli-0.0.24.tar.gz", hash = "sha256:1afc9c9e21d7ebc8a3ca5e31790cd8d837742be7e4f8b9236e99cb3451f0de00", size = 19043, upload-time = "2026-02-24T10:45:10.476Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/4b/68f9fe268e535d79c76910519530026a4f994ce07189ac0dded45c6af825/fastapi_cli-0.0.24-py3-none-any.whl", hash = "sha256:4a1f78ed798f106b4fee85ca93b85d8fe33c0a3570f775964d37edb80b8f0edc", size = 12304, upload-time = "2026-02-24T10:45:09.552Z" }, +] + +[package.optional-dependencies] +standard = [ + { name = "fastapi-cloud-cli" }, + { name = "uvicorn", extra = ["standard"] }, +] + +[[package]] +name = "fastapi-cloud-cli" +version = "0.20.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "detect-installer" }, + { name = "fastar" }, + { name = "httpx" }, + { name = "pydantic", extra = ["email"] }, + { name = "rich-toolkit" }, + { name = "rignore" }, + { name = "sentry-sdk" }, + { name = "typer" }, + { name = "uvicorn", extra = ["standard"] }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ad/bf/97d19633c6ec6fb0ef59df474b9705ea992f7b4f879208d0007ac6d25ab6/fastapi_cloud_cli-0.20.0.tar.gz", hash = "sha256:9681c46adcd299024d0775658bd5d88992fd35c4ad42b1f045c6df913390ba37", size = 85904, upload-time = "2026-06-11T17:41:02.814Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/6e/bbb2e1b8f3170b6426b707d49981a838fc1d5cbb428dd9a271f1c3951c23/fastapi_cloud_cli-0.20.0-py3-none-any.whl", hash = "sha256:dcbf071fc659ae2d3fb30e221a661c3fa240b7d5091203cf941face31f6d7860", size = 68793, upload-time = "2026-06-11T17:41:01.804Z" }, +] + +[[package]] +name = "fastar" +version = "0.11.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/03/0f/0aeb3fc50046617702acc0078b277b58367fd62eb727b9ec733ae0e8bbcc/fastar-0.11.0.tar.gz", hash = "sha256:aa7f100f7313c03fdb20f1385927ba95671071ba308ad0c1763fef295e1895ce", size = 70238, upload-time = "2026-04-13T17:11:17.143Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/06/a5773706afc8bd496769786590bbc56d2d0ee419a299cc12ea3f5717fcf3/fastar-0.11.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3c51f1c2cdddbd1420d2897ace7738e36c65e17f6ae84e0bfe763f8d1068bb97", size = 708394, upload-time = "2026-04-13T17:09:57.269Z" }, + { url = "https://files.pythonhosted.org/packages/cc/a6/d5e2a4e48495616440a21eed07558219ca90243ad00b0502586f95bd4833/fastar-0.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0d9d6b052baf5380baea866675dab6ccd04ec2460d12b1c46f10ce3f4ee6a820", size = 628417, upload-time = "2026-04-13T17:09:42.145Z" }, + { url = "https://files.pythonhosted.org/packages/ab/69/9816d69ac8265c9e50456637a487ccfb7a9c566efd9dbcd673df9c2558c2/fastar-0.11.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:bd2f05666d4df7e14885b5c38fefd92a785917387513d33d837ff42ec143a22f", size = 863950, upload-time = "2026-04-13T17:09:11.506Z" }, + { url = "https://files.pythonhosted.org/packages/5b/0d/f88daad53aff2e754b6b5ff2a7113f72447a34f6ef17cc23ca99988117b7/fastar-0.11.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1e6e74aba1ae77ca4aedcaf1697cd413319f4c88a5ccbe5b42c709517c5097e", size = 760737, upload-time = "2026-04-13T17:07:55.958Z" }, + { url = "https://files.pythonhosted.org/packages/2f/a6/82ef4ecd969d50d92ed3ed9dbd8fe77faa24be5e5736f716edc9f4ce8d62/fastar-0.11.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:38ef77fe940bbc9b37a98bd838727f844b11731cd39358a2640ff864fb385086", size = 757603, upload-time = "2026-04-13T17:08:10.623Z" }, + { url = "https://files.pythonhosted.org/packages/03/35/50249f0d827251f8ac511495e2eacccebda80a00a0ad73e9615b8113b84f/fastar-0.11.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8955e61b32d6aff82c983217abf80933fd823b0e727586fc72f08043d996fd59", size = 923952, upload-time = "2026-04-13T17:08:25.526Z" }, + { url = "https://files.pythonhosted.org/packages/7b/d8/faee41659e9c379d906d24eaee6d6833ac8cfef0a5df480e5c2a8d3efb33/fastar-0.11.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:483532442cdb08fbff0169510224eae0836f2f672cea6aacb52847d90fefdc46", size = 816574, upload-time = "2026-04-13T17:08:56.076Z" }, + { url = "https://files.pythonhosted.org/packages/22/47/0448ea7992b997dad2bf004bfd98eca74b5858630eae080b50c7b17d9ddc/fastar-0.11.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef5a6071121e05d8287fc75bccb054bcbac8bb0501200a0c0a8feeace5303ea4", size = 819382, upload-time = "2026-04-13T17:09:26.66Z" }, + { url = "https://files.pythonhosted.org/packages/33/ef/0d63eb43586831b7a6f8b22c4d77125a7c594423af1f4f090fa9541b9b40/fastar-0.11.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:e45e598af5afe8412197d4786efd6cf29be02e7d3d4f6a3461149eae5d7e94f1", size = 885254, upload-time = "2026-04-13T17:08:40.9Z" }, + { url = "https://files.pythonhosted.org/packages/01/25/edd584675d69e49a165052c3ee886df1c5d574f3e7d813c990306387c623/fastar-0.11.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2e160919b1c47ddb8538e7e8eb4cd527281b40f0bf75110a75993838ef61f286", size = 971239, upload-time = "2026-04-13T17:10:12.997Z" }, + { url = "https://files.pythonhosted.org/packages/a5/37/e8bb24f506ba2b08fbaf36c5800e843bd4d542954e9331f00418e2d23349/fastar-0.11.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:4bb4dc0fc8f7a6807febcebce8a2f3626ba4955a9263d81ecc630aad83be84c0", size = 1035185, upload-time = "2026-04-13T17:10:30.207Z" }, + { url = "https://files.pythonhosted.org/packages/9a/bf/be753736296338149ee4cb3e92e2b5423d6ba17c7b951d15218fd7e99bbf/fastar-0.11.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4ec95af56aa173f6e320e1183001bf108ba59beaf13edd1fc8200648db203588", size = 1072191, upload-time = "2026-04-13T17:10:47.072Z" }, + { url = "https://files.pythonhosted.org/packages/d2/cd/a81c1aaafb5a22ce57c98ae22f39c89413ed53e4ee6e1b1444b0bd666a6c/fastar-0.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:136cf342735464091c39dc3708168f9fdeb9ebea40b1ead937c61afaf46143d9", size = 1028054, upload-time = "2026-04-13T17:11:04.293Z" }, + { url = "https://files.pythonhosted.org/packages/ec/88/1ce4eed3d70627c95f49ca017f6bbbf2ddcc4b0c601d293259de7689bc20/fastar-0.11.0-cp312-cp312-win32.whl", hash = "sha256:35f23c11b556cc4d3704587faacbc0037f7bdf6c4525cd1d09c70bda4b1c6809", size = 454198, upload-time = "2026-04-13T17:11:45.168Z" }, + { url = "https://files.pythonhosted.org/packages/8f/1d/26ce92f4331cd61a69840db9ca6115829805eec24f285481a854f578e917/fastar-0.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:920bc56c3c0b8a8ca492904941d1883c1c947c858cd93343356c29122a38f44c", size = 486697, upload-time = "2026-04-13T17:11:31.084Z" }, + { url = "https://files.pythonhosted.org/packages/ed/96/e6eda4480559c69b05d466e7b5ea9170e81fef3795a73e059959a3258319/fastar-0.11.0-cp312-cp312-win_arm64.whl", hash = "sha256:395248faf89e8a6bd5dc1fd544c8465113b627cb6d7c8b296796b60ebea33593", size = 462591, upload-time = "2026-04-13T17:11:20.577Z" }, + { url = "https://files.pythonhosted.org/packages/c9/d6/3be260037e86fb694e88d47f583bac3a0188c99cee1a6b257ac26cb6b53c/fastar-0.11.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:33f544b08b4541b678e53749b4552a44720d96761fb79c172b005b1089c443ed", size = 707975, upload-time = "2026-04-13T17:09:58.866Z" }, + { url = "https://files.pythonhosted.org/packages/e1/cd/7867aefb1784662554a335f2952c75a50f0c70585ed0d2210d6cc15e5627/fastar-0.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:91c1c792447e4a642745f347ff9847c52af39633071c57ee67ed53c157fc3506", size = 628460, upload-time = "2026-04-13T17:09:43.776Z" }, + { url = "https://files.pythonhosted.org/packages/e5/2b/d11d84bdd5e0e377771b955755771e3460b290da5809cb78c1b735ee2228/fastar-0.11.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:881247e6b6eaea59fc6569f9b61447aa6b9fc2ee864e048b4643d69c52745805", size = 863054, upload-time = "2026-04-13T17:09:13.048Z" }, + { url = "https://files.pythonhosted.org/packages/25/39/d3f428b318fa940b1b6e785b8d54fc895dfb5d5b945ef8d5442ffa904fb2/fastar-0.11.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:863b7929845c9fec92ef6c8d59579cf46af5136655e5342f8df5cebe46cab06c", size = 760247, upload-time = "2026-04-13T17:07:57.396Z" }, + { url = "https://files.pythonhosted.org/packages/9e/04/03949aee82aabb8ede06ac5a4a5579ffaf98a8fe59ce958494508ff15513/fastar-0.11.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:96b4a57df12bf3211662627a3ea29d62ecb314a2434a0d0843f9fc23e47536e5", size = 756512, upload-time = "2026-04-13T17:08:12.415Z" }, + { url = "https://files.pythonhosted.org/packages/3f/0c/2ca1ae0a3828ca51047962d932b80daca2522db73e8cb9d040cb6ebe28d5/fastar-0.11.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ceef1c2c4df7b7b8ebd3f5d718bbf457b9bbdf25ce0bd07870211ec4fbd9aff4", size = 922183, upload-time = "2026-04-13T17:08:27.187Z" }, + { url = "https://files.pythonhosted.org/packages/65/68/7fe808b1f73a68e686f25434f538c6dc10ef4dfb3db0ace22cd861744bf8/fastar-0.11.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8e545918441910a779659d4759ad0eef349e935fbdb4668a666d3681567eb05", size = 816394, upload-time = "2026-04-13T17:08:57.657Z" }, + { url = "https://files.pythonhosted.org/packages/1f/17/07d086080f8a83b8d7966955e29bcdbd6a060f5bd949dc9d5abd3658cead/fastar-0.11.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28095bb8f821e85fc2764e1a55f03e5e2876dee2abe7cd0ee9420d929905d643", size = 818983, upload-time = "2026-04-13T17:09:28.46Z" }, + { url = "https://files.pythonhosted.org/packages/fb/e2/2c4edf0910af2e814ff6d65b77a91196d472ca8a9fb2033bd983f6856caa/fastar-0.11.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:0fafb95ecbe70f666a5e9b35dd63974ccdc9bb3d99ccdbd4014a823ec3e659b5", size = 884689, upload-time = "2026-04-13T17:08:42.763Z" }, + { url = "https://files.pythonhosted.org/packages/fa/ba/04fdcbd6558e60de4ced3b55230fac47675d181252582b2fcec3c74608e5/fastar-0.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:af48fed039b94016629dcdad1c95c90c486326dd068de2b0a4df419ee09b6821", size = 970677, upload-time = "2026-04-13T17:10:15.124Z" }, + { url = "https://files.pythonhosted.org/packages/df/b3/2b860a9658550167dbd5824c85e88d0b4b912bf493e42a6322544d6e483d/fastar-0.11.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:74cd96163f39b8638ab4e8d49708ca887959672a22871d8170d01f067319533b", size = 1034026, upload-time = "2026-04-13T17:10:32.318Z" }, + { url = "https://files.pythonhosted.org/packages/b7/9b/fa42ea1188b144bac4b1b60753dfd449974a4d5eda132029ee7711569f94/fastar-0.11.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4e8b993cb5613bab495ed482810bedc0986633fcb9a3b55c37ec88e0d6714f6a", size = 1071147, upload-time = "2026-04-13T17:10:48.833Z" }, + { url = "https://files.pythonhosted.org/packages/95/c8/d2e501556dca9f1fbc9246111a31792fb49ad908fa4927f34938a97a3604/fastar-0.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dfe39d91fc28e37e06162d94afe01050220edb7df554acb5b702b5503e564816", size = 1028377, upload-time = "2026-04-13T17:11:06.374Z" }, + { url = "https://files.pythonhosted.org/packages/db/33/5f11f23eca0a569cd052507bc45dda2e5468697f8665728d25be44120f7d/fastar-0.11.0-cp313-cp313-win32.whl", hash = "sha256:c5f63d4d99ff4bfb37c659982ec413358bdee747005348756cc50a04d412d989", size = 454089, upload-time = "2026-04-13T17:11:46.821Z" }, + { url = "https://files.pythonhosted.org/packages/da/2f/35ff03c939cba7a255a9132367873fec6c355fd06a7f84fedcbaf4c8129f/fastar-0.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:8690ed1928d31ded3ada308e1086525fb3871f5fa81e1b69601a3f7774004583", size = 486312, upload-time = "2026-04-13T17:11:32.86Z" }, + { url = "https://files.pythonhosted.org/packages/ef/71/ee9246cbfcbfd4144558f35e7e9a306ffe0a7564730a5188c45f21d2dab8/fastar-0.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:d977ded9d98a0719a305e0a4d5ee811f1d3e856d853a50acb8ae833c3cd6d5d2", size = 461975, upload-time = "2026-04-13T17:11:22.589Z" }, + { url = "https://files.pythonhosted.org/packages/7a/cd/3644c48ecac456f928c12d47ec3bed36c36555b17c3859856f1ff860265d/fastar-0.11.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:71375bd6f03c2a43eb47bd949ea38ff45434917f9cdac79675c5b9f60de4fa73", size = 707860, upload-time = "2026-04-13T17:10:00.371Z" }, + { url = "https://files.pythonhosted.org/packages/69/ca/dee04476ae3626b2b040a60ad84628f77e1ffd8444232f2426b0ca1e0d7e/fastar-0.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:eddfd9cab16e19ae247fe44bf992cb403ccfe27d3931d6de29a4695d95ad386c", size = 628216, upload-time = "2026-04-13T17:09:45.355Z" }, + { url = "https://files.pythonhosted.org/packages/dc/5e/9395c7353d079cb4f5be0f7982ce0dc9f2e7dec5fd175eef466729d6023a/fastar-0.11.0-cp314-cp314-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:7c371f1d4386c699018bb64eb2fa785feacf32785559049d2bb72fe4af023f53", size = 864378, upload-time = "2026-04-13T17:09:14.611Z" }, + { url = "https://files.pythonhosted.org/packages/fa/ba/1e4f67148223ff219612b6281a6000357abbcc2417964fa5c83f11d68fce/fastar-0.11.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cad7fa41e3e66554387481c1a09365e4638becd322904932674159d5f4046728", size = 760921, upload-time = "2026-04-13T17:07:59.138Z" }, + { url = "https://files.pythonhosted.org/packages/0f/82/09d11fb6d12f17993ffaf32ffd30c3c121a11e2966e84f19fb6f66430118/fastar-0.11.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cf36652fa71b83761717c9899b98732498f8a2cb6327ff16bbf07f6be85c3437", size = 757012, upload-time = "2026-04-13T17:08:14.186Z" }, + { url = "https://files.pythonhosted.org/packages/52/1f/5aeeacc4cb65615e2c9292cd9c5b0cd6fb6d2e6ee472ca6adc6c1b1b22ef/fastar-0.11.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f68ff8c17833053da4841720e95edde80ce45bb994b6b7d51418dddaac70ee47", size = 924510, upload-time = "2026-04-13T17:08:28.741Z" }, + { url = "https://files.pythonhosted.org/packages/bb/1a/1e5bdabbeaf2e856928956292609f2ff6a650f94480fb8afaca30229e483/fastar-0.11.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4563ed37a12ea1cdc398af8571258d24b988bf342b7b3bf5451bd5891243280c", size = 816602, upload-time = "2026-04-13T17:08:59.461Z" }, + { url = "https://files.pythonhosted.org/packages/87/24/f960147910da3bed41a3adfcb026e17d5f50f4cf467a3324237a7088f61a/fastar-0.11.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cee63c9875cba3b70dc44338c560facc5d6e763047dcc4a30501f9a68cf5f890", size = 819452, upload-time = "2026-04-13T17:09:29.926Z" }, + { url = "https://files.pythonhosted.org/packages/cc/f4/3e77d7901d5707fd7f8a352e153c8ae09ea974e6fabad0b7c4eb9944b8d4/fastar-0.11.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:bd76bfffae6d0a91f4ac4a612f721e7aec108db97dccdd120ae063cd66959f27", size = 885254, upload-time = "2026-04-13T17:08:44.285Z" }, + { url = "https://files.pythonhosted.org/packages/47/01/1585edd5ec47782ae93cd94edf05828e0ab02ef00aec00aea4194a600464/fastar-0.11.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8f5b707501ec01c1bc0518f741f01d322e50c9adc19a451aa24f67a2316e9397", size = 971496, upload-time = "2026-04-13T17:10:17.024Z" }, + { url = "https://files.pythonhosted.org/packages/f1/e9/6874c9d1236ded565a0bed54b320ac9f165f287b1d89490fb70f9f323c81/fastar-0.11.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:37c0b5a88a657839aad98b0a6c9e4ac4c2c15d6b49c44ee3935c6b08e9d3e479", size = 1034685, upload-time = "2026-04-13T17:10:34.063Z" }, + { url = "https://files.pythonhosted.org/packages/14/d8/4ab20613ce2983427aee958e39be878dba874aa227c530a845e32429c4f6/fastar-0.11.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:6c55f536c62a6efb180c1af0d5182948bff576bbfe6276e8e1359c9c7d2215d8", size = 1072675, upload-time = "2026-04-13T17:10:50.53Z" }, + { url = "https://files.pythonhosted.org/packages/1f/ae/5ac3b7c20ce4b08f011dd2b979f96caabe64f9b10b157f211ea91bdfadca/fastar-0.11.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3082eeca59e189b9039335862f4c2780c0c8871d656bfdf559db4414a105b251", size = 1029330, upload-time = "2026-04-13T17:11:08.138Z" }, + { url = "https://files.pythonhosted.org/packages/8a/e7/37cd6a1d4e288292170b64e19d79ecce2a7de8bb76790323399a2abc4619/fastar-0.11.0-cp314-cp314-win32.whl", hash = "sha256:b201a0a4e29f9fec2a177e13154b8725ec65ab9f83bd6415483efaa2aa18344b", size = 453940, upload-time = "2026-04-13T17:11:48.713Z" }, + { url = "https://files.pythonhosted.org/packages/ff/1c/795c878b1ee29d79021cf8ed81f18f2b25ccde58453b0d34b9bdc7e025ea/fastar-0.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:868fddb26072a43e870a8819134b9f80ee602931be5a76e6fb873e04da343637", size = 486334, upload-time = "2026-04-13T17:11:34.882Z" }, + { url = "https://files.pythonhosted.org/packages/ff/a4/113f104301df8bddcc0b3775b611a30cb7610baa3add933c7ccac9386467/fastar-0.11.0-cp314-cp314-win_arm64.whl", hash = "sha256:3db39c9cc42abb0c780a26b299f24dfbc8be455985e969e15336d70d7b2f833b", size = 461534, upload-time = "2026-04-13T17:11:24.329Z" }, + { url = "https://files.pythonhosted.org/packages/5a/a6/5c5f2c2c8e0c63e56a5636ebc7721589c889e94c0092cec7eb28ae7207e6/fastar-0.11.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:49c3299dec5e125e7ebaa27545714da9c7391777366015427e0ae62d548b442b", size = 707156, upload-time = "2026-04-13T17:10:02.176Z" }, + { url = "https://files.pythonhosted.org/packages/df/f7/982c01b61f0fc135ad2b16d01e6d0ee53cf8791e68827f5f7c5a65b2e5b1/fastar-0.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3328ed1ed56d31f5198350b17dd60449b8d6b9d47abb4688bab6aef4450a165b", size = 627032, upload-time = "2026-04-13T17:09:46.978Z" }, + { url = "https://files.pythonhosted.org/packages/2b/c3/38f1dac77ae0c71c37b176277c96d830796b8ce2fe69705f917829b53829/fastar-0.11.0-cp314-cp314t-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:bd3eca3bbfec84a614bcb4143b4ad4f784d0895babc26cfc88436af88ca23c7a", size = 864403, upload-time = "2026-04-13T17:09:16.58Z" }, + { url = "https://files.pythonhosted.org/packages/6e/f0/e69c363bdb3e5a5848e937b662b5469581ee6682c51bc1c0556494773929/fastar-0.11.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ff86a967acb0d621dd24063dda090daa67bf4993b9570e97fe156de88a9006ca", size = 759480, upload-time = "2026-04-13T17:08:00.599Z" }, + { url = "https://files.pythonhosted.org/packages/3b/29/4d8737590c2a6357d614d7cc7288e8f68e7e449680b8922997cc4349e65e/fastar-0.11.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:86eaf7c0e985d93a7734168be2fb232b2a8cca53e41431c2782d7c12b12c03b1", size = 756219, upload-time = "2026-04-13T17:08:15.699Z" }, + { url = "https://files.pythonhosted.org/packages/bb/ec/400de7b3b7d48801908f19cf5462177104395799472671b3e8152b2b04ca/fastar-0.11.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91f07b0b8eb67e2f177733a1f884edad7dfb9f8977ffef15927b20cb9604027d", size = 923669, upload-time = "2026-04-13T17:08:30.574Z" }, + { url = "https://files.pythonhosted.org/packages/5d/01/8926c53da923fed7ab4b96e7fbf7f73b663beb4f02095b654d6fab46f9ad/fastar-0.11.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f85c896885eb4abf1a635d54dea22cac6ae48d04fc2ea26ae652fcf1febe1220", size = 815729, upload-time = "2026-04-13T17:09:01.204Z" }, + { url = "https://files.pythonhosted.org/packages/89/f0/5fef4c7946e352651b504b1a4235dac3505e7cfd24020788ab50552e84bf/fastar-0.11.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:075c07095c8de4b774ba8f28b9c0a02b1a2cd254da50cbe464dd3bb2432e9158", size = 819812, upload-time = "2026-04-13T17:09:31.907Z" }, + { url = "https://files.pythonhosted.org/packages/b3/c8/0ebc3298b4a45e7bddc50b169ae6a6f5b80c939394d4befe6e60de535ee7/fastar-0.11.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:07f028933820c65750baf3383b807ecce1cd9385cf00ce192b79d263ad6b856c", size = 884074, upload-time = "2026-04-13T17:08:45.802Z" }, + { url = "https://files.pythonhosted.org/packages/ae/9f/7baa4cdff8d6fbca41fa5c764b48a941fed8a9ec6c4cc92de65895a28299/fastar-0.11.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:039f875efa0f01fa43c20bf4e2fc7305489c61d0ac76eda991acfba7820a0e63", size = 969450, upload-time = "2026-04-13T17:10:18.667Z" }, + { url = "https://files.pythonhosted.org/packages/d4/dc/1ebbfb58a47056ba866494f19efbcdd2ba2897096b94f36e796594b4d05b/fastar-0.11.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:fff12452a9a5c6814a012445f26365541cc3d99dcca61f09762e6a389f7a32ea", size = 1033775, upload-time = "2026-04-13T17:10:36.165Z" }, + { url = "https://files.pythonhosted.org/packages/c2/5f/ce4e3914066f08c99eb8c32952cc07c1a013e81b1db1b0f598130bf6b974/fastar-0.11.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:2bf733e09f942b6fa876efe30a90508d1f4caef5630c00fb2a84fba355873712", size = 1072158, upload-time = "2026-04-13T17:10:52.497Z" }, + { url = "https://files.pythonhosted.org/packages/03/2a/6bca72992c84151c387cc6558f3867f5ebe5fb3684ee6fa9b76280ba4b8e/fastar-0.11.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d1531fa848fdd3677d2dce0a4b436ea64d9ae38fb8babe2ddbc180dd153cb7a3", size = 1028577, upload-time = "2026-04-13T17:11:09.934Z" }, + { url = "https://files.pythonhosted.org/packages/83/18/7a7c15657a3da5569b26fc51cde6a80f8d84cb54b3b1aea6d74a103db4ad/fastar-0.11.0-cp314-cp314t-win32.whl", hash = "sha256:5744551bc67c6fc6581cbd0e34a0fd6e2cd0bd30b43e94b1c3119cf35064b162", size = 453601, upload-time = "2026-04-13T17:11:53.726Z" }, + { url = "https://files.pythonhosted.org/packages/6d/d8/331b59a6de279f3ad75c10c02c40a12f21d64a437d9c3d6f1af2dcbd7a76/fastar-0.11.0-cp314-cp314t-win_amd64.whl", hash = "sha256:f4ce44e3b56c47cf38244b98d29f269b259740a580c47a2552efa5b96a5458fb", size = 486436, upload-time = "2026-04-13T17:11:40.089Z" }, + { url = "https://files.pythonhosted.org/packages/6b/fd/5390ec4f49100f3ecb9968a392f9e6d039f1e3fe0ecd28443716ff01e589/fastar-0.11.0-cp314-cp314t-win_arm64.whl", hash = "sha256:76c1359314355eafbc6989f20fb1ad565a3d10200117923b9da765a17e2f6f11", size = 461049, upload-time = "2026-04-13T17:11:25.918Z" }, ] [[package]] @@ -426,6 +587,70 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, ] +[[package]] +name = "httpcore" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, +] + +[[package]] +name = "httptools" +version = "0.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/e5/d471fcb0e14523fe1c3f4ba58ca52480e7bd70ad7109a3846bc75892f7fb/httptools-0.8.0.tar.gz", hash = "sha256:6b2a32f18d97e16e90827d7a819ffa8dbd8cc245fc4e1fa9d1095b54ef4bd999", size = 271342, upload-time = "2026-05-25T22:17:48.841Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/88/1d21a36da8f5cb0fa49eafd4b169eba5608d57e75bbcf61845cbc6243216/httptools-0.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:880490234c10f70a9830743097e8958d6e4b9f5a0ffc24515023afeef984054d", size = 208247, upload-time = "2026-05-25T22:17:07.843Z" }, + { url = "https://files.pythonhosted.org/packages/a5/42/cc4feea2945cb3051038f090c9b36bd5b8a9d7f5a894a506a8983e33fd1c/httptools-0.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5931891fb7b441b8a3853cf1b85c82c903defce084dd5f6771ca46e31bf862c5", size = 113064, upload-time = "2026-05-25T22:17:09.136Z" }, + { url = "https://files.pythonhosted.org/packages/e3/a6/febbb8b8db0f58b38e44ad6cb946e6a255ae49b55f2e8543408fb7501ccd/httptools-0.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b15fc622b0f869d19207c4089a501d9bcc63ca5e071ffdd2f03f922df882dcb2", size = 523851, upload-time = "2026-05-25T22:17:10.106Z" }, + { url = "https://files.pythonhosted.org/packages/b7/e4/f90a0df0b83beff265b7e3b65f2a4cefd95792d4be0ac3e16049f2acd3c2/httptools-0.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:425f83884fd6343828d8c565f046cb72b6d19063f6924093e11bcd8e1548cd09", size = 518842, upload-time = "2026-05-25T22:17:11.218Z" }, + { url = "https://files.pythonhosted.org/packages/9e/2d/0c9ac76dd2c893841fbf6498d6acec4f2442e1b7067f6e3e316a80e494e8/httptools-0.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ef7c3c97f4311c7be57e2986629df89d49cb434dbff78eafcd48c2bff986b15a", size = 501238, upload-time = "2026-05-25T22:17:12.728Z" }, + { url = "https://files.pythonhosted.org/packages/ca/42/906adc91ae3a5fa9c59c0a2f21c139725bd7e5b41ae6acd485cd14123ebf/httptools-0.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a1afd7c9fbff0d9f5d489c4ce2768bd09c84a46ddefc7161e6aa82ae35c85745", size = 509567, upload-time = "2026-05-25T22:17:13.842Z" }, + { url = "https://files.pythonhosted.org/packages/05/0b/4240efeb672751ee5b9b380cb0e3fdc050bc05f68adc7a8aefc4fcd9a69a/httptools-0.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:cd96f29b4bab1d42fa6e3d008711c75e0f79e94e06827330160e3a304227f150", size = 90918, upload-time = "2026-05-25T22:17:15.155Z" }, + { url = "https://files.pythonhosted.org/packages/5e/e5/8cfcabc5546e8022f168be28bcdaa128a240a0befdd03b59d558b4f18bd6/httptools-0.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:614ceea8ea606848bece2338ac03b3ce5324bcb4be8dc7d377ed708012fa4db8", size = 205148, upload-time = "2026-05-25T22:17:16.333Z" }, + { url = "https://files.pythonhosted.org/packages/2a/0e/0fb14848c19a686c8062ff9067c1a48793e3224b47bc5b201535b6036fce/httptools-0.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2d689918c15a013c65ef52d9fd495d766893ab831a2c8d89f2ac5940a5df847c", size = 111368, upload-time = "2026-05-25T22:17:17.586Z" }, + { url = "https://files.pythonhosted.org/packages/2e/1b/46f1cecf06b9bbde8e4b8c88034ac7908989e5ff7a3a388ef38392949c1f/httptools-0.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:eb3028cca2fc0a6d720e52ef61d8ebb62fcbfeb1de56874546d858d3f25a26b7", size = 486447, upload-time = "2026-05-25T22:17:18.564Z" }, + { url = "https://files.pythonhosted.org/packages/77/00/258bfc0837221f81d9725c45f9b948a6a6b2994a147a4fb66e85100c668f/httptools-0.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:88bdd940f2b5d487b4d032c6afa5489a7dc4694410d43de3c38c4fb3af0dc45d", size = 482448, upload-time = "2026-05-25T22:17:19.912Z" }, + { url = "https://files.pythonhosted.org/packages/04/ab/d1cef3b5523f4d272a70f42a776c3169a2dddfe3a54de4b2ce4a36341528/httptools-0.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6a43c9dd399758ccc0531acb0a3c4a6c299ee893ee9400e9c893b7bdcfae0681", size = 464460, upload-time = "2026-05-25T22:17:20.882Z" }, + { url = "https://files.pythonhosted.org/packages/ce/48/5d1d072442277bb2b3434e0e60690b8e8c23840ef7de8b6ea54040a536d3/httptools-0.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0770728beb05094c809b98e814edff5fef69d26ad7d21185f2f6d5884a0ba683", size = 471312, upload-time = "2026-05-25T22:17:22.085Z" }, + { url = "https://files.pythonhosted.org/packages/0d/66/b96623b27e51a68199ef4efdda0613cced9233fe3062ac74e50749c5ad37/httptools-0.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:7685df791fad561384bfb139e77fde27a1ffd93134e016f95a0db424ffbf77b1", size = 90117, upload-time = "2026-05-25T22:17:23.074Z" }, + { url = "https://files.pythonhosted.org/packages/1a/12/fa3fbf5f9517b273edea2dc982aa82a8c634091e67c590792b729017bc6f/httptools-0.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:de242a49b5d18e0a8776e654e9f6bf6d89f3875a5c35b425a0e7ce940feb3fd6", size = 206183, upload-time = "2026-05-25T22:17:24.004Z" }, + { url = "https://files.pythonhosted.org/packages/30/fc/5e7c4cb443370f2090a3aba0453a07384d29ff66b7435bb90e77e1037599/httptools-0.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:159e9ab5f701ccd42e555a12f1ad8ff69702910fc1c996cf2bb66e5fcb7a231b", size = 112079, upload-time = "2026-05-25T22:17:25.216Z" }, + { url = "https://files.pythonhosted.org/packages/ba/53/771bd891eb0f236f32145d6a1775777ec85745f3cc983a1f23d1a3b8ddfe/httptools-0.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c4a9f1707e4823d54dfec6c33fa3697d302aed536ed352a7ebb5a061ddb869d0", size = 481596, upload-time = "2026-05-25T22:17:26.186Z" }, + { url = "https://files.pythonhosted.org/packages/62/42/94e15bc68ce3d423243c45d7f1b0c7561f13844f97dc52ae23182fb65628/httptools-0.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d76ad7b951387e3632c8716a9bb03ac5b45c5f16119aa409db0459520887944e", size = 480865, upload-time = "2026-05-25T22:17:27.542Z" }, + { url = "https://files.pythonhosted.org/packages/1c/7c/fe2980fc03723272e30f135b62360b075f513dfe7cc73aef36c7f04012bd/httptools-0.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a3b7387147361c3fd47a0bde763c5c91b5b4cd4dc9989b8ece84ff436c99843b", size = 463189, upload-time = "2026-05-25T22:17:28.546Z" }, + { url = "https://files.pythonhosted.org/packages/15/1b/47fc5fff68acd1bfa20b4734059c9a06cadb88119dcd5258b5b0d21d91c8/httptools-0.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f256d6ce930c52ca1cb2a960b7da03548c454e7d28b06059ad41bfe789036ce0", size = 466610, upload-time = "2026-05-25T22:17:29.816Z" }, + { url = "https://files.pythonhosted.org/packages/60/bd/07b13c93ffd9bec9546e0d43f8e19378dd696dbd278511406bc07371ef1f/httptools-0.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:19d1ee275bb59ba2643ba9a3a1e51cc0c788caf2b8df506368e03f56fdd08527", size = 92705, upload-time = "2026-05-25T22:17:31.133Z" }, + { url = "https://files.pythonhosted.org/packages/fd/c4/121648f68ce066d7bd762d6b6d97e620847642d38d54f3d90ff11d947629/httptools-0.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:de1ed58a974e75d56560acc7e7fed01a454994429456f65209789992e41f2568", size = 215023, upload-time = "2026-05-25T22:17:32.401Z" }, + { url = "https://files.pythonhosted.org/packages/b9/b0/312a062ae741ae3e8baa8c8bf20be81b2e67337b259ab4349bebc7b6142e/httptools-0.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e93c227b595c6926c1acee96891dd9da4be338cfbe82e5cd3bb9d8dd7dc4ac0b", size = 117405, upload-time = "2026-05-25T22:17:33.742Z" }, + { url = "https://files.pythonhosted.org/packages/fc/37/fccd705f795386bb05bf413012fecff2a33e5aa8c2f069096de3e9fd8702/httptools-0.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2a021c3a8e65cc125390d72f59b968afca3bdcaff25bd67965e0a055a14946ca", size = 558497, upload-time = "2026-05-25T22:17:34.732Z" }, + { url = "https://files.pythonhosted.org/packages/bd/39/f172e8003576de35f5ba77ff417cf0e34429d35dc014deef15afa337a72c/httptools-0.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48774d39cbb70e2b1f71f88852a3087ae1d3a1eb80482bb48c13067ab080c14f", size = 571585, upload-time = "2026-05-25T22:17:35.813Z" }, + { url = "https://files.pythonhosted.org/packages/3e/b9/f5564760af99f3dbbf3f9104dc00e5da27e96cf433c6bdcf77617f70bf3f/httptools-0.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:88eead8ec8680a9f146c655bc88445a325bd7921cfd8194c7337e9467282427d", size = 543297, upload-time = "2026-05-25T22:17:37.08Z" }, + { url = "https://files.pythonhosted.org/packages/99/67/8d9f2c313618e161b82f3873188e7196126da1d6e29688df40eb3997c77a/httptools-0.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2c032fa028f46871ec7e1fc59fc15e8023eab3e6bbe6ece786a1611719a5d081", size = 539535, upload-time = "2026-05-25T22:17:38.032Z" }, + { url = "https://files.pythonhosted.org/packages/48/63/b906c01e53f50d432c0defe43ce52764a111dc1bdd028bafbeb54dcfd008/httptools-0.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:384c17174464c8e873398b7af24f0b1f44d992c820328413951a625323155d77", size = 108209, upload-time = "2026-05-25T22:17:39.473Z" }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, +] + [[package]] name = "identify" version = "2.6.15" @@ -564,6 +789,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/70/81/54e3ce63502cd085a0c556652a4e1b919c45a446bd1e5300e10c44c8c521/markdown-3.10-py3-none-any.whl", hash = "sha256:b5b99d6951e2e4948d939255596523444c0e677c669700b1d17aa4a8a464cb7c", size = 107678, upload-time = "2025-11-03T19:51:13.887Z" }, ] +[[package]] +name = "markdown-it-py" +version = "4.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/ff/7841249c247aa650a76b9ee4bbaeae59370dc8bfd2f6c01f3630c35eb134/markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49", size = 82454, upload-time = "2026-05-07T12:08:28.36Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl", hash = "sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a", size = 91687, upload-time = "2026-05-07T12:08:27.182Z" }, +] + [[package]] name = "markupsafe" version = "3.0.3" @@ -681,6 +918,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/04/5f/e22e08da14bc1a0894184640d47819d2338b792732e20d292bf86e5ab785/matplotlib-3.10.7-cp314-cp314t-win_arm64.whl", hash = "sha256:cb783436e47fcf82064baca52ce748af71725d0352e1d31564cbe9c95df92b9c", size = 8172585, upload-time = "2025-10-09T00:27:47.185Z" }, ] +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, +] + [[package]] name = "mergedeep" version = "1.3.4" @@ -891,7 +1137,7 @@ wheels = [ [[package]] name = "ortools" -version = "9.14.6206" +version = "9.15.6755" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "absl-py" }, @@ -902,24 +1148,25 @@ dependencies = [ { name = "typing-extensions" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/72/2e/b0d7e7ee9d1a45001a05ca2f65ccc1ab28cee510e725e01e248496510ac1/ortools-9.14.6206-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:e38c8c4a184820cbfdb812a8d484f6506cf16993ce2a95c88bc1c9d23b17c63e", size = 22239884, upload-time = "2025-06-19T15:45:51.774Z" }, - { url = "https://files.pythonhosted.org/packages/31/ad/964ee5341767dd9e2f13e76f0a36d45aa8d81ad776c80bdd6dedc8f2f462/ortools-9.14.6206-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:db685073cbed9f8bfaa744f5e883f3dea57c93179b0abe1788276fd3b074fa61", size = 20211128, upload-time = "2025-06-19T15:45:54.829Z" }, - { url = "https://files.pythonhosted.org/packages/7c/93/d94a66cdfadeb2747d96f1c8d3f590d81c4ad47fd357dfc57de8d7a75bbe/ortools-9.14.6206-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4bfb8bffb29991834cf4bde7048ca8ee8caed73e8dd21e5ec7de99a33bbfea0", size = 25663650, upload-time = "2025-06-19T15:47:20.207Z" }, - { url = "https://files.pythonhosted.org/packages/eb/74/1d374bf510e9fb36bba82ecd3e09461cd8394afef3e418fa5b060f129401/ortools-9.14.6206-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eb464a698837e7f90ca5f9b3d748b6ddf553198a70032bc77824d1cd88695d2b", size = 27670004, upload-time = "2025-06-19T15:47:23.111Z" }, - { url = "https://files.pythonhosted.org/packages/e9/e3/5943467ae41efa06cff12b79bfd146c4a54903345f0cc5c896884829d14a/ortools-9.14.6206-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8f33deaeb7c3dda8ca1d29c5b9aa9c3a4f2ca9ecf34f12a1f809bb2995f41274", size = 27653302, upload-time = "2025-06-19T15:53:47.615Z" }, - { url = "https://files.pythonhosted.org/packages/6c/28/a896080fe6e13b4bdae74601f19c28b7ba05ec45f3adca3e992d6174ac57/ortools-9.14.6206-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:086e7c2dc4f23efffb20a5e20f618c7d6adb99b2d94f684cab482387da3bc434", size = 29503497, upload-time = "2025-06-19T15:53:51.174Z" }, - { url = "https://files.pythonhosted.org/packages/38/ad/8d02013116cc5d51084fe24c5b73f5b349dd10925d2927d19d21a2068a4c/ortools-9.14.6206-cp312-cp312-win_amd64.whl", hash = "sha256:17c13b0bfde17ac57789ad35243edf1318ecd5db23cf949b75ab62480599f188", size = 20512174, upload-time = "2025-06-19T15:46:33.339Z" }, - { url = "https://files.pythonhosted.org/packages/d8/9c/fe6d5ca549c2fb5ed3e8e1d928a11f4509c024c974e1f6e2d75d206bc226/ortools-9.14.6206-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:8d0df7eef8ba53ad235e29018389259bad2e667d9594b9c2a412ed6a5756bd4e", size = 22259719, upload-time = "2025-06-19T15:45:57.504Z" }, - { url = "https://files.pythonhosted.org/packages/19/0f/0b9858e7d5b31dfb23a1fe945d3e9fd5d2feba893190e53f70374ac7e084/ortools-9.14.6206-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:57dfe10844ce8331634d4723040fe249263fd490407346efc314c0bc656849b5", size = 20231092, upload-time = "2025-06-19T15:46:00.003Z" }, - { url = "https://files.pythonhosted.org/packages/6b/48/de4eff68cb4ea124c9d6edf68049d329a332aab36d011c2798ea1afb4c98/ortools-9.14.6206-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5c0c2c00a6e5d5c462e76fdda7dbd40d0f9139f1df4211d34b36906696248020", size = 25662903, upload-time = "2025-06-19T15:47:26.368Z" }, - { url = "https://files.pythonhosted.org/packages/c9/44/970f72cd5e537fc10e20582dadbdd2f8b2b3c802ac812105c40119572ea7/ortools-9.14.6206-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:38044cf39952d93cbcc02f6acdbe0a9bd3628fbf17f0d7eb0374060fa028c22e", size = 27669250, upload-time = "2025-06-19T15:47:29.738Z" }, - { url = "https://files.pythonhosted.org/packages/a4/31/37e2eacc37e4448090f693d482316a67bf2b47f2aefae6d6236b4ed41924/ortools-9.14.6206-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:98564de773d709e1e49cb3c32f6917589c314f047786d88bd5f324c0eb7be96e", size = 27652453, upload-time = "2025-06-19T15:53:54.742Z" }, - { url = "https://files.pythonhosted.org/packages/f1/8f/0b1bb2a3268114b95381d5a1146c293259d6e45c69e4e3f2d2cf52617796/ortools-9.14.6206-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:80528b0ac72dc3de00cbeef2ce028517a476450b5877b1cda1b8ecb9fa98505e", size = 29503368, upload-time = "2025-06-19T15:53:57.989Z" }, - { url = "https://files.pythonhosted.org/packages/d9/ad/93c05084eec710c38bc952a19002aac31d4c26bb6f8fcbf1057ccc55254b/ortools-9.14.6206-cp313-cp313-win_amd64.whl", hash = "sha256:47b1b15dcb085d32c61621b790259193aefa9e4577abadf233d47fbe7d0b81ef", size = 20530387, upload-time = "2025-06-19T15:46:35.964Z" }, - { url = "https://files.pythonhosted.org/packages/51/85/aeba0d90a3cee4a23ba74a4f2d4c27088923c8b77fa1b73a52ad34593be5/ortools-9.14.6206-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d26a0f9ed97ef9d3384a9069923585f5f974c3fde555a41f4d6381fbe7840bc4", size = 25704462, upload-time = "2025-06-19T15:47:33.064Z" }, - { url = "https://files.pythonhosted.org/packages/98/83/2954b6aa032b77f49a478e0d9682ae8176abfb4bd33d935f0260ecb1741b/ortools-9.14.6206-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d40d8141667d47405f296a9f687058c566d7816586e9a672b59e9fcec8493133", size = 27687594, upload-time = "2025-06-19T15:47:36.241Z" }, - { url = "https://files.pythonhosted.org/packages/b9/9a/9e9866952841e9f4aadccb110c0012d6a76f01b8798432fac0971d60aac1/ortools-9.14.6206-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:aefea81ed81aa937873efc520381785ed65380e52917f492ab566f46bbb5660d", size = 27698487, upload-time = "2025-06-19T15:54:01.03Z" }, - { url = "https://files.pythonhosted.org/packages/91/a5/97a86cdd52c961273981e9ab905a301b7f086593b597d8974c411decf61a/ortools-9.14.6206-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f044bb277db3ab6a1b958728fe1cf14ca87c3800d67d7b321d876b48269340f6", size = 29519050, upload-time = "2025-06-19T15:54:04.147Z" }, + { url = "https://files.pythonhosted.org/packages/6a/fc/9fa53f1a13710e6183df4d00fe4988c79a55b501e282645d49f1e250437f/ortools-9.15.6755-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:ae1c6e1fd844b4d756b22eb6c0ed574ea4342ee206d807c4f903039e748228fa", size = 23926322, upload-time = "2026-01-14T15:39:01.626Z" }, + { url = "https://files.pythonhosted.org/packages/f1/b6/7e6618ef7a88e8eb706a8a876806b4d336f1bef8c574f8a02d2da3e483ef/ortools-9.15.6755-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e16686c2b457fa6242c474ab890ee1712347ab53678e0d2fab307ae03e97a4b", size = 21912940, upload-time = "2026-01-14T15:39:04.403Z" }, + { url = "https://files.pythonhosted.org/packages/86/a9/37cb31fc5ffbec2650ebb0d2538a83842b5693a788a0ec6057559dab1169/ortools-9.15.6755-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3cd6bec0a2e00e3891a53e3b436f45a1000269f302085572f49e9856b7f8eaf0", size = 27646561, upload-time = "2026-01-14T15:37:57.414Z" }, + { url = "https://files.pythonhosted.org/packages/49/0f/6d6d722102a0ceccf4a5038e2bc91d023da84a6dba98482a4634df3d27ab/ortools-9.15.6755-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:033836c0eb33bc72697a299e0caedbb25fc9d1cee0b13832d69cb30405f57b3e", size = 29838435, upload-time = "2026-01-14T15:38:01.047Z" }, + { url = "https://files.pythonhosted.org/packages/83/a2/5aaf12e34bcd47ae16e70ae81b5c7fbc209da0615c0b79a93c9a0b1cda02/ortools-9.15.6755-cp312-cp312-win_amd64.whl", hash = "sha256:487796301fd9dad55f9cf21f9313c834697f74306d1a59f002e152862f8eb1b5", size = 23883161, upload-time = "2026-01-14T15:39:45.104Z" }, + { url = "https://files.pythonhosted.org/packages/f1/53/e21c54ff10002cc2e2b9748012ffc324ec32ea4acdcc85e190a920ab2766/ortools-9.15.6755-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:27a10474e62c9dceed37cfa0e4845c5ffaf792138ebf5b61483771b96f1290b6", size = 23927705, upload-time = "2026-01-14T15:39:07.29Z" }, + { url = "https://files.pythonhosted.org/packages/ce/e6/f7019048ffdf41f8a1bff6815b2203cf7b9117ba9e26bf46c4585421d1c4/ortools-9.15.6755-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:076565b803c85c4f87863e0616f537dd37f99c03e6f092e4068404f7b425d2b0", size = 21914246, upload-time = "2026-01-14T15:39:10.584Z" }, + { url = "https://files.pythonhosted.org/packages/8d/ad/aaacd340918b03e22c42f6ae4a9c72aac09810b4b398e99a7eeee58d9c42/ortools-9.15.6755-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b85bd20259b146abce5e0721ce1bfd8fd273efc904216aa3be178c31b6d34057", size = 27646600, upload-time = "2026-01-14T15:38:04.79Z" }, + { url = "https://files.pythonhosted.org/packages/08/b9/28d5efb832190b6edfccc5a703e88e64779c1eda34a42ea96d03307236c0/ortools-9.15.6755-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ebd5aea00374e3aad7a78de59058aca5e871a26a3c385cd0860ef1d685d03c9a", size = 29838741, upload-time = "2026-01-14T15:38:07.945Z" }, + { url = "https://files.pythonhosted.org/packages/be/22/ab894b6f846b4b1a89795c1ba966834e56cac394c4cf2b72433909739982/ortools-9.15.6755-cp313-cp313-win_amd64.whl", hash = "sha256:caac1d48b967adb877da2abcaf82c28f0f908a7cc208a6a1bbe01bc69590816c", size = 23908100, upload-time = "2026-01-14T15:39:48.398Z" }, + { url = "https://files.pythonhosted.org/packages/a3/53/ada4146ae491d7798c6eb045d93135158c0b66030853c7cd9607768dda59/ortools-9.15.6755-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:82b4a8e6e4f9380b453ab5fa4382ea7ee91e628f9b8be89d9ad760b33fca3323", size = 27681510, upload-time = "2026-01-14T15:38:11.033Z" }, + { url = "https://files.pythonhosted.org/packages/32/e6/239e96912fc8c4e0e917e72ec413983bc042cd9a0b20c3c6a7e43fc3002b/ortools-9.15.6755-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2d1f2fb2088e8953ccb902e68ffd06032cce0c7dcf7268b6135f3b6c553ca52b", size = 29850935, upload-time = "2026-01-14T15:38:14.595Z" }, + { url = "https://files.pythonhosted.org/packages/53/ef/53a172ad12cf0d762b9a5af681b1f13f1b4105b38bf65c2b383d530ed97f/ortools-9.15.6755-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:acdf06a167933307608e7eba23a9490255933504df44c8de5f62c48656c29688", size = 23916963, upload-time = "2026-01-14T15:39:13.282Z" }, + { url = "https://files.pythonhosted.org/packages/13/54/ed73ec00369fb6d6c71049d62e4b7c87c918b61f86ddd55a11c20ada395e/ortools-9.15.6755-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1a0677270b0cd317a6b8dae42514264eaf5da5756c5bc7215eeea409424577df", size = 21923649, upload-time = "2026-01-14T15:39:16.831Z" }, + { url = "https://files.pythonhosted.org/packages/1c/e0/ac57dd43eaadd73748bb542b30912e16c7dbf3a75f393f69efb8a1a2f032/ortools-9.15.6755-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:899b92afe3f775ab5867b9a8aa2850f81f2d95232db9b4ceec3456d69e6b8528", size = 27657273, upload-time = "2026-01-14T15:38:18.375Z" }, + { url = "https://files.pythonhosted.org/packages/b2/e0/11144feb4ddadc491dc9d833d3a2080e6556245f912bebe2c0c7e174f2a1/ortools-9.15.6755-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7181183cdcafe2b0d83ca5505b65048c7953dc7b5ad479361dded607964cc1b3", size = 29843939, upload-time = "2026-01-14T15:38:21.457Z" }, + { url = "https://files.pythonhosted.org/packages/96/97/771515ba3a05da3903b7da55a190d9f88f36a08c4bf848852e0ea4e3a731/ortools-9.15.6755-cp314-cp314-win_amd64.whl", hash = "sha256:afabb869e5fabeb704bd8147b22bf8139dee042e55fabd0d447a996428009e0c", size = 24673633, upload-time = "2026-01-14T15:39:51.212Z" }, + { url = "https://files.pythonhosted.org/packages/46/99/0932d6d7d6ad326adf68f4ce9063ef07db7e9859859dddbcd200102aedff/ortools-9.15.6755-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d9d07cddca201e25e2e219006a9d6cda10c7e9ee2c712c50d19d508f9ed8a888", size = 27682088, upload-time = "2026-01-14T15:38:25.174Z" }, + { url = "https://files.pythonhosted.org/packages/0e/4d/bd75961e2c82db69bb41dd2c4a82131ca580e997485be2d5f59f8d26f31e/ortools-9.15.6755-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:990838ad66a052e72a50e69da500878710e3420e91717fe88bf3071995caba9e", size = 29851493, upload-time = "2026-01-14T15:38:28.168Z" }, ] [[package]] @@ -1085,7 +1332,7 @@ wheels = [ [[package]] name = "pre-commit" -version = "4.5.0" +version = "4.6.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cfgv" }, @@ -1094,28 +1341,29 @@ dependencies = [ { name = "pyyaml" }, { name = "virtualenv" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f4/9b/6a4ffb4ed980519da959e1cf3122fc6cb41211daa58dbae1c73c0e519a37/pre_commit-4.5.0.tar.gz", hash = "sha256:dc5a065e932b19fc1d4c653c6939068fe54325af8e741e74e88db4d28a4dd66b", size = 198428, upload-time = "2025-11-22T21:02:42.304Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/22/2de9408ac81acbb8a7d05d4cc064a152ccf33b3d480ebe0cd292153db239/pre_commit-4.6.0.tar.gz", hash = "sha256:718d2208cef53fdc38206e40524a6d4d9576d103eb16f0fec11c875e7716e9d9", size = 198525, upload-time = "2026-04-21T20:31:41.613Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/c4/b2d28e9d2edf4f1713eb3c29307f1a63f3d67cf09bdda29715a36a68921a/pre_commit-4.5.0-py2.py3-none-any.whl", hash = "sha256:25e2ce09595174d9c97860a95609f9f852c0614ba602de3561e267547f2335e1", size = 226429, upload-time = "2025-11-22T21:02:40.836Z" }, + { url = "https://files.pythonhosted.org/packages/80/6e/4b28b62ecb6aae56769c34a8ff1d661473ec1e9519e2d5f8b2c150086b26/pre_commit-4.6.0-py2.py3-none-any.whl", hash = "sha256:e2cf246f7299edcabcf15f9b0571fdce06058527f0a06535068a86d38089f29b", size = 226472, upload-time = "2026-04-21T20:31:40.092Z" }, ] [[package]] name = "protobuf" -version = "6.31.1" +version = "6.33.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/52/f3/b9655a711b32c19720253f6f06326faf90580834e2e83f840472d752bc8b/protobuf-6.31.1.tar.gz", hash = "sha256:d8cac4c982f0b957a4dc73a80e2ea24fab08e679c0de9deb835f4a12d69aca9a", size = 441797, upload-time = "2025-05-28T19:25:54.947Z" } +sdist = { url = "https://files.pythonhosted.org/packages/66/70/e908e9c5e52ef7c3a6c7902c9dfbb34c7e29c25d2f81ade3856445fd5c94/protobuf-6.33.6.tar.gz", hash = "sha256:a6768d25248312c297558af96a9f9c929e8c4cee0659cb07e780731095f38135", size = 444531, upload-time = "2026-03-18T19:05:00.988Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/6f/6ab8e4bf962fd5570d3deaa2d5c38f0a363f57b4501047b5ebeb83ab1125/protobuf-6.31.1-cp310-abi3-win32.whl", hash = "sha256:7fa17d5a29c2e04b7d90e5e32388b8bfd0e7107cd8e616feef7ed3fa6bdab5c9", size = 423603, upload-time = "2025-05-28T19:25:41.198Z" }, - { url = "https://files.pythonhosted.org/packages/44/3a/b15c4347dd4bf3a1b0ee882f384623e2063bb5cf9fa9d57990a4f7df2fb6/protobuf-6.31.1-cp310-abi3-win_amd64.whl", hash = "sha256:426f59d2964864a1a366254fa703b8632dcec0790d8862d30034d8245e1cd447", size = 435283, upload-time = "2025-05-28T19:25:44.275Z" }, - { url = "https://files.pythonhosted.org/packages/6a/c9/b9689a2a250264a84e66c46d8862ba788ee7a641cdca39bccf64f59284b7/protobuf-6.31.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:6f1227473dc43d44ed644425268eb7c2e488ae245d51c6866d19fe158e207402", size = 425604, upload-time = "2025-05-28T19:25:45.702Z" }, - { url = "https://files.pythonhosted.org/packages/76/a1/7a5a94032c83375e4fe7e7f56e3976ea6ac90c5e85fac8576409e25c39c3/protobuf-6.31.1-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:a40fc12b84c154884d7d4c4ebd675d5b3b5283e155f324049ae396b95ddebc39", size = 322115, upload-time = "2025-05-28T19:25:47.128Z" }, - { url = "https://files.pythonhosted.org/packages/fa/b1/b59d405d64d31999244643d88c45c8241c58f17cc887e73bcb90602327f8/protobuf-6.31.1-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:4ee898bf66f7a8b0bd21bce523814e6fbd8c6add948045ce958b73af7e8878c6", size = 321070, upload-time = "2025-05-28T19:25:50.036Z" }, - { url = "https://files.pythonhosted.org/packages/f7/af/ab3c51ab7507a7325e98ffe691d9495ee3d3aa5f589afad65ec920d39821/protobuf-6.31.1-py3-none-any.whl", hash = "sha256:720a6c7e6b77288b85063569baae8536671b39f15cc22037ec7045658d80489e", size = 168724, upload-time = "2025-05-28T19:25:53.926Z" }, + { url = "https://files.pythonhosted.org/packages/fc/9f/2f509339e89cfa6f6a4c4ff50438db9ca488dec341f7e454adad60150b00/protobuf-6.33.6-cp310-abi3-win32.whl", hash = "sha256:7d29d9b65f8afef196f8334e80d6bc1d5d4adedb449971fefd3723824e6e77d3", size = 425739, upload-time = "2026-03-18T19:04:48.373Z" }, + { url = "https://files.pythonhosted.org/packages/76/5d/683efcd4798e0030c1bab27374fd13a89f7c2515fb1f3123efdfaa5eab57/protobuf-6.33.6-cp310-abi3-win_amd64.whl", hash = "sha256:0cd27b587afca21b7cfa59a74dcbd48a50f0a6400cfb59391340ad729d91d326", size = 437089, upload-time = "2026-03-18T19:04:50.381Z" }, + { url = "https://files.pythonhosted.org/packages/5c/01/a3c3ed5cd186f39e7880f8303cc51385a198a81469d53d0fdecf1f64d929/protobuf-6.33.6-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:9720e6961b251bde64edfdab7d500725a2af5280f3f4c87e57c0208376aa8c3a", size = 427737, upload-time = "2026-03-18T19:04:51.866Z" }, + { url = "https://files.pythonhosted.org/packages/ee/90/b3c01fdec7d2f627b3a6884243ba328c1217ed2d978def5c12dc50d328a3/protobuf-6.33.6-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:e2afbae9b8e1825e3529f88d514754e094278bb95eadc0e199751cdd9a2e82a2", size = 324610, upload-time = "2026-03-18T19:04:53.096Z" }, + { url = "https://files.pythonhosted.org/packages/9b/ca/25afc144934014700c52e05103c2421997482d561f3101ff352e1292fb81/protobuf-6.33.6-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:c96c37eec15086b79762ed265d59ab204dabc53056e3443e702d2681f4b39ce3", size = 339381, upload-time = "2026-03-18T19:04:54.616Z" }, + { url = "https://files.pythonhosted.org/packages/16/92/d1e32e3e0d894fe00b15ce28ad4944ab692713f2e7f0a99787405e43533a/protobuf-6.33.6-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:e9db7e292e0ab79dd108d7f1a94fe31601ce1ee3f7b79e0692043423020b0593", size = 323436, upload-time = "2026-03-18T19:04:55.768Z" }, + { url = "https://files.pythonhosted.org/packages/c4/72/02445137af02769918a93807b2b7890047c32bfb9f90371cbc12688819eb/protobuf-6.33.6-py3-none-any.whl", hash = "sha256:77179e006c476e69bf8e8ce866640091ec42e1beb80b213c3900006ecfba6901", size = 170656, upload-time = "2026-03-18T19:04:59.826Z" }, ] [[package]] name = "pydantic" -version = "2.12.5" +version = "2.13.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, @@ -1123,80 +1371,102 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } +sdist = { url = "https://files.pythonhosted.org/packages/18/a5/b60d21ac674192f8ab0ba4e9fd860690f9b4a6e51ca5df118733b487d8d6/pydantic-2.13.4.tar.gz", hash = "sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6", size = 844775, upload-time = "2026-05-06T13:43:05.343Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, + { url = "https://files.pythonhosted.org/packages/fd/7b/122376b1fd3c62c1ed9dc80c931ace4844b3c55407b6fb2d199377c9736f/pydantic-2.13.4-py3-none-any.whl", hash = "sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba", size = 472262, upload-time = "2026-05-06T13:43:02.641Z" }, +] + +[package.optional-dependencies] +email = [ + { name = "email-validator" }, ] [[package]] name = "pydantic-core" -version = "2.41.5" +version = "2.46.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9d/56/921726b776ace8d8f5db44c4ef961006580d91dc52b803c489fafd1aa249/pydantic_core-2.46.4.tar.gz", hash = "sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1", size = 471464, upload-time = "2026-05-06T13:37:06.98Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/8c/af022f0af448d7747c5154288d46b5f2bc5f17366eaa0e23e9aa04d59f3b/pydantic_core-2.46.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3245406455a5d98187ec35530fd772b1d799b26667980872c8d4614991e2c4a2", size = 2106158, upload-time = "2026-05-06T13:38:57.215Z" }, + { url = "https://files.pythonhosted.org/packages/19/95/6195171e385007300f0f5574592e467c568becce2d937a0b6804f218bc49/pydantic_core-2.46.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:962ccbab7b642487b1d8b7df90ef677e03134cf1fd8880bf698649b22a69371f", size = 1951724, upload-time = "2026-05-06T13:37:02.697Z" }, + { url = "https://files.pythonhosted.org/packages/8e/bc/f47d1ff9cbb1620e1b5b697eef06010035735f07820180e74178226b27b3/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8233f2947cf85404441fd7e0085f53b10c93e0ee78611099b5c7237e36aacbf7", size = 1975742, upload-time = "2026-05-06T13:37:09.448Z" }, + { url = "https://files.pythonhosted.org/packages/5b/11/9b9a5b0306345664a2da6410877af6e8082481b5884b3ddd78d47c6013ce/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a233125ac121aa3ffba9a2b59edfc4a985a76092dc8279586ab4b71390875e7", size = 2052418, upload-time = "2026-05-06T13:37:38.234Z" }, + { url = "https://files.pythonhosted.org/packages/f1/b7/a65fec226f5d78fc39f4a13c4cc0c768c22b113438f60c14adc9d2865038/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b712b53160b79a5850310b912a5ef8e57e56947c8ad690c227f5c9d7e561712", size = 2232274, upload-time = "2026-05-06T13:38:27.753Z" }, + { url = "https://files.pythonhosted.org/packages/68/f0/92039db98b907ef49269a8271f67db9cb78ae2fc68062ef7e4e77adb5f61/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9401557acd873c3a7f3eb9383edef8ac4968f9510e340f4808d427e75667e7b4", size = 2309940, upload-time = "2026-05-06T13:38:05.353Z" }, + { url = "https://files.pythonhosted.org/packages/5f/97/2aab507d3d00ca626e8e57c1eac6a79e4e5fbcc63eb99733ff55d1717f65/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:926c9541b14b12b1681dca8a0b75feb510b06c6341b70a8e500c2fdcff837cce", size = 2094516, upload-time = "2026-05-06T13:39:10.577Z" }, + { url = "https://files.pythonhosted.org/packages/22/37/a8aca44d40d737dde2bc05b3c6c07dff0de07ce6f82e9f3167aeaf4d5dea/pydantic_core-2.46.4-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:56cb4851bcaf3d117eddcef4fe66afd750a50274b0da8e22be256d10e5611987", size = 2136854, upload-time = "2026-05-06T13:40:22.59Z" }, + { url = "https://files.pythonhosted.org/packages/24/99/fcef1b79238c06a8cbec70819ac722ba76e02bc8ada9b0fd66eba40da01b/pydantic_core-2.46.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c68fcd102d71ea85c5b2dfac3f4f8476eff42a9e078fd5faefff6d145063536b", size = 2180306, upload-time = "2026-05-06T13:40:10.666Z" }, + { url = "https://files.pythonhosted.org/packages/ae/6c/fc44000918855b42779d007ae63b0532794739027b2f417321cddbc44f6a/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b2f69dec1725e79a012d920df1707de5caf7ed5e08f3be4435e25803efc47458", size = 2190044, upload-time = "2026-05-06T13:40:43.231Z" }, + { url = "https://files.pythonhosted.org/packages/6b/65/d9cadc9f1920d7a127ad2edba16c1db7916e59719285cd6c94600b0080ba/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:8d0820e8192167f80d88d64038e609c31452eeca865b4e1d9950a27a4609b00b", size = 2329133, upload-time = "2026-05-06T13:39:57.365Z" }, + { url = "https://files.pythonhosted.org/packages/d0/cf/c873d91679f3a30bcf5e7ac280ce5573483e72295307685120d0d5ad3416/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fbdb89b3e1c94a30cc5edfce477c6e6a5dc4d8f84665b455c27582f211a1c72c", size = 2374464, upload-time = "2026-05-06T13:38:06.976Z" }, + { url = "https://files.pythonhosted.org/packages/47/bd/6f2fc8188f31bf10590f1e98e7b306336161fac930a8c514cd7bd828c7dc/pydantic_core-2.46.4-cp312-cp312-win32.whl", hash = "sha256:9aa768456404a8bf48a4406685ac2bec8e72b62c69313734fa3b73cf33b3a894", size = 1974823, upload-time = "2026-05-06T13:40:47.985Z" }, + { url = "https://files.pythonhosted.org/packages/40/8c/985c1d41ea1107c2534abd9870e4ed5c8e7669b5c308297835c001e7a1c4/pydantic_core-2.46.4-cp312-cp312-win_amd64.whl", hash = "sha256:e9c26f834c65f5752f3f06cb08cb86a913ceb7274d0db6e267808a708b46bc89", size = 2072919, upload-time = "2026-05-06T13:39:21.153Z" }, + { url = "https://files.pythonhosted.org/packages/c4/ba/f463d006e0c47373ca7ec5e1a261c59dc01ef4d62b2657af925fb0deee3a/pydantic_core-2.46.4-cp312-cp312-win_arm64.whl", hash = "sha256:4fc73cb559bdb54b1134a706a2802a4cddd27a0633f5abb7e53056268751ac6a", size = 2027604, upload-time = "2026-05-06T13:39:03.753Z" }, + { url = "https://files.pythonhosted.org/packages/51/a2/5d30b469c5267a17b39dec53208222f76a8d351dfac4af661888c5aee77d/pydantic_core-2.46.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5d5902252db0d3cedf8d4a1bc68f70eeb430f7e4c7104c8c476753519b423008", size = 2106306, upload-time = "2026-05-06T13:37:48.029Z" }, + { url = "https://files.pythonhosted.org/packages/c1/81/4fa520eaffa8bd7d1525e644cd6d39e7d60b1592bc5b516693c7340b50f1/pydantic_core-2.46.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c94f0688e7b8d0a67abf40e57a7eaaecd17cc9586706a31b76c031f63df052b4", size = 1951906, upload-time = "2026-05-06T13:37:17.012Z" }, + { url = "https://files.pythonhosted.org/packages/03/d5/fd02da45b659668b05923b17ba3a0100a0a3d5541e3bd8fcc4ecb711309e/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f027324c56cd5406ca49c124b0db10e56c69064fec039acc571c29020cc87c76", size = 1976802, upload-time = "2026-05-06T13:37:35.113Z" }, + { url = "https://files.pythonhosted.org/packages/21/f2/95727e1368be3d3ed485eaab7adbd7dda408f33f7a36e8b48e0144002b91/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e739fee756ba1010f8bcccb534252e85a35fe45ae92c295a06059ce58b74ccd3", size = 2052446, upload-time = "2026-05-06T13:37:12.313Z" }, + { url = "https://files.pythonhosted.org/packages/9c/86/5d99feea3f77c7234b8718075b23db11532773c1a0dbd9b9490215dc2eeb/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d56801be94b86a9da183e5f3766e6310752b99ff647e38b09a9500d88e46e76", size = 2232757, upload-time = "2026-05-06T13:39:01.149Z" }, + { url = "https://files.pythonhosted.org/packages/d2/3a/508ac615935ef7588cf6d9e9b91309fdc2da751af865e02a9098de88258c/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2412e734dcb48da14d4e4006b82b46b74f2518b8a26ee7e58c6844a6cd6d03c4", size = 2309275, upload-time = "2026-05-06T13:37:41.406Z" }, + { url = "https://files.pythonhosted.org/packages/07/f8/41db9de19d7987d6b04715a02b3b40aea467000275d9d758ffaa31af7d50/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9551187363ffc0de2a00b2e47c25aeaeb1020b69b668762966df15fc5659dd5a", size = 2094467, upload-time = "2026-05-06T13:39:18.847Z" }, + { url = "https://files.pythonhosted.org/packages/2c/e2/f35033184cb11d0052daf4416e8e10a502ea2ac006fc4f459aee872727d1/pydantic_core-2.46.4-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:0186750b482eefa11d7f435892b09c5c606193ef3375bcf94aa00ae6bfb66262", size = 2134417, upload-time = "2026-05-06T13:40:17.944Z" }, + { url = "https://files.pythonhosted.org/packages/7e/7b/6ceeb1cc90e193862f444ebe373d8fdf613f0a82572dde03fb10734c6c71/pydantic_core-2.46.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5855698a4856556d86e8e6cd8434bc3ac0314ee8e12089ae0e143f64c6256e4e", size = 2179782, upload-time = "2026-05-06T13:40:32.618Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f2/c8d7773ede6af08036423a00ae0ceffce266c3c52a096c435d68c896083f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:cbaf13819775b7f769bf4a1f066cb6df7a28d4480081a589828ef190226881cd", size = 2188782, upload-time = "2026-05-06T13:36:51.018Z" }, + { url = "https://files.pythonhosted.org/packages/59/31/0c864784e31f09f05cdd87606f08923b9c9e7f6e51dd27f20f62f975ce9f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:633147d34cf4550417f12e2b1a0383973bdf5cdfde212cb09e9a581cf10820be", size = 2328334, upload-time = "2026-05-06T13:40:37.764Z" }, + { url = "https://files.pythonhosted.org/packages/c2/eb/4f6c8a41efa30baa755590f4141abf3a8c370fab610915733e74134a7270/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:82cf5301172168103724d49a1444d3378cb20cdee30b116a1bd6031236298a5d", size = 2372986, upload-time = "2026-05-06T13:39:34.152Z" }, + { url = "https://files.pythonhosted.org/packages/5b/24/b375a480d53113860c299764bfe9f349a3dc9108b3adc0d7f0d786492ebf/pydantic_core-2.46.4-cp313-cp313-win32.whl", hash = "sha256:9fa8ae11da9e2b3126c6426f147e0fba88d96d65921799bb30c6abd1cb2c97fb", size = 1973693, upload-time = "2026-05-06T13:37:55.072Z" }, + { url = "https://files.pythonhosted.org/packages/7e/e8/cff247591966f2d22ec8c003cd7587e27b7ba7b81ab2fb888e3ab75dc285/pydantic_core-2.46.4-cp313-cp313-win_amd64.whl", hash = "sha256:6b3ace8194b0e5204818c92802dcdca7fc6d88aabbb799d7c795540d9cd6d292", size = 2071819, upload-time = "2026-05-06T13:38:49.139Z" }, + { url = "https://files.pythonhosted.org/packages/c6/1a/f4aee670d5670e9e148e0c82c7db98d780be566c6e6a97ee8035528ca0b3/pydantic_core-2.46.4-cp313-cp313-win_arm64.whl", hash = "sha256:184c081504d17f1c1066e430e117142b2c77d9448a97f7b65c6ac9fd9aee238d", size = 2027411, upload-time = "2026-05-06T13:40:45.796Z" }, + { url = "https://files.pythonhosted.org/packages/8d/74/228a26ddad29c6672b805d9fd78e8d251cd04004fa7eed0e622096cd0250/pydantic_core-2.46.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:428e04521a40150c85216fc8b85e8d39fece235a9cf5e383761238c7fa9b96fb", size = 2102079, upload-time = "2026-05-06T13:38:41.019Z" }, + { url = "https://files.pythonhosted.org/packages/ad/1f/8970b150a4b4365623ae00fc88603491f763c627311ae8031e3111356d6e/pydantic_core-2.46.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23ace664830ee0bfe014a0c7bc248b1f7f25ed7ad103852c317624a1083af462", size = 1952179, upload-time = "2026-05-06T13:36:59.812Z" }, + { url = "https://files.pythonhosted.org/packages/95/30/5211a831ae054928054b2f79731661087a2bc5c01e825c672b3a4a8f1b3e/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce5c1d2a8b27468f433ca974829c44060b8097eedc39933e3c206a90ee49c4a9", size = 1978926, upload-time = "2026-05-06T13:37:39.933Z" }, + { url = "https://files.pythonhosted.org/packages/57/e9/689668733b1eb67adeef047db3c2e8788fcf65a7fd9c9e2b46b7744fe245/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7283d57845ecf5a163403eb0702dfc220cc4fbdd18919cb5ccea4f95ee1cdab4", size = 2046785, upload-time = "2026-05-06T13:38:01.995Z" }, + { url = "https://files.pythonhosted.org/packages/60/d9/6715260422ff50a2109878fd24d948a6c3446bb2664f34ee78cd972b3acd/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8daafc69c93ee8a0204506a3b6b30f586ef54028f52aeeeb5c4cfc5184fd5914", size = 2228733, upload-time = "2026-05-06T13:40:50.371Z" }, + { url = "https://files.pythonhosted.org/packages/18/ae/fdb2f64316afca925640f8e70bb1a564b0ec2721c1389e25b8eb4bf9a299/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd2213145bcc2ba85884d0ac63d222fece9209678f77b9b4d76f054c561adb28", size = 2307534, upload-time = "2026-05-06T13:37:21.531Z" }, + { url = "https://files.pythonhosted.org/packages/89/1d/8eff589b45bb8190a9d12c49cfad0f176a5cbd1534908a6b5125e2886239/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a5f930472650a82629163023e630d160863fce524c616f4e5186e5de9d9a49b", size = 2099732, upload-time = "2026-05-06T13:39:31.942Z" }, + { url = "https://files.pythonhosted.org/packages/06/d5/ee5a3366637fee41dee51a1fc91562dcf12ddbc68fda34e6b253da2324bb/pydantic_core-2.46.4-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:c1b3f518abeca3aa13c712fd202306e145abf59a18b094a6bafb2d2bbf59192c", size = 2129627, upload-time = "2026-05-06T13:37:25.033Z" }, + { url = "https://files.pythonhosted.org/packages/94/33/2414be571d2c6a6c4d08be21f9292b6d3fdb08949a97b6dfe985017821db/pydantic_core-2.46.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a7dd0b3ee80d90150e3495a3a13ac34dbcbfd4f012996a6a1d8900e91b5c0fb", size = 2179141, upload-time = "2026-05-06T13:37:14.046Z" }, + { url = "https://files.pythonhosted.org/packages/7b/79/7daa95be995be0eecc4cf75064cb33f9bbbfe3fe0158caf2f0d4a996a5c7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:3fb702cd90b0446a3a1c5e470bfa0dd23c0233b676a9099ddcc964fa6ca13898", size = 2184325, upload-time = "2026-05-06T13:36:53.615Z" }, + { url = "https://files.pythonhosted.org/packages/9f/cb/d0a382f5c0de8a222dc61c65348e0ce831b1f68e0a018450d31c2cace3a5/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b8458003118a712e66286df6a707db01c52c0f52f7db8e4a38f0da1d3b94fc4e", size = 2323990, upload-time = "2026-05-06T13:40:29.971Z" }, + { url = "https://files.pythonhosted.org/packages/05/db/d9ba624cc4a5aced1598e88c04fdbd8310c8a69b9d38b9a3d39ce3a61ed7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:372429a130e469c9cd698925ce5fc50940b7a1336b0d82038e63d5bbc4edc519", size = 2369978, upload-time = "2026-05-06T13:37:23.027Z" }, + { url = "https://files.pythonhosted.org/packages/f2/20/d15df15ba918c423461905802bfd2981c3af0bfa0e40d05e13edbfa48bc3/pydantic_core-2.46.4-cp314-cp314-win32.whl", hash = "sha256:85bb3611ff1802f3ee7fdd7dbff26b56f343fb432d57a4728fdd49b6ef35e2f4", size = 1966354, upload-time = "2026-05-06T13:38:03.499Z" }, + { url = "https://files.pythonhosted.org/packages/fc/b6/6b8de4c0a7d7ab3004c439c80c5c1e0a3e8d78bbae19379b01960383d9e5/pydantic_core-2.46.4-cp314-cp314-win_amd64.whl", hash = "sha256:811ff8e9c313ab425368bcbb36e5c4ebd7108c2bbf4e4089cfbb0b01eff63fac", size = 2072238, upload-time = "2026-05-06T13:39:40.807Z" }, + { url = "https://files.pythonhosted.org/packages/32/36/51eb763beec1f4cf59b1db243a7dcc39cbb41230f050a09b9d69faaf0a48/pydantic_core-2.46.4-cp314-cp314-win_arm64.whl", hash = "sha256:bfec22eab3c8cc2ceec0248aec886624116dc079afa027ecc8ad4a7e62010f8a", size = 2018251, upload-time = "2026-05-06T13:37:26.72Z" }, + { url = "https://files.pythonhosted.org/packages/e8/91/855af51d625b23aa987116a19e231d2aaef9c4a415273ddc189b79a45fee/pydantic_core-2.46.4-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:af8244b2bef6aaad6d92cda81372de7f8c8d36c9f0c3ea36e827c60e7d9467a0", size = 2099593, upload-time = "2026-05-06T13:39:47.682Z" }, + { url = "https://files.pythonhosted.org/packages/fb/1b/8784a54c65edb5f49f0a14d6977cf1b209bba85a4c77445b255c2de58ab3/pydantic_core-2.46.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a4330cdbc57162e4b3aa303f588ba752257694c9c9be3e7ebb11b4aca659b5d", size = 1935226, upload-time = "2026-05-06T13:40:40.428Z" }, + { url = "https://files.pythonhosted.org/packages/e8/e7/1955d28d1afc56dd4b3ad7cc0cf39df1b9852964cf16e5d13912756d6d6b/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29c61fc04a3d840155ff08e475a04809278972fe6aef51e2720554e96367e34b", size = 1974605, upload-time = "2026-05-06T13:37:32.029Z" }, + { url = "https://files.pythonhosted.org/packages/93/e2/3fedbf0ba7a22850e6e9fd78117f1c0f10f950182344d8a6c535d468fdd8/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c50f2528cf200c5eed56faf3f4e22fcd5f38c157a8b78576e6ba3168ec35f000", size = 2030777, upload-time = "2026-05-06T13:38:55.239Z" }, + { url = "https://files.pythonhosted.org/packages/f8/61/46be275fcaaba0b4f5b9669dd852267ce1ff616592dccf7a7845588df091/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0cbe8b01f948de4286c74cdd6c667aceb38f5c1e26f0693b3983d9d74887c65e", size = 2236641, upload-time = "2026-05-06T13:37:08.096Z" }, + { url = "https://files.pythonhosted.org/packages/60/db/12e93e46a8bac9988be3c016860f83293daea8c716c029c9ace279036f2f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:617d7e2ca7dcb8c5cf6bcb8c59b8832c94b36196bbf1cbd1bfb56ed341905edd", size = 2286404, upload-time = "2026-05-06T13:40:20.221Z" }, + { url = "https://files.pythonhosted.org/packages/e2/4a/4d8b19008f38d31c53b8219cfedc2e3d5de5fe99d90076b7e767de29274f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7027560ee92211647d0d34e3f7cd6f50da56399d26a9c8ad0da286d3869a53f3", size = 2109219, upload-time = "2026-05-06T13:38:12.153Z" }, + { url = "https://files.pythonhosted.org/packages/88/70/3cbc40978fefb7bb09c6708d40d4ad1a5d70fd7213c3d17f971de868ec1f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:f99626688942fb746e545232e7726926f3be91b5975f8b55327665fafda991c7", size = 2110594, upload-time = "2026-05-06T13:40:02.971Z" }, + { url = "https://files.pythonhosted.org/packages/9d/20/b8d36736216e29491125531685b2f9e61aa5b4b2599893f8268551da3338/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc3e9034a63de20e15e8ade85358bc6efc614008cab72898b4b4952bea0509ff", size = 2159542, upload-time = "2026-05-06T13:39:27.506Z" }, + { url = "https://files.pythonhosted.org/packages/1d/a2/367df868eb584dacf6bf82a389272406d7178e301c4ac82545ab98bc2dd9/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:97e7cf2be5c77b7d1a9713a05605d49460d02c6078d38d8bef3cbe323c548424", size = 2168146, upload-time = "2026-05-06T13:38:31.93Z" }, + { url = "https://files.pythonhosted.org/packages/c1/b8/4460f77f7e201893f649a29ab355dddd3beee8a97bcb1a320db414f9a06e/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:3bf92c5d0e00fefaab325a4d27828fe6b6e2a21848686b5b60d2d9eeb09d76c6", size = 2306309, upload-time = "2026-05-06T13:37:44.717Z" }, + { url = "https://files.pythonhosted.org/packages/64/c4/be2639293acd87dc8ddbcec41a73cee9b2ebf996fe6d892a1a74e88ad3f7/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:3ecbc122d18468d06ca279dc26a8c2e2d5acb10943bb35e36ae92096dc3b5565", size = 2369736, upload-time = "2026-05-06T13:37:05.645Z" }, + { url = "https://files.pythonhosted.org/packages/30/a6/9f9f380dbb301f67023bf8f707aaa75daadf84f7152d95c410fd7e81d994/pydantic_core-2.46.4-cp314-cp314t-win32.whl", hash = "sha256:e846ae7835bf0703ae43f534ab79a867146dadd59dc9ca5c8b53d5c8f7c9ef02", size = 1955575, upload-time = "2026-05-06T13:38:51.116Z" }, + { url = "https://files.pythonhosted.org/packages/40/1f/f1eb9eb350e795d1af8586289746f5c5677d16043040d63710e22abc43c9/pydantic_core-2.46.4-cp314-cp314t-win_amd64.whl", hash = "sha256:2108ba5c1c1eca18030634489dc544844144ee36357f2f9f780b93e7ddbb44b5", size = 2051624, upload-time = "2026-05-06T13:38:21.672Z" }, + { url = "https://files.pythonhosted.org/packages/f6/d2/42dd53d0a85c27606f316d3aa5d2869c4e8470a5ed6dec30e4a1abe19192/pydantic_core-2.46.4-cp314-cp314t-win_arm64.whl", hash = "sha256:4fcbe087dbc2068af7eda3aa87634eba216dbda64d1ae73c8684b621d33f6596", size = 2017325, upload-time = "2026-05-06T13:40:52.723Z" }, + { url = "https://files.pythonhosted.org/packages/9d/1d/8987ad40f65ae1432753072f214fb5c74fe47ffbd0698bb9cbbb585664f8/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:1d8ba486450b14f3b1d63bc521d410ec7565e52f887b9fb671791886436a42f7", size = 2095527, upload-time = "2026-05-06T13:39:52.283Z" }, + { url = "https://files.pythonhosted.org/packages/64/d3/84c282a7eee1d3ac4c0377546ef5a1ea436ce26840d9ac3b7ed54a377507/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:3009f12e4e90b7f88b4f9adb1b0c4a3d58fe7820f3238c190047209d148026df", size = 1936024, upload-time = "2026-05-06T13:40:15.671Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ca/eac61596cdeb4d7e174d3dc0bd8a6238f14f75f97a24e7b7db4c7e7340a0/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad785e92e6dc634c21555edc8bd6b64957ab844541bcb96a1366c202951ae526", size = 1990696, upload-time = "2026-05-06T13:38:34.717Z" }, + { url = "https://files.pythonhosted.org/packages/fa/c3/7c8b240552251faf6b3a957db200fcfbbcec36763c050428b601e0c9b83b/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00c603d540afdd6b80eb39f078f33ebd46211f02f33e34a32d9f053bba711de0", size = 2147590, upload-time = "2026-05-06T13:39:29.883Z" }, +] + +[[package]] +name = "pydantic-extra-types" +version = "2.11.1" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "pydantic" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" }, - { url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" }, - { url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" }, - { url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" }, - { url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" }, - { url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" }, - { url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" }, - { url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" }, - { url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" }, - { url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" }, - { url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" }, - { url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908, upload-time = "2025-11-04T13:40:19.309Z" }, - { url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145, upload-time = "2025-11-04T13:40:21.548Z" }, - { url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179, upload-time = "2025-11-04T13:40:23.393Z" }, - { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" }, - { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" }, - { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" }, - { url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" }, - { url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" }, - { url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" }, - { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" }, - { url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" }, - { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" }, - { url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" }, - { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" }, - { url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" }, - { url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" }, - { url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" }, - { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" }, - { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" }, - { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" }, - { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" }, - { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" }, - { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" }, - { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" }, - { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" }, - { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" }, - { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" }, - { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" }, - { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" }, - { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" }, - { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" }, - { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" }, - { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" }, - { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" }, - { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" }, - { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" }, - { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" }, - { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" }, - { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" }, - { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" }, - { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" }, - { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" }, - { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" }, - { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" }, - { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" }, - { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" }, - { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" }, - { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" }, - { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/66/71/dba38ee2651f84f7842206adbd2233d8bbdb59fb85e9fa14232486a8c471/pydantic_extra_types-2.11.1.tar.gz", hash = "sha256:46792d2307383859e923d8fcefa82108b1a141f8a9c0198982b3832ab5ef1049", size = 172002, upload-time = "2026-03-16T08:08:03.92Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/17/c1/3226e6d7f5a4f736f38ac11a6fbb262d701889802595cdb0f53a885ac2e0/pydantic_extra_types-2.11.1-py3-none-any.whl", hash = "sha256:1722ea2bddae5628ace25f2aa685b69978ef533123e5638cfbddb999e0100ec1", size = 79526, upload-time = "2026-03-16T08:08:02.533Z" }, ] [[package]] @@ -1290,20 +1560,20 @@ wheels = [ [[package]] name = "pyright" -version = "1.1.407" +version = "1.1.410" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "nodeenv" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a6/1b/0aa08ee42948b61745ac5b5b5ccaec4669e8884b53d31c8ec20b2fcd6b6f/pyright-1.1.407.tar.gz", hash = "sha256:099674dba5c10489832d4a4b2d302636152a9a42d317986c38474c76fe562262", size = 4122872, upload-time = "2025-10-24T23:17:15.145Z" } +sdist = { url = "https://files.pythonhosted.org/packages/10/53/e4d8ea1391bd4355231be6f91bf239479aa0014260ed3fb5526eeb12a1f2/pyright-1.1.410.tar.gz", hash = "sha256:07a073b8ba6749826773c1269773efa11b93440d9a6aa60419d9a3172d6dc488", size = 4062013, upload-time = "2026-06-01T17:35:48.894Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/93/b69052907d032b00c40cb656d21438ec00b3a471733de137a3f65a49a0a0/pyright-1.1.407-py3-none-any.whl", hash = "sha256:6dd419f54fcc13f03b52285796d65e639786373f433e243f8b94cf93a7444d21", size = 5997008, upload-time = "2025-10-24T23:17:13.159Z" }, + { url = "https://files.pythonhosted.org/packages/d7/33/288b5868fa00846dacf249633719d747893e54aebd196b9968ac1878a5d3/pyright-1.1.410-py3-none-any.whl", hash = "sha256:5e961bed37cacf96b3f7cd7b1da39b350a9239aa2e69138d0e88f728cfaf296c", size = 6082448, upload-time = "2026-06-01T17:35:46.387Z" }, ] [[package]] name = "pytest" -version = "9.0.1" +version = "9.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, @@ -1312,9 +1582,9 @@ dependencies = [ { name = "pluggy" }, { name = "pygments" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/07/56/f013048ac4bc4c1d9be45afd4ab209ea62822fb1598f40687e6bf45dcea4/pytest-9.0.1.tar.gz", hash = "sha256:3e9c069ea73583e255c3b21cf46b8d3c56f6e3a1a8f6da94ccb0fcf57b9d73c8", size = 1564125, upload-time = "2025-11-12T13:05:09.333Z" } +sdist = { url = "https://files.pythonhosted.org/packages/84/0e/b5858858d74958632c49b72cb25a3976ff9f632397626715be71c89d3971/pytest-9.1.0.tar.gz", hash = "sha256:41dd9148c08072446394cefd3d79701701335a9f4cae69ba92e39f6c7f5c061c", size = 1634181, upload-time = "2026-06-13T18:52:45.983Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/8b/6300fb80f858cda1c51ffa17075df5d846757081d11ab4aa35cef9e6258b/pytest-9.0.1-py3-none-any.whl", hash = "sha256:67be0030d194df2dfa7b556f2e56fb3c3315bd5c8822c6951162b92b32ce7dad", size = 373668, upload-time = "2025-11-12T13:05:07.379Z" }, + { url = "https://files.pythonhosted.org/packages/8b/5a/ba30a81239b909821b3153e303e7def45178bf353da4f72380e6c5e8793b/pytest-9.1.0-py3-none-any.whl", hash = "sha256:8ebb0e7888bdf2bdfc602ec51f8f62d50200af37356c74e503c79a94f5c81f32", size = 386453, upload-time = "2026-06-13T18:52:44.045Z" }, ] [[package]] @@ -1338,6 +1608,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" }, ] +[[package]] +name = "python-multipart" +version = "0.0.32" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5b/42/55c32bb9b12693c092ad250a0e82edb5b31ddeda6eb772de5f308b3804ad/python_multipart-0.0.32.tar.gz", hash = "sha256:be54b7f3fa167bb83e4fcd936b887b708f4e57fe75911c02aebf53efaf8d938e", size = 46881, upload-time = "2026-06-04T16:18:58.647Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/04/e8135ebd1ad02c56ec633277529b2602ff99ff634be76cdba5744cf554fd/python_multipart-0.0.32-py3-none-any.whl", hash = "sha256:ff6d3f776f16878c894e52e107296ffc890e913c611b1a4ec6c44e2821fe2e23", size = 30042, upload-time = "2026-06-04T16:18:57.319Z" }, +] + [[package]] name = "pytz" version = "2025.2" @@ -1420,30 +1699,146 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, ] +[[package]] +name = "rich" +version = "15.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680, upload-time = "2026-04-12T08:24:00.75Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654, upload-time = "2026-04-12T08:24:02.83Z" }, +] + +[[package]] +name = "rich-toolkit" +version = "0.20.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "rich" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/29/63/3e427c62f1992945c997d4ec31e2fcb37d26aadbe5aa44ae5b29f7f64d26/rich_toolkit-0.20.1.tar.gz", hash = "sha256:c7336ae281f435c785acecaedc4b71d4b663dc73d9c8079fea96372527e822a4", size = 203473, upload-time = "2026-06-05T08:56:57.679Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/88/309f07d08155da2ba1d5ceb42d270fb42fbe34a807684543e3ffc10fe713/rich_toolkit-0.20.1-py3-none-any.whl", hash = "sha256:2a6d5f8e15759b9eba5a9ee63da10b275359ead20e5a0fc92bd5b4dbae8ce4bf", size = 35525, upload-time = "2026-06-05T08:56:58.586Z" }, +] + +[[package]] +name = "rignore" +version = "0.7.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e5/f5/8bed2310abe4ae04b67a38374a4d311dd85220f5d8da56f47ae9361be0b0/rignore-0.7.6.tar.gz", hash = "sha256:00d3546cd793c30cb17921ce674d2c8f3a4b00501cb0e3dd0e82217dbeba2671", size = 57140, upload-time = "2025-11-05T21:41:21.968Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/0e/012556ef3047a2628842b44e753bb15f4dc46806780ff090f1e8fe4bf1eb/rignore-0.7.6-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:03e82348cb7234f8d9b2834f854400ddbbd04c0f8f35495119e66adbd37827a8", size = 883488, upload-time = "2025-11-05T20:42:41.359Z" }, + { url = "https://files.pythonhosted.org/packages/93/b0/d4f1f3fe9eb3f8e382d45ce5b0547ea01c4b7e0b4b4eb87bcd66a1d2b888/rignore-0.7.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b9e624f6be6116ea682e76c5feb71ea91255c67c86cb75befe774365b2931961", size = 820411, upload-time = "2025-11-05T20:42:24.782Z" }, + { url = "https://files.pythonhosted.org/packages/4a/c8/dea564b36dedac8de21c18e1851789545bc52a0c22ece9843444d5608a6a/rignore-0.7.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bda49950d405aa8d0ebe26af807c4e662dd281d926530f03f29690a2e07d649a", size = 897821, upload-time = "2025-11-05T20:40:52.613Z" }, + { url = "https://files.pythonhosted.org/packages/b3/2b/ee96db17ac1835e024c5d0742eefb7e46de60020385ac883dd3d1cde2c1f/rignore-0.7.6-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b5fd5ab3840b8c16851d327ed06e9b8be6459702a53e5ab1fc4073b684b3789e", size = 873963, upload-time = "2025-11-05T20:41:07.49Z" }, + { url = "https://files.pythonhosted.org/packages/a5/8c/ad5a57bbb9d14d5c7e5960f712a8a0b902472ea3f4a2138cbf70d1777b75/rignore-0.7.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ced2a248352636a5c77504cb755dc02c2eef9a820a44d3f33061ce1bb8a7f2d2", size = 1169216, upload-time = "2025-11-05T20:41:23.73Z" }, + { url = "https://files.pythonhosted.org/packages/80/e6/5b00bc2a6bc1701e6878fca798cf5d9125eb3113193e33078b6fc0d99123/rignore-0.7.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a04a3b73b75ddc12c9c9b21efcdaab33ca3832941d6f1d67bffd860941cd448a", size = 942942, upload-time = "2025-11-05T20:41:39.393Z" }, + { url = "https://files.pythonhosted.org/packages/85/e5/7f99bd0cc9818a91d0e8b9acc65b792e35750e3bdccd15a7ee75e64efca4/rignore-0.7.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d24321efac92140b7ec910ac7c53ab0f0c86a41133d2bb4b0e6a7c94967f44dd", size = 959787, upload-time = "2025-11-05T20:42:09.765Z" }, + { url = "https://files.pythonhosted.org/packages/55/54/2ffea79a7c1eabcede1926347ebc2a81bc6b81f447d05b52af9af14948b9/rignore-0.7.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:73c7aa109d41e593785c55fdaa89ad80b10330affa9f9d3e3a51fa695f739b20", size = 984245, upload-time = "2025-11-05T20:41:54.062Z" }, + { url = "https://files.pythonhosted.org/packages/41/f7/e80f55dfe0f35787fa482aa18689b9c8251e045076c35477deb0007b3277/rignore-0.7.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1734dc49d1e9501b07852ef44421f84d9f378da9fbeda729e77db71f49cac28b", size = 1078647, upload-time = "2025-11-05T21:40:13.463Z" }, + { url = "https://files.pythonhosted.org/packages/d4/cf/2c64f0b6725149f7c6e7e5a909d14354889b4beaadddaa5fff023ec71084/rignore-0.7.6-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5719ea14ea2b652c0c0894be5dfde954e1853a80dea27dd2fbaa749618d837f5", size = 1139186, upload-time = "2025-11-05T21:40:31.27Z" }, + { url = "https://files.pythonhosted.org/packages/75/95/a86c84909ccc24af0d094b50d54697951e576c252a4d9f21b47b52af9598/rignore-0.7.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8e23424fc7ce35726854f639cb7968151a792c0c3d9d082f7f67e0c362cfecca", size = 1117604, upload-time = "2025-11-05T21:40:48.07Z" }, + { url = "https://files.pythonhosted.org/packages/7f/5e/13b249613fd5d18d58662490ab910a9f0be758981d1797789913adb4e918/rignore-0.7.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3efdcf1dd84d45f3e2bd2f93303d9be103888f56dfa7c3349b5bf4f0657ec696", size = 1127725, upload-time = "2025-11-05T21:41:05.804Z" }, + { url = "https://files.pythonhosted.org/packages/c7/28/fa5dcd1e2e16982c359128664e3785f202d3eca9b22dd0b2f91c4b3d242f/rignore-0.7.6-cp312-cp312-win32.whl", hash = "sha256:ccca9d1a8b5234c76b71546fc3c134533b013f40495f394a65614a81f7387046", size = 646145, upload-time = "2025-11-05T21:41:51.096Z" }, + { url = "https://files.pythonhosted.org/packages/26/87/69387fb5dd81a0f771936381431780b8cf66fcd2cfe9495e1aaf41548931/rignore-0.7.6-cp312-cp312-win_amd64.whl", hash = "sha256:c96a285e4a8bfec0652e0bfcf42b1aabcdda1e7625f5006d188e3b1c87fdb543", size = 726090, upload-time = "2025-11-05T21:41:36.485Z" }, + { url = "https://files.pythonhosted.org/packages/24/5f/e8418108dcda8087fb198a6f81caadbcda9fd115d61154bf0df4d6d3619b/rignore-0.7.6-cp312-cp312-win_arm64.whl", hash = "sha256:a64a750e7a8277a323f01ca50b7784a764845f6cce2fe38831cb93f0508d0051", size = 656317, upload-time = "2025-11-05T21:41:25.305Z" }, + { url = "https://files.pythonhosted.org/packages/b7/8a/a4078f6e14932ac7edb171149c481de29969d96ddee3ece5dc4c26f9e0c3/rignore-0.7.6-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:2bdab1d31ec9b4fb1331980ee49ea051c0d7f7bb6baa28b3125ef03cdc48fdaf", size = 883057, upload-time = "2025-11-05T20:42:42.741Z" }, + { url = "https://files.pythonhosted.org/packages/f9/8f/f8daacd177db4bf7c2223bab41e630c52711f8af9ed279be2058d2fe4982/rignore-0.7.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:90f0a00ce0c866c275bf888271f1dc0d2140f29b82fcf33cdbda1e1a6af01010", size = 820150, upload-time = "2025-11-05T20:42:26.545Z" }, + { url = "https://files.pythonhosted.org/packages/36/31/b65b837e39c3f7064c426754714ac633b66b8c2290978af9d7f513e14aa9/rignore-0.7.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1ad295537041dc2ed4b540fb1a3906bd9ede6ccdad3fe79770cd89e04e3c73c", size = 897406, upload-time = "2025-11-05T20:40:53.854Z" }, + { url = "https://files.pythonhosted.org/packages/ca/58/1970ce006c427e202ac7c081435719a076c478f07b3a23f469227788dc23/rignore-0.7.6-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f782dbd3a65a5ac85adfff69e5c6b101285ef3f845c3a3cae56a54bebf9fe116", size = 874050, upload-time = "2025-11-05T20:41:08.922Z" }, + { url = "https://files.pythonhosted.org/packages/d4/00/eb45db9f90137329072a732273be0d383cb7d7f50ddc8e0bceea34c1dfdf/rignore-0.7.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65cece3b36e5b0826d946494734c0e6aaf5a0337e18ff55b071438efe13d559e", size = 1167835, upload-time = "2025-11-05T20:41:24.997Z" }, + { url = "https://files.pythonhosted.org/packages/f3/f1/6f1d72ddca41a64eed569680587a1236633587cc9f78136477ae69e2c88a/rignore-0.7.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d7e4bb66c13cd7602dc8931822c02dfbbd5252015c750ac5d6152b186f0a8be0", size = 941945, upload-time = "2025-11-05T20:41:40.628Z" }, + { url = "https://files.pythonhosted.org/packages/48/6f/2f178af1c1a276a065f563ec1e11e7a9e23d4996fd0465516afce4b5c636/rignore-0.7.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:297e500c15766e196f68aaaa70e8b6db85fa23fdc075b880d8231fdfba738cd7", size = 959067, upload-time = "2025-11-05T20:42:11.09Z" }, + { url = "https://files.pythonhosted.org/packages/5b/db/423a81c4c1e173877c7f9b5767dcaf1ab50484a94f60a0b2ed78be3fa765/rignore-0.7.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a07084211a8d35e1a5b1d32b9661a5ed20669970b369df0cf77da3adea3405de", size = 984438, upload-time = "2025-11-05T20:41:55.443Z" }, + { url = "https://files.pythonhosted.org/packages/31/eb/c4f92cc3f2825d501d3c46a244a671eb737fc1bcf7b05a3ecd34abb3e0d7/rignore-0.7.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:181eb2a975a22256a1441a9d2f15eb1292839ea3f05606620bd9e1938302cf79", size = 1078365, upload-time = "2025-11-05T21:40:15.148Z" }, + { url = "https://files.pythonhosted.org/packages/26/09/99442f02794bd7441bfc8ed1c7319e890449b816a7493b2db0e30af39095/rignore-0.7.6-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:7bbcdc52b5bf9f054b34ce4af5269df5d863d9c2456243338bc193c28022bd7b", size = 1139066, upload-time = "2025-11-05T21:40:32.771Z" }, + { url = "https://files.pythonhosted.org/packages/2c/88/bcfc21e520bba975410e9419450f4b90a2ac8236b9a80fd8130e87d098af/rignore-0.7.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f2e027a6da21a7c8c0d87553c24ca5cc4364def18d146057862c23a96546238e", size = 1118036, upload-time = "2025-11-05T21:40:49.646Z" }, + { url = "https://files.pythonhosted.org/packages/e2/25/d37215e4562cda5c13312636393aea0bafe38d54d4e0517520a4cc0753ec/rignore-0.7.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ee4a18b82cbbc648e4aac1510066682fe62beb5dc88e2c67c53a83954e541360", size = 1127550, upload-time = "2025-11-05T21:41:07.648Z" }, + { url = "https://files.pythonhosted.org/packages/dc/76/a264ab38bfa1620ec12a8ff1c07778da89e16d8c0f3450b0333020d3d6dc/rignore-0.7.6-cp313-cp313-win32.whl", hash = "sha256:a7d7148b6e5e95035d4390396895adc384d37ff4e06781a36fe573bba7c283e5", size = 646097, upload-time = "2025-11-05T21:41:53.201Z" }, + { url = "https://files.pythonhosted.org/packages/62/44/3c31b8983c29ea8832b6082ddb1d07b90379c2d993bd20fce4487b71b4f4/rignore-0.7.6-cp313-cp313-win_amd64.whl", hash = "sha256:b037c4b15a64dced08fc12310ee844ec2284c4c5c1ca77bc37d0a04f7bff386e", size = 726170, upload-time = "2025-11-05T21:41:38.131Z" }, + { url = "https://files.pythonhosted.org/packages/aa/41/e26a075cab83debe41a42661262f606166157df84e0e02e2d904d134c0d8/rignore-0.7.6-cp313-cp313-win_arm64.whl", hash = "sha256:e47443de9b12fe569889bdbe020abe0e0b667516ee2ab435443f6d0869bd2804", size = 656184, upload-time = "2025-11-05T21:41:27.396Z" }, + { url = "https://files.pythonhosted.org/packages/9a/b9/1f5bd82b87e5550cd843ceb3768b4a8ef274eb63f29333cf2f29644b3d75/rignore-0.7.6-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:8e41be9fa8f2f47239ded8920cc283699a052ac4c371f77f5ac017ebeed75732", size = 882632, upload-time = "2025-11-05T20:42:44.063Z" }, + { url = "https://files.pythonhosted.org/packages/e9/6b/07714a3efe4a8048864e8a5b7db311ba51b921e15268b17defaebf56d3db/rignore-0.7.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6dc1e171e52cefa6c20e60c05394a71165663b48bca6c7666dee4f778f2a7d90", size = 820760, upload-time = "2025-11-05T20:42:27.885Z" }, + { url = "https://files.pythonhosted.org/packages/ac/0f/348c829ea2d8d596e856371b14b9092f8a5dfbb62674ec9b3f67e4939a9d/rignore-0.7.6-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ce2268837c3600f82ab8db58f5834009dc638ee17103582960da668963bebc5", size = 899044, upload-time = "2025-11-05T20:40:55.336Z" }, + { url = "https://files.pythonhosted.org/packages/f0/30/2e1841a19b4dd23878d73edd5d82e998a83d5ed9570a89675f140ca8b2ad/rignore-0.7.6-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:690a3e1b54bfe77e89c4bacb13f046e642f8baadafc61d68f5a726f324a76ab6", size = 874144, upload-time = "2025-11-05T20:41:10.195Z" }, + { url = "https://files.pythonhosted.org/packages/c2/bf/0ce9beb2e5f64c30e3580bef09f5829236889f01511a125f98b83169b993/rignore-0.7.6-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09d12ac7a0b6210c07bcd145007117ebd8abe99c8eeb383e9e4673910c2754b2", size = 1168062, upload-time = "2025-11-05T20:41:26.511Z" }, + { url = "https://files.pythonhosted.org/packages/b9/8b/571c178414eb4014969865317da8a02ce4cf5241a41676ef91a59aab24de/rignore-0.7.6-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a2b2b74a8c60203b08452479b90e5ce3dbe96a916214bc9eb2e5af0b6a9beb0", size = 942542, upload-time = "2025-11-05T20:41:41.838Z" }, + { url = "https://files.pythonhosted.org/packages/19/62/7a3cf601d5a45137a7e2b89d10c05b5b86499190c4b7ca5c3c47d79ee519/rignore-0.7.6-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8fc5a531ef02131e44359419a366bfac57f773ea58f5278c2cdd915f7d10ea94", size = 958739, upload-time = "2025-11-05T20:42:12.463Z" }, + { url = "https://files.pythonhosted.org/packages/5f/1f/4261f6a0d7caf2058a5cde2f5045f565ab91aa7badc972b57d19ce58b14e/rignore-0.7.6-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b7a1f77d9c4cd7e76229e252614d963442686bfe12c787a49f4fe481df49e7a9", size = 984138, upload-time = "2025-11-05T20:41:56.775Z" }, + { url = "https://files.pythonhosted.org/packages/2b/bf/628dfe19c75e8ce1f45f7c248f5148b17dfa89a817f8e3552ab74c3ae812/rignore-0.7.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ead81f728682ba72b5b1c3d5846b011d3e0174da978de87c61645f2ed36659a7", size = 1079299, upload-time = "2025-11-05T21:40:16.639Z" }, + { url = "https://files.pythonhosted.org/packages/af/a5/be29c50f5c0c25c637ed32db8758fdf5b901a99e08b608971cda8afb293b/rignore-0.7.6-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:12ffd50f520c22ffdabed8cd8bfb567d9ac165b2b854d3e679f4bcaef11a9441", size = 1139618, upload-time = "2025-11-05T21:40:34.507Z" }, + { url = "https://files.pythonhosted.org/packages/2a/40/3c46cd7ce4fa05c20b525fd60f599165e820af66e66f2c371cd50644558f/rignore-0.7.6-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:e5a16890fbe3c894f8ca34b0fcacc2c200398d4d46ae654e03bc9b3dbf2a0a72", size = 1117626, upload-time = "2025-11-05T21:40:51.494Z" }, + { url = "https://files.pythonhosted.org/packages/8c/b9/aea926f263b8a29a23c75c2e0d8447965eb1879d3feb53cfcf84db67ed58/rignore-0.7.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3abab3bf99e8a77488ef6c7c9a799fac22224c28fe9f25cc21aa7cc2b72bfc0b", size = 1128144, upload-time = "2025-11-05T21:41:09.169Z" }, + { url = "https://files.pythonhosted.org/packages/a4/f6/0d6242f8d0df7f2ecbe91679fefc1f75e7cd2072cb4f497abaab3f0f8523/rignore-0.7.6-cp314-cp314-win32.whl", hash = "sha256:eeef421c1782953c4375aa32f06ecae470c1285c6381eee2a30d2e02a5633001", size = 646385, upload-time = "2025-11-05T21:41:55.105Z" }, + { url = "https://files.pythonhosted.org/packages/d5/38/c0dcd7b10064f084343d6af26fe9414e46e9619c5f3224b5272e8e5d9956/rignore-0.7.6-cp314-cp314-win_amd64.whl", hash = "sha256:6aeed503b3b3d5af939b21d72a82521701a4bd3b89cd761da1e7dc78621af304", size = 725738, upload-time = "2025-11-05T21:41:39.736Z" }, + { url = "https://files.pythonhosted.org/packages/d9/7a/290f868296c1ece914d565757ab363b04730a728b544beb567ceb3b2d96f/rignore-0.7.6-cp314-cp314-win_arm64.whl", hash = "sha256:104f215b60b3c984c386c3e747d6ab4376d5656478694e22c7bd2f788ddd8304", size = 656008, upload-time = "2025-11-05T21:41:29.028Z" }, + { url = "https://files.pythonhosted.org/packages/ca/d2/3c74e3cd81fe8ea08a8dcd2d755c09ac2e8ad8fe409508904557b58383d3/rignore-0.7.6-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:bb24a5b947656dd94cb9e41c4bc8b23cec0c435b58be0d74a874f63c259549e8", size = 882835, upload-time = "2025-11-05T20:42:45.443Z" }, + { url = "https://files.pythonhosted.org/packages/77/61/a772a34b6b63154877433ac2d048364815b24c2dd308f76b212c408101a2/rignore-0.7.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5b1e33c9501cefe24b70a1eafd9821acfd0ebf0b35c3a379430a14df089993e3", size = 820301, upload-time = "2025-11-05T20:42:29.226Z" }, + { url = "https://files.pythonhosted.org/packages/71/30/054880b09c0b1b61d17eeb15279d8bf729c0ba52b36c3ada52fb827cbb3c/rignore-0.7.6-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bec3994665a44454df86deb762061e05cd4b61e3772f5b07d1882a8a0d2748d5", size = 897611, upload-time = "2025-11-05T20:40:56.475Z" }, + { url = "https://files.pythonhosted.org/packages/1e/40/b2d1c169f833d69931bf232600eaa3c7998ba4f9a402e43a822dad2ea9f2/rignore-0.7.6-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:26cba2edfe3cff1dfa72bddf65d316ddebf182f011f2f61538705d6dbaf54986", size = 873875, upload-time = "2025-11-05T20:41:11.561Z" }, + { url = "https://files.pythonhosted.org/packages/55/59/ca5ae93d83a1a60e44b21d87deb48b177a8db1b85e82fc8a9abb24a8986d/rignore-0.7.6-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ffa86694fec604c613696cb91e43892aa22e1fec5f9870e48f111c603e5ec4e9", size = 1167245, upload-time = "2025-11-05T20:41:28.29Z" }, + { url = "https://files.pythonhosted.org/packages/a5/52/cf3dce392ba2af806cba265aad6bcd9c48bb2a6cb5eee448d3319f6e505b/rignore-0.7.6-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48efe2ed95aa8104145004afb15cdfa02bea5cdde8b0344afeb0434f0d989aa2", size = 941750, upload-time = "2025-11-05T20:41:43.111Z" }, + { url = "https://files.pythonhosted.org/packages/ec/be/3f344c6218d779395e785091d05396dfd8b625f6aafbe502746fcd880af2/rignore-0.7.6-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8dcae43eb44b7f2457fef7cc87f103f9a0013017a6f4e62182c565e924948f21", size = 958896, upload-time = "2025-11-05T20:42:13.784Z" }, + { url = "https://files.pythonhosted.org/packages/c9/34/d3fa71938aed7d00dcad87f0f9bcb02ad66c85d6ffc83ba31078ce53646a/rignore-0.7.6-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2cd649a7091c0dad2f11ef65630d30c698d505cbe8660dd395268e7c099cc99f", size = 983992, upload-time = "2025-11-05T20:41:58.022Z" }, + { url = "https://files.pythonhosted.org/packages/24/a4/52a697158e9920705bdbd0748d59fa63e0f3233fb92e9df9a71afbead6ca/rignore-0.7.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:42de84b0289d478d30ceb7ae59023f7b0527786a9a5b490830e080f0e4ea5aeb", size = 1078181, upload-time = "2025-11-05T21:40:18.151Z" }, + { url = "https://files.pythonhosted.org/packages/ac/65/aa76dbcdabf3787a6f0fd61b5cc8ed1e88580590556d6c0207960d2384bb/rignore-0.7.6-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:875a617e57b53b4acbc5a91de418233849711c02e29cc1f4f9febb2f928af013", size = 1139232, upload-time = "2025-11-05T21:40:35.966Z" }, + { url = "https://files.pythonhosted.org/packages/08/44/31b31a49b3233c6842acc1c0731aa1e7fb322a7170612acf30327f700b44/rignore-0.7.6-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:8703998902771e96e49968105207719f22926e4431b108450f3f430b4e268b7c", size = 1117349, upload-time = "2025-11-05T21:40:53.013Z" }, + { url = "https://files.pythonhosted.org/packages/e9/ae/1b199a2302c19c658cf74e5ee1427605234e8c91787cfba0015f2ace145b/rignore-0.7.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:602ef33f3e1b04c1e9a10a3c03f8bc3cef2d2383dcc250d309be42b49923cabc", size = 1127702, upload-time = "2025-11-05T21:41:10.881Z" }, + { url = "https://files.pythonhosted.org/packages/fc/d3/18210222b37e87e36357f7b300b7d98c6dd62b133771e71ae27acba83a4f/rignore-0.7.6-cp314-cp314t-win32.whl", hash = "sha256:c1d8f117f7da0a4a96a8daef3da75bc090e3792d30b8b12cfadc240c631353f9", size = 647033, upload-time = "2025-11-05T21:42:00.095Z" }, + { url = "https://files.pythonhosted.org/packages/3e/87/033eebfbee3ec7d92b3bb1717d8f68c88e6fc7de54537040f3b3a405726f/rignore-0.7.6-cp314-cp314t-win_amd64.whl", hash = "sha256:ca36e59408bec81de75d307c568c2d0d410fb880b1769be43611472c61e85c96", size = 725647, upload-time = "2025-11-05T21:41:44.449Z" }, + { url = "https://files.pythonhosted.org/packages/79/62/b88e5879512c55b8ee979c666ee6902adc4ed05007226de266410ae27965/rignore-0.7.6-cp314-cp314t-win_arm64.whl", hash = "sha256:b83adabeb3e8cf662cabe1931b83e165b88c526fa6af6b3aa90429686e474896", size = 656035, upload-time = "2025-11-05T21:41:31.13Z" }, +] + [[package]] name = "ruff" -version = "0.14.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/52/f0/62b5a1a723fe183650109407fa56abb433b00aa1c0b9ba555f9c4efec2c6/ruff-0.14.6.tar.gz", hash = "sha256:6f0c742ca6a7783a736b867a263b9a7a80a45ce9bee391eeda296895f1b4e1cc", size = 5669501, upload-time = "2025-11-21T14:26:17.903Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/67/d2/7dd544116d107fffb24a0064d41a5d2ed1c9d6372d142f9ba108c8e39207/ruff-0.14.6-py3-none-linux_armv6l.whl", hash = "sha256:d724ac2f1c240dbd01a2ae98db5d1d9a5e1d9e96eba999d1c48e30062df578a3", size = 13326119, upload-time = "2025-11-21T14:25:24.2Z" }, - { url = "https://files.pythonhosted.org/packages/36/6a/ad66d0a3315d6327ed6b01f759d83df3c4d5f86c30462121024361137b6a/ruff-0.14.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9f7539ea257aa4d07b7ce87aed580e485c40143f2473ff2f2b75aee003186004", size = 13526007, upload-time = "2025-11-21T14:25:26.906Z" }, - { url = "https://files.pythonhosted.org/packages/a3/9d/dae6db96df28e0a15dea8e986ee393af70fc97fd57669808728080529c37/ruff-0.14.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7f6007e55b90a2a7e93083ba48a9f23c3158c433591c33ee2e99a49b889c6332", size = 12676572, upload-time = "2025-11-21T14:25:29.826Z" }, - { url = "https://files.pythonhosted.org/packages/76/a4/f319e87759949062cfee1b26245048e92e2acce900ad3a909285f9db1859/ruff-0.14.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a8e7b9d73d8728b68f632aa8e824ef041d068d231d8dbc7808532d3629a6bef", size = 13140745, upload-time = "2025-11-21T14:25:32.788Z" }, - { url = "https://files.pythonhosted.org/packages/95/d3/248c1efc71a0a8ed4e8e10b4b2266845d7dfc7a0ab64354afe049eaa1310/ruff-0.14.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d50d45d4553a3ebcbd33e7c5e0fe6ca4aafd9a9122492de357205c2c48f00775", size = 13076486, upload-time = "2025-11-21T14:25:35.601Z" }, - { url = "https://files.pythonhosted.org/packages/a5/19/b68d4563fe50eba4b8c92aa842149bb56dd24d198389c0ed12e7faff4f7d/ruff-0.14.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:118548dd121f8a21bfa8ab2c5b80e5b4aed67ead4b7567790962554f38e598ce", size = 13727563, upload-time = "2025-11-21T14:25:38.514Z" }, - { url = "https://files.pythonhosted.org/packages/47/ac/943169436832d4b0e867235abbdb57ce3a82367b47e0280fa7b4eabb7593/ruff-0.14.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:57256efafbfefcb8748df9d1d766062f62b20150691021f8ab79e2d919f7c11f", size = 15199755, upload-time = "2025-11-21T14:25:41.516Z" }, - { url = "https://files.pythonhosted.org/packages/c9/b9/288bb2399860a36d4bb0541cb66cce3c0f4156aaff009dc8499be0c24bf2/ruff-0.14.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ff18134841e5c68f8e5df1999a64429a02d5549036b394fafbe410f886e1989d", size = 14850608, upload-time = "2025-11-21T14:25:44.428Z" }, - { url = "https://files.pythonhosted.org/packages/ee/b1/a0d549dd4364e240f37e7d2907e97ee80587480d98c7799d2d8dc7a2f605/ruff-0.14.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29c4b7ec1e66a105d5c27bd57fa93203637d66a26d10ca9809dc7fc18ec58440", size = 14118754, upload-time = "2025-11-21T14:25:47.214Z" }, - { url = "https://files.pythonhosted.org/packages/13/ac/9b9fe63716af8bdfddfacd0882bc1586f29985d3b988b3c62ddce2e202c3/ruff-0.14.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:167843a6f78680746d7e226f255d920aeed5e4ad9c03258094a2d49d3028b105", size = 13949214, upload-time = "2025-11-21T14:25:50.002Z" }, - { url = "https://files.pythonhosted.org/packages/12/27/4dad6c6a77fede9560b7df6802b1b697e97e49ceabe1f12baf3ea20862e9/ruff-0.14.6-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:16a33af621c9c523b1ae006b1b99b159bf5ac7e4b1f20b85b2572455018e0821", size = 14106112, upload-time = "2025-11-21T14:25:52.841Z" }, - { url = "https://files.pythonhosted.org/packages/6a/db/23e322d7177873eaedea59a7932ca5084ec5b7e20cb30f341ab594130a71/ruff-0.14.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1432ab6e1ae2dc565a7eea707d3b03a0c234ef401482a6f1621bc1f427c2ff55", size = 13035010, upload-time = "2025-11-21T14:25:55.536Z" }, - { url = "https://files.pythonhosted.org/packages/a8/9c/20e21d4d69dbb35e6a1df7691e02f363423658a20a2afacf2a2c011800dc/ruff-0.14.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:4c55cfbbe7abb61eb914bfd20683d14cdfb38a6d56c6c66efa55ec6570ee4e71", size = 13054082, upload-time = "2025-11-21T14:25:58.625Z" }, - { url = "https://files.pythonhosted.org/packages/66/25/906ee6a0464c3125c8d673c589771a974965c2be1a1e28b5c3b96cb6ef88/ruff-0.14.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:efea3c0f21901a685fff4befda6d61a1bf4cb43de16da87e8226a281d614350b", size = 13303354, upload-time = "2025-11-21T14:26:01.816Z" }, - { url = "https://files.pythonhosted.org/packages/4c/58/60577569e198d56922b7ead07b465f559002b7b11d53f40937e95067ca1c/ruff-0.14.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:344d97172576d75dc6afc0e9243376dbe1668559c72de1864439c4fc95f78185", size = 14054487, upload-time = "2025-11-21T14:26:05.058Z" }, - { url = "https://files.pythonhosted.org/packages/67/0b/8e4e0639e4cc12547f41cb771b0b44ec8225b6b6a93393176d75fe6f7d40/ruff-0.14.6-py3-none-win32.whl", hash = "sha256:00169c0c8b85396516fdd9ce3446c7ca20c2a8f90a77aa945ba6b8f2bfe99e85", size = 13013361, upload-time = "2025-11-21T14:26:08.152Z" }, - { url = "https://files.pythonhosted.org/packages/fb/02/82240553b77fd1341f80ebb3eaae43ba011c7a91b4224a9f317d8e6591af/ruff-0.14.6-py3-none-win_amd64.whl", hash = "sha256:390e6480c5e3659f8a4c8d6a0373027820419ac14fa0d2713bd8e6c3e125b8b9", size = 14432087, upload-time = "2025-11-21T14:26:10.891Z" }, - { url = "https://files.pythonhosted.org/packages/a5/1f/93f9b0fad9470e4c829a5bb678da4012f0c710d09331b860ee555216f4ea/ruff-0.14.6-py3-none-win_arm64.whl", hash = "sha256:d43c81fbeae52cfa8728d8766bbf46ee4298c888072105815b392da70ca836b2", size = 13520930, upload-time = "2025-11-21T14:26:13.951Z" }, +version = "0.15.17" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/a9/3abdf488f1bf3d24c699415e454ed554a6350d5d89ce183be1ee0a3361ac/ruff-0.15.17.tar.gz", hash = "sha256:2ec446937fd16c8c4de2674a209cc5af64d9c6f17d21fbf1151054fa0bcf5219", size = 4743346, upload-time = "2026-06-11T17:54:47.663Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/4d/e11259f5da07cb6afb2d074c31bf09da9671993f7329d4f15d2fdc458301/ruff-0.15.17-py3-none-linux_armv6l.whl", hash = "sha256:d9feddb927fc68bd295f5eebc587a7e42cfaf9b65f60ca4a2386febff575da8f", size = 10856677, upload-time = "2026-06-11T17:54:49.533Z" }, + { url = "https://files.pythonhosted.org/packages/29/3e/772d679e1a0dc058e58875bd2c0cb713a0530877b4a76fee3c7966df0d49/ruff-0.15.17-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:25805a226d741c47d274a35ad5c10a7dde175fcddfa511d7cf3da0a21eb3eab7", size = 11223443, upload-time = "2026-06-11T17:55:00.573Z" }, + { url = "https://files.pythonhosted.org/packages/68/58/bd41f7688b2fd5623012605130ed70e60aa7f2244baa3d5066bdd61530c8/ruff-0.15.17-py3-none-macosx_11_0_arm64.whl", hash = "sha256:f6ad73b14c2d18a3bf8ad7cb6974294d7f613a7898604826058e6ac64918ef4d", size = 10566458, upload-time = "2026-06-11T17:55:07.52Z" }, + { url = "https://files.pythonhosted.org/packages/d8/5b/733371013fcf1ec339e477ece6ab42bfe10bdd9bba8ee88a9516aa56bfc0/ruff-0.15.17-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ba0c1e4f95bcb3869d0d30cbd5917071ef2e28665abfec970cdab0492c713ed", size = 10914483, upload-time = "2026-06-11T17:55:05.501Z" }, + { url = "https://files.pythonhosted.org/packages/bd/cc/6f24251cc0252f7239391ccb85833f320efad14ebe5b443943f37ced6332/ruff-0.15.17-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:81647960f10bff57d2e51cadd0c3950fe598400c852863a038720ef5b8cca91e", size = 10647497, upload-time = "2026-06-11T17:54:57.733Z" }, + { url = "https://files.pythonhosted.org/packages/68/dd/0d10c17ce1a1624d6fc3156309c3f834fdb5dfaad026ec90c85684f3990e/ruff-0.15.17-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e01a84ddbc8c16c23055ba3924476850f1bbc1917cebbb9376665a63e74260d", size = 11416967, upload-time = "2026-06-11T17:54:51.461Z" }, + { url = "https://files.pythonhosted.org/packages/2f/91/556bfb156f6144f355e831c23db00b2fc4120f86b3ce81cc5f7fd2df51f3/ruff-0.15.17-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84fe9f653152f8f294f9f7e03bf3a453d8b4a27f7a59c78c8666167f2b17b96c", size = 12335770, upload-time = "2026-06-11T17:54:45.793Z" }, + { url = "https://files.pythonhosted.org/packages/88/82/8b5999aa13355e926f06d9f42a32dcca862f623bf0363785ff89d607dffd/ruff-0.15.17-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c0fe88a7676e7a05b73174d4d4a59cb2ac21ff8263583f87a81a6018475a978", size = 11575441, upload-time = "2026-06-11T17:54:32.661Z" }, + { url = "https://files.pythonhosted.org/packages/11/93/f10377bb04109ca0e8cbc483ff1982c54b6d418210041776f93e8cdc7fa9/ruff-0.15.17-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ecfc3c7878fff94633ab0348524e093f9ce3243080416dd7d14f8ba400174719", size = 11557614, upload-time = "2026-06-11T17:54:34.698Z" }, + { url = "https://files.pythonhosted.org/packages/c7/a6/eeeae7f7d5493df41649ab3db92f086b2d0a30199e4efdf8e3dd7a033f24/ruff-0.15.17-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:b8461180b22420b1bdc289909410930761629fddf2a5aaf60fae1ab26cedc4c4", size = 11544450, upload-time = "2026-06-11T17:54:39.042Z" }, + { url = "https://files.pythonhosted.org/packages/32/88/5991ce565129a24dd4a00db1254b3b5db2e53018cbe4018ea5a89738e727/ruff-0.15.17-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6eccbe50a038b503e7140b441aa9c7fc8c1f36edf23ebef9f4165c2f28f568b7", size = 10892524, upload-time = "2026-06-11T17:55:09.432Z" }, + { url = "https://files.pythonhosted.org/packages/f5/1d/0fdd248313425f55223968af04b0a42125466a8d88d21c1d99c6af0a51e8/ruff-0.15.17-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:382fc0521025f5a8ad447d8bdd523545d0d7646adb718eb1c2dac5065ec27c0f", size = 10659573, upload-time = "2026-06-11T17:54:36.824Z" }, + { url = "https://files.pythonhosted.org/packages/9e/0e/072e8260deb9461062ce9311ced27a8e541229a6ffd483013dd37661e43e/ruff-0.15.17-py3-none-musllinux_1_2_i686.whl", hash = "sha256:456d41fcd1b2777ad63f09a6e7121d43f7b688bbc76a800c10f7f8fb1f912c3f", size = 11127818, upload-time = "2026-06-11T17:55:03.124Z" }, + { url = "https://files.pythonhosted.org/packages/ab/b4/55060a34163121498014696b5f656db5b8c6963768f227dbf0d76b311073/ruff-0.15.17-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b1a04bcc94ae6194e9db05d16ad31f298a7194bfbcb08258bbe589cee1d587b8", size = 11655901, upload-time = "2026-06-11T17:54:53.562Z" }, + { url = "https://files.pythonhosted.org/packages/49/71/9b29d6b87cef468d697f43c6a91e3fae4a80185779d7d5a4ef27d173439f/ruff-0.15.17-py3-none-win32.whl", hash = "sha256:596065960ab1ff593f744220c9fe6580eda00a95003cffa9f4048bb5b1bf0392", size = 10925574, upload-time = "2026-06-11T17:54:55.723Z" }, + { url = "https://files.pythonhosted.org/packages/3d/b2/8fc77f3723228836fa5d12497eb71c808f83782e10d058d2b15cfa14640b/ruff-0.15.17-py3-none-win_amd64.whl", hash = "sha256:6769e5fa1710b179b92e0bfa5a51735b35baea9013dadb06d5f44cbcf9547084", size = 12058788, upload-time = "2026-06-11T17:54:41.042Z" }, + { url = "https://files.pythonhosted.org/packages/2d/c7/c53e8dbff9c9dc4b7928773421ae294a5d28fcb8dcda1a089579d3a7e510/ruff-0.15.17-py3-none-win_arm64.whl", hash = "sha256:f3be1fbb34bcdfd146240d8fb92a709d4c2c8191348580a3c044ec60fa0b4456", size = 11355275, upload-time = "2026-06-11T17:54:43.635Z" }, +] + +[[package]] +name = "sentry-sdk" +version = "2.62.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f6/5d/a343201726150e05f2036eeb6e493e2e2f8bf8a66f5aa70f2f4ac96f9ca3/sentry_sdk-2.62.0.tar.gz", hash = "sha256:3c870b9f50d9fd15b58c817dbde1c7cfaa9fe3f05df0a4c6edd5571cb82f5491", size = 463986, upload-time = "2026-06-08T13:23:49.223Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3d/07/05440381627877aae223fd68f330df9b9fc6641d08bf65328b55235617a2/sentry_sdk-2.62.0-py3-none-any.whl", hash = "sha256:27f61d13a86c3c1648dec666dd5a64f79772dd6a84b446f11866601ecab24f6f", size = 490586, upload-time = "2026-06-08T13:23:47.486Z" }, +] + +[[package]] +name = "shellingham" +version = "1.5.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, ] [[package]] @@ -1457,31 +1852,43 @@ wheels = [ [[package]] name = "sqlalchemy" -version = "2.0.44" +version = "2.0.50" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f0/f2/840d7b9496825333f532d2e3976b8eadbf52034178aac53630d09fe6e1ef/sqlalchemy-2.0.44.tar.gz", hash = "sha256:0ae7454e1ab1d780aee69fd2aae7d6b8670a581d8847f2d1e0f7ddfbf47e5a22", size = 9819830, upload-time = "2025-10-10T14:39:12.935Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/62/c4/59c7c9b068e6813c898b771204aad36683c96318ed12d4233e1b18762164/sqlalchemy-2.0.44-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:72fea91746b5890f9e5e0997f16cbf3d53550580d76355ba2d998311b17b2250", size = 2139675, upload-time = "2025-10-10T16:03:31.064Z" }, - { url = "https://files.pythonhosted.org/packages/d6/ae/eeb0920537a6f9c5a3708e4a5fc55af25900216bdb4847ec29cfddf3bf3a/sqlalchemy-2.0.44-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:585c0c852a891450edbb1eaca8648408a3cc125f18cf433941fa6babcc359e29", size = 2127726, upload-time = "2025-10-10T16:03:35.934Z" }, - { url = "https://files.pythonhosted.org/packages/d8/d5/2ebbabe0379418eda8041c06b0b551f213576bfe4c2f09d77c06c07c8cc5/sqlalchemy-2.0.44-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b94843a102efa9ac68a7a30cd46df3ff1ed9c658100d30a725d10d9c60a2f44", size = 3327603, upload-time = "2025-10-10T15:35:28.322Z" }, - { url = "https://files.pythonhosted.org/packages/45/e5/5aa65852dadc24b7d8ae75b7efb8d19303ed6ac93482e60c44a585930ea5/sqlalchemy-2.0.44-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:119dc41e7a7defcefc57189cfa0e61b1bf9c228211aba432b53fb71ef367fda1", size = 3337842, upload-time = "2025-10-10T15:43:45.431Z" }, - { url = "https://files.pythonhosted.org/packages/41/92/648f1afd3f20b71e880ca797a960f638d39d243e233a7082c93093c22378/sqlalchemy-2.0.44-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0765e318ee9179b3718c4fd7ba35c434f4dd20332fbc6857a5e8df17719c24d7", size = 3264558, upload-time = "2025-10-10T15:35:29.93Z" }, - { url = "https://files.pythonhosted.org/packages/40/cf/e27d7ee61a10f74b17740918e23cbc5bc62011b48282170dc4c66da8ec0f/sqlalchemy-2.0.44-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2e7b5b079055e02d06a4308d0481658e4f06bc7ef211567edc8f7d5dce52018d", size = 3301570, upload-time = "2025-10-10T15:43:48.407Z" }, - { url = "https://files.pythonhosted.org/packages/3b/3d/3116a9a7b63e780fb402799b6da227435be878b6846b192f076d2f838654/sqlalchemy-2.0.44-cp312-cp312-win32.whl", hash = "sha256:846541e58b9a81cce7dee8329f352c318de25aa2f2bbe1e31587eb1f057448b4", size = 2103447, upload-time = "2025-10-10T15:03:21.678Z" }, - { url = "https://files.pythonhosted.org/packages/25/83/24690e9dfc241e6ab062df82cc0df7f4231c79ba98b273fa496fb3dd78ed/sqlalchemy-2.0.44-cp312-cp312-win_amd64.whl", hash = "sha256:7cbcb47fd66ab294703e1644f78971f6f2f1126424d2b300678f419aa73c7b6e", size = 2130912, upload-time = "2025-10-10T15:03:24.656Z" }, - { url = "https://files.pythonhosted.org/packages/45/d3/c67077a2249fdb455246e6853166360054c331db4613cda3e31ab1cadbef/sqlalchemy-2.0.44-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ff486e183d151e51b1d694c7aa1695747599bb00b9f5f604092b54b74c64a8e1", size = 2135479, upload-time = "2025-10-10T16:03:37.671Z" }, - { url = "https://files.pythonhosted.org/packages/2b/91/eabd0688330d6fd114f5f12c4f89b0d02929f525e6bf7ff80aa17ca802af/sqlalchemy-2.0.44-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b1af8392eb27b372ddb783b317dea0f650241cea5bd29199b22235299ca2e45", size = 2123212, upload-time = "2025-10-10T16:03:41.755Z" }, - { url = "https://files.pythonhosted.org/packages/b0/bb/43e246cfe0e81c018076a16036d9b548c4cc649de241fa27d8d9ca6f85ab/sqlalchemy-2.0.44-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2b61188657e3a2b9ac4e8f04d6cf8e51046e28175f79464c67f2fd35bceb0976", size = 3255353, upload-time = "2025-10-10T15:35:31.221Z" }, - { url = "https://files.pythonhosted.org/packages/b9/96/c6105ed9a880abe346b64d3b6ddef269ddfcab04f7f3d90a0bf3c5a88e82/sqlalchemy-2.0.44-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b87e7b91a5d5973dda5f00cd61ef72ad75a1db73a386b62877d4875a8840959c", size = 3260222, upload-time = "2025-10-10T15:43:50.124Z" }, - { url = "https://files.pythonhosted.org/packages/44/16/1857e35a47155b5ad927272fee81ae49d398959cb749edca6eaa399b582f/sqlalchemy-2.0.44-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:15f3326f7f0b2bfe406ee562e17f43f36e16167af99c4c0df61db668de20002d", size = 3189614, upload-time = "2025-10-10T15:35:32.578Z" }, - { url = "https://files.pythonhosted.org/packages/88/ee/4afb39a8ee4fc786e2d716c20ab87b5b1fb33d4ac4129a1aaa574ae8a585/sqlalchemy-2.0.44-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e77faf6ff919aa8cd63f1c4e561cac1d9a454a191bb864d5dd5e545935e5a40", size = 3226248, upload-time = "2025-10-10T15:43:51.862Z" }, - { url = "https://files.pythonhosted.org/packages/32/d5/0e66097fc64fa266f29a7963296b40a80d6a997b7ac13806183700676f86/sqlalchemy-2.0.44-cp313-cp313-win32.whl", hash = "sha256:ee51625c2d51f8baadf2829fae817ad0b66b140573939dd69284d2ba3553ae73", size = 2101275, upload-time = "2025-10-10T15:03:26.096Z" }, - { url = "https://files.pythonhosted.org/packages/03/51/665617fe4f8c6450f42a6d8d69243f9420f5677395572c2fe9d21b493b7b/sqlalchemy-2.0.44-cp313-cp313-win_amd64.whl", hash = "sha256:c1c80faaee1a6c3428cecf40d16a2365bcf56c424c92c2b6f0f9ad204b899e9e", size = 2127901, upload-time = "2025-10-10T15:03:27.548Z" }, - { url = "https://files.pythonhosted.org/packages/9c/5e/6a29fa884d9fb7ddadf6b69490a9d45fded3b38541713010dad16b77d015/sqlalchemy-2.0.44-py3-none-any.whl", hash = "sha256:19de7ca1246fbef9f9d1bff8f1ab25641569df226364a0e40457dc5457c54b05", size = 1928718, upload-time = "2025-10-10T15:29:45.32Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/57/da/6fbf010c8ebb347679d0d100b22fe9ba5e13fd04046c5df7280d2f0bf706/sqlalchemy-2.0.50.tar.gz", hash = "sha256:af5607d11ef90fd6a5c0549fe0045dce1663d427426bcfb506dcb5346a85a3b9", size = 9907424, upload-time = "2026-05-24T19:20:04.018Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/b0/a9d19b43f38f878b1278bca5b00b909f7540d41494396dd2561f9ad0956d/sqlalchemy-2.0.50-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:23ae23d8b9d344d30d0a92f06d45825024a5790f1c1dd4cf452636a50d3e58cb", size = 2159807, upload-time = "2026-05-24T19:27:53.086Z" }, + { url = "https://files.pythonhosted.org/packages/f5/2c/191dd58a248fd2cfd4780fa82c375c505e4ad98c8b522fa69ec492130d77/sqlalchemy-2.0.50-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:47b71b933e7b4ebad407c8fdfd70d2c4f08b78b3238bb30eebdd6eb32ca51b89", size = 3343358, upload-time = "2026-05-24T20:09:29.279Z" }, + { url = "https://files.pythonhosted.org/packages/8a/2b/514fce8a7df81cf5bad7ff7865de7ac0c5776a38cc043475c4703eb7fe8b/sqlalchemy-2.0.50-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:110fdac56ace278949f00de805edacbd6141e382d992f9ba28238b3a0827a600", size = 3357994, upload-time = "2026-05-24T20:17:13.495Z" }, + { url = "https://files.pythonhosted.org/packages/35/a6/a0e283f5494f92b0d77e319ff77e437b1ffe4a051ba67c81d53234825475/sqlalchemy-2.0.50-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0f5e4ac70e9e757f6b3e87c0491ff034442ecd8dfd36d041a50564c322dafc0e", size = 3289399, upload-time = "2026-05-24T20:09:32.239Z" }, + { url = "https://files.pythonhosted.org/packages/b7/96/1b07325ba71752d6a028b77d07bed1483ad545f794e8b1dc89b3ba3b3c68/sqlalchemy-2.0.50-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:724f3dcbe53dd0151e3cb5e7ec4ba4c620bede579caacd16275dc35ce06e8615", size = 3321216, upload-time = "2026-05-24T20:17:15.581Z" }, + { url = "https://files.pythonhosted.org/packages/ed/8e/bad6ed253e8a99edfc99af02f7173ec48a1d3ed1b9b35a1b8bc1700900cc/sqlalchemy-2.0.50-cp312-cp312-win32.whl", hash = "sha256:1208050441471d003b7c8cb4054fb084f185cf35ac3f0ea270803865bca9939a", size = 2119194, upload-time = "2026-05-24T19:50:04.943Z" }, + { url = "https://files.pythonhosted.org/packages/b6/2d/314a6690dda4b9cfc571eab1a63cf6fe6e1470aa3759ccda6aa016ee0f5a/sqlalchemy-2.0.50-cp312-cp312-win_amd64.whl", hash = "sha256:9d1af51558029a156a70986b7df88f042b3d158d7c8d8fb5072912d4b32d89c7", size = 2146186, upload-time = "2026-05-24T19:50:06.74Z" }, + { url = "https://files.pythonhosted.org/packages/0b/c4/c42356b527296e9862f67990efce31ef78b4cf69cd3f80873a528a060320/sqlalchemy-2.0.50-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:06a9210bdc5f4298cff0781087e2ff45683922252dacc452846373a58761f093", size = 2156697, upload-time = "2026-05-24T19:27:54.764Z" }, + { url = "https://files.pythonhosted.org/packages/60/a1/b1a70e3c4365ac7fe9e347f3710f19b562c866fb96d45e3c891588789a7b/sqlalchemy-2.0.50-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b53784972ade4f8174b9aa661f31a06f8a936d2cfdd602913ff3c6dd40ae873", size = 3284260, upload-time = "2026-05-24T20:09:34.195Z" }, + { url = "https://files.pythonhosted.org/packages/3f/4a/f3ac3caa19f263d57b0a47f8c91bbf56583dc2d3fc63acfbf644abb24fe0/sqlalchemy-2.0.50-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:31648fa14460537e768a7303b078e4344d208e0d23e06867c1f376a227ed82db", size = 3302280, upload-time = "2026-05-24T20:17:17.825Z" }, + { url = "https://files.pythonhosted.org/packages/66/55/ccada3e3d62254587819749a0bc69f41173eb48a6e385d10e66d32a9c88e/sqlalchemy-2.0.50-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:03f4323c980ad0e918cc9e5369b015f759f4e534db5bbaf4dc36832c10d05064", size = 3231580, upload-time = "2026-05-24T20:09:36.406Z" }, + { url = "https://files.pythonhosted.org/packages/05/f6/6809349130a2de0e109e7f00fd7d431da9565b9b2868b32ee684754f672b/sqlalchemy-2.0.50-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2b9dcc43afef8ac157cd92fce96985d6b8b0cfbd3df4d666f66b4d55a75d202f", size = 3269375, upload-time = "2026-05-24T20:17:20.34Z" }, + { url = "https://files.pythonhosted.org/packages/48/84/278a811ef4e07be9c89dc5cdd7be833268509a66a68c4897cf585e67428f/sqlalchemy-2.0.50-cp313-cp313-win32.whl", hash = "sha256:60922d6599065ddca2c6f376b9aa2f41a6b85a271725e0909490bbc50b1998a5", size = 2117229, upload-time = "2026-05-24T19:50:08.215Z" }, + { url = "https://files.pythonhosted.org/packages/f6/1c/067cc6187ed32d2ec222fe6d2643acc1659a6d0659f8a7cbc5ad3ae83280/sqlalchemy-2.0.50-cp313-cp313-win_amd64.whl", hash = "sha256:287086e67275a212c4582d166a6fb03a65ccc5551d80866270ce0dd9f34eccd3", size = 2143126, upload-time = "2026-05-24T19:50:09.691Z" }, + { url = "https://files.pythonhosted.org/packages/df/32/10ac51b4be7cdecd7e93d069251c86dfbf70b7adbd7c67b48ccea6c49e1c/sqlalchemy-2.0.50-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c966932507a4d7d0a37314927dbfcd89720e3f37d2a1e3352e7ae7939fa8e8a0", size = 2158519, upload-time = "2026-05-24T19:27:56.472Z" }, + { url = "https://files.pythonhosted.org/packages/5a/76/e703d2f7681d7d66c4c891af3f07c7ccf4c76ad7f18351de035b5eda007a/sqlalchemy-2.0.50-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:faffef4bcc20a1892e65e155293d99d60855bbbc79250ab712819cfd56a8e6bb", size = 3282063, upload-time = "2026-05-24T20:09:38.57Z" }, + { url = "https://files.pythonhosted.org/packages/31/26/ef168b184a25701f9995e8fb7e503fafd7a99c1c77cda1bc1a26ea2ed486/sqlalchemy-2.0.50-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c206aec519a2e7bd08abbfb33436e325fd22c632d9c21a9047e376ce241646e", size = 3287069, upload-time = "2026-05-24T20:17:21.942Z" }, + { url = "https://files.pythonhosted.org/packages/c2/15/765acc2bc693bccc43ca4a95d5b69750da8aaf6db1b5c616536e087f8920/sqlalchemy-2.0.50-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:bef4ac756363227ef6402a75fee025a4bc690f92328e825868939b3b3a446a6d", size = 3230453, upload-time = "2026-05-24T20:09:40.398Z" }, + { url = "https://files.pythonhosted.org/packages/63/61/08e03c3adbf5db0087a0b6816746fec8f3032fb2f7fc899a9bb9b2a48ce4/sqlalchemy-2.0.50-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:96fbee6b19c19cd1556c8bf9419447cf2ec149ffcab7ab64348c23e54ef8547f", size = 3252413, upload-time = "2026-05-24T20:17:24.067Z" }, + { url = "https://files.pythonhosted.org/packages/03/0c/370a1f2db38436c615e10134c8a37de3688e74084792380695f3f5083860/sqlalchemy-2.0.50-cp314-cp314-win32.whl", hash = "sha256:8f00e3eb43ba30eb1b238ee03a8a62309486d1321eda3328bb611e0340033ad8", size = 2120063, upload-time = "2026-05-24T19:50:11.08Z" }, + { url = "https://files.pythonhosted.org/packages/7f/a0/fe92bb9817863bc13ba093bda931979a26cc2ca69f8e8f26d07add3d7c6f/sqlalchemy-2.0.50-cp314-cp314-win_amd64.whl", hash = "sha256:15708c613cd5005b7dffe1f66ee6a63ee8f5e46799f71c70ebad74178c676a39", size = 2145830, upload-time = "2026-05-24T19:50:12.452Z" }, + { url = "https://files.pythonhosted.org/packages/cc/ff/e5640a98a0b2f491eb8fde10fb6c773621a2e44340de231fafcc9370f4a9/sqlalchemy-2.0.50-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3699dac4be410e97049a1658e9480da9cde956594aa0f3aebc60b88f21c5ba70", size = 2178435, upload-time = "2026-05-24T19:42:58.889Z" }, + { url = "https://files.pythonhosted.org/packages/b7/85/337116e186f1236375b5fb70c21cfac98e8e8ab0d3a47be838dc47a59e08/sqlalchemy-2.0.50-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f96233858e3df43932ac11589e22520da6e8aeb624b03fedfeebb0e8ea213086", size = 3566059, upload-time = "2026-05-24T20:01:20.848Z" }, + { url = "https://files.pythonhosted.org/packages/96/34/bb0e190e161c3c2c24314a65add57218be14a4a9486886b7f5047c1ff7c8/sqlalchemy-2.0.50-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c4e70c46fad30c3bcc6a4708bc0130a3173e11a5b25f0ea4a9d8911b450f1f52", size = 3535366, upload-time = "2026-05-24T20:03:56.768Z" }, + { url = "https://files.pythonhosted.org/packages/df/5a/a7f759f97e4fd499c5d4e4488c760d5a7fbecf3028b465a04274fcd52384/sqlalchemy-2.0.50-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1918a3cf564d16d95bca7301005f41ab2ad50b07cd3b9da50d3ed986db148d6a", size = 3474879, upload-time = "2026-05-24T20:01:23.058Z" }, + { url = "https://files.pythonhosted.org/packages/9d/d9/2907ea38eb60687d297bf9c39e5ee58053c87b57fe8a9cae97090cecbf10/sqlalchemy-2.0.50-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b00098cdbdbd38c7be3d568b0c9c3122b8c0ec62b911b57cd5e6e0254d60a76d", size = 3486117, upload-time = "2026-05-24T20:03:59.052Z" }, + { url = "https://files.pythonhosted.org/packages/f2/e3/5aa06f167559f8c0bdae487e297d23ba548150ab016a3418265d617a4985/sqlalchemy-2.0.50-cp314-cp314t-win32.whl", hash = "sha256:1fbd55a969d7ac44a98e3dec75016074f809fa08f871585ace58dde110d1bf3e", size = 2150823, upload-time = "2026-05-24T20:08:58.644Z" }, + { url = "https://files.pythonhosted.org/packages/65/9b/112fb8f977582d7489d036e409e3723948bcf5320b3ac465f3c481bbe8f9/sqlalchemy-2.0.50-cp314-cp314t-win_amd64.whl", hash = "sha256:c5c3cdb753a9004183e1ccb634b41611654c989e61bc68617ce878e46d6f1e51", size = 2185794, upload-time = "2026-05-24T20:09:00.319Z" }, + { url = "https://files.pythonhosted.org/packages/d0/10/f7220e9b784d295d241c86ed99aeb537f92afcd469a64861f2717e9bb077/sqlalchemy-2.0.50-py3-none-any.whl", hash = "sha256:92064363517a3ff8212b5a93b8c62876579d8dfd1ca5b561335f30152d884fa9", size = 1943861, upload-time = "2026-05-24T19:59:01.119Z" }, ] [[package]] @@ -1491,7 +1898,7 @@ source = { editable = "." } dependencies = [ { name = "click" }, { name = "dotenv" }, - { name = "fastapi" }, + { name = "fastapi", extra = ["standard"] }, { name = "flask" }, { name = "matplotlib" }, { name = "numpy" }, @@ -1500,9 +1907,7 @@ dependencies = [ { name = "pydantic" }, { name = "pydantic-settings" }, { name = "pyodbc" }, - { name = "pytest" }, { name = "sqlalchemy" }, - { name = "uvicorn" }, ] [package.optional-dependencies] @@ -1520,6 +1925,7 @@ docs = [ dev = [ { name = "pre-commit" }, { name = "pyright" }, + { name = "pytest" }, { name = "ruff" }, ] @@ -1527,7 +1933,7 @@ dev = [ requires-dist = [ { name = "click", specifier = ">=8.2.0" }, { name = "dotenv", specifier = ">=0.9.9" }, - { name = "fastapi", specifier = ">=0.115.0" }, + { name = "fastapi", extras = ["standard"], specifier = ">=0.137.0" }, { name = "flask", specifier = ">=3.1.0" }, { name = "matplotlib", specifier = ">=3.10.1" }, { name = "mkdocs", marker = "extra == 'docs'", specifier = ">=1.4.2" }, @@ -1538,22 +1944,21 @@ requires-dist = [ { name = "mkdocstrings", marker = "extra == 'docs'", specifier = ">=0.15.2" }, { name = "mkdocstrings-python", marker = "extra == 'docs'", specifier = ">=0.15.2" }, { name = "numpy", specifier = ">=2.2.5" }, - { name = "ortools", specifier = ">=9.12.4544" }, + { name = "ortools", specifier = ">=9.15.6755" }, { name = "pandas", specifier = ">=2.2.3" }, - { name = "pydantic", specifier = ">=2.12.5" }, + { name = "pydantic", specifier = ">=2.13.4" }, { name = "pydantic-settings", specifier = ">=2.14.1" }, { name = "pyodbc", specifier = ">=5.2.0" }, - { name = "pytest", specifier = ">=9.0.1" }, - { name = "sqlalchemy", specifier = ">=2.0.41" }, - { name = "uvicorn", specifier = ">=0.34.0" }, + { name = "sqlalchemy", specifier = ">=2.0.50" }, ] provides-extras = ["docs"] [package.metadata.requires-dev] dev = [ - { name = "pre-commit", specifier = ">=4.2.0" }, - { name = "pyright", specifier = ">=1.1.407" }, - { name = "ruff", specifier = ">=0.14.4" }, + { name = "pre-commit", specifier = ">=4.6.0" }, + { name = "pyright", specifier = ">=1.1.410" }, + { name = "pytest", specifier = ">=9.1.0" }, + { name = "ruff", specifier = ">=0.15.17" }, ] [[package]] @@ -1569,6 +1974,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/81/0d/13d1d239a25cbfb19e740db83143e95c772a1fe10202dda4b76792b114dd/starlette-0.52.1-py3-none-any.whl", hash = "sha256:0029d43eb3d273bc4f83a08720b4912ea4b071087a3b48db01b7c839f7954d74", size = 74272, upload-time = "2026-01-18T13:34:09.188Z" }, ] +[[package]] +name = "typer" +version = "0.26.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-doc" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "rich" }, + { name = "shellingham" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5e/ed/ef06584ccdd5c410df0837951ecd7e15d9a6144ea1bd4c73cecab1a89891/typer-0.26.7.tar.gz", hash = "sha256:e314a34c617e419c091b2830dda3ea1f257134ff593061a8f5b9717ab8dddb3a", size = 201709, upload-time = "2026-06-03T07:18:06.843Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/24/25/2201973529af2c954de0bb725323c3aaed6d7f0ceee8f550dec9185df013/typer-0.26.7-py3-none-any.whl", hash = "sha256:5c87cfbc5d34491c5346ebf49c23e18d56ccb863268d3a8d592b26087c2f5e58", size = 122456, upload-time = "2026-06-03T07:18:05.732Z" }, +] + [[package]] name = "typing-extensions" version = "4.15.0" @@ -1621,6 +2041,49 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/83/e4/d04a086285c20886c0daad0e026f250869201013d18f81d9ff5eada73a88/uvicorn-0.41.0-py3-none-any.whl", hash = "sha256:29e35b1d2c36a04b9e180d4007ede3bcb32a85fbdfd6c6aeb3f26839de088187", size = 68783, upload-time = "2026-02-16T23:07:22.357Z" }, ] +[package.optional-dependencies] +standard = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "httptools" }, + { name = "python-dotenv" }, + { name = "pyyaml" }, + { name = "uvloop", marker = "platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32'" }, + { name = "watchfiles" }, + { name = "websockets" }, +] + +[[package]] +name = "uvloop" +version = "0.22.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/06/f0/18d39dbd1971d6d62c4629cc7fa67f74821b0dc1f5a77af43719de7936a7/uvloop-0.22.1.tar.gz", hash = "sha256:6c84bae345b9147082b17371e3dd5d42775bddce91f885499017f4607fdaf39f", size = 2443250, upload-time = "2025-10-16T22:17:19.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3d/ff/7f72e8170be527b4977b033239a83a68d5c881cc4775fca255c677f7ac5d/uvloop-0.22.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fe94b4564e865d968414598eea1a6de60adba0c040ba4ed05ac1300de402cd42", size = 1359936, upload-time = "2025-10-16T22:16:29.436Z" }, + { url = "https://files.pythonhosted.org/packages/c3/c6/e5d433f88fd54d81ef4be58b2b7b0cea13c442454a1db703a1eea0db1a59/uvloop-0.22.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:51eb9bd88391483410daad430813d982010f9c9c89512321f5b60e2cddbdddd6", size = 752769, upload-time = "2025-10-16T22:16:30.493Z" }, + { url = "https://files.pythonhosted.org/packages/24/68/a6ac446820273e71aa762fa21cdcc09861edd3536ff47c5cd3b7afb10eeb/uvloop-0.22.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:700e674a166ca5778255e0e1dc4e9d79ab2acc57b9171b79e65feba7184b3370", size = 4317413, upload-time = "2025-10-16T22:16:31.644Z" }, + { url = "https://files.pythonhosted.org/packages/5f/6f/e62b4dfc7ad6518e7eff2516f680d02a0f6eb62c0c212e152ca708a0085e/uvloop-0.22.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b5b1ac819a3f946d3b2ee07f09149578ae76066d70b44df3fa990add49a82e4", size = 4426307, upload-time = "2025-10-16T22:16:32.917Z" }, + { url = "https://files.pythonhosted.org/packages/90/60/97362554ac21e20e81bcef1150cb2a7e4ffdaf8ea1e5b2e8bf7a053caa18/uvloop-0.22.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e047cc068570bac9866237739607d1313b9253c3051ad84738cbb095be0537b2", size = 4131970, upload-time = "2025-10-16T22:16:34.015Z" }, + { url = "https://files.pythonhosted.org/packages/99/39/6b3f7d234ba3964c428a6e40006340f53ba37993f46ed6e111c6e9141d18/uvloop-0.22.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:512fec6815e2dd45161054592441ef76c830eddaad55c8aa30952e6fe1ed07c0", size = 4296343, upload-time = "2025-10-16T22:16:35.149Z" }, + { url = "https://files.pythonhosted.org/packages/89/8c/182a2a593195bfd39842ea68ebc084e20c850806117213f5a299dfc513d9/uvloop-0.22.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:561577354eb94200d75aca23fbde86ee11be36b00e52a4eaf8f50fb0c86b7705", size = 1358611, upload-time = "2025-10-16T22:16:36.833Z" }, + { url = "https://files.pythonhosted.org/packages/d2/14/e301ee96a6dc95224b6f1162cd3312f6d1217be3907b79173b06785f2fe7/uvloop-0.22.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cdf5192ab3e674ca26da2eada35b288d2fa49fdd0f357a19f0e7c4e7d5077c8", size = 751811, upload-time = "2025-10-16T22:16:38.275Z" }, + { url = "https://files.pythonhosted.org/packages/b7/02/654426ce265ac19e2980bfd9ea6590ca96a56f10c76e63801a2df01c0486/uvloop-0.22.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e2ea3d6190a2968f4a14a23019d3b16870dd2190cd69c8180f7c632d21de68d", size = 4288562, upload-time = "2025-10-16T22:16:39.375Z" }, + { url = "https://files.pythonhosted.org/packages/15/c0/0be24758891ef825f2065cd5db8741aaddabe3e248ee6acc5e8a80f04005/uvloop-0.22.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0530a5fbad9c9e4ee3f2b33b148c6a64d47bbad8000ea63704fa8260f4cf728e", size = 4366890, upload-time = "2025-10-16T22:16:40.547Z" }, + { url = "https://files.pythonhosted.org/packages/d2/53/8369e5219a5855869bcee5f4d317f6da0e2c669aecf0ef7d371e3d084449/uvloop-0.22.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bc5ef13bbc10b5335792360623cc378d52d7e62c2de64660616478c32cd0598e", size = 4119472, upload-time = "2025-10-16T22:16:41.694Z" }, + { url = "https://files.pythonhosted.org/packages/f8/ba/d69adbe699b768f6b29a5eec7b47dd610bd17a69de51b251126a801369ea/uvloop-0.22.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1f38ec5e3f18c8a10ded09742f7fb8de0108796eb673f30ce7762ce1b8550cad", size = 4239051, upload-time = "2025-10-16T22:16:43.224Z" }, + { url = "https://files.pythonhosted.org/packages/90/cd/b62bdeaa429758aee8de8b00ac0dd26593a9de93d302bff3d21439e9791d/uvloop-0.22.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3879b88423ec7e97cd4eba2a443aa26ed4e59b45e6b76aabf13fe2f27023a142", size = 1362067, upload-time = "2025-10-16T22:16:44.503Z" }, + { url = "https://files.pythonhosted.org/packages/0d/f8/a132124dfda0777e489ca86732e85e69afcd1ff7686647000050ba670689/uvloop-0.22.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4baa86acedf1d62115c1dc6ad1e17134476688f08c6efd8a2ab076e815665c74", size = 752423, upload-time = "2025-10-16T22:16:45.968Z" }, + { url = "https://files.pythonhosted.org/packages/a3/94/94af78c156f88da4b3a733773ad5ba0b164393e357cc4bd0ab2e2677a7d6/uvloop-0.22.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:297c27d8003520596236bdb2335e6b3f649480bd09e00d1e3a99144b691d2a35", size = 4272437, upload-time = "2025-10-16T22:16:47.451Z" }, + { url = "https://files.pythonhosted.org/packages/b5/35/60249e9fd07b32c665192cec7af29e06c7cd96fa1d08b84f012a56a0b38e/uvloop-0.22.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c1955d5a1dd43198244d47664a5858082a3239766a839b2102a269aaff7a4e25", size = 4292101, upload-time = "2025-10-16T22:16:49.318Z" }, + { url = "https://files.pythonhosted.org/packages/02/62/67d382dfcb25d0a98ce73c11ed1a6fba5037a1a1d533dcbb7cab033a2636/uvloop-0.22.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b31dc2fccbd42adc73bc4e7cdbae4fc5086cf378979e53ca5d0301838c5682c6", size = 4114158, upload-time = "2025-10-16T22:16:50.517Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/f1171b4a882a5d13c8b7576f348acfe6074d72eaf52cccef752f748d4a9f/uvloop-0.22.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:93f617675b2d03af4e72a5333ef89450dfaa5321303ede6e67ba9c9d26878079", size = 4177360, upload-time = "2025-10-16T22:16:52.646Z" }, + { url = "https://files.pythonhosted.org/packages/79/7b/b01414f31546caf0919da80ad57cbfe24c56b151d12af68cee1b04922ca8/uvloop-0.22.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:37554f70528f60cad66945b885eb01f1bb514f132d92b6eeed1c90fd54ed6289", size = 1454790, upload-time = "2025-10-16T22:16:54.355Z" }, + { url = "https://files.pythonhosted.org/packages/d4/31/0bb232318dd838cad3fa8fb0c68c8b40e1145b32025581975e18b11fab40/uvloop-0.22.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b76324e2dc033a0b2f435f33eb88ff9913c156ef78e153fb210e03c13da746b3", size = 796783, upload-time = "2025-10-16T22:16:55.906Z" }, + { url = "https://files.pythonhosted.org/packages/42/38/c9b09f3271a7a723a5de69f8e237ab8e7803183131bc57c890db0b6bb872/uvloop-0.22.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:badb4d8e58ee08dad957002027830d5c3b06aea446a6a3744483c2b3b745345c", size = 4647548, upload-time = "2025-10-16T22:16:57.008Z" }, + { url = "https://files.pythonhosted.org/packages/c1/37/945b4ca0ac27e3dc4952642d4c900edd030b3da6c9634875af6e13ae80e5/uvloop-0.22.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b91328c72635f6f9e0282e4a57da7470c7350ab1c9f48546c0f2866205349d21", size = 4467065, upload-time = "2025-10-16T22:16:58.206Z" }, + { url = "https://files.pythonhosted.org/packages/97/cc/48d232f33d60e2e2e0b42f4e73455b146b76ebe216487e862700457fbf3c/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:daf620c2995d193449393d6c62131b3fbd40a63bf7b307a1527856ace637fe88", size = 4328384, upload-time = "2025-10-16T22:16:59.36Z" }, + { url = "https://files.pythonhosted.org/packages/e4/16/c1fd27e9549f3c4baf1dc9c20c456cd2f822dbf8de9f463824b0c0357e06/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6cde23eeda1a25c75b2e07d39970f3374105d5eafbaab2a4482be82f272d5a5e", size = 4296730, upload-time = "2025-10-16T22:17:00.744Z" }, +] + [[package]] name = "virtualenv" version = "20.35.4" @@ -1659,6 +2122,92 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" }, ] +[[package]] +name = "watchfiles" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cd/41/5e1a4bb12aac5f1493fa1bdc11154eca3b258ca4eba65d39c473fe19d8e9/watchfiles-1.2.0.tar.gz", hash = "sha256:c995fba777f1ea992f090f9236e9284cf7a5d1a0130dd5a3d82c598cacd76838", size = 108252, upload-time = "2026-05-18T04:32:04.251Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b8/2f/e42c992d2afda3108ea1c02acecc991b9f31d05c14adc2a7cee9ee211fc4/watchfiles-1.2.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:bc13eb17538be00c874699dc0abe4ee2bc8d50bb1166a6b9e175ef3fd7eb8f26", size = 400115, upload-time = "2026-05-18T04:32:02.06Z" }, + { url = "https://files.pythonhosted.org/packages/5f/8f/6af2ea19065c91d8b0ea3516fdfc8c0d349f407e8e9fbf4e5a17360de8ad/watchfiles-1.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2d95ddc1eb6914154253d239089900813f6a767e174b8e6a50e7fdacb7e4236c", size = 393659, upload-time = "2026-05-18T04:30:50.951Z" }, + { url = "https://files.pythonhosted.org/packages/13/01/b32a967c56fb3e3e5be3db52c3d3b87fa4513aa367d8ed1ad96d42952e5f/watchfiles-1.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f70d8b291ef6e88d19b1f297a6905ddb978888d9272b0d05e6f53309856bcfc", size = 453207, upload-time = "2026-05-18T04:31:04.231Z" }, + { url = "https://files.pythonhosted.org/packages/04/98/97557a812180338cb1abd32e1cffcc4588f59b5f23e0cb006b2ba95ba64a/watchfiles-1.2.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:56d8641cf834c2836922899105bd3ce3d0dfc69291d52edf0b4d0436829b34c0", size = 459273, upload-time = "2026-05-18T04:31:50.377Z" }, + { url = "https://files.pythonhosted.org/packages/e8/a8/b4b08dcb7653b8087c6586f7ce649505900e866bbcfe40dc9587af02e686/watchfiles-1.2.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2581a94056e55d7d0a31a823ea92bf73749c489ca2285bfdc0fbe6b2bb49d50c", size = 489927, upload-time = "2026-05-18T04:31:42.485Z" }, + { url = "https://files.pythonhosted.org/packages/50/94/3dceea03545d2e5ddfd839f0ddd5e1cecbf1697b5a428d5ba11cef6af95d/watchfiles-1.2.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:41bc1199f7523b3f82843c88cbb979180c949caef0342cf90968f178e5d49b01", size = 570476, upload-time = "2026-05-18T04:31:03.071Z" }, + { url = "https://files.pythonhosted.org/packages/cc/f2/d39a5450c3532092b91f81d274360e613c2371bc874a89c7a1a3c5e8d138/watchfiles-1.2.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7571e4464cb6e434958f867f7f730b8ab0b75e3f8e5eac0499168486ab3c33a8", size = 465650, upload-time = "2026-05-18T04:30:12.701Z" }, + { url = "https://files.pythonhosted.org/packages/22/24/ed72f68cbc1333ca9b9f2200aa048bb6658ae41709bc1caad4310f4bdffd/watchfiles-1.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e53a384f76b631c3ae5334ce6a52f0baa3a911eb94a4eac7f160079868b716d5", size = 456398, upload-time = "2026-05-18T04:30:13.784Z" }, + { url = "https://files.pythonhosted.org/packages/0d/64/982ef4a4e5bab5b6e5b6becc8cd5e732f6130a78b855f0abec6439a9a135/watchfiles-1.2.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:d20029a60a71a052a24c4db7673bc4de39ab89adbaccbfb5d67987c5d73f424d", size = 465140, upload-time = "2026-05-18T04:31:52.111Z" }, + { url = "https://files.pythonhosted.org/packages/a0/0c/95282abf4ed680b6096010bcfc30c5fa7a041fc5aa5a2ad17a2cc6c75bba/watchfiles-1.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:2cb93af48550faf1cea04c303107c8b75833de7013e57ce27d3b8d21d8d0f58c", size = 630259, upload-time = "2026-05-18T04:31:25.676Z" }, + { url = "https://files.pythonhosted.org/packages/30/45/607c1de1530c4bdcf2cf1d1ecc2505ddba5d96bd43ba9f2b0e79876f850f/watchfiles-1.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2995c176de7692b86a2e4c58d9ec718f753150a979cb4a754e2b4ffa38e70906", size = 659859, upload-time = "2026-05-18T04:30:24.333Z" }, + { url = "https://files.pythonhosted.org/packages/fa/08/d9e2e0f9e8e6791d33aefc694ad7eefa7f901f63caff84a81ded38692f9c/watchfiles-1.2.0-cp312-cp312-win32.whl", hash = "sha256:7a2cffd17d27d2ecbb310c2b1d8174f222a5495b1a721894afa88ec11e25b898", size = 275480, upload-time = "2026-05-18T04:30:31.307Z" }, + { url = "https://files.pythonhosted.org/packages/1c/e6/9d42569c0102645cc8cea5d8c7d8a1e9d4ada2cb7f05f75e554b8aa2202a/watchfiles-1.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:f155b3a1b2a5fc89cdc70d47ee5d54e3b75e88efa34982028a35daef9ba00379", size = 288718, upload-time = "2026-05-18T04:32:10.745Z" }, + { url = "https://files.pythonhosted.org/packages/0a/26/88e0dc6ee3898169d7fa22bb6a69cabf2502d2ee25cb8c876d1262d204f8/watchfiles-1.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:8fa585ede612ee9f9e91b18bebf9ba11b9ae29a4e3a0d0cf6fca3e382133f0d5", size = 281026, upload-time = "2026-05-18T04:30:22.23Z" }, + { url = "https://files.pythonhosted.org/packages/d1/4d/70a7feced9f87e2ff26dba42667290f41694fc64646c67261fbb8cab5d5c/watchfiles-1.2.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:01ea8d66f0693b9b60a6541c8d10263091ca9a9060d242f3c1f3143f9aad2c98", size = 399730, upload-time = "2026-05-18T04:31:38.162Z" }, + { url = "https://files.pythonhosted.org/packages/31/3a/0da302f2307aee316922806ebd5726c542cbd787c938271cf14a074c7daf/watchfiles-1.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7ba0480b9a74af058f43b337e937a451e109295c420916d68ad24e3dc02f5e44", size = 392842, upload-time = "2026-05-18T04:30:27.051Z" }, + { url = "https://files.pythonhosted.org/packages/db/ef/d5bdb705c224dbc256aa0c1ec47bf4e61ec52558f2afb44a71a1fe4d7015/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f34e26a19f91f710c08e0183429f0d1d15df734e6bc78c31e77b9ea9c433658", size = 452989, upload-time = "2026-05-18T04:31:11.945Z" }, + { url = "https://files.pythonhosted.org/packages/71/29/5495f2c1661949ef7a35e4d71111d129cfe7606414a26887a919d0a55406/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b4e77f6a55f858504069abd35d336a637555c09bca453dde1ee1e5ada8a6a1fb", size = 458978, upload-time = "2026-05-18T04:30:52.606Z" }, + { url = "https://files.pythonhosted.org/packages/d5/8c/7f9c07c433811c2fffd93e13fdfb7135de9aab5f2ae41be08960fa0047dc/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0cb4d80e212f116474a545c21c912b445f16bb0cef9e6a73a498164223e14e2f", size = 490248, upload-time = "2026-05-18T04:31:36.003Z" }, + { url = "https://files.pythonhosted.org/packages/3c/11/d93632febc52fbc21be90231bb7c17fd5387f46c9076fd40a5f9c2ae6910/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b974946a10af379d425e2eef5b62f5c6ebeaccf91d45eaad6f5b27ecd4f91aa0", size = 571847, upload-time = "2026-05-18T04:31:10.862Z" }, + { url = "https://files.pythonhosted.org/packages/55/b4/383173e73aabb07ad1d9c7aa859d95437ac46a6d6a1e11005facda0c9d19/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86bc13c25a8d1fcd70b51d0ce7c9b65e90de5666fcbfd3e34957cc73ee19aeb5", size = 465974, upload-time = "2026-05-18T04:30:17.006Z" }, + { url = "https://files.pythonhosted.org/packages/a7/6c/89b1a230a78f57c52dd8893adb1f92f94411721b6ec12596c56d98c74356/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca148d73dea36c9763aaa351e4d7a51780ec1584217c45276f4fe8239c768b71", size = 454782, upload-time = "2026-05-18T04:30:35.656Z" }, + { url = "https://files.pythonhosted.org/packages/24/62/1732118367cfff0a9fce3bf62ff4bfded09ef5df21d9d446b858b3f70a96/watchfiles-1.2.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:c525543d91961c6955b2636b308569e84a1d1c5f5f2932041ab9ef46422f43e3", size = 465182, upload-time = "2026-05-18T04:30:20.846Z" }, + { url = "https://files.pythonhosted.org/packages/28/96/716f7e5f51339bf22963f3345f9f27d7f3b30e2eadc597e257c881dd3c53/watchfiles-1.2.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:a204794696ffb8f9b10fba6f7cb5216d42f3b2b71860ccac6b6e42f5f10973b0", size = 629841, upload-time = "2026-05-18T04:31:05.397Z" }, + { url = "https://files.pythonhosted.org/packages/4c/fe/c40783950fd771ccf66ab3ec2722d188a9af1c7f96c6e811f36e40c6e03f/watchfiles-1.2.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:10d86db20695afe7997ac9e1717637d6714a8d0220458c33f3d2061f54cec427", size = 658028, upload-time = "2026-05-18T04:31:48.22Z" }, + { url = "https://files.pythonhosted.org/packages/71/72/4508db1856d1d87fcbb3b63f4839bab1b5682cb0e8d224d122263c09654a/watchfiles-1.2.0-cp313-cp313-win32.whl", hash = "sha256:eb283ee99e21ad6443c8cdb06ac5b34b1308c329cbdf03fa02b445363714c799", size = 275183, upload-time = "2026-05-18T04:30:59.57Z" }, + { url = "https://files.pythonhosted.org/packages/f9/36/14b76ca57652e5cc5fd1c11f32a261292c08a0d19a00351013c2549cbfb2/watchfiles-1.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:a0f27f01bee51861392bb6b7c4fdb290b27d1eb194e9e28788d68102a0e898d9", size = 288059, upload-time = "2026-05-18T04:32:07.937Z" }, + { url = "https://files.pythonhosted.org/packages/1b/8d/0a85e395398d8d20fadfe5c5d32c726eee17a519e78fb356f2cf7531bffe/watchfiles-1.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:3651aa7058595e9cfb75d35dd5ada2bf9f48a5b8a0f3562821d3e210c507e077", size = 280186, upload-time = "2026-05-18T04:31:54.484Z" }, + { url = "https://files.pythonhosted.org/packages/37/68/36db056f1fdcc5f07302f56e631774d6835bcd6fa3ace402304621d5f9e5/watchfiles-1.2.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:faea288b6f0ab1902ef08f4ca6de005dccf856c4e0c4f21b8c5fce02d90a1b08", size = 399031, upload-time = "2026-05-18T04:30:44.576Z" }, + { url = "https://files.pythonhosted.org/packages/c1/64/01a9d6f66a82a5c101ce939274106cc72759d62427e153f01edd2b9f87c2/watchfiles-1.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:01859b11fd9fbca670f4d5da00fbac282cfea9bd67a2125d8b2833a3b5617ea9", size = 391205, upload-time = "2026-05-18T04:30:25.413Z" }, + { url = "https://files.pythonhosted.org/packages/84/2c/0a44fe058cb4bb7b8ede6b6670698bbb7c0400740e378d00022189b7b31d/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fff610d7bb2256a317bb1e96f0d7862c7aa8076733ee5df0fd41bbe76a24a4f4", size = 451892, upload-time = "2026-05-18T04:32:14.005Z" }, + { url = "https://files.pythonhosted.org/packages/67/a1/351e0d56cd35e6488b5c8b4fb11a809a5bc923e8fe8fed9faf8920be0c89/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b141a4891c995a039cd89e9a49e62df1dc8a559a5d1a6e4c7106d16c12777a55", size = 458867, upload-time = "2026-05-18T04:31:22.279Z" }, + { url = "https://files.pythonhosted.org/packages/d5/7d/9d09605187f1b838998624049fcf8bf47b73c1a3b76901fcac1782f62277/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f22943b7770483f6ea0721c6b11d022947a98eb0acae14694de034f4d0d38925", size = 490217, upload-time = "2026-05-18T04:31:43.657Z" }, + { url = "https://files.pythonhosted.org/packages/60/5d/a17a16eccb182f04188cd308ec24b1a71a9b5c4e7098269cf35d9fa56d02/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1bc6195825b7dcd217968bb1f801a60fd4c16e8eeab5bedc7fe917d7d5995ab4", size = 571458, upload-time = "2026-05-18T04:32:11.875Z" }, + { url = "https://files.pythonhosted.org/packages/d3/3d/4dd457062083ab1938e5dfd45032eb425cee2ac817287ca8ff4356183e5d/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4a4b147f5dca2a5d325a06a832fb43f345751adfbc63204aec30e0d9ca965a2", size = 464707, upload-time = "2026-05-18T04:30:43.492Z" }, + { url = "https://files.pythonhosted.org/packages/c6/71/ea8c57b128f5383de74d0c7d2d9c57ad7c9a65a930c451bd25d524b295b7/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4543579a9bdb0c9560039b4ffddbdb39545707659fbc430ce4c10f3f68d557f9", size = 454663, upload-time = "2026-05-18T04:30:16.061Z" }, + { url = "https://files.pythonhosted.org/packages/53/fd/2e812bf938406d7db351f0703ddd3fc6c061cf30d96153a77bc79a943a44/watchfiles-1.2.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:20aa0e708b920bde876a4aa82dc7dd6ebea228a63a67cda6632c2fc87b787efa", size = 463537, upload-time = "2026-05-18T04:31:44.9Z" }, + { url = "https://files.pythonhosted.org/packages/86/56/d17a7f1dd1bc3035f1072694a551301272f1739c2d8e319c927cb9e29b38/watchfiles-1.2.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:d413349d565dab74297f2a63e84a097936be69bf8f3b3801f27f380e32040f44", size = 629194, upload-time = "2026-05-18T04:31:14.141Z" }, + { url = "https://files.pythonhosted.org/packages/be/06/f1ff66bf5cae50aa4062779a0ecd0bbaf15e466195719074078947d9a17d/watchfiles-1.2.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:f28b2725eb8cce327b9b3ab02415c853011dc55c95832fe90de6bc56f5315f72", size = 656194, upload-time = "2026-05-18T04:31:47.14Z" }, + { url = "https://files.pythonhosted.org/packages/e7/54/a9c7ea9a82a4ac65e7004c0a03920b5cdd2f9c3b678757d9cd425aa51d53/watchfiles-1.2.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:b8c8358484d5fa12ef34f05b7f4168eaf1932f408725ff6d023c33ec17bd79d4", size = 400205, upload-time = "2026-05-18T04:32:05.153Z" }, + { url = "https://files.pythonhosted.org/packages/aa/5d/c9ab3534374a4a67450696905d6ef16a04405448b8dc52bd752ae50423d4/watchfiles-1.2.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f04b092229ad2c50126dd3c922c8822e51e605993764a33058d4a791ab42281", size = 392508, upload-time = "2026-05-18T04:30:54.849Z" }, + { url = "https://files.pythonhosted.org/packages/26/ca/1ad30103535cf0cecd7b993e8d50edc5351b1820e38f2d22e3df58962feb/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a7ce236284f002a156f70add88efe5c70879cccbb658be0822c54b1306fc09d", size = 452448, upload-time = "2026-05-18T04:30:53.727Z" }, + { url = "https://files.pythonhosted.org/packages/37/a1/ceee2cdf2afbd715fa07758d39c9859513eae411b23196f7fd039e5feedd/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b9909cc2b48468b575eefa944919e1fe8a36c5849d5c7c168f80a8c1db69398e", size = 459605, upload-time = "2026-05-18T04:30:23.312Z" }, + { url = "https://files.pythonhosted.org/packages/e8/f6/421e30fd1cb3907a84ed92ab3f1983e37ba2dca015e9a894a048418417a2/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a37faaed405c67e28e6be45a1fa4f206ef5a2860f27c237db9fa30704c38242", size = 490757, upload-time = "2026-05-18T04:30:47.358Z" }, + { url = "https://files.pythonhosted.org/packages/41/b0/55ed1b97ed08be7bba6f9a541cac15f2a858e1d74d2b07b6da70a82aab00/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9649193aa27bd9ff2e80ff29bfaa93085496c7a3a377592823cc58b77ee88add", size = 568672, upload-time = "2026-05-18T04:30:38.915Z" }, + { url = "https://files.pythonhosted.org/packages/d1/cf/d8ae8a80dd7bafab395ea7681c10237311bbf34d37704a8c744e7cf31fc7/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e4ff8e37f99cf1da89e255e07c9c4b37c214038c4283707bdec308cb1b0ea1f", size = 464197, upload-time = "2026-05-18T04:30:09.914Z" }, + { url = "https://files.pythonhosted.org/packages/7c/8a/3076c496ca8dafe0e8cd03fcebdfc47be4b1174b4e5b24ff6e396e6b3af2/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:054dc20fd2e3132b4c3883b4a00d72fd6e1f56fdaf89fccd12e8057d74cd74d7", size = 453181, upload-time = "2026-05-18T04:30:14.829Z" }, + { url = "https://files.pythonhosted.org/packages/e5/10/9745e17c98e7b8a86454df0a3c7b5686bd650383f1e9f26e4ebcbd6cc0c0/watchfiles-1.2.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:e140ed30ebde76796b686e67c182cff10ea2fbab186fafd1560f74bb5a473a6e", size = 465109, upload-time = "2026-05-18T04:30:28.123Z" }, + { url = "https://files.pythonhosted.org/packages/8f/95/8ef4a95481d3e0cb52d62a06fa6e972e81424be2d9698b91a2fecca9904c/watchfiles-1.2.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:bb7e52ecf68ba46d22df23467b87cffeb2146908aa523ebfe803019618cfda06", size = 630653, upload-time = "2026-05-18T04:31:49.304Z" }, + { url = "https://files.pythonhosted.org/packages/fd/e4/3b3bf36b0f829b50c6ebcb8d031583863c59f923d6a6af3d485e470d0fac/watchfiles-1.2.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:23282a321c8baf9b3a3c4afff673f9fe65eb7fdc2338d765ccad9d3d1916a5ba", size = 657838, upload-time = "2026-05-18T04:31:06.497Z" }, + { url = "https://files.pythonhosted.org/packages/21/b1/6cbbb50c1f3002ab568777d44aa21206dfb8807a840990c4037523b51812/watchfiles-1.2.0-cp314-cp314-win32.whl", hash = "sha256:c0db965c5f79aa49fe672d297cf1febc5ad149b658594944f49a54a2b96270a7", size = 275108, upload-time = "2026-05-18T04:30:06.891Z" }, + { url = "https://files.pythonhosted.org/packages/92/45/190ce6db8dcb4536682cf75d3889ff1a27182a58cb519d343cb6d9ea63d8/watchfiles-1.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:71283b39fd17e5408eb123bd37aeecfd9d54c81fc184421943208aadb879d103", size = 288441, upload-time = "2026-05-18T04:32:12.901Z" }, + { url = "https://files.pythonhosted.org/packages/74/0d/3eae1c2313ab08378431d907c3f8095ecca00f3eda33111cf4f0f2591799/watchfiles-1.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:c5c19526f4e54a00f2666a6c0e9e40d582c09e865055ea7378bf0009aab857b3", size = 280684, upload-time = "2026-05-18T04:31:26.902Z" }, + { url = "https://files.pythonhosted.org/packages/b1/75/fb64e6c25d6b5ca636d03df34ffb1c6e9873303e76d27967e045f8df088f/watchfiles-1.2.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:d73a585accffa5ae39c17264c36ec3166d2fad7000c780f5ef83b2722afb9dd2", size = 398857, upload-time = "2026-05-18T04:32:17.108Z" }, + { url = "https://files.pythonhosted.org/packages/73/4e/9f7adf01754cbf81843722ccfec169d8f26c69778281a302855cecd2ee08/watchfiles-1.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ae99b14c5f21e026e0e9d96f40e07d8570ebee6cafd9d8fc318354606daa7a28", size = 392413, upload-time = "2026-05-18T04:31:07.911Z" }, + { url = "https://files.pythonhosted.org/packages/47/c8/bec626bcc2d69f44b9acb24ce7d60ed7b16b73628eea747fcbd169d8edda/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4429f3b105524a10b72c3a819b091c495d2811d419c1e1e8df773a5a5974f831", size = 452409, upload-time = "2026-05-18T04:31:20.142Z" }, + { url = "https://files.pythonhosted.org/packages/00/b7/b6362068e81e7c556d155a34c35d40ac3ef42d747b06d7f6e5bf58e359c2/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:43d818978d06062d9b22c4fab2ebe44cf5213d42dc8e62bda8c2760cfa2eeb33", size = 458827, upload-time = "2026-05-18T04:32:06.219Z" }, + { url = "https://files.pythonhosted.org/packages/67/f8/9a813fa42afb1e0b4625e75f0479826644d3ee8dc287e093799bc01f390c/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b9f732dc58b2dbe69e464ccf8fff7a03b0dd0be439da4c0720d3558527d3d6b4", size = 490104, upload-time = "2026-05-18T04:31:56.034Z" }, + { url = "https://files.pythonhosted.org/packages/2f/bf/27dfb6094ca4c9aad21298b5525b6c53cb36121ee454331d05161e58d130/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f200104103feb097de4cab8fe4f5dd18a2026934c7dea98c55a2f5fd6d5a33b", size = 571360, upload-time = "2026-05-18T04:31:57.133Z" }, + { url = "https://files.pythonhosted.org/packages/fb/39/44a096d67270ea93df91d33877dbe91fbda3aa4f8ec2edf799d93eda8736/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:63ac26eefbf4af1741247d6fb68b11c49a25b2f7413fbd318a83a12aaa9cf666", size = 464644, upload-time = "2026-05-18T04:30:57.33Z" }, + { url = "https://files.pythonhosted.org/packages/0e/80/c7472203bad6268e3ef1ad260739704847898938ad7ea8b63a5131f46b50/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c4997d4e4a55f0d02b6cde327322daf3a0400e5df6c6b15948994bf72497925", size = 454771, upload-time = "2026-05-18T04:30:48.736Z" }, + { url = "https://files.pythonhosted.org/packages/51/cf/3b10b268b4b7f0fc26e9debb5eef1998b515887840f444cd3ec80c688755/watchfiles-1.2.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:4c887eba18b7945ac73067a8b4a66f21cd46c2539b2bc68588f7be6c7eb6d26b", size = 463494, upload-time = "2026-05-18T04:31:33.826Z" }, + { url = "https://files.pythonhosted.org/packages/3d/3e/a4302545cd589262a0dc7d140e86f7688eba3f9c72776c27f7e23b8864c4/watchfiles-1.2.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:3416ff151bb6b5a8d8d11664974fbef4d9305b9b2957839ab5a270468fd8df30", size = 629383, upload-time = "2026-05-18T04:31:15.596Z" }, + { url = "https://files.pythonhosted.org/packages/db/99/d5649df0a9a410d45b7c882304d0b790903ac9b6e8f2cfd12114e0c6b9f2/watchfiles-1.2.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:0e831a271c035d89789cffc386b6aa1375f39f1cd25eb7ca0997e4970d152fc5", size = 656093, upload-time = "2026-05-18T04:31:58.707Z" }, + { url = "https://files.pythonhosted.org/packages/92/b9/362702539275019a54dd2e94511b31a9b89c5f9e6a21966de7eb692549fc/watchfiles-1.2.0-cp315-cp315-macosx_10_12_x86_64.whl", hash = "sha256:37a6721cdf3f65dbb13aa9503510ccb4451603ac837e44d265d7992a597e1374", size = 400109, upload-time = "2026-05-18T04:31:16.879Z" }, + { url = "https://files.pythonhosted.org/packages/8f/75/71d5ba62db781e5587bded1d944c675374bc4aa37ff33d5018d98e8b6538/watchfiles-1.2.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:2b37d10b5a63bd4d87e18472d80fa525bd670586fae62e5dd580452764879b65", size = 392167, upload-time = "2026-05-18T04:31:28.058Z" }, + { url = "https://files.pythonhosted.org/packages/3c/01/c66dd95d0423fe30d31820e2d1d5bda773764131bbb6ac0cb1cf303ac328/watchfiles-1.2.0-cp315-cp315-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a105bc2283f67e8fbec74253ec2d94925de92ed72c0393f1206bf326b7b7b69", size = 452372, upload-time = "2026-05-18T04:31:00.836Z" }, + { url = "https://files.pythonhosted.org/packages/91/15/2fe99557e72f85627c6a8eed50d889e8d101623e060a22ad75b875cb932d/watchfiles-1.2.0-cp315-cp315-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5327989a465505f05cfe06f04fa9d0c2fd5432bb243e10e6f012b1bdca3c8579", size = 459596, upload-time = "2026-05-18T04:31:34.96Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/d4acfa0023367428ed48351b3b9b267893037b6cadae55620c61c24bcfd4/watchfiles-1.2.0-cp315-cp315-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ecb47f183a8025b2aa18b546725c3657e542112ae9c0613a2af79b4fa8d04ad7", size = 490869, upload-time = "2026-05-18T04:31:59.923Z" }, + { url = "https://files.pythonhosted.org/packages/a4/5f/3164cbdce06c9fb95c4f7b9e2f9760b5e2797af43a9ecc317ef42a23a278/watchfiles-1.2.0-cp315-cp315-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8520a4ab0e37f770afc34459c4f8f7019e153f9124dc101c15538365875d1ab2", size = 571641, upload-time = "2026-05-18T04:32:00.948Z" }, + { url = "https://files.pythonhosted.org/packages/41/e6/85d3731c55e65cd7690f3f803d24c139588aaf863e4bf2148fe7a7fa1a19/watchfiles-1.2.0-cp315-cp315-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:71cd71740ed2c15211ebb237ced4e39a1cdf6f80566e5fe95428da1626f4fde6", size = 464444, upload-time = "2026-05-18T04:30:34.298Z" }, + { url = "https://files.pythonhosted.org/packages/f4/7d/562641012b8b09872742c3b8adf9629ec479fd78f8d68ae4a0c13da8add6/watchfiles-1.2.0-cp315-cp315-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f88af53d6ddaf72179ef613ddc905e6f4785f712b49b80b3bef9f3525e6194b4", size = 453593, upload-time = "2026-05-18T04:31:23.464Z" }, + { url = "https://files.pythonhosted.org/packages/56/fe/cb8ef3d6f929d14158fdaaad9925985b7310abc9384dcd4d82dd0016fb59/watchfiles-1.2.0-cp315-cp315-manylinux_2_31_riscv64.whl", hash = "sha256:cee9d5efd929efdac5f7e58f72b3376f676b64050a91c5b99a7094c5b2317488", size = 465096, upload-time = "2026-05-18T04:31:30.384Z" }, + { url = "https://files.pythonhosted.org/packages/25/91/80908e835e100527a9267147b08c0eee1fa6ab0ffec15edc04d1d44885f7/watchfiles-1.2.0-cp315-cp315-musllinux_1_1_aarch64.whl", hash = "sha256:b718bf356bbc15e559bd8ef41782b573b8ae0e3f177ab244b440568d7ea02cfb", size = 630638, upload-time = "2026-05-18T04:30:49.89Z" }, + { url = "https://files.pythonhosted.org/packages/46/4b/95ab2f256bb4af3cb2eb23b9317bda984ee6e0f11733a5c004a6c95b06e3/watchfiles-1.2.0-cp315-cp315-musllinux_1_1_x86_64.whl", hash = "sha256:922c0e019fe68b3ae392965a766b02a71ba1168c932cebc3733cd52c5fe5b377", size = 657684, upload-time = "2026-05-18T04:31:32.027Z" }, +] + [[package]] name = "wcmatch" version = "10.1" @@ -1671,6 +2220,51 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/eb/d8/0d1d2e9d3fabcf5d6840362adcf05f8cf3cd06a73358140c3a97189238ae/wcmatch-10.1-py3-none-any.whl", hash = "sha256:5848ace7dbb0476e5e55ab63c6bbd529745089343427caa5537f230cc01beb8a", size = 39854, upload-time = "2025-06-22T19:14:00.978Z" }, ] +[[package]] +name = "websockets" +version = "16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/04/24/4b2031d72e840ce4c1ccb255f693b15c334757fc50023e4db9537080b8c4/websockets-16.0.tar.gz", hash = "sha256:5f6261a5e56e8d5c42a4497b364ea24d94d9563e8fbd44e78ac40879c60179b5", size = 179346, upload-time = "2026-01-10T09:23:47.181Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/7b/bac442e6b96c9d25092695578dda82403c77936104b5682307bd4deb1ad4/websockets-16.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:71c989cbf3254fbd5e84d3bff31e4da39c43f884e64f2551d14bb3c186230f00", size = 177365, upload-time = "2026-01-10T09:22:46.787Z" }, + { url = "https://files.pythonhosted.org/packages/b0/fe/136ccece61bd690d9c1f715baaeefd953bb2360134de73519d5df19d29ca/websockets-16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8b6e209ffee39ff1b6d0fa7bfef6de950c60dfb91b8fcead17da4ee539121a79", size = 175038, upload-time = "2026-01-10T09:22:47.999Z" }, + { url = "https://files.pythonhosted.org/packages/40/1e/9771421ac2286eaab95b8575b0cb701ae3663abf8b5e1f64f1fd90d0a673/websockets-16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:86890e837d61574c92a97496d590968b23c2ef0aeb8a9bc9421d174cd378ae39", size = 175328, upload-time = "2026-01-10T09:22:49.809Z" }, + { url = "https://files.pythonhosted.org/packages/18/29/71729b4671f21e1eaa5d6573031ab810ad2936c8175f03f97f3ff164c802/websockets-16.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9b5aca38b67492ef518a8ab76851862488a478602229112c4b0d58d63a7a4d5c", size = 184915, upload-time = "2026-01-10T09:22:51.071Z" }, + { url = "https://files.pythonhosted.org/packages/97/bb/21c36b7dbbafc85d2d480cd65df02a1dc93bf76d97147605a8e27ff9409d/websockets-16.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e0334872c0a37b606418ac52f6ab9cfd17317ac26365f7f65e203e2d0d0d359f", size = 186152, upload-time = "2026-01-10T09:22:52.224Z" }, + { url = "https://files.pythonhosted.org/packages/4a/34/9bf8df0c0cf88fa7bfe36678dc7b02970c9a7d5e065a3099292db87b1be2/websockets-16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a0b31e0b424cc6b5a04b8838bbaec1688834b2383256688cf47eb97412531da1", size = 185583, upload-time = "2026-01-10T09:22:53.443Z" }, + { url = "https://files.pythonhosted.org/packages/47/88/4dd516068e1a3d6ab3c7c183288404cd424a9a02d585efbac226cb61ff2d/websockets-16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:485c49116d0af10ac698623c513c1cc01c9446c058a4e61e3bf6c19dff7335a2", size = 184880, upload-time = "2026-01-10T09:22:55.033Z" }, + { url = "https://files.pythonhosted.org/packages/91/d6/7d4553ad4bf1c0421e1ebd4b18de5d9098383b5caa1d937b63df8d04b565/websockets-16.0-cp312-cp312-win32.whl", hash = "sha256:eaded469f5e5b7294e2bdca0ab06becb6756ea86894a47806456089298813c89", size = 178261, upload-time = "2026-01-10T09:22:56.251Z" }, + { url = "https://files.pythonhosted.org/packages/c3/f0/f3a17365441ed1c27f850a80b2bc680a0fa9505d733fe152fdf5e98c1c0b/websockets-16.0-cp312-cp312-win_amd64.whl", hash = "sha256:5569417dc80977fc8c2d43a86f78e0a5a22fee17565d78621b6bb264a115d4ea", size = 178693, upload-time = "2026-01-10T09:22:57.478Z" }, + { url = "https://files.pythonhosted.org/packages/cc/9c/baa8456050d1c1b08dd0ec7346026668cbc6f145ab4e314d707bb845bf0d/websockets-16.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:878b336ac47938b474c8f982ac2f7266a540adc3fa4ad74ae96fea9823a02cc9", size = 177364, upload-time = "2026-01-10T09:22:59.333Z" }, + { url = "https://files.pythonhosted.org/packages/7e/0c/8811fc53e9bcff68fe7de2bcbe75116a8d959ac699a3200f4847a8925210/websockets-16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:52a0fec0e6c8d9a784c2c78276a48a2bdf099e4ccc2a4cad53b27718dbfd0230", size = 175039, upload-time = "2026-01-10T09:23:01.171Z" }, + { url = "https://files.pythonhosted.org/packages/aa/82/39a5f910cb99ec0b59e482971238c845af9220d3ab9fa76dd9162cda9d62/websockets-16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e6578ed5b6981005df1860a56e3617f14a6c307e6a71b4fff8c48fdc50f3ed2c", size = 175323, upload-time = "2026-01-10T09:23:02.341Z" }, + { url = "https://files.pythonhosted.org/packages/bd/28/0a25ee5342eb5d5f297d992a77e56892ecb65e7854c7898fb7d35e9b33bd/websockets-16.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:95724e638f0f9c350bb1c2b0a7ad0e83d9cc0c9259f3ea94e40d7b02a2179ae5", size = 184975, upload-time = "2026-01-10T09:23:03.756Z" }, + { url = "https://files.pythonhosted.org/packages/f9/66/27ea52741752f5107c2e41fda05e8395a682a1e11c4e592a809a90c6a506/websockets-16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0204dc62a89dc9d50d682412c10b3542d748260d743500a85c13cd1ee4bde82", size = 186203, upload-time = "2026-01-10T09:23:05.01Z" }, + { url = "https://files.pythonhosted.org/packages/37/e5/8e32857371406a757816a2b471939d51c463509be73fa538216ea52b792a/websockets-16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:52ac480f44d32970d66763115edea932f1c5b1312de36df06d6b219f6741eed8", size = 185653, upload-time = "2026-01-10T09:23:06.301Z" }, + { url = "https://files.pythonhosted.org/packages/9b/67/f926bac29882894669368dc73f4da900fcdf47955d0a0185d60103df5737/websockets-16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6e5a82b677f8f6f59e8dfc34ec06ca6b5b48bc4fcda346acd093694cc2c24d8f", size = 184920, upload-time = "2026-01-10T09:23:07.492Z" }, + { url = "https://files.pythonhosted.org/packages/3c/a1/3d6ccdcd125b0a42a311bcd15a7f705d688f73b2a22d8cf1c0875d35d34a/websockets-16.0-cp313-cp313-win32.whl", hash = "sha256:abf050a199613f64c886ea10f38b47770a65154dc37181bfaff70c160f45315a", size = 178255, upload-time = "2026-01-10T09:23:09.245Z" }, + { url = "https://files.pythonhosted.org/packages/6b/ae/90366304d7c2ce80f9b826096a9e9048b4bb760e44d3b873bb272cba696b/websockets-16.0-cp313-cp313-win_amd64.whl", hash = "sha256:3425ac5cf448801335d6fdc7ae1eb22072055417a96cc6b31b3861f455fbc156", size = 178689, upload-time = "2026-01-10T09:23:10.483Z" }, + { url = "https://files.pythonhosted.org/packages/f3/1d/e88022630271f5bd349ed82417136281931e558d628dd52c4d8621b4a0b2/websockets-16.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8cc451a50f2aee53042ac52d2d053d08bf89bcb31ae799cb4487587661c038a0", size = 177406, upload-time = "2026-01-10T09:23:12.178Z" }, + { url = "https://files.pythonhosted.org/packages/f2/78/e63be1bf0724eeb4616efb1ae1c9044f7c3953b7957799abb5915bffd38e/websockets-16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:daa3b6ff70a9241cf6c7fc9e949d41232d9d7d26fd3522b1ad2b4d62487e9904", size = 175085, upload-time = "2026-01-10T09:23:13.511Z" }, + { url = "https://files.pythonhosted.org/packages/bb/f4/d3c9220d818ee955ae390cf319a7c7a467beceb24f05ee7aaaa2414345ba/websockets-16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:fd3cb4adb94a2a6e2b7c0d8d05cb94e6f1c81a0cf9dc2694fb65c7e8d94c42e4", size = 175328, upload-time = "2026-01-10T09:23:14.727Z" }, + { url = "https://files.pythonhosted.org/packages/63/bc/d3e208028de777087e6fb2b122051a6ff7bbcca0d6df9d9c2bf1dd869ae9/websockets-16.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:781caf5e8eee67f663126490c2f96f40906594cb86b408a703630f95550a8c3e", size = 185044, upload-time = "2026-01-10T09:23:15.939Z" }, + { url = "https://files.pythonhosted.org/packages/ad/6e/9a0927ac24bd33a0a9af834d89e0abc7cfd8e13bed17a86407a66773cc0e/websockets-16.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:caab51a72c51973ca21fa8a18bd8165e1a0183f1ac7066a182ff27107b71e1a4", size = 186279, upload-time = "2026-01-10T09:23:17.148Z" }, + { url = "https://files.pythonhosted.org/packages/b9/ca/bf1c68440d7a868180e11be653c85959502efd3a709323230314fda6e0b3/websockets-16.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:19c4dc84098e523fd63711e563077d39e90ec6702aff4b5d9e344a60cb3c0cb1", size = 185711, upload-time = "2026-01-10T09:23:18.372Z" }, + { url = "https://files.pythonhosted.org/packages/c4/f8/fdc34643a989561f217bb477cbc47a3a07212cbda91c0e4389c43c296ebf/websockets-16.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a5e18a238a2b2249c9a9235466b90e96ae4795672598a58772dd806edc7ac6d3", size = 184982, upload-time = "2026-01-10T09:23:19.652Z" }, + { url = "https://files.pythonhosted.org/packages/dd/d1/574fa27e233764dbac9c52730d63fcf2823b16f0856b3329fc6268d6ae4f/websockets-16.0-cp314-cp314-win32.whl", hash = "sha256:a069d734c4a043182729edd3e9f247c3b2a4035415a9172fd0f1b71658a320a8", size = 177915, upload-time = "2026-01-10T09:23:21.458Z" }, + { url = "https://files.pythonhosted.org/packages/8a/f1/ae6b937bf3126b5134ce1f482365fde31a357c784ac51852978768b5eff4/websockets-16.0-cp314-cp314-win_amd64.whl", hash = "sha256:c0ee0e63f23914732c6d7e0cce24915c48f3f1512ec1d079ed01fc629dab269d", size = 178381, upload-time = "2026-01-10T09:23:22.715Z" }, + { url = "https://files.pythonhosted.org/packages/06/9b/f791d1db48403e1f0a27577a6beb37afae94254a8c6f08be4a23e4930bc0/websockets-16.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:a35539cacc3febb22b8f4d4a99cc79b104226a756aa7400adc722e83b0d03244", size = 177737, upload-time = "2026-01-10T09:23:24.523Z" }, + { url = "https://files.pythonhosted.org/packages/bd/40/53ad02341fa33b3ce489023f635367a4ac98b73570102ad2cdd770dacc9a/websockets-16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:b784ca5de850f4ce93ec85d3269d24d4c82f22b7212023c974c401d4980ebc5e", size = 175268, upload-time = "2026-01-10T09:23:25.781Z" }, + { url = "https://files.pythonhosted.org/packages/74/9b/6158d4e459b984f949dcbbb0c5d270154c7618e11c01029b9bbd1bb4c4f9/websockets-16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:569d01a4e7fba956c5ae4fc988f0d4e187900f5497ce46339c996dbf24f17641", size = 175486, upload-time = "2026-01-10T09:23:27.033Z" }, + { url = "https://files.pythonhosted.org/packages/e5/2d/7583b30208b639c8090206f95073646c2c9ffd66f44df967981a64f849ad/websockets-16.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:50f23cdd8343b984957e4077839841146f67a3d31ab0d00e6b824e74c5b2f6e8", size = 185331, upload-time = "2026-01-10T09:23:28.259Z" }, + { url = "https://files.pythonhosted.org/packages/45/b0/cce3784eb519b7b5ad680d14b9673a31ab8dcb7aad8b64d81709d2430aa8/websockets-16.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:152284a83a00c59b759697b7f9e9cddf4e3c7861dd0d964b472b70f78f89e80e", size = 186501, upload-time = "2026-01-10T09:23:29.449Z" }, + { url = "https://files.pythonhosted.org/packages/19/60/b8ebe4c7e89fb5f6cdf080623c9d92789a53636950f7abacfc33fe2b3135/websockets-16.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bc59589ab64b0022385f429b94697348a6a234e8ce22544e3681b2e9331b5944", size = 186062, upload-time = "2026-01-10T09:23:31.368Z" }, + { url = "https://files.pythonhosted.org/packages/88/a8/a080593f89b0138b6cba1b28f8df5673b5506f72879322288b031337c0b8/websockets-16.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:32da954ffa2814258030e5a57bc73a3635463238e797c7375dc8091327434206", size = 185356, upload-time = "2026-01-10T09:23:32.627Z" }, + { url = "https://files.pythonhosted.org/packages/c2/b6/b9afed2afadddaf5ebb2afa801abf4b0868f42f8539bfe4b071b5266c9fe/websockets-16.0-cp314-cp314t-win32.whl", hash = "sha256:5a4b4cc550cb665dd8a47f868c8d04c8230f857363ad3c9caf7a0c3bf8c61ca6", size = 178085, upload-time = "2026-01-10T09:23:33.816Z" }, + { url = "https://files.pythonhosted.org/packages/9f/3e/28135a24e384493fa804216b79a6a6759a38cc4ff59118787b9fb693df93/websockets-16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b14dc141ed6d2dde437cddb216004bcac6a1df0935d79656387bd41632ba0bbd", size = 178531, upload-time = "2026-01-10T09:23:35.016Z" }, + { url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598, upload-time = "2026-01-10T09:23:45.395Z" }, +] + [[package]] name = "werkzeug" version = "3.1.3" From a2f205a8996e28891d2e6b0bdf34502dbc8cc548 Mon Sep 17 00:00:00 2001 From: Tom Wawerek Date: Thu, 18 Jun 2026 01:12:20 +0200 Subject: [PATCH 07/18] refactor: data validation --- Dockerfile | 2 +- pyproject.toml | 38 +- src/SELECT.sql | 27 +- src/scheduling/api/app.py | 109 +++- src/scheduling/api/data_router.py | 48 -- src/scheduling/api/dependencies.py | 15 +- src/scheduling/api/solver_router.py | 65 --- src/scheduling/api/web_router.py | 13 + src/scheduling/models/__init__.py | 25 +- src/scheduling/models/assignment.py | 12 +- src/scheduling/models/availability.py | 6 +- src/scheduling/models/dataset.py | 350 +----------- src/scheduling/models/demand.py | 8 +- src/scheduling/models/employee.py | 2 +- src/scheduling/models/monthly_work_account.py | 10 + src/scheduling/models/plan.py | 6 +- src/scheduling/models/planning_unit.py | 4 +- src/scheduling/models/shift.py | 2 +- src/scheduling/models/sunday_work_history.py | 4 +- src/scheduling/models/wish.py | 34 ++ src/{ => scheduling/solver}/__init__.py | 0 src/scheduling/solver/tmp.py | 24 + src/scheduling/timeoffice/database.py | 29 +- src/scheduling/timeoffice/facts.py | 124 ++--- .../timeoffice/repositories/__init__.py | 14 +- .../timeoffice/repositories/container.py | 20 +- .../timeoffice/repositories/demand.py | 6 +- .../timeoffice/repositories/helpers.py | 76 --- .../repositories/monthly_work_accounts.py | 189 +++++++ .../timeoffice/repositories/personnel.py | 317 +++++------ .../timeoffice/repositories/planning_units.py | 165 +++--- .../timeoffice/repositories/roster.py | 421 +++++++-------- .../timeoffice/repositories/shifts.py | 247 ++++----- .../repositories/sunday_work_history.py | 127 +++-- .../timeoffice/repositories/types.py | 38 ++ .../timeoffice/repositories/wishes.py | 315 +++++++++++ src/scheduling/timeoffice/service.py | 9 +- src/scheduling/validation/__init__.py | 0 src/scheduling/validation/context.py | 58 ++ src/scheduling/validation/dataset.py | 35 ++ src/scheduling/validation/helpers.py | 17 + src/scheduling/validation/validators.py | 266 +++++++++ uv.lock | 506 ++---------------- 43 files changed, 1967 insertions(+), 1816 deletions(-) delete mode 100644 src/scheduling/api/data_router.py delete mode 100644 src/scheduling/api/solver_router.py create mode 100644 src/scheduling/api/web_router.py create mode 100644 src/scheduling/models/monthly_work_account.py create mode 100644 src/scheduling/models/wish.py rename src/{ => scheduling/solver}/__init__.py (100%) create mode 100644 src/scheduling/solver/tmp.py delete mode 100644 src/scheduling/timeoffice/repositories/helpers.py create mode 100644 src/scheduling/timeoffice/repositories/monthly_work_accounts.py create mode 100644 src/scheduling/timeoffice/repositories/types.py create mode 100644 src/scheduling/timeoffice/repositories/wishes.py create mode 100644 src/scheduling/validation/__init__.py create mode 100644 src/scheduling/validation/context.py create mode 100644 src/scheduling/validation/dataset.py create mode 100644 src/scheduling/validation/helpers.py create mode 100644 src/scheduling/validation/validators.py diff --git a/Dockerfile b/Dockerfile index 52c89d41..e20d513e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -35,7 +35,7 @@ COPY pyproject.toml uv.lock README.md ./ COPY src ./src RUN --mount=type=cache,target=/root/.cache/uv \ - uv sync --locked --no-dev + uv sync --locked EXPOSE 8000 diff --git a/pyproject.toml b/pyproject.toml index 74465cad..8ab09608 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,24 +1,16 @@ [project] -name = "staffscheduling" -version = "0.0.0" +name = "scheduling" +version = "0.1.0" description = "" readme = "README.md" requires-python = ">=3.12" dependencies = [ "fastapi[standard]>=0.137.0", + "ortools>=9.15.6755", "SQLAlchemy>=2.0.50", + "pyodbc>=5.3.0", "pydantic>=2.13.4", "pydantic-settings>=2.14.1", - "ortools>=9.15.6755", - - # Legacy -> Should be removed after refactoring - "click>=8.2.0", - "flask>=3.1.0", - "pyodbc>=5.2.0", - "matplotlib>=3.10.1", - "pandas>=2.2.3", - "numpy>=2.2.5", - "dotenv>=0.9.9", ] [dependency-groups] @@ -41,22 +33,8 @@ docs = [ ] [build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - -[tool.hatch.build.targets.wheel] -packages = ["src"] - -[project.scripts] -staff-scheduling = "src.main:main" -staff-scheduling-api = "src.api.main:main" - -[tool.pyright] -typeCheckingMode = "strict" -reportUnusedVariable = "warning" -ignore = [ - "src/db/**" # Database integration needs significant refactoring and is excluded from type checking -] +requires = ["uv_build>=0.11.21,<0.12"] +build-backend = "uv_build" [tool.ruff] line-length = 120 @@ -64,6 +42,10 @@ line-length = 120 [tool.ruff.lint] select = ["E", "F", "B", "W", "I", "C4", "ISC", "PT", "Q", "UP"] # Plan to add 'N' and 'ANN' later +[tool.pyright] +typeCheckingMode = "strict" +reportUnusedVariable = "warning" + [tool.pytest.ini_options] addopts = "-m 'not integration'" markers = [ diff --git a/src/SELECT.sql b/src/SELECT.sql index 1f084063..d59eba4e 100644 --- a/src/SELECT.sql +++ b/src/SELECT.sql @@ -1,18 +1,11 @@ -WITH selected_employees AS ( - SELECT DISTINCT pp.RefPersonal AS employee_id - FROM TPlanPersonal pp - WHERE pp.RefPlan IN (17916, 17045) -) SELECT - se.employee_id, - COUNT(DISTINCT CAST(pkt.Datum AS date)) AS worked_sundays -FROM selected_employees se -LEFT JOIN TPersonalKontenJeTag pkt - ON pkt.RefPersonal = se.employee_id - AND pkt.RefKonten = 40 - AND pkt.Datum BETWEEN CONVERT(date, '2023-11-30', 23) - AND CONVERT(date, '2024-11-30', 23) - AND DATEDIFF(day, CONVERT(date, '1900-01-07', 23), CAST(pkt.Datum AS date)) % 7 = 0 - AND ISNULL(pkt.Wert, 0) > 0 -GROUP BY se.employee_id -ORDER BY worked_sundays DESC, se.employee_id; + Prim, + KurzBez, + Bezeichnung, + BezProg, + VbaRelevantJN, + GlobalJN, + PpugRelevant, + PpprlRelevant +FROM TEinsatzArten +WHERE Prim IN (151); diff --git a/src/scheduling/api/app.py b/src/scheduling/api/app.py index dfa63c06..9d413af1 100644 --- a/src/scheduling/api/app.py +++ b/src/scheduling/api/app.py @@ -1,11 +1,20 @@ +import asyncio import logging +import uuid +from typing import Annotated -from fastapi import FastAPI +from fastapi import BackgroundTasks, Depends, FastAPI, HTTPException, Query, status -from src.scheduling.api.data_router import data_router -from src.scheduling.api.solver_router import solver_router -from src.scheduling.logging import configure_logging -from src.scheduling.settings import get_settings +from scheduling.api.dependencies import get_solver_service, get_timeoffice_service +from scheduling.api.types import ApiDate +from scheduling.api.web_router import web_router +from scheduling.logging import configure_logging +from scheduling.models import PlanningPeriod +from scheduling.models.assignment import AssignmentType +from scheduling.models.wish import WishKind +from scheduling.settings import get_settings +from scheduling.solver.tmp import FakeSolution, SolverService +from scheduling.timeoffice.service import TimeOfficeService settings = get_settings() configure_logging(level=settings.log_level) @@ -13,10 +22,96 @@ logger = logging.getLogger(__name__) app = FastAPI(title="Staff Scheduling API") -app.include_router(solver_router, prefix="/solver") -app.include_router(data_router, prefix="/data") +app.include_router(web_router) + +solve_lock = asyncio.Lock() @app.get("/health") async def healthcheck(): return {"status": "healthy"} + + +@app.get("/debug") +def fetch_timeoffice_dataset( + station_ids: Annotated[list[int], Query(alias="station")], + start: Annotated[ApiDate, Query()], + end: Annotated[ApiDate, Query()], + timeoffice: Annotated[TimeOfficeService, Depends(get_timeoffice_service)], +) -> dict[str, int]: + period = PlanningPeriod(start=start, end=end) + + dataset = timeoffice.fetch_dataset( + planning_unit_ids=tuple(station_ids), + period=period, + ) + + return { + "planning_units": len(dataset.planning_units), + "plans": len(dataset.plans), + "employees": len(dataset.employees), + "plan_participants": len(dataset.plan_participants), + "planning_unit_memberships": len(dataset.planning_unit_memberships), + "shifts": len(dataset.shifts), + "assignments": len(dataset.assignments), + "planned_assignments": sum( + assignment.assignment_type == AssignmentType.PLANNED for assignment in dataset.assignments + ), + "external_assignments": sum( + assignment.assignment_type == AssignmentType.EXTERNAL for assignment in dataset.assignments + ), + "availability": len(dataset.availability), + "demand_requirements": len(dataset.demand_requirements), + "sunday_work_history": len(dataset.sunday_work_history), + "wishes": len(dataset.wishes), + "shift_wishes": sum(wish.kind == WishKind.SHIFT for wish in dataset.wishes), + "free_day_wishes": sum(wish.kind == WishKind.FREE_DAY for wish in dataset.wishes), + "monthly_work_accounts": len(dataset.monthly_work_accounts), + "employees_without_monthly_work_account": len(dataset.employees) - len(dataset.monthly_work_accounts), + } + + +@app.post("/solve") +async def solve_schedule( + background_tasks: BackgroundTasks, + solver: Annotated[SolverService, Depends(get_solver_service)], +) -> dict[str, str]: + try: + async with asyncio.timeout(2): + await solve_lock.acquire() + except TimeoutError as e: + raise HTTPException( + status_code=status.HTTP_423_LOCKED, + detail="Solver already working on a different solution. Try again later!", + ) from e + + unique_id = uuid.uuid4() + + async def task() -> None: + try: + await solver.fake_solve(id=unique_id) + finally: + solve_lock.release() + + background_tasks.add_task(task) + + return { + "status": "accepted", + "solution_id": str(unique_id), + } + + +@app.get("/solution") +def get_solution( + id: Annotated[uuid.UUID, Query()], + solver: Annotated[SolverService, Depends(get_solver_service)], +) -> FakeSolution: + solution = next(filter(lambda s: s.id == id, solver.solutions), None) + + if solution is None: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="Solution not found!", + ) + + return solution diff --git a/src/scheduling/api/data_router.py b/src/scheduling/api/data_router.py deleted file mode 100644 index 2f35c0bc..00000000 --- a/src/scheduling/api/data_router.py +++ /dev/null @@ -1,48 +0,0 @@ -import logging -from typing import Annotated - -from fastapi import APIRouter, Depends, Query - -from src.scheduling.api.dependencies import get_timeoffice_service -from src.scheduling.api.types import ApiDate -from src.scheduling.models import PlanningPeriod -from src.scheduling.models.assignment import AssignmentType -from src.scheduling.timeoffice.service import TimeOfficeService - -logger = logging.getLogger(__name__) - -data_router = APIRouter() - - -@data_router.get("/fetch") -def fetch_timeoffice_dataset( - station_ids: Annotated[list[int], Query(alias="station")], - start: Annotated[ApiDate, Query()], - end: Annotated[ApiDate, Query()], - timeoffice: Annotated[TimeOfficeService, Depends(get_timeoffice_service)], -) -> dict[str, int]: - period = PlanningPeriod(start=start, end=end) - - dataset = timeoffice.fetch_dataset( - planning_unit_ids=tuple(station_ids), - period=period, - ) - - return { - "planning_units": len(dataset.planning_units), - "plans": len(dataset.plans), - "employees": len(dataset.employees), - "plan_participants": len(dataset.plan_participants), - "planning_unit_memberships": len(dataset.planning_unit_memberships), - "shifts": len(dataset.shifts), - "assignments": len(dataset.assignments), - "planned_assignments": sum( - assignment.assignment_type == AssignmentType.PLANNED for assignment in dataset.assignments - ), - "external_assignments": sum( - assignment.assignment_type == AssignmentType.EXTERNAL for assignment in dataset.assignments - ), - "availability": len(dataset.availability), - "demand_requirements": len(dataset.demand_requirements), - "sunday_work_history": len(dataset.sunday_work_history), - } diff --git a/src/scheduling/api/dependencies.py b/src/scheduling/api/dependencies.py index 5a0c9d0c..c4dd3550 100644 --- a/src/scheduling/api/dependencies.py +++ b/src/scheduling/api/dependencies.py @@ -4,11 +4,12 @@ from fastapi import Depends from sqlalchemy import Engine -from src.scheduling.settings import get_settings -from src.scheduling.timeoffice.database import TimeOfficeDatabase, create_db_engine -from src.scheduling.timeoffice.facts import TIMEOFFICE_FACTS, TimeOfficeFacts -from src.scheduling.timeoffice.repositories import TimeOfficeRepositories -from src.scheduling.timeoffice.service import TimeOfficeService +from scheduling.settings import get_settings +from scheduling.solver.tmp import SolverService +from scheduling.timeoffice.database import TimeOfficeDatabase, create_db_engine +from scheduling.timeoffice.facts import TIMEOFFICE_FACTS, TimeOfficeFacts +from scheduling.timeoffice.repositories import TimeOfficeRepositories +from scheduling.timeoffice.service import TimeOfficeService @lru_cache(maxsize=1) @@ -44,3 +45,7 @@ def get_timeoffice_service( ) -> TimeOfficeService: """Create the high-level TimeOffice service.""" return TimeOfficeService(database=database, facts=facts) + + +async def get_solver_service() -> SolverService: + return SolverService() diff --git a/src/scheduling/api/solver_router.py b/src/scheduling/api/solver_router.py deleted file mode 100644 index f9eb266a..00000000 --- a/src/scheduling/api/solver_router.py +++ /dev/null @@ -1,65 +0,0 @@ -import asyncio -import logging -import uuid -from dataclasses import dataclass -from typing import Annotated - -from fastapi import APIRouter, BackgroundTasks, Query, status -from fastapi.responses import JSONResponse - -logger = logging.getLogger(__name__) - - -solver_router = APIRouter() - - -@dataclass -class FakeSolution: - id: str - value: str - - -solutions: list[FakeSolution] = [] -lock = asyncio.Lock() - - -async def fake_solve(id: uuid.UUID) -> str: - logger.info("Started processing") - await asyncio.sleep(5) - logger.info("Finished processing") - - return f"{id} processed!" - - -@solver_router.post("/solve") -async def solve_schedule(background_tasks: BackgroundTasks): - try: - async with asyncio.timeout(2): - await lock.acquire() - except TimeoutError: - return JSONResponse( - status_code=status.HTTP_423_LOCKED, - content={ - "status": "blocked", - "message": "Solver already working on a different solution. Try again later!", - }, - ) - - unique_id = uuid.uuid4() - background_tasks.add_task(fake_solve, unique_id) - - -@solver_router.get("/solution") -def get_solution(id: Annotated[uuid.UUID, Query()]): - solution = next(filter(lambda s: s.id == id, solutions), None) - - if solution is None: - return JSONResponse( - status_code=status.HTTP_404_NOT_FOUND, - content={ - "status": "failed", - "message": "Solution not found!", - }, - ) - - return solution diff --git a/src/scheduling/api/web_router.py b/src/scheduling/api/web_router.py new file mode 100644 index 00000000..3a9c073e --- /dev/null +++ b/src/scheduling/api/web_router.py @@ -0,0 +1,13 @@ +import logging + +from fastapi import APIRouter + +logger = logging.getLogger(__name__) + + +web_router = APIRouter() + + +@web_router.get("/employee") +async def get_employees(): + pass diff --git a/src/scheduling/models/__init__.py b/src/scheduling/models/__init__.py index 8b92090d..d65ac4cf 100644 --- a/src/scheduling/models/__init__.py +++ b/src/scheduling/models/__init__.py @@ -1,13 +1,15 @@ -from src.scheduling.models.assignment import Assignment, AssignmentType -from src.scheduling.models.availability import Availability, AvailabilityType -from src.scheduling.models.core import MinuteOfDay, NonEmptyStr, NonNegativeInt, PositiveId, SchedulingBaseModel -from src.scheduling.models.dataset import PlanningPeriod, SchedulingDataset -from src.scheduling.models.demand import DemandRequirement -from src.scheduling.models.employee import Capability, Employee, EmployeeId, StaffLevel -from src.scheduling.models.plan import Plan, PlanId, PlanParticipant -from src.scheduling.models.planning_unit import PlanningUnit, PlanningUnitId, PlanningUnitKind, PlanningUnitMembership -from src.scheduling.models.shift import Shift, ShiftId, ShiftKind, StaffingDemandRole -from src.scheduling.models.sunday_work_history import EmployeeSundayWorkHistory +from scheduling.models.assignment import Assignment, AssignmentType +from scheduling.models.availability import Availability, AvailabilityType +from scheduling.models.core import MinuteOfDay, NonEmptyStr, NonNegativeInt, PositiveId, SchedulingBaseModel +from scheduling.models.dataset import PlanningPeriod, SchedulingDataset +from scheduling.models.demand import DemandRequirement +from scheduling.models.employee import Capability, Employee, EmployeeId, StaffLevel +from scheduling.models.monthly_work_account import MonthlyWorkAccount +from scheduling.models.plan import Plan, PlanId, PlanParticipant +from scheduling.models.planning_unit import PlanningUnit, PlanningUnitId, PlanningUnitKind, PlanningUnitMembership +from scheduling.models.shift import Shift, ShiftId, ShiftKind, StaffingDemandRole +from scheduling.models.sunday_work_history import EmployeeSundayWorkHistory +from scheduling.models.wish import Wish, WishKind __all__ = [ "PositiveId", @@ -38,4 +40,7 @@ "StaffingDemandRole", "DemandRequirement", "EmployeeSundayWorkHistory", + "Wish", + "WishKind", + "MonthlyWorkAccount", ] diff --git a/src/scheduling/models/assignment.py b/src/scheduling/models/assignment.py index 2e73f2bd..ba88ef54 100644 --- a/src/scheduling/models/assignment.py +++ b/src/scheduling/models/assignment.py @@ -4,10 +4,10 @@ from pydantic import model_validator -from src.scheduling.models.core import SchedulingBaseModel -from src.scheduling.models.employee import EmployeeId -from src.scheduling.models.planning_unit import PlanningUnitId -from src.scheduling.models.shift import ShiftId +from scheduling.models.core import SchedulingBaseModel +from scheduling.models.employee import EmployeeId +from scheduling.models.planning_unit import PlanningUnitId +from scheduling.models.shift import ShiftId class AssignmentType(StrEnum): @@ -40,9 +40,9 @@ class Assignment(SchedulingBaseModel): @model_validator(mode="after") def validate_assignment(self) -> Self: if self.assignment_type == AssignmentType.PLANNED and self.planning_unit_id is None: - raise ValueError("PLANNED assignments must reference planning_unit_id.") + raise ValueError("Planned assignment must reference planning_unit_id.") if self.assignment_type == AssignmentType.EXTERNAL and self.planning_unit_id is not None: - raise ValueError("EXTERNAL assignments must not reference planning_unit_id.") + raise ValueError("External assignment must not reference planning_unit_id.") return self diff --git a/src/scheduling/models/availability.py b/src/scheduling/models/availability.py index 190b7108..d6220f9f 100644 --- a/src/scheduling/models/availability.py +++ b/src/scheduling/models/availability.py @@ -4,9 +4,9 @@ from pydantic import model_validator -from src.scheduling.models.core import SchedulingBaseModel -from src.scheduling.models.employee import EmployeeId -from src.scheduling.models.shift import ShiftId +from scheduling.models.core import SchedulingBaseModel +from scheduling.models.employee import EmployeeId +from scheduling.models.shift import ShiftId class AvailabilityType(StrEnum): diff --git a/src/scheduling/models/dataset.py b/src/scheduling/models/dataset.py index a3795f7a..97d3cd76 100644 --- a/src/scheduling/models/dataset.py +++ b/src/scheduling/models/dataset.py @@ -1,18 +1,19 @@ -from collections.abc import Sequence from datetime import date as Date from typing import Self from pydantic import model_validator -from src.scheduling.models.assignment import Assignment, AssignmentType -from src.scheduling.models.availability import Availability -from src.scheduling.models.core import SchedulingBaseModel -from src.scheduling.models.demand import DemandRequirement -from src.scheduling.models.employee import Employee, EmployeeId, StaffLevel -from src.scheduling.models.plan import Plan, PlanId, PlanParticipant -from src.scheduling.models.planning_unit import PlanningUnit, PlanningUnitId, PlanningUnitKind, PlanningUnitMembership -from src.scheduling.models.shift import Shift, ShiftId, StaffingDemandRole -from src.scheduling.models.sunday_work_history import EmployeeSundayWorkHistory +from scheduling.models.assignment import Assignment +from scheduling.models.availability import Availability +from scheduling.models.core import SchedulingBaseModel +from scheduling.models.demand import DemandRequirement +from scheduling.models.employee import Employee +from scheduling.models.monthly_work_account import MonthlyWorkAccount +from scheduling.models.plan import Plan, PlanParticipant +from scheduling.models.planning_unit import PlanningUnit, PlanningUnitMembership +from scheduling.models.shift import Shift +from scheduling.models.sunday_work_history import EmployeeSundayWorkHistory +from scheduling.models.wish import Wish class PlanningPeriod(SchedulingBaseModel): @@ -44,335 +45,16 @@ class SchedulingDataset(SchedulingBaseModel): planning_units: tuple[PlanningUnit, ...] plans: tuple[Plan, ...] + shifts: tuple[Shift, ...] = () + demand_requirements: tuple[DemandRequirement, ...] = () employees: tuple[Employee, ...] = () plan_participants: tuple[PlanParticipant, ...] = () planning_unit_memberships: tuple[PlanningUnitMembership, ...] = () + sunday_work_history: tuple[EmployeeSundayWorkHistory, ...] = () + wishes: tuple[Wish, ...] = () - shifts: tuple[Shift, ...] = () assignments: tuple[Assignment, ...] = () availability: tuple[Availability, ...] = () - demand_requirements: tuple[DemandRequirement, ...] = () - - sunday_work_history: tuple[EmployeeSundayWorkHistory, ...] = () - - @model_validator(mode="after") - def validate_dataset(self) -> Self: - employee_ids = self._unique_employee_ids() - planning_unit_ids = self._unique_planning_unit_ids() - plan_ids = self._unique_plan_ids() - shift_ids = self._unique_shift_ids() - - self._validate_plans( - planning_unit_ids=planning_unit_ids, - ) - self._validate_plan_participants( - employee_ids=employee_ids, - plan_ids=plan_ids, - planning_unit_ids=planning_unit_ids, - ) - self._validate_planning_unit_memberships( - employee_ids=employee_ids, - planning_unit_ids=planning_unit_ids, - ) - self._validate_assignments( - employee_ids=employee_ids, - planning_unit_ids=planning_unit_ids, - shift_ids=shift_ids, - ) - self._validate_availability( - employee_ids=employee_ids, - shift_ids=shift_ids, - ) - self._validate_demand_requirements( - planning_unit_ids=planning_unit_ids, - shift_ids=shift_ids, - ) - self._validate_sunday_work_history( - employee_ids=employee_ids, - ) - - return self - - def _unique_employee_ids(self) -> set[EmployeeId]: - employee_ids = [employee.employee_id for employee in self.employees] - self._ensure_unique(employee_ids, "employee_id") - return set(employee_ids) - - def _unique_planning_unit_ids(self) -> set[PlanningUnitId]: - planning_unit_ids = [unit.planning_unit_id for unit in self.planning_units] - self._ensure_unique(planning_unit_ids, "planning_unit_id") - return set(planning_unit_ids) - - def _unique_plan_ids(self) -> set[PlanId]: - plan_ids = [plan.plan_id for plan in self.plans] - self._ensure_unique(plan_ids, "plan_id") - return set(plan_ids) - - def _unique_shift_ids(self) -> set[ShiftId]: - shift_ids = [shift.shift_id for shift in self.shifts] - self._ensure_unique(shift_ids, "shift_id") - return set(shift_ids) - - def _validate_plans( - self, - *, - planning_unit_ids: set[PlanningUnitId], - ) -> None: - seen_planning_units: set[PlanningUnitId] = set() - - for plan in self.plans: - if plan.planning_unit_id not in planning_unit_ids: - raise ValueError(f"Plan references unknown planning_unit_id={plan.planning_unit_id}.") - - if plan.planning_unit_id in seen_planning_units: - raise ValueError( - f"Multiple selected plans reference the same planning_unit_id={plan.planning_unit_id}." - ) - - seen_planning_units.add(plan.planning_unit_id) - - def _validate_plan_participants( - self, - *, - employee_ids: set[EmployeeId], - plan_ids: set[PlanId], - planning_unit_ids: set[PlanningUnitId], - ) -> None: - plans_by_id = {plan.plan_id: plan for plan in self.plans} - seen: set[tuple[PlanId, EmployeeId]] = set() - - for participant in self.plan_participants: - if participant.plan_id not in plan_ids: - raise ValueError(f"PlanParticipant references unknown plan_id={participant.plan_id}.") - - if participant.planning_unit_id not in planning_unit_ids: - raise ValueError(f"PlanParticipant references unknown planning_unit_id={participant.planning_unit_id}.") - - if participant.employee_id not in employee_ids: - raise ValueError(f"PlanParticipant references unknown employee_id={participant.employee_id}.") - - plan = plans_by_id[participant.plan_id] - if participant.planning_unit_id != plan.planning_unit_id: - raise ValueError( - "PlanParticipant planning_unit_id does not match its Plan: " - f"plan_id={participant.plan_id} " - f"participant_planning_unit_id={participant.planning_unit_id} " - f"plan_planning_unit_id={plan.planning_unit_id}." - ) - - key = (participant.plan_id, participant.employee_id) - if key in seen: - raise ValueError( - f"Duplicate PlanParticipant plan_id={participant.plan_id} employee_id={participant.employee_id}." - ) - - seen.add(key) - - def _validate_planning_unit_memberships( - self, - *, - employee_ids: set[EmployeeId], - planning_unit_ids: set[PlanningUnitId], - ) -> None: - seen: set[tuple[PlanningUnitId, EmployeeId, Date, Date | None]] = set() - - for membership in self.planning_unit_memberships: - if membership.planning_unit_id not in planning_unit_ids: - raise ValueError( - f"PlanningUnitMembership references unknown planning_unit_id={membership.planning_unit_id}." - ) - - if membership.employee_id not in employee_ids: - raise ValueError(f"PlanningUnitMembership references unknown employee_id={membership.employee_id}.") - - key = ( - membership.planning_unit_id, - membership.employee_id, - membership.valid_from, - membership.valid_until, - ) - if key in seen: - raise ValueError( - "Duplicate PlanningUnitMembership " - f"planning_unit_id={membership.planning_unit_id} " - f"employee_id={membership.employee_id} " - f"valid_from={membership.valid_from} " - f"valid_until={membership.valid_until}." - ) - - seen.add(key) - - def _validate_assignments( - self, - *, - employee_ids: set[EmployeeId], - planning_unit_ids: set[PlanningUnitId], - shift_ids: set[ShiftId], - ) -> None: - seen: set[tuple[EmployeeId, Date, ShiftId, AssignmentType, PlanningUnitId | None]] = set() - - for assignment in self.assignments: - if not self.period.contains(assignment.date): - raise ValueError( - f"Assignment outside planning period: employee_id={assignment.employee_id} date={assignment.date}." - ) - - if assignment.employee_id not in employee_ids: - raise ValueError(f"Assignment references unknown employee_id={assignment.employee_id}.") - - if assignment.shift_id not in shift_ids: - raise ValueError(f"Assignment references unknown shift_id={assignment.shift_id}.") - - if assignment.assignment_type == AssignmentType.PLANNED: - if assignment.planning_unit_id is None: - raise ValueError("Planned assignment must reference planning_unit_id.") - - if assignment.planning_unit_id not in planning_unit_ids: - raise ValueError( - f"Planned assignment references unknown planning_unit_id={assignment.planning_unit_id}." - ) - - elif assignment.assignment_type == AssignmentType.EXTERNAL: - if assignment.planning_unit_id is not None: - raise ValueError("External assignment must not reference planning_unit_id.") - - key = ( - assignment.employee_id, - assignment.date, - assignment.shift_id, - assignment.assignment_type, - assignment.planning_unit_id, - ) - if key in seen: - raise ValueError( - f"Duplicate assignment employee_id={assignment.employee_id} " - f"date={assignment.date} shift_id={assignment.shift_id} " - f"type={assignment.assignment_type} " - f"planning_unit_id={assignment.planning_unit_id}." - ) - - seen.add(key) - - def _validate_availability( - self, - *, - employee_ids: set[EmployeeId], - shift_ids: set[ShiftId], - ) -> None: - seen: set[tuple[EmployeeId, Date, str, tuple[ShiftId, ...] | None]] = set() - - for availability in self.availability: - if not self.period.contains(availability.date): - raise ValueError( - "Availability outside planning period: " - f"employee_id={availability.employee_id} date={availability.date}." - ) - - if availability.employee_id not in employee_ids: - raise ValueError(f"Availability references unknown employee_id={availability.employee_id}.") - - if availability.shift_ids is not None: - unknown_shift_ids = sorted(set(availability.shift_ids) - shift_ids) - if unknown_shift_ids: - raise ValueError(f"Availability references unknown shift_ids={unknown_shift_ids}.") - - key = ( - availability.employee_id, - availability.date, - str(availability.availability_type), - availability.shift_ids, - ) - if key in seen: - raise ValueError( - f"Duplicate availability employee_id={availability.employee_id} " - f"date={availability.date} " - f"type={availability.availability_type} " - f"shift_ids={availability.shift_ids}." - ) - - seen.add(key) - - def _validate_demand_requirements( - self, - *, - planning_unit_ids: set[PlanningUnitId], - shift_ids: set[ShiftId], - ) -> None: - planning_unit_kind_by_id = {unit.planning_unit_id: unit.kind for unit in self.planning_units} - shift_by_id = {shift.shift_id: shift for shift in self.shifts} - - seen: set[tuple[PlanningUnitId, Date, ShiftId, StaffLevel]] = set() - - for demand in self.demand_requirements: - if not self.period.contains(demand.date): - raise ValueError( - "DemandRequirement outside planning period: " - f"planning_unit_id={demand.planning_unit_id} date={demand.date}." - ) - - if demand.planning_unit_id not in planning_unit_ids: - raise ValueError(f"DemandRequirement references unknown planning_unit_id={demand.planning_unit_id}.") - - if planning_unit_kind_by_id[demand.planning_unit_id] != PlanningUnitKind.STATION: - raise ValueError( - "DemandRequirement must target a station planning unit: " - f"planning_unit_id={demand.planning_unit_id}." - ) - - if demand.shift_id not in shift_ids: - raise ValueError(f"DemandRequirement references unknown shift_id={demand.shift_id}.") - - shift = shift_by_id[demand.shift_id] - if shift.staffing_role != StaffingDemandRole.REQUIRED_MINIMUM: - raise ValueError( - f"DemandRequirement must reference a REQUIRED_MINIMUM shift: shift_id={demand.shift_id}." - ) - - key = ( - demand.planning_unit_id, - demand.date, - demand.shift_id, - demand.staff_level, - ) - if key in seen: - raise ValueError( - "Duplicate DemandRequirement " - f"planning_unit_id={demand.planning_unit_id} " - f"date={demand.date} " - f"shift_id={demand.shift_id} " - f"staff_level={demand.staff_level}." - ) - - seen.add(key) - - def _validate_sunday_work_history( - self, - *, - employee_ids: set[EmployeeId], - ) -> None: - seen: set[EmployeeId] = set() - - for history in self.sunday_work_history: - if history.employee_id not in employee_ids: - raise ValueError(f"EmployeeSundayWorkHistory references unknown employee_id={history.employee_id}.") - - if history.employee_id in seen: - raise ValueError(f"Duplicate EmployeeSundayWorkHistory employee_id={history.employee_id}.") - - seen.add(history.employee_id) - - def _ensure_unique(self, values: Sequence[object], field_name: str) -> None: - seen: set[object] = set() - duplicates: set[object] = set() - - for value in values: - if value in seen: - duplicates.add(value) - - seen.add(value) - - if duplicates: - duplicate_values = ", ".join(sorted(str(value) for value in duplicates)) - raise ValueError(f"Duplicate {field_name} values: {duplicate_values}.") + monthly_work_accounts: tuple[MonthlyWorkAccount, ...] = () diff --git a/src/scheduling/models/demand.py b/src/scheduling/models/demand.py index a6dd92d2..37e3def2 100644 --- a/src/scheduling/models/demand.py +++ b/src/scheduling/models/demand.py @@ -2,10 +2,10 @@ from pydantic import Field -from src.scheduling.models.core import SchedulingBaseModel -from src.scheduling.models.employee import StaffLevel -from src.scheduling.models.planning_unit import PlanningUnitId -from src.scheduling.models.shift import ShiftId +from scheduling.models.core import SchedulingBaseModel +from scheduling.models.employee import StaffLevel +from scheduling.models.planning_unit import PlanningUnitId +from scheduling.models.shift import ShiftId class DemandRequirement(SchedulingBaseModel): diff --git a/src/scheduling/models/employee.py b/src/scheduling/models/employee.py index 7db36c9c..faa2b1ff 100644 --- a/src/scheduling/models/employee.py +++ b/src/scheduling/models/employee.py @@ -1,6 +1,6 @@ from enum import StrEnum -from src.scheduling.models.core import NonEmptyStr, PositiveId, SchedulingBaseModel +from scheduling.models.core import NonEmptyStr, PositiveId, SchedulingBaseModel EmployeeId = PositiveId diff --git a/src/scheduling/models/monthly_work_account.py b/src/scheduling/models/monthly_work_account.py new file mode 100644 index 00000000..acf000ba --- /dev/null +++ b/src/scheduling/models/monthly_work_account.py @@ -0,0 +1,10 @@ +from pydantic import NonNegativeInt + +from scheduling.models.core import SchedulingBaseModel +from scheduling.models.employee import EmployeeId + + +class MonthlyWorkAccount(SchedulingBaseModel): + employee_id: EmployeeId + target_minutes: NonNegativeInt + actual_minutes: NonNegativeInt | None = None diff --git a/src/scheduling/models/plan.py b/src/scheduling/models/plan.py index 0fffc447..813a1b62 100644 --- a/src/scheduling/models/plan.py +++ b/src/scheduling/models/plan.py @@ -1,6 +1,6 @@ -from src.scheduling.models.core import PositiveId, SchedulingBaseModel -from src.scheduling.models.employee import EmployeeId -from src.scheduling.models.planning_unit import PlanningUnitId +from scheduling.models.core import PositiveId, SchedulingBaseModel +from scheduling.models.employee import EmployeeId +from scheduling.models.planning_unit import PlanningUnitId PlanId = PositiveId diff --git a/src/scheduling/models/planning_unit.py b/src/scheduling/models/planning_unit.py index f2f5d38f..a222f734 100644 --- a/src/scheduling/models/planning_unit.py +++ b/src/scheduling/models/planning_unit.py @@ -4,8 +4,8 @@ from pydantic import model_validator -from src.scheduling.models.core import NonEmptyStr, PositiveId, SchedulingBaseModel -from src.scheduling.models.employee import EmployeeId, StaffLevel +from scheduling.models.core import NonEmptyStr, PositiveId, SchedulingBaseModel +from scheduling.models.employee import EmployeeId, StaffLevel PlanningUnitId = PositiveId diff --git a/src/scheduling/models/shift.py b/src/scheduling/models/shift.py index 16a6684c..e71b7cad 100644 --- a/src/scheduling/models/shift.py +++ b/src/scheduling/models/shift.py @@ -3,7 +3,7 @@ from pydantic import Field, model_validator -from src.scheduling.models.core import ( +from scheduling.models.core import ( MinuteOfDay, NonEmptyStr, PositiveId, diff --git a/src/scheduling/models/sunday_work_history.py b/src/scheduling/models/sunday_work_history.py index 5de7b8d9..5790276a 100644 --- a/src/scheduling/models/sunday_work_history.py +++ b/src/scheduling/models/sunday_work_history.py @@ -1,7 +1,7 @@ from pydantic import Field -from src.scheduling.models.core import SchedulingBaseModel -from src.scheduling.models.employee import EmployeeId +from scheduling.models.core import SchedulingBaseModel +from scheduling.models.employee import EmployeeId class EmployeeSundayWorkHistory(SchedulingBaseModel): diff --git a/src/scheduling/models/wish.py b/src/scheduling/models/wish.py new file mode 100644 index 00000000..58769cd1 --- /dev/null +++ b/src/scheduling/models/wish.py @@ -0,0 +1,34 @@ +from datetime import date as Date +from enum import StrEnum +from typing import Self + +from pydantic import model_validator + +from scheduling.models.core import SchedulingBaseModel +from scheduling.models.employee import EmployeeId +from scheduling.models.planning_unit import PlanningUnitId +from scheduling.models.shift import ShiftId + + +class WishKind(StrEnum): + SHIFT = "shift" + FREE_DAY = "free_day" + + +class Wish(SchedulingBaseModel): + employee_id: EmployeeId + planning_unit_id: PlanningUnitId + + date: Date + kind: WishKind + shift_id: ShiftId | None = None + + @model_validator(mode="after") + def validate_wish(self) -> Self: + if self.kind == WishKind.SHIFT and self.shift_id is None: + raise ValueError("SHIFT wish requires shift_id.") + + if self.kind == WishKind.FREE_DAY and self.shift_id is not None: + raise ValueError("FREE_DAY wish must not define shift_id.") + + return self diff --git a/src/__init__.py b/src/scheduling/solver/__init__.py similarity index 100% rename from src/__init__.py rename to src/scheduling/solver/__init__.py diff --git a/src/scheduling/solver/tmp.py b/src/scheduling/solver/tmp.py new file mode 100644 index 00000000..75c6083d --- /dev/null +++ b/src/scheduling/solver/tmp.py @@ -0,0 +1,24 @@ +import asyncio +import logging +import uuid +from dataclasses import dataclass + +logger = logging.getLogger(__name__) + + +@dataclass +class FakeSolution: + id: uuid.UUID + value: str + + +class SolverService: + solutions: list[FakeSolution] = [] + + async def fake_solve(self, id: uuid.UUID) -> None: + logger.info("Started processing") + await asyncio.sleep(5) + logger.info("Finished processing") + + solution = FakeSolution(id=id, value=f"{id} processed!") + self.solutions.append(solution) diff --git a/src/scheduling/timeoffice/database.py b/src/scheduling/timeoffice/database.py index 5702453c..bb39bfa7 100644 --- a/src/scheduling/timeoffice/database.py +++ b/src/scheduling/timeoffice/database.py @@ -2,10 +2,11 @@ from sqlalchemy import URL, Engine, create_engine -from src.scheduling.models import PlanningPeriod, SchedulingDataset -from src.scheduling.settings import Settings -from src.scheduling.timeoffice.facts import TimeOfficeFacts -from src.scheduling.timeoffice.repositories import TimeOfficeRepositories +from scheduling.models import PlanningPeriod +from scheduling.settings import Settings +from scheduling.timeoffice.facts import TimeOfficeFacts +from scheduling.timeoffice.repositories import TimeOfficeRepositories +from scheduling.validation.dataset import ValidatedSchedulingDataset logger = logging.getLogger(__name__) @@ -50,7 +51,7 @@ def fetch_dataset( *, selected_planning_unit_ids: tuple[int, ...], period: PlanningPeriod, - ) -> SchedulingDataset: + ) -> ValidatedSchedulingDataset: if not selected_planning_unit_ids: raise ValueError("At least one planning unit must be selected.") @@ -96,7 +97,21 @@ def fetch_dataset( employees=personnel_result.employees, ) - return SchedulingDataset( + wish_result = self._repositories.wishes.fetch( + connection=connection, + plans=planning_unit_result.plans, + employees=personnel_result.employees, + shifts=shift_result.shifts, + period=period, + ) + + monthly_work_account_result = self._repositories.monthly_work_accounts.fetch( + connection=connection, + employees=personnel_result.employees, + period=period, + ) + + return ValidatedSchedulingDataset( period=period, planning_units=planning_unit_result.planning_units, plans=planning_unit_result.plans, @@ -108,4 +123,6 @@ def fetch_dataset( availability=roster_result.availability, demand_requirements=demand_result.demand_requirements, sunday_work_history=sunday_work_history_result.sunday_work_history, + wishes=wish_result.wishes, + monthly_work_accounts=monthly_work_account_result.monthly_work_accounts, ) diff --git a/src/scheduling/timeoffice/facts.py b/src/scheduling/timeoffice/facts.py index ac6bdc15..090190c6 100644 --- a/src/scheduling/timeoffice/facts.py +++ b/src/scheduling/timeoffice/facts.py @@ -3,10 +3,11 @@ from enum import IntEnum from types import MappingProxyType -from src.scheduling.models.availability import AvailabilityType -from src.scheduling.models.employee import Capability, StaffLevel -from src.scheduling.models.planning_unit import PlanningUnitId, PlanningUnitKind -from src.scheduling.models.shift import ShiftId, ShiftKind, StaffingDemandRole +from scheduling.models.availability import AvailabilityType +from scheduling.models.employee import Capability, StaffLevel +from scheduling.models.planning_unit import PlanningUnitId, PlanningUnitKind +from scheduling.models.shift import ShiftId, ShiftKind, StaffingDemandRole +from scheduling.models.wish import WishKind class TimeOfficePlanStatusId(IntEnum): @@ -53,13 +54,6 @@ class TimeOfficeShiftFact: staffing_role: StaffingDemandRole -@dataclass(frozen=True, slots=True) -class TimeOfficeAvailabilityFact: - source_shift_id: int - expected_code: str - availability_type: AvailabilityType - - IsoWeekday = int # Monday=1 ... Sunday=7 @@ -94,16 +88,20 @@ class TimeOfficeFacts: planning_unit_kind_map: dict[int, PlanningUnitKind] - real_work_shift_type_ids: tuple[int, ...] - shift_facts: tuple[TimeOfficeShiftFact, ...] + work_shift_type_ids: tuple[int, ...] + shift_facts_by_id: Mapping[int, TimeOfficeShiftFact] + + staff_level_by_profession_id_map: dict[int, StaffLevel] + demand_facts: tuple[TimeOfficeDemandFact, ...] - profession_staff_level_map: dict[int, StaffLevel] # Temporary project/problem assumptions. Not DB-backed. - employee_capabilities_map: dict[int, tuple[Capability, ...]] + capabilities_by_employee_id_map: dict[int, tuple[Capability, ...]] - availability_facts: tuple[TimeOfficeAvailabilityFact, ...] + availability_type_by_absence_code: Mapping[str, AvailabilityType] + wish_kind_by_absence_code: Mapping[str, WishKind] - demand_facts: tuple[TimeOfficeDemandFact, ...] + monthly_target_work_account_id: int + monthly_actual_work_account_id: int TIMEOFFICE_FACTS = TimeOfficeFacts( @@ -118,64 +116,64 @@ class TimeOfficeFacts: 337: PlanningUnitKind.STATION, 408: PlanningUnitKind.SHARED_POOL, }, - real_work_shift_type_ids=(1,), - shift_facts=( - TimeOfficeShiftFact( + work_shift_type_ids=(1,), + shift_facts_by_id={ + EARLY_F2_SHIFT_ID: TimeOfficeShiftFact( source_shift_id=EARLY_F2_SHIFT_ID, expected_code="F2_", kind=ShiftKind.EARLY, staffing_role=StaffingDemandRole.REQUIRED_MINIMUM, ), - TimeOfficeShiftFact( + LATE_S2_SHIFT_ID: TimeOfficeShiftFact( source_shift_id=LATE_S2_SHIFT_ID, expected_code="S2_", kind=ShiftKind.LATE, staffing_role=StaffingDemandRole.REQUIRED_MINIMUM, ), - TimeOfficeShiftFact( + NIGHT_N2_SHIFT_ID: TimeOfficeShiftFact( source_shift_id=NIGHT_N2_SHIFT_ID, expected_code="N2_", kind=ShiftKind.NIGHT, staffing_role=StaffingDemandRole.REQUIRED_MINIMUM, ), - TimeOfficeShiftFact( + INTERMEDIATE_T75_SHIFT_ID: TimeOfficeShiftFact( source_shift_id=INTERMEDIATE_T75_SHIFT_ID, expected_code="T75_", kind=ShiftKind.INTERMEDIATE, staffing_role=StaffingDemandRole.OPTIONAL_COVERAGE, ), - TimeOfficeShiftFact( + MANAGEMENT_Z60_SHIFT_ID: TimeOfficeShiftFact( source_shift_id=MANAGEMENT_Z60_SHIFT_ID, expected_code="Z60", kind=ShiftKind.MANAGEMENT, staffing_role=StaffingDemandRole.NON_MINIMUM_WORK, ), - TimeOfficeShiftFact( + NIGHT_N5_SHIFT_ID: TimeOfficeShiftFact( source_shift_id=NIGHT_N5_SHIFT_ID, expected_code="N5", kind=ShiftKind.NIGHT, staffing_role=StaffingDemandRole.NON_MINIMUM_WORK, ), - TimeOfficeShiftFact( + NIGHT_N15_SHIFT_ID: TimeOfficeShiftFact( source_shift_id=NIGHT_N15_SHIFT_ID, expected_code="N15", kind=ShiftKind.NIGHT, staffing_role=StaffingDemandRole.NON_MINIMUM_WORK, ), - TimeOfficeShiftFact( + OTHER_T8X_SHIFT_ID: TimeOfficeShiftFact( source_shift_id=OTHER_T8X_SHIFT_ID, expected_code="T8x", kind=ShiftKind.OTHER, staffing_role=StaffingDemandRole.NON_MINIMUM_WORK, ), - TimeOfficeShiftFact( + OTHER_Z52_SHIFT_ID: TimeOfficeShiftFact( source_shift_id=OTHER_Z52_SHIFT_ID, expected_code="Z52", kind=ShiftKind.OTHER, staffing_role=StaffingDemandRole.NON_MINIMUM_WORK, ), - ), - profession_staff_level_map={ + }, + staff_level_by_profession_id_map={ # Fachkraft 803: StaffLevel.PROFESSIONAL, # Gesundheits- und Krankenpfleger/in 110: StaffLevel.PROFESSIONAL, # Pflegefachkraft (Krankenpflege) @@ -195,61 +193,37 @@ class TimeOfficeFacts: 837: StaffLevel.TRAINEE, # A-Pflegefachkraft (Krankenpflege) 1478: StaffLevel.TRAINEE, # A-Pflegefachkraft (Altenpflege) }, - employee_capabilities_map={ - # Problem/legacy assumption: FWB employees for weekday early rounds. + capabilities_by_employee_id_map={ # Not DB-backed yet. + # Problem/legacy assumption: FWB employees for weekday early rounds. 791: (Capability.ROUNDS,), # Branz, Janett 2963: (Capability.ROUNDS,), # Hoots, Renilde 3868: (Capability.ROUNDS,), # Vanfleet, Eike # Problem assumption: night-watch employees. - # TPersonalVertraege.IstReineNachtwache did not confirm this, so keep it - # explicitly marked as temporary/problem-derived. 925: (Capability.NIGHT_WATCH,), # Farniok, Lina 6681: (Capability.NIGHT_WATCH,), # Labelle, Saskia 928: (Capability.NIGHT_WATCH,), # Wunderlich, Daniele }, - availability_facts=( - TimeOfficeAvailabilityFact( - source_shift_id=2434, - expected_code="U", - availability_type=AvailabilityType.VACATION, - ), - TimeOfficeAvailabilityFact( - source_shift_id=2091, - expected_code="ZU", - availability_type=AvailabilityType.VACATION, - ), - TimeOfficeAvailabilityFact( - source_shift_id=1089, - expected_code="FR", - availability_type=AvailabilityType.FREE_DAY, - ), - TimeOfficeAvailabilityFact( - source_shift_id=26, - expected_code="SC", - availability_type=AvailabilityType.TRAINING, - ), - TimeOfficeAvailabilityFact( - source_shift_id=739, - expected_code="FI", - availability_type=AvailabilityType.TRAINING, - ), - TimeOfficeAvailabilityFact( - source_shift_id=1078, - expected_code="EZ", - availability_type=AvailabilityType.UNAVAILABLE, - ), - TimeOfficeAvailabilityFact( - source_shift_id=1086, - expected_code="RE", - availability_type=AvailabilityType.UNAVAILABLE, - ), - TimeOfficeAvailabilityFact( - source_shift_id=1092, - expected_code="AZV", - availability_type=AvailabilityType.FREE_DAY, - ), + availability_type_by_absence_code=MappingProxyType( + { + "U": AvailabilityType.VACATION, + "ZU": AvailabilityType.VACATION, + "FR": AvailabilityType.FREE_DAY, + "AZV": AvailabilityType.FREE_DAY, + # Conservative hard blockers until TimeOffice/domain semantics are confirmed. + "SC": AvailabilityType.UNAVAILABLE, + "EZ": AvailabilityType.UNAVAILABLE, + "RE": AvailabilityType.UNAVAILABLE, + "FI": AvailabilityType.UNAVAILABLE, + } + ), + wish_kind_by_absence_code=MappingProxyType( + { + "FR": WishKind.FREE_DAY, + } ), + monthly_target_work_account_id=1, + monthly_actual_work_account_id=55, demand_facts=( # Fachkraft TimeOfficeDemandFact( diff --git a/src/scheduling/timeoffice/repositories/__init__.py b/src/scheduling/timeoffice/repositories/__init__.py index ba3fa426..5d0ff5b9 100644 --- a/src/scheduling/timeoffice/repositories/__init__.py +++ b/src/scheduling/timeoffice/repositories/__init__.py @@ -1,13 +1,13 @@ -from src.scheduling.timeoffice.repositories.container import TimeOfficeRepositories -from src.scheduling.timeoffice.repositories.demand import DemandRepositoryResult, TimeOfficeDemandRepository -from src.scheduling.timeoffice.repositories.personnel import PersonnelRepositoryResult, TimeOfficePersonnelRepository -from src.scheduling.timeoffice.repositories.planning_units import ( +from scheduling.timeoffice.repositories.container import TimeOfficeRepositories +from scheduling.timeoffice.repositories.demand import DemandRepositoryResult, TimeOfficeDemandRepository +from scheduling.timeoffice.repositories.personnel import PersonnelRepositoryResult, TimeOfficePersonnelRepository +from scheduling.timeoffice.repositories.planning_units import ( PlanningUnitRepositoryResult, TimeOfficePlanningUnitRepository, ) -from src.scheduling.timeoffice.repositories.roster import RosterRepositoryResult, TimeOfficeRosterRepository -from src.scheduling.timeoffice.repositories.shifts import ShiftRepositoryResult, TimeOfficeShiftRepository -from src.scheduling.timeoffice.repositories.sunday_work_history import ( +from scheduling.timeoffice.repositories.roster import RosterRepositoryResult, TimeOfficeRosterRepository +from scheduling.timeoffice.repositories.shifts import ShiftRepositoryResult, TimeOfficeShiftRepository +from scheduling.timeoffice.repositories.sunday_work_history import ( SundayWorkHistoryRepositoryResult, TimeOfficeSundayWorkHistoryRepository, ) diff --git a/src/scheduling/timeoffice/repositories/container.py b/src/scheduling/timeoffice/repositories/container.py index c0ce52d0..f768698d 100644 --- a/src/scheduling/timeoffice/repositories/container.py +++ b/src/scheduling/timeoffice/repositories/container.py @@ -1,12 +1,14 @@ from dataclasses import dataclass -from src.scheduling.timeoffice.facts import TimeOfficeFacts -from src.scheduling.timeoffice.repositories.demand import TimeOfficeDemandRepository -from src.scheduling.timeoffice.repositories.personnel import TimeOfficePersonnelRepository -from src.scheduling.timeoffice.repositories.planning_units import TimeOfficePlanningUnitRepository -from src.scheduling.timeoffice.repositories.roster import TimeOfficeRosterRepository -from src.scheduling.timeoffice.repositories.shifts import TimeOfficeShiftRepository -from src.scheduling.timeoffice.repositories.sunday_work_history import TimeOfficeSundayWorkHistoryRepository +from scheduling.timeoffice.facts import TimeOfficeFacts +from scheduling.timeoffice.repositories.demand import TimeOfficeDemandRepository +from scheduling.timeoffice.repositories.monthly_work_accounts import TimeOfficeMonthlyWorkAccountRepository +from scheduling.timeoffice.repositories.personnel import TimeOfficePersonnelRepository +from scheduling.timeoffice.repositories.planning_units import TimeOfficePlanningUnitRepository +from scheduling.timeoffice.repositories.roster import TimeOfficeRosterRepository +from scheduling.timeoffice.repositories.shifts import TimeOfficeShiftRepository +from scheduling.timeoffice.repositories.sunday_work_history import TimeOfficeSundayWorkHistoryRepository +from scheduling.timeoffice.repositories.wishes import TimeOfficeWishRepository @dataclass(frozen=True, slots=True) @@ -17,6 +19,8 @@ class TimeOfficeRepositories: roster: TimeOfficeRosterRepository demand: TimeOfficeDemandRepository sunday_work_history: TimeOfficeSundayWorkHistoryRepository + wishes: TimeOfficeWishRepository + monthly_work_accounts: TimeOfficeMonthlyWorkAccountRepository @classmethod def create(cls, *, facts: TimeOfficeFacts) -> "TimeOfficeRepositories": @@ -27,4 +31,6 @@ def create(cls, *, facts: TimeOfficeFacts) -> "TimeOfficeRepositories": roster=TimeOfficeRosterRepository(facts=facts), demand=TimeOfficeDemandRepository(facts=facts), sunday_work_history=TimeOfficeSundayWorkHistoryRepository(), + wishes=TimeOfficeWishRepository(facts=facts), + monthly_work_accounts=TimeOfficeMonthlyWorkAccountRepository(facts=facts), ) diff --git a/src/scheduling/timeoffice/repositories/demand.py b/src/scheduling/timeoffice/repositories/demand.py index c0bf10a7..2cfbb3e1 100644 --- a/src/scheduling/timeoffice/repositories/demand.py +++ b/src/scheduling/timeoffice/repositories/demand.py @@ -2,7 +2,7 @@ from sqlalchemy import Connection -from src.scheduling.models import ( +from scheduling.models import ( DemandRequirement, PlanningPeriod, PlanningUnit, @@ -11,8 +11,8 @@ Shift, StaffingDemandRole, ) -from src.scheduling.models.employee import StaffLevel -from src.scheduling.timeoffice.facts import TimeOfficeDemandFact, TimeOfficeFacts +from scheduling.models.employee import StaffLevel +from scheduling.timeoffice.facts import TimeOfficeDemandFact, TimeOfficeFacts class DemandRepositoryResult(SchedulingBaseModel): diff --git a/src/scheduling/timeoffice/repositories/helpers.py b/src/scheduling/timeoffice/repositories/helpers.py deleted file mode 100644 index fad07338..00000000 --- a/src/scheduling/timeoffice/repositories/helpers.py +++ /dev/null @@ -1,76 +0,0 @@ -from datetime import date as Date -from datetime import datetime as DateTime -from datetime import time as Time -from typing import Any - - -def required[T](value: T | None, *, field_name: str, context: str) -> T: - """Return a required TimeOffice value or fail with source context.""" - if value is None: - raise ValueError(f"Missing required TimeOffice field {field_name} for {context}.") - - return value - - -def clean_text(value: Any) -> str | None: - """Normalize empty source text values to None.""" - if value is None: - return None - - cleaned = str(value).strip() - - if not cleaned: - return None - - return cleaned - - -def normalize_code(value: str) -> str: - """Normalize TimeOffice code values for stable comparison.""" - return value.strip().upper() - - -def to_date(value: Any) -> Date | None: - """Convert SQL date/datetime values to date values.""" - if value is None: - return None - - if isinstance(value, Date) and not isinstance(value, DateTime): - return value - - if isinstance(value, DateTime): - return value.date() - - if hasattr(value, "date"): - return value.date() - - raise TypeError(f"Cannot convert TimeOffice value to date: {value!r}") - - -def to_datetime(value: Any) -> DateTime: - """Convert SQL datetime-like values to Python datetime.""" - if isinstance(value, DateTime): - return value - - if hasattr(value, "to_pydatetime"): - return value.to_pydatetime() - - raise TypeError(f"Cannot convert TimeOffice value to datetime: {value!r}") - - -def minute_of_day(value: DateTime | Time) -> int: - """Return minutes after midnight.""" - return value.hour * 60 + value.minute - - -def to_non_negative_int(value: Any) -> int: - """Convert nullable numeric source values to non-negative int.""" - if value is None: - return 0 - - result = int(value) - - if result < 0: - raise ValueError(f"Expected non-negative TimeOffice integer, got {result}.") - - return result diff --git a/src/scheduling/timeoffice/repositories/monthly_work_accounts.py b/src/scheduling/timeoffice/repositories/monthly_work_accounts.py new file mode 100644 index 00000000..d9c1a4db --- /dev/null +++ b/src/scheduling/timeoffice/repositories/monthly_work_accounts.py @@ -0,0 +1,189 @@ +from typing import Self + +from pydantic import model_validator +from sqlalchemy import Connection, bindparam, text + +from scheduling.models import Employee, MonthlyWorkAccount, PlanningPeriod, SchedulingBaseModel +from scheduling.timeoffice.facts import TimeOfficeFacts +from scheduling.timeoffice.repositories.types import ( + CleanNullableText, + SourceInt, + SourceNullableInt, + TimeOfficeSourceRow, +) + + +class _TimeOfficeMonthlyWorkAccountRow(TimeOfficeSourceRow): + employee_id: SourceInt + month: SourceInt + + target_account_id: SourceInt + target_account_code: CleanNullableText = None + target_account_name: CleanNullableText = None + target_account_short_name: CleanNullableText = None + target_hours: float | None = None + + actual_account_id: SourceNullableInt = None + actual_account_code: CleanNullableText = None + actual_account_name: CleanNullableText = None + actual_account_short_name: CleanNullableText = None + actual_hours: float | None = None + + @model_validator(mode="after") + def validate_actual_account_shape(self) -> Self: + if self.actual_account_id is None and self.actual_hours is not None: + raise ValueError( + "Monthly actual hours are present without an actual account id: " + f"employee_id={self.employee_id} " + f"month={self.month} " + f"actual_hours={self.actual_hours!r}." + ) + + return self + + +class TimeOfficeMonthlyWorkAccountRepositoryResult(SchedulingBaseModel): + monthly_work_accounts: tuple[MonthlyWorkAccount, ...] + + +class TimeOfficeMonthlyWorkAccountRepository: + """Reads monthly target/actual work account values from TimeOffice. + + Source: + - TPersonalKontenJeMonat.Monat is YYYYMM + - RefKonten=1 / SOLL_MONAT provides target monthly hours in Wert2 + - RefKonten=55 / TOTAL provides actual monthly hours in Wert2 + + Rows with target_minutes <= 0 are intentionally not emitted because an + all-zero target would be misleading for scheduling. + """ + + def __init__(self, *, facts: TimeOfficeFacts) -> None: + self._facts = facts + + def fetch( + self, + *, + connection: Connection, + employees: tuple[Employee, ...], + period: PlanningPeriod, + ) -> TimeOfficeMonthlyWorkAccountRepositoryResult: + if not employees: + return TimeOfficeMonthlyWorkAccountRepositoryResult(monthly_work_accounts=()) + + rows = self._fetch_rows( + connection=connection, + employees=employees, + month=self._month(period), + ) + + self._validate_unique_employee_rows(rows) + + return TimeOfficeMonthlyWorkAccountRepositoryResult(monthly_work_accounts=self._map_accounts(rows)) + + def _fetch_rows( + self, + *, + connection: Connection, + employees: tuple[Employee, ...], + month: int, + ) -> tuple[_TimeOfficeMonthlyWorkAccountRow, ...]: + query = text( + """ + SELECT + target.RefPersonal AS employee_id, + target.Monat AS month, + + target.RefKonten AS target_account_id, + target_account.BezProg AS target_account_code, + target_account.Bez AS target_account_name, + target_account.BezKurz AS target_account_short_name, + target.Wert2 AS target_hours, + + actual.RefKonten AS actual_account_id, + actual_account.BezProg AS actual_account_code, + actual_account.Bez AS actual_account_name, + actual_account.BezKurz AS actual_account_short_name, + actual.Wert2 AS actual_hours + FROM TPersonalKontenJeMonat target + JOIN TKonten target_account + ON target_account.Prim = target.RefKonten + LEFT JOIN TPersonalKontenJeMonat actual + ON actual.RefPersonal = target.RefPersonal + AND actual.Monat = target.Monat + AND actual.RefKonten = :actual_account_id + LEFT JOIN TKonten actual_account + ON actual_account.Prim = actual.RefKonten + WHERE target.RefPersonal IN :employee_ids + AND target.Monat = :month + AND target.RefKonten = :target_account_id + ORDER BY + target.RefPersonal + """ + ).bindparams(bindparam("employee_ids", expanding=True)) + + raw_rows = ( + connection.execute( + query, + { + "employee_ids": tuple(employee.employee_id for employee in employees), + "month": month, + "target_account_id": self._facts.monthly_target_work_account_id, + "actual_account_id": self._facts.monthly_actual_work_account_id, + }, + ) + .mappings() + .all() + ) + + return tuple(_TimeOfficeMonthlyWorkAccountRow.model_validate(row) for row in raw_rows) + + def _validate_unique_employee_rows(self, rows: tuple[_TimeOfficeMonthlyWorkAccountRow, ...]) -> None: + seen_employee_ids: set[int] = set() + + for row in rows: + if row.employee_id in seen_employee_ids: + raise ValueError( + "Duplicate TimeOffice monthly work target account row for " + f"employee_id={row.employee_id} month={row.month!r}." + ) + + seen_employee_ids.add(row.employee_id) + + def _map_accounts(self, rows: tuple[_TimeOfficeMonthlyWorkAccountRow, ...]) -> tuple[MonthlyWorkAccount, ...]: + accounts: list[MonthlyWorkAccount] = [] + + for row in rows: + target_minutes = self._hours_to_minutes(row.target_hours) + + if target_minutes <= 0: + continue + + accounts.append( + MonthlyWorkAccount( + employee_id=row.employee_id, + target_minutes=target_minutes, + actual_minutes=self._optional_hours_to_minutes(row.actual_hours), + ) + ) + + return tuple(sorted(accounts, key=lambda account: account.employee_id)) + + def _month(self, period: PlanningPeriod) -> int: + if period.start.year != period.end.year or period.start.month != period.end.month: + raise ValueError( + "Monthly work accounts can only be imported for a single calendar month. " + f"Got period {period.start}..{period.end}." + ) + + return period.start.year * 100 + period.start.month + + def _hours_to_minutes(self, value: float | None) -> int: + if value is None: + return 0 + + return round(value * 60) + + def _optional_hours_to_minutes(self, value: float | None) -> int | None: + minutes = self._hours_to_minutes(value) + return minutes if minutes > 0 else None diff --git a/src/scheduling/timeoffice/repositories/personnel.py b/src/scheduling/timeoffice/repositories/personnel.py index 3391ecb5..a3dd7a08 100644 --- a/src/scheduling/timeoffice/repositories/personnel.py +++ b/src/scheduling/timeoffice/repositories/personnel.py @@ -1,7 +1,10 @@ +from datetime import datetime +from typing import Self + +from pydantic import model_validator from sqlalchemy import Connection, bindparam, text -from sqlalchemy.engine import RowMapping -from src.scheduling.models import ( +from scheduling.models import ( Capability, Employee, Plan, @@ -11,8 +14,40 @@ SchedulingBaseModel, StaffLevel, ) -from src.scheduling.timeoffice.facts import TimeOfficeFacts -from src.scheduling.timeoffice.repositories.helpers import clean_text, required, to_datetime +from scheduling.timeoffice.facts import TimeOfficeFacts +from scheduling.timeoffice.repositories.types import CleanNullableText, SourceInt, TimeOfficeSourceRow + + +class _TimeOfficePlanPersonnelRow(TimeOfficeSourceRow): + plan_id: SourceInt + planning_unit_id: SourceInt + employee_id: SourceInt + employee_profession_id: SourceInt + first_name: CleanNullableText = None + last_name: CleanNullableText = None + + +class _TimeOfficePlanningUnitMembershipRow(TimeOfficeSourceRow): + planning_unit_id: SourceInt + employee_id: SourceInt + membership_profession_id: SourceInt + valid_from: datetime + valid_until: datetime | None = None + is_home: bool + is_replacement: bool + + @model_validator(mode="after") + def validate_interval(self) -> Self: + if self.valid_until is not None and self.valid_until < self.valid_from: + raise ValueError( + "Invalid TimeOffice planning-unit membership interval: " + f"planning_unit_id={self.planning_unit_id} " + f"employee_id={self.employee_id} " + f"valid_from={self.valid_from!r} " + f"valid_until={self.valid_until!r}." + ) + + return self class PersonnelRepositoryResult(SchedulingBaseModel): @@ -47,22 +82,21 @@ def fetch( plans=plans, ) - employees = self._map_employees(plan_personnel_rows) - plan_participants = tuple(self._map_plan_participant(row) for row in plan_personnel_rows) + employees = self._deduplicate_employees(tuple(self._map_employee(row) for row in plan_personnel_rows)) - employee_ids = tuple(employee.employee_id for employee in employees) + plan_participants = self._map_plan_participants(plan_personnel_rows) - memberships = self._fetch_memberships( + membership_rows = self._fetch_membership_rows( connection=connection, planning_unit_ids=planning_unit_ids, - employee_ids=employee_ids, + employee_ids=tuple(employee.employee_id for employee in employees), period=period, ) return PersonnelRepositoryResult( employees=employees, plan_participants=plan_participants, - planning_unit_memberships=memberships, + planning_unit_memberships=self._map_memberships(membership_rows), ) def _fetch_plan_personnel_rows( @@ -70,47 +104,8 @@ def _fetch_plan_personnel_rows( *, connection: Connection, plans: tuple[Plan, ...], - ) -> tuple[RowMapping, ...]: - plan_ids = tuple(plan.plan_id for plan in plans) - - return tuple( - connection.execute( - self._plan_personnel_query(), - {"plan_ids": plan_ids}, - ) - .mappings() - .all() - ) - - def _fetch_memberships( - self, - *, - connection: Connection, - planning_unit_ids: tuple[int, ...], - employee_ids: tuple[int, ...], - period: PlanningPeriod, - ) -> tuple[PlanningUnitMembership, ...]: - if not planning_unit_ids or not employee_ids: - return () - - rows = tuple( - connection.execute( - self._membership_query(), - { - "planning_unit_ids": planning_unit_ids, - "employee_ids": employee_ids, - "period_start": period.start, - "period_end": period.end, - }, - ) - .mappings() - .all() - ) - - return tuple(self._map_membership(row) for row in rows) - - def _plan_personnel_query(self): - return text( + ) -> tuple[_TimeOfficePlanPersonnelRow, ...]: + query = text( """ SELECT DISTINCT pp.RefPlan AS plan_id, @@ -133,8 +128,29 @@ def _plan_personnel_query(self): """ ).bindparams(bindparam("plan_ids", expanding=True)) - def _membership_query(self): - return text( + raw_rows = ( + connection.execute( + query, + {"plan_ids": tuple(plan.plan_id for plan in plans)}, + ) + .mappings() + .all() + ) + + return tuple(_TimeOfficePlanPersonnelRow.model_validate(row) for row in raw_rows) + + def _fetch_membership_rows( + self, + *, + connection: Connection, + planning_unit_ids: tuple[int, ...], + employee_ids: tuple[int, ...], + period: PlanningPeriod, + ) -> tuple[_TimeOfficePlanningUnitMembershipRow, ...]: + if not planning_unit_ids or not employee_ids: + return () + + query = text( """ SELECT DISTINCT pep.RefPlanungseinheiten AS planning_unit_id, @@ -164,56 +180,54 @@ def _membership_query(self): bindparam("employee_ids", expanding=True), ) - def _map_employees(self, rows: tuple[RowMapping, ...]) -> tuple[Employee, ...]: - employees_by_id: dict[int, Employee] = {} - - for row in rows: - employee_id = int( - required( - row["employee_id"], - field_name="employee_id", - context="TPlanPersonal", - ) + raw_rows = ( + connection.execute( + query, + { + "planning_unit_ids": planning_unit_ids, + "employee_ids": employee_ids, + "period_start": period.start, + "period_end": period.end, + }, ) + .mappings() + .all() + ) - employee_profession_id = int( - required( - row["employee_profession_id"], - field_name="employee_profession_id", - context=f"TPersonal employee_id={employee_id}", - ) - ) + return tuple(_TimeOfficePlanningUnitMembershipRow.model_validate(row) for row in raw_rows) - employee = Employee( - employee_id=employee_id, - display_name=self._display_name( - employee_id=employee_id, - first_name=row["first_name"], - last_name=row["last_name"], - ), - staff_level=self._staff_level_from_profession( - employee_profession_id, - context=f"TPersonal employee_id={employee_id}", - ), - capabilities=self._capabilities_for_employee(employee_id), - ) + def _map_employee(self, row: _TimeOfficePlanPersonnelRow) -> Employee: + return Employee( + employee_id=row.employee_id, + display_name=self._display_name( + employee_id=row.employee_id, + first_name=row.first_name, + last_name=row.last_name, + ), + staff_level=self._staff_level_from_profession( + row.employee_profession_id, + context=f"TPersonal employee_id={row.employee_id}", + ), + capabilities=self._capabilities_for_employee(row.employee_id), + ) + + def _deduplicate_employees(self, employees: tuple[Employee, ...]) -> tuple[Employee, ...]: + employees_by_id: dict[int, Employee] = {} + + for employee in employees: + existing = employees_by_id.get(employee.employee_id) - existing = employees_by_id.get(employee_id) if existing is not None: - if ( - existing.display_name != employee.display_name - or existing.staff_level != employee.staff_level - or existing.capabilities != employee.capabilities - ): + if existing != employee: raise ValueError( "Conflicting duplicate employee rows from TPlanPersonal/TPersonal: " - f"employee_id={employee_id} " + f"employee_id={employee.employee_id} " f"existing={existing!r} new={employee!r}." ) continue - employees_by_id[employee_id] = employee + employees_by_id[employee.employee_id] = employee return tuple( sorted( @@ -222,95 +236,42 @@ def _map_employees(self, rows: tuple[RowMapping, ...]) -> tuple[Employee, ...]: ) ) - def _map_plan_participant(self, row: RowMapping) -> PlanParticipant: - return PlanParticipant( - plan_id=int( - required( - row["plan_id"], - field_name="plan_id", - context="TPlanPersonal", - ) - ), - planning_unit_id=int( - required( - row["planning_unit_id"], - field_name="planning_unit_id", - context="TPlanPersonal", - ) - ), - employee_id=int( - required( - row["employee_id"], - field_name="employee_id", - context="TPlanPersonal", - ) - ), - ) - - def _map_membership(self, row: RowMapping) -> PlanningUnitMembership: - planning_unit_id = int( - required( - row["planning_unit_id"], - field_name="planning_unit_id", - context="TPlanungseinheitenPersonal", - ) - ) - employee_id = int( - required( - row["employee_id"], - field_name="employee_id", - context="TPlanungseinheitenPersonal", + def _map_plan_participants(self, rows: tuple[_TimeOfficePlanPersonnelRow, ...]) -> tuple[PlanParticipant, ...]: + return tuple( + PlanParticipant( + plan_id=row.plan_id, + planning_unit_id=row.planning_unit_id, + employee_id=row.employee_id, ) + for row in rows ) - membership_profession_id = int( - required( - row["membership_profession_id"], - field_name="membership_profession_id", - context=(f"TPlanungseinheitenPersonal planning_unit_id={planning_unit_id} employee_id={employee_id}"), + def _map_memberships( + self, rows: tuple[_TimeOfficePlanningUnitMembershipRow, ...] + ) -> tuple[PlanningUnitMembership, ...]: + return tuple( + PlanningUnitMembership( + planning_unit_id=row.planning_unit_id, + employee_id=row.employee_id, + valid_from=row.valid_from.date(), + valid_until=row.valid_until.date() if row.valid_until is not None else None, + staff_level=self._staff_level_from_profession( + row.membership_profession_id, + context=( + "TPlanungseinheitenPersonal " + f"planning_unit_id={row.planning_unit_id} " + f"employee_id={row.employee_id}" + ), + ), + is_home=row.is_home, + is_replacement=row.is_replacement, ) + for row in rows ) - valid_from = required( - to_datetime(row["valid_from"]), - field_name="valid_from", - context=(f"TPlanungseinheitenPersonal planning_unit_id={planning_unit_id} employee_id={employee_id}"), - ).date() - - valid_until = to_datetime(row["valid_until"]).date() if row["valid_until"] is not None else None - - return PlanningUnitMembership( - planning_unit_id=planning_unit_id, - employee_id=employee_id, - valid_from=valid_from, - valid_until=valid_until, - staff_level=self._staff_level_from_profession( - membership_profession_id, - context=(f"TPlanungseinheitenPersonal planning_unit_id={planning_unit_id} employee_id={employee_id}"), - ), - is_home=bool( - required( - row["is_home"], - field_name="is_home", - context="TPlanungseinheitenPersonal", - ) - ), - is_replacement=bool( - required( - row["is_replacement"], - field_name="is_replacement", - context="TPlanungseinheitenPersonal", - ) - ), - ) + def _staff_level_from_profession(self, profession_id: int, *, context: str) -> StaffLevel: + staff_level = self._facts.staff_level_by_profession_id_map.get(profession_id) - def _staff_level_from_profession( - self, - profession_id: int, - *, - context: str, - ) -> StaffLevel: - staff_level = self._facts.profession_staff_level_map.get(profession_id) if staff_level is None: raise ValueError( f"No StaffLevel mapping configured for TimeOffice profession_id={profession_id} in {context}." @@ -319,19 +280,17 @@ def _staff_level_from_profession( return staff_level def _capabilities_for_employee(self, employee_id: int) -> tuple[Capability, ...]: - return tuple(self._facts.employee_capabilities_map.get(employee_id, ())) + return tuple(self._facts.capabilities_by_employee_id_map.get(employee_id, ())) def _display_name( self, *, employee_id: int, - first_name: object, - last_name: object, + first_name: str | None, + last_name: str | None, ) -> str: - first = clean_text(first_name) - last = clean_text(last_name) + display_name = " ".join(part for part in (last_name, first_name) if part) - display_name = " ".join(part for part in (last, first) if part) if display_name: return display_name diff --git a/src/scheduling/timeoffice/repositories/planning_units.py b/src/scheduling/timeoffice/repositories/planning_units.py index a26e3a0c..b295656a 100644 --- a/src/scheduling/timeoffice/repositories/planning_units.py +++ b/src/scheduling/timeoffice/repositories/planning_units.py @@ -1,15 +1,29 @@ +from typing import Self + +from pydantic import model_validator from sqlalchemy import Connection, bindparam, text -from sqlalchemy.engine import RowMapping -from src.scheduling.models import ( - Plan, - PlanningPeriod, - PlanningUnit, - PlanningUnitKind, - SchedulingBaseModel, -) -from src.scheduling.timeoffice.facts import TimeOfficeFacts -from src.scheduling.timeoffice.repositories.helpers import required +from scheduling.models import Plan, PlanningPeriod, PlanningUnit, PlanningUnitKind, SchedulingBaseModel +from scheduling.timeoffice.facts import TimeOfficeFacts +from scheduling.timeoffice.repositories.types import SourceInt, TimeOfficeSourceRow + + +class _TimeOfficePlanningUnitRow(TimeOfficeSourceRow): + planning_unit_id: SourceInt + plan_id: SourceInt + plan_planning_unit_id: SourceInt + + @model_validator(mode="after") + def validate_plan_reference(self) -> Self: + if self.plan_planning_unit_id != self.planning_unit_id: + raise ValueError( + "TimeOffice plan row references a different planning unit than the selected unit: " + f"planning_unit_id={self.planning_unit_id} " + f"plan_id={self.plan_id} " + f"plan_planning_unit_id={self.plan_planning_unit_id}." + ) + + return self class PlanningUnitRepositoryResult(SchedulingBaseModel): @@ -33,37 +47,30 @@ def fetch( if not selected_planning_unit_ids: return PlanningUnitRepositoryResult(planning_units=(), plans=()) - rows = tuple( - connection.execute( - self._query(), - { - "planning_unit_ids": selected_planning_unit_ids, - "period_start": period.start, - "period_end": period.end, - "planning_interval_id": self._facts.monthly_planning_interval_id, - "planning_status_id": self._facts.target_planning_status_id, - }, - ) - .mappings() - .all() + rows = self._fetch_rows( + connection=connection, + selected_planning_unit_ids=selected_planning_unit_ids, + period=period, ) - planning_units = tuple(self._map_planning_unit(row) for row in rows) - plans = tuple(self._map_plan(row) for row in rows) - - self._validate_result( + self._validate_rows( requested_ids=selected_planning_unit_ids, - planning_units=planning_units, - plans=plans, + rows=rows, ) return PlanningUnitRepositoryResult( - planning_units=planning_units, - plans=plans, + planning_units=self._map_planning_units(rows), + plans=self._map_plans(rows), ) - def _query(self): - return text( + def _fetch_rows( + self, + *, + connection: Connection, + selected_planning_unit_ids: tuple[int, ...], + period: PlanningPeriod, + ) -> tuple[_TimeOfficePlanningUnitRow, ...]: + query = text( """ SELECT pe.Prim AS planning_unit_id, @@ -81,60 +88,64 @@ def _query(self): """ ).bindparams(bindparam("planning_unit_ids", expanding=True)) - def _map_planning_unit(self, row: RowMapping) -> PlanningUnit: - planning_unit_id = int( - required( - row["planning_unit_id"], - field_name="planning_unit_id", - context="TPlanungseinheiten", + raw_rows = ( + connection.execute( + query, + { + "planning_unit_ids": selected_planning_unit_ids, + "period_start": period.start, + "period_end": period.end, + "planning_interval_id": self._facts.monthly_planning_interval_id, + "planning_status_id": self._facts.target_planning_status_id, + }, ) + .mappings() + .all() ) - return PlanningUnit( - planning_unit_id=planning_unit_id, - display_name=f"Planning Unit {planning_unit_id}", - kind=self._facts.planning_unit_kind_map.get( - planning_unit_id, - PlanningUnitKind.STATION, - ), - ) - - def _map_plan(self, row: RowMapping) -> Plan: - return Plan( - plan_id=int(required(row["plan_id"], field_name="plan_id", context="TPlan")), - planning_unit_id=int( - required( - row["plan_planning_unit_id"], - field_name="plan_planning_unit_id", - context="TPlan", - ) - ), - ) + return tuple(_TimeOfficePlanningUnitRow.model_validate(row) for row in raw_rows) - def _validate_result( - self, - *, - requested_ids: tuple[int, ...], - planning_units: tuple[PlanningUnit, ...], - plans: tuple[Plan, ...], - ) -> None: + def _validate_rows(self, *, requested_ids: tuple[int, ...], rows: tuple[_TimeOfficePlanningUnitRow, ...]) -> None: requested = set(requested_ids) - returned_units = [unit.planning_unit_id for unit in planning_units] - returned_plans = [plan.planning_unit_id for plan in plans] + returned_ids = [row.planning_unit_id for row in rows] - missing = sorted(requested - set(returned_units)) + missing = sorted(requested - set(returned_ids)) if missing: raise ValueError(f"No selected TimeOffice target plan found for planning_unit_ids={missing}.") - duplicates = sorted( - {planning_unit_id for planning_unit_id in returned_units if returned_units.count(planning_unit_id) > 1} - ) + duplicates = self._duplicate_values(returned_ids) if duplicates: raise ValueError(f"Multiple selected TimeOffice target plans found for planning_unit_ids={duplicates}.") - if set(returned_units) != set(returned_plans): - raise ValueError( - "Planning units and plans do not match: " - f"planning_units={sorted(returned_units)} " - f"plans={sorted(returned_plans)}." + def _map_planning_units(self, rows: tuple[_TimeOfficePlanningUnitRow, ...]) -> tuple[PlanningUnit, ...]: + return tuple( + PlanningUnit( + planning_unit_id=row.planning_unit_id, + display_name=f"Planning Unit {row.planning_unit_id}", + kind=self._facts.planning_unit_kind_map.get( + row.planning_unit_id, + PlanningUnitKind.STATION, + ), + ) + for row in rows + ) + + def _map_plans(self, rows: tuple[_TimeOfficePlanningUnitRow, ...]) -> tuple[Plan, ...]: + return tuple( + Plan( + plan_id=row.plan_id, + planning_unit_id=row.plan_planning_unit_id, ) + for row in rows + ) + + def _duplicate_values(self, values: list[int]) -> list[int]: + seen: set[int] = set() + duplicates: set[int] = set() + + for value in values: + if value in seen: + duplicates.add(value) + seen.add(value) + + return sorted(duplicates) diff --git a/src/scheduling/timeoffice/repositories/roster.py b/src/scheduling/timeoffice/repositories/roster.py index 91571bc1..2d70b22e 100644 --- a/src/scheduling/timeoffice/repositories/roster.py +++ b/src/scheduling/timeoffice/repositories/roster.py @@ -1,9 +1,10 @@ -from datetime import date as Date +from datetime import date, datetime +from typing import Self +from pydantic import model_validator from sqlalchemy import Connection, bindparam, text -from sqlalchemy.engine import RowMapping -from src.scheduling.models import ( +from scheduling.models import ( Assignment, AssignmentType, Availability, @@ -13,12 +14,58 @@ PlanningPeriod, SchedulingBaseModel, ) -from src.scheduling.timeoffice.facts import ( - TimeOfficeAvailabilityFact, - TimeOfficeFacts, - TimeOfficeShiftFact, +from scheduling.timeoffice.facts import TimeOfficeFacts, TimeOfficeShiftFact +from scheduling.timeoffice.repositories.types import ( + CleanNullableText, + SourceInt, + SourceNullableInt, + TimeOfficeSourceRow, ) -from src.scheduling.timeoffice.repositories.helpers import required, to_datetime + + +class _TimeOfficeRosterRow(TimeOfficeSourceRow): + plan_id: SourceNullableInt = None + employee_id: SourceInt + roster_date: datetime + + work_shift_id: SourceNullableInt = None + work_shift_code: CleanNullableText = None + + global_absence_shift_id: SourceNullableInt = None + absence_shift_id: SourceNullableInt = None + resolved_absence_shift_id: SourceNullableInt = None + resolved_absence_code: CleanNullableText = None + + planning_unit_id: SourceNullableInt = None + + @model_validator(mode="after") + def validate_row_kind(self) -> Self: + has_work_shift = self.work_shift_id is not None + has_absence = self.global_absence_shift_id is not None or self.absence_shift_id is not None + + if not has_work_shift and not has_absence: + raise ValueError( + "Invalid TimeOffice roster row: neither work shift nor absence is set " + f"for employee_id={self.employee_id}, roster_date={self.roster_date}." + ) + + return self + + @model_validator(mode="after") + def validate_absence_references(self) -> Self: + if ( + self.global_absence_shift_id is not None + and self.absence_shift_id is not None + and self.global_absence_shift_id != self.absence_shift_id + ): + raise ValueError( + "Conflicting TimeOffice absence references in " + "TPlanPersonalKommtGeht: " + f"RefgAbw={self.global_absence_shift_id} " + f"RefDienstAbw={self.absence_shift_id}." + ) + + return self class RosterRepositoryResult(SchedulingBaseModel): @@ -33,9 +80,9 @@ class TimeOfficeRosterRepository: - work rows as Assignment - absence rows as Availability - Wishes/preferences are intentionally not emitted here yet, even though - TPlanPersonalKommtGeht has `Wunschdienst`. They need a separate source - analysis and a separate Preference model. + Wishes/preferences are handled by TimeOfficeWishRepository. This repository + intentionally keeps the existing roster import behavior and does not change + semantics around Wunschdienst rows in this refactor. """ def __init__(self, *, facts: TimeOfficeFacts) -> None: @@ -52,23 +99,15 @@ def fetch( if not plans or not employees: return RosterRepositoryResult(assignments=(), availability=()) - selected_plan_ids = tuple(plan.plan_id for plan in plans) - selected_planning_unit_ids = tuple(plan.planning_unit_id for plan in plans) - employee_ids = tuple(employee.employee_id for employee in employees) - - rows = tuple( - connection.execute( - self._query(), - { - "employee_ids": employee_ids, - "period_start": period.start, - "period_end": period.end, - }, - ) - .mappings() - .all() + rows = self._fetch_rows( + connection=connection, + employees=employees, + period=period, ) + selected_plan_ids = {plan.plan_id for plan in plans} + selected_planning_unit_ids = {plan.planning_unit_id for plan in plans} + return RosterRepositoryResult( assignments=self._map_assignments( rows=rows, @@ -78,28 +117,30 @@ def fetch( availability=self._map_availability(rows=rows), ) - def _query(self): - return text( + def _fetch_rows( + self, + *, + connection: Connection, + employees: tuple[Employee, ...], + period: PlanningPeriod, + ) -> tuple[_TimeOfficeRosterRow, ...]: + query = text( """ SELECT pkg.RefPlan AS plan_id, pkg.RefPersonal AS employee_id, pkg.Datum AS roster_date, - pkg.lfdNr AS segment_number, pkg.RefDienste AS work_shift_id, work_d.KurzBez AS work_shift_code, pkg.RefgAbw AS global_absence_shift_id, - global_absence_d.KurzBez AS global_absence_shift_code, - pkg.RefDienstAbw AS absence_shift_id, - absence_d.KurzBez AS absence_shift_code, - pkg.RefPlanungseinheiten AS planning_unit_id, - pkg.RefPeinheitOwner AS planning_unit_owner_id, + COALESCE(pkg.RefgAbw, pkg.RefDienstAbw) AS resolved_absence_shift_id, + COALESCE(global_absence_d.KurzBez, absence_d.KurzBez) AS resolved_absence_code, - pkg.Wunschdienst AS is_wish + pkg.RefPlanungseinheiten AS planning_unit_id FROM TPlanPersonalKommtGeht pkg LEFT JOIN TDienste work_d ON work_d.Prim = pkg.RefDienste @@ -122,74 +163,99 @@ def _query(self): """ ).bindparams(bindparam("employee_ids", expanding=True)) + raw_rows = ( + connection.execute( + query, + { + "employee_ids": tuple(employee.employee_id for employee in employees), + "period_start": period.start, + "period_end": period.end, + }, + ) + .mappings() + .all() + ) + + return tuple(_TimeOfficeRosterRow.model_validate(row) for row in raw_rows) + def _map_assignments( self, *, - rows: tuple[RowMapping, ...], - selected_plan_ids: tuple[int, ...], - selected_planning_unit_ids: tuple[int, ...], + rows: tuple[_TimeOfficeRosterRow, ...], + selected_plan_ids: set[int], + selected_planning_unit_ids: set[int], ) -> tuple[Assignment, ...]: - shift_facts = {int(fact.source_shift_id): fact for fact in self._facts.shift_facts} - - selected_plan_id_set = set(selected_plan_ids) - selected_planning_unit_id_set = set(selected_planning_unit_ids) - - assignments_by_key: dict[ - tuple[int, Date, int, AssignmentType, int | None], - Assignment, - ] = {} + assignments: list[Assignment] = [] unmapped_shift_ids: dict[int, int] = {} for row in rows: - raw_shift_id = row["work_shift_id"] - if raw_shift_id is None: + if row.work_shift_id is None: continue - shift_id = int(raw_shift_id) - shift_fact = shift_facts.get(shift_id) + shift_fact = self._facts.shift_facts_by_id.get(row.work_shift_id) if shift_fact is None: - unmapped_shift_ids[shift_id] = unmapped_shift_ids.get(shift_id, 0) + 1 + unmapped_shift_ids[row.work_shift_id] = unmapped_shift_ids.get(row.work_shift_id, 0) + 1 continue - self._validate_work_shift_code(row=row, fact=shift_fact) - - employee_id = self._employee_id(row) - roster_date = required( - to_datetime(row["roster_date"]), - field_name="roster_date", - context="TPlanPersonalKommtGeht", - ).date() - - plan_id = self._optional_int( - row["plan_id"], - field_name="plan_id", - context="TPlanPersonalKommtGeht", + assignments.append( + self._map_assignment( + row=row, + shift_fact=shift_fact, + selected_plan_ids=selected_plan_ids, + selected_planning_unit_ids=selected_planning_unit_ids, + ) ) - planning_unit_id = self._optional_int( - row["planning_unit_id"], - field_name="planning_unit_id", - context="TPlanPersonalKommtGeht", + if unmapped_shift_ids: + details = ", ".join(f"{shift_id} count={count}" for shift_id, count in sorted(unmapped_shift_ids.items())) + raise ValueError( + "Unmapped TimeOffice work shift ids found in " + "TPlanPersonalKommtGeht. Add them to " + "TIMEOFFICE_FACTS.shift_facts_by_id or explicitly decide " + f"to exclude them. Details: {details}." ) - assignment_type = self._assignment_type( - plan_id=plan_id, - planning_unit_id=planning_unit_id, - selected_plan_ids=selected_plan_id_set, - selected_planning_unit_ids=selected_planning_unit_id_set, + return self._deduplicate_assignments(tuple(assignments)) + + def _map_assignment( + self, + *, + row: _TimeOfficeRosterRow, + shift_fact: TimeOfficeShiftFact, + selected_plan_ids: set[int], + selected_planning_unit_ids: set[int], + ) -> Assignment: + if row.work_shift_id is None: + raise ValueError( + "Cannot map TimeOffice assignment without work_shift_id: " + f"employee_id={row.employee_id} roster_date={row.roster_date}." ) - effective_planning_unit_id = planning_unit_id if assignment_type == AssignmentType.PLANNED else None + self._validate_work_shift_code(row=row, fact=shift_fact) - assignment = Assignment( - employee_id=employee_id, - date=roster_date, - shift_id=shift_id, - assignment_type=assignment_type, - planning_unit_id=effective_planning_unit_id, - ) + assignment_type = self._assignment_type( + plan_id=row.plan_id, + planning_unit_id=row.planning_unit_id, + selected_plan_ids=selected_plan_ids, + selected_planning_unit_ids=selected_planning_unit_ids, + ) + + return Assignment( + employee_id=row.employee_id, + date=row.roster_date.date(), + shift_id=row.work_shift_id, + assignment_type=assignment_type, + planning_unit_id=(row.planning_unit_id if assignment_type == AssignmentType.PLANNED else None), + ) + def _deduplicate_assignments(self, assignments: tuple[Assignment, ...]) -> tuple[Assignment, ...]: + assignments_by_key: dict[ + tuple[int, date, int, AssignmentType, int | None], + Assignment, + ] = {} + + for assignment in assignments: key = ( assignment.employee_id, assignment.date, @@ -199,15 +265,6 @@ def _map_assignments( ) assignments_by_key.setdefault(key, assignment) - if unmapped_shift_ids: - details = ", ".join(f"{shift_id} count={count}" for shift_id, count in sorted(unmapped_shift_ids.items())) - raise ValueError( - "Unmapped TimeOffice work shift ids found in " - "TPlanPersonalKommtGeht. Add them to " - "TIMEOFFICE_FACTS.scheduling_shift_facts or explicitly decide " - f"to exclude them. Details: {details}." - ) - return tuple( assignments_by_key[key] for key in sorted( @@ -216,66 +273,67 @@ def _map_assignments( item[0], item[1], item[2], - item[3], - item[4] or -1, + item[3].value, + -1 if item[4] is None else item[4], ), ) ) - def _map_availability( - self, - *, - rows: tuple[RowMapping, ...], - ) -> tuple[Availability, ...]: - availability_facts = {int(fact.source_shift_id): fact for fact in self._facts.availability_facts} - - availability_by_key: dict[ - tuple[int, Date, AvailabilityType], - Availability, - ] = {} - unmapped_absence_ids: dict[int, int] = {} + def _map_availability(self, *, rows: tuple[_TimeOfficeRosterRow, ...]) -> tuple[Availability, ...]: + availability: list[Availability] = [] for row in rows: - absence_shift_id = self._absence_shift_id(row) - if absence_shift_id is None: - continue + absence_shift_id = self._resolved_absence_shift_id(row) - fact = availability_facts.get(absence_shift_id) - if fact is None: - unmapped_absence_ids[absence_shift_id] = unmapped_absence_ids.get(absence_shift_id, 0) + 1 + if absence_shift_id is None: continue - self._validate_absence_code(row=row, fact=fact) + availability.append( + self._map_availability_row( + row=row, + absence_shift_id=absence_shift_id, + ) + ) - employee_id = self._employee_id(row) - roster_date = required( - to_datetime(row["roster_date"]), - field_name="roster_date", - context="TPlanPersonalKommtGeht", - ).date() + return self._deduplicate_availability(tuple(availability)) - availability = Availability( - employee_id=employee_id, - date=roster_date, - availability_type=fact.availability_type, + def _map_availability_row(self, *, row: _TimeOfficeRosterRow, absence_shift_id: int) -> Availability: + if row.resolved_absence_code is None: + raise ValueError( + "Missing resolved absence code for TimeOffice roster row: " + f"absence_shift_id={absence_shift_id} " + f"employee_id={row.employee_id} " + f"roster_date={row.roster_date}." ) - key = ( - availability.employee_id, - availability.date, - availability.availability_type, - ) - availability_by_key.setdefault(key, availability) + availability_type = self._facts.availability_type_by_absence_code.get(row.resolved_absence_code) - if unmapped_absence_ids: - details = ", ".join( - f"{absence_id} count={count}" for absence_id, count in sorted(unmapped_absence_ids.items()) - ) + if availability_type is None: raise ValueError( - "Unmapped TimeOffice absence shift ids found in " - "TPlanPersonalKommtGeht. Add them to " - f"TIMEOFFICE_FACTS.availability_facts. Details: {details}." + "Unmapped TimeOffice absence code: " + f"absence_shift_id={absence_shift_id} " + f"absence_code={row.resolved_absence_code!r}." + ) + + return Availability( + employee_id=row.employee_id, + date=row.roster_date.date(), + availability_type=availability_type, + ) + + def _deduplicate_availability(self, availability: tuple[Availability, ...]) -> tuple[Availability, ...]: + availability_by_key: dict[ + tuple[int, date, AvailabilityType], + Availability, + ] = {} + + for item in availability: + key = ( + item.employee_id, + item.date, + item.availability_type, ) + availability_by_key.setdefault(key, item) return tuple( availability_by_key[key] @@ -284,7 +342,7 @@ def _map_availability( key=lambda item: ( item[0], item[1], - item[2], + item[2].value, ), ) ) @@ -302,88 +360,25 @@ def _assignment_type( return AssignmentType.EXTERNAL - def _absence_shift_id(self, row: RowMapping) -> int | None: - global_absence_shift_id = self._optional_int( - row["global_absence_shift_id"], - field_name="global_absence_shift_id", - context="TPlanPersonalKommtGeht", - ) - absence_shift_id = self._optional_int( - row["absence_shift_id"], - field_name="absence_shift_id", - context="TPlanPersonalKommtGeht", - ) + def _resolved_absence_shift_id(self, row: _TimeOfficeRosterRow) -> int | None: + if row.global_absence_shift_id is not None: + return row.global_absence_shift_id - if global_absence_shift_id is None: - return absence_shift_id + if row.absence_shift_id is not None: + return row.absence_shift_id - if absence_shift_id is None: - return global_absence_shift_id + return None - if global_absence_shift_id != absence_shift_id: - raise ValueError( - "Conflicting TimeOffice absence references in " - "TPlanPersonalKommtGeht: " - f"RefgAbw={global_absence_shift_id} " - f"RefDienstAbw={absence_shift_id}." - ) + def _validate_work_shift_code(self, *, row: _TimeOfficeRosterRow, fact: TimeOfficeShiftFact) -> None: + if row.work_shift_id is None: + raise ValueError("Cannot validate work shift code without work_shift_id.") - return absence_shift_id + if row.work_shift_code is None: + raise ValueError(f"Missing TDienste.KurzBez for TimeOffice work shift source_shift_id={row.work_shift_id}.") - def _validate_work_shift_code( - self, - *, - row: RowMapping, - fact: TimeOfficeShiftFact, - ) -> None: - actual_code = row["work_shift_code"] - if actual_code is None: - raise ValueError( - f"Missing TDienste.KurzBez for TimeOffice work shift source_shift_id={fact.source_shift_id}." - ) - - actual = str(actual_code).strip() - if actual != fact.expected_code: + if row.work_shift_code != fact.expected_code: raise ValueError( "Unexpected TimeOffice work shift code: " - f"source_shift_id={fact.source_shift_id} " - f"expected={fact.expected_code!r} actual={actual!r}." - ) - - def _validate_absence_code( - self, - *, - row: RowMapping, - fact: TimeOfficeAvailabilityFact, - ) -> None: - actual_code = row["absence_shift_code"] or row["global_absence_shift_code"] - if actual_code is None: - raise ValueError( - f"Missing TDienste.KurzBez for TimeOffice absence shift source_shift_id={fact.source_shift_id}." - ) - - actual = str(actual_code).strip() - if actual != fact.expected_code: - raise ValueError( - "Unexpected TimeOffice absence code: " - f"source_shift_id={fact.source_shift_id} " - f"expected={fact.expected_code!r} actual={actual!r}." - ) - - def _employee_id(self, row: RowMapping) -> int: - return int( - required( - row["employee_id"], - field_name="employee_id", - context="TPlanPersonalKommtGeht", + f"source_shift_id={row.work_shift_id} " + f"expected={fact.expected_code!r} actual={row.work_shift_code!r}." ) - ) - - def _optional_int(self, value: object, *, field_name: str, context: str) -> int | None: - if value is None: - return None - - if isinstance(value, bool) or not isinstance(value, int): - raise ValueError(f"Expected int or NULL for {field_name} in {context}, got {value!r}.") - - return value diff --git a/src/scheduling/timeoffice/repositories/shifts.py b/src/scheduling/timeoffice/repositories/shifts.py index f362e820..9ec967b5 100644 --- a/src/scheduling/timeoffice/repositories/shifts.py +++ b/src/scheduling/timeoffice/repositories/shifts.py @@ -1,19 +1,54 @@ from collections import defaultdict -from collections.abc import Sequence -from datetime import datetime as DateTime +from datetime import datetime +from typing import Self +from pydantic import model_validator from sqlalchemy import Connection, bindparam, text -from sqlalchemy.engine import RowMapping -from src.scheduling.models import SchedulingBaseModel, Shift -from src.scheduling.timeoffice.facts import TimeOfficeFacts, TimeOfficeShiftFact -from src.scheduling.timeoffice.repositories.helpers import ( - clean_text, - normalize_code, - required, - to_datetime, - to_non_negative_int, -) +from scheduling.models import SchedulingBaseModel, Shift +from scheduling.timeoffice.facts import TimeOfficeFacts, TimeOfficeShiftFact +from scheduling.timeoffice.repositories.types import CleanText, SourceInt, SourceNullableInt, TimeOfficeSourceRow + + +class _TimeOfficeShiftRow(TimeOfficeSourceRow): + shift_id: SourceInt + shift_code: CleanText + shift_type_id: SourceInt + + segment_start: datetime | None = None + segment_end: datetime | None = None + segment_minutes: SourceNullableInt = None + + @model_validator(mode="after") + def validate_segment_shape(self) -> Self: + has_start = self.segment_start is not None + has_end = self.segment_end is not None + + if has_start != has_end: + raise ValueError( + "Incomplete TimeOffice shift segment: " + f"shift_id={self.shift_id} " + f"segment_start={self.segment_start!r} " + f"segment_end={self.segment_end!r}." + ) + + if self.segment_start is not None and self.segment_end is not None: + if self.segment_end <= self.segment_start: + raise ValueError( + "Invalid TimeOffice shift segment: " + f"shift_id={self.shift_id} " + f"segment_start={self.segment_start!r} " + f"segment_end={self.segment_end!r}." + ) + + if self.segment_minutes is not None and self.segment_minutes < 0: + raise ValueError( + "Invalid negative TimeOffice shift segment minutes: " + f"shift_id={self.shift_id} " + f"segment_minutes={self.segment_minutes!r}." + ) + + return self class ShiftRepositoryResult(SchedulingBaseModel): @@ -26,26 +61,19 @@ class TimeOfficeShiftRepository: def __init__(self, *, facts: TimeOfficeFacts) -> None: self._facts = facts - def fetch( - self, - *, - connection: Connection, - ) -> ShiftRepositoryResult: - shift_ids = tuple(int(shift_fact.source_shift_id) for shift_fact in self._facts.shift_facts) + def fetch(self, *, connection: Connection) -> ShiftRepositoryResult: + shift_ids = tuple(self._facts.shift_facts_by_id.keys()) if not shift_ids: return ShiftRepositoryResult(shifts=()) - rows = tuple( - connection.execute( - self._query(), - {"shift_ids": shift_ids}, - ) - .mappings() - .all() + rows = self._fetch_rows( + connection=connection, + shift_ids=shift_ids, ) shifts = self._map_rows(rows) + self._validate_requested_shifts( requested_shift_ids=shift_ids, shifts=shifts, @@ -53,13 +81,17 @@ def fetch( return ShiftRepositoryResult(shifts=shifts) - def _query(self): - return text( + def _fetch_rows( + self, + *, + connection: Connection, + shift_ids: tuple[int, ...], + ) -> tuple[_TimeOfficeShiftRow, ...]: + query = text( """ SELECT d.Prim AS shift_id, d.KurzBez AS shift_code, - d.Bezeichnung AS shift_name, d.RefDienstTypen AS shift_type_id, sz.Kommt AS segment_start, @@ -76,72 +108,64 @@ def _query(self): """ ).bindparams(bindparam("shift_ids", expanding=True)) - def _map_rows(self, rows: Sequence[RowMapping]) -> tuple[Shift, ...]: - rows_by_shift_id: dict[int, list[RowMapping]] = defaultdict(list) - - for row in rows: - shift_id = int( - required( - row["shift_id"], - field_name="shift_id", - context="TDienste", - ) + raw_rows = ( + connection.execute( + query, + {"shift_ids": shift_ids}, ) - rows_by_shift_id[shift_id].append(row) + .mappings() + .all() + ) - shift_facts_by_id = {int(shift_fact.source_shift_id): shift_fact for shift_fact in self._facts.shift_facts} + return tuple(_TimeOfficeShiftRow.model_validate(row) for row in raw_rows) + + def _map_rows(self, rows: tuple[_TimeOfficeShiftRow, ...]) -> tuple[Shift, ...]: + rows_by_shift_id = self._group_rows_by_shift_id(rows) return tuple( self._map_shift( shift_id=shift_id, rows=shift_rows, - shift_fact=shift_facts_by_id[shift_id], + shift_fact=self._facts.shift_facts_by_id[shift_id], ) for shift_id, shift_rows in sorted(rows_by_shift_id.items()) ) + def _group_rows_by_shift_id(self, rows: tuple[_TimeOfficeShiftRow, ...]) -> dict[int, list[_TimeOfficeShiftRow]]: + rows_by_shift_id: dict[int, list[_TimeOfficeShiftRow]] = defaultdict(list) + + for row in rows: + rows_by_shift_id[row.shift_id].append(row) + + return dict(rows_by_shift_id) + def _map_shift( self, *, shift_id: int, - rows: list[RowMapping], + rows: list[_TimeOfficeShiftRow], shift_fact: TimeOfficeShiftFact, ) -> Shift: first_row = rows[0] - context = f"TDienste shift_id={shift_id}" - source_code = clean_text( - required( - first_row["shift_code"], - field_name="shift_code", - context=context, - ) - ) - if source_code is None: - raise ValueError(f"Empty TimeOffice shift code in {context}.") + source_code = first_row.shift_code - self._validate_expected_code( - shift_id=shift_id, - actual_code=source_code, - expected_code=shift_fact.expected_code, - ) + if self._normalize_shift_code(source_code) != self._normalize_shift_code(shift_fact.expected_code): + raise ValueError( + "Unexpected TimeOffice shift code for known scheduling shift: " + f"shift_id={shift_id} expected={shift_fact.expected_code!r} actual={source_code!r}." + ) - shift_type_id = int( - required( - first_row["shift_type_id"], - field_name="shift_type_id", - context=context, + if first_row.shift_type_id not in self._facts.work_shift_type_ids: + raise ValueError( + "Known scheduling shift is not configured as real work shift in TimeOffice: " + f"shift_id={shift_id} shift_type_id={first_row.shift_type_id}." ) - ) - self._validate_real_work_shift( - shift_id=shift_id, - shift_type_id=shift_type_id, - ) segments = self._map_segments(rows=rows, shift_id=shift_id) + start_at = segments[0][0] end_at = segments[-1][1] - net_work_minutes = self._net_work_minutes(segments) return Shift( shift_id=shift_id, @@ -150,95 +174,56 @@ def _map_shift( staffing_role=shift_fact.staffing_role, start_minute=self._minute_of_day(start_at), end_minute=self._minute_of_day(end_at), - net_work_minutes=net_work_minutes, + net_work_minutes=self._net_work_minutes(segments), ) def _map_segments( - self, - *, - rows: list[RowMapping], - shift_id: int, - ) -> tuple[tuple[DateTime, DateTime, int], ...]: - segments: list[tuple[DateTime, DateTime, int]] = [] + self, *, rows: list[_TimeOfficeShiftRow], shift_id: int + ) -> tuple[tuple[datetime, datetime, int], ...]: + segments: list[tuple[datetime, datetime, int]] = [] for row in rows: - if row["segment_start"] is None and row["segment_end"] is None: + if row.segment_start is None and row.segment_end is None: continue - context = f"TDiensteSollzeiten shift_id={shift_id}" - - start_at = required( - to_datetime(row["segment_start"]), - field_name="segment_start", - context=context, - ) - end_at = required( - to_datetime(row["segment_end"]), - field_name="segment_end", - context=context, - ) - minutes = to_non_negative_int(row["segment_minutes"]) - - if end_at <= start_at: + if row.segment_start is None or row.segment_end is None: raise ValueError( - f"Invalid shift segment in {context}: segment_start={start_at!r} segment_end={end_at!r}." + "Invalid TimeOffice shift row after source-row validation: " + f"shift_id={shift_id} " + f"segment_start={row.segment_start!r} " + f"segment_end={row.segment_end!r}." ) - segments.append((start_at, end_at, minutes)) + segments.append( + ( + row.segment_start, + row.segment_end, + row.segment_minutes or 0, + ) + ) if not segments: raise ValueError(f"No timing segments found for TimeOffice shift_id={shift_id}.") return tuple(segments) - def _net_work_minutes( - self, - segments: tuple[tuple[DateTime, DateTime, int], ...], - ) -> int: + def _net_work_minutes(self, segments: tuple[tuple[datetime, datetime, int], ...]) -> int: source_minutes = sum(segment_minutes for _, _, segment_minutes in segments) + if source_minutes > 0: return source_minutes return sum(int((end_at - start_at).total_seconds() // 60) for start_at, end_at, _ in segments) - def _validate_expected_code( - self, - *, - shift_id: int, - actual_code: str, - expected_code: str, - ) -> None: - if normalize_code(actual_code) != normalize_code(expected_code): - raise ValueError( - "Unexpected TimeOffice shift code for known scheduling shift: " - f"shift_id={shift_id} expected={expected_code!r} actual={actual_code!r}." - ) - - def _validate_real_work_shift( - self, - *, - shift_id: int, - shift_type_id: int, - ) -> None: - real_work_shift_type_ids = {int(shift_type_id) for shift_type_id in self._facts.real_work_shift_type_ids} - - if shift_type_id not in real_work_shift_type_ids: - raise ValueError( - "Known scheduling shift is not configured as real work shift in TimeOffice: " - f"shift_id={shift_id} shift_type_id={shift_type_id}." - ) - - def _validate_requested_shifts( - self, - *, - requested_shift_ids: tuple[int, ...], - shifts: tuple[Shift, ...], - ) -> None: + def _validate_requested_shifts(self, *, requested_shift_ids: tuple[int, ...], shifts: tuple[Shift, ...]) -> None: returned_shift_ids = {shift.shift_id for shift in shifts} missing_shift_ids = sorted(set(requested_shift_ids) - returned_shift_ids) if missing_shift_ids: raise ValueError(f"Missing TimeOffice shift definitions for shift_ids={missing_shift_ids}.") - def _minute_of_day(self, value: DateTime) -> int: + def _minute_of_day(self, value: datetime) -> int: return value.hour * 60 + value.minute + + def _normalize_shift_code(self, value: str) -> str: + return value.strip().casefold() diff --git a/src/scheduling/timeoffice/repositories/sunday_work_history.py b/src/scheduling/timeoffice/repositories/sunday_work_history.py index ffd746bb..b32a44d8 100644 --- a/src/scheduling/timeoffice/repositories/sunday_work_history.py +++ b/src/scheduling/timeoffice/repositories/sunday_work_history.py @@ -1,14 +1,21 @@ -from datetime import date as Date +from datetime import date from sqlalchemy import Connection, bindparam, text -from sqlalchemy.engine import RowMapping -from src.scheduling.models import ( - Employee, - EmployeeSundayWorkHistory, - PlanningPeriod, - SchedulingBaseModel, -) +from scheduling.models import Employee, EmployeeSundayWorkHistory, PlanningPeriod, SchedulingBaseModel +from scheduling.timeoffice.repositories.types import CleanNullableText, SourceInt, TimeOfficeSourceRow + + +class _TimeOfficeSundayAccountRow(TimeOfficeSourceRow): + account_id: SourceInt + account_code: CleanNullableText = None + account_name: CleanNullableText = None + is_daily_account: bool | None = None + + +class _TimeOfficeSundayHistoryRow(TimeOfficeSourceRow): + employee_id: SourceInt + worked_sundays: SourceInt class SundayWorkHistoryRepositoryResult(SchedulingBaseModel): @@ -39,32 +46,32 @@ def fetch( if not employees: return SundayWorkHistoryRepositoryResult(sunday_work_history=()) - employee_ids = tuple(employee.employee_id for employee in employees) sunday_account_id = self._fetch_sunday_account_id(connection) - lookback_start = self._subtract_years(period.end, self.LOOKBACK_YEARS) - lookback_end = period.end - - rows = connection.execute( - self._history_query(), - { - "employee_ids": employee_ids, - "sunday_account_id": sunday_account_id, - "lookback_start": lookback_start, - "lookback_end": lookback_end, - }, - ).mappings() + rows = self._fetch_history_rows( + connection=connection, + employees=employees, + sunday_account_id=sunday_account_id, + lookback_start=self._subtract_years(period.end, self.LOOKBACK_YEARS), + lookback_end=period.end, + ) - return SundayWorkHistoryRepositoryResult(sunday_work_history=tuple(self._map_history_row(row) for row in rows)) + return SundayWorkHistoryRepositoryResult(sunday_work_history=self._map_history_rows(rows)) def _fetch_sunday_account_id(self, connection: Connection) -> int: - rows = ( - connection.execute( - self._sunday_account_query(), - {"sunday_account_code": self.SUNDAY_ACCOUNT_CODE}, - ) - .mappings() - .all() + query = text(""" + SELECT + Prim AS account_id, + BezProg AS account_code, + Bez AS account_name, + IstTagesKonto AS is_daily_account + FROM TKonten + WHERE BezProg = :sunday_account_code + """) + + rows = tuple( + _TimeOfficeSundayAccountRow.model_validate(row) + for row in connection.execute(query, {"sunday_account_code": self.SUNDAY_ACCOUNT_CODE}).mappings().all() ) if len(rows) != 1: @@ -74,32 +81,27 @@ def _fetch_sunday_account_id(self, connection: Connection) -> int: ) row = rows[0] - is_daily_account = int(row["is_daily_account"] or 0) - if is_daily_account != 1: + if row.is_daily_account is not True: raise ValueError( "TimeOffice Sunday account must be a daily account: " - f"account_id={row['account_id']} " - f"code={row['account_code']!r} " - f"name={row['account_name']!r}." + f"account_id={row.account_id} " + f"code={row.account_code!r} " + f"name={row.account_name!r}." ) - return int(row["account_id"]) - - def _sunday_account_query(self): - return text(""" - SELECT - Prim AS account_id, - BezProg AS account_code, - Bez AS account_name, - BezKurz AS account_short_name, - IstTagesKonto AS is_daily_account - FROM TKonten - WHERE BezProg = :sunday_account_code - """) + return row.account_id - def _history_query(self): - return text(""" + def _fetch_history_rows( + self, + *, + connection: Connection, + employees: tuple[Employee, ...], + sunday_account_id: int, + lookback_start: date, + lookback_end: date, + ) -> tuple[_TimeOfficeSundayHistoryRow, ...]: + query = text(""" SELECT p.Prim AS employee_id, COUNT(DISTINCT CAST(pkt.Datum AS date)) AS worked_sundays @@ -119,13 +121,32 @@ def _history_query(self): ORDER BY p.Prim """).bindparams(bindparam("employee_ids", expanding=True)) - def _map_history_row(self, row: RowMapping) -> EmployeeSundayWorkHistory: + raw_rows = ( + connection.execute( + query, + { + "employee_ids": tuple(employee.employee_id for employee in employees), + "sunday_account_id": sunday_account_id, + "lookback_start": lookback_start, + "lookback_end": lookback_end, + }, + ) + .mappings() + .all() + ) + + return tuple(_TimeOfficeSundayHistoryRow.model_validate(row) for row in raw_rows) + + def _map_history_rows(self, rows: tuple[_TimeOfficeSundayHistoryRow, ...]) -> tuple[EmployeeSundayWorkHistory, ...]: + return tuple(self._map_history_row(row) for row in rows) + + def _map_history_row(self, row: _TimeOfficeSundayHistoryRow) -> EmployeeSundayWorkHistory: return EmployeeSundayWorkHistory( - employee_id=int(row["employee_id"]), - worked_sundays=int(row["worked_sundays"] or 0), + employee_id=row.employee_id, + worked_sundays=row.worked_sundays, ) - def _subtract_years(self, value: Date, years: int) -> Date: + def _subtract_years(self, value: date, years: int) -> date: try: return value.replace(year=value.year - years) except ValueError: diff --git a/src/scheduling/timeoffice/repositories/types.py b/src/scheduling/timeoffice/repositories/types.py new file mode 100644 index 00000000..539d5deb --- /dev/null +++ b/src/scheduling/timeoffice/repositories/types.py @@ -0,0 +1,38 @@ +from typing import Annotated, Any + +from pydantic import BaseModel, BeforeValidator, ConfigDict, StrictFloat, StrictInt + + +class TimeOfficeSourceRow(BaseModel): + """Base class for validated TimeOffice SQL result rows.""" + + model_config = ConfigDict(extra="forbid", frozen=True) + + +def none_if_blank(value: Any) -> Any | None: + if value is None: + return None + + if isinstance(value, str) and not value.strip(): + return None + + return value + + +def clean_text(value: Any) -> str | None: + value = none_if_blank(value) + if value is None: + return None + + cleaned = str(value).strip() + return cleaned or None + + +CleanText = Annotated[str, BeforeValidator(clean_text)] +CleanNullableText = Annotated[str | None, BeforeValidator(clean_text)] + +SourceInt = StrictInt +SourceNullableInt = Annotated[StrictInt | None, BeforeValidator(none_if_blank)] + +SourceFloat = StrictFloat +SourceNullableFloat = Annotated[StrictFloat | None, BeforeValidator(none_if_blank)] diff --git a/src/scheduling/timeoffice/repositories/wishes.py b/src/scheduling/timeoffice/repositories/wishes.py new file mode 100644 index 00000000..bba5ddae --- /dev/null +++ b/src/scheduling/timeoffice/repositories/wishes.py @@ -0,0 +1,315 @@ +from datetime import date as Date +from datetime import datetime +from typing import Self + +from pydantic import model_validator +from sqlalchemy import Connection, bindparam, text + +from scheduling.models import Employee, Plan, PlanningPeriod, SchedulingBaseModel, Shift, Wish, WishKind +from scheduling.timeoffice.facts import TimeOfficeFacts, TimeOfficeShiftFact +from scheduling.timeoffice.repositories.types import ( + CleanNullableText, + SourceInt, + SourceNullableInt, + TimeOfficeSourceRow, +) + + +class _TimeOfficeWishRow(TimeOfficeSourceRow): + employee_id: SourceInt + wish_date: datetime + plan_id: SourceInt + planning_unit_id: SourceInt + + work_shift_id: SourceNullableInt = None + work_shift_code: CleanNullableText = None + work_shift_name: CleanNullableText = None + + global_absence_shift_id: SourceNullableInt = None + global_absence_shift_code: CleanNullableText = None + global_absence_shift_name: CleanNullableText = None + + absence_shift_id: SourceNullableInt = None + absence_shift_code: CleanNullableText = None + absence_shift_name: CleanNullableText = None + + resolved_absence_shift_id: SourceNullableInt = None + resolved_absence_code: CleanNullableText = None + resolved_absence_name: CleanNullableText = None + + @model_validator(mode="after") + def validate_row_kind(self) -> Self: + has_work_shift = self.work_shift_id is not None + has_absence = self.global_absence_shift_id is not None or self.absence_shift_id is not None + + if has_work_shift and has_absence: + raise ValueError( + "Ambiguous TimeOffice wish row: both work shift and absence are set " + f"for employee_id={self.employee_id}, wish_date={self.wish_date}." + ) + + if not has_work_shift and not has_absence: + raise ValueError( + "Invalid TimeOffice wish row: neither work shift nor absence is set " + f"for employee_id={self.employee_id}, wish_date={self.wish_date}." + ) + + return self + + @model_validator(mode="after") + def validate_absence_references(self) -> Self: + if ( + self.global_absence_shift_id is not None + and self.absence_shift_id is not None + and self.global_absence_shift_id != self.absence_shift_id + ): + raise ValueError( + "Conflicting TimeOffice wish absence references in " + "TPlanPersonalKommtGeht: " + f"RefgAbw={self.global_absence_shift_id} " + f"RefDienstAbw={self.absence_shift_id}." + ) + + return self + + +class TimeOfficeWishRepositoryResult(SchedulingBaseModel): + wishes: tuple[Wish, ...] + + +class TimeOfficeWishRepository: + """Reads employee wishes from TimeOffice TPlanPersonalKommtGeht. + + Source rule: + - only rows with Wunschdienst != 0 + - work shift rows become SHIFT wishes + - mapped absence rows become FREE_DAY wishes for now + + TPersonalAntraege is intentionally not used yet because source validation did + not show usable request workflow rows for the selected planning context. + """ + + def __init__(self, *, facts: TimeOfficeFacts) -> None: + self._facts = facts + + def fetch( + self, + *, + connection: Connection, + plans: tuple[Plan, ...], + employees: tuple[Employee, ...], + shifts: tuple[Shift, ...], + period: PlanningPeriod, + ) -> TimeOfficeWishRepositoryResult: + if not plans or not employees: + return TimeOfficeWishRepositoryResult(wishes=()) + + rows = self._fetch_rows( + connection=connection, + plans=plans, + employees=employees, + period=period, + ) + + wishes = self._map_wishes(rows=rows, shifts=shifts) + + return TimeOfficeWishRepositoryResult(wishes=self._deduplicate_wishes(wishes)) + + def _fetch_rows( + self, + *, + connection: Connection, + plans: tuple[Plan, ...], + employees: tuple[Employee, ...], + period: PlanningPeriod, + ) -> tuple[_TimeOfficeWishRow, ...]: + query = text( + """ + SELECT + pkg.RefPersonal AS employee_id, + pkg.Datum AS wish_date, + pkg.RefPlan AS plan_id, + pkg.RefPlanungseinheiten AS planning_unit_id, + + pkg.RefDienste AS work_shift_id, + work_d.KurzBez AS work_shift_code, + work_d.Bezeichnung AS work_shift_name, + + pkg.RefgAbw AS global_absence_shift_id, + global_absence_d.KurzBez AS global_absence_shift_code, + global_absence_d.Bezeichnung AS global_absence_shift_name, + + pkg.RefDienstAbw AS absence_shift_id, + absence_d.KurzBez AS absence_shift_code, + absence_d.Bezeichnung AS absence_shift_name, + + COALESCE(pkg.RefgAbw, pkg.RefDienstAbw) AS resolved_absence_shift_id, + COALESCE(global_absence_d.KurzBez, absence_d.KurzBez) AS resolved_absence_code, + COALESCE(global_absence_d.Bezeichnung, absence_d.Bezeichnung) AS resolved_absence_name + FROM TPlanPersonalKommtGeht pkg + LEFT JOIN TDienste work_d + ON work_d.Prim = pkg.RefDienste + LEFT JOIN TDienste global_absence_d + ON global_absence_d.Prim = pkg.RefgAbw + LEFT JOIN TDienste absence_d + ON absence_d.Prim = pkg.RefDienstAbw + WHERE pkg.RefPersonal IN :employee_ids + AND pkg.RefPlan IN :plan_ids + AND pkg.RefPlanungseinheiten IN :planning_unit_ids + AND CONVERT(date, pkg.Datum) BETWEEN :period_start AND :period_end + AND ISNULL(pkg.Wunschdienst, 0) <> 0 + AND ( + pkg.RefDienste IS NOT NULL + OR pkg.RefgAbw IS NOT NULL + OR pkg.RefDienstAbw IS NOT NULL + ) + ORDER BY + pkg.RefPersonal, + pkg.Datum, + pkg.RefPlan, + pkg.RefPlanungseinheiten, + pkg.RefDienste, + pkg.RefgAbw, + pkg.RefDienstAbw + """ + ).bindparams( + bindparam("employee_ids", expanding=True), + bindparam("plan_ids", expanding=True), + bindparam("planning_unit_ids", expanding=True), + ) + + raw_rows = tuple( + connection.execute( + query, + { + "plan_ids": tuple(plan.plan_id for plan in plans), + "planning_unit_ids": tuple(plan.planning_unit_id for plan in plans), + "employee_ids": tuple(employee.employee_id for employee in employees), + "period_start": period.start, + "period_end": period.end, + }, + ) + .mappings() + .all() + ) + + return tuple(_TimeOfficeWishRow.model_validate(row) for row in raw_rows) + + def _map_wishes(self, *, rows: tuple[_TimeOfficeWishRow, ...], shifts: tuple[Shift, ...]) -> tuple[Wish, ...]: + known_shift_ids = {shift.shift_id for shift in shifts} + + return tuple( + self._map_wish( + row=row, + known_shift_ids=known_shift_ids, + ) + for row in rows + ) + + def _map_wish(self, *, row: _TimeOfficeWishRow, known_shift_ids: set[int]) -> Wish: + if row.work_shift_id is not None: + return self._map_shift_wish(row=row, known_shift_ids=known_shift_ids) + + return self._map_absence_wish(row=row) + + def _map_shift_wish(self, *, row: _TimeOfficeWishRow, known_shift_ids: set[int]) -> Wish: + if row.work_shift_id is None: + raise ValueError("Cannot map shift wish without work_shift_id.") + + fact = self._facts.shift_facts_by_id.get(row.work_shift_id) + if fact is None or row.work_shift_id not in known_shift_ids: + raise ValueError( + "Unmapped TimeOffice wish work shift id found in " + "TPlanPersonalKommtGeht. Add it to TIMEOFFICE_FACTS.shift_facts_by_id " + f"or explicitly decide to exclude it. Details: shift_id={row.work_shift_id}." + ) + + self._validate_work_shift_code(row=row, fact=fact) + + return Wish( + employee_id=row.employee_id, + planning_unit_id=row.planning_unit_id, + date=row.wish_date.date(), + kind=WishKind.SHIFT, + shift_id=row.work_shift_id, + ) + + def _map_absence_wish(self, *, row: _TimeOfficeWishRow) -> Wish: + absence_shift_id = self._resolved_absence_shift_id(row) + + if row.resolved_absence_code is None: + raise ValueError( + "Missing resolved absence code for TimeOffice wish row: " + f"absence_shift_id={absence_shift_id} " + f"employee_id={row.employee_id} " + f"wish_date={row.wish_date}." + ) + + wish_kind = self._facts.wish_kind_by_absence_code.get(row.resolved_absence_code) + if wish_kind is None: + raise ValueError( + "Unmapped TimeOffice wish absence code: " + f"absence_shift_id={absence_shift_id} " + f"absence_code={row.resolved_absence_code!r} " + f"absence_name={row.resolved_absence_name!r}." + ) + + return Wish( + employee_id=row.employee_id, + planning_unit_id=row.planning_unit_id, + date=row.wish_date.date(), + kind=wish_kind, + ) + + def _validate_work_shift_code(self, *, row: _TimeOfficeWishRow, fact: TimeOfficeShiftFact) -> None: + if row.work_shift_code is None: + raise ValueError( + f"Missing TDienste.KurzBez for TimeOffice wish work shift source_shift_id={fact.source_shift_id}." + ) + + if row.work_shift_code != fact.expected_code: + raise ValueError( + "Unexpected TimeOffice wish work shift code: " + f"source_shift_id={fact.source_shift_id} " + f"expected={fact.expected_code!r} actual={row.work_shift_code!r}." + ) + + def _resolved_absence_shift_id(self, row: _TimeOfficeWishRow) -> int: + if row.global_absence_shift_id is not None: + return row.global_absence_shift_id + + if row.absence_shift_id is not None: + return row.absence_shift_id + + raise ValueError( + "Invalid TimeOffice wish row after source-row validation: " + f"missing absence shift id for employee_id={row.employee_id}, " + f"wish_date={row.wish_date}." + ) + + def _deduplicate_wishes(self, wishes: tuple[Wish, ...]) -> tuple[Wish, ...]: + wishes_by_key: dict[tuple[int, int, Date, WishKind, int | None], Wish] = {} + + for wish in wishes: + key = ( + wish.employee_id, + wish.planning_unit_id, + wish.date, + wish.kind, + wish.shift_id, + ) + wishes_by_key.setdefault(key, wish) + + return tuple( + wishes_by_key[key] + for key in sorted( + wishes_by_key, + key=lambda item: ( + item[0], + item[1], + item[2], + item[3], + item[4] or -1, + ), + ) + ) diff --git a/src/scheduling/timeoffice/service.py b/src/scheduling/timeoffice/service.py index 14c72d3f..5b828650 100644 --- a/src/scheduling/timeoffice/service.py +++ b/src/scheduling/timeoffice/service.py @@ -1,6 +1,7 @@ -from src.scheduling.models import PlanningPeriod, SchedulingDataset -from src.scheduling.timeoffice.database import TimeOfficeDatabase -from src.scheduling.timeoffice.facts import TimeOfficeFacts +from scheduling.models import PlanningPeriod +from scheduling.timeoffice.database import TimeOfficeDatabase +from scheduling.timeoffice.facts import TimeOfficeFacts +from scheduling.validation.dataset import ValidatedSchedulingDataset class TimeOfficeService: @@ -20,7 +21,7 @@ def fetch_dataset( *, planning_unit_ids: tuple[int, ...], period: PlanningPeriod, - ) -> SchedulingDataset: + ) -> ValidatedSchedulingDataset: selected_planning_unit_ids = self._normalize_planning_unit_ids(planning_unit_ids) return self._database.fetch_dataset( diff --git a/src/scheduling/validation/__init__.py b/src/scheduling/validation/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/scheduling/validation/context.py b/src/scheduling/validation/context.py new file mode 100644 index 00000000..eea8805c --- /dev/null +++ b/src/scheduling/validation/context.py @@ -0,0 +1,58 @@ +from collections.abc import Mapping +from dataclasses import dataclass +from types import MappingProxyType + +from scheduling.models import ( + EmployeeId, + Plan, + PlanId, + PlanningUnitId, + PlanningUnitKind, + Shift, + ShiftId, +) +from scheduling.models.dataset import SchedulingDataset +from scheduling.validation.helpers import ensure_unique + + +@dataclass(frozen=True, slots=True) +class DatasetValidationContext: + employee_ids: frozenset[EmployeeId] + planning_unit_ids: frozenset[PlanningUnitId] + plan_ids: frozenset[PlanId] + shift_ids: frozenset[ShiftId] + + plans_by_id: Mapping[PlanId, Plan] + planning_unit_kind_by_id: Mapping[PlanningUnitId, PlanningUnitKind] + shifts_by_id: Mapping[ShiftId, Shift] + + @classmethod + def from_dataset(cls, dataset: SchedulingDataset) -> "DatasetValidationContext": + employee_ids = ensure_unique( + (employee.employee_id for employee in dataset.employees), + "employee_id", + ) + planning_unit_ids = ensure_unique( + (unit.planning_unit_id for unit in dataset.planning_units), + "planning_unit_id", + ) + plan_ids = ensure_unique( + (plan.plan_id for plan in dataset.plans), + "plan_id", + ) + shift_ids = ensure_unique( + (shift.shift_id for shift in dataset.shifts), + "shift_id", + ) + + return cls( + employee_ids=employee_ids, + planning_unit_ids=planning_unit_ids, + plan_ids=plan_ids, + shift_ids=shift_ids, + plans_by_id=MappingProxyType({plan.plan_id: plan for plan in dataset.plans}), + planning_unit_kind_by_id=MappingProxyType( + {unit.planning_unit_id: unit.kind for unit in dataset.planning_units} + ), + shifts_by_id=MappingProxyType({shift.shift_id: shift for shift in dataset.shifts}), + ) diff --git a/src/scheduling/validation/dataset.py b/src/scheduling/validation/dataset.py new file mode 100644 index 00000000..40c1f55b --- /dev/null +++ b/src/scheduling/validation/dataset.py @@ -0,0 +1,35 @@ +from typing import Self + +from pydantic import model_validator + +from scheduling.models.dataset import SchedulingDataset +from scheduling.validation.context import DatasetValidationContext +from scheduling.validation.validators import ( + validate_assignments, + validate_availability, + validate_demand_requirements, + validate_monthly_work_accounts, + validate_plan_participants, + validate_planning_unit_memberships, + validate_plans, + validate_sunday_work_history, + validate_wishes, +) + + +class ValidatedSchedulingDataset(SchedulingDataset): + @model_validator(mode="after") + def validate_cross_references(self) -> Self: + context = DatasetValidationContext.from_dataset(self) + + validate_plans(self, context) + validate_plan_participants(self, context) + validate_planning_unit_memberships(self, context) + validate_assignments(self, context) + validate_availability(self, context) + validate_demand_requirements(self, context) + validate_sunday_work_history(self, context) + validate_wishes(self, context) + validate_monthly_work_accounts(self, context) + + return self diff --git a/src/scheduling/validation/helpers.py b/src/scheduling/validation/helpers.py new file mode 100644 index 00000000..1f39f81d --- /dev/null +++ b/src/scheduling/validation/helpers.py @@ -0,0 +1,17 @@ +from collections.abc import Iterable + + +def ensure_unique[T](values: Iterable[T], field_name: str) -> frozenset[T]: + seen: set[T] = set() + duplicates: set[T] = set() + + for value in values: + if value in seen: + duplicates.add(value) + seen.add(value) + + if duplicates: + duplicate_values = ", ".join(sorted(str(value) for value in duplicates)) + raise ValueError(f"Duplicate {field_name} values: {duplicate_values}.") + + return frozenset(seen) diff --git a/src/scheduling/validation/validators.py b/src/scheduling/validation/validators.py new file mode 100644 index 00000000..4d7d6256 --- /dev/null +++ b/src/scheduling/validation/validators.py @@ -0,0 +1,266 @@ +from datetime import date as Date + +from scheduling.models import ( + AssignmentType, + AvailabilityType, + EmployeeId, + PlanId, + PlanningUnitId, + PlanningUnitKind, + ShiftId, + StaffingDemandRole, + StaffLevel, + WishKind, +) +from scheduling.models.dataset import SchedulingDataset +from scheduling.validation.context import DatasetValidationContext + + +def validate_plans(dataset: SchedulingDataset, context: DatasetValidationContext) -> None: + seen_planning_units: set[PlanningUnitId] = set() + + for plan in dataset.plans: + if plan.planning_unit_id not in context.planning_unit_ids: + raise ValueError(f"Plan references unknown planning_unit_id={plan.planning_unit_id}.") + + if plan.planning_unit_id in seen_planning_units: + raise ValueError(f"Multiple selected plans reference the same planning_unit_id={plan.planning_unit_id}.") + + seen_planning_units.add(plan.planning_unit_id) + + +def validate_plan_participants(dataset: SchedulingDataset, context: DatasetValidationContext) -> None: + seen: set[tuple[PlanId, EmployeeId]] = set() + + for participant in dataset.plan_participants: + if participant.plan_id not in context.plan_ids: + raise ValueError(f"PlanParticipant references unknown plan_id={participant.plan_id}.") + + if participant.planning_unit_id not in context.planning_unit_ids: + raise ValueError(f"PlanParticipant references unknown planning_unit_id={participant.planning_unit_id}.") + + if participant.employee_id not in context.employee_ids: + raise ValueError(f"PlanParticipant references unknown employee_id={participant.employee_id}.") + + plan = context.plans_by_id[participant.plan_id] + if participant.planning_unit_id != plan.planning_unit_id: + raise ValueError( + "PlanParticipant planning_unit_id does not match its Plan: " + f"plan_id={participant.plan_id} " + f"participant_planning_unit_id={participant.planning_unit_id} " + f"plan_planning_unit_id={plan.planning_unit_id}." + ) + + key = (participant.plan_id, participant.employee_id) + if key in seen: + raise ValueError( + f"Duplicate PlanParticipant plan_id={participant.plan_id} employee_id={participant.employee_id}." + ) + + seen.add(key) + + +def validate_planning_unit_memberships(dataset: SchedulingDataset, context: DatasetValidationContext) -> None: + seen: set[tuple[PlanningUnitId, EmployeeId, Date, Date | None]] = set() + + for membership in dataset.planning_unit_memberships: + if membership.planning_unit_id not in context.planning_unit_ids: + raise ValueError( + f"PlanningUnitMembership references unknown planning_unit_id={membership.planning_unit_id}." + ) + + if membership.employee_id not in context.employee_ids: + raise ValueError(f"PlanningUnitMembership references unknown employee_id={membership.employee_id}.") + + key = ( + membership.planning_unit_id, + membership.employee_id, + membership.valid_from, + membership.valid_until, + ) + if key in seen: + raise ValueError( + "Duplicate PlanningUnitMembership " + f"planning_unit_id={membership.planning_unit_id} " + f"employee_id={membership.employee_id} " + f"valid_from={membership.valid_from} " + f"valid_until={membership.valid_until}." + ) + + seen.add(key) + + +def validate_assignments(dataset: SchedulingDataset, context: DatasetValidationContext) -> None: + seen: set[tuple[EmployeeId, Date, ShiftId, AssignmentType, PlanningUnitId | None]] = set() + + for assignment in dataset.assignments: + if not dataset.period.contains(assignment.date): + raise ValueError( + f"Assignment outside planning period: employee_id={assignment.employee_id} date={assignment.date}." + ) + + if assignment.employee_id not in context.employee_ids: + raise ValueError(f"Assignment references unknown employee_id={assignment.employee_id}.") + + if assignment.shift_id not in context.shift_ids: + raise ValueError(f"Assignment references unknown shift_id={assignment.shift_id}.") + + # Assignment shape invariants belong in Assignment itself. + # Dataset validation only checks references. + if ( + assignment.assignment_type == AssignmentType.PLANNED + and assignment.planning_unit_id is not None + and assignment.planning_unit_id not in context.planning_unit_ids + ): + raise ValueError(f"Planned assignment references unknown planning_unit_id={assignment.planning_unit_id}.") + + key = ( + assignment.employee_id, + assignment.date, + assignment.shift_id, + assignment.assignment_type, + assignment.planning_unit_id, + ) + if key in seen: + raise ValueError( + f"Duplicate assignment employee_id={assignment.employee_id} " + f"date={assignment.date} shift_id={assignment.shift_id} " + f"type={assignment.assignment_type} " + f"planning_unit_id={assignment.planning_unit_id}." + ) + + seen.add(key) + + +def validate_availability(dataset: SchedulingDataset, context: DatasetValidationContext) -> None: + seen: set[tuple[EmployeeId, Date, AvailabilityType, tuple[ShiftId, ...] | None]] = set() + + for availability in dataset.availability: + if not dataset.period.contains(availability.date): + raise ValueError( + "Availability outside planning period: " + f"employee_id={availability.employee_id} date={availability.date}." + ) + + if availability.employee_id not in context.employee_ids: + raise ValueError(f"Availability references unknown employee_id={availability.employee_id}.") + + if availability.shift_ids is not None: + unknown_shift_ids = sorted(set(availability.shift_ids) - context.shift_ids) + if unknown_shift_ids: + raise ValueError(f"Availability references unknown shift_ids={unknown_shift_ids}.") + + key = ( + availability.employee_id, + availability.date, + availability.availability_type, + availability.shift_ids, + ) + if key in seen: + raise ValueError( + f"Duplicate availability employee_id={availability.employee_id} " + f"date={availability.date} " + f"type={availability.availability_type} " + f"shift_ids={availability.shift_ids}." + ) + + seen.add(key) + + +def validate_demand_requirements(dataset: SchedulingDataset, context: DatasetValidationContext) -> None: + seen: set[tuple[PlanningUnitId, Date, ShiftId, StaffLevel]] = set() + + for demand in dataset.demand_requirements: + if not dataset.period.contains(demand.date): + raise ValueError( + "DemandRequirement outside planning period: " + f"planning_unit_id={demand.planning_unit_id} date={demand.date}." + ) + + if demand.planning_unit_id not in context.planning_unit_ids: + raise ValueError(f"DemandRequirement references unknown planning_unit_id={demand.planning_unit_id}.") + + if context.planning_unit_kind_by_id[demand.planning_unit_id] != PlanningUnitKind.STATION: + raise ValueError( + f"DemandRequirement must target a station planning unit: planning_unit_id={demand.planning_unit_id}." + ) + + if demand.shift_id not in context.shift_ids: + raise ValueError(f"DemandRequirement references unknown shift_id={demand.shift_id}.") + + shift = context.shifts_by_id[demand.shift_id] + if shift.staffing_role != StaffingDemandRole.REQUIRED_MINIMUM: + raise ValueError(f"DemandRequirement must reference a REQUIRED_MINIMUM shift: shift_id={demand.shift_id}.") + + key = ( + demand.planning_unit_id, + demand.date, + demand.shift_id, + demand.staff_level, + ) + if key in seen: + raise ValueError( + "Duplicate DemandRequirement " + f"planning_unit_id={demand.planning_unit_id} " + f"date={demand.date} " + f"shift_id={demand.shift_id} " + f"staff_level={demand.staff_level}." + ) + + seen.add(key) + + +def validate_sunday_work_history(dataset: SchedulingDataset, context: DatasetValidationContext) -> None: + seen: set[EmployeeId] = set() + + for history in dataset.sunday_work_history: + if history.employee_id not in context.employee_ids: + raise ValueError(f"EmployeeSundayWorkHistory references unknown employee_id={history.employee_id}.") + + if history.employee_id in seen: + raise ValueError(f"Duplicate EmployeeSundayWorkHistory employee_id={history.employee_id}.") + + seen.add(history.employee_id) + + +def validate_wishes(dataset: SchedulingDataset, context: DatasetValidationContext) -> None: + seen: set[tuple[EmployeeId, PlanningUnitId, Date, WishKind, ShiftId | None]] = set() + + for wish in dataset.wishes: + if wish.employee_id not in context.employee_ids: + raise ValueError(f"Wish references unknown employee_id={wish.employee_id}.") + + if wish.planning_unit_id not in context.planning_unit_ids: + raise ValueError(f"Wish references unknown planning_unit_id={wish.planning_unit_id}.") + + if not dataset.period.contains(wish.date): + raise ValueError(f"Wish date outside planning period: employee_id={wish.employee_id}, date={wish.date}.") + + if wish.shift_id is not None and wish.shift_id not in context.shift_ids: + raise ValueError(f"Wish references unknown shift_id={wish.shift_id}.") + + key = ( + wish.employee_id, + wish.planning_unit_id, + wish.date, + wish.kind, + wish.shift_id, + ) + + if key in seen: + raise ValueError(f"Duplicate wish: {key}.") + + seen.add(key) + + +def validate_monthly_work_accounts(dataset: SchedulingDataset, context: DatasetValidationContext) -> None: + seen_employee_ids: set[EmployeeId] = set() + + for account in dataset.monthly_work_accounts: + if account.employee_id not in context.employee_ids: + raise ValueError(f"Monthly work account references unknown employee_id={account.employee_id}.") + + if account.employee_id in seen_employee_ids: + raise ValueError(f"Duplicate monthly work account for employee_id={account.employee_id}.") + + seen_employee_ids.add(account.employee_id) diff --git a/uv.lock b/uv.lock index 12c3f3db..804ce9bc 100644 --- a/uv.lock +++ b/uv.lock @@ -70,15 +70,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/41/ff/392bff89415399a979be4a65357a41d92729ae8580a66073d8ec8d810f98/backrefs-5.9-py39-none-any.whl", hash = "sha256:f48ee18f6252b8f5777a22a00a09a85de0ca931658f1dd96d4406a34f3748c60", size = 380265, upload-time = "2025-06-22T19:34:12.405Z" }, ] -[[package]] -name = "blinker" -version = "1.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460, upload-time = "2024-11-08T17:25:47.436Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458, upload-time = "2024-11-08T17:25:46.184Z" }, -] - [[package]] name = "bracex" version = "2.6" @@ -184,81 +175,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] -[[package]] -name = "contourpy" -version = "1.3.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174, upload-time = "2025-07-26T12:03:12.549Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/be/45/adfee365d9ea3d853550b2e735f9d66366701c65db7855cd07621732ccfc/contourpy-1.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b08a32ea2f8e42cf1d4be3169a98dd4be32bafe4f22b6c4cb4ba810fa9e5d2cb", size = 293419, upload-time = "2025-07-26T12:01:21.16Z" }, - { url = "https://files.pythonhosted.org/packages/53/3e/405b59cfa13021a56bba395a6b3aca8cec012b45bf177b0eaf7a202cde2c/contourpy-1.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:556dba8fb6f5d8742f2923fe9457dbdd51e1049c4a43fd3986a0b14a1d815fc6", size = 273979, upload-time = "2025-07-26T12:01:22.448Z" }, - { url = "https://files.pythonhosted.org/packages/d4/1c/a12359b9b2ca3a845e8f7f9ac08bdf776114eb931392fcad91743e2ea17b/contourpy-1.3.3-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92d9abc807cf7d0e047b95ca5d957cf4792fcd04e920ca70d48add15c1a90ea7", size = 332653, upload-time = "2025-07-26T12:01:24.155Z" }, - { url = "https://files.pythonhosted.org/packages/63/12/897aeebfb475b7748ea67b61e045accdfcf0d971f8a588b67108ed7f5512/contourpy-1.3.3-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b2e8faa0ed68cb29af51edd8e24798bb661eac3bd9f65420c1887b6ca89987c8", size = 379536, upload-time = "2025-07-26T12:01:25.91Z" }, - { url = "https://files.pythonhosted.org/packages/43/8a/a8c584b82deb248930ce069e71576fc09bd7174bbd35183b7943fb1064fd/contourpy-1.3.3-cp312-cp312-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:626d60935cf668e70a5ce6ff184fd713e9683fb458898e4249b63be9e28286ea", size = 384397, upload-time = "2025-07-26T12:01:27.152Z" }, - { url = "https://files.pythonhosted.org/packages/cc/8f/ec6289987824b29529d0dfda0d74a07cec60e54b9c92f3c9da4c0ac732de/contourpy-1.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d00e655fcef08aba35ec9610536bfe90267d7ab5ba944f7032549c55a146da1", size = 362601, upload-time = "2025-07-26T12:01:28.808Z" }, - { url = "https://files.pythonhosted.org/packages/05/0a/a3fe3be3ee2dceb3e615ebb4df97ae6f3828aa915d3e10549ce016302bd1/contourpy-1.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:451e71b5a7d597379ef572de31eeb909a87246974d960049a9848c3bc6c41bf7", size = 1331288, upload-time = "2025-07-26T12:01:31.198Z" }, - { url = "https://files.pythonhosted.org/packages/33/1d/acad9bd4e97f13f3e2b18a3977fe1b4a37ecf3d38d815333980c6c72e963/contourpy-1.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:459c1f020cd59fcfe6650180678a9993932d80d44ccde1fa1868977438f0b411", size = 1403386, upload-time = "2025-07-26T12:01:33.947Z" }, - { url = "https://files.pythonhosted.org/packages/cf/8f/5847f44a7fddf859704217a99a23a4f6417b10e5ab1256a179264561540e/contourpy-1.3.3-cp312-cp312-win32.whl", hash = "sha256:023b44101dfe49d7d53932be418477dba359649246075c996866106da069af69", size = 185018, upload-time = "2025-07-26T12:01:35.64Z" }, - { url = "https://files.pythonhosted.org/packages/19/e8/6026ed58a64563186a9ee3f29f41261fd1828f527dd93d33b60feca63352/contourpy-1.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:8153b8bfc11e1e4d75bcb0bff1db232f9e10b274e0929de9d608027e0d34ff8b", size = 226567, upload-time = "2025-07-26T12:01:36.804Z" }, - { url = "https://files.pythonhosted.org/packages/d1/e2/f05240d2c39a1ed228d8328a78b6f44cd695f7ef47beb3e684cf93604f86/contourpy-1.3.3-cp312-cp312-win_arm64.whl", hash = "sha256:07ce5ed73ecdc4a03ffe3e1b3e3c1166db35ae7584be76f65dbbe28a7791b0cc", size = 193655, upload-time = "2025-07-26T12:01:37.999Z" }, - { url = "https://files.pythonhosted.org/packages/68/35/0167aad910bbdb9599272bd96d01a9ec6852f36b9455cf2ca67bd4cc2d23/contourpy-1.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:177fb367556747a686509d6fef71d221a4b198a3905fe824430e5ea0fda54eb5", size = 293257, upload-time = "2025-07-26T12:01:39.367Z" }, - { url = "https://files.pythonhosted.org/packages/96/e4/7adcd9c8362745b2210728f209bfbcf7d91ba868a2c5f40d8b58f54c509b/contourpy-1.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d002b6f00d73d69333dac9d0b8d5e84d9724ff9ef044fd63c5986e62b7c9e1b1", size = 274034, upload-time = "2025-07-26T12:01:40.645Z" }, - { url = "https://files.pythonhosted.org/packages/73/23/90e31ceeed1de63058a02cb04b12f2de4b40e3bef5e082a7c18d9c8ae281/contourpy-1.3.3-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:348ac1f5d4f1d66d3322420f01d42e43122f43616e0f194fc1c9f5d830c5b286", size = 334672, upload-time = "2025-07-26T12:01:41.942Z" }, - { url = "https://files.pythonhosted.org/packages/ed/93/b43d8acbe67392e659e1d984700e79eb67e2acb2bd7f62012b583a7f1b55/contourpy-1.3.3-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:655456777ff65c2c548b7c454af9c6f33f16c8884f11083244b5819cc214f1b5", size = 381234, upload-time = "2025-07-26T12:01:43.499Z" }, - { url = "https://files.pythonhosted.org/packages/46/3b/bec82a3ea06f66711520f75a40c8fc0b113b2a75edb36aa633eb11c4f50f/contourpy-1.3.3-cp313-cp313-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:644a6853d15b2512d67881586bd03f462c7ab755db95f16f14d7e238f2852c67", size = 385169, upload-time = "2025-07-26T12:01:45.219Z" }, - { url = "https://files.pythonhosted.org/packages/4b/32/e0f13a1c5b0f8572d0ec6ae2f6c677b7991fafd95da523159c19eff0696a/contourpy-1.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4debd64f124ca62069f313a9cb86656ff087786016d76927ae2cf37846b006c9", size = 362859, upload-time = "2025-07-26T12:01:46.519Z" }, - { url = "https://files.pythonhosted.org/packages/33/71/e2a7945b7de4e58af42d708a219f3b2f4cff7386e6b6ab0a0fa0033c49a9/contourpy-1.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a15459b0f4615b00bbd1e91f1b9e19b7e63aea7483d03d804186f278c0af2659", size = 1332062, upload-time = "2025-07-26T12:01:48.964Z" }, - { url = "https://files.pythonhosted.org/packages/12/fc/4e87ac754220ccc0e807284f88e943d6d43b43843614f0a8afa469801db0/contourpy-1.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ca0fdcd73925568ca027e0b17ab07aad764be4706d0a925b89227e447d9737b7", size = 1403932, upload-time = "2025-07-26T12:01:51.979Z" }, - { url = "https://files.pythonhosted.org/packages/a6/2e/adc197a37443f934594112222ac1aa7dc9a98faf9c3842884df9a9d8751d/contourpy-1.3.3-cp313-cp313-win32.whl", hash = "sha256:b20c7c9a3bf701366556e1b1984ed2d0cedf999903c51311417cf5f591d8c78d", size = 185024, upload-time = "2025-07-26T12:01:53.245Z" }, - { url = "https://files.pythonhosted.org/packages/18/0b/0098c214843213759692cc638fce7de5c289200a830e5035d1791d7a2338/contourpy-1.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:1cadd8b8969f060ba45ed7c1b714fe69185812ab43bd6b86a9123fe8f99c3263", size = 226578, upload-time = "2025-07-26T12:01:54.422Z" }, - { url = "https://files.pythonhosted.org/packages/8a/9a/2f6024a0c5995243cd63afdeb3651c984f0d2bc727fd98066d40e141ad73/contourpy-1.3.3-cp313-cp313-win_arm64.whl", hash = "sha256:fd914713266421b7536de2bfa8181aa8c699432b6763a0ea64195ebe28bff6a9", size = 193524, upload-time = "2025-07-26T12:01:55.73Z" }, - { url = "https://files.pythonhosted.org/packages/c0/b3/f8a1a86bd3298513f500e5b1f5fd92b69896449f6cab6a146a5d52715479/contourpy-1.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:88df9880d507169449d434c293467418b9f6cbe82edd19284aa0409e7fdb933d", size = 306730, upload-time = "2025-07-26T12:01:57.051Z" }, - { url = "https://files.pythonhosted.org/packages/3f/11/4780db94ae62fc0c2053909b65dc3246bd7cecfc4f8a20d957ad43aa4ad8/contourpy-1.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d06bb1f751ba5d417047db62bca3c8fde202b8c11fb50742ab3ab962c81e8216", size = 287897, upload-time = "2025-07-26T12:01:58.663Z" }, - { url = "https://files.pythonhosted.org/packages/ae/15/e59f5f3ffdd6f3d4daa3e47114c53daabcb18574a26c21f03dc9e4e42ff0/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e4e6b05a45525357e382909a4c1600444e2a45b4795163d3b22669285591c1ae", size = 326751, upload-time = "2025-07-26T12:02:00.343Z" }, - { url = "https://files.pythonhosted.org/packages/0f/81/03b45cfad088e4770b1dcf72ea78d3802d04200009fb364d18a493857210/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ab3074b48c4e2cf1a960e6bbeb7f04566bf36b1861d5c9d4d8ac04b82e38ba20", size = 375486, upload-time = "2025-07-26T12:02:02.128Z" }, - { url = "https://files.pythonhosted.org/packages/0c/ba/49923366492ffbdd4486e970d421b289a670ae8cf539c1ea9a09822b371a/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c3d53c796f8647d6deb1abe867daeb66dcc8a97e8455efa729516b997b8ed99", size = 388106, upload-time = "2025-07-26T12:02:03.615Z" }, - { url = "https://files.pythonhosted.org/packages/9f/52/5b00ea89525f8f143651f9f03a0df371d3cbd2fccd21ca9b768c7a6500c2/contourpy-1.3.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50ed930df7289ff2a8d7afeb9603f8289e5704755c7e5c3bbd929c90c817164b", size = 352548, upload-time = "2025-07-26T12:02:05.165Z" }, - { url = "https://files.pythonhosted.org/packages/32/1d/a209ec1a3a3452d490f6b14dd92e72280c99ae3d1e73da74f8277d4ee08f/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4feffb6537d64b84877da813a5c30f1422ea5739566abf0bd18065ac040e120a", size = 1322297, upload-time = "2025-07-26T12:02:07.379Z" }, - { url = "https://files.pythonhosted.org/packages/bc/9e/46f0e8ebdd884ca0e8877e46a3f4e633f6c9c8c4f3f6e72be3fe075994aa/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2b7e9480ffe2b0cd2e787e4df64270e3a0440d9db8dc823312e2c940c167df7e", size = 1391023, upload-time = "2025-07-26T12:02:10.171Z" }, - { url = "https://files.pythonhosted.org/packages/b9/70/f308384a3ae9cd2209e0849f33c913f658d3326900d0ff5d378d6a1422d2/contourpy-1.3.3-cp313-cp313t-win32.whl", hash = "sha256:283edd842a01e3dcd435b1c5116798d661378d83d36d337b8dde1d16a5fc9ba3", size = 196157, upload-time = "2025-07-26T12:02:11.488Z" }, - { url = "https://files.pythonhosted.org/packages/b2/dd/880f890a6663b84d9e34a6f88cded89d78f0091e0045a284427cb6b18521/contourpy-1.3.3-cp313-cp313t-win_amd64.whl", hash = "sha256:87acf5963fc2b34825e5b6b048f40e3635dd547f590b04d2ab317c2619ef7ae8", size = 240570, upload-time = "2025-07-26T12:02:12.754Z" }, - { url = "https://files.pythonhosted.org/packages/80/99/2adc7d8ffead633234817ef8e9a87115c8a11927a94478f6bb3d3f4d4f7d/contourpy-1.3.3-cp313-cp313t-win_arm64.whl", hash = "sha256:3c30273eb2a55024ff31ba7d052dde990d7d8e5450f4bbb6e913558b3d6c2301", size = 199713, upload-time = "2025-07-26T12:02:14.4Z" }, - { url = "https://files.pythonhosted.org/packages/72/8b/4546f3ab60f78c514ffb7d01a0bd743f90de36f0019d1be84d0a708a580a/contourpy-1.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fde6c716d51c04b1c25d0b90364d0be954624a0ee9d60e23e850e8d48353d07a", size = 292189, upload-time = "2025-07-26T12:02:16.095Z" }, - { url = "https://files.pythonhosted.org/packages/fd/e1/3542a9cb596cadd76fcef413f19c79216e002623158befe6daa03dbfa88c/contourpy-1.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:cbedb772ed74ff5be440fa8eee9bd49f64f6e3fc09436d9c7d8f1c287b121d77", size = 273251, upload-time = "2025-07-26T12:02:17.524Z" }, - { url = "https://files.pythonhosted.org/packages/b1/71/f93e1e9471d189f79d0ce2497007731c1e6bf9ef6d1d61b911430c3db4e5/contourpy-1.3.3-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:22e9b1bd7a9b1d652cd77388465dc358dafcd2e217d35552424aa4f996f524f5", size = 335810, upload-time = "2025-07-26T12:02:18.9Z" }, - { url = "https://files.pythonhosted.org/packages/91/f9/e35f4c1c93f9275d4e38681a80506b5510e9327350c51f8d4a5a724d178c/contourpy-1.3.3-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a22738912262aa3e254e4f3cb079a95a67132fc5a063890e224393596902f5a4", size = 382871, upload-time = "2025-07-26T12:02:20.418Z" }, - { url = "https://files.pythonhosted.org/packages/b5/71/47b512f936f66a0a900d81c396a7e60d73419868fba959c61efed7a8ab46/contourpy-1.3.3-cp314-cp314-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:afe5a512f31ee6bd7d0dda52ec9864c984ca3d66664444f2d72e0dc4eb832e36", size = 386264, upload-time = "2025-07-26T12:02:21.916Z" }, - { url = "https://files.pythonhosted.org/packages/04/5f/9ff93450ba96b09c7c2b3f81c94de31c89f92292f1380261bd7195bea4ea/contourpy-1.3.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f64836de09927cba6f79dcd00fdd7d5329f3fccc633468507079c829ca4db4e3", size = 363819, upload-time = "2025-07-26T12:02:23.759Z" }, - { url = "https://files.pythonhosted.org/packages/3e/a6/0b185d4cc480ee494945cde102cb0149ae830b5fa17bf855b95f2e70ad13/contourpy-1.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1fd43c3be4c8e5fd6e4f2baeae35ae18176cf2e5cced681cca908addf1cdd53b", size = 1333650, upload-time = "2025-07-26T12:02:26.181Z" }, - { url = "https://files.pythonhosted.org/packages/43/d7/afdc95580ca56f30fbcd3060250f66cedbde69b4547028863abd8aa3b47e/contourpy-1.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6afc576f7b33cf00996e5c1102dc2a8f7cc89e39c0b55df93a0b78c1bd992b36", size = 1404833, upload-time = "2025-07-26T12:02:28.782Z" }, - { url = "https://files.pythonhosted.org/packages/e2/e2/366af18a6d386f41132a48f033cbd2102e9b0cf6345d35ff0826cd984566/contourpy-1.3.3-cp314-cp314-win32.whl", hash = "sha256:66c8a43a4f7b8df8b71ee1840e4211a3c8d93b214b213f590e18a1beca458f7d", size = 189692, upload-time = "2025-07-26T12:02:30.128Z" }, - { url = "https://files.pythonhosted.org/packages/7d/c2/57f54b03d0f22d4044b8afb9ca0e184f8b1afd57b4f735c2fa70883dc601/contourpy-1.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:cf9022ef053f2694e31d630feaacb21ea24224be1c3ad0520b13d844274614fd", size = 232424, upload-time = "2025-07-26T12:02:31.395Z" }, - { url = "https://files.pythonhosted.org/packages/18/79/a9416650df9b525737ab521aa181ccc42d56016d2123ddcb7b58e926a42c/contourpy-1.3.3-cp314-cp314-win_arm64.whl", hash = "sha256:95b181891b4c71de4bb404c6621e7e2390745f887f2a026b2d99e92c17892339", size = 198300, upload-time = "2025-07-26T12:02:32.956Z" }, - { url = "https://files.pythonhosted.org/packages/1f/42/38c159a7d0f2b7b9c04c64ab317042bb6952b713ba875c1681529a2932fe/contourpy-1.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:33c82d0138c0a062380332c861387650c82e4cf1747aaa6938b9b6516762e772", size = 306769, upload-time = "2025-07-26T12:02:34.2Z" }, - { url = "https://files.pythonhosted.org/packages/c3/6c/26a8205f24bca10974e77460de68d3d7c63e282e23782f1239f226fcae6f/contourpy-1.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ea37e7b45949df430fe649e5de8351c423430046a2af20b1c1961cae3afcda77", size = 287892, upload-time = "2025-07-26T12:02:35.807Z" }, - { url = "https://files.pythonhosted.org/packages/66/06/8a475c8ab718ebfd7925661747dbb3c3ee9c82ac834ccb3570be49d129f4/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d304906ecc71672e9c89e87c4675dc5c2645e1f4269a5063b99b0bb29f232d13", size = 326748, upload-time = "2025-07-26T12:02:37.193Z" }, - { url = "https://files.pythonhosted.org/packages/b4/a3/c5ca9f010a44c223f098fccd8b158bb1cb287378a31ac141f04730dc49be/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca658cd1a680a5c9ea96dc61cdbae1e85c8f25849843aa799dfd3cb370ad4fbe", size = 375554, upload-time = "2025-07-26T12:02:38.894Z" }, - { url = "https://files.pythonhosted.org/packages/80/5b/68bd33ae63fac658a4145088c1e894405e07584a316738710b636c6d0333/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ab2fd90904c503739a75b7c8c5c01160130ba67944a7b77bbf36ef8054576e7f", size = 388118, upload-time = "2025-07-26T12:02:40.642Z" }, - { url = "https://files.pythonhosted.org/packages/40/52/4c285a6435940ae25d7410a6c36bda5145839bc3f0beb20c707cda18b9d2/contourpy-1.3.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7301b89040075c30e5768810bc96a8e8d78085b47d8be6e4c3f5a0b4ed478a0", size = 352555, upload-time = "2025-07-26T12:02:42.25Z" }, - { url = "https://files.pythonhosted.org/packages/24/ee/3e81e1dd174f5c7fefe50e85d0892de05ca4e26ef1c9a59c2a57e43b865a/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2a2a8b627d5cc6b7c41a4beff6c5ad5eb848c88255fda4a8745f7e901b32d8e4", size = 1322295, upload-time = "2025-07-26T12:02:44.668Z" }, - { url = "https://files.pythonhosted.org/packages/3c/b2/6d913d4d04e14379de429057cd169e5e00f6c2af3bb13e1710bcbdb5da12/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fd6ec6be509c787f1caf6b247f0b1ca598bef13f4ddeaa126b7658215529ba0f", size = 1391027, upload-time = "2025-07-26T12:02:47.09Z" }, - { url = "https://files.pythonhosted.org/packages/93/8a/68a4ec5c55a2971213d29a9374913f7e9f18581945a7a31d1a39b5d2dfe5/contourpy-1.3.3-cp314-cp314t-win32.whl", hash = "sha256:e74a9a0f5e3fff48fb5a7f2fd2b9b70a3fe014a67522f79b7cca4c0c7e43c9ae", size = 202428, upload-time = "2025-07-26T12:02:48.691Z" }, - { url = "https://files.pythonhosted.org/packages/fa/96/fd9f641ffedc4fa3ace923af73b9d07e869496c9cc7a459103e6e978992f/contourpy-1.3.3-cp314-cp314t-win_amd64.whl", hash = "sha256:13b68d6a62db8eafaebb8039218921399baf6e47bf85006fd8529f2a08ef33fc", size = 250331, upload-time = "2025-07-26T12:02:50.137Z" }, - { url = "https://files.pythonhosted.org/packages/ae/8c/469afb6465b853afff216f9528ffda78a915ff880ed58813ba4faf4ba0b6/contourpy-1.3.3-cp314-cp314t-win_arm64.whl", hash = "sha256:b7448cb5a725bb1e35ce88771b86fba35ef418952474492cf7c764059933ff8b", size = 203831, upload-time = "2025-07-26T12:02:51.449Z" }, -] - -[[package]] -name = "cycler" -version = "0.12.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615, upload-time = "2023-10-07T05:32:18.335Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload-time = "2023-10-07T05:32:16.783Z" }, -] - [[package]] name = "detect-installer" version = "0.1.0" @@ -286,17 +202,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094, upload-time = "2025-09-07T18:57:58.071Z" }, ] -[[package]] -name = "dotenv" -version = "0.9.9" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "python-dotenv" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/b7/545d2c10c1fc15e48653c91efde329a790f2eecfbbf2bd16003b5db2bab0/dotenv-0.9.9-py2.py3-none-any.whl", hash = "sha256:29cf74a087b31dafdb5a446b6d7e11cbce8ed2741540e2339c69fbef92c94ce9", size = 1892, upload-time = "2025-02-19T22:15:01.647Z" }, -] - [[package]] name = "email-validator" version = "2.3.0" @@ -460,64 +365,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/76/91/7216b27286936c16f5b4d0c530087e4a54eead683e6b0b73dd0c64844af6/filelock-3.20.0-py3-none-any.whl", hash = "sha256:339b4732ffda5cd79b13f4e2711a31b0365ce445d95d243bb996273d072546a2", size = 16054, upload-time = "2025-10-08T18:03:48.35Z" }, ] -[[package]] -name = "flask" -version = "3.1.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "blinker" }, - { name = "click" }, - { name = "itsdangerous" }, - { name = "jinja2" }, - { name = "markupsafe" }, - { name = "werkzeug" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/dc/6d/cfe3c0fcc5e477df242b98bfe186a4c34357b4847e87ecaef04507332dab/flask-3.1.2.tar.gz", hash = "sha256:bf656c15c80190ed628ad08cdfd3aaa35beb087855e2f494910aa3774cc4fd87", size = 720160, upload-time = "2025-08-19T21:03:21.205Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/f9/7f9263c5695f4bd0023734af91bedb2ff8209e8de6ead162f35d8dc762fd/flask-3.1.2-py3-none-any.whl", hash = "sha256:ca1d8112ec8a6158cc29ea4858963350011b5c846a414cdb7a954aa9e967d03c", size = 103308, upload-time = "2025-08-19T21:03:19.499Z" }, -] - -[[package]] -name = "fonttools" -version = "4.60.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4b/42/97a13e47a1e51a5a7142475bbcf5107fe3a68fc34aef331c897d5fb98ad0/fonttools-4.60.1.tar.gz", hash = "sha256:ef00af0439ebfee806b25f24c8f92109157ff3fac5731dc7867957812e87b8d9", size = 3559823, upload-time = "2025-09-29T21:13:27.129Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e3/f7/a10b101b7a6f8836a5adb47f2791f2075d044a6ca123f35985c42edc82d8/fonttools-4.60.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:7b0c6d57ab00dae9529f3faf187f2254ea0aa1e04215cf2f1a8ec277c96661bc", size = 2832953, upload-time = "2025-09-29T21:11:39.616Z" }, - { url = "https://files.pythonhosted.org/packages/ed/fe/7bd094b59c926acf2304d2151354ddbeb74b94812f3dc943c231db09cb41/fonttools-4.60.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:839565cbf14645952d933853e8ade66a463684ed6ed6c9345d0faf1f0e868877", size = 2352706, upload-time = "2025-09-29T21:11:41.826Z" }, - { url = "https://files.pythonhosted.org/packages/c0/ca/4bb48a26ed95a1e7eba175535fe5805887682140ee0a0d10a88e1de84208/fonttools-4.60.1-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8177ec9676ea6e1793c8a084a90b65a9f778771998eb919d05db6d4b1c0b114c", size = 4923716, upload-time = "2025-09-29T21:11:43.893Z" }, - { url = "https://files.pythonhosted.org/packages/b8/9f/2cb82999f686c1d1ddf06f6ae1a9117a880adbec113611cc9d22b2fdd465/fonttools-4.60.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:996a4d1834524adbb423385d5a629b868ef9d774670856c63c9a0408a3063401", size = 4968175, upload-time = "2025-09-29T21:11:46.439Z" }, - { url = "https://files.pythonhosted.org/packages/18/79/be569699e37d166b78e6218f2cde8c550204f2505038cdd83b42edc469b9/fonttools-4.60.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a46b2f450bc79e06ef3b6394f0c68660529ed51692606ad7f953fc2e448bc903", size = 4911031, upload-time = "2025-09-29T21:11:48.977Z" }, - { url = "https://files.pythonhosted.org/packages/cc/9f/89411cc116effaec5260ad519162f64f9c150e5522a27cbb05eb62d0c05b/fonttools-4.60.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6ec722ee589e89a89f5b7574f5c45604030aa6ae24cb2c751e2707193b466fed", size = 5062966, upload-time = "2025-09-29T21:11:54.344Z" }, - { url = "https://files.pythonhosted.org/packages/62/a1/f888221934b5731d46cb9991c7a71f30cb1f97c0ef5fcf37f8da8fce6c8e/fonttools-4.60.1-cp312-cp312-win32.whl", hash = "sha256:b2cf105cee600d2de04ca3cfa1f74f1127f8455b71dbad02b9da6ec266e116d6", size = 2218750, upload-time = "2025-09-29T21:11:56.601Z" }, - { url = "https://files.pythonhosted.org/packages/88/8f/a55b5550cd33cd1028601df41acd057d4be20efa5c958f417b0c0613924d/fonttools-4.60.1-cp312-cp312-win_amd64.whl", hash = "sha256:992775c9fbe2cf794786fa0ffca7f09f564ba3499b8fe9f2f80bd7197db60383", size = 2267026, upload-time = "2025-09-29T21:11:58.852Z" }, - { url = "https://files.pythonhosted.org/packages/7c/5b/cdd2c612277b7ac7ec8c0c9bc41812c43dc7b2d5f2b0897e15fdf5a1f915/fonttools-4.60.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6f68576bb4bbf6060c7ab047b1574a1ebe5c50a17de62830079967b211059ebb", size = 2825777, upload-time = "2025-09-29T21:12:01.22Z" }, - { url = "https://files.pythonhosted.org/packages/d6/8a/de9cc0540f542963ba5e8f3a1f6ad48fa211badc3177783b9d5cadf79b5d/fonttools-4.60.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:eedacb5c5d22b7097482fa834bda0dafa3d914a4e829ec83cdea2a01f8c813c4", size = 2348080, upload-time = "2025-09-29T21:12:03.785Z" }, - { url = "https://files.pythonhosted.org/packages/2d/8b/371ab3cec97ee3fe1126b3406b7abd60c8fec8975fd79a3c75cdea0c3d83/fonttools-4.60.1-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b33a7884fabd72bdf5f910d0cf46be50dce86a0362a65cfc746a4168c67eb96c", size = 4903082, upload-time = "2025-09-29T21:12:06.382Z" }, - { url = "https://files.pythonhosted.org/packages/04/05/06b1455e4bc653fcb2117ac3ef5fa3a8a14919b93c60742d04440605d058/fonttools-4.60.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2409d5fb7b55fd70f715e6d34e7a6e4f7511b8ad29a49d6df225ee76da76dd77", size = 4960125, upload-time = "2025-09-29T21:12:09.314Z" }, - { url = "https://files.pythonhosted.org/packages/8e/37/f3b840fcb2666f6cb97038793606bdd83488dca2d0b0fc542ccc20afa668/fonttools-4.60.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c8651e0d4b3bdeda6602b85fdc2abbefc1b41e573ecb37b6779c4ca50753a199", size = 4901454, upload-time = "2025-09-29T21:12:11.931Z" }, - { url = "https://files.pythonhosted.org/packages/fd/9e/eb76f77e82f8d4a46420aadff12cec6237751b0fb9ef1de373186dcffb5f/fonttools-4.60.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:145daa14bf24824b677b9357c5e44fd8895c2a8f53596e1b9ea3496081dc692c", size = 5044495, upload-time = "2025-09-29T21:12:15.241Z" }, - { url = "https://files.pythonhosted.org/packages/f8/b3/cede8f8235d42ff7ae891bae8d619d02c8ac9fd0cfc450c5927a6200c70d/fonttools-4.60.1-cp313-cp313-win32.whl", hash = "sha256:2299df884c11162617a66b7c316957d74a18e3758c0274762d2cc87df7bc0272", size = 2217028, upload-time = "2025-09-29T21:12:17.96Z" }, - { url = "https://files.pythonhosted.org/packages/75/4d/b022c1577807ce8b31ffe055306ec13a866f2337ecee96e75b24b9b753ea/fonttools-4.60.1-cp313-cp313-win_amd64.whl", hash = "sha256:a3db56f153bd4c5c2b619ab02c5db5192e222150ce5a1bc10f16164714bc39ac", size = 2266200, upload-time = "2025-09-29T21:12:20.14Z" }, - { url = "https://files.pythonhosted.org/packages/9a/83/752ca11c1aa9a899b793a130f2e466b79ea0cf7279c8d79c178fc954a07b/fonttools-4.60.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:a884aef09d45ba1206712c7dbda5829562d3fea7726935d3289d343232ecb0d3", size = 2822830, upload-time = "2025-09-29T21:12:24.406Z" }, - { url = "https://files.pythonhosted.org/packages/57/17/bbeab391100331950a96ce55cfbbff27d781c1b85ebafb4167eae50d9fe3/fonttools-4.60.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8a44788d9d91df72d1a5eac49b31aeb887a5f4aab761b4cffc4196c74907ea85", size = 2345524, upload-time = "2025-09-29T21:12:26.819Z" }, - { url = "https://files.pythonhosted.org/packages/3d/2e/d4831caa96d85a84dd0da1d9f90d81cec081f551e0ea216df684092c6c97/fonttools-4.60.1-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e852d9dda9f93ad3651ae1e3bb770eac544ec93c3807888798eccddf84596537", size = 4843490, upload-time = "2025-09-29T21:12:29.123Z" }, - { url = "https://files.pythonhosted.org/packages/49/13/5e2ea7c7a101b6fc3941be65307ef8df92cbbfa6ec4804032baf1893b434/fonttools-4.60.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:154cb6ee417e417bf5f7c42fe25858c9140c26f647c7347c06f0cc2d47eff003", size = 4944184, upload-time = "2025-09-29T21:12:31.414Z" }, - { url = "https://files.pythonhosted.org/packages/0c/2b/cf9603551c525b73fc47c52ee0b82a891579a93d9651ed694e4e2cd08bb8/fonttools-4.60.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:5664fd1a9ea7f244487ac8f10340c4e37664675e8667d6fee420766e0fb3cf08", size = 4890218, upload-time = "2025-09-29T21:12:33.936Z" }, - { url = "https://files.pythonhosted.org/packages/fd/2f/933d2352422e25f2376aae74f79eaa882a50fb3bfef3c0d4f50501267101/fonttools-4.60.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:583b7f8e3c49486e4d489ad1deacfb8d5be54a8ef34d6df824f6a171f8511d99", size = 4999324, upload-time = "2025-09-29T21:12:36.637Z" }, - { url = "https://files.pythonhosted.org/packages/38/99/234594c0391221f66216bc2c886923513b3399a148defaccf81dc3be6560/fonttools-4.60.1-cp314-cp314-win32.whl", hash = "sha256:66929e2ea2810c6533a5184f938502cfdaea4bc3efb7130d8cc02e1c1b4108d6", size = 2220861, upload-time = "2025-09-29T21:12:39.108Z" }, - { url = "https://files.pythonhosted.org/packages/3e/1d/edb5b23726dde50fc4068e1493e4fc7658eeefcaf75d4c5ffce067d07ae5/fonttools-4.60.1-cp314-cp314-win_amd64.whl", hash = "sha256:f3d5be054c461d6a2268831f04091dc82753176f6ea06dc6047a5e168265a987", size = 2270934, upload-time = "2025-09-29T21:12:41.339Z" }, - { url = "https://files.pythonhosted.org/packages/fb/da/1392aaa2170adc7071fe7f9cfd181a5684a7afcde605aebddf1fb4d76df5/fonttools-4.60.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:b6379e7546ba4ae4b18f8ae2b9bc5960936007a1c0e30b342f662577e8bc3299", size = 2894340, upload-time = "2025-09-29T21:12:43.774Z" }, - { url = "https://files.pythonhosted.org/packages/bf/a7/3b9f16e010d536ce567058b931a20b590d8f3177b2eda09edd92e392375d/fonttools-4.60.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9d0ced62b59e0430b3690dbc5373df1c2aa7585e9a8ce38eff87f0fd993c5b01", size = 2375073, upload-time = "2025-09-29T21:12:46.437Z" }, - { url = "https://files.pythonhosted.org/packages/9b/b5/e9bcf51980f98e59bb5bb7c382a63c6f6cac0eec5f67de6d8f2322382065/fonttools-4.60.1-cp314-cp314t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:875cb7764708b3132637f6c5fb385b16eeba0f7ac9fa45a69d35e09b47045801", size = 4849758, upload-time = "2025-09-29T21:12:48.694Z" }, - { url = "https://files.pythonhosted.org/packages/e3/dc/1d2cf7d1cba82264b2f8385db3f5960e3d8ce756b4dc65b700d2c496f7e9/fonttools-4.60.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a184b2ea57b13680ab6d5fbde99ccef152c95c06746cb7718c583abd8f945ccc", size = 5085598, upload-time = "2025-09-29T21:12:51.081Z" }, - { url = "https://files.pythonhosted.org/packages/5d/4d/279e28ba87fb20e0c69baf72b60bbf1c4d873af1476806a7b5f2b7fac1ff/fonttools-4.60.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:026290e4ec76583881763fac284aca67365e0be9f13a7fb137257096114cb3bc", size = 4957603, upload-time = "2025-09-29T21:12:53.423Z" }, - { url = "https://files.pythonhosted.org/packages/78/d4/ff19976305e0c05aa3340c805475abb00224c954d3c65e82c0a69633d55d/fonttools-4.60.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f0e8817c7d1a0c2eedebf57ef9a9896f3ea23324769a9a2061a80fe8852705ed", size = 4974184, upload-time = "2025-09-29T21:12:55.962Z" }, - { url = "https://files.pythonhosted.org/packages/63/22/8553ff6166f5cd21cfaa115aaacaa0dc73b91c079a8cfd54a482cbc0f4f5/fonttools-4.60.1-cp314-cp314t-win32.whl", hash = "sha256:1410155d0e764a4615774e5c2c6fc516259fe3eca5882f034eb9bfdbee056259", size = 2282241, upload-time = "2025-09-29T21:12:58.179Z" }, - { url = "https://files.pythonhosted.org/packages/8a/cb/fa7b4d148e11d5a72761a22e595344133e83a9507a4c231df972e657579b/fonttools-4.60.1-cp314-cp314t-win_amd64.whl", hash = "sha256:022beaea4b73a70295b688f817ddc24ed3e3418b5036ffcd5658141184ef0d0c", size = 2345760, upload-time = "2025-09-29T21:13:00.375Z" }, - { url = "https://files.pythonhosted.org/packages/c7/93/0dd45cd283c32dea1545151d8c3637b4b8c53cdb3a625aeb2885b184d74d/fonttools-4.60.1-py3-none-any.whl", hash = "sha256:906306ac7afe2156fcf0042173d6ebbb05416af70f6b370967b47f8f00103bbb", size = 1143175, upload-time = "2025-09-29T21:13:24.134Z" }, -] - [[package]] name = "ghp-import" version = "2.1.0" @@ -687,15 +534,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, ] -[[package]] -name = "itsdangerous" -version = "2.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9c/cb/8ac0172223afbccb63986cc25049b154ecfb5e85932587206f42317be31d/itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173", size = 54410, upload-time = "2024-04-16T21:28:15.614Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", size = 16234, upload-time = "2024-04-16T21:28:14.499Z" }, -] - [[package]] name = "jinja2" version = "3.1.6" @@ -708,78 +546,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, ] -[[package]] -name = "kiwisolver" -version = "1.4.9" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5c/3c/85844f1b0feb11ee581ac23fe5fce65cd049a200c1446708cc1b7f922875/kiwisolver-1.4.9.tar.gz", hash = "sha256:c3b22c26c6fd6811b0ae8363b95ca8ce4ea3c202d3d0975b2914310ceb1bcc4d", size = 97564, upload-time = "2025-08-10T21:27:49.279Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/86/c9/13573a747838aeb1c76e3267620daa054f4152444d1f3d1a2324b78255b5/kiwisolver-1.4.9-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ac5a486ac389dddcc5bef4f365b6ae3ffff2c433324fb38dd35e3fab7c957999", size = 123686, upload-time = "2025-08-10T21:26:10.034Z" }, - { url = "https://files.pythonhosted.org/packages/51/ea/2ecf727927f103ffd1739271ca19c424d0e65ea473fbaeea1c014aea93f6/kiwisolver-1.4.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f2ba92255faa7309d06fe44c3a4a97efe1c8d640c2a79a5ef728b685762a6fd2", size = 66460, upload-time = "2025-08-10T21:26:11.083Z" }, - { url = "https://files.pythonhosted.org/packages/5b/5a/51f5464373ce2aeb5194508298a508b6f21d3867f499556263c64c621914/kiwisolver-1.4.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a2899935e724dd1074cb568ce7ac0dce28b2cd6ab539c8e001a8578eb106d14", size = 64952, upload-time = "2025-08-10T21:26:12.058Z" }, - { url = "https://files.pythonhosted.org/packages/70/90/6d240beb0f24b74371762873e9b7f499f1e02166a2d9c5801f4dbf8fa12e/kiwisolver-1.4.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f6008a4919fdbc0b0097089f67a1eb55d950ed7e90ce2cc3e640abadd2757a04", size = 1474756, upload-time = "2025-08-10T21:26:13.096Z" }, - { url = "https://files.pythonhosted.org/packages/12/42/f36816eaf465220f683fb711efdd1bbf7a7005a2473d0e4ed421389bd26c/kiwisolver-1.4.9-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:67bb8b474b4181770f926f7b7d2f8c0248cbcb78b660fdd41a47054b28d2a752", size = 1276404, upload-time = "2025-08-10T21:26:14.457Z" }, - { url = "https://files.pythonhosted.org/packages/2e/64/bc2de94800adc830c476dce44e9b40fd0809cddeef1fde9fcf0f73da301f/kiwisolver-1.4.9-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2327a4a30d3ee07d2fbe2e7933e8a37c591663b96ce42a00bc67461a87d7df77", size = 1294410, upload-time = "2025-08-10T21:26:15.73Z" }, - { url = "https://files.pythonhosted.org/packages/5f/42/2dc82330a70aa8e55b6d395b11018045e58d0bb00834502bf11509f79091/kiwisolver-1.4.9-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7a08b491ec91b1d5053ac177afe5290adacf1f0f6307d771ccac5de30592d198", size = 1343631, upload-time = "2025-08-10T21:26:17.045Z" }, - { url = "https://files.pythonhosted.org/packages/22/fd/f4c67a6ed1aab149ec5a8a401c323cee7a1cbe364381bb6c9c0d564e0e20/kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d8fc5c867c22b828001b6a38d2eaeb88160bf5783c6cb4a5e440efc981ce286d", size = 2224963, upload-time = "2025-08-10T21:26:18.737Z" }, - { url = "https://files.pythonhosted.org/packages/45/aa/76720bd4cb3713314677d9ec94dcc21ced3f1baf4830adde5bb9b2430a5f/kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:3b3115b2581ea35bb6d1f24a4c90af37e5d9b49dcff267eeed14c3893c5b86ab", size = 2321295, upload-time = "2025-08-10T21:26:20.11Z" }, - { url = "https://files.pythonhosted.org/packages/80/19/d3ec0d9ab711242f56ae0dc2fc5d70e298bb4a1f9dfab44c027668c673a1/kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:858e4c22fb075920b96a291928cb7dea5644e94c0ee4fcd5af7e865655e4ccf2", size = 2487987, upload-time = "2025-08-10T21:26:21.49Z" }, - { url = "https://files.pythonhosted.org/packages/39/e9/61e4813b2c97e86b6fdbd4dd824bf72d28bcd8d4849b8084a357bc0dd64d/kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ed0fecd28cc62c54b262e3736f8bb2512d8dcfdc2bcf08be5f47f96bf405b145", size = 2291817, upload-time = "2025-08-10T21:26:22.812Z" }, - { url = "https://files.pythonhosted.org/packages/a0/41/85d82b0291db7504da3c2defe35c9a8a5c9803a730f297bd823d11d5fb77/kiwisolver-1.4.9-cp312-cp312-win_amd64.whl", hash = "sha256:f68208a520c3d86ea51acf688a3e3002615a7f0238002cccc17affecc86a8a54", size = 73895, upload-time = "2025-08-10T21:26:24.37Z" }, - { url = "https://files.pythonhosted.org/packages/e2/92/5f3068cf15ee5cb624a0c7596e67e2a0bb2adee33f71c379054a491d07da/kiwisolver-1.4.9-cp312-cp312-win_arm64.whl", hash = "sha256:2c1a4f57df73965f3f14df20b80ee29e6a7930a57d2d9e8491a25f676e197c60", size = 64992, upload-time = "2025-08-10T21:26:25.732Z" }, - { url = "https://files.pythonhosted.org/packages/31/c1/c2686cda909742ab66c7388e9a1a8521a59eb89f8bcfbee28fc980d07e24/kiwisolver-1.4.9-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a5d0432ccf1c7ab14f9949eec60c5d1f924f17c037e9f8b33352fa05799359b8", size = 123681, upload-time = "2025-08-10T21:26:26.725Z" }, - { url = "https://files.pythonhosted.org/packages/ca/f0/f44f50c9f5b1a1860261092e3bc91ecdc9acda848a8b8c6abfda4a24dd5c/kiwisolver-1.4.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efb3a45b35622bb6c16dbfab491a8f5a391fe0e9d45ef32f4df85658232ca0e2", size = 66464, upload-time = "2025-08-10T21:26:27.733Z" }, - { url = "https://files.pythonhosted.org/packages/2d/7a/9d90a151f558e29c3936b8a47ac770235f436f2120aca41a6d5f3d62ae8d/kiwisolver-1.4.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1a12cf6398e8a0a001a059747a1cbf24705e18fe413bc22de7b3d15c67cffe3f", size = 64961, upload-time = "2025-08-10T21:26:28.729Z" }, - { url = "https://files.pythonhosted.org/packages/e9/e9/f218a2cb3a9ffbe324ca29a9e399fa2d2866d7f348ec3a88df87fc248fc5/kiwisolver-1.4.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b67e6efbf68e077dd71d1a6b37e43e1a99d0bff1a3d51867d45ee8908b931098", size = 1474607, upload-time = "2025-08-10T21:26:29.798Z" }, - { url = "https://files.pythonhosted.org/packages/d9/28/aac26d4c882f14de59041636292bc838db8961373825df23b8eeb807e198/kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5656aa670507437af0207645273ccdfee4f14bacd7f7c67a4306d0dcaeaf6eed", size = 1276546, upload-time = "2025-08-10T21:26:31.401Z" }, - { url = "https://files.pythonhosted.org/packages/8b/ad/8bfc1c93d4cc565e5069162f610ba2f48ff39b7de4b5b8d93f69f30c4bed/kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bfc08add558155345129c7803b3671cf195e6a56e7a12f3dde7c57d9b417f525", size = 1294482, upload-time = "2025-08-10T21:26:32.721Z" }, - { url = "https://files.pythonhosted.org/packages/da/f1/6aca55ff798901d8ce403206d00e033191f63d82dd708a186e0ed2067e9c/kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:40092754720b174e6ccf9e845d0d8c7d8e12c3d71e7fc35f55f3813e96376f78", size = 1343720, upload-time = "2025-08-10T21:26:34.032Z" }, - { url = "https://files.pythonhosted.org/packages/d1/91/eed031876c595c81d90d0f6fc681ece250e14bf6998c3d7c419466b523b7/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:497d05f29a1300d14e02e6441cf0f5ee81c1ff5a304b0d9fb77423974684e08b", size = 2224907, upload-time = "2025-08-10T21:26:35.824Z" }, - { url = "https://files.pythonhosted.org/packages/e9/ec/4d1925f2e49617b9cca9c34bfa11adefad49d00db038e692a559454dfb2e/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:bdd1a81a1860476eb41ac4bc1e07b3f07259e6d55bbf739b79c8aaedcf512799", size = 2321334, upload-time = "2025-08-10T21:26:37.534Z" }, - { url = "https://files.pythonhosted.org/packages/43/cb/450cd4499356f68802750c6ddc18647b8ea01ffa28f50d20598e0befe6e9/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:e6b93f13371d341afee3be9f7c5964e3fe61d5fa30f6a30eb49856935dfe4fc3", size = 2488313, upload-time = "2025-08-10T21:26:39.191Z" }, - { url = "https://files.pythonhosted.org/packages/71/67/fc76242bd99f885651128a5d4fa6083e5524694b7c88b489b1b55fdc491d/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d75aa530ccfaa593da12834b86a0724f58bff12706659baa9227c2ccaa06264c", size = 2291970, upload-time = "2025-08-10T21:26:40.828Z" }, - { url = "https://files.pythonhosted.org/packages/75/bd/f1a5d894000941739f2ae1b65a32892349423ad49c2e6d0771d0bad3fae4/kiwisolver-1.4.9-cp313-cp313-win_amd64.whl", hash = "sha256:dd0a578400839256df88c16abddf9ba14813ec5f21362e1fe65022e00c883d4d", size = 73894, upload-time = "2025-08-10T21:26:42.33Z" }, - { url = "https://files.pythonhosted.org/packages/95/38/dce480814d25b99a391abbddadc78f7c117c6da34be68ca8b02d5848b424/kiwisolver-1.4.9-cp313-cp313-win_arm64.whl", hash = "sha256:d4188e73af84ca82468f09cadc5ac4db578109e52acb4518d8154698d3a87ca2", size = 64995, upload-time = "2025-08-10T21:26:43.889Z" }, - { url = "https://files.pythonhosted.org/packages/e2/37/7d218ce5d92dadc5ebdd9070d903e0c7cf7edfe03f179433ac4d13ce659c/kiwisolver-1.4.9-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:5a0f2724dfd4e3b3ac5a82436a8e6fd16baa7d507117e4279b660fe8ca38a3a1", size = 126510, upload-time = "2025-08-10T21:26:44.915Z" }, - { url = "https://files.pythonhosted.org/packages/23/b0/e85a2b48233daef4b648fb657ebbb6f8367696a2d9548a00b4ee0eb67803/kiwisolver-1.4.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:1b11d6a633e4ed84fc0ddafd4ebfd8ea49b3f25082c04ad12b8315c11d504dc1", size = 67903, upload-time = "2025-08-10T21:26:45.934Z" }, - { url = "https://files.pythonhosted.org/packages/44/98/f2425bc0113ad7de24da6bb4dae1343476e95e1d738be7c04d31a5d037fd/kiwisolver-1.4.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61874cdb0a36016354853593cffc38e56fc9ca5aa97d2c05d3dcf6922cd55a11", size = 66402, upload-time = "2025-08-10T21:26:47.101Z" }, - { url = "https://files.pythonhosted.org/packages/98/d8/594657886df9f34c4177cc353cc28ca7e6e5eb562d37ccc233bff43bbe2a/kiwisolver-1.4.9-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:60c439763a969a6af93b4881db0eed8fadf93ee98e18cbc35bc8da868d0c4f0c", size = 1582135, upload-time = "2025-08-10T21:26:48.665Z" }, - { url = "https://files.pythonhosted.org/packages/5c/c6/38a115b7170f8b306fc929e166340c24958347308ea3012c2b44e7e295db/kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92a2f997387a1b79a75e7803aa7ded2cfbe2823852ccf1ba3bcf613b62ae3197", size = 1389409, upload-time = "2025-08-10T21:26:50.335Z" }, - { url = "https://files.pythonhosted.org/packages/bf/3b/e04883dace81f24a568bcee6eb3001da4ba05114afa622ec9b6fafdc1f5e/kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a31d512c812daea6d8b3be3b2bfcbeb091dbb09177706569bcfc6240dcf8b41c", size = 1401763, upload-time = "2025-08-10T21:26:51.867Z" }, - { url = "https://files.pythonhosted.org/packages/9f/80/20ace48e33408947af49d7d15c341eaee69e4e0304aab4b7660e234d6288/kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:52a15b0f35dad39862d376df10c5230155243a2c1a436e39eb55623ccbd68185", size = 1453643, upload-time = "2025-08-10T21:26:53.592Z" }, - { url = "https://files.pythonhosted.org/packages/64/31/6ce4380a4cd1f515bdda976a1e90e547ccd47b67a1546d63884463c92ca9/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a30fd6fdef1430fd9e1ba7b3398b5ee4e2887783917a687d86ba69985fb08748", size = 2330818, upload-time = "2025-08-10T21:26:55.051Z" }, - { url = "https://files.pythonhosted.org/packages/fa/e9/3f3fcba3bcc7432c795b82646306e822f3fd74df0ee81f0fa067a1f95668/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cc9617b46837c6468197b5945e196ee9ca43057bb7d9d1ae688101e4e1dddf64", size = 2419963, upload-time = "2025-08-10T21:26:56.421Z" }, - { url = "https://files.pythonhosted.org/packages/99/43/7320c50e4133575c66e9f7dadead35ab22d7c012a3b09bb35647792b2a6d/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:0ab74e19f6a2b027ea4f845a78827969af45ce790e6cb3e1ebab71bdf9f215ff", size = 2594639, upload-time = "2025-08-10T21:26:57.882Z" }, - { url = "https://files.pythonhosted.org/packages/65/d6/17ae4a270d4a987ef8a385b906d2bdfc9fce502d6dc0d3aea865b47f548c/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dba5ee5d3981160c28d5490f0d1b7ed730c22470ff7f6cc26cfcfaacb9896a07", size = 2391741, upload-time = "2025-08-10T21:26:59.237Z" }, - { url = "https://files.pythonhosted.org/packages/2a/8f/8f6f491d595a9e5912971f3f863d81baddccc8a4d0c3749d6a0dd9ffc9df/kiwisolver-1.4.9-cp313-cp313t-win_arm64.whl", hash = "sha256:0749fd8f4218ad2e851e11cc4dc05c7cbc0cbc4267bdfdb31782e65aace4ee9c", size = 68646, upload-time = "2025-08-10T21:27:00.52Z" }, - { url = "https://files.pythonhosted.org/packages/6b/32/6cc0fbc9c54d06c2969faa9c1d29f5751a2e51809dd55c69055e62d9b426/kiwisolver-1.4.9-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:9928fe1eb816d11ae170885a74d074f57af3a0d65777ca47e9aeb854a1fba386", size = 123806, upload-time = "2025-08-10T21:27:01.537Z" }, - { url = "https://files.pythonhosted.org/packages/b2/dd/2bfb1d4a4823d92e8cbb420fe024b8d2167f72079b3bb941207c42570bdf/kiwisolver-1.4.9-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d0005b053977e7b43388ddec89fa567f43d4f6d5c2c0affe57de5ebf290dc552", size = 66605, upload-time = "2025-08-10T21:27:03.335Z" }, - { url = "https://files.pythonhosted.org/packages/f7/69/00aafdb4e4509c2ca6064646cba9cd4b37933898f426756adb2cb92ebbed/kiwisolver-1.4.9-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2635d352d67458b66fd0667c14cb1d4145e9560d503219034a18a87e971ce4f3", size = 64925, upload-time = "2025-08-10T21:27:04.339Z" }, - { url = "https://files.pythonhosted.org/packages/43/dc/51acc6791aa14e5cb6d8a2e28cefb0dc2886d8862795449d021334c0df20/kiwisolver-1.4.9-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:767c23ad1c58c9e827b649a9ab7809fd5fd9db266a9cf02b0e926ddc2c680d58", size = 1472414, upload-time = "2025-08-10T21:27:05.437Z" }, - { url = "https://files.pythonhosted.org/packages/3d/bb/93fa64a81db304ac8a246f834d5094fae4b13baf53c839d6bb6e81177129/kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72d0eb9fba308b8311685c2268cf7d0a0639a6cd027d8128659f72bdd8a024b4", size = 1281272, upload-time = "2025-08-10T21:27:07.063Z" }, - { url = "https://files.pythonhosted.org/packages/70/e6/6df102916960fb8d05069d4bd92d6d9a8202d5a3e2444494e7cd50f65b7a/kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f68e4f3eeca8fb22cc3d731f9715a13b652795ef657a13df1ad0c7dc0e9731df", size = 1298578, upload-time = "2025-08-10T21:27:08.452Z" }, - { url = "https://files.pythonhosted.org/packages/7c/47/e142aaa612f5343736b087864dbaebc53ea8831453fb47e7521fa8658f30/kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d84cd4061ae292d8ac367b2c3fa3aad11cb8625a95d135fe93f286f914f3f5a6", size = 1345607, upload-time = "2025-08-10T21:27:10.125Z" }, - { url = "https://files.pythonhosted.org/packages/54/89/d641a746194a0f4d1a3670fb900d0dbaa786fb98341056814bc3f058fa52/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a60ea74330b91bd22a29638940d115df9dc00af5035a9a2a6ad9399ffb4ceca5", size = 2230150, upload-time = "2025-08-10T21:27:11.484Z" }, - { url = "https://files.pythonhosted.org/packages/aa/6b/5ee1207198febdf16ac11f78c5ae40861b809cbe0e6d2a8d5b0b3044b199/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ce6a3a4e106cf35c2d9c4fa17c05ce0b180db622736845d4315519397a77beaf", size = 2325979, upload-time = "2025-08-10T21:27:12.917Z" }, - { url = "https://files.pythonhosted.org/packages/fc/ff/b269eefd90f4ae14dcc74973d5a0f6d28d3b9bb1afd8c0340513afe6b39a/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:77937e5e2a38a7b48eef0585114fe7930346993a88060d0bf886086d2aa49ef5", size = 2491456, upload-time = "2025-08-10T21:27:14.353Z" }, - { url = "https://files.pythonhosted.org/packages/fc/d4/10303190bd4d30de547534601e259a4fbf014eed94aae3e5521129215086/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:24c175051354f4a28c5d6a31c93906dc653e2bf234e8a4bbfb964892078898ce", size = 2294621, upload-time = "2025-08-10T21:27:15.808Z" }, - { url = "https://files.pythonhosted.org/packages/28/e0/a9a90416fce5c0be25742729c2ea52105d62eda6c4be4d803c2a7be1fa50/kiwisolver-1.4.9-cp314-cp314-win_amd64.whl", hash = "sha256:0763515d4df10edf6d06a3c19734e2566368980d21ebec439f33f9eb936c07b7", size = 75417, upload-time = "2025-08-10T21:27:17.436Z" }, - { url = "https://files.pythonhosted.org/packages/1f/10/6949958215b7a9a264299a7db195564e87900f709db9245e4ebdd3c70779/kiwisolver-1.4.9-cp314-cp314-win_arm64.whl", hash = "sha256:0e4e2bf29574a6a7b7f6cb5fa69293b9f96c928949ac4a53ba3f525dffb87f9c", size = 66582, upload-time = "2025-08-10T21:27:18.436Z" }, - { url = "https://files.pythonhosted.org/packages/ec/79/60e53067903d3bc5469b369fe0dfc6b3482e2133e85dae9daa9527535991/kiwisolver-1.4.9-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d976bbb382b202f71c67f77b0ac11244021cfa3f7dfd9e562eefcea2df711548", size = 126514, upload-time = "2025-08-10T21:27:19.465Z" }, - { url = "https://files.pythonhosted.org/packages/25/d1/4843d3e8d46b072c12a38c97c57fab4608d36e13fe47d47ee96b4d61ba6f/kiwisolver-1.4.9-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2489e4e5d7ef9a1c300a5e0196e43d9c739f066ef23270607d45aba368b91f2d", size = 67905, upload-time = "2025-08-10T21:27:20.51Z" }, - { url = "https://files.pythonhosted.org/packages/8c/ae/29ffcbd239aea8b93108de1278271ae764dfc0d803a5693914975f200596/kiwisolver-1.4.9-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e2ea9f7ab7fbf18fffb1b5434ce7c69a07582f7acc7717720f1d69f3e806f90c", size = 66399, upload-time = "2025-08-10T21:27:21.496Z" }, - { url = "https://files.pythonhosted.org/packages/a1/ae/d7ba902aa604152c2ceba5d352d7b62106bedbccc8e95c3934d94472bfa3/kiwisolver-1.4.9-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b34e51affded8faee0dfdb705416153819d8ea9250bbbf7ea1b249bdeb5f1122", size = 1582197, upload-time = "2025-08-10T21:27:22.604Z" }, - { url = "https://files.pythonhosted.org/packages/f2/41/27c70d427eddb8bc7e4f16420a20fefc6f480312122a59a959fdfe0445ad/kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8aacd3d4b33b772542b2e01beb50187536967b514b00003bdda7589722d2a64", size = 1390125, upload-time = "2025-08-10T21:27:24.036Z" }, - { url = "https://files.pythonhosted.org/packages/41/42/b3799a12bafc76d962ad69083f8b43b12bf4fe78b097b12e105d75c9b8f1/kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7cf974dd4e35fa315563ac99d6287a1024e4dc2077b8a7d7cd3d2fb65d283134", size = 1402612, upload-time = "2025-08-10T21:27:25.773Z" }, - { url = "https://files.pythonhosted.org/packages/d2/b5/a210ea073ea1cfaca1bb5c55a62307d8252f531beb364e18aa1e0888b5a0/kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:85bd218b5ecfbee8c8a82e121802dcb519a86044c9c3b2e4aef02fa05c6da370", size = 1453990, upload-time = "2025-08-10T21:27:27.089Z" }, - { url = "https://files.pythonhosted.org/packages/5f/ce/a829eb8c033e977d7ea03ed32fb3c1781b4fa0433fbadfff29e39c676f32/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0856e241c2d3df4efef7c04a1e46b1936b6120c9bcf36dd216e3acd84bc4fb21", size = 2331601, upload-time = "2025-08-10T21:27:29.343Z" }, - { url = "https://files.pythonhosted.org/packages/e0/4b/b5e97eb142eb9cd0072dacfcdcd31b1c66dc7352b0f7c7255d339c0edf00/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:9af39d6551f97d31a4deebeac6f45b156f9755ddc59c07b402c148f5dbb6482a", size = 2422041, upload-time = "2025-08-10T21:27:30.754Z" }, - { url = "https://files.pythonhosted.org/packages/40/be/8eb4cd53e1b85ba4edc3a9321666f12b83113a178845593307a3e7891f44/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:bb4ae2b57fc1d8cbd1cf7b1d9913803681ffa903e7488012be5b76dedf49297f", size = 2594897, upload-time = "2025-08-10T21:27:32.803Z" }, - { url = "https://files.pythonhosted.org/packages/99/dd/841e9a66c4715477ea0abc78da039832fbb09dac5c35c58dc4c41a407b8a/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:aedff62918805fb62d43a4aa2ecd4482c380dc76cd31bd7c8878588a61bd0369", size = 2391835, upload-time = "2025-08-10T21:27:34.23Z" }, - { url = "https://files.pythonhosted.org/packages/0c/28/4b2e5c47a0da96896fdfdb006340ade064afa1e63675d01ea5ac222b6d52/kiwisolver-1.4.9-cp314-cp314t-win_amd64.whl", hash = "sha256:1fa333e8b2ce4d9660f2cda9c0e1b6bafcfb2457a9d259faa82289e73ec24891", size = 79988, upload-time = "2025-08-10T21:27:35.587Z" }, - { url = "https://files.pythonhosted.org/packages/80/be/3578e8afd18c88cdf9cb4cffde75a96d2be38c5a903f1ed0ceec061bd09e/kiwisolver-1.4.9-cp314-cp314t-win_arm64.whl", hash = "sha256:4a48a2ce79d65d363597ef7b567ce3d14d68783d2b2263d98db3d9477805ba32", size = 70260, upload-time = "2025-08-10T21:27:36.606Z" }, -] - [[package]] name = "markdown" version = "3.10" @@ -864,60 +630,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, ] -[[package]] -name = "matplotlib" -version = "3.10.7" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "contourpy" }, - { name = "cycler" }, - { name = "fonttools" }, - { name = "kiwisolver" }, - { name = "numpy" }, - { name = "packaging" }, - { name = "pillow" }, - { name = "pyparsing" }, - { name = "python-dateutil" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ae/e2/d2d5295be2f44c678ebaf3544ba32d20c1f9ef08c49fe47f496180e1db15/matplotlib-3.10.7.tar.gz", hash = "sha256:a06ba7e2a2ef9131c79c49e63dad355d2d878413a0376c1727c8b9335ff731c7", size = 34804865, upload-time = "2025-10-09T00:28:00.669Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/be/b3/09eb0f7796932826ec20c25b517d568627754f6c6462fca19e12c02f2e12/matplotlib-3.10.7-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7a0edb7209e21840e8361e91ea84ea676658aa93edd5f8762793dec77a4a6748", size = 8272389, upload-time = "2025-10-09T00:26:42.474Z" }, - { url = "https://files.pythonhosted.org/packages/11/0b/1ae80ddafb8652fd8046cb5c8460ecc8d4afccb89e2c6d6bec61e04e1eaf/matplotlib-3.10.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c380371d3c23e0eadf8ebff114445b9f970aff2010198d498d4ab4c3b41eea4f", size = 8128247, upload-time = "2025-10-09T00:26:44.77Z" }, - { url = "https://files.pythonhosted.org/packages/7d/18/95ae2e242d4a5c98bd6e90e36e128d71cf1c7e39b0874feaed3ef782e789/matplotlib-3.10.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d5f256d49fea31f40f166a5e3131235a5d2f4b7f44520b1cf0baf1ce568ccff0", size = 8696996, upload-time = "2025-10-09T00:26:46.792Z" }, - { url = "https://files.pythonhosted.org/packages/7e/3d/5b559efc800bd05cb2033aa85f7e13af51958136a48327f7c261801ff90a/matplotlib-3.10.7-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:11ae579ac83cdf3fb72573bb89f70e0534de05266728740d478f0f818983c695", size = 9530153, upload-time = "2025-10-09T00:26:49.07Z" }, - { url = "https://files.pythonhosted.org/packages/88/57/eab4a719fd110312d3c220595d63a3c85ec2a39723f0f4e7fa7e6e3f74ba/matplotlib-3.10.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4c14b6acd16cddc3569a2d515cfdd81c7a68ac5639b76548cfc1a9e48b20eb65", size = 9593093, upload-time = "2025-10-09T00:26:51.067Z" }, - { url = "https://files.pythonhosted.org/packages/31/3c/80816f027b3a4a28cd2a0a6ef7f89a2db22310e945cd886ec25bfb399221/matplotlib-3.10.7-cp312-cp312-win_amd64.whl", hash = "sha256:0d8c32b7ea6fb80b1aeff5a2ceb3fb9778e2759e899d9beff75584714afcc5ee", size = 8122771, upload-time = "2025-10-09T00:26:53.296Z" }, - { url = "https://files.pythonhosted.org/packages/de/77/ef1fc78bfe99999b2675435cc52120887191c566b25017d78beaabef7f2d/matplotlib-3.10.7-cp312-cp312-win_arm64.whl", hash = "sha256:5f3f6d315dcc176ba7ca6e74c7768fb7e4cf566c49cb143f6bc257b62e634ed8", size = 7992812, upload-time = "2025-10-09T00:26:54.882Z" }, - { url = "https://files.pythonhosted.org/packages/02/9c/207547916a02c78f6bdd83448d9b21afbc42f6379ed887ecf610984f3b4e/matplotlib-3.10.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1d9d3713a237970569156cfb4de7533b7c4eacdd61789726f444f96a0d28f57f", size = 8273212, upload-time = "2025-10-09T00:26:56.752Z" }, - { url = "https://files.pythonhosted.org/packages/bc/d0/b3d3338d467d3fc937f0bb7f256711395cae6f78e22cef0656159950adf0/matplotlib-3.10.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:37a1fea41153dd6ee061d21ab69c9cf2cf543160b1b85d89cd3d2e2a7902ca4c", size = 8128713, upload-time = "2025-10-09T00:26:59.001Z" }, - { url = "https://files.pythonhosted.org/packages/22/ff/6425bf5c20d79aa5b959d1ce9e65f599632345391381c9a104133fe0b171/matplotlib-3.10.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b3c4ea4948d93c9c29dc01c0c23eef66f2101bf75158c291b88de6525c55c3d1", size = 8698527, upload-time = "2025-10-09T00:27:00.69Z" }, - { url = "https://files.pythonhosted.org/packages/d0/7f/ccdca06f4c2e6c7989270ed7829b8679466682f4cfc0f8c9986241c023b6/matplotlib-3.10.7-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:22df30ffaa89f6643206cf13877191c63a50e8f800b038bc39bee9d2d4957632", size = 9529690, upload-time = "2025-10-09T00:27:02.664Z" }, - { url = "https://files.pythonhosted.org/packages/b8/95/b80fc2c1f269f21ff3d193ca697358e24408c33ce2b106a7438a45407b63/matplotlib-3.10.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b69676845a0a66f9da30e87f48be36734d6748024b525ec4710be40194282c84", size = 9593732, upload-time = "2025-10-09T00:27:04.653Z" }, - { url = "https://files.pythonhosted.org/packages/e1/b6/23064a96308b9aeceeffa65e96bcde459a2ea4934d311dee20afde7407a0/matplotlib-3.10.7-cp313-cp313-win_amd64.whl", hash = "sha256:744991e0cc863dd669c8dc9136ca4e6e0082be2070b9d793cbd64bec872a6815", size = 8122727, upload-time = "2025-10-09T00:27:06.814Z" }, - { url = "https://files.pythonhosted.org/packages/b3/a6/2faaf48133b82cf3607759027f82b5c702aa99cdfcefb7f93d6ccf26a424/matplotlib-3.10.7-cp313-cp313-win_arm64.whl", hash = "sha256:fba2974df0bf8ce3c995fa84b79cde38326e0f7b5409e7a3a481c1141340bcf7", size = 7992958, upload-time = "2025-10-09T00:27:08.567Z" }, - { url = "https://files.pythonhosted.org/packages/4a/f0/b018fed0b599bd48d84c08794cb242227fe3341952da102ee9d9682db574/matplotlib-3.10.7-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:932c55d1fa7af4423422cb6a492a31cbcbdbe68fd1a9a3f545aa5e7a143b5355", size = 8316849, upload-time = "2025-10-09T00:27:10.254Z" }, - { url = "https://files.pythonhosted.org/packages/b0/b7/bb4f23856197659f275e11a2a164e36e65e9b48ea3e93c4ec25b4f163198/matplotlib-3.10.7-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5e38c2d581d62ee729a6e144c47a71b3f42fb4187508dbbf4fe71d5612c3433b", size = 8178225, upload-time = "2025-10-09T00:27:12.241Z" }, - { url = "https://files.pythonhosted.org/packages/62/56/0600609893ff277e6f3ab3c0cef4eafa6e61006c058e84286c467223d4d5/matplotlib-3.10.7-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:786656bb13c237bbcebcd402f65f44dd61ead60ee3deb045af429d889c8dbc67", size = 8711708, upload-time = "2025-10-09T00:27:13.879Z" }, - { url = "https://files.pythonhosted.org/packages/d8/1a/6bfecb0cafe94d6658f2f1af22c43b76cf7a1c2f0dc34ef84cbb6809617e/matplotlib-3.10.7-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:09d7945a70ea43bf9248f4b6582734c2fe726723204a76eca233f24cffc7ef67", size = 9541409, upload-time = "2025-10-09T00:27:15.684Z" }, - { url = "https://files.pythonhosted.org/packages/08/50/95122a407d7f2e446fd865e2388a232a23f2b81934960ea802f3171518e4/matplotlib-3.10.7-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d0b181e9fa8daf1d9f2d4c547527b167cb8838fc587deabca7b5c01f97199e84", size = 9594054, upload-time = "2025-10-09T00:27:17.547Z" }, - { url = "https://files.pythonhosted.org/packages/13/76/75b194a43b81583478a81e78a07da8d9ca6ddf50dd0a2ccabf258059481d/matplotlib-3.10.7-cp313-cp313t-win_amd64.whl", hash = "sha256:31963603041634ce1a96053047b40961f7a29eb8f9a62e80cc2c0427aa1d22a2", size = 8200100, upload-time = "2025-10-09T00:27:20.039Z" }, - { url = "https://files.pythonhosted.org/packages/f5/9e/6aefebdc9f8235c12bdeeda44cc0383d89c1e41da2c400caf3ee2073a3ce/matplotlib-3.10.7-cp313-cp313t-win_arm64.whl", hash = "sha256:aebed7b50aa6ac698c90f60f854b47e48cd2252b30510e7a1feddaf5a3f72cbf", size = 8042131, upload-time = "2025-10-09T00:27:21.608Z" }, - { url = "https://files.pythonhosted.org/packages/0d/4b/e5bc2c321b6a7e3a75638d937d19ea267c34bd5a90e12bee76c4d7c7a0d9/matplotlib-3.10.7-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d883460c43e8c6b173fef244a2341f7f7c0e9725c7fe68306e8e44ed9c8fb100", size = 8273787, upload-time = "2025-10-09T00:27:23.27Z" }, - { url = "https://files.pythonhosted.org/packages/86/ad/6efae459c56c2fbc404da154e13e3a6039129f3c942b0152624f1c621f05/matplotlib-3.10.7-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:07124afcf7a6504eafcb8ce94091c5898bbdd351519a1beb5c45f7a38c67e77f", size = 8131348, upload-time = "2025-10-09T00:27:24.926Z" }, - { url = "https://files.pythonhosted.org/packages/a6/5a/a4284d2958dee4116359cc05d7e19c057e64ece1b4ac986ab0f2f4d52d5a/matplotlib-3.10.7-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c17398b709a6cce3d9fdb1595c33e356d91c098cd9486cb2cc21ea2ea418e715", size = 9533949, upload-time = "2025-10-09T00:27:26.704Z" }, - { url = "https://files.pythonhosted.org/packages/de/ff/f3781b5057fa3786623ad8976fc9f7b0d02b2f28534751fd5a44240de4cf/matplotlib-3.10.7-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7146d64f561498764561e9cd0ed64fcf582e570fc519e6f521e2d0cfd43365e1", size = 9804247, upload-time = "2025-10-09T00:27:28.514Z" }, - { url = "https://files.pythonhosted.org/packages/47/5a/993a59facb8444efb0e197bf55f545ee449902dcee86a4dfc580c3b61314/matplotlib-3.10.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:90ad854c0a435da3104c01e2c6f0028d7e719b690998a2333d7218db80950722", size = 9595497, upload-time = "2025-10-09T00:27:30.418Z" }, - { url = "https://files.pythonhosted.org/packages/0d/a5/77c95aaa9bb32c345cbb49626ad8eb15550cba2e6d4c88081a6c2ac7b08d/matplotlib-3.10.7-cp314-cp314-win_amd64.whl", hash = "sha256:4645fc5d9d20ffa3a39361fcdbcec731382763b623b72627806bf251b6388866", size = 8252732, upload-time = "2025-10-09T00:27:32.332Z" }, - { url = "https://files.pythonhosted.org/packages/74/04/45d269b4268d222390d7817dae77b159651909669a34ee9fdee336db5883/matplotlib-3.10.7-cp314-cp314-win_arm64.whl", hash = "sha256:9257be2f2a03415f9105c486d304a321168e61ad450f6153d77c69504ad764bb", size = 8124240, upload-time = "2025-10-09T00:27:33.94Z" }, - { url = "https://files.pythonhosted.org/packages/4b/c7/ca01c607bb827158b439208c153d6f14ddb9fb640768f06f7ca3488ae67b/matplotlib-3.10.7-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1e4bbad66c177a8fdfa53972e5ef8be72a5f27e6a607cec0d8579abd0f3102b1", size = 8316938, upload-time = "2025-10-09T00:27:35.534Z" }, - { url = "https://files.pythonhosted.org/packages/84/d2/5539e66e9f56d2fdec94bb8436f5e449683b4e199bcc897c44fbe3c99e28/matplotlib-3.10.7-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d8eb7194b084b12feb19142262165832fc6ee879b945491d1c3d4660748020c4", size = 8178245, upload-time = "2025-10-09T00:27:37.334Z" }, - { url = "https://files.pythonhosted.org/packages/77/b5/e6ca22901fd3e4fe433a82e583436dd872f6c966fca7e63cf806b40356f8/matplotlib-3.10.7-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4d41379b05528091f00e1728004f9a8d7191260f3862178b88e8fd770206318", size = 9541411, upload-time = "2025-10-09T00:27:39.387Z" }, - { url = "https://files.pythonhosted.org/packages/9e/99/a4524db57cad8fee54b7237239a8f8360bfcfa3170d37c9e71c090c0f409/matplotlib-3.10.7-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4a74f79fafb2e177f240579bc83f0b60f82cc47d2f1d260f422a0627207008ca", size = 9803664, upload-time = "2025-10-09T00:27:41.492Z" }, - { url = "https://files.pythonhosted.org/packages/e6/a5/85e2edf76ea0ad4288d174926d9454ea85f3ce5390cc4e6fab196cbf250b/matplotlib-3.10.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:702590829c30aada1e8cef0568ddbffa77ca747b4d6e36c6d173f66e301f89cc", size = 9594066, upload-time = "2025-10-09T00:27:43.694Z" }, - { url = "https://files.pythonhosted.org/packages/39/69/9684368a314f6d83fe5c5ad2a4121a3a8e03723d2e5c8ea17b66c1bad0e7/matplotlib-3.10.7-cp314-cp314t-win_amd64.whl", hash = "sha256:f79d5de970fc90cd5591f60053aecfce1fcd736e0303d9f0bf86be649fa68fb8", size = 8342832, upload-time = "2025-10-09T00:27:45.543Z" }, - { url = "https://files.pythonhosted.org/packages/04/5f/e22e08da14bc1a0894184640d47819d2338b792732e20d292bf86e5ab785/matplotlib-3.10.7-cp314-cp314t-win_arm64.whl", hash = "sha256:cb783436e47fcf82064baca52ce748af71725d0352e1d31564cbe9c95df92b9c", size = 8172585, upload-time = "2025-10-09T00:27:47.185Z" }, -] - [[package]] name = "mdurl" version = "0.1.2" @@ -1243,75 +955,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, ] -[[package]] -name = "pillow" -version = "12.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/cace85a1b0c9775a9f8f5d5423c8261c858760e2466c79b2dd184638b056/pillow-12.0.0.tar.gz", hash = "sha256:87d4f8125c9988bfbed67af47dd7a953e2fc7b0cc1e7800ec6d2080d490bb353", size = 47008828, upload-time = "2025-10-15T18:24:14.008Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/90/4fcce2c22caf044e660a198d740e7fbc14395619e3cb1abad12192c0826c/pillow-12.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:53561a4ddc36facb432fae7a9d8afbfaf94795414f5cdc5fc52f28c1dca90371", size = 5249377, upload-time = "2025-10-15T18:22:05.993Z" }, - { url = "https://files.pythonhosted.org/packages/fd/e0/ed960067543d080691d47d6938ebccbf3976a931c9567ab2fbfab983a5dd/pillow-12.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:71db6b4c1653045dacc1585c1b0d184004f0d7e694c7b34ac165ca70c0838082", size = 4650343, upload-time = "2025-10-15T18:22:07.718Z" }, - { url = "https://files.pythonhosted.org/packages/e7/a1/f81fdeddcb99c044bf7d6faa47e12850f13cee0849537a7d27eeab5534d4/pillow-12.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2fa5f0b6716fc88f11380b88b31fe591a06c6315e955c096c35715788b339e3f", size = 6232981, upload-time = "2025-10-15T18:22:09.287Z" }, - { url = "https://files.pythonhosted.org/packages/88/e1/9098d3ce341a8750b55b0e00c03f1630d6178f38ac191c81c97a3b047b44/pillow-12.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:82240051c6ca513c616f7f9da06e871f61bfd7805f566275841af15015b8f98d", size = 8041399, upload-time = "2025-10-15T18:22:10.872Z" }, - { url = "https://files.pythonhosted.org/packages/a7/62/a22e8d3b602ae8cc01446d0c57a54e982737f44b6f2e1e019a925143771d/pillow-12.0.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:55f818bd74fe2f11d4d7cbc65880a843c4075e0ac7226bc1a23261dbea531953", size = 6347740, upload-time = "2025-10-15T18:22:12.769Z" }, - { url = "https://files.pythonhosted.org/packages/4f/87/424511bdcd02c8d7acf9f65caa09f291a519b16bd83c3fb3374b3d4ae951/pillow-12.0.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b87843e225e74576437fd5b6a4c2205d422754f84a06942cfaf1dc32243e45a8", size = 7040201, upload-time = "2025-10-15T18:22:14.813Z" }, - { url = "https://files.pythonhosted.org/packages/dc/4d/435c8ac688c54d11755aedfdd9f29c9eeddf68d150fe42d1d3dbd2365149/pillow-12.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c607c90ba67533e1b2355b821fef6764d1dd2cbe26b8c1005ae84f7aea25ff79", size = 6462334, upload-time = "2025-10-15T18:22:16.375Z" }, - { url = "https://files.pythonhosted.org/packages/2b/f2/ad34167a8059a59b8ad10bc5c72d4d9b35acc6b7c0877af8ac885b5f2044/pillow-12.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:21f241bdd5080a15bc86d3466a9f6074a9c2c2b314100dd896ac81ee6db2f1ba", size = 7134162, upload-time = "2025-10-15T18:22:17.996Z" }, - { url = "https://files.pythonhosted.org/packages/0c/b1/a7391df6adacf0a5c2cf6ac1cf1fcc1369e7d439d28f637a847f8803beb3/pillow-12.0.0-cp312-cp312-win32.whl", hash = "sha256:dd333073e0cacdc3089525c7df7d39b211bcdf31fc2824e49d01c6b6187b07d0", size = 6298769, upload-time = "2025-10-15T18:22:19.923Z" }, - { url = "https://files.pythonhosted.org/packages/a2/0b/d87733741526541c909bbf159e338dcace4f982daac6e5a8d6be225ca32d/pillow-12.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:9fe611163f6303d1619bbcb653540a4d60f9e55e622d60a3108be0d5b441017a", size = 7001107, upload-time = "2025-10-15T18:22:21.644Z" }, - { url = "https://files.pythonhosted.org/packages/bc/96/aaa61ce33cc98421fb6088af2a03be4157b1e7e0e87087c888e2370a7f45/pillow-12.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:7dfb439562f234f7d57b1ac6bc8fe7f838a4bd49c79230e0f6a1da93e82f1fad", size = 2436012, upload-time = "2025-10-15T18:22:23.621Z" }, - { url = "https://files.pythonhosted.org/packages/62/f2/de993bb2d21b33a98d031ecf6a978e4b61da207bef02f7b43093774c480d/pillow-12.0.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:0869154a2d0546545cde61d1789a6524319fc1897d9ee31218eae7a60ccc5643", size = 4045493, upload-time = "2025-10-15T18:22:25.758Z" }, - { url = "https://files.pythonhosted.org/packages/0e/b6/bc8d0c4c9f6f111a783d045310945deb769b806d7574764234ffd50bc5ea/pillow-12.0.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:a7921c5a6d31b3d756ec980f2f47c0cfdbce0fc48c22a39347a895f41f4a6ea4", size = 4120461, upload-time = "2025-10-15T18:22:27.286Z" }, - { url = "https://files.pythonhosted.org/packages/5d/57/d60d343709366a353dc56adb4ee1e7d8a2cc34e3fbc22905f4167cfec119/pillow-12.0.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:1ee80a59f6ce048ae13cda1abf7fbd2a34ab9ee7d401c46be3ca685d1999a399", size = 3576912, upload-time = "2025-10-15T18:22:28.751Z" }, - { url = "https://files.pythonhosted.org/packages/a4/a4/a0a31467e3f83b94d37568294b01d22b43ae3c5d85f2811769b9c66389dd/pillow-12.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c50f36a62a22d350c96e49ad02d0da41dbd17ddc2e29750dbdba4323f85eb4a5", size = 5249132, upload-time = "2025-10-15T18:22:30.641Z" }, - { url = "https://files.pythonhosted.org/packages/83/06/48eab21dd561de2914242711434c0c0eb992ed08ff3f6107a5f44527f5e9/pillow-12.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5193fde9a5f23c331ea26d0cf171fbf67e3f247585f50c08b3e205c7aeb4589b", size = 4650099, upload-time = "2025-10-15T18:22:32.73Z" }, - { url = "https://files.pythonhosted.org/packages/fc/bd/69ed99fd46a8dba7c1887156d3572fe4484e3f031405fcc5a92e31c04035/pillow-12.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bde737cff1a975b70652b62d626f7785e0480918dece11e8fef3c0cf057351c3", size = 6230808, upload-time = "2025-10-15T18:22:34.337Z" }, - { url = "https://files.pythonhosted.org/packages/ea/94/8fad659bcdbf86ed70099cb60ae40be6acca434bbc8c4c0d4ef356d7e0de/pillow-12.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a6597ff2b61d121172f5844b53f21467f7082f5fb385a9a29c01414463f93b07", size = 8037804, upload-time = "2025-10-15T18:22:36.402Z" }, - { url = "https://files.pythonhosted.org/packages/20/39/c685d05c06deecfd4e2d1950e9a908aa2ca8bc4e6c3b12d93b9cafbd7837/pillow-12.0.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b817e7035ea7f6b942c13aa03bb554fc44fea70838ea21f8eb31c638326584e", size = 6345553, upload-time = "2025-10-15T18:22:38.066Z" }, - { url = "https://files.pythonhosted.org/packages/38/57/755dbd06530a27a5ed74f8cb0a7a44a21722ebf318edbe67ddbd7fb28f88/pillow-12.0.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f4f1231b7dec408e8670264ce63e9c71409d9583dd21d32c163e25213ee2a344", size = 7037729, upload-time = "2025-10-15T18:22:39.769Z" }, - { url = "https://files.pythonhosted.org/packages/ca/b6/7e94f4c41d238615674d06ed677c14883103dce1c52e4af16f000338cfd7/pillow-12.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6e51b71417049ad6ab14c49608b4a24d8fb3fe605e5dfabfe523b58064dc3d27", size = 6459789, upload-time = "2025-10-15T18:22:41.437Z" }, - { url = "https://files.pythonhosted.org/packages/9c/14/4448bb0b5e0f22dd865290536d20ec8a23b64e2d04280b89139f09a36bb6/pillow-12.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d120c38a42c234dc9a8c5de7ceaaf899cf33561956acb4941653f8bdc657aa79", size = 7130917, upload-time = "2025-10-15T18:22:43.152Z" }, - { url = "https://files.pythonhosted.org/packages/dd/ca/16c6926cc1c015845745d5c16c9358e24282f1e588237a4c36d2b30f182f/pillow-12.0.0-cp313-cp313-win32.whl", hash = "sha256:4cc6b3b2efff105c6a1656cfe59da4fdde2cda9af1c5e0b58529b24525d0a098", size = 6302391, upload-time = "2025-10-15T18:22:44.753Z" }, - { url = "https://files.pythonhosted.org/packages/6d/2a/dd43dcfd6dae9b6a49ee28a8eedb98c7d5ff2de94a5d834565164667b97b/pillow-12.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:4cf7fed4b4580601c4345ceb5d4cbf5a980d030fd5ad07c4d2ec589f95f09905", size = 7007477, upload-time = "2025-10-15T18:22:46.838Z" }, - { url = "https://files.pythonhosted.org/packages/77/f0/72ea067f4b5ae5ead653053212af05ce3705807906ba3f3e8f58ddf617e6/pillow-12.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:9f0b04c6b8584c2c193babcccc908b38ed29524b29dd464bc8801bf10d746a3a", size = 2435918, upload-time = "2025-10-15T18:22:48.399Z" }, - { url = "https://files.pythonhosted.org/packages/f5/5e/9046b423735c21f0487ea6cb5b10f89ea8f8dfbe32576fe052b5ba9d4e5b/pillow-12.0.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7fa22993bac7b77b78cae22bad1e2a987ddf0d9015c63358032f84a53f23cdc3", size = 5251406, upload-time = "2025-10-15T18:22:49.905Z" }, - { url = "https://files.pythonhosted.org/packages/12/66/982ceebcdb13c97270ef7a56c3969635b4ee7cd45227fa707c94719229c5/pillow-12.0.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f135c702ac42262573fe9714dfe99c944b4ba307af5eb507abef1667e2cbbced", size = 4653218, upload-time = "2025-10-15T18:22:51.587Z" }, - { url = "https://files.pythonhosted.org/packages/16/b3/81e625524688c31859450119bf12674619429cab3119eec0e30a7a1029cb/pillow-12.0.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c85de1136429c524e55cfa4e033b4a7940ac5c8ee4d9401cc2d1bf48154bbc7b", size = 6266564, upload-time = "2025-10-15T18:22:53.215Z" }, - { url = "https://files.pythonhosted.org/packages/98/59/dfb38f2a41240d2408096e1a76c671d0a105a4a8471b1871c6902719450c/pillow-12.0.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:38df9b4bfd3db902c9c2bd369bcacaf9d935b2fff73709429d95cc41554f7b3d", size = 8069260, upload-time = "2025-10-15T18:22:54.933Z" }, - { url = "https://files.pythonhosted.org/packages/dc/3d/378dbea5cd1874b94c312425ca77b0f47776c78e0df2df751b820c8c1d6c/pillow-12.0.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7d87ef5795da03d742bf49439f9ca4d027cde49c82c5371ba52464aee266699a", size = 6379248, upload-time = "2025-10-15T18:22:56.605Z" }, - { url = "https://files.pythonhosted.org/packages/84/b0/d525ef47d71590f1621510327acec75ae58c721dc071b17d8d652ca494d8/pillow-12.0.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aff9e4d82d082ff9513bdd6acd4f5bd359f5b2c870907d2b0a9c5e10d40c88fe", size = 7066043, upload-time = "2025-10-15T18:22:58.53Z" }, - { url = "https://files.pythonhosted.org/packages/61/2c/aced60e9cf9d0cde341d54bf7932c9ffc33ddb4a1595798b3a5150c7ec4e/pillow-12.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:8d8ca2b210ada074d57fcee40c30446c9562e542fc46aedc19baf758a93532ee", size = 6490915, upload-time = "2025-10-15T18:23:00.582Z" }, - { url = "https://files.pythonhosted.org/packages/ef/26/69dcb9b91f4e59f8f34b2332a4a0a951b44f547c4ed39d3e4dcfcff48f89/pillow-12.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:99a7f72fb6249302aa62245680754862a44179b545ded638cf1fef59befb57ef", size = 7157998, upload-time = "2025-10-15T18:23:02.627Z" }, - { url = "https://files.pythonhosted.org/packages/61/2b/726235842220ca95fa441ddf55dd2382b52ab5b8d9c0596fe6b3f23dafe8/pillow-12.0.0-cp313-cp313t-win32.whl", hash = "sha256:4078242472387600b2ce8d93ade8899c12bf33fa89e55ec89fe126e9d6d5d9e9", size = 6306201, upload-time = "2025-10-15T18:23:04.709Z" }, - { url = "https://files.pythonhosted.org/packages/c0/3d/2afaf4e840b2df71344ababf2f8edd75a705ce500e5dc1e7227808312ae1/pillow-12.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2c54c1a783d6d60595d3514f0efe9b37c8808746a66920315bfd34a938d7994b", size = 7013165, upload-time = "2025-10-15T18:23:06.46Z" }, - { url = "https://files.pythonhosted.org/packages/6f/75/3fa09aa5cf6ed04bee3fa575798ddf1ce0bace8edb47249c798077a81f7f/pillow-12.0.0-cp313-cp313t-win_arm64.whl", hash = "sha256:26d9f7d2b604cd23aba3e9faf795787456ac25634d82cd060556998e39c6fa47", size = 2437834, upload-time = "2025-10-15T18:23:08.194Z" }, - { url = "https://files.pythonhosted.org/packages/54/2a/9a8c6ba2c2c07b71bec92cf63e03370ca5e5f5c5b119b742bcc0cde3f9c5/pillow-12.0.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:beeae3f27f62308f1ddbcfb0690bf44b10732f2ef43758f169d5e9303165d3f9", size = 4045531, upload-time = "2025-10-15T18:23:10.121Z" }, - { url = "https://files.pythonhosted.org/packages/84/54/836fdbf1bfb3d66a59f0189ff0b9f5f666cee09c6188309300df04ad71fa/pillow-12.0.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:d4827615da15cd59784ce39d3388275ec093ae3ee8d7f0c089b76fa87af756c2", size = 4120554, upload-time = "2025-10-15T18:23:12.14Z" }, - { url = "https://files.pythonhosted.org/packages/0d/cd/16aec9f0da4793e98e6b54778a5fbce4f375c6646fe662e80600b8797379/pillow-12.0.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:3e42edad50b6909089750e65c91aa09aaf1e0a71310d383f11321b27c224ed8a", size = 3576812, upload-time = "2025-10-15T18:23:13.962Z" }, - { url = "https://files.pythonhosted.org/packages/f6/b7/13957fda356dc46339298b351cae0d327704986337c3c69bb54628c88155/pillow-12.0.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:e5d8efac84c9afcb40914ab49ba063d94f5dbdf5066db4482c66a992f47a3a3b", size = 5252689, upload-time = "2025-10-15T18:23:15.562Z" }, - { url = "https://files.pythonhosted.org/packages/fc/f5/eae31a306341d8f331f43edb2e9122c7661b975433de5e447939ae61c5da/pillow-12.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:266cd5f2b63ff316d5a1bba46268e603c9caf5606d44f38c2873c380950576ad", size = 4650186, upload-time = "2025-10-15T18:23:17.379Z" }, - { url = "https://files.pythonhosted.org/packages/86/62/2a88339aa40c4c77e79108facbd307d6091e2c0eb5b8d3cf4977cfca2fe6/pillow-12.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:58eea5ebe51504057dd95c5b77d21700b77615ab0243d8152793dc00eb4faf01", size = 6230308, upload-time = "2025-10-15T18:23:18.971Z" }, - { url = "https://files.pythonhosted.org/packages/c7/33/5425a8992bcb32d1cb9fa3dd39a89e613d09a22f2c8083b7bf43c455f760/pillow-12.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f13711b1a5ba512d647a0e4ba79280d3a9a045aaf7e0cc6fbe96b91d4cdf6b0c", size = 8039222, upload-time = "2025-10-15T18:23:20.909Z" }, - { url = "https://files.pythonhosted.org/packages/d8/61/3f5d3b35c5728f37953d3eec5b5f3e77111949523bd2dd7f31a851e50690/pillow-12.0.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6846bd2d116ff42cba6b646edf5bf61d37e5cbd256425fa089fee4ff5c07a99e", size = 6346657, upload-time = "2025-10-15T18:23:23.077Z" }, - { url = "https://files.pythonhosted.org/packages/3a/be/ee90a3d79271227e0f0a33c453531efd6ed14b2e708596ba5dd9be948da3/pillow-12.0.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c98fa880d695de164b4135a52fd2e9cd7b7c90a9d8ac5e9e443a24a95ef9248e", size = 7038482, upload-time = "2025-10-15T18:23:25.005Z" }, - { url = "https://files.pythonhosted.org/packages/44/34/a16b6a4d1ad727de390e9bd9f19f5f669e079e5826ec0f329010ddea492f/pillow-12.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa3ed2a29a9e9d2d488b4da81dcb54720ac3104a20bf0bd273f1e4648aff5af9", size = 6461416, upload-time = "2025-10-15T18:23:27.009Z" }, - { url = "https://files.pythonhosted.org/packages/b6/39/1aa5850d2ade7d7ba9f54e4e4c17077244ff7a2d9e25998c38a29749eb3f/pillow-12.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d034140032870024e6b9892c692fe2968493790dd57208b2c37e3fb35f6df3ab", size = 7131584, upload-time = "2025-10-15T18:23:29.752Z" }, - { url = "https://files.pythonhosted.org/packages/bf/db/4fae862f8fad0167073a7733973bfa955f47e2cac3dc3e3e6257d10fab4a/pillow-12.0.0-cp314-cp314-win32.whl", hash = "sha256:1b1b133e6e16105f524a8dec491e0586d072948ce15c9b914e41cdadd209052b", size = 6400621, upload-time = "2025-10-15T18:23:32.06Z" }, - { url = "https://files.pythonhosted.org/packages/2b/24/b350c31543fb0107ab2599464d7e28e6f856027aadda995022e695313d94/pillow-12.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:8dc232e39d409036af549c86f24aed8273a40ffa459981146829a324e0848b4b", size = 7142916, upload-time = "2025-10-15T18:23:34.71Z" }, - { url = "https://files.pythonhosted.org/packages/0f/9b/0ba5a6fd9351793996ef7487c4fdbde8d3f5f75dbedc093bb598648fddf0/pillow-12.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:d52610d51e265a51518692045e372a4c363056130d922a7351429ac9f27e70b0", size = 2523836, upload-time = "2025-10-15T18:23:36.967Z" }, - { url = "https://files.pythonhosted.org/packages/f5/7a/ceee0840aebc579af529b523d530840338ecf63992395842e54edc805987/pillow-12.0.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1979f4566bb96c1e50a62d9831e2ea2d1211761e5662afc545fa766f996632f6", size = 5255092, upload-time = "2025-10-15T18:23:38.573Z" }, - { url = "https://files.pythonhosted.org/packages/44/76/20776057b4bfd1aef4eeca992ebde0f53a4dce874f3ae693d0ec90a4f79b/pillow-12.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b2e4b27a6e15b04832fe9bf292b94b5ca156016bbc1ea9c2c20098a0320d6cf6", size = 4653158, upload-time = "2025-10-15T18:23:40.238Z" }, - { url = "https://files.pythonhosted.org/packages/82/3f/d9ff92ace07be8836b4e7e87e6a4c7a8318d47c2f1463ffcf121fc57d9cb/pillow-12.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fb3096c30df99fd01c7bf8e544f392103d0795b9f98ba71a8054bcbf56b255f1", size = 6267882, upload-time = "2025-10-15T18:23:42.434Z" }, - { url = "https://files.pythonhosted.org/packages/9f/7a/4f7ff87f00d3ad33ba21af78bfcd2f032107710baf8280e3722ceec28cda/pillow-12.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7438839e9e053ef79f7112c881cef684013855016f928b168b81ed5835f3e75e", size = 8071001, upload-time = "2025-10-15T18:23:44.29Z" }, - { url = "https://files.pythonhosted.org/packages/75/87/fcea108944a52dad8cca0715ae6247e271eb80459364a98518f1e4f480c1/pillow-12.0.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d5c411a8eaa2299322b647cd932586b1427367fd3184ffbb8f7a219ea2041ca", size = 6380146, upload-time = "2025-10-15T18:23:46.065Z" }, - { url = "https://files.pythonhosted.org/packages/91/52/0d31b5e571ef5fd111d2978b84603fce26aba1b6092f28e941cb46570745/pillow-12.0.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7e091d464ac59d2c7ad8e7e08105eaf9dafbc3883fd7265ffccc2baad6ac925", size = 7067344, upload-time = "2025-10-15T18:23:47.898Z" }, - { url = "https://files.pythonhosted.org/packages/7b/f4/2dd3d721f875f928d48e83bb30a434dee75a2531bca839bb996bb0aa5a91/pillow-12.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:792a2c0be4dcc18af9d4a2dfd8a11a17d5e25274a1062b0ec1c2d79c76f3e7f8", size = 6491864, upload-time = "2025-10-15T18:23:49.607Z" }, - { url = "https://files.pythonhosted.org/packages/30/4b/667dfcf3d61fc309ba5a15b141845cece5915e39b99c1ceab0f34bf1d124/pillow-12.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:afbefa430092f71a9593a99ab6a4e7538bc9eabbf7bf94f91510d3503943edc4", size = 7158911, upload-time = "2025-10-15T18:23:51.351Z" }, - { url = "https://files.pythonhosted.org/packages/a2/2f/16cabcc6426c32218ace36bf0d55955e813f2958afddbf1d391849fee9d1/pillow-12.0.0-cp314-cp314t-win32.whl", hash = "sha256:3830c769decf88f1289680a59d4f4c46c72573446352e2befec9a8512104fa52", size = 6408045, upload-time = "2025-10-15T18:23:53.177Z" }, - { url = "https://files.pythonhosted.org/packages/35/73/e29aa0c9c666cf787628d3f0dcf379f4791fba79f4936d02f8b37165bdf8/pillow-12.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:905b0365b210c73afb0ebe9101a32572152dfd1c144c7e28968a331b9217b94a", size = 7148282, upload-time = "2025-10-15T18:23:55.316Z" }, - { url = "https://files.pythonhosted.org/packages/c1/70/6b41bdcddf541b437bbb9f47f94d2db5d9ddef6c37ccab8c9107743748a4/pillow-12.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:99353a06902c2e43b43e8ff74ee65a7d90307d82370604746738a1e0661ccca7", size = 2525630, upload-time = "2025-10-15T18:23:57.149Z" }, -] - [[package]] name = "platformdirs" version = "4.5.0" @@ -1549,15 +1192,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/4b/8f/d8889efd96bbe8e5d43ff9701f6b1565a8e09c3e1f58c388d550724f777b/pyodbc-5.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:13656184faa3f2d5c6f19b701b8f247342ed581484f58bf39af7315c054e69db", size = 70142, upload-time = "2025-10-17T18:03:55.551Z" }, ] -[[package]] -name = "pyparsing" -version = "3.2.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f2/a5/181488fc2b9d093e3972d2a472855aae8a03f000592dbfce716a512b3359/pyparsing-3.2.5.tar.gz", hash = "sha256:2df8d5b7b2802ef88e8d016a2eb9c7aeaa923529cd251ed0fe4608275d4105b6", size = 1099274, upload-time = "2025-09-21T04:11:06.277Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/10/5e/1aa9a93198c6b64513c9d7752de7422c06402de6600a8767da1524f9570b/pyparsing-3.2.5-py3-none-any.whl", hash = "sha256:e38a4f02064cf41fe6593d328d0512495ad1f3d8a91c4f73fc401b3079a59a5e", size = 113890, upload-time = "2025-09-21T04:11:04.117Z" }, -] - [[package]] name = "pyright" version = "1.1.410" @@ -1819,6 +1453,64 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2d/c7/c53e8dbff9c9dc4b7928773421ae294a5d28fcb8dcda1a089579d3a7e510/ruff-0.15.17-py3-none-win_arm64.whl", hash = "sha256:f3be1fbb34bcdfd146240d8fb92a709d4c2c8191348580a3c044ec60fa0b4456", size = 11355275, upload-time = "2026-06-11T17:54:43.635Z" }, ] +[[package]] +name = "scheduling" +version = "0.1.0" +source = { editable = "." } +dependencies = [ + { name = "fastapi", extra = ["standard"] }, + { name = "ortools" }, + { name = "pydantic" }, + { name = "pydantic-settings" }, + { name = "pyodbc" }, + { name = "sqlalchemy" }, +] + +[package.optional-dependencies] +docs = [ + { name = "mkdocs" }, + { name = "mkdocs-autorefs" }, + { name = "mkdocs-include-markdown-plugin" }, + { name = "mkdocs-material" }, + { name = "mkdocs-material-extensions" }, + { name = "mkdocstrings" }, + { name = "mkdocstrings-python" }, +] + +[package.dev-dependencies] +dev = [ + { name = "pre-commit" }, + { name = "pyright" }, + { name = "pytest" }, + { name = "ruff" }, +] + +[package.metadata] +requires-dist = [ + { name = "fastapi", extras = ["standard"], specifier = ">=0.137.0" }, + { name = "mkdocs", marker = "extra == 'docs'", specifier = ">=1.4.2" }, + { name = "mkdocs-autorefs", marker = "extra == 'docs'", specifier = ">=0.2.1" }, + { name = "mkdocs-include-markdown-plugin", marker = "extra == 'docs'", specifier = ">=1.0.0" }, + { name = "mkdocs-material", marker = "extra == 'docs'", specifier = "==9.6.14" }, + { name = "mkdocs-material-extensions", marker = "extra == 'docs'", specifier = ">=1.0.0" }, + { name = "mkdocstrings", marker = "extra == 'docs'", specifier = ">=0.15.2" }, + { name = "mkdocstrings-python", marker = "extra == 'docs'", specifier = ">=0.15.2" }, + { name = "ortools", specifier = ">=9.15.6755" }, + { name = "pydantic", specifier = ">=2.13.4" }, + { name = "pydantic-settings", specifier = ">=2.14.1" }, + { name = "pyodbc", specifier = ">=5.3.0" }, + { name = "sqlalchemy", specifier = ">=2.0.50" }, +] +provides-extras = ["docs"] + +[package.metadata.requires-dev] +dev = [ + { name = "pre-commit", specifier = ">=4.6.0" }, + { name = "pyright", specifier = ">=1.1.410" }, + { name = "pytest", specifier = ">=9.1.0" }, + { name = "ruff", specifier = ">=0.15.17" }, +] + [[package]] name = "sentry-sdk" version = "2.62.0" @@ -1891,76 +1583,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d0/10/f7220e9b784d295d241c86ed99aeb537f92afcd469a64861f2717e9bb077/sqlalchemy-2.0.50-py3-none-any.whl", hash = "sha256:92064363517a3ff8212b5a93b8c62876579d8dfd1ca5b561335f30152d884fa9", size = 1943861, upload-time = "2026-05-24T19:59:01.119Z" }, ] -[[package]] -name = "staffscheduling" -version = "0.0.0" -source = { editable = "." } -dependencies = [ - { name = "click" }, - { name = "dotenv" }, - { name = "fastapi", extra = ["standard"] }, - { name = "flask" }, - { name = "matplotlib" }, - { name = "numpy" }, - { name = "ortools" }, - { name = "pandas" }, - { name = "pydantic" }, - { name = "pydantic-settings" }, - { name = "pyodbc" }, - { name = "sqlalchemy" }, -] - -[package.optional-dependencies] -docs = [ - { name = "mkdocs" }, - { name = "mkdocs-autorefs" }, - { name = "mkdocs-include-markdown-plugin" }, - { name = "mkdocs-material" }, - { name = "mkdocs-material-extensions" }, - { name = "mkdocstrings" }, - { name = "mkdocstrings-python" }, -] - -[package.dev-dependencies] -dev = [ - { name = "pre-commit" }, - { name = "pyright" }, - { name = "pytest" }, - { name = "ruff" }, -] - -[package.metadata] -requires-dist = [ - { name = "click", specifier = ">=8.2.0" }, - { name = "dotenv", specifier = ">=0.9.9" }, - { name = "fastapi", extras = ["standard"], specifier = ">=0.137.0" }, - { name = "flask", specifier = ">=3.1.0" }, - { name = "matplotlib", specifier = ">=3.10.1" }, - { name = "mkdocs", marker = "extra == 'docs'", specifier = ">=1.4.2" }, - { name = "mkdocs-autorefs", marker = "extra == 'docs'", specifier = ">=0.2.1" }, - { name = "mkdocs-include-markdown-plugin", marker = "extra == 'docs'", specifier = ">=1.0.0" }, - { name = "mkdocs-material", marker = "extra == 'docs'", specifier = "==9.6.14" }, - { name = "mkdocs-material-extensions", marker = "extra == 'docs'", specifier = ">=1.0.0" }, - { name = "mkdocstrings", marker = "extra == 'docs'", specifier = ">=0.15.2" }, - { name = "mkdocstrings-python", marker = "extra == 'docs'", specifier = ">=0.15.2" }, - { name = "numpy", specifier = ">=2.2.5" }, - { name = "ortools", specifier = ">=9.15.6755" }, - { name = "pandas", specifier = ">=2.2.3" }, - { name = "pydantic", specifier = ">=2.13.4" }, - { name = "pydantic-settings", specifier = ">=2.14.1" }, - { name = "pyodbc", specifier = ">=5.2.0" }, - { name = "sqlalchemy", specifier = ">=2.0.50" }, -] -provides-extras = ["docs"] - -[package.metadata.requires-dev] -dev = [ - { name = "pre-commit", specifier = ">=4.6.0" }, - { name = "pyright", specifier = ">=1.1.410" }, - { name = "pytest", specifier = ">=9.1.0" }, - { name = "ruff", specifier = ">=0.15.17" }, -] - [[package]] name = "starlette" version = "0.52.1" @@ -2264,15 +1886,3 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9f/3e/28135a24e384493fa804216b79a6a6759a38cc4ff59118787b9fb693df93/websockets-16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b14dc141ed6d2dde437cddb216004bcac6a1df0935d79656387bd41632ba0bbd", size = 178531, upload-time = "2026-01-10T09:23:35.016Z" }, { url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598, upload-time = "2026-01-10T09:23:45.395Z" }, ] - -[[package]] -name = "werkzeug" -version = "3.1.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markupsafe" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9f/69/83029f1f6300c5fb2471d621ab06f6ec6b3324685a2ce0f9777fd4a8b71e/werkzeug-3.1.3.tar.gz", hash = "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746", size = 806925, upload-time = "2024-11-08T15:52:18.093Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/52/24/ab44c871b0f07f491e5d2ad12c9bd7358e527510618cb1b803a88e986db1/werkzeug-3.1.3-py3-none-any.whl", hash = "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e", size = 224498, upload-time = "2024-11-08T15:52:16.132Z" }, -] From fba9f1ac270b1ff3a14fa2428f926c6922a2864b Mon Sep 17 00:00:00 2001 From: Tom Wawerek Date: Fri, 19 Jun 2026 11:45:20 +0200 Subject: [PATCH 08/18] feat: add debugger vscode --- Dockerfile | 3 +-- Justfile | 22 +++++++++++++++++++--- pyproject.toml | 1 + uv.lock | 23 +++++++++++++++++++++++ 4 files changed, 44 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index e20d513e..408f42a0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.12-slim-bookworm +FROM python:3.12-slim-bookworm AS base ENV PYTHONUNBUFFERED=1 \ UV_COMPILE_BYTECODE=1 \ @@ -39,5 +39,4 @@ RUN --mount=type=cache,target=/root/.cache/uv \ EXPOSE 8000 -# Bind to 0.0.0.0 so the API is reachable from outside the container. CMD ["fastapi", "run", "src/scheduling/api/app.py", "--host", "0.0.0.0", "--port", "8000"] diff --git a/Justfile b/Justfile index 65845f2f..8f626818 100644 --- a/Justfile +++ b/Justfile @@ -2,7 +2,7 @@ IMAGE_NAME := "staff-scheduling-api" PORT := "8000" # Shared Docker args for dev commands -DOCKER_DEV_ARGS := "-p " + PORT + ":8000 --env-file .env -v $PWD/src:/app/src" +DOCKER_DEV_ARGS := "--env-file .env -v $PWD/src:/app/src" + " -p " + PORT + ":8000" _default: just --list @@ -27,11 +27,27 @@ check: lint typecheck test build: docker build -t {{IMAGE_NAME}} . -dev: +run: docker run --rm -it \ {{DOCKER_DEV_ARGS}} \ {{IMAGE_NAME}} \ - uv run fastapi dev src/scheduling/api/app.py --host 0.0.0.0 --port 8000 + uv run \ + fastapi dev \ + src/scheduling/api/app.py \ + --host 0.0.0.0 --port 8000 + +debug: + docker run --rm -it \ + {{DOCKER_DEV_ARGS}} \ + -p 5678:5678 \ + {{IMAGE_NAME}} \ + uv run \ + python -m debugpy \ + --listen 0.0.0.0:5678 \ + --wait-for-client \ + -m fastapi dev \ + src/scheduling/api/app.py \ + --host 0.0.0.0 --port 8000 docker-shell: docker run --rm -it \ diff --git a/pyproject.toml b/pyproject.toml index 8ab09608..484121e6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,6 +19,7 @@ dev = [ "pyright>=1.1.410", "ruff>=0.15.17", "pytest>=9.1.0", + "debugpy>=1.8.21", ] [project.optional-dependencies] diff --git a/uv.lock b/uv.lock index 804ce9bc..17dc53b1 100644 --- a/uv.lock +++ b/uv.lock @@ -175,6 +175,27 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] +[[package]] +name = "debugpy" +version = "1.8.21" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/aa/12037145b7a56eaa5b29b41872f7a21b538e807e13f32c4d3c46e59be084/debugpy-1.8.21.tar.gz", hash = "sha256:a3c53278e84c94e11bd87c53970ec391d1a67396c8b22609fcac576520e611a6", size = 1697577, upload-time = "2026-06-01T19:30:35.156Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/df/bf625547431a9cadc9f4cbfeda38866e2b17f6aed147b625377e87834449/debugpy-1.8.21-cp312-cp312-macosx_15_0_universal2.whl", hash = "sha256:9f96713896f39c3dff0ee841f47320c3f2983d33c341e009361bb0ebc79adc4e", size = 2483609, upload-time = "2026-06-01T19:30:50.794Z" }, + { url = "https://files.pythonhosted.org/packages/bf/09/59324b903599031ff9faaec1758292409f6561a0ec2492fe4b703327705a/debugpy-1.8.21-cp312-cp312-manylinux_2_34_x86_64.whl", hash = "sha256:c193d474f0a211191f2b4449d2d06157c689013035bd952f3b617e0ef422b176", size = 3968900, upload-time = "2026-06-01T19:30:52.341Z" }, + { url = "https://files.pythonhosted.org/packages/14/cd/27f65b805d7fe005c44e1a36b9183ecdfbcdbf9d3e721a5115d461ecc7ee/debugpy-1.8.21-cp312-cp312-win32.whl", hash = "sha256:4743373c1cac7f9e74a1b9915bf1dbe0e900eca657ffb170ae07ac8363205ae9", size = 5336340, upload-time = "2026-06-01T19:30:54.047Z" }, + { url = "https://files.pythonhosted.org/packages/77/1d/c84e30c0c674184948b66f076ab271c01d940618a2824c23cd035a27bc20/debugpy-1.8.21-cp312-cp312-win_amd64.whl", hash = "sha256:bd7ba9dd3daa7c2f942c6ca8d4695a16bf9ac16b63615261c7982bc74f7ed20c", size = 5374751, upload-time = "2026-06-01T19:30:55.891Z" }, + { url = "https://files.pythonhosted.org/packages/77/6b/d817e1f8cc77aa055d37fba092e0febfdff40fe652d8d53d4cd7a86ad98d/debugpy-1.8.21-cp313-cp313-macosx_15_0_universal2.whl", hash = "sha256:13678151fc401e2d68c9880b91e28714f797d40422994572b24560ef80910a88", size = 2477398, upload-time = "2026-06-01T19:30:57.644Z" }, + { url = "https://files.pythonhosted.org/packages/48/57/412421516afc3055fa577516f00beec3d663f9b0ab330639547ae6c57720/debugpy-1.8.21-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:ecbd158386c31ffe71d46f72d44d56e66331ab9b16cad649156d514368f23ab2", size = 3962096, upload-time = "2026-06-01T19:30:59.235Z" }, + { url = "https://files.pythonhosted.org/packages/c1/62/2c616337cf6ba7b07ebbc97f02c6c945a8e2f76b365e33ee809c32ee36d1/debugpy-1.8.21-cp313-cp313-win32.whl", hash = "sha256:2c2ae706dec41d99a9ca1f7ebc987a83e65578363be6f6b3ac9067504917fae1", size = 5336288, upload-time = "2026-06-01T19:31:00.79Z" }, + { url = "https://files.pythonhosted.org/packages/f8/99/9175103392f84c4b1bf7622888cdc68da07f0ff7d9e581266428f6776033/debugpy-1.8.21-cp313-cp313-win_amd64.whl", hash = "sha256:aa648733047443eb1d07682c4ef287d36a54507b643ffdf38b09a3ef002c72a0", size = 5376567, upload-time = "2026-06-01T19:31:02.56Z" }, + { url = "https://files.pythonhosted.org/packages/ce/3d/f4bbb323a548bfab2af3d6b4ffd9bf22636e55956a1285d317a1de643aad/debugpy-1.8.21-cp314-cp314-macosx_15_0_universal2.whl", hash = "sha256:9bb2a685287a2ac9b181cde89edcec64845cb51de7faaa75badb9a698bc24782", size = 2477209, upload-time = "2026-06-01T19:31:04.157Z" }, + { url = "https://files.pythonhosted.org/packages/8c/2d/6e7ec524984a1702777868de49a4c53202bddac2a432a76a093469587750/debugpy-1.8.21-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:3d6922439bf33fd38a3e2c447869ebc7b97da5cd3d329ff1ef9bc06c4903437e", size = 3927115, upload-time = "2026-06-01T19:31:05.863Z" }, + { url = "https://files.pythonhosted.org/packages/97/47/d1aa6d64005a98a9144647d99306b419396f9ad7bf1d73c119e17a81fb4d/debugpy-1.8.21-cp314-cp314-win32.whl", hash = "sha256:15d4963bd5ffa48f0da0947fd06757fa7621945048a14ad7705431566d3c0e7c", size = 5336724, upload-time = "2026-06-01T19:31:07.711Z" }, + { url = "https://files.pythonhosted.org/packages/5f/67/b905b90d163af11878c1af8abafa4a25206335e112e284e413454543a6da/debugpy-1.8.21-cp314-cp314-win_amd64.whl", hash = "sha256:fe0744a12353406de0ae8ccff0d0a4a666f00801a3db8fd04e7a5f761cd520e8", size = 5373803, upload-time = "2026-06-01T19:31:09.469Z" }, + { url = "https://files.pythonhosted.org/packages/95/51/67e7cf11a53e40694f720457d5b3a1cdaaa3d5a9a633e482f225456b93ff/debugpy-1.8.21-py2.py3-none-any.whl", hash = "sha256:b1e37d333663c8851516a47364ef473da127f9caebe4417e6df6f5825a7e9a92", size = 5352888, upload-time = "2026-06-01T19:31:25.186Z" }, +] + [[package]] name = "detect-installer" version = "0.1.0" @@ -1479,6 +1500,7 @@ docs = [ [package.dev-dependencies] dev = [ + { name = "debugpy" }, { name = "pre-commit" }, { name = "pyright" }, { name = "pytest" }, @@ -1505,6 +1527,7 @@ provides-extras = ["docs"] [package.metadata.requires-dev] dev = [ + { name = "debugpy", specifier = ">=1.8.21" }, { name = "pre-commit", specifier = ">=4.6.0" }, { name = "pyright", specifier = ">=1.1.410" }, { name = "pytest", specifier = ">=9.1.0" }, From 1255b90ac7aa48ca7affb646f02fdd856d39f2f8 Mon Sep 17 00:00:00 2001 From: Tom Wawerek Date: Fri, 19 Jun 2026 15:50:58 +0200 Subject: [PATCH 09/18] refactor: split async solve api router from webapp router --- .vscode/settings.json | 8 -- src/scheduling/api/app.py | 101 ++++++++---------- src/scheduling/api/dependencies.py | 62 +++++------ src/scheduling/api/solve/__init__.py | 0 src/scheduling/api/solve/job_store.py | 64 +++++++++++ src/scheduling/api/solve/router.py | 85 +++++++++++++++ src/scheduling/api/solve/schemas.py | 52 +++++++++ src/scheduling/api/web/__init__.py | 0 .../api/{web_router.py => web/router.py} | 0 src/scheduling/{models => domain}/__init__.py | 24 ++--- .../{models => domain}/assignment.py | 8 +- .../{models => domain}/availability.py | 6 +- src/scheduling/{models => domain}/core.py | 0 src/scheduling/{models => domain}/dataset.py | 22 ++-- src/scheduling/{models => domain}/demand.py | 8 +- src/scheduling/{models => domain}/employee.py | 2 +- .../monthly_work_account.py | 4 +- src/scheduling/{models => domain}/plan.py | 6 +- .../{models => domain}/planning_unit.py | 4 +- src/scheduling/{models => domain}/shift.py | 2 +- .../{models => domain}/sunday_work_history.py | 4 +- src/scheduling/{models => domain}/wish.py | 8 +- src/scheduling/solver/__init__.py | 7 ++ src/scheduling/solver/service.py | 22 ++++ src/scheduling/solver/tmp.py | 27 +---- src/scheduling/timeoffice/database.py | 2 +- src/scheduling/timeoffice/facts.py | 10 +- .../timeoffice/repositories/demand.py | 4 +- .../repositories/monthly_work_accounts.py | 2 +- .../timeoffice/repositories/personnel.py | 2 +- .../timeoffice/repositories/planning_units.py | 2 +- .../timeoffice/repositories/roster.py | 2 +- .../timeoffice/repositories/shifts.py | 2 +- .../repositories/sunday_work_history.py | 2 +- .../timeoffice/repositories/wishes.py | 2 +- src/scheduling/timeoffice/service.py | 2 +- src/scheduling/validation/context.py | 4 +- src/scheduling/validation/dataset.py | 2 +- src/scheduling/validation/validators.py | 4 +- 39 files changed, 373 insertions(+), 195 deletions(-) delete mode 100644 .vscode/settings.json create mode 100644 src/scheduling/api/solve/__init__.py create mode 100644 src/scheduling/api/solve/job_store.py create mode 100644 src/scheduling/api/solve/router.py create mode 100644 src/scheduling/api/solve/schemas.py create mode 100644 src/scheduling/api/web/__init__.py rename src/scheduling/api/{web_router.py => web/router.py} (100%) rename src/scheduling/{models => domain}/__init__.py (52%) rename src/scheduling/{models => domain}/assignment.py (87%) rename src/scheduling/{models => domain}/availability.py (89%) rename src/scheduling/{models => domain}/core.py (100%) rename src/scheduling/{models => domain}/dataset.py (72%) rename src/scheduling/{models => domain}/demand.py (62%) rename src/scheduling/{models => domain}/employee.py (95%) rename src/scheduling/{models => domain}/monthly_work_account.py (65%) rename src/scheduling/{models => domain}/plan.py (79%) rename src/scheduling/{models => domain}/planning_unit.py (93%) rename src/scheduling/{models => domain}/shift.py (97%) rename src/scheduling/{models => domain}/sunday_work_history.py (69%) rename src/scheduling/{models => domain}/wish.py (77%) create mode 100644 src/scheduling/solver/service.py diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index de5d0f49..00000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "python.testing.pytestArgs": [ - "-s", - "tests" - ], - "python.testing.pytestEnabled": true, - "python.testing.unittestEnabled": false -} diff --git a/src/scheduling/api/app.py b/src/scheduling/api/app.py index 9d413af1..95cedeb5 100644 --- a/src/scheduling/api/app.py +++ b/src/scheduling/api/app.py @@ -1,19 +1,24 @@ -import asyncio import logging -import uuid +from collections.abc import AsyncIterator +from contextlib import asynccontextmanager from typing import Annotated -from fastapi import BackgroundTasks, Depends, FastAPI, HTTPException, Query, status +from fastapi import Depends, FastAPI, Query -from scheduling.api.dependencies import get_solver_service, get_timeoffice_service +from scheduling.api.dependencies import ApiRuntime, get_timeoffice_service +from scheduling.api.solve.job_store import InMemorySolveJobStore +from scheduling.api.solve.router import solve_router from scheduling.api.types import ApiDate -from scheduling.api.web_router import web_router +from scheduling.api.web.router import web_router +from scheduling.domain import PlanningPeriod +from scheduling.domain.assignment import AssignmentType +from scheduling.domain.wish import WishKind from scheduling.logging import configure_logging -from scheduling.models import PlanningPeriod -from scheduling.models.assignment import AssignmentType -from scheduling.models.wish import WishKind from scheduling.settings import get_settings -from scheduling.solver.tmp import FakeSolution, SolverService +from scheduling.solver.service import SolverService +from scheduling.timeoffice.database import TimeOfficeDatabase, create_db_engine +from scheduling.timeoffice.facts import TIMEOFFICE_FACTS +from scheduling.timeoffice.repositories.container import TimeOfficeRepositories from scheduling.timeoffice.service import TimeOfficeService settings = get_settings() @@ -21,10 +26,36 @@ logger = logging.getLogger(__name__) -app = FastAPI(title="Staff Scheduling API") -app.include_router(web_router) -solve_lock = asyncio.Lock() +@asynccontextmanager +async def lifespan(app: FastAPI) -> AsyncIterator[None]: + configure_logging(level=settings.log_level) + + engine = create_db_engine(settings=settings) + facts = TIMEOFFICE_FACTS + repositories = TimeOfficeRepositories.create(facts=facts) + database = TimeOfficeDatabase( + engine=engine, + repositories=repositories, + facts=facts, + ) + + app.state.runtime = ApiRuntime( + engine=engine, + timeoffice_service=TimeOfficeService(database=database, facts=facts), + solver_service=SolverService(), + solve_job_store=InMemorySolveJobStore(), + ) + + try: + yield + finally: + engine.dispose() + + +app = FastAPI(title="Staff Scheduling API", lifespan=lifespan) +app.include_router(solve_router) +app.include_router(web_router) @app.get("/health") @@ -69,49 +100,3 @@ def fetch_timeoffice_dataset( "monthly_work_accounts": len(dataset.monthly_work_accounts), "employees_without_monthly_work_account": len(dataset.employees) - len(dataset.monthly_work_accounts), } - - -@app.post("/solve") -async def solve_schedule( - background_tasks: BackgroundTasks, - solver: Annotated[SolverService, Depends(get_solver_service)], -) -> dict[str, str]: - try: - async with asyncio.timeout(2): - await solve_lock.acquire() - except TimeoutError as e: - raise HTTPException( - status_code=status.HTTP_423_LOCKED, - detail="Solver already working on a different solution. Try again later!", - ) from e - - unique_id = uuid.uuid4() - - async def task() -> None: - try: - await solver.fake_solve(id=unique_id) - finally: - solve_lock.release() - - background_tasks.add_task(task) - - return { - "status": "accepted", - "solution_id": str(unique_id), - } - - -@app.get("/solution") -def get_solution( - id: Annotated[uuid.UUID, Query()], - solver: Annotated[SolverService, Depends(get_solver_service)], -) -> FakeSolution: - solution = next(filter(lambda s: s.id == id, solver.solutions), None) - - if solution is None: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, - detail="Solution not found!", - ) - - return solution diff --git a/src/scheduling/api/dependencies.py b/src/scheduling/api/dependencies.py index c4dd3550..c2a56a78 100644 --- a/src/scheduling/api/dependencies.py +++ b/src/scheduling/api/dependencies.py @@ -1,51 +1,39 @@ -from functools import lru_cache -from typing import Annotated +from dataclasses import dataclass +from typing import Annotated, cast -from fastapi import Depends +from fastapi import Depends, Request from sqlalchemy import Engine -from scheduling.settings import get_settings -from scheduling.solver.tmp import SolverService -from scheduling.timeoffice.database import TimeOfficeDatabase, create_db_engine -from scheduling.timeoffice.facts import TIMEOFFICE_FACTS, TimeOfficeFacts -from scheduling.timeoffice.repositories import TimeOfficeRepositories +from scheduling.api.solve.job_store import InMemorySolveJobStore +from scheduling.solver.service import SolverService from scheduling.timeoffice.service import TimeOfficeService -@lru_cache(maxsize=1) -def get_db_engine() -> Engine: - """Create and cache the SQLAlchemy engine for the app process.""" - settings = get_settings() - return create_db_engine(settings=settings) +@dataclass(frozen=True, slots=True) +class ApiRuntime: + engine: Engine + timeoffice_service: TimeOfficeService + solver_service: SolverService + solve_job_store: InMemorySolveJobStore -def get_timeoffice_facts() -> TimeOfficeFacts: - """Return static TimeOffice source facts.""" - return TIMEOFFICE_FACTS - - -@lru_cache(maxsize=1) -def get_timeoffice_repositories() -> TimeOfficeRepositories: - """Create stateless TimeOffice repositories.""" - return TimeOfficeRepositories.create(facts=TIMEOFFICE_FACTS) - - -def get_timeoffice_database( - engine: Annotated[Engine, Depends(get_db_engine)], - repositories: Annotated[TimeOfficeRepositories, Depends(get_timeoffice_repositories)], - facts: Annotated[TimeOfficeFacts, Depends(get_timeoffice_facts)], -) -> TimeOfficeDatabase: - """Create the TimeOffice database gateway.""" - return TimeOfficeDatabase(engine=engine, repositories=repositories, facts=facts) +def get_api_runtime(request: Request) -> ApiRuntime: + return cast(ApiRuntime, request.app.state.runtime) def get_timeoffice_service( - database: Annotated[TimeOfficeDatabase, Depends(get_timeoffice_database)], - facts: Annotated[TimeOfficeFacts, Depends(get_timeoffice_facts)], + runtime: Annotated[ApiRuntime, Depends(get_api_runtime)], ) -> TimeOfficeService: - """Create the high-level TimeOffice service.""" - return TimeOfficeService(database=database, facts=facts) + return runtime.timeoffice_service + + +def get_solver_service( + runtime: Annotated[ApiRuntime, Depends(get_api_runtime)], +) -> SolverService: + return runtime.solver_service -async def get_solver_service() -> SolverService: - return SolverService() +def get_solve_job_store( + runtime: Annotated[ApiRuntime, Depends(get_api_runtime)], +) -> InMemorySolveJobStore: + return runtime.solve_job_store diff --git a/src/scheduling/api/solve/__init__.py b/src/scheduling/api/solve/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/scheduling/api/solve/job_store.py b/src/scheduling/api/solve/job_store.py new file mode 100644 index 00000000..6bda3079 --- /dev/null +++ b/src/scheduling/api/solve/job_store.py @@ -0,0 +1,64 @@ +import uuid +from datetime import UTC, datetime + +from scheduling.api.solve.schemas import SolveCommand, SolveJob, SolveJobStatus +from scheduling.solver.tmp import SolverResult + + +class InMemorySolveJobStore: + """Process-local in-memory solve job store. + + This is intentionally API runtime state. It is not shared across multiple + Uvicorn workers and should be replaced by persistent storage if needed. + """ + + def __init__(self) -> None: + self._jobs: dict[uuid.UUID, SolveJob] = {} + + def create(self, command: SolveCommand) -> SolveJob: + job = SolveJob( + job_id=uuid.uuid4(), + status=SolveJobStatus.ACCEPTED, + command=command, + created_at=datetime.now(UTC), + ) + self._jobs[job.job_id] = job + return job + + def get(self, job_id: uuid.UUID) -> SolveJob | None: + return self._jobs.get(job_id) + + def mark_running(self, job_id: uuid.UUID) -> SolveJob: + return self._update( + job_id, + status=SolveJobStatus.RUNNING, + started_at=datetime.now(UTC), + error=None, + ) + + def mark_succeeded(self, job_id: uuid.UUID, result: SolverResult) -> SolveJob: + return self._update( + job_id, + status=SolveJobStatus.SUCCEEDED, + finished_at=datetime.now(UTC), + result=result, + error=None, + ) + + def mark_failed(self, job_id: uuid.UUID, error: str) -> SolveJob: + return self._update( + job_id, + status=SolveJobStatus.FAILED, + finished_at=datetime.now(UTC), + error=error, + ) + + def _update(self, job_id: uuid.UUID, **values: object) -> SolveJob: + job = self._jobs.get(job_id) + + if job is None: + raise KeyError(f"Solve job not found: {job_id}") + + updated = job.model_copy(update=values) + self._jobs[job_id] = updated + return updated diff --git a/src/scheduling/api/solve/router.py b/src/scheduling/api/solve/router.py new file mode 100644 index 00000000..5b0dd3ce --- /dev/null +++ b/src/scheduling/api/solve/router.py @@ -0,0 +1,85 @@ +import asyncio +import logging +import uuid +from typing import Annotated + +from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, status + +from scheduling.api.dependencies import get_solve_job_store, get_solver_service, get_timeoffice_service +from scheduling.api.solve.job_store import InMemorySolveJobStore +from scheduling.api.solve.schemas import SolveAcceptedResponse, SolveCommand, SolveJob, SolveRequest +from scheduling.domain.dataset import PlanningPeriod +from scheduling.solver.service import SolverService +from scheduling.timeoffice.service import TimeOfficeService + +logger = logging.getLogger(__name__) + + +solve_router = APIRouter() +lock = asyncio.Lock() + + +@solve_router.post("/solve") +async def solve_schedule( + request: SolveRequest, + timeoffice: Annotated[TimeOfficeService, Depends(get_timeoffice_service)], + solver: Annotated[SolverService, Depends(get_solver_service)], + job_store: Annotated[InMemorySolveJobStore, Depends(get_solve_job_store)], + background_tasks: BackgroundTasks, +) -> SolveAcceptedResponse: + command = SolveCommand( + planning_unit_ids=request.planning_unit_ids, + period=PlanningPeriod(start=request.start, end=request.end), + ) + + try: + async with asyncio.timeout(2): + await lock.acquire() + except TimeoutError as e: + raise HTTPException( + status_code=status.HTTP_423_LOCKED, + detail="Solver already working on a different solution. Try again later!", + ) from e + + job = job_store.create(command) + + def task(job: SolveJob) -> None: + try: + job_store.mark_running(job.job_id) + + dataset = timeoffice.fetch_dataset( + planning_unit_ids=command.planning_unit_ids, + period=command.period, + ) + + result = solver.solve(dataset) + + job_store.mark_succeeded(job.job_id, result) + except Exception as error: + logger.exception("Solve job failed: job_id=%s", job.job_id) + job_store.mark_failed(job.job_id, f"{type(error).__name__}: {error}") + finally: + lock.release() + + background_tasks.add_task(task, job) + + return SolveAcceptedResponse( + job_id=job.job_id, + status=job.status, + ) + + +@solve_router.get("/solution/{job_id}") +def get_solution( + job_id: uuid.UUID, + job_store: Annotated[InMemorySolveJobStore, Depends(get_solve_job_store)], +) -> SolveJob: + job = job_store.get(job_id) + + if job is None: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="Solve job not found.", + ) + + return job diff --git a/src/scheduling/api/solve/schemas.py b/src/scheduling/api/solve/schemas.py new file mode 100644 index 00000000..0a4a279e --- /dev/null +++ b/src/scheduling/api/solve/schemas.py @@ -0,0 +1,52 @@ +import uuid +from datetime import datetime +from enum import StrEnum +from typing import Self + +from pydantic import model_validator + +from scheduling.api.types import ApiDate +from scheduling.domain import SchedulingBaseModel +from scheduling.domain.dataset import PlanningPeriod +from scheduling.solver.tmp import SolverResult + + +class SolveJobStatus(StrEnum): + ACCEPTED = "accepted" + RUNNING = "running" + SUCCEEDED = "succeeded" + FAILED = "failed" + + +class SolveRequest(SchedulingBaseModel): + planning_unit_ids: tuple[int, ...] + start: ApiDate + end: ApiDate + + @model_validator(mode="after") + def validate_request(self) -> Self: + if not self.planning_unit_ids: + raise ValueError("At least one planning unit id is required.") + + return self + + +class SolveCommand(SchedulingBaseModel): + planning_unit_ids: tuple[int, ...] + period: PlanningPeriod + + +class SolveAcceptedResponse(SchedulingBaseModel): + job_id: uuid.UUID + status: SolveJobStatus + + +class SolveJob(SchedulingBaseModel): + job_id: uuid.UUID + status: SolveJobStatus + command: SolveCommand + created_at: datetime + started_at: datetime | None = None + finished_at: datetime | None = None + result: SolverResult | None = None + error: str | None = None diff --git a/src/scheduling/api/web/__init__.py b/src/scheduling/api/web/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/scheduling/api/web_router.py b/src/scheduling/api/web/router.py similarity index 100% rename from src/scheduling/api/web_router.py rename to src/scheduling/api/web/router.py diff --git a/src/scheduling/models/__init__.py b/src/scheduling/domain/__init__.py similarity index 52% rename from src/scheduling/models/__init__.py rename to src/scheduling/domain/__init__.py index d65ac4cf..1b1c85cb 100644 --- a/src/scheduling/models/__init__.py +++ b/src/scheduling/domain/__init__.py @@ -1,15 +1,15 @@ -from scheduling.models.assignment import Assignment, AssignmentType -from scheduling.models.availability import Availability, AvailabilityType -from scheduling.models.core import MinuteOfDay, NonEmptyStr, NonNegativeInt, PositiveId, SchedulingBaseModel -from scheduling.models.dataset import PlanningPeriod, SchedulingDataset -from scheduling.models.demand import DemandRequirement -from scheduling.models.employee import Capability, Employee, EmployeeId, StaffLevel -from scheduling.models.monthly_work_account import MonthlyWorkAccount -from scheduling.models.plan import Plan, PlanId, PlanParticipant -from scheduling.models.planning_unit import PlanningUnit, PlanningUnitId, PlanningUnitKind, PlanningUnitMembership -from scheduling.models.shift import Shift, ShiftId, ShiftKind, StaffingDemandRole -from scheduling.models.sunday_work_history import EmployeeSundayWorkHistory -from scheduling.models.wish import Wish, WishKind +from scheduling.domain.assignment import Assignment, AssignmentType +from scheduling.domain.availability import Availability, AvailabilityType +from scheduling.domain.core import MinuteOfDay, NonEmptyStr, NonNegativeInt, PositiveId, SchedulingBaseModel +from scheduling.domain.dataset import PlanningPeriod, SchedulingDataset +from scheduling.domain.demand import DemandRequirement +from scheduling.domain.employee import Capability, Employee, EmployeeId, StaffLevel +from scheduling.domain.monthly_work_account import MonthlyWorkAccount +from scheduling.domain.plan import Plan, PlanId, PlanParticipant +from scheduling.domain.planning_unit import PlanningUnit, PlanningUnitId, PlanningUnitKind, PlanningUnitMembership +from scheduling.domain.shift import Shift, ShiftId, ShiftKind, StaffingDemandRole +from scheduling.domain.sunday_work_history import EmployeeSundayWorkHistory +from scheduling.domain.wish import Wish, WishKind __all__ = [ "PositiveId", diff --git a/src/scheduling/models/assignment.py b/src/scheduling/domain/assignment.py similarity index 87% rename from src/scheduling/models/assignment.py rename to src/scheduling/domain/assignment.py index ba88ef54..704c16d4 100644 --- a/src/scheduling/models/assignment.py +++ b/src/scheduling/domain/assignment.py @@ -4,10 +4,10 @@ from pydantic import model_validator -from scheduling.models.core import SchedulingBaseModel -from scheduling.models.employee import EmployeeId -from scheduling.models.planning_unit import PlanningUnitId -from scheduling.models.shift import ShiftId +from scheduling.domain.core import SchedulingBaseModel +from scheduling.domain.employee import EmployeeId +from scheduling.domain.planning_unit import PlanningUnitId +from scheduling.domain.shift import ShiftId class AssignmentType(StrEnum): diff --git a/src/scheduling/models/availability.py b/src/scheduling/domain/availability.py similarity index 89% rename from src/scheduling/models/availability.py rename to src/scheduling/domain/availability.py index d6220f9f..abde6758 100644 --- a/src/scheduling/models/availability.py +++ b/src/scheduling/domain/availability.py @@ -4,9 +4,9 @@ from pydantic import model_validator -from scheduling.models.core import SchedulingBaseModel -from scheduling.models.employee import EmployeeId -from scheduling.models.shift import ShiftId +from scheduling.domain.core import SchedulingBaseModel +from scheduling.domain.employee import EmployeeId +from scheduling.domain.shift import ShiftId class AvailabilityType(StrEnum): diff --git a/src/scheduling/models/core.py b/src/scheduling/domain/core.py similarity index 100% rename from src/scheduling/models/core.py rename to src/scheduling/domain/core.py diff --git a/src/scheduling/models/dataset.py b/src/scheduling/domain/dataset.py similarity index 72% rename from src/scheduling/models/dataset.py rename to src/scheduling/domain/dataset.py index 97d3cd76..b72df3e2 100644 --- a/src/scheduling/models/dataset.py +++ b/src/scheduling/domain/dataset.py @@ -3,17 +3,17 @@ from pydantic import model_validator -from scheduling.models.assignment import Assignment -from scheduling.models.availability import Availability -from scheduling.models.core import SchedulingBaseModel -from scheduling.models.demand import DemandRequirement -from scheduling.models.employee import Employee -from scheduling.models.monthly_work_account import MonthlyWorkAccount -from scheduling.models.plan import Plan, PlanParticipant -from scheduling.models.planning_unit import PlanningUnit, PlanningUnitMembership -from scheduling.models.shift import Shift -from scheduling.models.sunday_work_history import EmployeeSundayWorkHistory -from scheduling.models.wish import Wish +from scheduling.domain.assignment import Assignment +from scheduling.domain.availability import Availability +from scheduling.domain.core import SchedulingBaseModel +from scheduling.domain.demand import DemandRequirement +from scheduling.domain.employee import Employee +from scheduling.domain.monthly_work_account import MonthlyWorkAccount +from scheduling.domain.plan import Plan, PlanParticipant +from scheduling.domain.planning_unit import PlanningUnit, PlanningUnitMembership +from scheduling.domain.shift import Shift +from scheduling.domain.sunday_work_history import EmployeeSundayWorkHistory +from scheduling.domain.wish import Wish class PlanningPeriod(SchedulingBaseModel): diff --git a/src/scheduling/models/demand.py b/src/scheduling/domain/demand.py similarity index 62% rename from src/scheduling/models/demand.py rename to src/scheduling/domain/demand.py index 37e3def2..c8eda416 100644 --- a/src/scheduling/models/demand.py +++ b/src/scheduling/domain/demand.py @@ -2,10 +2,10 @@ from pydantic import Field -from scheduling.models.core import SchedulingBaseModel -from scheduling.models.employee import StaffLevel -from scheduling.models.planning_unit import PlanningUnitId -from scheduling.models.shift import ShiftId +from scheduling.domain.core import SchedulingBaseModel +from scheduling.domain.employee import StaffLevel +from scheduling.domain.planning_unit import PlanningUnitId +from scheduling.domain.shift import ShiftId class DemandRequirement(SchedulingBaseModel): diff --git a/src/scheduling/models/employee.py b/src/scheduling/domain/employee.py similarity index 95% rename from src/scheduling/models/employee.py rename to src/scheduling/domain/employee.py index faa2b1ff..86fea475 100644 --- a/src/scheduling/models/employee.py +++ b/src/scheduling/domain/employee.py @@ -1,6 +1,6 @@ from enum import StrEnum -from scheduling.models.core import NonEmptyStr, PositiveId, SchedulingBaseModel +from scheduling.domain.core import NonEmptyStr, PositiveId, SchedulingBaseModel EmployeeId = PositiveId diff --git a/src/scheduling/models/monthly_work_account.py b/src/scheduling/domain/monthly_work_account.py similarity index 65% rename from src/scheduling/models/monthly_work_account.py rename to src/scheduling/domain/monthly_work_account.py index acf000ba..7e521484 100644 --- a/src/scheduling/models/monthly_work_account.py +++ b/src/scheduling/domain/monthly_work_account.py @@ -1,7 +1,7 @@ from pydantic import NonNegativeInt -from scheduling.models.core import SchedulingBaseModel -from scheduling.models.employee import EmployeeId +from scheduling.domain.core import SchedulingBaseModel +from scheduling.domain.employee import EmployeeId class MonthlyWorkAccount(SchedulingBaseModel): diff --git a/src/scheduling/models/plan.py b/src/scheduling/domain/plan.py similarity index 79% rename from src/scheduling/models/plan.py rename to src/scheduling/domain/plan.py index 813a1b62..fd8febe0 100644 --- a/src/scheduling/models/plan.py +++ b/src/scheduling/domain/plan.py @@ -1,6 +1,6 @@ -from scheduling.models.core import PositiveId, SchedulingBaseModel -from scheduling.models.employee import EmployeeId -from scheduling.models.planning_unit import PlanningUnitId +from scheduling.domain.core import PositiveId, SchedulingBaseModel +from scheduling.domain.employee import EmployeeId +from scheduling.domain.planning_unit import PlanningUnitId PlanId = PositiveId diff --git a/src/scheduling/models/planning_unit.py b/src/scheduling/domain/planning_unit.py similarity index 93% rename from src/scheduling/models/planning_unit.py rename to src/scheduling/domain/planning_unit.py index a222f734..8300b0bc 100644 --- a/src/scheduling/models/planning_unit.py +++ b/src/scheduling/domain/planning_unit.py @@ -4,8 +4,8 @@ from pydantic import model_validator -from scheduling.models.core import NonEmptyStr, PositiveId, SchedulingBaseModel -from scheduling.models.employee import EmployeeId, StaffLevel +from scheduling.domain.core import NonEmptyStr, PositiveId, SchedulingBaseModel +from scheduling.domain.employee import EmployeeId, StaffLevel PlanningUnitId = PositiveId diff --git a/src/scheduling/models/shift.py b/src/scheduling/domain/shift.py similarity index 97% rename from src/scheduling/models/shift.py rename to src/scheduling/domain/shift.py index e71b7cad..1221239b 100644 --- a/src/scheduling/models/shift.py +++ b/src/scheduling/domain/shift.py @@ -3,7 +3,7 @@ from pydantic import Field, model_validator -from scheduling.models.core import ( +from scheduling.domain.core import ( MinuteOfDay, NonEmptyStr, PositiveId, diff --git a/src/scheduling/models/sunday_work_history.py b/src/scheduling/domain/sunday_work_history.py similarity index 69% rename from src/scheduling/models/sunday_work_history.py rename to src/scheduling/domain/sunday_work_history.py index 5790276a..0d18bfe9 100644 --- a/src/scheduling/models/sunday_work_history.py +++ b/src/scheduling/domain/sunday_work_history.py @@ -1,7 +1,7 @@ from pydantic import Field -from scheduling.models.core import SchedulingBaseModel -from scheduling.models.employee import EmployeeId +from scheduling.domain.core import SchedulingBaseModel +from scheduling.domain.employee import EmployeeId class EmployeeSundayWorkHistory(SchedulingBaseModel): diff --git a/src/scheduling/models/wish.py b/src/scheduling/domain/wish.py similarity index 77% rename from src/scheduling/models/wish.py rename to src/scheduling/domain/wish.py index 58769cd1..dd317d41 100644 --- a/src/scheduling/models/wish.py +++ b/src/scheduling/domain/wish.py @@ -4,10 +4,10 @@ from pydantic import model_validator -from scheduling.models.core import SchedulingBaseModel -from scheduling.models.employee import EmployeeId -from scheduling.models.planning_unit import PlanningUnitId -from scheduling.models.shift import ShiftId +from scheduling.domain.core import SchedulingBaseModel +from scheduling.domain.employee import EmployeeId +from scheduling.domain.planning_unit import PlanningUnitId +from scheduling.domain.shift import ShiftId class WishKind(StrEnum): diff --git a/src/scheduling/solver/__init__.py b/src/scheduling/solver/__init__.py index e69de29b..26fbdb20 100644 --- a/src/scheduling/solver/__init__.py +++ b/src/scheduling/solver/__init__.py @@ -0,0 +1,7 @@ +from scheduling.solver.service import SolverService +from scheduling.solver.tmp import SolverResult + +__all__ = [ + "SolverResult", + "SolverService", +] diff --git a/src/scheduling/solver/service.py b/src/scheduling/solver/service.py new file mode 100644 index 00000000..ad4466af --- /dev/null +++ b/src/scheduling/solver/service.py @@ -0,0 +1,22 @@ +from scheduling.domain.dataset import SchedulingDataset +from scheduling.solver.tmp import SolverResult + + +class SolverService: + """Thin solver boundary. + + Currently fake. Later this class can delegate to OR-Tools without changing + the API integration contract. + """ + + def solve(self, dataset: SchedulingDataset) -> SolverResult: + return SolverResult( + status="succeeded", + message=( + "Fake solve completed for " + f"{len(dataset.employees)} employees, " + f"{len(dataset.planning_units)} planning units, " + f"{len(dataset.demand_requirements)} demand requirements." + ), + assignments_created=0, + ) diff --git a/src/scheduling/solver/tmp.py b/src/scheduling/solver/tmp.py index 75c6083d..2bc5bd84 100644 --- a/src/scheduling/solver/tmp.py +++ b/src/scheduling/solver/tmp.py @@ -1,24 +1,7 @@ -import asyncio -import logging -import uuid -from dataclasses import dataclass +from scheduling.domain import SchedulingBaseModel -logger = logging.getLogger(__name__) - -@dataclass -class FakeSolution: - id: uuid.UUID - value: str - - -class SolverService: - solutions: list[FakeSolution] = [] - - async def fake_solve(self, id: uuid.UUID) -> None: - logger.info("Started processing") - await asyncio.sleep(5) - logger.info("Finished processing") - - solution = FakeSolution(id=id, value=f"{id} processed!") - self.solutions.append(solution) +class SolverResult(SchedulingBaseModel): + status: str + message: str + assignments_created: int = 0 diff --git a/src/scheduling/timeoffice/database.py b/src/scheduling/timeoffice/database.py index bb39bfa7..8357bbe3 100644 --- a/src/scheduling/timeoffice/database.py +++ b/src/scheduling/timeoffice/database.py @@ -2,7 +2,7 @@ from sqlalchemy import URL, Engine, create_engine -from scheduling.models import PlanningPeriod +from scheduling.domain import PlanningPeriod from scheduling.settings import Settings from scheduling.timeoffice.facts import TimeOfficeFacts from scheduling.timeoffice.repositories import TimeOfficeRepositories diff --git a/src/scheduling/timeoffice/facts.py b/src/scheduling/timeoffice/facts.py index 090190c6..24fd724f 100644 --- a/src/scheduling/timeoffice/facts.py +++ b/src/scheduling/timeoffice/facts.py @@ -3,11 +3,11 @@ from enum import IntEnum from types import MappingProxyType -from scheduling.models.availability import AvailabilityType -from scheduling.models.employee import Capability, StaffLevel -from scheduling.models.planning_unit import PlanningUnitId, PlanningUnitKind -from scheduling.models.shift import ShiftId, ShiftKind, StaffingDemandRole -from scheduling.models.wish import WishKind +from scheduling.domain.availability import AvailabilityType +from scheduling.domain.employee import Capability, StaffLevel +from scheduling.domain.planning_unit import PlanningUnitId, PlanningUnitKind +from scheduling.domain.shift import ShiftId, ShiftKind, StaffingDemandRole +from scheduling.domain.wish import WishKind class TimeOfficePlanStatusId(IntEnum): diff --git a/src/scheduling/timeoffice/repositories/demand.py b/src/scheduling/timeoffice/repositories/demand.py index 2cfbb3e1..6433d284 100644 --- a/src/scheduling/timeoffice/repositories/demand.py +++ b/src/scheduling/timeoffice/repositories/demand.py @@ -2,7 +2,7 @@ from sqlalchemy import Connection -from scheduling.models import ( +from scheduling.domain import ( DemandRequirement, PlanningPeriod, PlanningUnit, @@ -11,7 +11,7 @@ Shift, StaffingDemandRole, ) -from scheduling.models.employee import StaffLevel +from scheduling.domain.employee import StaffLevel from scheduling.timeoffice.facts import TimeOfficeDemandFact, TimeOfficeFacts diff --git a/src/scheduling/timeoffice/repositories/monthly_work_accounts.py b/src/scheduling/timeoffice/repositories/monthly_work_accounts.py index d9c1a4db..336d1917 100644 --- a/src/scheduling/timeoffice/repositories/monthly_work_accounts.py +++ b/src/scheduling/timeoffice/repositories/monthly_work_accounts.py @@ -3,7 +3,7 @@ from pydantic import model_validator from sqlalchemy import Connection, bindparam, text -from scheduling.models import Employee, MonthlyWorkAccount, PlanningPeriod, SchedulingBaseModel +from scheduling.domain import Employee, MonthlyWorkAccount, PlanningPeriod, SchedulingBaseModel from scheduling.timeoffice.facts import TimeOfficeFacts from scheduling.timeoffice.repositories.types import ( CleanNullableText, diff --git a/src/scheduling/timeoffice/repositories/personnel.py b/src/scheduling/timeoffice/repositories/personnel.py index a3dd7a08..b9b6d084 100644 --- a/src/scheduling/timeoffice/repositories/personnel.py +++ b/src/scheduling/timeoffice/repositories/personnel.py @@ -4,7 +4,7 @@ from pydantic import model_validator from sqlalchemy import Connection, bindparam, text -from scheduling.models import ( +from scheduling.domain import ( Capability, Employee, Plan, diff --git a/src/scheduling/timeoffice/repositories/planning_units.py b/src/scheduling/timeoffice/repositories/planning_units.py index b295656a..ad26549d 100644 --- a/src/scheduling/timeoffice/repositories/planning_units.py +++ b/src/scheduling/timeoffice/repositories/planning_units.py @@ -3,7 +3,7 @@ from pydantic import model_validator from sqlalchemy import Connection, bindparam, text -from scheduling.models import Plan, PlanningPeriod, PlanningUnit, PlanningUnitKind, SchedulingBaseModel +from scheduling.domain import Plan, PlanningPeriod, PlanningUnit, PlanningUnitKind, SchedulingBaseModel from scheduling.timeoffice.facts import TimeOfficeFacts from scheduling.timeoffice.repositories.types import SourceInt, TimeOfficeSourceRow diff --git a/src/scheduling/timeoffice/repositories/roster.py b/src/scheduling/timeoffice/repositories/roster.py index 2d70b22e..ff93f3ad 100644 --- a/src/scheduling/timeoffice/repositories/roster.py +++ b/src/scheduling/timeoffice/repositories/roster.py @@ -4,7 +4,7 @@ from pydantic import model_validator from sqlalchemy import Connection, bindparam, text -from scheduling.models import ( +from scheduling.domain import ( Assignment, AssignmentType, Availability, diff --git a/src/scheduling/timeoffice/repositories/shifts.py b/src/scheduling/timeoffice/repositories/shifts.py index 9ec967b5..6077eb3a 100644 --- a/src/scheduling/timeoffice/repositories/shifts.py +++ b/src/scheduling/timeoffice/repositories/shifts.py @@ -5,7 +5,7 @@ from pydantic import model_validator from sqlalchemy import Connection, bindparam, text -from scheduling.models import SchedulingBaseModel, Shift +from scheduling.domain import SchedulingBaseModel, Shift from scheduling.timeoffice.facts import TimeOfficeFacts, TimeOfficeShiftFact from scheduling.timeoffice.repositories.types import CleanText, SourceInt, SourceNullableInt, TimeOfficeSourceRow diff --git a/src/scheduling/timeoffice/repositories/sunday_work_history.py b/src/scheduling/timeoffice/repositories/sunday_work_history.py index b32a44d8..77679345 100644 --- a/src/scheduling/timeoffice/repositories/sunday_work_history.py +++ b/src/scheduling/timeoffice/repositories/sunday_work_history.py @@ -2,7 +2,7 @@ from sqlalchemy import Connection, bindparam, text -from scheduling.models import Employee, EmployeeSundayWorkHistory, PlanningPeriod, SchedulingBaseModel +from scheduling.domain import Employee, EmployeeSundayWorkHistory, PlanningPeriod, SchedulingBaseModel from scheduling.timeoffice.repositories.types import CleanNullableText, SourceInt, TimeOfficeSourceRow diff --git a/src/scheduling/timeoffice/repositories/wishes.py b/src/scheduling/timeoffice/repositories/wishes.py index bba5ddae..d00ce721 100644 --- a/src/scheduling/timeoffice/repositories/wishes.py +++ b/src/scheduling/timeoffice/repositories/wishes.py @@ -5,7 +5,7 @@ from pydantic import model_validator from sqlalchemy import Connection, bindparam, text -from scheduling.models import Employee, Plan, PlanningPeriod, SchedulingBaseModel, Shift, Wish, WishKind +from scheduling.domain import Employee, Plan, PlanningPeriod, SchedulingBaseModel, Shift, Wish, WishKind from scheduling.timeoffice.facts import TimeOfficeFacts, TimeOfficeShiftFact from scheduling.timeoffice.repositories.types import ( CleanNullableText, diff --git a/src/scheduling/timeoffice/service.py b/src/scheduling/timeoffice/service.py index 5b828650..d4466279 100644 --- a/src/scheduling/timeoffice/service.py +++ b/src/scheduling/timeoffice/service.py @@ -1,4 +1,4 @@ -from scheduling.models import PlanningPeriod +from scheduling.domain import PlanningPeriod from scheduling.timeoffice.database import TimeOfficeDatabase from scheduling.timeoffice.facts import TimeOfficeFacts from scheduling.validation.dataset import ValidatedSchedulingDataset diff --git a/src/scheduling/validation/context.py b/src/scheduling/validation/context.py index eea8805c..a9d6495e 100644 --- a/src/scheduling/validation/context.py +++ b/src/scheduling/validation/context.py @@ -2,7 +2,7 @@ from dataclasses import dataclass from types import MappingProxyType -from scheduling.models import ( +from scheduling.domain import ( EmployeeId, Plan, PlanId, @@ -11,7 +11,7 @@ Shift, ShiftId, ) -from scheduling.models.dataset import SchedulingDataset +from scheduling.domain.dataset import SchedulingDataset from scheduling.validation.helpers import ensure_unique diff --git a/src/scheduling/validation/dataset.py b/src/scheduling/validation/dataset.py index 40c1f55b..0e28d2e9 100644 --- a/src/scheduling/validation/dataset.py +++ b/src/scheduling/validation/dataset.py @@ -2,7 +2,7 @@ from pydantic import model_validator -from scheduling.models.dataset import SchedulingDataset +from scheduling.domain.dataset import SchedulingDataset from scheduling.validation.context import DatasetValidationContext from scheduling.validation.validators import ( validate_assignments, diff --git a/src/scheduling/validation/validators.py b/src/scheduling/validation/validators.py index 4d7d6256..402dc87a 100644 --- a/src/scheduling/validation/validators.py +++ b/src/scheduling/validation/validators.py @@ -1,6 +1,6 @@ from datetime import date as Date -from scheduling.models import ( +from scheduling.domain import ( AssignmentType, AvailabilityType, EmployeeId, @@ -12,7 +12,7 @@ StaffLevel, WishKind, ) -from scheduling.models.dataset import SchedulingDataset +from scheduling.domain.dataset import SchedulingDataset from scheduling.validation.context import DatasetValidationContext From b4017174d7f3dcfc2ada1cfb93910f18601e08df Mon Sep 17 00:00:00 2001 From: Tom Wawerek Date: Mon, 22 Jun 2026 15:25:08 +0200 Subject: [PATCH 10/18] feat: working solver layer --- src/SELECT.sql | 25 ++- src/scheduling/api/app.py | 56 +---- src/scheduling/api/dependencies.py | 27 ++- src/scheduling/api/solve/job_models.py | 29 +++ src/scheduling/api/solve/job_store.py | 14 +- src/scheduling/api/solve/router.py | 104 ++++++--- src/scheduling/api/solve/schemas.py | 40 +--- src/scheduling/api/types.py | 34 --- src/scheduling/domain/__init__.py | 4 +- src/scheduling/domain/assignment.py | 1 + src/scheduling/domain/dataset.py | 40 ++-- src/scheduling/domain/planning_unit.py | 2 +- src/scheduling/settings.py | 10 +- src/scheduling/solver/__init__.py | 7 - src/scheduling/solver/cp_sat/__init__.py | 0 .../solver/cp_sat/constraints/__init__.py | 0 .../cp_sat/constraints/employee_daily.py | 18 ++ .../cp_sat/constraints/minimum_staffing.py | 96 ++++++++ src/scheduling/solver/cp_sat/context.py | 38 ++++ src/scheduling/solver/cp_sat/eligibility.py | 92 ++++++++ src/scheduling/solver/cp_sat/index.py | 124 ++++++++++ src/scheduling/solver/cp_sat/keys.py | 23 ++ .../solver/cp_sat/objectives/__init__.py | 0 .../balance_generated_assignments.py | 35 +++ src/scheduling/solver/cp_sat/variables.py | 52 +++++ src/scheduling/solver/models.py | 19 ++ src/scheduling/solver/service.py | 211 ++++++++++++++++-- src/scheduling/solver/tmp.py | 7 - src/scheduling/timeoffice/database.py | 20 +- .../timeoffice/repositories/demand.py | 12 +- .../repositories/monthly_work_accounts.py | 94 ++------ .../timeoffice/repositories/personnel.py | 16 +- .../timeoffice/repositories/planning_units.py | 16 +- .../timeoffice/repositories/roster.py | 14 +- .../repositories/sunday_work_history.py | 8 +- .../timeoffice/repositories/wishes.py | 14 +- src/scheduling/timeoffice/service.py | 66 +++++- src/scheduling/validation/context.py | 2 +- src/scheduling/validation/dataset.py | 2 +- src/scheduling/validation/validators.py | 94 +++++--- 40 files changed, 1083 insertions(+), 383 deletions(-) create mode 100644 src/scheduling/api/solve/job_models.py delete mode 100644 src/scheduling/api/types.py create mode 100644 src/scheduling/solver/cp_sat/__init__.py create mode 100644 src/scheduling/solver/cp_sat/constraints/__init__.py create mode 100644 src/scheduling/solver/cp_sat/constraints/employee_daily.py create mode 100644 src/scheduling/solver/cp_sat/constraints/minimum_staffing.py create mode 100644 src/scheduling/solver/cp_sat/context.py create mode 100644 src/scheduling/solver/cp_sat/eligibility.py create mode 100644 src/scheduling/solver/cp_sat/index.py create mode 100644 src/scheduling/solver/cp_sat/keys.py create mode 100644 src/scheduling/solver/cp_sat/objectives/__init__.py create mode 100644 src/scheduling/solver/cp_sat/objectives/balance_generated_assignments.py create mode 100644 src/scheduling/solver/cp_sat/variables.py create mode 100644 src/scheduling/solver/models.py delete mode 100644 src/scheduling/solver/tmp.py diff --git a/src/SELECT.sql b/src/SELECT.sql index d59eba4e..d56586a7 100644 --- a/src/SELECT.sql +++ b/src/SELECT.sql @@ -1,11 +1,16 @@ SELECT - Prim, - KurzBez, - Bezeichnung, - BezProg, - VbaRelevantJN, - GlobalJN, - PpugRelevant, - PpprlRelevant -FROM TEinsatzArten -WHERE Prim IN (151); + TABLE_NAME, + COLUMN_NAME, + DATA_TYPE, + IS_NULLABLE +FROM INFORMATION_SCHEMA.COLUMNS +WHERE TABLE_NAME IN ( + 'TPersonalDienstArten', + 'TPersonalRollmodelle', + 'TPersonalParameter', + 'TPersonalEinsatzorte', + 'TPersonalStatusJeTag' +) +ORDER BY + TABLE_NAME, + ORDINAL_POSITION; diff --git a/src/scheduling/api/app.py b/src/scheduling/api/app.py index 95cedeb5..7f35b2a9 100644 --- a/src/scheduling/api/app.py +++ b/src/scheduling/api/app.py @@ -1,18 +1,14 @@ +import asyncio import logging from collections.abc import AsyncIterator from contextlib import asynccontextmanager -from typing import Annotated -from fastapi import Depends, FastAPI, Query +from fastapi import FastAPI -from scheduling.api.dependencies import ApiRuntime, get_timeoffice_service +from scheduling.api.dependencies import ApiRuntime from scheduling.api.solve.job_store import InMemorySolveJobStore from scheduling.api.solve.router import solve_router -from scheduling.api.types import ApiDate from scheduling.api.web.router import web_router -from scheduling.domain import PlanningPeriod -from scheduling.domain.assignment import AssignmentType -from scheduling.domain.wish import WishKind from scheduling.logging import configure_logging from scheduling.settings import get_settings from scheduling.solver.service import SolverService @@ -29,9 +25,8 @@ @asynccontextmanager async def lifespan(app: FastAPI) -> AsyncIterator[None]: - configure_logging(level=settings.log_level) - engine = create_db_engine(settings=settings) + facts = TIMEOFFICE_FACTS repositories = TimeOfficeRepositories.create(facts=facts) database = TimeOfficeDatabase( @@ -41,10 +36,10 @@ async def lifespan(app: FastAPI) -> AsyncIterator[None]: ) app.state.runtime = ApiRuntime( - engine=engine, timeoffice_service=TimeOfficeService(database=database, facts=facts), - solver_service=SolverService(), + solver_service=SolverService(settings=settings), solve_job_store=InMemorySolveJobStore(), + solve_lock=asyncio.Lock(), ) try: @@ -61,42 +56,3 @@ async def lifespan(app: FastAPI) -> AsyncIterator[None]: @app.get("/health") async def healthcheck(): return {"status": "healthy"} - - -@app.get("/debug") -def fetch_timeoffice_dataset( - station_ids: Annotated[list[int], Query(alias="station")], - start: Annotated[ApiDate, Query()], - end: Annotated[ApiDate, Query()], - timeoffice: Annotated[TimeOfficeService, Depends(get_timeoffice_service)], -) -> dict[str, int]: - period = PlanningPeriod(start=start, end=end) - - dataset = timeoffice.fetch_dataset( - planning_unit_ids=tuple(station_ids), - period=period, - ) - - return { - "planning_units": len(dataset.planning_units), - "plans": len(dataset.plans), - "employees": len(dataset.employees), - "plan_participants": len(dataset.plan_participants), - "planning_unit_memberships": len(dataset.planning_unit_memberships), - "shifts": len(dataset.shifts), - "assignments": len(dataset.assignments), - "planned_assignments": sum( - assignment.assignment_type == AssignmentType.PLANNED for assignment in dataset.assignments - ), - "external_assignments": sum( - assignment.assignment_type == AssignmentType.EXTERNAL for assignment in dataset.assignments - ), - "availability": len(dataset.availability), - "demand_requirements": len(dataset.demand_requirements), - "sunday_work_history": len(dataset.sunday_work_history), - "wishes": len(dataset.wishes), - "shift_wishes": sum(wish.kind == WishKind.SHIFT for wish in dataset.wishes), - "free_day_wishes": sum(wish.kind == WishKind.FREE_DAY for wish in dataset.wishes), - "monthly_work_accounts": len(dataset.monthly_work_accounts), - "employees_without_monthly_work_account": len(dataset.employees) - len(dataset.monthly_work_accounts), - } diff --git a/src/scheduling/api/dependencies.py b/src/scheduling/api/dependencies.py index c2a56a78..eb3f230a 100644 --- a/src/scheduling/api/dependencies.py +++ b/src/scheduling/api/dependencies.py @@ -1,8 +1,8 @@ +import asyncio from dataclasses import dataclass from typing import Annotated, cast from fastapi import Depends, Request -from sqlalchemy import Engine from scheduling.api.solve.job_store import InMemorySolveJobStore from scheduling.solver.service import SolverService @@ -11,29 +11,32 @@ @dataclass(frozen=True, slots=True) class ApiRuntime: - engine: Engine timeoffice_service: TimeOfficeService solver_service: SolverService solve_job_store: InMemorySolveJobStore + solve_lock: asyncio.Lock def get_api_runtime(request: Request) -> ApiRuntime: - return cast(ApiRuntime, request.app.state.runtime) + runtime = getattr(request.app.state, "runtime", None) + if runtime is None: + raise RuntimeError("API runtime has not been initialized.") -def get_timeoffice_service( - runtime: Annotated[ApiRuntime, Depends(get_api_runtime)], -) -> TimeOfficeService: + return cast(ApiRuntime, runtime) + + +def get_timeoffice_service(runtime: Annotated[ApiRuntime, Depends(get_api_runtime)]) -> TimeOfficeService: return runtime.timeoffice_service -def get_solver_service( - runtime: Annotated[ApiRuntime, Depends(get_api_runtime)], -) -> SolverService: +def get_solver_service(runtime: Annotated[ApiRuntime, Depends(get_api_runtime)]) -> SolverService: return runtime.solver_service -def get_solve_job_store( - runtime: Annotated[ApiRuntime, Depends(get_api_runtime)], -) -> InMemorySolveJobStore: +def get_solve_lock(runtime: Annotated[ApiRuntime, Depends(get_api_runtime)]) -> asyncio.Lock: + return runtime.solve_lock + + +def get_solve_job_store(runtime: Annotated[ApiRuntime, Depends(get_api_runtime)]) -> InMemorySolveJobStore: return runtime.solve_job_store diff --git a/src/scheduling/api/solve/job_models.py b/src/scheduling/api/solve/job_models.py new file mode 100644 index 00000000..ca022cab --- /dev/null +++ b/src/scheduling/api/solve/job_models.py @@ -0,0 +1,29 @@ +import uuid +from datetime import datetime +from enum import StrEnum + +from scheduling.domain import PlanningMonth, SchedulingBaseModel +from scheduling.solver.models import Solution + + +class SolveCommand(SchedulingBaseModel): + planning_unit_ids: tuple[int, ...] + planning_month: PlanningMonth + + +class SolveJobStatus(StrEnum): + ACCEPTED = "accepted" + RUNNING = "running" + SUCCEEDED = "succeeded" + FAILED = "failed" + + +class SolveJob(SchedulingBaseModel): + job_id: uuid.UUID + status: SolveJobStatus + command: SolveCommand + created_at: datetime + started_at: datetime | None = None + finished_at: datetime | None = None + result: Solution | None = None + error: str | None = None diff --git a/src/scheduling/api/solve/job_store.py b/src/scheduling/api/solve/job_store.py index 6bda3079..a8470b34 100644 --- a/src/scheduling/api/solve/job_store.py +++ b/src/scheduling/api/solve/job_store.py @@ -1,21 +1,22 @@ import uuid from datetime import UTC, datetime -from scheduling.api.solve.schemas import SolveCommand, SolveJob, SolveJobStatus -from scheduling.solver.tmp import SolverResult +from scheduling.api.solve.job_models import SolveCommand, SolveJob, SolveJobStatus +from scheduling.solver.models import Solution class InMemorySolveJobStore: - """Process-local in-memory solve job store. + """Process-local store for solve job state. - This is intentionally API runtime state. It is not shared across multiple - Uvicorn workers and should be replaced by persistent storage if needed. + Jobs are kept only in the current API process. They are lost on restart and + are not shared across multiple Uvicorn workers. """ def __init__(self) -> None: self._jobs: dict[uuid.UUID, SolveJob] = {} def create(self, command: SolveCommand) -> SolveJob: + """Create an accepted solve job without starting execution.""" job = SolveJob( job_id=uuid.uuid4(), status=SolveJobStatus.ACCEPTED, @@ -26,6 +27,7 @@ def create(self, command: SolveCommand) -> SolveJob: return job def get(self, job_id: uuid.UUID) -> SolveJob | None: + """Return a job known to this process.""" return self._jobs.get(job_id) def mark_running(self, job_id: uuid.UUID) -> SolveJob: @@ -36,7 +38,7 @@ def mark_running(self, job_id: uuid.UUID) -> SolveJob: error=None, ) - def mark_succeeded(self, job_id: uuid.UUID, result: SolverResult) -> SolveJob: + def mark_succeeded(self, job_id: uuid.UUID, result: Solution) -> SolveJob: return self._update( job_id, status=SolveJobStatus.SUCCEEDED, diff --git a/src/scheduling/api/solve/router.py b/src/scheduling/api/solve/router.py index 5b0dd3ce..791e86f0 100644 --- a/src/scheduling/api/solve/router.py +++ b/src/scheduling/api/solve/router.py @@ -1,67 +1,117 @@ import asyncio import logging import uuid +from time import monotonic from typing import Annotated from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, status -from scheduling.api.dependencies import get_solve_job_store, get_solver_service, get_timeoffice_service +from scheduling.api.dependencies import ( + get_solve_job_store, + get_solve_lock, + get_solver_service, + get_timeoffice_service, +) +from scheduling.api.solve.job_models import SolveCommand, SolveJob from scheduling.api.solve.job_store import InMemorySolveJobStore -from scheduling.api.solve.schemas import SolveAcceptedResponse, SolveCommand, SolveJob, SolveRequest -from scheduling.domain.dataset import PlanningPeriod +from scheduling.api.solve.schemas import SolveAcceptedResponse, SolveRequest +from scheduling.solver.models import Solution from scheduling.solver.service import SolverService from scheduling.timeoffice.service import TimeOfficeService logger = logging.getLogger(__name__) - solve_router = APIRouter() -lock = asyncio.Lock() -@solve_router.post("/solve") -async def solve_schedule( +@solve_router.post("/solve", status_code=status.HTTP_202_ACCEPTED) +async def create_solve_task( request: SolveRequest, timeoffice: Annotated[TimeOfficeService, Depends(get_timeoffice_service)], solver: Annotated[SolverService, Depends(get_solver_service)], job_store: Annotated[InMemorySolveJobStore, Depends(get_solve_job_store)], + solve_lock: Annotated[asyncio.Lock, Depends(get_solve_lock)], background_tasks: BackgroundTasks, ) -> SolveAcceptedResponse: command = SolveCommand( planning_unit_ids=request.planning_unit_ids, - period=PlanningPeriod(start=request.start, end=request.end), + planning_month=request.planning_month(), ) - try: - async with asyncio.timeout(2): - await lock.acquire() - except TimeoutError as e: + if solve_lock.locked(): + logger.info( + "Solve job rejected because solver is busy: planning_units=%s planning_month=%s", + command.planning_unit_ids, + command.planning_month.label, + ) raise HTTPException( status_code=status.HTTP_423_LOCKED, - detail="Solver already working on a different solution. Try again later!", - ) from e + detail="Solver is already working on another solution. Try again later.", + ) + + await solve_lock.acquire() job = job_store.create(command) + logger.info( + "Solve job accepted: job_id=%s planning_units=%s planning_month=%s", + job.job_id, + command.planning_unit_ids, + command.planning_month.label, + ) + + def kernel(command: SolveCommand) -> Solution: + logger.info( + "Fetching scheduling dataset for solve job: job_id=%s planning_units=%s planning_month=%s", + job.job_id, + command.planning_unit_ids, + command.planning_month.label, + ) + + dataset = timeoffice.fetch_dataset( + planning_unit_ids=command.planning_unit_ids, + planning_month=command.planning_month, + ) + + logger.info("Solving scheduling dataset for solve job: job_id=%s", job.job_id) + solution = solver.solve(dataset) + + logger.info("Running TimeOffice writeback dry-run for solve job: job_id=%s", job.job_id) + timeoffice.write_solution_dry_run(solution) + + return solution + + async def task() -> None: + """Run the accepted solve job and persist its lifecycle state.""" + started = monotonic() - def task(job: SolveJob) -> None: try: job_store.mark_running(job.job_id) + logger.info("Solve job started: job_id=%s", job.job_id) - dataset = timeoffice.fetch_dataset( - planning_unit_ids=command.planning_unit_ids, - period=command.period, + result = await asyncio.to_thread(kernel, command) + + job_store.mark_succeeded(job.job_id, result) + logger.info( + "Solve job succeeded: job_id=%s duration_seconds=%.2f", + job.job_id, + monotonic() - started, ) - result = solver.solve(dataset) + except Exception as e: + logger.exception( + "Solve job failed: job_id=%s duration_seconds=%.2f", + job.job_id, + monotonic() - started, + ) + try: + job_store.mark_failed(job.job_id, f"{type(e).__name__}: {e}") + except Exception: + logger.exception("Failed to mark solve job as failed: job_id=%s", job.job_id) - job_store.mark_succeeded(job.job_id, result) - except Exception as error: - logger.exception("Solve job failed: job_id=%s", job.job_id) - job_store.mark_failed(job.job_id, f"{type(error).__name__}: {error}") finally: - lock.release() + solve_lock.release() - background_tasks.add_task(task, job) + background_tasks.add_task(task) return SolveAcceptedResponse( job_id=job.job_id, @@ -69,8 +119,8 @@ def task(job: SolveJob) -> None: ) -@solve_router.get("/solution/{job_id}") -def get_solution( +@solve_router.get("/solve/jobs/{job_id}") +async def check_solve_task( job_id: uuid.UUID, job_store: Annotated[InMemorySolveJobStore, Depends(get_solve_job_store)], ) -> SolveJob: diff --git a/src/scheduling/api/solve/schemas.py b/src/scheduling/api/solve/schemas.py index 0a4a279e..b060318d 100644 --- a/src/scheduling/api/solve/schemas.py +++ b/src/scheduling/api/solve/schemas.py @@ -1,27 +1,19 @@ import uuid -from datetime import datetime -from enum import StrEnum from typing import Self -from pydantic import model_validator +from pydantic import Field, model_validator -from scheduling.api.types import ApiDate -from scheduling.domain import SchedulingBaseModel -from scheduling.domain.dataset import PlanningPeriod -from scheduling.solver.tmp import SolverResult - - -class SolveJobStatus(StrEnum): - ACCEPTED = "accepted" - RUNNING = "running" - SUCCEEDED = "succeeded" - FAILED = "failed" +from scheduling.api.solve.job_models import SolveJobStatus +from scheduling.domain import PlanningMonth, SchedulingBaseModel class SolveRequest(SchedulingBaseModel): planning_unit_ids: tuple[int, ...] - start: ApiDate - end: ApiDate + year: int = Field(ge=2000, le=2100) + month: int = Field(ge=1, le=12) + + def planning_month(self) -> PlanningMonth: + return PlanningMonth(year=self.year, month=self.month) @model_validator(mode="after") def validate_request(self) -> Self: @@ -31,22 +23,6 @@ def validate_request(self) -> Self: return self -class SolveCommand(SchedulingBaseModel): - planning_unit_ids: tuple[int, ...] - period: PlanningPeriod - - class SolveAcceptedResponse(SchedulingBaseModel): job_id: uuid.UUID status: SolveJobStatus - - -class SolveJob(SchedulingBaseModel): - job_id: uuid.UUID - status: SolveJobStatus - command: SolveCommand - created_at: datetime - started_at: datetime | None = None - finished_at: datetime | None = None - result: SolverResult | None = None - error: str | None = None diff --git a/src/scheduling/api/types.py b/src/scheduling/api/types.py deleted file mode 100644 index 7b89535a..00000000 --- a/src/scheduling/api/types.py +++ /dev/null @@ -1,34 +0,0 @@ -from datetime import date as Date -from datetime import datetime as DateTime -from typing import Annotated, Any - -from pydantic import BeforeValidator - - -def parse_api_date(value: Any) -> Date: - """Parse API date values. - - Primary API format is ISO: YYYY-MM-DD. - DD.MM.YYYY is accepted for compatibility with previous CLI usage. - """ - if isinstance(value, Date) and not isinstance(value, DateTime): - return value - - if isinstance(value, DateTime): - return value.date() - - if not isinstance(value, str): - raise ValueError("Date must be a string in YYYY-MM-DD format.") - - cleaned = value.strip() - - for date_format in ("%Y-%m-%d", "%d.%m.%Y"): - try: - return DateTime.strptime(cleaned, date_format).date() - except ValueError: - pass - - raise ValueError("Date must use YYYY-MM-DD, for example 2024-11-01.") - - -ApiDate = Annotated[Date, BeforeValidator(parse_api_date)] diff --git a/src/scheduling/domain/__init__.py b/src/scheduling/domain/__init__.py index 1b1c85cb..40f60876 100644 --- a/src/scheduling/domain/__init__.py +++ b/src/scheduling/domain/__init__.py @@ -1,7 +1,7 @@ from scheduling.domain.assignment import Assignment, AssignmentType from scheduling.domain.availability import Availability, AvailabilityType from scheduling.domain.core import MinuteOfDay, NonEmptyStr, NonNegativeInt, PositiveId, SchedulingBaseModel -from scheduling.domain.dataset import PlanningPeriod, SchedulingDataset +from scheduling.domain.dataset import PlanningMonth, SchedulingDataset from scheduling.domain.demand import DemandRequirement from scheduling.domain.employee import Capability, Employee, EmployeeId, StaffLevel from scheduling.domain.monthly_work_account import MonthlyWorkAccount @@ -18,7 +18,7 @@ "MinuteOfDay", "SchedulingBaseModel", "SchedulingDataset", - "PlanningPeriod", + "PlanningMonth", "Plan", "PlanId", "PlanParticipant", diff --git a/src/scheduling/domain/assignment.py b/src/scheduling/domain/assignment.py index 704c16d4..6b1b9f13 100644 --- a/src/scheduling/domain/assignment.py +++ b/src/scheduling/domain/assignment.py @@ -15,6 +15,7 @@ class AssignmentType(StrEnum): PLANNED = "planned" EXTERNAL = "external" + GENERATED = "generated" class Assignment(SchedulingBaseModel): diff --git a/src/scheduling/domain/dataset.py b/src/scheduling/domain/dataset.py index b72df3e2..a9f8822c 100644 --- a/src/scheduling/domain/dataset.py +++ b/src/scheduling/domain/dataset.py @@ -1,11 +1,11 @@ -from datetime import date as Date -from typing import Self +from calendar import monthrange +from datetime import date -from pydantic import model_validator +from pydantic import Field, computed_field +from scheduling.domain import SchedulingBaseModel from scheduling.domain.assignment import Assignment from scheduling.domain.availability import Availability -from scheduling.domain.core import SchedulingBaseModel from scheduling.domain.demand import DemandRequirement from scheduling.domain.employee import Employee from scheduling.domain.monthly_work_account import MonthlyWorkAccount @@ -16,21 +16,27 @@ from scheduling.domain.wish import Wish -class PlanningPeriod(SchedulingBaseModel): - """Inclusive planning period for one scheduling dataset.""" +class PlanningMonth(SchedulingBaseModel): + year: int = Field(ge=2000, le=2200) + month: int = Field(ge=1, le=12) - start: Date - end: Date + @computed_field + @property + def start(self) -> date: + return date(self.year, self.month, 1) - @model_validator(mode="after") - def validate_period(self) -> Self: - if self.start > self.end: - raise ValueError(f"PlanningPeriod.start must be before or equal to end: {self.start} > {self.end}") + @computed_field + @property + def end(self) -> date: + return date( + self.year, + self.month, + monthrange(self.year, self.month)[1], + ) - return self - - def contains(self, date: Date) -> bool: - return self.start <= date <= self.end + @property + def label(self) -> str: + return f"{self.year:04d}-{self.month:02d}" class SchedulingDataset(SchedulingBaseModel): @@ -41,7 +47,7 @@ class SchedulingDataset(SchedulingBaseModel): OR-Tools variables are derived later. """ - period: PlanningPeriod + planning_month: PlanningMonth planning_units: tuple[PlanningUnit, ...] plans: tuple[Plan, ...] diff --git a/src/scheduling/domain/planning_unit.py b/src/scheduling/domain/planning_unit.py index 8300b0bc..a5efa729 100644 --- a/src/scheduling/domain/planning_unit.py +++ b/src/scheduling/domain/planning_unit.py @@ -46,7 +46,7 @@ class PlanningUnitMembership(SchedulingBaseModel): This comes from TimeOffice `TPlanungseinheitenPersonal`. Multiple intervals for the same employee and planning unit are valid because - eligibility can change inside the planning period. + eligibility can change inside the planning month. """ planning_unit_id: PlanningUnitId diff --git a/src/scheduling/settings.py b/src/scheduling/settings.py index 663bb99f..4b4f77f7 100644 --- a/src/scheduling/settings.py +++ b/src/scheduling/settings.py @@ -1,6 +1,6 @@ from functools import lru_cache -from pydantic import Field, SecretStr +from pydantic import SecretStr from pydantic_settings import BaseSettings, SettingsConfigDict @@ -10,7 +10,7 @@ class Settings(BaseSettings): extra="ignore", ) - log_level: str = Field(default="INFO") + log_level: str = "INFO" # TimeOffice Database db_driver: str = "ODBC Driver 18 for SQL Server" @@ -19,6 +19,12 @@ class Settings(BaseSettings): db_user: str db_password: SecretStr + # Solver + solver_max_time_seconds: float = 30 + solver_num_search_workers: int | None = None + solver_random_seed: int | None = None + solver_log_search_progress: bool = False + @lru_cache(maxsize=1) def get_settings() -> Settings: diff --git a/src/scheduling/solver/__init__.py b/src/scheduling/solver/__init__.py index 26fbdb20..e69de29b 100644 --- a/src/scheduling/solver/__init__.py +++ b/src/scheduling/solver/__init__.py @@ -1,7 +0,0 @@ -from scheduling.solver.service import SolverService -from scheduling.solver.tmp import SolverResult - -__all__ = [ - "SolverResult", - "SolverService", -] diff --git a/src/scheduling/solver/cp_sat/__init__.py b/src/scheduling/solver/cp_sat/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/scheduling/solver/cp_sat/constraints/__init__.py b/src/scheduling/solver/cp_sat/constraints/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/scheduling/solver/cp_sat/constraints/employee_daily.py b/src/scheduling/solver/cp_sat/constraints/employee_daily.py new file mode 100644 index 00000000..dd2ef963 --- /dev/null +++ b/src/scheduling/solver/cp_sat/constraints/employee_daily.py @@ -0,0 +1,18 @@ +from collections import defaultdict + +from ortools.sat.python import cp_model + +from scheduling.solver.cp_sat.context import SolverContext +from scheduling.solver.cp_sat.keys import EmployeeDateKey + + +def add_one_assignment_per_employee_day_constraints(ctx: SolverContext) -> None: + """Prevent more than one generated assignment per employee and day.""" + vars_by_employee_date: defaultdict[EmployeeDateKey, list[cp_model.IntVar]] = defaultdict(list) + + for key, variable in ctx.assignment_variables.items(): + employee_id, _, assignment_date, _, _ = key + vars_by_employee_date[(employee_id, assignment_date)].append(variable) + + for variables in vars_by_employee_date.values(): + ctx.model.add(sum(variables) <= 1) diff --git a/src/scheduling/solver/cp_sat/constraints/minimum_staffing.py b/src/scheduling/solver/cp_sat/constraints/minimum_staffing.py new file mode 100644 index 00000000..cd8e2eaf --- /dev/null +++ b/src/scheduling/solver/cp_sat/constraints/minimum_staffing.py @@ -0,0 +1,96 @@ +from collections import defaultdict + +from ortools.sat.python import cp_model + +from scheduling.solver.cp_sat.context import SolverContext +from scheduling.solver.cp_sat.keys import AssignmentVariableKey, DemandKey + + +def add_minimum_staffing_constraints(ctx: SolverContext) -> None: + """Cover minimum staffing requirements per planning unit, date, shift, and staff level.""" + vars_by_demand = _group_vars_by_demand(ctx) + + for demand_key, required_count in ctx.index.required_count_by_demand_key.items(): + fixed_count = ctx.index.fixed_planned_count_by_demand_key.get(demand_key, 0) + remaining_required = required_count - fixed_count + + if remaining_required <= 0: + if fixed_count > required_count: + ctx.diagnostics.append( + _overcovered_message( + demand_key=demand_key, + required_count=required_count, + fixed_count=fixed_count, + ) + ) + continue + + variables = vars_by_demand.get(demand_key, []) + + if not variables: + ctx.diagnostics.append( + _missing_candidates_message( + demand_key=demand_key, + remaining_required=remaining_required, + ) + ) + + ctx.model.add(sum(variables) >= remaining_required) + + +def _group_vars_by_demand( + ctx: SolverContext, +) -> dict[DemandKey, list[cp_model.IntVar]]: + grouped: defaultdict[DemandKey, list[cp_model.IntVar]] = defaultdict(list) + + for key, variable in ctx.assignment_variables.items(): + grouped[_demand_key_from_assignment_key(key)].append(variable) + + return dict(grouped) + + +def _demand_key_from_assignment_key(key: AssignmentVariableKey) -> DemandKey: + _, planning_unit_id, assignment_date, shift_id, staff_level = key + + return ( + planning_unit_id, + assignment_date, + shift_id, + staff_level, + ) + + +def _missing_candidates_message( + *, + demand_key: DemandKey, + remaining_required: int, +) -> str: + planning_unit_id, demand_date, shift_id, staff_level = demand_key + + return ( + "No eligible candidates for demand " + f"planning_unit_id={planning_unit_id} " + f"date={demand_date.isoformat()} " + f"shift_id={shift_id} " + f"staff_level={staff_level.value} " + f"remaining_required={remaining_required}." + ) + + +def _overcovered_message( + *, + demand_key: DemandKey, + required_count: int, + fixed_count: int, +) -> str: + planning_unit_id, demand_date, shift_id, staff_level = demand_key + + return ( + "Existing planned assignments exceed demand " + f"planning_unit_id={planning_unit_id} " + f"date={demand_date.isoformat()} " + f"shift_id={shift_id} " + f"staff_level={staff_level.value} " + f"required_count={required_count} " + f"fixed_count={fixed_count}." + ) diff --git a/src/scheduling/solver/cp_sat/context.py b/src/scheduling/solver/cp_sat/context.py new file mode 100644 index 00000000..74520639 --- /dev/null +++ b/src/scheduling/solver/cp_sat/context.py @@ -0,0 +1,38 @@ +from dataclasses import dataclass + +from ortools.sat.python import cp_model + +from scheduling.domain import SchedulingDataset +from scheduling.solver.cp_sat.index import SolverIndex, build_schedule_index +from scheduling.solver.cp_sat.keys import AssignmentVariableKey + + +@dataclass(frozen=True, slots=True) +class ObjectiveTerm: + name: str + expression: cp_model.LinearExpr + weight: int + + +@dataclass(slots=True) +class SolverContext: + dataset: SchedulingDataset + index: SolverIndex + model: cp_model.CpModel + assignment_variables: dict[AssignmentVariableKey, cp_model.IntVar] + objective_terms: list[ObjectiveTerm] + diagnostics: list[str] + + +def create_context(dataset: SchedulingDataset) -> SolverContext: + """Create the mutable CP-SAT build context for a scheduling dataset.""" + index = build_schedule_index(dataset) + + return SolverContext( + dataset=dataset, + index=index, + model=cp_model.CpModel(), + assignment_variables={}, + diagnostics=[], + objective_terms=[], + ) diff --git a/src/scheduling/solver/cp_sat/eligibility.py b/src/scheduling/solver/cp_sat/eligibility.py new file mode 100644 index 00000000..76df5fe1 --- /dev/null +++ b/src/scheduling/solver/cp_sat/eligibility.py @@ -0,0 +1,92 @@ +from datetime import date + +from scheduling.domain.availability import AvailabilityType +from scheduling.domain.demand import DemandRequirement +from scheduling.domain.employee import Employee +from scheduling.solver.cp_sat.index import SolverIndex + + +def is_employee_eligible_for_demand( + *, + employee: Employee, + demand: DemandRequirement, + index: SolverIndex, +) -> bool: + return ( + _has_active_membership_for_demand(employee=employee, demand=demand, index=index) + and not _has_existing_assignment_on_date(employee=employee, demand=demand, index=index) + and not _is_blocked_by_availability(employee=employee, demand=demand, index=index) + ) + + +def _has_active_membership_for_demand( + *, + employee: Employee, + demand: DemandRequirement, + index: SolverIndex, +) -> bool: + memberships = index.memberships_by_employee_unit.get( + (employee.employee_id, demand.planning_unit_id), + [], + ) + + return any( + membership.staff_level == demand.staff_level + and _date_in_interval( + demand.date, + valid_from=membership.valid_from, + valid_until=membership.valid_until, + ) + for membership in memberships + ) + + +def _date_in_interval( + target_date: date, + *, + valid_from: date, + valid_until: date | None, +) -> bool: + return target_date >= valid_from and (valid_until is None or target_date <= valid_until) + + +def _has_existing_assignment_on_date( + *, + employee: Employee, + demand: DemandRequirement, + index: SolverIndex, +) -> bool: + return bool(index.assignments_by_employee_date.get((employee.employee_id, demand.date))) + + +def _is_blocked_by_availability( + *, + employee: Employee, + demand: DemandRequirement, + index: SolverIndex, +) -> bool: + availability_items = index.availability_by_employee_date.get( + (employee.employee_id, demand.date), + [], + ) + + hard_blockers = { + AvailabilityType.UNAVAILABLE, + AvailabilityType.VACATION, + AvailabilityType.TRAINING, + AvailabilityType.FREE_DAY, + } + + if any(item.availability_type in hard_blockers for item in availability_items): + return True + + available_only_items = [ + item for item in availability_items if item.availability_type == AvailabilityType.AVAILABLE_ONLY + ] + + if not available_only_items: + return False + + allowed_shift_ids = {shift_id for item in available_only_items for shift_id in (item.shift_ids or ())} + + return demand.shift_id not in allowed_shift_ids diff --git a/src/scheduling/solver/cp_sat/index.py b/src/scheduling/solver/cp_sat/index.py new file mode 100644 index 00000000..569e1f03 --- /dev/null +++ b/src/scheduling/solver/cp_sat/index.py @@ -0,0 +1,124 @@ +from collections import defaultdict +from dataclasses import dataclass + +from scheduling.domain import ( + Assignment, + AssignmentType, + Availability, + DemandRequirement, + Employee, + EmployeeId, + PlanningUnitId, + PlanningUnitMembership, + SchedulingDataset, + Shift, + ShiftId, +) +from scheduling.solver.cp_sat.keys import DemandKey, EmployeeDateKey + +type MembershipKey = tuple[EmployeeId, PlanningUnitId] + + +@dataclass(frozen=True, slots=True) +class SolverIndex: + employees_by_id: dict[EmployeeId, Employee] + shifts_by_id: dict[ShiftId, Shift] + memberships_by_employee_unit: dict[MembershipKey, list[PlanningUnitMembership]] + assignments_by_employee_date: dict[EmployeeDateKey, list[Assignment]] + availability_by_employee_date: dict[EmployeeDateKey, list[Availability]] + required_count_by_demand_key: dict[DemandKey, int] + fixed_planned_count_by_demand_key: dict[DemandKey, int] + + +def build_schedule_index(dataset: SchedulingDataset) -> SolverIndex: + employees_by_id = {employee.employee_id: employee for employee in dataset.employees} + shifts_by_id = {shift.shift_id: shift for shift in dataset.shifts} + + return SolverIndex( + employees_by_id=employees_by_id, + shifts_by_id=shifts_by_id, + memberships_by_employee_unit=_group_memberships(dataset.planning_unit_memberships), + assignments_by_employee_date=_group_assignments_by_employee_date(dataset.assignments), + availability_by_employee_date=_group_availability_by_employee_date(dataset.availability), + required_count_by_demand_key=_count_required_demand_by_key(dataset.demand_requirements), + fixed_planned_count_by_demand_key=_count_fixed_planned_assignments_by_demand_key( + assignments=dataset.assignments, + employees_by_id=employees_by_id, + ), + ) + + +def _group_memberships( + memberships: tuple[PlanningUnitMembership, ...], +) -> dict[MembershipKey, list[PlanningUnitMembership]]: + grouped: defaultdict[MembershipKey, list[PlanningUnitMembership]] = defaultdict(list) + + for membership in memberships: + grouped[(membership.employee_id, membership.planning_unit_id)].append(membership) + + return dict(grouped) + + +def _group_assignments_by_employee_date( + assignments: tuple[Assignment, ...], +) -> dict[EmployeeDateKey, list[Assignment]]: + grouped: defaultdict[EmployeeDateKey, list[Assignment]] = defaultdict(list) + + for assignment in assignments: + grouped[(assignment.employee_id, assignment.date)].append(assignment) + + return dict(grouped) + + +def _group_availability_by_employee_date( + availability: tuple[Availability, ...], +) -> dict[EmployeeDateKey, list[Availability]]: + grouped: defaultdict[EmployeeDateKey, list[Availability]] = defaultdict(list) + + for item in availability: + grouped[(item.employee_id, item.date)].append(item) + + return dict(grouped) + + +def _count_required_demand_by_key( + demand_requirements: tuple[DemandRequirement, ...], +) -> dict[DemandKey, int]: + required: defaultdict[DemandKey, int] = defaultdict(int) + + for demand in demand_requirements: + key = ( + demand.planning_unit_id, + demand.date, + demand.shift_id, + demand.staff_level, + ) + required[key] += demand.required_count + + return dict(required) + + +def _count_fixed_planned_assignments_by_demand_key( + *, + assignments: tuple[Assignment, ...], + employees_by_id: dict[EmployeeId, Employee], +) -> dict[DemandKey, int]: + fixed: defaultdict[DemandKey, int] = defaultdict(int) + + for assignment in assignments: + if assignment.assignment_type != AssignmentType.PLANNED: + continue + + if assignment.planning_unit_id is None: + continue + + employee = employees_by_id[assignment.employee_id] + key = ( + assignment.planning_unit_id, + assignment.date, + assignment.shift_id, + employee.staff_level, + ) + fixed[key] += 1 + + return dict(fixed) diff --git a/src/scheduling/solver/cp_sat/keys.py b/src/scheduling/solver/cp_sat/keys.py new file mode 100644 index 00000000..6a63c392 --- /dev/null +++ b/src/scheduling/solver/cp_sat/keys.py @@ -0,0 +1,23 @@ +from datetime import date + +from scheduling.domain.employee import EmployeeId, StaffLevel +from scheduling.domain.planning_unit import PlanningUnitId +from scheduling.domain.shift import ShiftId + +type AssignmentVariableKey = tuple[ + EmployeeId, + PlanningUnitId, + date, + ShiftId, + StaffLevel, +] + +type DemandKey = tuple[ + PlanningUnitId, + date, + ShiftId, + StaffLevel, +] + +type EmployeeDateKey = tuple[EmployeeId, date] +type MembershipKey = tuple[EmployeeId, PlanningUnitId] diff --git a/src/scheduling/solver/cp_sat/objectives/__init__.py b/src/scheduling/solver/cp_sat/objectives/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/scheduling/solver/cp_sat/objectives/balance_generated_assignments.py b/src/scheduling/solver/cp_sat/objectives/balance_generated_assignments.py new file mode 100644 index 00000000..051c18ca --- /dev/null +++ b/src/scheduling/solver/cp_sat/objectives/balance_generated_assignments.py @@ -0,0 +1,35 @@ +from collections import defaultdict + +from ortools.sat.python import cp_model + +from scheduling.solver.cp_sat.context import ObjectiveTerm, SolverContext + + +def add_balance_generated_assignments_objective(ctx: SolverContext) -> None: + """Prefer distributing generated assignments across employees.""" + if not ctx.assignment_variables: + return + + variables_by_employee: defaultdict[int, list[cp_model.IntVar]] = defaultdict(list) + + for key, variable in ctx.assignment_variables.items(): + employee_id, _, _, _, _ = key + variables_by_employee[employee_id].append(variable) + + generated_counts = [sum(variables) for variables in variables_by_employee.values()] + + max_generated_assignments = ctx.model.new_int_var( + 0, + len(ctx.assignment_variables), + "max_generated_assignments_per_employee", + ) + + ctx.model.add_max_equality(max_generated_assignments, generated_counts) + + ctx.objective_terms.append( + ObjectiveTerm( + name="balance_generated_assignments", + expression=max_generated_assignments, + weight=1, + ) + ) diff --git a/src/scheduling/solver/cp_sat/variables.py b/src/scheduling/solver/cp_sat/variables.py new file mode 100644 index 00000000..40a5b4b6 --- /dev/null +++ b/src/scheduling/solver/cp_sat/variables.py @@ -0,0 +1,52 @@ +from scheduling.domain.demand import DemandRequirement +from scheduling.solver.cp_sat.context import SolverContext +from scheduling.solver.cp_sat.eligibility import is_employee_eligible_for_demand +from scheduling.solver.cp_sat.keys import AssignmentVariableKey, DemandKey + + +def create_assignment_variables(ctx: SolverContext) -> None: + """Create one boolean variable for each valid generated assignment.""" + for demand in ctx.dataset.demand_requirements: + if _remaining_required(ctx, demand) <= 0: + continue + + for employee in ctx.dataset.employees: + if not is_employee_eligible_for_demand( + employee=employee, + demand=demand, + index=ctx.index, + ): + continue + + key: AssignmentVariableKey = ( + employee.employee_id, + demand.planning_unit_id, + demand.date, + demand.shift_id, + demand.staff_level, + ) + + if key in ctx.assignment_variables: + continue + + ctx.assignment_variables[key] = ctx.model.new_bool_var(_variable_name(key)) + + +def _remaining_required(ctx: SolverContext, demand: DemandRequirement) -> int: + demand_key: DemandKey = ( + demand.planning_unit_id, + demand.date, + demand.shift_id, + demand.staff_level, + ) + + required_count = ctx.index.required_count_by_demand_key.get(demand_key, 0) + fixed_count = ctx.index.fixed_planned_count_by_demand_key.get(demand_key, 0) + + return max(required_count - fixed_count, 0) + + +def _variable_name(key: AssignmentVariableKey) -> str: + employee_id, planning_unit_id, assignment_date, shift_id, staff_level = key + + return f"assign_e{employee_id}_p{planning_unit_id}_d{assignment_date.isoformat()}_s{shift_id}_l{staff_level.value}" diff --git a/src/scheduling/solver/models.py b/src/scheduling/solver/models.py new file mode 100644 index 00000000..e9608b69 --- /dev/null +++ b/src/scheduling/solver/models.py @@ -0,0 +1,19 @@ +from enum import StrEnum + +from scheduling.domain import SchedulingBaseModel +from scheduling.domain.assignment import Assignment + + +class SolutionStatus(StrEnum): + OPTIMAL = "optimal" + FEASIBLE = "feasible" + INFEASIBLE = "infeasible" + MODEL_INVALID = "model_invalid" + UNKNOWN = "unknown" + ERROR = "error" + + +class Solution(SchedulingBaseModel): + status: SolutionStatus + assignments: tuple[Assignment, ...] = () + diagnostics: tuple[str, ...] = () diff --git a/src/scheduling/solver/service.py b/src/scheduling/solver/service.py index ad4466af..b865aed7 100644 --- a/src/scheduling/solver/service.py +++ b/src/scheduling/solver/service.py @@ -1,22 +1,197 @@ -from scheduling.domain.dataset import SchedulingDataset -from scheduling.solver.tmp import SolverResult +import logging +from collections.abc import Callable + +from ortools.sat.python import cp_model + +from scheduling.domain import Assignment, AssignmentType, SchedulingDataset +from scheduling.settings import Settings +from scheduling.solver.cp_sat.constraints.employee_daily import add_one_assignment_per_employee_day_constraints +from scheduling.solver.cp_sat.constraints.minimum_staffing import add_minimum_staffing_constraints +from scheduling.solver.cp_sat.context import SolverContext, create_context +from scheduling.solver.cp_sat.objectives.balance_generated_assignments import ( + add_balance_generated_assignments_objective, +) +from scheduling.solver.cp_sat.variables import create_assignment_variables +from scheduling.solver.models import Solution, SolutionStatus + +logger = logging.getLogger(__name__) + +type ModelStepFunction = Callable[[SolverContext], None] + + +CONSTRAINTS: tuple[ModelStepFunction, ...] = ( + add_minimum_staffing_constraints, + add_one_assignment_per_employee_day_constraints, +) + +OBJECTIVES: tuple[ModelStepFunction, ...] = (add_balance_generated_assignments_objective,) class SolverService: - """Thin solver boundary. - - Currently fake. Later this class can delegate to OR-Tools without changing - the API integration contract. - """ - - def solve(self, dataset: SchedulingDataset) -> SolverResult: - return SolverResult( - status="succeeded", - message=( - "Fake solve completed for " - f"{len(dataset.employees)} employees, " - f"{len(dataset.planning_units)} planning units, " - f"{len(dataset.demand_requirements)} demand requirements." - ), - assignments_created=0, + """Builds and solves the CP-SAT scheduling model.""" + + def __init__(self, settings: Settings) -> None: + self._settings = settings + + def solve(self, dataset: SchedulingDataset) -> Solution: + ctx = self._build_model(dataset) + solver = self._create_solver() + + logger.info( + "Solving CP-SAT model: max_time_seconds=%s search_workers=%s random_seed=%s", + self._settings.solver_max_time_seconds, + self._settings.solver_num_search_workers, + self._settings.solver_random_seed, + ) + + cp_status = solver.solve(ctx.model) + status = self._map_cp_sat_status(cp_status) + + assignments = ( + self._extract_assignments(ctx=ctx, solver=solver) + if status in {SolutionStatus.OPTIMAL, SolutionStatus.FEASIBLE} + else () + ) + + self._log_solve_result( + status=status, + assignment_count=len(assignments), + diagnostic_count=len(ctx.diagnostics), + solver=solver, + ) + + if ctx.diagnostics: + logger.debug("Solver diagnostics: diagnostics=%s", tuple(ctx.diagnostics)) + + return Solution( + status=status, + assignments=assignments, + diagnostics=tuple(ctx.diagnostics), ) + + def _build_model(self, dataset: SchedulingDataset) -> SolverContext: + logger.info( + "Building CP-SAT model: employees=%s planning_units=%s shifts=%s " + "existing_assignments=%s demand_requirements=%s", + len(dataset.employees), + len(dataset.planning_units), + len(dataset.shifts), + len(dataset.assignments), + len(dataset.demand_requirements), + ) + + ctx = create_context(dataset=dataset) + + create_assignment_variables(ctx) + + for add_constraint in CONSTRAINTS: + logger.debug("Applying solver constraint: name=%s", add_constraint.__name__) + add_constraint(ctx) + + for add_objective in OBJECTIVES: + logger.debug("Applying solver objective: name=%s", add_objective.__name__) + add_objective(ctx) + + if ctx.objective_terms: + objective = sum(term.weight * term.expression for term in ctx.objective_terms) + ctx.model.minimize(objective) + else: + logger.debug("No solver objective terms registered.") + + logger.info( + "Built CP-SAT model: variables=%s constraints=%s objective_terms=%s diagnostics=%s", + len(ctx.assignment_variables), + len(CONSTRAINTS), + tuple(term.name for term in ctx.objective_terms), + len(ctx.diagnostics), + ) + + return ctx + + def _create_solver(self) -> cp_model.CpSolver: + solver = cp_model.CpSolver() + + solver.parameters.max_time_in_seconds = self._settings.solver_max_time_seconds + solver.parameters.log_search_progress = self._settings.solver_log_search_progress + + if self._settings.solver_num_search_workers is not None: + solver.parameters.num_search_workers = self._settings.solver_num_search_workers + + if self._settings.solver_random_seed is not None: + solver.parameters.random_seed = self._settings.solver_random_seed + + return solver + + def _map_cp_sat_status(self, status: cp_model.CpSolverStatus) -> SolutionStatus: + if status == cp_model.OPTIMAL: + return SolutionStatus.OPTIMAL + + if status == cp_model.FEASIBLE: + return SolutionStatus.FEASIBLE + + if status == cp_model.INFEASIBLE: + return SolutionStatus.INFEASIBLE + + if status == cp_model.MODEL_INVALID: + return SolutionStatus.MODEL_INVALID + + return SolutionStatus.UNKNOWN + + def _extract_assignments( + self, + *, + ctx: SolverContext, + solver: cp_model.CpSolver, + ) -> tuple[Assignment, ...]: + assignments: list[Assignment] = [] + + for key, variable in ctx.assignment_variables.items(): + if solver.value(variable) != 1: + continue + + employee_id, planning_unit_id, assignment_date, shift_id, _ = key + + assignments.append( + Assignment( + employee_id=employee_id, + planning_unit_id=planning_unit_id, + date=assignment_date, + shift_id=shift_id, + assignment_type=AssignmentType.GENERATED, + ) + ) + + return tuple( + sorted( + assignments, + key=lambda assignment: ( + assignment.planning_unit_id, + assignment.date, + assignment.shift_id, + assignment.employee_id, + ), + ) + ) + + def _log_solve_result( + self, + *, + status: SolutionStatus, + assignment_count: int, + diagnostic_count: int, + solver: cp_model.CpSolver, + ) -> None: + message = "Solved CP-SAT model: status=%s generated_assignments=%s diagnostics=%s wall_time_seconds=%.3f" + args = ( + status.value, + assignment_count, + diagnostic_count, + solver.wall_time, + ) + + if status in {SolutionStatus.OPTIMAL, SolutionStatus.FEASIBLE}: + logger.info(message, *args) + elif status == SolutionStatus.MODEL_INVALID: + logger.error(message, *args) + else: + logger.warning(message, *args) diff --git a/src/scheduling/solver/tmp.py b/src/scheduling/solver/tmp.py deleted file mode 100644 index 2bc5bd84..00000000 --- a/src/scheduling/solver/tmp.py +++ /dev/null @@ -1,7 +0,0 @@ -from scheduling.domain import SchedulingBaseModel - - -class SolverResult(SchedulingBaseModel): - status: str - message: str - assignments_created: int = 0 diff --git a/src/scheduling/timeoffice/database.py b/src/scheduling/timeoffice/database.py index 8357bbe3..af383abb 100644 --- a/src/scheduling/timeoffice/database.py +++ b/src/scheduling/timeoffice/database.py @@ -2,7 +2,7 @@ from sqlalchemy import URL, Engine, create_engine -from scheduling.domain import PlanningPeriod +from scheduling.domain import PlanningMonth from scheduling.settings import Settings from scheduling.timeoffice.facts import TimeOfficeFacts from scheduling.timeoffice.repositories import TimeOfficeRepositories @@ -50,7 +50,7 @@ def fetch_dataset( self, *, selected_planning_unit_ids: tuple[int, ...], - period: PlanningPeriod, + planning_month: PlanningMonth, ) -> ValidatedSchedulingDataset: if not selected_planning_unit_ids: raise ValueError("At least one planning unit must be selected.") @@ -59,7 +59,7 @@ def fetch_dataset( planning_unit_result = self._repositories.planning_units.fetch( connection=connection, selected_planning_unit_ids=selected_planning_unit_ids, - period=period, + planning_month=planning_month, ) planning_unit_ids = tuple( @@ -70,7 +70,7 @@ def fetch_dataset( connection=connection, plans=planning_unit_result.plans, planning_unit_ids=planning_unit_ids, - period=period, + planning_month=planning_month, ) shift_result = self._repositories.shifts.fetch( @@ -81,19 +81,19 @@ def fetch_dataset( connection=connection, plans=planning_unit_result.plans, employees=personnel_result.employees, - period=period, + planning_month=planning_month, ) demand_result = self._repositories.demand.fetch( connection=connection, - period=period, + planning_month=planning_month, planning_units=planning_unit_result.planning_units, shifts=shift_result.shifts, ) sunday_work_history_result = self._repositories.sunday_work_history.fetch( connection=connection, - period=period, + planning_month=planning_month, employees=personnel_result.employees, ) @@ -102,17 +102,17 @@ def fetch_dataset( plans=planning_unit_result.plans, employees=personnel_result.employees, shifts=shift_result.shifts, - period=period, + planning_month=planning_month, ) monthly_work_account_result = self._repositories.monthly_work_accounts.fetch( connection=connection, employees=personnel_result.employees, - period=period, + planning_month=planning_month, ) return ValidatedSchedulingDataset( - period=period, + planning_month=planning_month, planning_units=planning_unit_result.planning_units, plans=planning_unit_result.plans, employees=personnel_result.employees, diff --git a/src/scheduling/timeoffice/repositories/demand.py b/src/scheduling/timeoffice/repositories/demand.py index 6433d284..423abbd8 100644 --- a/src/scheduling/timeoffice/repositories/demand.py +++ b/src/scheduling/timeoffice/repositories/demand.py @@ -4,7 +4,7 @@ from scheduling.domain import ( DemandRequirement, - PlanningPeriod, + PlanningMonth, PlanningUnit, PlanningUnitKind, SchedulingBaseModel, @@ -40,7 +40,7 @@ def fetch( self, *, connection: Connection, - period: PlanningPeriod, + planning_month: PlanningMonth, planning_units: tuple[PlanningUnit, ...], shifts: tuple[Shift, ...], ) -> DemandRepositoryResult: @@ -50,7 +50,7 @@ def fetch( return DemandRepositoryResult( demand_requirements=self._build_from_facts( - period=period, + planning_month=planning_month, planning_units=planning_units, shifts=shifts, ) @@ -59,7 +59,7 @@ def fetch( def _build_from_facts( self, *, - period: PlanningPeriod, + planning_month: PlanningMonth, planning_units: tuple[PlanningUnit, ...], shifts: tuple[Shift, ...], ) -> tuple[DemandRequirement, ...]: @@ -74,8 +74,8 @@ def _build_from_facts( DemandRequirement, ] = {} - current_date = period.start - while current_date <= period.end: + current_date = planning_month.start + while current_date <= planning_month.end: iso_weekday = current_date.isoweekday() for planning_unit in station_planning_units: diff --git a/src/scheduling/timeoffice/repositories/monthly_work_accounts.py b/src/scheduling/timeoffice/repositories/monthly_work_accounts.py index 336d1917..fe019d04 100644 --- a/src/scheduling/timeoffice/repositories/monthly_work_accounts.py +++ b/src/scheduling/timeoffice/repositories/monthly_work_accounts.py @@ -1,14 +1,9 @@ -from typing import Self - -from pydantic import model_validator from sqlalchemy import Connection, bindparam, text -from scheduling.domain import Employee, MonthlyWorkAccount, PlanningPeriod, SchedulingBaseModel +from scheduling.domain import Employee, MonthlyWorkAccount, PlanningMonth, SchedulingBaseModel from scheduling.timeoffice.facts import TimeOfficeFacts from scheduling.timeoffice.repositories.types import ( - CleanNullableText, SourceInt, - SourceNullableInt, TimeOfficeSourceRow, ) @@ -16,47 +11,16 @@ class _TimeOfficeMonthlyWorkAccountRow(TimeOfficeSourceRow): employee_id: SourceInt month: SourceInt - - target_account_id: SourceInt - target_account_code: CleanNullableText = None - target_account_name: CleanNullableText = None - target_account_short_name: CleanNullableText = None target_hours: float | None = None - - actual_account_id: SourceNullableInt = None - actual_account_code: CleanNullableText = None - actual_account_name: CleanNullableText = None - actual_account_short_name: CleanNullableText = None actual_hours: float | None = None - @model_validator(mode="after") - def validate_actual_account_shape(self) -> Self: - if self.actual_account_id is None and self.actual_hours is not None: - raise ValueError( - "Monthly actual hours are present without an actual account id: " - f"employee_id={self.employee_id} " - f"month={self.month} " - f"actual_hours={self.actual_hours!r}." - ) - - return self - class TimeOfficeMonthlyWorkAccountRepositoryResult(SchedulingBaseModel): monthly_work_accounts: tuple[MonthlyWorkAccount, ...] class TimeOfficeMonthlyWorkAccountRepository: - """Reads monthly target/actual work account values from TimeOffice. - - Source: - - TPersonalKontenJeMonat.Monat is YYYYMM - - RefKonten=1 / SOLL_MONAT provides target monthly hours in Wert2 - - RefKonten=55 / TOTAL provides actual monthly hours in Wert2 - - Rows with target_minutes <= 0 are intentionally not emitted because an - all-zero target would be misleading for scheduling. - """ + """Reads monthly target and actual work hours from TimeOffice account values.""" def __init__(self, *, facts: TimeOfficeFacts) -> None: self._facts = facts @@ -66,15 +30,15 @@ def fetch( *, connection: Connection, employees: tuple[Employee, ...], - period: PlanningPeriod, + planning_month: PlanningMonth, ) -> TimeOfficeMonthlyWorkAccountRepositoryResult: if not employees: return TimeOfficeMonthlyWorkAccountRepositoryResult(monthly_work_accounts=()) rows = self._fetch_rows( connection=connection, - employees=employees, - month=self._month(period), + employee_ids=tuple(employee.employee_id for employee in employees), + month=self._timeoffice_month(planning_month), ) self._validate_unique_employee_rows(rows) @@ -85,7 +49,7 @@ def _fetch_rows( self, *, connection: Connection, - employees: tuple[Employee, ...], + employee_ids: tuple[int, ...], month: int, ) -> tuple[_TimeOfficeMonthlyWorkAccountRow, ...]: query = text( @@ -93,27 +57,13 @@ def _fetch_rows( SELECT target.RefPersonal AS employee_id, target.Monat AS month, - - target.RefKonten AS target_account_id, - target_account.BezProg AS target_account_code, - target_account.Bez AS target_account_name, - target_account.BezKurz AS target_account_short_name, target.Wert2 AS target_hours, - - actual.RefKonten AS actual_account_id, - actual_account.BezProg AS actual_account_code, - actual_account.Bez AS actual_account_name, - actual_account.BezKurz AS actual_account_short_name, actual.Wert2 AS actual_hours FROM TPersonalKontenJeMonat target - JOIN TKonten target_account - ON target_account.Prim = target.RefKonten LEFT JOIN TPersonalKontenJeMonat actual ON actual.RefPersonal = target.RefPersonal AND actual.Monat = target.Monat AND actual.RefKonten = :actual_account_id - LEFT JOIN TKonten actual_account - ON actual_account.Prim = actual.RefKonten WHERE target.RefPersonal IN :employee_ids AND target.Monat = :month AND target.RefKonten = :target_account_id @@ -126,7 +76,7 @@ def _fetch_rows( connection.execute( query, { - "employee_ids": tuple(employee.employee_id for employee in employees), + "employee_ids": employee_ids, "month": month, "target_account_id": self._facts.monthly_target_work_account_id, "actual_account_id": self._facts.monthly_actual_work_account_id, @@ -138,19 +88,25 @@ def _fetch_rows( return tuple(_TimeOfficeMonthlyWorkAccountRow.model_validate(row) for row in raw_rows) - def _validate_unique_employee_rows(self, rows: tuple[_TimeOfficeMonthlyWorkAccountRow, ...]) -> None: + def _validate_unique_employee_rows( + self, + rows: tuple[_TimeOfficeMonthlyWorkAccountRow, ...], + ) -> None: seen_employee_ids: set[int] = set() for row in rows: if row.employee_id in seen_employee_ids: raise ValueError( "Duplicate TimeOffice monthly work target account row for " - f"employee_id={row.employee_id} month={row.month!r}." + f"employee_id={row.employee_id} month={row.month}." ) seen_employee_ids.add(row.employee_id) - def _map_accounts(self, rows: tuple[_TimeOfficeMonthlyWorkAccountRow, ...]) -> tuple[MonthlyWorkAccount, ...]: + def _map_accounts( + self, + rows: tuple[_TimeOfficeMonthlyWorkAccountRow, ...], + ) -> tuple[MonthlyWorkAccount, ...]: accounts: list[MonthlyWorkAccount] = [] for row in rows: @@ -159,31 +115,23 @@ def _map_accounts(self, rows: tuple[_TimeOfficeMonthlyWorkAccountRow, ...]) -> t if target_minutes <= 0: continue + actual_minutes = self._hours_to_minutes(row.actual_hours) + accounts.append( MonthlyWorkAccount( employee_id=row.employee_id, target_minutes=target_minutes, - actual_minutes=self._optional_hours_to_minutes(row.actual_hours), + actual_minutes=actual_minutes if actual_minutes > 0 else None, ) ) return tuple(sorted(accounts, key=lambda account: account.employee_id)) - def _month(self, period: PlanningPeriod) -> int: - if period.start.year != period.end.year or period.start.month != period.end.month: - raise ValueError( - "Monthly work accounts can only be imported for a single calendar month. " - f"Got period {period.start}..{period.end}." - ) - - return period.start.year * 100 + period.start.month + def _timeoffice_month(self, planning_month: PlanningMonth) -> int: + return planning_month.year * 100 + planning_month.month def _hours_to_minutes(self, value: float | None) -> int: if value is None: return 0 return round(value * 60) - - def _optional_hours_to_minutes(self, value: float | None) -> int | None: - minutes = self._hours_to_minutes(value) - return minutes if minutes > 0 else None diff --git a/src/scheduling/timeoffice/repositories/personnel.py b/src/scheduling/timeoffice/repositories/personnel.py index b9b6d084..728ff894 100644 --- a/src/scheduling/timeoffice/repositories/personnel.py +++ b/src/scheduling/timeoffice/repositories/personnel.py @@ -8,7 +8,7 @@ Capability, Employee, Plan, - PlanningPeriod, + PlanningMonth, PlanningUnitMembership, PlanParticipant, SchedulingBaseModel, @@ -68,7 +68,7 @@ def fetch( connection: Connection, plans: tuple[Plan, ...], planning_unit_ids: tuple[int, ...], - period: PlanningPeriod, + planning_month: PlanningMonth, ) -> PersonnelRepositoryResult: if not plans: return PersonnelRepositoryResult( @@ -90,7 +90,7 @@ def fetch( connection=connection, planning_unit_ids=planning_unit_ids, employee_ids=tuple(employee.employee_id for employee in employees), - period=period, + planning_month=planning_month, ) return PersonnelRepositoryResult( @@ -145,7 +145,7 @@ def _fetch_membership_rows( connection: Connection, planning_unit_ids: tuple[int, ...], employee_ids: tuple[int, ...], - period: PlanningPeriod, + planning_month: PlanningMonth, ) -> tuple[_TimeOfficePlanningUnitMembershipRow, ...]: if not planning_unit_ids or not employee_ids: return () @@ -163,10 +163,10 @@ def _fetch_membership_rows( FROM TPlanungseinheitenPersonal pep WHERE pep.RefPlanungseinheiten IN :planning_unit_ids AND pep.RefPersonal IN :employee_ids - AND CONVERT(date, pep.VonDat) <= :period_end + AND CONVERT(date, pep.VonDat) <= :end AND ( pep.BisDat IS NULL - OR CONVERT(date, pep.BisDat) >= :period_start + OR CONVERT(date, pep.BisDat) >= :start ) AND ISNULL(pep.KeinEPlan, 0) = 0 ORDER BY @@ -186,8 +186,8 @@ def _fetch_membership_rows( { "planning_unit_ids": planning_unit_ids, "employee_ids": employee_ids, - "period_start": period.start, - "period_end": period.end, + "start": planning_month.start, + "end": planning_month.end, }, ) .mappings() diff --git a/src/scheduling/timeoffice/repositories/planning_units.py b/src/scheduling/timeoffice/repositories/planning_units.py index ad26549d..7bec66f6 100644 --- a/src/scheduling/timeoffice/repositories/planning_units.py +++ b/src/scheduling/timeoffice/repositories/planning_units.py @@ -3,7 +3,7 @@ from pydantic import model_validator from sqlalchemy import Connection, bindparam, text -from scheduling.domain import Plan, PlanningPeriod, PlanningUnit, PlanningUnitKind, SchedulingBaseModel +from scheduling.domain import Plan, PlanningMonth, PlanningUnit, PlanningUnitKind, SchedulingBaseModel from scheduling.timeoffice.facts import TimeOfficeFacts from scheduling.timeoffice.repositories.types import SourceInt, TimeOfficeSourceRow @@ -42,7 +42,7 @@ def fetch( *, connection: Connection, selected_planning_unit_ids: tuple[int, ...], - period: PlanningPeriod, + planning_month: PlanningMonth, ) -> PlanningUnitRepositoryResult: if not selected_planning_unit_ids: return PlanningUnitRepositoryResult(planning_units=(), plans=()) @@ -50,7 +50,7 @@ def fetch( rows = self._fetch_rows( connection=connection, selected_planning_unit_ids=selected_planning_unit_ids, - period=period, + planning_month=planning_month, ) self._validate_rows( @@ -68,7 +68,7 @@ def _fetch_rows( *, connection: Connection, selected_planning_unit_ids: tuple[int, ...], - period: PlanningPeriod, + planning_month: PlanningMonth, ) -> tuple[_TimeOfficePlanningUnitRow, ...]: query = text( """ @@ -82,8 +82,8 @@ def _fetch_rows( WHERE pe.Prim IN :planning_unit_ids AND p.RefPlanungsIntervalle = :planning_interval_id AND p.RefStati = :planning_status_id - AND CONVERT(date, p.VonDat) = :period_start - AND CONVERT(date, p.BisDat) = :period_end + AND CONVERT(date, p.VonDat) = :start + AND CONVERT(date, p.BisDat) = :end ORDER BY pe.Prim """ ).bindparams(bindparam("planning_unit_ids", expanding=True)) @@ -93,8 +93,8 @@ def _fetch_rows( query, { "planning_unit_ids": selected_planning_unit_ids, - "period_start": period.start, - "period_end": period.end, + "start": planning_month.start, + "end": planning_month.end, "planning_interval_id": self._facts.monthly_planning_interval_id, "planning_status_id": self._facts.target_planning_status_id, }, diff --git a/src/scheduling/timeoffice/repositories/roster.py b/src/scheduling/timeoffice/repositories/roster.py index ff93f3ad..025a2f22 100644 --- a/src/scheduling/timeoffice/repositories/roster.py +++ b/src/scheduling/timeoffice/repositories/roster.py @@ -11,7 +11,7 @@ AvailabilityType, Employee, Plan, - PlanningPeriod, + PlanningMonth, SchedulingBaseModel, ) from scheduling.timeoffice.facts import TimeOfficeFacts, TimeOfficeShiftFact @@ -94,7 +94,7 @@ def fetch( connection: Connection, plans: tuple[Plan, ...], employees: tuple[Employee, ...], - period: PlanningPeriod, + planning_month: PlanningMonth, ) -> RosterRepositoryResult: if not plans or not employees: return RosterRepositoryResult(assignments=(), availability=()) @@ -102,7 +102,7 @@ def fetch( rows = self._fetch_rows( connection=connection, employees=employees, - period=period, + planning_month=planning_month, ) selected_plan_ids = {plan.plan_id for plan in plans} @@ -122,7 +122,7 @@ def _fetch_rows( *, connection: Connection, employees: tuple[Employee, ...], - period: PlanningPeriod, + planning_month: PlanningMonth, ) -> tuple[_TimeOfficeRosterRow, ...]: query = text( """ @@ -149,7 +149,7 @@ def _fetch_rows( LEFT JOIN TDienste absence_d ON absence_d.Prim = pkg.RefDienstAbw WHERE pkg.RefPersonal IN :employee_ids - AND CONVERT(date, pkg.Datum) BETWEEN :period_start AND :period_end + AND CONVERT(date, pkg.Datum) BETWEEN :start AND :end AND ( pkg.RefDienste IS NOT NULL OR pkg.RefgAbw IS NOT NULL @@ -168,8 +168,8 @@ def _fetch_rows( query, { "employee_ids": tuple(employee.employee_id for employee in employees), - "period_start": period.start, - "period_end": period.end, + "start": planning_month.start, + "end": planning_month.end, }, ) .mappings() diff --git a/src/scheduling/timeoffice/repositories/sunday_work_history.py b/src/scheduling/timeoffice/repositories/sunday_work_history.py index 77679345..1dc5ba6d 100644 --- a/src/scheduling/timeoffice/repositories/sunday_work_history.py +++ b/src/scheduling/timeoffice/repositories/sunday_work_history.py @@ -2,7 +2,7 @@ from sqlalchemy import Connection, bindparam, text -from scheduling.domain import Employee, EmployeeSundayWorkHistory, PlanningPeriod, SchedulingBaseModel +from scheduling.domain import Employee, EmployeeSundayWorkHistory, PlanningMonth, SchedulingBaseModel from scheduling.timeoffice.repositories.types import CleanNullableText, SourceInt, TimeOfficeSourceRow @@ -40,7 +40,7 @@ def fetch( self, *, connection: Connection, - period: PlanningPeriod, + planning_month: PlanningMonth, employees: tuple[Employee, ...], ) -> SundayWorkHistoryRepositoryResult: if not employees: @@ -52,8 +52,8 @@ def fetch( connection=connection, employees=employees, sunday_account_id=sunday_account_id, - lookback_start=self._subtract_years(period.end, self.LOOKBACK_YEARS), - lookback_end=period.end, + lookback_start=self._subtract_years(planning_month.end, self.LOOKBACK_YEARS), + lookback_end=planning_month.end, ) return SundayWorkHistoryRepositoryResult(sunday_work_history=self._map_history_rows(rows)) diff --git a/src/scheduling/timeoffice/repositories/wishes.py b/src/scheduling/timeoffice/repositories/wishes.py index d00ce721..8b498030 100644 --- a/src/scheduling/timeoffice/repositories/wishes.py +++ b/src/scheduling/timeoffice/repositories/wishes.py @@ -5,7 +5,7 @@ from pydantic import model_validator from sqlalchemy import Connection, bindparam, text -from scheduling.domain import Employee, Plan, PlanningPeriod, SchedulingBaseModel, Shift, Wish, WishKind +from scheduling.domain import Employee, Plan, PlanningMonth, SchedulingBaseModel, Shift, Wish, WishKind from scheduling.timeoffice.facts import TimeOfficeFacts, TimeOfficeShiftFact from scheduling.timeoffice.repositories.types import ( CleanNullableText, @@ -99,7 +99,7 @@ def fetch( plans: tuple[Plan, ...], employees: tuple[Employee, ...], shifts: tuple[Shift, ...], - period: PlanningPeriod, + planning_month: PlanningMonth, ) -> TimeOfficeWishRepositoryResult: if not plans or not employees: return TimeOfficeWishRepositoryResult(wishes=()) @@ -108,7 +108,7 @@ def fetch( connection=connection, plans=plans, employees=employees, - period=period, + planning_month=planning_month, ) wishes = self._map_wishes(rows=rows, shifts=shifts) @@ -121,7 +121,7 @@ def _fetch_rows( connection: Connection, plans: tuple[Plan, ...], employees: tuple[Employee, ...], - period: PlanningPeriod, + planning_month: PlanningMonth, ) -> tuple[_TimeOfficeWishRow, ...]: query = text( """ @@ -156,7 +156,7 @@ def _fetch_rows( WHERE pkg.RefPersonal IN :employee_ids AND pkg.RefPlan IN :plan_ids AND pkg.RefPlanungseinheiten IN :planning_unit_ids - AND CONVERT(date, pkg.Datum) BETWEEN :period_start AND :period_end + AND CONVERT(date, pkg.Datum) BETWEEN :start AND :end AND ISNULL(pkg.Wunschdienst, 0) <> 0 AND ( pkg.RefDienste IS NOT NULL @@ -185,8 +185,8 @@ def _fetch_rows( "plan_ids": tuple(plan.plan_id for plan in plans), "planning_unit_ids": tuple(plan.planning_unit_id for plan in plans), "employee_ids": tuple(employee.employee_id for employee in employees), - "period_start": period.start, - "period_end": period.end, + "start": planning_month.start, + "end": planning_month.end, }, ) .mappings() diff --git a/src/scheduling/timeoffice/service.py b/src/scheduling/timeoffice/service.py index d4466279..e79994c7 100644 --- a/src/scheduling/timeoffice/service.py +++ b/src/scheduling/timeoffice/service.py @@ -1,8 +1,13 @@ -from scheduling.domain import PlanningPeriod +import logging + +from scheduling.domain import AssignmentType, PlanningMonth +from scheduling.solver.models import Solution, SolutionStatus from scheduling.timeoffice.database import TimeOfficeDatabase from scheduling.timeoffice.facts import TimeOfficeFacts from scheduling.validation.dataset import ValidatedSchedulingDataset +logger = logging.getLogger(__name__) + class TimeOfficeService: """Application-facing service for loading scheduling data from TimeOffice.""" @@ -20,14 +25,34 @@ def fetch_dataset( self, *, planning_unit_ids: tuple[int, ...], - period: PlanningPeriod, + planning_month: PlanningMonth, ) -> ValidatedSchedulingDataset: selected_planning_unit_ids = self._normalize_planning_unit_ids(planning_unit_ids) - return self._database.fetch_dataset( + logger.info( + "Fetching TimeOffice dataset: planning_units=%s planning_month=%s", + planning_unit_ids, + planning_month.label, + ) + dataset = self._database.fetch_dataset( selected_planning_unit_ids=selected_planning_unit_ids, - period=period, + planning_month=planning_month, + ) + logger.info( + "Fetched TimeOffice dataset: planning_units=%s plans=%s employees=%s " + "memberships=%s shifts=%s assignments=%s availability=%s " + "minimum_staffing_requirements=%s wishes=%s", + len(dataset.planning_units), + len(dataset.plans), + len(dataset.employees), + len(dataset.planning_unit_memberships), + len(dataset.shifts), + len(dataset.assignments), + len(dataset.availability), + len(dataset.demand_requirements), + len(dataset.wishes), ) + return dataset def _normalize_planning_unit_ids( self, @@ -51,3 +76,36 @@ def _normalize_planning_unit_ids( ) return normalized + + def write_solution_dry_run(self, solution: Solution) -> None: + """Log generated assignments that would later be written to TimeOffice.""" + if solution.status not in {SolutionStatus.OPTIMAL, SolutionStatus.FEASIBLE}: + logger.info( + "Skipping TimeOffice writeback dry-run because solution is not feasible: status=%s", + solution.status.value, + ) + return + + generated_assignments = [ + assignment for assignment in solution.assignments if assignment.assignment_type == AssignmentType.GENERATED + ] + + logger.info( + "Running TimeOffice writeback dry-run: generated_assignments=%s", + len(generated_assignments), + ) + + for assignment in generated_assignments: + logger.debug( + "Generated assignment for TimeOffice writeback dry-run: " + "employee_id=%s planning_unit_id=%s date=%s shift_id=%s", + assignment.employee_id, + assignment.planning_unit_id, + assignment.date.isoformat(), + assignment.shift_id, + ) + + logger.info( + "Finished TimeOffice writeback dry-run: generated_assignments=%s written=0", + len(generated_assignments), + ) diff --git a/src/scheduling/validation/context.py b/src/scheduling/validation/context.py index a9d6495e..0cf70075 100644 --- a/src/scheduling/validation/context.py +++ b/src/scheduling/validation/context.py @@ -8,10 +8,10 @@ PlanId, PlanningUnitId, PlanningUnitKind, + SchedulingDataset, Shift, ShiftId, ) -from scheduling.domain.dataset import SchedulingDataset from scheduling.validation.helpers import ensure_unique diff --git a/src/scheduling/validation/dataset.py b/src/scheduling/validation/dataset.py index 0e28d2e9..fd77f3ab 100644 --- a/src/scheduling/validation/dataset.py +++ b/src/scheduling/validation/dataset.py @@ -2,7 +2,7 @@ from pydantic import model_validator -from scheduling.domain.dataset import SchedulingDataset +from scheduling.domain import SchedulingDataset from scheduling.validation.context import DatasetValidationContext from scheduling.validation.validators import ( validate_assignments, diff --git a/src/scheduling/validation/validators.py b/src/scheduling/validation/validators.py index 402dc87a..02354479 100644 --- a/src/scheduling/validation/validators.py +++ b/src/scheduling/validation/validators.py @@ -1,18 +1,19 @@ -from datetime import date as Date +from datetime import date from scheduling.domain import ( AssignmentType, AvailabilityType, EmployeeId, PlanId, + PlanningMonth, PlanningUnitId, PlanningUnitKind, + SchedulingDataset, ShiftId, StaffingDemandRole, StaffLevel, WishKind, ) -from scheduling.domain.dataset import SchedulingDataset from scheduling.validation.context import DatasetValidationContext @@ -61,7 +62,7 @@ def validate_plan_participants(dataset: SchedulingDataset, context: DatasetValid def validate_planning_unit_memberships(dataset: SchedulingDataset, context: DatasetValidationContext) -> None: - seen: set[tuple[PlanningUnitId, EmployeeId, Date, Date | None]] = set() + seen: set[tuple[PlanningUnitId, EmployeeId, date, date | None]] = set() for membership in dataset.planning_unit_memberships: if membership.planning_unit_id not in context.planning_unit_ids: @@ -91,13 +92,15 @@ def validate_planning_unit_memberships(dataset: SchedulingDataset, context: Data def validate_assignments(dataset: SchedulingDataset, context: DatasetValidationContext) -> None: - seen: set[tuple[EmployeeId, Date, ShiftId, AssignmentType, PlanningUnitId | None]] = set() + seen: set[tuple[EmployeeId, date, ShiftId, AssignmentType, PlanningUnitId | None]] = set() for assignment in dataset.assignments: - if not dataset.period.contains(assignment.date): - raise ValueError( - f"Assignment outside planning period: employee_id={assignment.employee_id} date={assignment.date}." - ) + _validate_date_in_planning_month( + planning_month=dataset.planning_month, + value=assignment.date, + label="Assignment", + details=f"employee_id={assignment.employee_id}", + ) if assignment.employee_id not in context.employee_ids: raise ValueError(f"Assignment references unknown employee_id={assignment.employee_id}.") @@ -108,11 +111,14 @@ def validate_assignments(dataset: SchedulingDataset, context: DatasetValidationC # Assignment shape invariants belong in Assignment itself. # Dataset validation only checks references. if ( - assignment.assignment_type == AssignmentType.PLANNED + assignment.assignment_type in {AssignmentType.PLANNED, AssignmentType.GENERATED} and assignment.planning_unit_id is not None and assignment.planning_unit_id not in context.planning_unit_ids ): - raise ValueError(f"Planned assignment references unknown planning_unit_id={assignment.planning_unit_id}.") + raise ValueError( + f"{assignment.assignment_type.value} assignment references unknown " + f"planning_unit_id={assignment.planning_unit_id}." + ) key = ( assignment.employee_id, @@ -133,14 +139,15 @@ def validate_assignments(dataset: SchedulingDataset, context: DatasetValidationC def validate_availability(dataset: SchedulingDataset, context: DatasetValidationContext) -> None: - seen: set[tuple[EmployeeId, Date, AvailabilityType, tuple[ShiftId, ...] | None]] = set() + seen: set[tuple[EmployeeId, date, AvailabilityType, tuple[ShiftId, ...] | None]] = set() for availability in dataset.availability: - if not dataset.period.contains(availability.date): - raise ValueError( - "Availability outside planning period: " - f"employee_id={availability.employee_id} date={availability.date}." - ) + _validate_date_in_planning_month( + planning_month=dataset.planning_month, + value=availability.date, + label="Availability", + details=f"employee_id={availability.employee_id}", + ) if availability.employee_id not in context.employee_ids: raise ValueError(f"Availability references unknown employee_id={availability.employee_id}.") @@ -168,14 +175,15 @@ def validate_availability(dataset: SchedulingDataset, context: DatasetValidation def validate_demand_requirements(dataset: SchedulingDataset, context: DatasetValidationContext) -> None: - seen: set[tuple[PlanningUnitId, Date, ShiftId, StaffLevel]] = set() + seen: set[tuple[PlanningUnitId, date, ShiftId, StaffLevel]] = set() for demand in dataset.demand_requirements: - if not dataset.period.contains(demand.date): - raise ValueError( - "DemandRequirement outside planning period: " - f"planning_unit_id={demand.planning_unit_id} date={demand.date}." - ) + _validate_date_in_planning_month( + planning_month=dataset.planning_month, + value=demand.date, + label="DemandRequirement", + details=f"planning_unit_id={demand.planning_unit_id}", + ) if demand.planning_unit_id not in context.planning_unit_ids: raise ValueError(f"DemandRequirement references unknown planning_unit_id={demand.planning_unit_id}.") @@ -224,7 +232,7 @@ def validate_sunday_work_history(dataset: SchedulingDataset, context: DatasetVal def validate_wishes(dataset: SchedulingDataset, context: DatasetValidationContext) -> None: - seen: set[tuple[EmployeeId, PlanningUnitId, Date, WishKind, ShiftId | None]] = set() + seen: set[tuple[EmployeeId, PlanningUnitId, date, WishKind, ShiftId | None]] = set() for wish in dataset.wishes: if wish.employee_id not in context.employee_ids: @@ -233,8 +241,12 @@ def validate_wishes(dataset: SchedulingDataset, context: DatasetValidationContex if wish.planning_unit_id not in context.planning_unit_ids: raise ValueError(f"Wish references unknown planning_unit_id={wish.planning_unit_id}.") - if not dataset.period.contains(wish.date): - raise ValueError(f"Wish date outside planning period: employee_id={wish.employee_id}, date={wish.date}.") + _validate_date_in_planning_month( + planning_month=dataset.planning_month, + value=wish.date, + label="Wish", + details=f"employee_id={wish.employee_id}", + ) if wish.shift_id is not None and wish.shift_id not in context.shift_ids: raise ValueError(f"Wish references unknown shift_id={wish.shift_id}.") @@ -246,9 +258,15 @@ def validate_wishes(dataset: SchedulingDataset, context: DatasetValidationContex wish.kind, wish.shift_id, ) - if key in seen: - raise ValueError(f"Duplicate wish: {key}.") + raise ValueError( + "Duplicate Wish " + f"employee_id={wish.employee_id} " + f"planning_unit_id={wish.planning_unit_id} " + f"date={wish.date} " + f"kind={wish.kind} " + f"shift_id={wish.shift_id}." + ) seen.add(key) @@ -258,9 +276,27 @@ def validate_monthly_work_accounts(dataset: SchedulingDataset, context: DatasetV for account in dataset.monthly_work_accounts: if account.employee_id not in context.employee_ids: - raise ValueError(f"Monthly work account references unknown employee_id={account.employee_id}.") + raise ValueError(f"MonthlyWorkAccount references unknown employee_id={account.employee_id}.") if account.employee_id in seen_employee_ids: - raise ValueError(f"Duplicate monthly work account for employee_id={account.employee_id}.") + raise ValueError(f"Duplicate MonthlyWorkAccount employee_id={account.employee_id}.") seen_employee_ids.add(account.employee_id) + + +def _validate_date_in_planning_month( + *, + planning_month: PlanningMonth, + value: date, + label: str, + details: str, +) -> None: + if planning_month.start <= value <= planning_month.end: + return + + raise ValueError( + f"{label} outside planning month: " + f"{details} " + f"date={value} " + f"planning_month={planning_month.year:04d}-{planning_month.month:02d}." + ) From 8c55cb66e73461235b85c5f25e66206a983707f7 Mon Sep 17 00:00:00 2001 From: Tom Wawerek Date: Tue, 23 Jun 2026 17:41:40 +0200 Subject: [PATCH 11/18] refactor: facts with better mappings --- src/SELECT.sql | 36 +- src/scheduling/api/app.py | 19 +- src/scheduling/domain/__init__.py | 3 +- src/scheduling/domain/dataset.py | 3 +- src/scheduling/domain/plan.py | 13 - src/scheduling/timeoffice/database.py | 104 ---- src/scheduling/timeoffice/facts.py | 462 +++++++----------- src/scheduling/timeoffice/mapping/__init__.py | 3 + src/scheduling/timeoffice/mapping/dataset.py | 50 ++ src/scheduling/timeoffice/mapping/demand.py | 94 ++++ .../timeoffice/mapping/personnel.py | 87 ++++ src/scheduling/timeoffice/mapping/planning.py | 35 ++ src/scheduling/timeoffice/mapping/roster.py | 123 +++++ src/scheduling/timeoffice/mapping/shifts.py | 194 ++++++++ .../timeoffice/mapping/sunday_work.py | 14 + src/scheduling/timeoffice/mapping/wishes.py | 71 +++ .../timeoffice/mapping/work_accounts.py | 32 ++ src/scheduling/timeoffice/reading/__init__.py | 6 + .../timeoffice/reading/container.py | 161 ++++++ .../timeoffice/reading/personnel.py | 184 +++++++ .../planning_units.py | 95 +--- src/scheduling/timeoffice/reading/roster.py | 120 +++++ src/scheduling/timeoffice/reading/shifts.py | 87 ++++ src/scheduling/timeoffice/reading/sources.py | 0 .../timeoffice/reading/sunday_work.py | 123 +++++ .../{repositories => reading}/types.py | 0 src/scheduling/timeoffice/reading/wishes.py | 153 ++++++ .../timeoffice/reading/work_accounts.py | 69 +++ .../timeoffice/repositories/__init__.py | 29 -- .../timeoffice/repositories/container.py | 36 -- .../timeoffice/repositories/demand.py | 158 ------ .../repositories/monthly_work_accounts.py | 137 ------ .../timeoffice/repositories/personnel.py | 297 ----------- .../timeoffice/repositories/roster.py | 384 --------------- .../timeoffice/repositories/shifts.py | 229 --------- .../repositories/sunday_work_history.py | 154 ------ .../timeoffice/repositories/wishes.py | 315 ------------ src/scheduling/timeoffice/service.py | 116 ++--- src/scheduling/timeoffice/writing/__init__.py | 0 src/scheduling/timeoffice/writing/solution.py | 46 ++ src/scheduling/validation/__init__.py | 3 + src/scheduling/validation/context.py | 40 +- src/scheduling/validation/dataset.py | 39 +- src/scheduling/validation/helpers.py | 17 - src/scheduling/validation/validators.py | 64 +-- 45 files changed, 2014 insertions(+), 2391 deletions(-) create mode 100644 src/scheduling/timeoffice/mapping/__init__.py create mode 100644 src/scheduling/timeoffice/mapping/dataset.py create mode 100644 src/scheduling/timeoffice/mapping/demand.py create mode 100644 src/scheduling/timeoffice/mapping/personnel.py create mode 100644 src/scheduling/timeoffice/mapping/planning.py create mode 100644 src/scheduling/timeoffice/mapping/roster.py create mode 100644 src/scheduling/timeoffice/mapping/shifts.py create mode 100644 src/scheduling/timeoffice/mapping/sunday_work.py create mode 100644 src/scheduling/timeoffice/mapping/wishes.py create mode 100644 src/scheduling/timeoffice/mapping/work_accounts.py create mode 100644 src/scheduling/timeoffice/reading/__init__.py create mode 100644 src/scheduling/timeoffice/reading/container.py create mode 100644 src/scheduling/timeoffice/reading/personnel.py rename src/scheduling/timeoffice/{repositories => reading}/planning_units.py (50%) create mode 100644 src/scheduling/timeoffice/reading/roster.py create mode 100644 src/scheduling/timeoffice/reading/shifts.py create mode 100644 src/scheduling/timeoffice/reading/sources.py create mode 100644 src/scheduling/timeoffice/reading/sunday_work.py rename src/scheduling/timeoffice/{repositories => reading}/types.py (100%) create mode 100644 src/scheduling/timeoffice/reading/wishes.py create mode 100644 src/scheduling/timeoffice/reading/work_accounts.py delete mode 100644 src/scheduling/timeoffice/repositories/__init__.py delete mode 100644 src/scheduling/timeoffice/repositories/container.py delete mode 100644 src/scheduling/timeoffice/repositories/demand.py delete mode 100644 src/scheduling/timeoffice/repositories/monthly_work_accounts.py delete mode 100644 src/scheduling/timeoffice/repositories/personnel.py delete mode 100644 src/scheduling/timeoffice/repositories/roster.py delete mode 100644 src/scheduling/timeoffice/repositories/shifts.py delete mode 100644 src/scheduling/timeoffice/repositories/sunday_work_history.py delete mode 100644 src/scheduling/timeoffice/repositories/wishes.py create mode 100644 src/scheduling/timeoffice/writing/__init__.py create mode 100644 src/scheduling/timeoffice/writing/solution.py delete mode 100644 src/scheduling/validation/helpers.py diff --git a/src/SELECT.sql b/src/SELECT.sql index d56586a7..79db3b7a 100644 --- a/src/SELECT.sql +++ b/src/SELECT.sql @@ -1,16 +1,24 @@ SELECT - TABLE_NAME, - COLUMN_NAME, - DATA_TYPE, - IS_NULLABLE -FROM INFORMATION_SCHEMA.COLUMNS -WHERE TABLE_NAME IN ( - 'TPersonalDienstArten', - 'TPersonalRollmodelle', - 'TPersonalParameter', - 'TPersonalEinsatzorte', - 'TPersonalStatusJeTag' -) + pkg.RefPlan AS plan_id, + p.RefPlanungseinheiten AS plan_planning_unit_id, + pkg.RefPlanungseinheiten AS row_planning_unit_id, + pkg.RefPersonal AS employee_id, + pkg.Datum AS roster_date, + pkg.RefDienste AS work_shift_id, + d.KurzBez AS work_shift_code, + d.Bezeichnung AS work_shift_name, + pkg.RefgAbw AS global_absence_shift_id, + pkg.RefDienstAbw AS absence_shift_id, + pkg.lfdNr AS line_number +FROM TPlanPersonalKommtGeht pkg +LEFT JOIN TPlan p + ON p.Prim = pkg.RefPlan +LEFT JOIN TDienste d + ON d.Prim = pkg.RefDienste +WHERE pkg.RefPersonal = 803 + AND CONVERT(date, pkg.Datum) = '2024-11-04' ORDER BY - TABLE_NAME, - ORDINAL_POSITION; + pkg.RefPlan, + pkg.RefPlanungseinheiten, + pkg.RefDienste, + pkg.lfdNr; diff --git a/src/scheduling/api/app.py b/src/scheduling/api/app.py index 7f35b2a9..7597b121 100644 --- a/src/scheduling/api/app.py +++ b/src/scheduling/api/app.py @@ -12,10 +12,11 @@ from scheduling.logging import configure_logging from scheduling.settings import get_settings from scheduling.solver.service import SolverService -from scheduling.timeoffice.database import TimeOfficeDatabase, create_db_engine +from scheduling.timeoffice.database import create_db_engine from scheduling.timeoffice.facts import TIMEOFFICE_FACTS -from scheduling.timeoffice.repositories.container import TimeOfficeRepositories +from scheduling.timeoffice.reading.container import TimeOfficeReaders from scheduling.timeoffice.service import TimeOfficeService +from scheduling.timeoffice.writing.solution import TimeOfficeSolutionWriter settings = get_settings() configure_logging(level=settings.log_level) @@ -26,17 +27,15 @@ @asynccontextmanager async def lifespan(app: FastAPI) -> AsyncIterator[None]: engine = create_db_engine(settings=settings) - facts = TIMEOFFICE_FACTS - repositories = TimeOfficeRepositories.create(facts=facts) - database = TimeOfficeDatabase( - engine=engine, - repositories=repositories, - facts=facts, - ) app.state.runtime = ApiRuntime( - timeoffice_service=TimeOfficeService(database=database, facts=facts), + timeoffice_service=TimeOfficeService( + facts=facts, + engine=engine, + readers=TimeOfficeReaders.create(facts=facts), + solution_writer=TimeOfficeSolutionWriter(), + ), solver_service=SolverService(settings=settings), solve_job_store=InMemorySolveJobStore(), solve_lock=asyncio.Lock(), diff --git a/src/scheduling/domain/__init__.py b/src/scheduling/domain/__init__.py index 40f60876..e1c07f31 100644 --- a/src/scheduling/domain/__init__.py +++ b/src/scheduling/domain/__init__.py @@ -5,7 +5,7 @@ from scheduling.domain.demand import DemandRequirement from scheduling.domain.employee import Capability, Employee, EmployeeId, StaffLevel from scheduling.domain.monthly_work_account import MonthlyWorkAccount -from scheduling.domain.plan import Plan, PlanId, PlanParticipant +from scheduling.domain.plan import Plan, PlanId from scheduling.domain.planning_unit import PlanningUnit, PlanningUnitId, PlanningUnitKind, PlanningUnitMembership from scheduling.domain.shift import Shift, ShiftId, ShiftKind, StaffingDemandRole from scheduling.domain.sunday_work_history import EmployeeSundayWorkHistory @@ -21,7 +21,6 @@ "PlanningMonth", "Plan", "PlanId", - "PlanParticipant", "PlanningUnit", "PlanningUnitId", "PlanningUnitKind", diff --git a/src/scheduling/domain/dataset.py b/src/scheduling/domain/dataset.py index a9f8822c..a32d8a32 100644 --- a/src/scheduling/domain/dataset.py +++ b/src/scheduling/domain/dataset.py @@ -9,7 +9,7 @@ from scheduling.domain.demand import DemandRequirement from scheduling.domain.employee import Employee from scheduling.domain.monthly_work_account import MonthlyWorkAccount -from scheduling.domain.plan import Plan, PlanParticipant +from scheduling.domain.plan import Plan from scheduling.domain.planning_unit import PlanningUnit, PlanningUnitMembership from scheduling.domain.shift import Shift from scheduling.domain.sunday_work_history import EmployeeSundayWorkHistory @@ -55,7 +55,6 @@ class SchedulingDataset(SchedulingBaseModel): demand_requirements: tuple[DemandRequirement, ...] = () employees: tuple[Employee, ...] = () - plan_participants: tuple[PlanParticipant, ...] = () planning_unit_memberships: tuple[PlanningUnitMembership, ...] = () sunday_work_history: tuple[EmployeeSundayWorkHistory, ...] = () wishes: tuple[Wish, ...] = () diff --git a/src/scheduling/domain/plan.py b/src/scheduling/domain/plan.py index fd8febe0..badc7c59 100644 --- a/src/scheduling/domain/plan.py +++ b/src/scheduling/domain/plan.py @@ -1,5 +1,4 @@ from scheduling.domain.core import PositiveId, SchedulingBaseModel -from scheduling.domain.employee import EmployeeId from scheduling.domain.planning_unit import PlanningUnitId PlanId = PositiveId @@ -14,15 +13,3 @@ class Plan(SchedulingBaseModel): plan_id: PlanId planning_unit_id: PlanningUnitId - - -class PlanParticipant(SchedulingBaseModel): - """Employee included in a concrete selected Plan. - - This comes from TimeOffice `TPlanPersonal` and defines the concrete employee - set for the imported planning context. - """ - - plan_id: PlanId - planning_unit_id: PlanningUnitId - employee_id: EmployeeId diff --git a/src/scheduling/timeoffice/database.py b/src/scheduling/timeoffice/database.py index af383abb..91bc56e7 100644 --- a/src/scheduling/timeoffice/database.py +++ b/src/scheduling/timeoffice/database.py @@ -2,11 +2,7 @@ from sqlalchemy import URL, Engine, create_engine -from scheduling.domain import PlanningMonth from scheduling.settings import Settings -from scheduling.timeoffice.facts import TimeOfficeFacts -from scheduling.timeoffice.repositories import TimeOfficeRepositories -from scheduling.validation.dataset import ValidatedSchedulingDataset logger = logging.getLogger(__name__) @@ -26,103 +22,3 @@ def create_db_engine(settings: Settings) -> Engine: ) return create_engine(url) - - -class TimeOfficeDatabase: - """Loads reduced scheduling data from TimeOffice. - - This class owns the database connection boundary and repository call order. - It does not contain solver logic or TimeOffice row mapping details. - """ - - def __init__( - self, - *, - engine: Engine, - repositories: TimeOfficeRepositories, - facts: TimeOfficeFacts, - ) -> None: - self._engine = engine - self._repositories = repositories - self._facts = facts - - def fetch_dataset( - self, - *, - selected_planning_unit_ids: tuple[int, ...], - planning_month: PlanningMonth, - ) -> ValidatedSchedulingDataset: - if not selected_planning_unit_ids: - raise ValueError("At least one planning unit must be selected.") - - with self._engine.connect() as connection: - planning_unit_result = self._repositories.planning_units.fetch( - connection=connection, - selected_planning_unit_ids=selected_planning_unit_ids, - planning_month=planning_month, - ) - - planning_unit_ids = tuple( - planning_unit.planning_unit_id for planning_unit in planning_unit_result.planning_units - ) - - personnel_result = self._repositories.personnel.fetch( - connection=connection, - plans=planning_unit_result.plans, - planning_unit_ids=planning_unit_ids, - planning_month=planning_month, - ) - - shift_result = self._repositories.shifts.fetch( - connection=connection, - ) - - roster_result = self._repositories.roster.fetch( - connection=connection, - plans=planning_unit_result.plans, - employees=personnel_result.employees, - planning_month=planning_month, - ) - - demand_result = self._repositories.demand.fetch( - connection=connection, - planning_month=planning_month, - planning_units=planning_unit_result.planning_units, - shifts=shift_result.shifts, - ) - - sunday_work_history_result = self._repositories.sunday_work_history.fetch( - connection=connection, - planning_month=planning_month, - employees=personnel_result.employees, - ) - - wish_result = self._repositories.wishes.fetch( - connection=connection, - plans=planning_unit_result.plans, - employees=personnel_result.employees, - shifts=shift_result.shifts, - planning_month=planning_month, - ) - - monthly_work_account_result = self._repositories.monthly_work_accounts.fetch( - connection=connection, - employees=personnel_result.employees, - planning_month=planning_month, - ) - - return ValidatedSchedulingDataset( - planning_month=planning_month, - planning_units=planning_unit_result.planning_units, - plans=planning_unit_result.plans, - employees=personnel_result.employees, - plan_participants=personnel_result.plan_participants, - planning_unit_memberships=personnel_result.planning_unit_memberships, - shifts=shift_result.shifts, - assignments=roster_result.assignments, - availability=roster_result.availability, - demand_requirements=demand_result.demand_requirements, - sunday_work_history=sunday_work_history_result.sunday_work_history, - wishes=wish_result.wishes, - monthly_work_accounts=monthly_work_account_result.monthly_work_accounts, - ) diff --git a/src/scheduling/timeoffice/facts.py b/src/scheduling/timeoffice/facts.py index 24fd724f..ff0d0ef6 100644 --- a/src/scheduling/timeoffice/facts.py +++ b/src/scheduling/timeoffice/facts.py @@ -1,6 +1,5 @@ from collections.abc import Mapping from dataclasses import dataclass -from enum import IntEnum from types import MappingProxyType from scheduling.domain.availability import AvailabilityType @@ -9,93 +8,90 @@ from scheduling.domain.shift import ShiftId, ShiftKind, StaffingDemandRole from scheduling.domain.wish import WishKind +# TPlan.RefPlanungsIntervalle value for monthly planning. +MONTHLY_PLANNING_INTERVAL_ID = 1 -class TimeOfficePlanStatusId(IntEnum): - """Known TimeOffice RefStati values used around plan selection. +# TPlan.RefStati value for the editable target roster used as planning input. +TARGET_PLANNING_STATUS_ID = 20 - These are source-system IDs from TimeOffice. +# TDienste.RefDienstTypen value for normal work shifts. +WORK_SHIFT_TYPE_ID = 1 - TARGET_PLANNING is the currently used plan status for reading the editable - target roster that we want to repair/optimize. +# TPersonalKontenJeMonat.RefKonten for planned monthly target hours. +MONTHLY_TARGET_WORK_ACCOUNT_ID = 1 - The other values are kept because they were already known in the inherited - implementation and make the meaning of TARGET_PLANNING reviewable. They are - not used by the current read pipeline. - """ +# TPersonalKontenJeMonat.RefKonten for current monthly actual hours. +MONTHLY_ACTUAL_WORK_ACCOUNT_ID = 55 - TARGET_PLANNING = 20 - ACTUAL = 50 - COMPLETED = 70 - SETTLED = 80 +# TPlanungseinheiten.Prim values currently known to the project. +STATION_77_ID = 77 +STATION_78_ID = 78 +STATION_79_ID = 79 +STATION_85_ID = 85 +STATION_239_ID = 239 +STATION_337_ID = 337 +# Known TimeOffice planning unit for the shared/jump pool. +SHARED_POOL_408_ID = 408 -# TimeOffice TDienste.Prim values used by the reduced scheduling model. -# Keep these source IDs local to TimeOffice facts. +# TDienste.Prim values for the reduced reference shifts exposed to the solver. +# These are the only TimeOffice shift IDs that should become canonical Shift.shift_id +# values in the reduced SchedulingDataset. EARLY_F2_SHIFT_ID = 2939 LATE_S2_SHIFT_ID = 2947 NIGHT_N2_SHIFT_ID = 2953 INTERMEDIATE_T75_SHIFT_ID = 2906 MANAGEMENT_Z60_SHIFT_ID = 1406 -# Additional TimeOffice work shifts observed in roster rows. -NIGHT_N5_SHIFT_ID = 2889 -NIGHT_N15_SHIFT_ID = 1692 -OTHER_T8X_SHIFT_ID = 2994 -OTHER_Z52_SHIFT_ID = 3066 - @dataclass(frozen=True, slots=True) -class TimeOfficeShiftFact: - """Validated meaning of one known TimeOffice shift.""" +class TimeOfficeReferenceShiftFact: + """Domain meaning of one reduced reference shift. + + The mapping code verifies that the TimeOffice shift row identified by the + reference shift ID still has expected_code. Source-shift variants are mapped + separately by shift_code_overrides. + """ - source_shift_id: int expected_code: str kind: ShiftKind staffing_role: StaffingDemandRole -IsoWeekday = int # Monday=1 ... Sunday=7 - - -@dataclass(frozen=True, slots=True) -class TimeOfficeDemandFact: - """Fallback TimeOffice demand fact. - - This represents the same reduced information that should later come from - TimeOffice `TBenutzerBedarf*` tables. - - If planning_unit_ids is None, the demand applies to all selected station-like - planning units. - """ - - source_shift_id: ShiftId - staff_level: StaffLevel - required_by_iso_weekday: Mapping[IsoWeekday, int] - planning_unit_ids: tuple[PlanningUnitId, ...] | None = None +type WeekdayDemand = tuple[int, int, int, int, int, int, int] # Mo, Di, Mi, Do, Fr, Sa, So +type PlanningUnitDemandMatrix = Mapping[StaffLevel, Mapping[ShiftId, WeekdayDemand]] @dataclass(frozen=True, slots=True) class TimeOfficeFacts: - """Flat source assumptions for the TimeOffice adapter. + """Source assumptions and reduced-domain mappings for the TimeOffice adapter. - This object carries constants only. It must not contain behavior. - Repositories and validation code consume these facts for fetching, mapping, - and source-drift checks. + Facts contain adapter constants and source-to-domain mappings only. They do + not perform checks themselves; readers provide source rows and mapping code + uses these facts to fail loudly on unmapped or drifted source semantics. """ monthly_planning_interval_id: int target_planning_status_id: int - planning_unit_kind_map: dict[int, PlanningUnitKind] + planning_unit_kind_by_id: Mapping[PlanningUnitId, PlanningUnitKind] + + work_shift_type_id: int + + reference_shift_facts_by_id: Mapping[ShiftId, TimeOfficeReferenceShiftFact] + + # Non-reference source shift IDs normalized to reduced reference shifts. + # Missing source shift ID => fail loudly in mapping. + shift_id_overrides: Mapping[ShiftId, ShiftId] - work_shift_type_ids: tuple[int, ...] - shift_facts_by_id: Mapping[int, TimeOfficeShiftFact] + staff_level_by_profession_code: Mapping[str, StaffLevel] - staff_level_by_profession_id_map: dict[int, StaffLevel] - demand_facts: tuple[TimeOfficeDemandFact, ...] + # Temporary fallback until demand is read from TimeOffice demand tables. + # Shape mirrors the future source concept: planning unit -> staff level -> shift -> weekday demand. + fallback_demand_by_planning_unit: Mapping[PlanningUnitId, PlanningUnitDemandMatrix] - # Temporary project/problem assumptions. Not DB-backed. - capabilities_by_employee_id_map: dict[int, tuple[Capability, ...]] + # Temporary project/problem assumptions. Not DB-backed yet. + capabilities_by_employee_id: Mapping[int, tuple[Capability, ...]] availability_type_by_absence_code: Mapping[str, AvailabilityType] wish_kind_by_absence_code: Mapping[str, WishKind] @@ -104,106 +100,166 @@ class TimeOfficeFacts: monthly_actual_work_account_id: int -TIMEOFFICE_FACTS = TimeOfficeFacts( - monthly_planning_interval_id=1, # Known TimeOffice RefPlanungsIntervalle value - target_planning_status_id=int(TimeOfficePlanStatusId.TARGET_PLANNING), - planning_unit_kind_map={ - 77: PlanningUnitKind.STATION, - 78: PlanningUnitKind.STATION, - 79: PlanningUnitKind.STATION, - 85: PlanningUnitKind.STATION, - 239: PlanningUnitKind.STATION, - 337: PlanningUnitKind.STATION, - 408: PlanningUnitKind.SHARED_POOL, - }, - work_shift_type_ids=(1,), - shift_facts_by_id={ - EARLY_F2_SHIFT_ID: TimeOfficeShiftFact( - source_shift_id=EARLY_F2_SHIFT_ID, +REFERENCE_SHIFT_FACTS_BY_ID: Mapping[ShiftId, TimeOfficeReferenceShiftFact] = MappingProxyType( + { + EARLY_F2_SHIFT_ID: TimeOfficeReferenceShiftFact( expected_code="F2_", kind=ShiftKind.EARLY, staffing_role=StaffingDemandRole.REQUIRED_MINIMUM, ), - LATE_S2_SHIFT_ID: TimeOfficeShiftFact( - source_shift_id=LATE_S2_SHIFT_ID, + LATE_S2_SHIFT_ID: TimeOfficeReferenceShiftFact( expected_code="S2_", kind=ShiftKind.LATE, staffing_role=StaffingDemandRole.REQUIRED_MINIMUM, ), - NIGHT_N2_SHIFT_ID: TimeOfficeShiftFact( - source_shift_id=NIGHT_N2_SHIFT_ID, + NIGHT_N2_SHIFT_ID: TimeOfficeReferenceShiftFact( expected_code="N2_", kind=ShiftKind.NIGHT, staffing_role=StaffingDemandRole.REQUIRED_MINIMUM, ), - INTERMEDIATE_T75_SHIFT_ID: TimeOfficeShiftFact( - source_shift_id=INTERMEDIATE_T75_SHIFT_ID, + INTERMEDIATE_T75_SHIFT_ID: TimeOfficeReferenceShiftFact( expected_code="T75_", kind=ShiftKind.INTERMEDIATE, staffing_role=StaffingDemandRole.OPTIONAL_COVERAGE, ), - MANAGEMENT_Z60_SHIFT_ID: TimeOfficeShiftFact( - source_shift_id=MANAGEMENT_Z60_SHIFT_ID, + MANAGEMENT_Z60_SHIFT_ID: TimeOfficeReferenceShiftFact( expected_code="Z60", kind=ShiftKind.MANAGEMENT, staffing_role=StaffingDemandRole.NON_MINIMUM_WORK, ), - NIGHT_N5_SHIFT_ID: TimeOfficeShiftFact( - source_shift_id=NIGHT_N5_SHIFT_ID, - expected_code="N5", - kind=ShiftKind.NIGHT, - staffing_role=StaffingDemandRole.NON_MINIMUM_WORK, - ), - NIGHT_N15_SHIFT_ID: TimeOfficeShiftFact( - source_shift_id=NIGHT_N15_SHIFT_ID, - expected_code="N15", - kind=ShiftKind.NIGHT, - staffing_role=StaffingDemandRole.NON_MINIMUM_WORK, + } +) + +# Source shift variants observed in roster rows. +# Reference shift codes must not be repeated here. +SHIFT_ID_OVERRIDES: Mapping[ShiftId, ShiftId] = MappingProxyType( + { + # Night variants normalized to canonical N2_ night shift. + 1692: NIGHT_N2_SHIFT_ID, # N15, partial night + 3076: NIGHT_N2_SHIFT_ID, # N5 + 2889: NIGHT_N2_SHIFT_ID, # N5 + 1698: NIGHT_N2_SHIFT_ID, # N5 + 2866: NIGHT_N2_SHIFT_ID, # N5 + # Day/intermediate variant normalized to canonical T75_ intermediate shift. + 2994: INTERMEDIATE_T75_SHIFT_ID, # T8x + # Short/day special variants normalized to canonical Z60 non-minimum work shift. + 2957: MANAGEMENT_Z60_SHIFT_ID, # Z52 + 2687: MANAGEMENT_Z60_SHIFT_ID, # Z52 + 1403: MANAGEMENT_Z60_SHIFT_ID, # Z52 + 3066: MANAGEMENT_Z60_SHIFT_ID, # Z52 + } +) + +STAFF_LEVEL_BY_PROFESSION_CODE: Mapping[str, StaffLevel] = MappingProxyType( + { + # Fachkraft + "81302-003": StaffLevel.PROFESSIONAL, # Gesundheits- und Kinderkrankenpfleger/in + "81302-005": StaffLevel.PROFESSIONAL, # Gesundheits- und Krankenpfleger/in + "81302-007": StaffLevel.PROFESSIONAL, # Kinderkrankenschwester/-pfleger + "81302-008": StaffLevel.PROFESSIONAL, # Krankenschwester/-pfleger + "81302-009": StaffLevel.PROFESSIONAL, # Krankenschwester/-pfleger - Nachtwache + "81302-016": StaffLevel.PROFESSIONAL, # Pflegefachkraft - Kinderkrankenpflege + "81302-018": StaffLevel.PROFESSIONAL, # Pflegefachkraft Krankenpflege + "81302-028": StaffLevel.PROFESSIONAL, # Pflegefachmann/-frau + "81313-059": StaffLevel.PROFESSIONAL, # Fachkrankenpfleger/in - Notfallpflege + "81393-011": StaffLevel.PROFESSIONAL, # Stationsleiter/in - Pflegedienst + "82102-002": StaffLevel.PROFESSIONAL, # Altenpfleger/in + "EX-81302-028": StaffLevel.PROFESSIONAL, # EX-Pflegefachmann/-frau + # Legacy classified this as Fachkraft. + "63302-045": StaffLevel.PROFESSIONAL, # Servicekraft + # Hilfskraft / support + "81102-001": StaffLevel.ASSISTANT, # Arzthelfer/in + "81102-004": StaffLevel.ASSISTANT, # Medizinische/r Fachangestellte/r + "81301-002": StaffLevel.ASSISTANT, # Helfer/in - stationäre Krankenpflege + "81301-006": StaffLevel.ASSISTANT, # Krankenpflegehelfer/in, 1-jährige Ausbildung + "81301-010": StaffLevel.ASSISTANT, # Pflegehelfer/in ohne 1-jährige Ausbildung + "81301-014": StaffLevel.ASSISTANT, # Schwesterhelfer/in + "81301-018": StaffLevel.ASSISTANT, # Stationshilfe + "81302-014": StaffLevel.ASSISTANT, # Pflegeassistent/in + "BFD": StaffLevel.ASSISTANT, # Bundesfreiwilligendienst + # Legacy classified this as Hilfskraft. + "Pra": StaffLevel.ASSISTANT, # Praktikant/-in + # Ausbildung / Praktikum + "A-31342-005": StaffLevel.TRAINEE, # A-Notfallsanitäter + "A-81302-007": StaffLevel.TRAINEE, # A-Kinderkrankenschwester/-pfleger + "A-81302-008": StaffLevel.TRAINEE, # A-Krankenschwester/-pfleger + "A-81302-014": StaffLevel.TRAINEE, # A-Pflegeassistent/in + "A-81302-016": StaffLevel.TRAINEE, # A-Pflegefachkraft Kinderkrankenpflege + "A-81302-018": StaffLevel.TRAINEE, # A-Pflegefachkraft Krankenpflege + "A-81302-019": StaffLevel.TRAINEE, # A-Pflegefachkraft Altenpflege + } +) + +DEFAULT_STATION_DEMAND: PlanningUnitDemandMatrix = MappingProxyType( + { + # Weekday tuple order: Mo, Di, Mi, Do, Fr, Sa, So. + StaffLevel.PROFESSIONAL: MappingProxyType( + { + EARLY_F2_SHIFT_ID: (3, 3, 4, 3, 3, 2, 2), + LATE_S2_SHIFT_ID: (2, 2, 2, 2, 2, 2, 2), + NIGHT_N2_SHIFT_ID: (2, 2, 2, 2, 2, 1, 1), + } ), - OTHER_T8X_SHIFT_ID: TimeOfficeShiftFact( - source_shift_id=OTHER_T8X_SHIFT_ID, - expected_code="T8x", - kind=ShiftKind.OTHER, - staffing_role=StaffingDemandRole.NON_MINIMUM_WORK, + StaffLevel.ASSISTANT: MappingProxyType( + { + EARLY_F2_SHIFT_ID: (2, 2, 2, 2, 2, 2, 2), + LATE_S2_SHIFT_ID: (2, 2, 2, 2, 2, 2, 2), + NIGHT_N2_SHIFT_ID: (0, 0, 0, 0, 0, 1, 1), + } ), - OTHER_Z52_SHIFT_ID: TimeOfficeShiftFact( - source_shift_id=OTHER_Z52_SHIFT_ID, - expected_code="Z52", - kind=ShiftKind.OTHER, - staffing_role=StaffingDemandRole.NON_MINIMUM_WORK, + StaffLevel.TRAINEE: MappingProxyType( + { + EARLY_F2_SHIFT_ID: (1, 1, 1, 1, 1, 1, 1), + LATE_S2_SHIFT_ID: (1, 1, 1, 1, 1, 1, 1), + NIGHT_N2_SHIFT_ID: (0, 0, 0, 0, 0, 0, 0), + } ), - }, - staff_level_by_profession_id_map={ - # Fachkraft - 803: StaffLevel.PROFESSIONAL, # Gesundheits- und Krankenpfleger/in - 110: StaffLevel.PROFESSIONAL, # Pflegefachkraft (Krankenpflege) - 129: StaffLevel.PROFESSIONAL, # Altenpfleger/in - 651: StaffLevel.PROFESSIONAL, # Pflegefachmann/-frau - 736: StaffLevel.PROFESSIONAL, # Krankenschwester/-pfleger - 987: StaffLevel.PROFESSIONAL, # Pflegefachkraft - Kinderkrankenpflege - # Hilfskraft - 90: StaffLevel.ASSISTANT, # Krankenpflegehelfer/in - 124: StaffLevel.ASSISTANT, # Medizinische/r Fachangestellte/r - 326: StaffLevel.ASSISTANT, # Helfer/in - stationäre Krankenpflege - 334: StaffLevel.ASSISTANT, # Pflegehelfer/in - stationäre Pflege - 793: StaffLevel.ASSISTANT, # Stationshilfe - 1245: StaffLevel.ASSISTANT, # Bundesfreiwilligendienst - # Azubi / Ausbildung - 835: StaffLevel.TRAINEE, # A-Pflegeassistent/in - 837: StaffLevel.TRAINEE, # A-Pflegefachkraft (Krankenpflege) - 1478: StaffLevel.TRAINEE, # A-Pflegefachkraft (Altenpflege) - }, - capabilities_by_employee_id_map={ - # Not DB-backed yet. - # Problem/legacy assumption: FWB employees for weekday early rounds. - 791: (Capability.ROUNDS,), # Branz, Janett - 2963: (Capability.ROUNDS,), # Hoots, Renilde - 3868: (Capability.ROUNDS,), # Vanfleet, Eike - # Problem assumption: night-watch employees. - 925: (Capability.NIGHT_WATCH,), # Farniok, Lina - 6681: (Capability.NIGHT_WATCH,), # Labelle, Saskia - 928: (Capability.NIGHT_WATCH,), # Wunderlich, Daniele - }, + } +) + + +TIMEOFFICE_FACTS = TimeOfficeFacts( + monthly_planning_interval_id=MONTHLY_PLANNING_INTERVAL_ID, + target_planning_status_id=TARGET_PLANNING_STATUS_ID, + planning_unit_kind_by_id=MappingProxyType( + { + STATION_77_ID: PlanningUnitKind.STATION, + STATION_78_ID: PlanningUnitKind.STATION, + STATION_79_ID: PlanningUnitKind.STATION, + STATION_85_ID: PlanningUnitKind.STATION, + STATION_239_ID: PlanningUnitKind.STATION, + STATION_337_ID: PlanningUnitKind.STATION, + SHARED_POOL_408_ID: PlanningUnitKind.SHARED_POOL, + } + ), + work_shift_type_id=WORK_SHIFT_TYPE_ID, + reference_shift_facts_by_id=REFERENCE_SHIFT_FACTS_BY_ID, + shift_id_overrides=SHIFT_ID_OVERRIDES, + staff_level_by_profession_code=STAFF_LEVEL_BY_PROFESSION_CODE, + fallback_demand_by_planning_unit=MappingProxyType( + { + STATION_77_ID: DEFAULT_STATION_DEMAND, + STATION_78_ID: DEFAULT_STATION_DEMAND, + STATION_79_ID: DEFAULT_STATION_DEMAND, + STATION_85_ID: DEFAULT_STATION_DEMAND, + STATION_239_ID: DEFAULT_STATION_DEMAND, + STATION_337_ID: DEFAULT_STATION_DEMAND, + # Intentionally no demand for SHARED_POOL_408_ID. + } + ), + capabilities_by_employee_id=MappingProxyType( + { + # Not DB-backed yet. + # Problem/legacy assumption: FWB employees for weekday early rounds. + 791: (Capability.ROUNDS,), # Branz, Janett + 2963: (Capability.ROUNDS,), # Hoots, Renilde + 3868: (Capability.ROUNDS,), # Vanfleet, Eike + # Problem assumption: night-watch employees. + 925: (Capability.NIGHT_WATCH,), # Farniok, Lina + 6681: (Capability.NIGHT_WATCH,), # Labelle, Saskia + 928: (Capability.NIGHT_WATCH,), # Wunderlich, Daniele + } + ), availability_type_by_absence_code=MappingProxyType( { "U": AvailabilityType.VACATION, @@ -222,146 +278,6 @@ class TimeOfficeFacts: "FR": WishKind.FREE_DAY, } ), - monthly_target_work_account_id=1, - monthly_actual_work_account_id=55, - demand_facts=( - # Fachkraft - TimeOfficeDemandFact( - source_shift_id=EARLY_F2_SHIFT_ID, - staff_level=StaffLevel.PROFESSIONAL, - required_by_iso_weekday=MappingProxyType( - { - 1: 3, # Mo - 2: 3, # Di - 3: 4, # Mi - 4: 3, # Do - 5: 3, # Fr - 6: 2, # Sa - 7: 2, # So - } - ), - ), - TimeOfficeDemandFact( - source_shift_id=LATE_S2_SHIFT_ID, - staff_level=StaffLevel.PROFESSIONAL, - required_by_iso_weekday=MappingProxyType( - { - 1: 2, # Mo - 2: 2, # Di - 3: 2, # Mi - 4: 2, # Do - 5: 2, # Fr - 6: 2, # Sa - 7: 2, # So - } - ), - ), - TimeOfficeDemandFact( - source_shift_id=NIGHT_N2_SHIFT_ID, - staff_level=StaffLevel.PROFESSIONAL, - required_by_iso_weekday=MappingProxyType( - { - 1: 2, # Mo - 2: 2, # Di - 3: 2, # Mi - 4: 2, # Do - 5: 2, # Fr - 6: 1, # Sa - 7: 1, # So - } - ), - ), - # Hilfskraft - TimeOfficeDemandFact( - source_shift_id=EARLY_F2_SHIFT_ID, - staff_level=StaffLevel.ASSISTANT, - required_by_iso_weekday=MappingProxyType( - { - 1: 2, # Mo - 2: 2, # Di - 3: 2, # Mi - 4: 2, # Do - 5: 2, # Fr - 6: 2, # Sa - 7: 2, # So - } - ), - ), - TimeOfficeDemandFact( - source_shift_id=LATE_S2_SHIFT_ID, - staff_level=StaffLevel.ASSISTANT, - required_by_iso_weekday=MappingProxyType( - { - 1: 2, # Mo - 2: 2, # Di - 3: 2, # Mi - 4: 2, # Do - 5: 2, # Fr - 6: 2, # Sa - 7: 2, # So - } - ), - ), - TimeOfficeDemandFact( - source_shift_id=NIGHT_N2_SHIFT_ID, - staff_level=StaffLevel.ASSISTANT, - required_by_iso_weekday=MappingProxyType( - { - 1: 0, # Mo - 2: 0, # Di - 3: 0, # Mi - 4: 0, # Do - 5: 0, # Fr - 6: 1, # Sa - 7: 1, # So - } - ), - ), - # Azubi - TimeOfficeDemandFact( - source_shift_id=EARLY_F2_SHIFT_ID, - staff_level=StaffLevel.TRAINEE, - required_by_iso_weekday=MappingProxyType( - { - 1: 1, # Mo - 2: 1, # Di - 3: 1, # Mi - 4: 1, # Do - 5: 1, # Fr - 6: 1, # Sa - 7: 1, # So - } - ), - ), - TimeOfficeDemandFact( - source_shift_id=LATE_S2_SHIFT_ID, - staff_level=StaffLevel.TRAINEE, - required_by_iso_weekday=MappingProxyType( - { - 1: 1, # Mo - 2: 1, # Di - 3: 1, # Mi - 4: 1, # Do - 5: 1, # Fr - 6: 1, # Sa - 7: 1, # So - } - ), - ), - TimeOfficeDemandFact( - source_shift_id=NIGHT_N2_SHIFT_ID, - staff_level=StaffLevel.TRAINEE, - required_by_iso_weekday=MappingProxyType( - { - 1: 0, # Mo - 2: 0, # Di - 3: 0, # Mi - 4: 0, # Do - 5: 0, # Fr - 6: 0, # Sa - 7: 0, # So - } - ), - ), - ), + monthly_target_work_account_id=MONTHLY_TARGET_WORK_ACCOUNT_ID, + monthly_actual_work_account_id=MONTHLY_ACTUAL_WORK_ACCOUNT_ID, ) diff --git a/src/scheduling/timeoffice/mapping/__init__.py b/src/scheduling/timeoffice/mapping/__init__.py new file mode 100644 index 00000000..568061cd --- /dev/null +++ b/src/scheduling/timeoffice/mapping/__init__.py @@ -0,0 +1,3 @@ +from scheduling.timeoffice.mapping.dataset import map_scheduling_dataset + +__all__ = ["map_scheduling_dataset"] diff --git a/src/scheduling/timeoffice/mapping/dataset.py b/src/scheduling/timeoffice/mapping/dataset.py new file mode 100644 index 00000000..83131f3d --- /dev/null +++ b/src/scheduling/timeoffice/mapping/dataset.py @@ -0,0 +1,50 @@ +from scheduling.domain import SchedulingDataset +from scheduling.timeoffice.facts import TimeOfficeFacts +from scheduling.timeoffice.mapping.demand import map_demand_requirements +from scheduling.timeoffice.mapping.personnel import map_employees, map_planning_unit_memberships +from scheduling.timeoffice.mapping.planning import map_planning_units, map_plans +from scheduling.timeoffice.mapping.roster import map_assignments, map_availability +from scheduling.timeoffice.mapping.shifts import map_shifts +from scheduling.timeoffice.mapping.sunday_work import map_sunday_work_history +from scheduling.timeoffice.mapping.wishes import map_wishes +from scheduling.timeoffice.mapping.work_accounts import map_monthly_work_accounts +from scheduling.timeoffice.reading.container import TimeOfficeSources + + +def map_scheduling_dataset(*, sources: TimeOfficeSources, facts: TimeOfficeFacts) -> SchedulingDataset: + planning_units = map_planning_units(sources.planning_unit_rows, facts=facts) + plans = map_plans(sources.planning_unit_rows) + shifts = map_shifts(sources.shift_rows, facts=facts) + + return SchedulingDataset( + planning_month=sources.planning_month, + planning_units=planning_units, + plans=plans, + employees=map_employees(sources.employee_rows, facts=facts), + planning_unit_memberships=map_planning_unit_memberships( + sources.planning_unit_membership_rows, + facts=facts, + ), + shifts=shifts, + assignments=map_assignments( + rows=sources.roster_rows, + selected_plan_ids={plan.plan_id for plan in plans}, + selected_planning_unit_ids={planning_unit.planning_unit_id for planning_unit in planning_units}, + facts=facts, + ), + availability=map_availability( + rows=sources.roster_rows, + facts=facts, + ), + demand_requirements=map_demand_requirements( + planning_month=sources.planning_month, + planning_units=planning_units, + facts=facts, + ), + sunday_work_history=map_sunday_work_history(sources.sunday_history_rows), + wishes=map_wishes( + rows=sources.wish_rows, + facts=facts, + ), + monthly_work_accounts=map_monthly_work_accounts(sources.monthly_work_account_rows), + ) diff --git a/src/scheduling/timeoffice/mapping/demand.py b/src/scheduling/timeoffice/mapping/demand.py new file mode 100644 index 00000000..aa85eb75 --- /dev/null +++ b/src/scheduling/timeoffice/mapping/demand.py @@ -0,0 +1,94 @@ +from datetime import timedelta + +from scheduling.domain import DemandRequirement, PlanningMonth, PlanningUnit, PlanningUnitKind, StaffingDemandRole +from scheduling.domain.shift import ShiftId +from scheduling.timeoffice.facts import PlanningUnitDemandMatrix, TimeOfficeFacts + + +def map_demand_requirements( + *, + planning_month: PlanningMonth, + planning_units: tuple[PlanningUnit, ...], + facts: TimeOfficeFacts, +) -> tuple[DemandRequirement, ...]: + requirements: list[DemandRequirement] = [] + + selected_station_ids = sorted( + unit.planning_unit_id for unit in planning_units if unit.kind == PlanningUnitKind.STATION + ) + + for planning_unit_id in selected_station_ids: + demand_matrix = facts.fallback_demand_by_planning_unit.get(planning_unit_id) + if demand_matrix is None: + raise ValueError( + f"Missing fallback demand matrix for selected station planning_unit_id={planning_unit_id}." + ) + + requirements.extend( + _expand_planning_unit_demand( + planning_unit_id=planning_unit_id, + planning_month=planning_month, + demand_matrix=demand_matrix, + facts=facts, + ) + ) + + return tuple(requirements) + + +def _expand_planning_unit_demand( + *, + planning_unit_id: int, + planning_month: PlanningMonth, + demand_matrix: PlanningUnitDemandMatrix, + facts: TimeOfficeFacts, +) -> tuple[DemandRequirement, ...]: + requirements: list[DemandRequirement] = [] + + current_date = planning_month.start + while current_date <= planning_month.end: + weekday_index = current_date.isoweekday() - 1 + + for staff_level, demand_by_shift_id in demand_matrix.items(): + for shift_id, weekday_demand in demand_by_shift_id.items(): + _validate_weekday_demand(shift_id=shift_id, weekday_demand=weekday_demand) + + _require_minimum_staffing_reference_shift(shift_id=shift_id, facts=facts) + + required_count = weekday_demand[weekday_index] + if required_count <= 0: + continue + + requirements.append( + DemandRequirement( + planning_unit_id=planning_unit_id, + date=current_date, + shift_id=shift_id, + staff_level=staff_level, + required_count=required_count, + ) + ) + + current_date += timedelta(days=1) + + return tuple(requirements) + + +def _validate_weekday_demand(*, shift_id: ShiftId, weekday_demand: tuple[int, ...]) -> None: + if len(weekday_demand) != 7: + raise ValueError( + "Fallback demand weekday tuple must contain exactly seven values " + f"(Mo, Di, Mi, Do, Fr, Sa, So): shift_id={shift_id} values={weekday_demand}." + ) + + +def _require_minimum_staffing_reference_shift(*, shift_id: ShiftId, facts: TimeOfficeFacts) -> None: + shift_fact = facts.reference_shift_facts_by_id.get(shift_id) + if shift_fact is None: + raise ValueError(f"Fallback demand references non-reference shift_id={shift_id}.") + + if shift_fact.staffing_role != StaffingDemandRole.REQUIRED_MINIMUM: + raise ValueError( + "Fallback demand must reference REQUIRED_MINIMUM shifts only: " + f"shift_id={shift_id} staffing_role={shift_fact.staffing_role}." + ) diff --git a/src/scheduling/timeoffice/mapping/personnel.py b/src/scheduling/timeoffice/mapping/personnel.py new file mode 100644 index 00000000..d1418fef --- /dev/null +++ b/src/scheduling/timeoffice/mapping/personnel.py @@ -0,0 +1,87 @@ +from scheduling.domain import Capability, Employee, PlanningUnitMembership, StaffLevel +from scheduling.timeoffice.facts import TimeOfficeFacts +from scheduling.timeoffice.reading.personnel import TimeOfficeEmployeeRow, TimeOfficePlanningUnitMembershipRow + + +def map_employees(rows: tuple[TimeOfficeEmployeeRow, ...], *, facts: TimeOfficeFacts) -> tuple[Employee, ...]: + return tuple( + Employee( + employee_id=row.employee_id, + display_name=_display_name( + employee_id=row.employee_id, + first_name=row.first_name, + last_name=row.last_name, + ), + staff_level=_staff_level_from_profession( + profession_id=row.employee_profession_id, + profession_code=row.employee_profession_code, + facts=facts, + context=f"TPersonal employee_id={row.employee_id}", + ), + capabilities=_capabilities_for_employee(row.employee_id, facts=facts), + ) + for row in rows + ) + + +def map_planning_unit_memberships( + rows: tuple[TimeOfficePlanningUnitMembershipRow, ...], *, facts: TimeOfficeFacts +) -> tuple[PlanningUnitMembership, ...]: + return tuple( + PlanningUnitMembership( + planning_unit_id=row.planning_unit_id, + employee_id=row.employee_id, + valid_from=row.valid_from.date(), + valid_until=row.valid_until.date() if row.valid_until is not None else None, + staff_level=_staff_level_from_profession( + profession_id=row.membership_profession_id, + profession_code=row.membership_profession_code, + facts=facts, + context=( + f"TPlanungseinheitenPersonal planning_unit_id={row.planning_unit_id} employee_id={row.employee_id}" + ), + ), + is_home=row.is_home, + is_replacement=row.is_replacement, + ) + for row in rows + ) + + +def _staff_level_from_profession( + *, + profession_id: int, + profession_code: str | None, + facts: TimeOfficeFacts, + context: str, +) -> StaffLevel: + if profession_code is None: + raise ValueError(f"Missing TimeOffice profession code: context={context} profession_id={profession_id}.") + + staff_level = facts.staff_level_by_profession_code.get(profession_code) + + if staff_level is None: + known_codes = ", ".join(sorted(facts.staff_level_by_profession_code)) + raise ValueError( + "No StaffLevel mapping configured for TimeOffice profession: " + f"context={context} " + f"profession_id={profession_id} " + f"profession_code={profession_code!r}. " + f"Known profession_codes=[{known_codes}]." + ) + + return staff_level + + +def _capabilities_for_employee(employee_id: int, *, facts: TimeOfficeFacts) -> tuple[Capability, ...]: + return tuple(facts.capabilities_by_employee_id.get(employee_id, ())) + + +def _display_name( + *, + employee_id: int, + first_name: str | None, + last_name: str | None, +) -> str: + display_name = " ".join(part for part in (last_name, first_name) if part) + return display_name or f"Employee {employee_id}" diff --git a/src/scheduling/timeoffice/mapping/planning.py b/src/scheduling/timeoffice/mapping/planning.py new file mode 100644 index 00000000..f693796f --- /dev/null +++ b/src/scheduling/timeoffice/mapping/planning.py @@ -0,0 +1,35 @@ +from scheduling.domain import Plan, PlanningUnit, PlanningUnitKind +from scheduling.timeoffice.facts import TimeOfficeFacts +from scheduling.timeoffice.reading.planning_units import TimeOfficePlanningUnitRow + + +def map_planning_units( + rows: tuple[TimeOfficePlanningUnitRow, ...], *, facts: TimeOfficeFacts +) -> tuple[PlanningUnit, ...]: + return tuple( + PlanningUnit( + planning_unit_id=row.planning_unit_id, + display_name=f"Planning Unit {row.planning_unit_id}", + kind=_planning_unit_kind(row.planning_unit_id, facts=facts), + ) + for row in rows + ) + + +def map_plans(rows: tuple[TimeOfficePlanningUnitRow, ...]) -> tuple[Plan, ...]: + return tuple( + Plan( + plan_id=row.plan_id, + planning_unit_id=row.plan_planning_unit_id, + ) + for row in rows + ) + + +def _planning_unit_kind(planning_unit_id: int, *, facts: TimeOfficeFacts) -> PlanningUnitKind: + kind = facts.planning_unit_kind_by_id.get(planning_unit_id) + + if kind is None: + raise ValueError(f"No PlanningUnitKind configured for TimeOffice planning_unit_id={planning_unit_id}.") + + return kind diff --git a/src/scheduling/timeoffice/mapping/roster.py b/src/scheduling/timeoffice/mapping/roster.py new file mode 100644 index 00000000..802e166b --- /dev/null +++ b/src/scheduling/timeoffice/mapping/roster.py @@ -0,0 +1,123 @@ +from datetime import date, datetime + +from scheduling.domain import Assignment, AssignmentType, Availability, AvailabilityType +from scheduling.timeoffice.facts import TimeOfficeFacts +from scheduling.timeoffice.mapping.shifts import reference_shift_id_for_source_shift +from scheduling.timeoffice.reading.roster import TimeOfficeRosterRow + +type AssignmentKey = tuple[int, date, int, AssignmentType, int | None] + + +def map_assignments( + *, + rows: tuple[TimeOfficeRosterRow, ...], + selected_plan_ids: set[int], + selected_planning_unit_ids: set[int], + facts: TimeOfficeFacts, +) -> tuple[Assignment, ...]: + assignments: list[Assignment] = [] + seen_assignment_keys: set[AssignmentKey] = set() + + for row in rows: + if row.work_shift_id is None: + continue + + reference_shift_id = reference_shift_id_for_source_shift( + source_shift_id=row.work_shift_id, + source_shift_code=row.work_shift_code, + facts=facts, + context=(f"TimeOffice roster work row employee_id={row.employee_id} date={row.roster_date.date()}"), + ) + + assignment_type = _assignment_type( + plan_id=row.plan_id, + planning_unit_id=row.planning_unit_id, + selected_plan_ids=selected_plan_ids, + selected_planning_unit_ids=selected_planning_unit_ids, + ) + + assignment = Assignment( + employee_id=row.employee_id, + date=row.roster_date.date(), + shift_id=reference_shift_id, + assignment_type=assignment_type, + planning_unit_id=(row.planning_unit_id if assignment_type == AssignmentType.PLANNED else None), + ) + + assignment_key = _assignment_key(assignment) + if assignment_key in seen_assignment_keys: + continue + + seen_assignment_keys.add(assignment_key) + assignments.append(assignment) + + return tuple(assignments) + + +def _assignment_key(assignment: Assignment) -> AssignmentKey: + return ( + assignment.employee_id, + assignment.date, + assignment.shift_id, + assignment.assignment_type, + assignment.planning_unit_id, + ) + + +def map_availability(*, rows: tuple[TimeOfficeRosterRow, ...], facts: TimeOfficeFacts) -> tuple[Availability, ...]: + return tuple( + Availability( + employee_id=row.employee_id, + date=row.roster_date.date(), + availability_type=_availability_type_for_absence_code( + row.resolved_absence_code, + facts=facts, + employee_id=row.employee_id, + roster_date=row.roster_date, + ), + ) + for row in rows + if _has_absence(row) + ) + + +def _assignment_type( + *, + plan_id: int | None, + planning_unit_id: int | None, + selected_plan_ids: set[int], + selected_planning_unit_ids: set[int], +) -> AssignmentType: + if plan_id in selected_plan_ids and planning_unit_id in selected_planning_unit_ids: + return AssignmentType.PLANNED + + return AssignmentType.EXTERNAL + + +def _has_absence(row: TimeOfficeRosterRow) -> bool: + return row.global_absence_shift_id is not None or row.absence_shift_id is not None + + +def _availability_type_for_absence_code( + absence_code: str | None, + *, + facts: TimeOfficeFacts, + employee_id: int, + roster_date: datetime, +) -> AvailabilityType: + if absence_code is None: + raise ValueError( + "Missing resolved absence code for TimeOffice roster row: " + f"employee_id={employee_id} roster_date={roster_date}." + ) + + availability_type = facts.availability_type_by_absence_code.get(absence_code) + + if availability_type is None: + raise ValueError( + "Unmapped TimeOffice absence code for availability: " + f"employee_id={employee_id} roster_date={roster_date} " + f"absence_code={absence_code!r}." + ) + + return availability_type diff --git a/src/scheduling/timeoffice/mapping/shifts.py b/src/scheduling/timeoffice/mapping/shifts.py new file mode 100644 index 00000000..2b025a9e --- /dev/null +++ b/src/scheduling/timeoffice/mapping/shifts.py @@ -0,0 +1,194 @@ +from collections import defaultdict +from datetime import datetime + +from scheduling.domain import Shift +from scheduling.domain.shift import ShiftId +from scheduling.timeoffice.facts import TimeOfficeFacts, TimeOfficeReferenceShiftFact +from scheduling.timeoffice.reading.shifts import TimeOfficeShiftRow + + +def map_shifts(rows: tuple[TimeOfficeShiftRow, ...], *, facts: TimeOfficeFacts) -> tuple[Shift, ...]: + rows_by_shift_id = _group_shift_rows(rows) + _fail_if_reference_shifts_are_missing(rows_by_shift_id=rows_by_shift_id, facts=facts) + _fail_if_unexpected_shift_rows_exist(rows_by_shift_id=rows_by_shift_id, facts=facts) + + return tuple( + _map_reference_shift( + shift_id=shift_id, + rows=shift_rows, + shift_fact=_require_reference_shift_fact(shift_id=shift_id, facts=facts), + facts=facts, + ) + for shift_id, shift_rows in sorted(rows_by_shift_id.items()) + ) + + +def reference_shift_id_for_source_shift( + *, + source_shift_id: int | None, + source_shift_code: str | None, + facts: TimeOfficeFacts, + context: str, +) -> ShiftId: + if source_shift_id is None: + raise ValueError(f"Missing TimeOffice source shift ID for {context}: source_shift_code={source_shift_code!r}.") + + reference_fact = facts.reference_shift_facts_by_id.get(source_shift_id) + if reference_fact is not None: + _check_reference_shift_code( + source_shift_id=source_shift_id, + source_shift_code=source_shift_code, + expected_code=reference_fact.expected_code, + context=context, + ) + return source_shift_id + + override_shift_id = facts.shift_id_overrides.get(source_shift_id) + if override_shift_id is not None: + if override_shift_id not in facts.reference_shift_facts_by_id: + raise ValueError( + f"TimeOffice shift override points to non-reference shift for {context}: " + f"source_shift_id={source_shift_id} " + f"source_shift_code={source_shift_code!r} " + f"override_shift_id={override_shift_id}." + ) + + return override_shift_id + + known_reference_ids = sorted(facts.reference_shift_facts_by_id) + known_override_ids = sorted(facts.shift_id_overrides) + + raise ValueError( + f"Unmapped TimeOffice source shift for {context}: " + f"source_shift_id={source_shift_id} " + f"source_shift_code={source_shift_code!r}. " + f"Known reference_shift_ids={known_reference_ids}; " + f"known_override_shift_ids={known_override_ids}." + ) + + +def _map_reference_shift( + *, + shift_id: ShiftId, + rows: list[TimeOfficeShiftRow], + shift_fact: TimeOfficeReferenceShiftFact, + facts: TimeOfficeFacts, +) -> Shift: + first_row = rows[0] + + _check_reference_shift_code( + source_shift_id=shift_id, + source_shift_code=first_row.shift_code, + expected_code=shift_fact.expected_code, + context="reference shift definition", + ) + + if first_row.shift_type_id != facts.work_shift_type_id: + raise ValueError( + "Reference shift is not configured as normal work shift in TimeOffice: " + f"shift_id={shift_id} " + f"shift_code={first_row.shift_code!r} " + f"shift_type_id={first_row.shift_type_id} " + f"expected_work_shift_type_id={facts.work_shift_type_id}." + ) + + segments = _shift_segments(rows=rows, shift_id=shift_id) + + return Shift( + shift_id=shift_id, + code=first_row.shift_code, + kind=shift_fact.kind, + staffing_role=shift_fact.staffing_role, + start_minute=_minute_of_day(segments[0][0]), + end_minute=_minute_of_day(segments[-1][1]), + net_work_minutes=_net_work_minutes(segments), + ) + + +def _check_reference_shift_code( + *, + source_shift_id: int, + source_shift_code: str | None, + expected_code: str, + context: str, +) -> None: + if source_shift_code is None: + raise ValueError( + f"Missing TimeOffice shift code for {context}: " + f"source_shift_id={source_shift_id} " + f"expected_code={expected_code!r}." + ) + + if source_shift_code != expected_code: + raise ValueError( + f"Unexpected TimeOffice shift code for {context}: " + f"source_shift_id={source_shift_id} " + f"expected_code={expected_code!r} " + f"actual_code={source_shift_code!r}." + ) + + +def _require_reference_shift_fact(*, shift_id: ShiftId, facts: TimeOfficeFacts) -> TimeOfficeReferenceShiftFact: + shift_fact = facts.reference_shift_facts_by_id.get(shift_id) + + if shift_fact is None: + raise ValueError(f"Unknown reference shift_id: shift_id={shift_id}.") + + return shift_fact + + +def _group_shift_rows(rows: tuple[TimeOfficeShiftRow, ...]) -> dict[ShiftId, list[TimeOfficeShiftRow]]: + rows_by_shift_id: dict[ShiftId, list[TimeOfficeShiftRow]] = defaultdict(list) + + for row in rows: + rows_by_shift_id[row.shift_id].append(row) + + return dict(rows_by_shift_id) + + +def _fail_if_reference_shifts_are_missing( + *, rows_by_shift_id: dict[ShiftId, list[TimeOfficeShiftRow]], facts: TimeOfficeFacts +) -> None: + missing_shift_ids = sorted(set(facts.reference_shift_facts_by_id) - set(rows_by_shift_id)) + + if missing_shift_ids: + raise ValueError(f"Missing TimeOffice rows for reference shift_ids={missing_shift_ids}.") + + +def _fail_if_unexpected_shift_rows_exist( + *, rows_by_shift_id: dict[ShiftId, list[TimeOfficeShiftRow]], facts: TimeOfficeFacts +) -> None: + unexpected_shift_ids = sorted(set(rows_by_shift_id) - set(facts.reference_shift_facts_by_id)) + + if unexpected_shift_ids: + raise ValueError(f"TimeOffice returned unexpected shift rows for shift_ids={unexpected_shift_ids}.") + + +def _shift_segments(*, rows: list[TimeOfficeShiftRow], shift_id: ShiftId) -> tuple[tuple[datetime, datetime, int], ...]: + segments = tuple( + ( + row.segment_start, + row.segment_end, + row.segment_minutes or 0, + ) + for row in rows + if row.segment_start is not None and row.segment_end is not None + ) + + if not segments: + raise ValueError(f"No timing segments found for reference shift_id={shift_id}.") + + return segments + + +def _minute_of_day(value: datetime) -> int: + return value.hour * 60 + value.minute + + +def _net_work_minutes(segments: tuple[tuple[datetime, datetime, int], ...]) -> int: + source_minutes = sum(segment_minutes for _, _, segment_minutes in segments) + + if source_minutes > 0: + return source_minutes + + return sum(int((end_at - start_at).total_seconds() // 60) for start_at, end_at, _ in segments) diff --git a/src/scheduling/timeoffice/mapping/sunday_work.py b/src/scheduling/timeoffice/mapping/sunday_work.py new file mode 100644 index 00000000..f4c00998 --- /dev/null +++ b/src/scheduling/timeoffice/mapping/sunday_work.py @@ -0,0 +1,14 @@ +from scheduling.domain import EmployeeSundayWorkHistory +from scheduling.timeoffice.reading.sunday_work import TimeOfficeSundayHistoryRow + + +def map_sunday_work_history( + rows: tuple[TimeOfficeSundayHistoryRow, ...], +) -> tuple[EmployeeSundayWorkHistory, ...]: + return tuple( + EmployeeSundayWorkHistory( + employee_id=row.employee_id, + worked_sundays=row.worked_sundays, + ) + for row in rows + ) diff --git a/src/scheduling/timeoffice/mapping/wishes.py b/src/scheduling/timeoffice/mapping/wishes.py new file mode 100644 index 00000000..d5265526 --- /dev/null +++ b/src/scheduling/timeoffice/mapping/wishes.py @@ -0,0 +1,71 @@ +from datetime import datetime + +from scheduling.domain import Wish, WishKind +from scheduling.timeoffice.facts import TimeOfficeFacts +from scheduling.timeoffice.mapping.shifts import reference_shift_id_for_source_shift +from scheduling.timeoffice.reading.wishes import TimeOfficeWishRow + + +def map_wishes(*, rows: tuple[TimeOfficeWishRow, ...], facts: TimeOfficeFacts) -> tuple[Wish, ...]: + return tuple(_map_wish(row=row, facts=facts) for row in rows) + + +def _map_wish(*, row: TimeOfficeWishRow, facts: TimeOfficeFacts) -> Wish: + if row.work_shift_id is not None: + return _map_shift_wish(row=row, facts=facts) + + return _map_absence_wish(row=row, facts=facts) + + +def _map_shift_wish(*, row: TimeOfficeWishRow, facts: TimeOfficeFacts) -> Wish: + reference_shift_id = reference_shift_id_for_source_shift( + source_shift_id=row.work_shift_id, + source_shift_code=row.work_shift_code, + facts=facts, + context=(f"TimeOffice wish work row employee_id={row.employee_id} date={row.wish_date.date()}"), + ) + + return Wish( + employee_id=row.employee_id, + planning_unit_id=row.planning_unit_id, + date=row.wish_date.date(), + kind=WishKind.SHIFT, + shift_id=reference_shift_id, + ) + + +def _map_absence_wish(*, row: TimeOfficeWishRow, facts: TimeOfficeFacts) -> Wish: + return Wish( + employee_id=row.employee_id, + planning_unit_id=row.planning_unit_id, + date=row.wish_date.date(), + kind=_wish_kind_for_absence_code( + row.resolved_absence_code, + facts=facts, + employee_id=row.employee_id, + wish_date=row.wish_date, + ), + ) + + +def _wish_kind_for_absence_code( + absence_code: str | None, + *, + facts: TimeOfficeFacts, + employee_id: int, + wish_date: datetime, +) -> WishKind: + if absence_code is None: + raise ValueError( + f"Missing resolved absence code for TimeOffice wish row: employee_id={employee_id} wish_date={wish_date}." + ) + + wish_kind = facts.wish_kind_by_absence_code.get(absence_code) + if wish_kind is None: + raise ValueError( + "Unmapped TimeOffice absence code for wish: " + f"employee_id={employee_id} wish_date={wish_date} " + f"absence_code={absence_code!r}." + ) + + return wish_kind diff --git a/src/scheduling/timeoffice/mapping/work_accounts.py b/src/scheduling/timeoffice/mapping/work_accounts.py new file mode 100644 index 00000000..330f83df --- /dev/null +++ b/src/scheduling/timeoffice/mapping/work_accounts.py @@ -0,0 +1,32 @@ +from scheduling.domain import MonthlyWorkAccount +from scheduling.timeoffice.reading.work_accounts import TimeOfficeMonthlyWorkAccountRow + + +def map_monthly_work_accounts( + rows: tuple[TimeOfficeMonthlyWorkAccountRow, ...], +) -> tuple[MonthlyWorkAccount, ...]: + accounts: list[MonthlyWorkAccount] = [] + + for row in rows: + target_minutes = _hours_to_minutes(row.target_hours) + if target_minutes <= 0: + continue + + actual_minutes = _hours_to_minutes(row.actual_hours) + + accounts.append( + MonthlyWorkAccount( + employee_id=row.employee_id, + target_minutes=target_minutes, + actual_minutes=actual_minutes if actual_minutes > 0 else None, + ) + ) + + return tuple(sorted(accounts, key=lambda account: account.employee_id)) + + +def _hours_to_minutes(value: float | None) -> int: + if value is None: + return 0 + + return round(value * 60) diff --git a/src/scheduling/timeoffice/reading/__init__.py b/src/scheduling/timeoffice/reading/__init__.py new file mode 100644 index 00000000..3fc1de88 --- /dev/null +++ b/src/scheduling/timeoffice/reading/__init__.py @@ -0,0 +1,6 @@ +from scheduling.timeoffice.reading.container import TimeOfficeReaders, TimeOfficeSources + +__all__ = [ + "TimeOfficeReaders", + "TimeOfficeSources", +] diff --git a/src/scheduling/timeoffice/reading/container.py b/src/scheduling/timeoffice/reading/container.py new file mode 100644 index 00000000..5b6f7511 --- /dev/null +++ b/src/scheduling/timeoffice/reading/container.py @@ -0,0 +1,161 @@ +from dataclasses import dataclass + +from sqlalchemy import Connection + +from scheduling.domain import PlanningMonth +from scheduling.timeoffice.facts import TimeOfficeFacts +from scheduling.timeoffice.reading.personnel import ( + TimeOfficeEmployeeRow, + TimeOfficePersonnelReader, + TimeOfficePlanningUnitMembershipRow, + TimeOfficePlanPersonnelRow, +) +from scheduling.timeoffice.reading.planning_units import TimeOfficePlanningUnitReader, TimeOfficePlanningUnitRow +from scheduling.timeoffice.reading.roster import TimeOfficeRosterReader, TimeOfficeRosterRow +from scheduling.timeoffice.reading.shifts import TimeOfficeShiftReader, TimeOfficeShiftRow +from scheduling.timeoffice.reading.sunday_work import ( + TimeOfficeSundayHistoryRow, + TimeOfficeSundayWorkHistoryReader, +) +from scheduling.timeoffice.reading.wishes import TimeOfficeWishReader, TimeOfficeWishRow +from scheduling.timeoffice.reading.work_accounts import ( + TimeOfficeMonthlyWorkAccountReader, + TimeOfficeMonthlyWorkAccountRow, +) + + +@dataclass(frozen=True, slots=True) +class TimeOfficeSources: + """TimeOffice source rows for one selected planning month.""" + + planning_month: PlanningMonth + + planning_unit_rows: tuple[TimeOfficePlanningUnitRow, ...] + + # TimeOffice plan artifact. Not canonical solver input. + plan_personnel_rows: tuple[TimeOfficePlanPersonnelRow, ...] + + employee_rows: tuple[TimeOfficeEmployeeRow, ...] + planning_unit_membership_rows: tuple[TimeOfficePlanningUnitMembershipRow, ...] + + shift_rows: tuple[TimeOfficeShiftRow, ...] + roster_rows: tuple[TimeOfficeRosterRow, ...] + wish_rows: tuple[TimeOfficeWishRow, ...] + sunday_history_rows: tuple[TimeOfficeSundayHistoryRow, ...] + monthly_work_account_rows: tuple[TimeOfficeMonthlyWorkAccountRow, ...] + + +@dataclass(frozen=True, slots=True) +class TimeOfficeReaders: + planning_units: TimeOfficePlanningUnitReader + personnel: TimeOfficePersonnelReader + shifts: TimeOfficeShiftReader + roster: TimeOfficeRosterReader + wishes: TimeOfficeWishReader + sunday_work_history: TimeOfficeSundayWorkHistoryReader + monthly_work_accounts: TimeOfficeMonthlyWorkAccountReader + + @classmethod + def create(cls, *, facts: TimeOfficeFacts) -> "TimeOfficeReaders": + return cls( + planning_units=TimeOfficePlanningUnitReader(facts=facts), + personnel=TimeOfficePersonnelReader(), + shifts=TimeOfficeShiftReader(facts=facts), + roster=TimeOfficeRosterReader(), + wishes=TimeOfficeWishReader(), + sunday_work_history=TimeOfficeSundayWorkHistoryReader(), + monthly_work_accounts=TimeOfficeMonthlyWorkAccountReader(facts=facts), + ) + + def read_sources( + self, + *, + connection: Connection, + selected_planning_unit_ids: tuple[int, ...], + planning_month: PlanningMonth, + ) -> TimeOfficeSources: + planning_unit_rows = self.planning_units.read_rows( + connection=connection, + selected_planning_unit_ids=selected_planning_unit_ids, + planning_month=planning_month, + ) + + plan_ids = tuple(row.plan_id for row in planning_unit_rows) + planning_unit_ids = tuple(row.planning_unit_id for row in planning_unit_rows) + + plan_personnel_rows = self.personnel.read_plan_personnel_rows( + connection=connection, + plan_ids=plan_ids, + ) + + planning_unit_membership_rows = self.personnel.read_membership_rows( + connection=connection, + planning_unit_ids=planning_unit_ids, + planning_month=planning_month, + ) + + employee_ids = _collect_relevant_employee_ids( + plan_personnel_rows=plan_personnel_rows, + planning_unit_membership_rows=planning_unit_membership_rows, + ) + + employee_rows = self.personnel.read_employee_rows( + connection=connection, + employee_ids=employee_ids, + ) + + shift_rows = self.shifts.read_rows(connection=connection) + + roster_rows = self.roster.read_rows( + connection=connection, + employee_ids=employee_ids, + planning_month=planning_month, + ) + + wish_rows = self.wishes.read_rows( + connection=connection, + plan_ids=plan_ids, + planning_unit_ids=planning_unit_ids, + employee_ids=employee_ids, + planning_month=planning_month, + ) + + sunday_history_rows = self.sunday_work_history.read_rows( + connection=connection, + employee_ids=employee_ids, + planning_month=planning_month, + ) + + monthly_work_account_rows = self.monthly_work_accounts.read_rows( + connection=connection, + employee_ids=employee_ids, + planning_month=planning_month, + ) + + return TimeOfficeSources( + planning_month=planning_month, + planning_unit_rows=planning_unit_rows, + plan_personnel_rows=plan_personnel_rows, + employee_rows=employee_rows, + planning_unit_membership_rows=planning_unit_membership_rows, + shift_rows=shift_rows, + roster_rows=roster_rows, + wish_rows=wish_rows, + sunday_history_rows=sunday_history_rows, + monthly_work_account_rows=monthly_work_account_rows, + ) + + +def _collect_relevant_employee_ids( + *, + plan_personnel_rows: tuple[TimeOfficePlanPersonnelRow, ...], + planning_unit_membership_rows: tuple[TimeOfficePlanningUnitMembershipRow, ...], +) -> tuple[int, ...]: + return tuple( + dict.fromkeys( + ( + *(row.employee_id for row in planning_unit_membership_rows), + *(row.employee_id for row in plan_personnel_rows), + ) + ) + ) diff --git a/src/scheduling/timeoffice/reading/personnel.py b/src/scheduling/timeoffice/reading/personnel.py new file mode 100644 index 00000000..724ef0cd --- /dev/null +++ b/src/scheduling/timeoffice/reading/personnel.py @@ -0,0 +1,184 @@ +from datetime import datetime +from typing import Self + +from pydantic import model_validator +from sqlalchemy import Connection, bindparam, text + +from scheduling.domain import PlanningMonth +from scheduling.timeoffice.reading.types import CleanNullableText, SourceInt, TimeOfficeSourceRow + + +class TimeOfficePlanPersonnelRow(TimeOfficeSourceRow): + plan_id: SourceInt + planning_unit_id: SourceInt + employee_id: SourceInt + + +class TimeOfficeEmployeeRow(TimeOfficeSourceRow): + employee_id: SourceInt + employee_profession_id: SourceInt + employee_profession_code: CleanNullableText = None + first_name: CleanNullableText = None + last_name: CleanNullableText = None + + +class TimeOfficePlanningUnitMembershipRow(TimeOfficeSourceRow): + planning_unit_id: SourceInt + employee_id: SourceInt + membership_profession_id: SourceInt + membership_profession_code: CleanNullableText = None + valid_from: datetime + valid_until: datetime | None = None + is_home: bool + is_replacement: bool + + @model_validator(mode="after") + def validate_interval(self) -> Self: + if self.valid_until is not None and self.valid_until < self.valid_from: + raise ValueError( + "Invalid TimeOffice planning-unit membership interval: " + f"planning_unit_id={self.planning_unit_id} " + f"employee_id={self.employee_id} " + f"valid_from={self.valid_from!r} " + f"valid_until={self.valid_until!r}." + ) + + return self + + +class TimeOfficePersonnelReader: + """Reads TimeOffice personnel source rows used for scheduling.""" + + def read_plan_personnel_rows( + self, + *, + connection: Connection, + plan_ids: tuple[int, ...], + ) -> tuple[TimeOfficePlanPersonnelRow, ...]: + if not plan_ids: + return () + + query = text( + """ + SELECT + pp.RefPlan AS plan_id, + p.RefPlanungseinheiten AS planning_unit_id, + pp.RefPersonal AS employee_id + FROM TPlanPersonal pp + JOIN TPlan p + ON p.Prim = pp.RefPlan + WHERE pp.RefPlan IN :plan_ids + ORDER BY + p.RefPlanungseinheiten, + pp.RefPlan, + pp.RefPersonal + """ + ).bindparams(bindparam("plan_ids", expanding=True)) + + raw_rows = connection.execute(query, {"plan_ids": plan_ids}).mappings().all() + + return tuple(TimeOfficePlanPersonnelRow.model_validate(row) for row in raw_rows) + + def read_membership_rows( + self, + *, + connection: Connection, + planning_unit_ids: tuple[int, ...], + planning_month: PlanningMonth, + ) -> tuple[TimeOfficePlanningUnitMembershipRow, ...]: + if not planning_unit_ids: + return () + + query = text( + """ + SELECT + pep.RefPlanungseinheiten AS planning_unit_id, + pep.RefPersonal AS employee_id, + pep.RefBerufe AS membership_profession_id, + b.KurzBez AS membership_profession_code, + pep.VonDat AS valid_from, + pep.BisDat AS valid_until, + pep.IstHeimat AS is_home, + pep.IstVonErsatz AS is_replacement + FROM TPlanungseinheitenPersonal pep + LEFT JOIN TBerufe b + ON b.Prim = pep.RefBerufe + WHERE pep.RefPlanungseinheiten IN :planning_unit_ids + AND CONVERT(date, pep.VonDat) <= :end + AND ( + pep.BisDat IS NULL + OR CONVERT(date, pep.BisDat) >= :start + ) + AND ISNULL(pep.KeinEPlan, 0) = 0 + ORDER BY + planning_unit_id, + employee_id, + valid_from, + valid_until + """ + ).bindparams(bindparam("planning_unit_ids", expanding=True)) + + raw_rows = ( + connection.execute( + query, + { + "planning_unit_ids": planning_unit_ids, + "start": planning_month.start, + "end": planning_month.end, + }, + ) + .mappings() + .all() + ) + + return tuple(TimeOfficePlanningUnitMembershipRow.model_validate(row) for row in raw_rows) + + def read_employee_rows( + self, + *, + connection: Connection, + employee_ids: tuple[int, ...], + ) -> tuple[TimeOfficeEmployeeRow, ...]: + if not employee_ids: + return () + + query = text( + """ + SELECT + per.Prim AS employee_id, + per.RefBerufe AS employee_profession_id, + b.KurzBez AS employee_profession_code, + per.Vorname AS first_name, + per.Name AS last_name + FROM TPersonal per + LEFT JOIN TBerufe b + ON b.Prim = per.RefBerufe + WHERE per.Prim IN :employee_ids + ORDER BY + per.Name, + per.Vorname, + per.Prim + """ + ).bindparams(bindparam("employee_ids", expanding=True)) + + raw_rows = connection.execute(query, {"employee_ids": employee_ids}).mappings().all() + + rows = tuple(TimeOfficeEmployeeRow.model_validate(row) for row in raw_rows) + self._validate_all_requested_employees_found( + requested_employee_ids=employee_ids, + rows=rows, + ) + + return rows + + def _validate_all_requested_employees_found( + self, + *, + requested_employee_ids: tuple[int, ...], + rows: tuple[TimeOfficeEmployeeRow, ...], + ) -> None: + returned_employee_ids = {row.employee_id for row in rows} + missing_employee_ids = sorted(set(requested_employee_ids) - returned_employee_ids) + + if missing_employee_ids: + raise ValueError(f"Missing TimeOffice employee master rows for employee_ids={missing_employee_ids}.") diff --git a/src/scheduling/timeoffice/repositories/planning_units.py b/src/scheduling/timeoffice/reading/planning_units.py similarity index 50% rename from src/scheduling/timeoffice/repositories/planning_units.py rename to src/scheduling/timeoffice/reading/planning_units.py index 7bec66f6..33716fef 100644 --- a/src/scheduling/timeoffice/repositories/planning_units.py +++ b/src/scheduling/timeoffice/reading/planning_units.py @@ -3,12 +3,12 @@ from pydantic import model_validator from sqlalchemy import Connection, bindparam, text -from scheduling.domain import Plan, PlanningMonth, PlanningUnit, PlanningUnitKind, SchedulingBaseModel +from scheduling.domain import PlanningMonth from scheduling.timeoffice.facts import TimeOfficeFacts -from scheduling.timeoffice.repositories.types import SourceInt, TimeOfficeSourceRow +from scheduling.timeoffice.reading.types import SourceInt, TimeOfficeSourceRow -class _TimeOfficePlanningUnitRow(TimeOfficeSourceRow): +class TimeOfficePlanningUnitRow(TimeOfficeSourceRow): planning_unit_id: SourceInt plan_id: SourceInt plan_planning_unit_id: SourceInt @@ -26,50 +26,22 @@ def validate_plan_reference(self) -> Self: return self -class PlanningUnitRepositoryResult(SchedulingBaseModel): - planning_units: tuple[PlanningUnit, ...] - plans: tuple[Plan, ...] - - -class TimeOfficePlanningUnitRepository: - """Reads selected TimeOffice planning units and their concrete plans.""" +class TimeOfficePlanningUnitReader: + """Reads selected TimeOffice planning-unit and target-plan source rows.""" def __init__(self, *, facts: TimeOfficeFacts) -> None: self._facts = facts - def fetch( + def read_rows( self, *, connection: Connection, selected_planning_unit_ids: tuple[int, ...], planning_month: PlanningMonth, - ) -> PlanningUnitRepositoryResult: + ) -> tuple[TimeOfficePlanningUnitRow, ...]: if not selected_planning_unit_ids: - return PlanningUnitRepositoryResult(planning_units=(), plans=()) - - rows = self._fetch_rows( - connection=connection, - selected_planning_unit_ids=selected_planning_unit_ids, - planning_month=planning_month, - ) - - self._validate_rows( - requested_ids=selected_planning_unit_ids, - rows=rows, - ) + return () - return PlanningUnitRepositoryResult( - planning_units=self._map_planning_units(rows), - plans=self._map_plans(rows), - ) - - def _fetch_rows( - self, - *, - connection: Connection, - selected_planning_unit_ids: tuple[int, ...], - planning_month: PlanningMonth, - ) -> tuple[_TimeOfficePlanningUnitRow, ...]: query = text( """ SELECT @@ -103,9 +75,17 @@ def _fetch_rows( .all() ) - return tuple(_TimeOfficePlanningUnitRow.model_validate(row) for row in raw_rows) + rows = tuple(TimeOfficePlanningUnitRow.model_validate(row) for row in raw_rows) + self._validate_requested_units(requested_ids=selected_planning_unit_ids, rows=rows) + + return rows - def _validate_rows(self, *, requested_ids: tuple[int, ...], rows: tuple[_TimeOfficePlanningUnitRow, ...]) -> None: + def _validate_requested_units( + self, + *, + requested_ids: tuple[int, ...], + rows: tuple[TimeOfficePlanningUnitRow, ...], + ) -> None: requested = set(requested_ids) returned_ids = [row.planning_unit_id for row in rows] @@ -113,39 +93,18 @@ def _validate_rows(self, *, requested_ids: tuple[int, ...], rows: tuple[_TimeOff if missing: raise ValueError(f"No selected TimeOffice target plan found for planning_unit_ids={missing}.") - duplicates = self._duplicate_values(returned_ids) + duplicates = _duplicate_values(returned_ids) if duplicates: raise ValueError(f"Multiple selected TimeOffice target plans found for planning_unit_ids={duplicates}.") - def _map_planning_units(self, rows: tuple[_TimeOfficePlanningUnitRow, ...]) -> tuple[PlanningUnit, ...]: - return tuple( - PlanningUnit( - planning_unit_id=row.planning_unit_id, - display_name=f"Planning Unit {row.planning_unit_id}", - kind=self._facts.planning_unit_kind_map.get( - row.planning_unit_id, - PlanningUnitKind.STATION, - ), - ) - for row in rows - ) - - def _map_plans(self, rows: tuple[_TimeOfficePlanningUnitRow, ...]) -> tuple[Plan, ...]: - return tuple( - Plan( - plan_id=row.plan_id, - planning_unit_id=row.plan_planning_unit_id, - ) - for row in rows - ) - def _duplicate_values(self, values: list[int]) -> list[int]: - seen: set[int] = set() - duplicates: set[int] = set() +def _duplicate_values(values: list[int]) -> list[int]: + seen: set[int] = set() + duplicates: set[int] = set() - for value in values: - if value in seen: - duplicates.add(value) - seen.add(value) + for value in values: + if value in seen: + duplicates.add(value) + seen.add(value) - return sorted(duplicates) + return sorted(duplicates) diff --git a/src/scheduling/timeoffice/reading/roster.py b/src/scheduling/timeoffice/reading/roster.py new file mode 100644 index 00000000..ab5e3d98 --- /dev/null +++ b/src/scheduling/timeoffice/reading/roster.py @@ -0,0 +1,120 @@ +from datetime import datetime +from typing import Self + +from pydantic import model_validator +from sqlalchemy import Connection, bindparam, text + +from scheduling.domain import PlanningMonth +from scheduling.timeoffice.reading.types import CleanNullableText, SourceInt, SourceNullableInt, TimeOfficeSourceRow + + +class TimeOfficeRosterRow(TimeOfficeSourceRow): + plan_id: SourceNullableInt = None + employee_id: SourceInt + roster_date: datetime + + work_shift_id: SourceNullableInt = None + work_shift_code: CleanNullableText = None + + global_absence_shift_id: SourceNullableInt = None + absence_shift_id: SourceNullableInt = None + resolved_absence_shift_id: SourceNullableInt = None + resolved_absence_code: CleanNullableText = None + + planning_unit_id: SourceNullableInt = None + + @model_validator(mode="after") + def validate_row_kind(self) -> Self: + has_work_shift = self.work_shift_id is not None + has_absence = self.global_absence_shift_id is not None or self.absence_shift_id is not None + + if not has_work_shift and not has_absence: + raise ValueError( + "Invalid TimeOffice roster row: neither work shift nor absence is set " + f"for employee_id={self.employee_id}, roster_date={self.roster_date}." + ) + + return self + + @model_validator(mode="after") + def validate_absence_references(self) -> Self: + if ( + self.global_absence_shift_id is not None + and self.absence_shift_id is not None + and self.global_absence_shift_id != self.absence_shift_id + ): + raise ValueError( + "Conflicting TimeOffice absence references in TPlanPersonalKommtGeht: " + f"RefgAbw={self.global_absence_shift_id} " + f"RefDienstAbw={self.absence_shift_id}." + ) + + return self + + +class TimeOfficeRosterReader: + """Reads TimeOffice roster source rows from TPlanPersonalKommtGeht.""" + + def read_rows( + self, + *, + connection: Connection, + employee_ids: tuple[int, ...], + planning_month: PlanningMonth, + ) -> tuple[TimeOfficeRosterRow, ...]: + if not employee_ids: + return () + + query = text( + """ + SELECT + pkg.RefPlan AS plan_id, + pkg.RefPersonal AS employee_id, + pkg.Datum AS roster_date, + + pkg.RefDienste AS work_shift_id, + work_d.KurzBez AS work_shift_code, + + pkg.RefgAbw AS global_absence_shift_id, + pkg.RefDienstAbw AS absence_shift_id, + + COALESCE(pkg.RefgAbw, pkg.RefDienstAbw) AS resolved_absence_shift_id, + COALESCE(global_absence_d.KurzBez, absence_d.KurzBez) AS resolved_absence_code, + + pkg.RefPlanungseinheiten AS planning_unit_id + FROM TPlanPersonalKommtGeht pkg + LEFT JOIN TDienste work_d + ON work_d.Prim = pkg.RefDienste + LEFT JOIN TDienste global_absence_d + ON global_absence_d.Prim = pkg.RefgAbw + LEFT JOIN TDienste absence_d + ON absence_d.Prim = pkg.RefDienstAbw + WHERE pkg.RefPersonal IN :employee_ids + AND CONVERT(date, pkg.Datum) BETWEEN :start AND :end + AND ( + pkg.RefDienste IS NOT NULL + OR pkg.RefgAbw IS NOT NULL + OR pkg.RefDienstAbw IS NOT NULL + ) + ORDER BY + pkg.RefPersonal, + pkg.Datum, + pkg.RefPlan, + pkg.lfdNr + """ + ).bindparams(bindparam("employee_ids", expanding=True)) + + raw_rows = ( + connection.execute( + query, + { + "employee_ids": employee_ids, + "start": planning_month.start, + "end": planning_month.end, + }, + ) + .mappings() + .all() + ) + + return tuple(TimeOfficeRosterRow.model_validate(row) for row in raw_rows) diff --git a/src/scheduling/timeoffice/reading/shifts.py b/src/scheduling/timeoffice/reading/shifts.py new file mode 100644 index 00000000..6b250df8 --- /dev/null +++ b/src/scheduling/timeoffice/reading/shifts.py @@ -0,0 +1,87 @@ +from datetime import datetime +from typing import Self + +from pydantic import model_validator +from sqlalchemy import Connection, bindparam, text + +from scheduling.timeoffice.facts import TimeOfficeFacts +from scheduling.timeoffice.reading.types import CleanText, SourceInt, SourceNullableInt, TimeOfficeSourceRow + + +class TimeOfficeShiftRow(TimeOfficeSourceRow): + shift_id: SourceInt + shift_code: CleanText + shift_type_id: SourceInt + + segment_start: datetime | None = None + segment_end: datetime | None = None + segment_minutes: SourceNullableInt = None + + @model_validator(mode="after") + def validate_segment_shape(self) -> Self: + has_start = self.segment_start is not None + has_end = self.segment_end is not None + + if has_start != has_end: + raise ValueError( + "Incomplete TimeOffice shift segment: " + f"shift_id={self.shift_id} " + f"segment_start={self.segment_start!r} " + f"segment_end={self.segment_end!r}." + ) + + if self.segment_start is not None and self.segment_end is not None: + if self.segment_end <= self.segment_start: + raise ValueError( + "Invalid TimeOffice shift segment: " + f"shift_id={self.shift_id} " + f"segment_start={self.segment_start!r} " + f"segment_end={self.segment_end!r}." + ) + + if self.segment_minutes is not None and self.segment_minutes < 0: + raise ValueError( + "Invalid negative TimeOffice shift segment minutes: " + f"shift_id={self.shift_id} " + f"segment_minutes={self.segment_minutes!r}." + ) + + return self + + +class TimeOfficeShiftReader: + """Reads TimeOffice reference-shift source rows.""" + + def __init__(self, *, facts: TimeOfficeFacts) -> None: + self._facts = facts + + def read_rows(self, *, connection: Connection) -> tuple[TimeOfficeShiftRow, ...]: + shift_ids = tuple(self._facts.reference_shift_facts_by_id.keys()) + + if not shift_ids: + return () + + query = text( + """ + SELECT + d.Prim AS shift_id, + d.KurzBez AS shift_code, + d.RefDienstTypen AS shift_type_id, + + sz.Kommt AS segment_start, + sz.Geht AS segment_end, + sz.Minuten AS segment_minutes + FROM TDienste d + LEFT JOIN TDiensteSollzeiten sz + ON sz.RefDienste = d.Prim + WHERE d.Prim IN :shift_ids + ORDER BY + d.Prim, + sz.Kommt, + sz.Geht + """ + ).bindparams(bindparam("shift_ids", expanding=True)) + + raw_rows = connection.execute(query, {"shift_ids": shift_ids}).mappings().all() + + return tuple(TimeOfficeShiftRow.model_validate(row) for row in raw_rows) diff --git a/src/scheduling/timeoffice/reading/sources.py b/src/scheduling/timeoffice/reading/sources.py new file mode 100644 index 00000000..e69de29b diff --git a/src/scheduling/timeoffice/reading/sunday_work.py b/src/scheduling/timeoffice/reading/sunday_work.py new file mode 100644 index 00000000..7a168da7 --- /dev/null +++ b/src/scheduling/timeoffice/reading/sunday_work.py @@ -0,0 +1,123 @@ +from datetime import date + +from sqlalchemy import Connection, bindparam, text + +from scheduling.domain import PlanningMonth +from scheduling.timeoffice.reading.types import CleanNullableText, SourceInt, TimeOfficeSourceRow + + +class TimeOfficeSundayAccountRow(TimeOfficeSourceRow): + account_id: SourceInt + account_code: CleanNullableText = None + account_name: CleanNullableText = None + is_daily_account: bool | None = None + + +class TimeOfficeSundayHistoryRow(TimeOfficeSourceRow): + employee_id: SourceInt + worked_sundays: SourceInt + + +class TimeOfficeSundayWorkHistoryReader: + """Reads historical worked-Sunday count source rows from TimeOffice.""" + + LOOKBACK_YEARS = 1 + SUNDAY_ACCOUNT_CODE = "SONNTAG" + + def read_rows( + self, + *, + connection: Connection, + employee_ids: tuple[int, ...], + planning_month: PlanningMonth, + ) -> tuple[TimeOfficeSundayHistoryRow, ...]: + if not employee_ids: + return () + + sunday_account_id = self._read_sunday_account_id(connection) + + query = text( + """ + SELECT + p.Prim AS employee_id, + COUNT(DISTINCT CAST(pkt.Datum AS date)) AS worked_sundays + FROM TPersonal p + LEFT JOIN TPersonalKontenJeTag pkt + ON pkt.RefPersonal = p.Prim + AND pkt.RefKonten = :sunday_account_id + AND CAST(pkt.Datum AS date) BETWEEN :lookback_start AND :lookback_end + AND DATEDIFF( + day, + CONVERT(date, '1900-01-07', 23), + CAST(pkt.Datum AS date) + ) % 7 = 0 + AND ISNULL(pkt.Wert, 0) > 0 + WHERE p.Prim IN :employee_ids + GROUP BY p.Prim + ORDER BY p.Prim + """ + ).bindparams(bindparam("employee_ids", expanding=True)) + + raw_rows = ( + connection.execute( + query, + { + "employee_ids": employee_ids, + "sunday_account_id": sunday_account_id, + "lookback_start": _subtract_years(planning_month.end, self.LOOKBACK_YEARS), + "lookback_end": planning_month.end, + }, + ) + .mappings() + .all() + ) + + return tuple(TimeOfficeSundayHistoryRow.model_validate(row) for row in raw_rows) + + def _read_sunday_account_id(self, connection: Connection) -> int: + query = text( + """ + SELECT + Prim AS account_id, + BezProg AS account_code, + Bez AS account_name, + IstTagesKonto AS is_daily_account + FROM TKonten + WHERE BezProg = :sunday_account_code + """ + ) + + rows = tuple( + TimeOfficeSundayAccountRow.model_validate(row) + for row in connection.execute( + query, + {"sunday_account_code": self.SUNDAY_ACCOUNT_CODE}, + ) + .mappings() + .all() + ) + + if len(rows) != 1: + raise ValueError( + "Expected exactly one TimeOffice Sunday account with " + f"BezProg={self.SUNDAY_ACCOUNT_CODE!r}, found {len(rows)}." + ) + + row = rows[0] + + if row.is_daily_account is not True: + raise ValueError( + "TimeOffice Sunday account must be a daily account: " + f"account_id={row.account_id} " + f"code={row.account_code!r} " + f"name={row.account_name!r}." + ) + + return row.account_id + + +def _subtract_years(value: date, years: int) -> date: + try: + return value.replace(year=value.year - years) + except ValueError: + return value.replace(year=value.year - years, day=28) diff --git a/src/scheduling/timeoffice/repositories/types.py b/src/scheduling/timeoffice/reading/types.py similarity index 100% rename from src/scheduling/timeoffice/repositories/types.py rename to src/scheduling/timeoffice/reading/types.py diff --git a/src/scheduling/timeoffice/reading/wishes.py b/src/scheduling/timeoffice/reading/wishes.py new file mode 100644 index 00000000..aaa60270 --- /dev/null +++ b/src/scheduling/timeoffice/reading/wishes.py @@ -0,0 +1,153 @@ +from datetime import datetime +from typing import Self + +from pydantic import model_validator +from sqlalchemy import Connection, bindparam, text + +from scheduling.domain import PlanningMonth +from scheduling.timeoffice.reading.types import CleanNullableText, SourceInt, SourceNullableInt, TimeOfficeSourceRow + + +class TimeOfficeWishRow(TimeOfficeSourceRow): + employee_id: SourceInt + wish_date: datetime + plan_id: SourceInt + planning_unit_id: SourceInt + + work_shift_id: SourceNullableInt = None + work_shift_code: CleanNullableText = None + work_shift_name: CleanNullableText = None + + global_absence_shift_id: SourceNullableInt = None + global_absence_shift_code: CleanNullableText = None + global_absence_shift_name: CleanNullableText = None + + absence_shift_id: SourceNullableInt = None + absence_shift_code: CleanNullableText = None + absence_shift_name: CleanNullableText = None + + resolved_absence_shift_id: SourceNullableInt = None + resolved_absence_code: CleanNullableText = None + resolved_absence_name: CleanNullableText = None + + @model_validator(mode="after") + def validate_row_kind(self) -> Self: + has_work_shift = self.work_shift_id is not None + has_absence = self.global_absence_shift_id is not None or self.absence_shift_id is not None + + if has_work_shift and has_absence: + raise ValueError( + "Ambiguous TimeOffice wish row: both work shift and absence are set " + f"for employee_id={self.employee_id}, wish_date={self.wish_date}." + ) + + if not has_work_shift and not has_absence: + raise ValueError( + "Invalid TimeOffice wish row: neither work shift nor absence is set " + f"for employee_id={self.employee_id}, wish_date={self.wish_date}." + ) + + return self + + @model_validator(mode="after") + def validate_absence_references(self) -> Self: + if ( + self.global_absence_shift_id is not None + and self.absence_shift_id is not None + and self.global_absence_shift_id != self.absence_shift_id + ): + raise ValueError( + "Conflicting TimeOffice wish absence references in TPlanPersonalKommtGeht: " + f"RefgAbw={self.global_absence_shift_id} " + f"RefDienstAbw={self.absence_shift_id}." + ) + + return self + + +class TimeOfficeWishReader: + """Reads employee wish source rows from TimeOffice.""" + + def read_rows( + self, + *, + connection: Connection, + plan_ids: tuple[int, ...], + planning_unit_ids: tuple[int, ...], + employee_ids: tuple[int, ...], + planning_month: PlanningMonth, + ) -> tuple[TimeOfficeWishRow, ...]: + if not plan_ids or not planning_unit_ids or not employee_ids: + return () + + query = text( + """ + SELECT + pkg.RefPersonal AS employee_id, + pkg.Datum AS wish_date, + pkg.RefPlan AS plan_id, + pkg.RefPlanungseinheiten AS planning_unit_id, + + pkg.RefDienste AS work_shift_id, + work_d.KurzBez AS work_shift_code, + work_d.Bezeichnung AS work_shift_name, + + pkg.RefgAbw AS global_absence_shift_id, + global_absence_d.KurzBez AS global_absence_shift_code, + global_absence_d.Bezeichnung AS global_absence_shift_name, + + pkg.RefDienstAbw AS absence_shift_id, + absence_d.KurzBez AS absence_shift_code, + absence_d.Bezeichnung AS absence_shift_name, + + COALESCE(pkg.RefgAbw, pkg.RefDienstAbw) AS resolved_absence_shift_id, + COALESCE(global_absence_d.KurzBez, absence_d.KurzBez) AS resolved_absence_code, + COALESCE(global_absence_d.Bezeichnung, absence_d.Bezeichnung) AS resolved_absence_name + FROM TPlanPersonalKommtGeht pkg + LEFT JOIN TDienste work_d + ON work_d.Prim = pkg.RefDienste + LEFT JOIN TDienste global_absence_d + ON global_absence_d.Prim = pkg.RefgAbw + LEFT JOIN TDienste absence_d + ON absence_d.Prim = pkg.RefDienstAbw + WHERE pkg.RefPersonal IN :employee_ids + AND pkg.RefPlan IN :plan_ids + AND pkg.RefPlanungseinheiten IN :planning_unit_ids + AND CONVERT(date, pkg.Datum) BETWEEN :start AND :end + AND ISNULL(pkg.Wunschdienst, 0) <> 0 + AND ( + pkg.RefDienste IS NOT NULL + OR pkg.RefgAbw IS NOT NULL + OR pkg.RefDienstAbw IS NOT NULL + ) + ORDER BY + pkg.RefPersonal, + pkg.Datum, + pkg.RefPlan, + pkg.RefPlanungseinheiten, + pkg.RefDienste, + pkg.RefgAbw, + pkg.RefDienstAbw + """ + ).bindparams( + bindparam("employee_ids", expanding=True), + bindparam("plan_ids", expanding=True), + bindparam("planning_unit_ids", expanding=True), + ) + + raw_rows = ( + connection.execute( + query, + { + "plan_ids": plan_ids, + "planning_unit_ids": planning_unit_ids, + "employee_ids": employee_ids, + "start": planning_month.start, + "end": planning_month.end, + }, + ) + .mappings() + .all() + ) + + return tuple(TimeOfficeWishRow.model_validate(row) for row in raw_rows) diff --git a/src/scheduling/timeoffice/reading/work_accounts.py b/src/scheduling/timeoffice/reading/work_accounts.py new file mode 100644 index 00000000..9ec1e3fa --- /dev/null +++ b/src/scheduling/timeoffice/reading/work_accounts.py @@ -0,0 +1,69 @@ +from sqlalchemy import Connection, bindparam, text + +from scheduling.domain import PlanningMonth +from scheduling.timeoffice.facts import TimeOfficeFacts +from scheduling.timeoffice.reading.types import SourceInt, TimeOfficeSourceRow + + +class TimeOfficeMonthlyWorkAccountRow(TimeOfficeSourceRow): + employee_id: SourceInt + month: SourceInt + target_hours: float | None = None + actual_hours: float | None = None + + +class TimeOfficeMonthlyWorkAccountReader: + """Reads monthly target and actual work-account source rows.""" + + def __init__(self, *, facts: TimeOfficeFacts) -> None: + self._facts = facts + + def read_rows( + self, + *, + connection: Connection, + employee_ids: tuple[int, ...], + planning_month: PlanningMonth, + ) -> tuple[TimeOfficeMonthlyWorkAccountRow, ...]: + if not employee_ids: + return () + + query = text( + """ + SELECT + target.RefPersonal AS employee_id, + target.Monat AS month, + target.Wert2 AS target_hours, + actual.Wert2 AS actual_hours + FROM TPersonalKontenJeMonat target + LEFT JOIN TPersonalKontenJeMonat actual + ON actual.RefPersonal = target.RefPersonal + AND actual.Monat = target.Monat + AND actual.RefKonten = :actual_account_id + WHERE target.RefPersonal IN :employee_ids + AND target.Monat = :month + AND target.RefKonten = :target_account_id + ORDER BY + target.RefPersonal + """ + ).bindparams(bindparam("employee_ids", expanding=True)) + + raw_rows = ( + connection.execute( + query, + { + "employee_ids": employee_ids, + "month": _timeoffice_month(planning_month), + "target_account_id": self._facts.monthly_target_work_account_id, + "actual_account_id": self._facts.monthly_actual_work_account_id, + }, + ) + .mappings() + .all() + ) + + return tuple(TimeOfficeMonthlyWorkAccountRow.model_validate(row) for row in raw_rows) + + +def _timeoffice_month(planning_month: PlanningMonth) -> int: + return planning_month.year * 100 + planning_month.month diff --git a/src/scheduling/timeoffice/repositories/__init__.py b/src/scheduling/timeoffice/repositories/__init__.py deleted file mode 100644 index 5d0ff5b9..00000000 --- a/src/scheduling/timeoffice/repositories/__init__.py +++ /dev/null @@ -1,29 +0,0 @@ -from scheduling.timeoffice.repositories.container import TimeOfficeRepositories -from scheduling.timeoffice.repositories.demand import DemandRepositoryResult, TimeOfficeDemandRepository -from scheduling.timeoffice.repositories.personnel import PersonnelRepositoryResult, TimeOfficePersonnelRepository -from scheduling.timeoffice.repositories.planning_units import ( - PlanningUnitRepositoryResult, - TimeOfficePlanningUnitRepository, -) -from scheduling.timeoffice.repositories.roster import RosterRepositoryResult, TimeOfficeRosterRepository -from scheduling.timeoffice.repositories.shifts import ShiftRepositoryResult, TimeOfficeShiftRepository -from scheduling.timeoffice.repositories.sunday_work_history import ( - SundayWorkHistoryRepositoryResult, - TimeOfficeSundayWorkHistoryRepository, -) - -__all__ = [ - "PersonnelRepositoryResult", - "PlanningUnitRepositoryResult", - "RosterRepositoryResult", - "ShiftRepositoryResult", - "TimeOfficePersonnelRepository", - "TimeOfficePlanningUnitRepository", - "TimeOfficeRepositories", - "TimeOfficeRosterRepository", - "TimeOfficeShiftRepository", - "DemandRepositoryResult", - "TimeOfficeDemandRepository", - "SundayWorkHistoryRepositoryResult", - "TimeOfficeSundayWorkHistoryRepository", -] diff --git a/src/scheduling/timeoffice/repositories/container.py b/src/scheduling/timeoffice/repositories/container.py deleted file mode 100644 index f768698d..00000000 --- a/src/scheduling/timeoffice/repositories/container.py +++ /dev/null @@ -1,36 +0,0 @@ -from dataclasses import dataclass - -from scheduling.timeoffice.facts import TimeOfficeFacts -from scheduling.timeoffice.repositories.demand import TimeOfficeDemandRepository -from scheduling.timeoffice.repositories.monthly_work_accounts import TimeOfficeMonthlyWorkAccountRepository -from scheduling.timeoffice.repositories.personnel import TimeOfficePersonnelRepository -from scheduling.timeoffice.repositories.planning_units import TimeOfficePlanningUnitRepository -from scheduling.timeoffice.repositories.roster import TimeOfficeRosterRepository -from scheduling.timeoffice.repositories.shifts import TimeOfficeShiftRepository -from scheduling.timeoffice.repositories.sunday_work_history import TimeOfficeSundayWorkHistoryRepository -from scheduling.timeoffice.repositories.wishes import TimeOfficeWishRepository - - -@dataclass(frozen=True, slots=True) -class TimeOfficeRepositories: - planning_units: TimeOfficePlanningUnitRepository - personnel: TimeOfficePersonnelRepository - shifts: TimeOfficeShiftRepository - roster: TimeOfficeRosterRepository - demand: TimeOfficeDemandRepository - sunday_work_history: TimeOfficeSundayWorkHistoryRepository - wishes: TimeOfficeWishRepository - monthly_work_accounts: TimeOfficeMonthlyWorkAccountRepository - - @classmethod - def create(cls, *, facts: TimeOfficeFacts) -> "TimeOfficeRepositories": - return cls( - planning_units=TimeOfficePlanningUnitRepository(facts=facts), - personnel=TimeOfficePersonnelRepository(facts=facts), - shifts=TimeOfficeShiftRepository(facts=facts), - roster=TimeOfficeRosterRepository(facts=facts), - demand=TimeOfficeDemandRepository(facts=facts), - sunday_work_history=TimeOfficeSundayWorkHistoryRepository(), - wishes=TimeOfficeWishRepository(facts=facts), - monthly_work_accounts=TimeOfficeMonthlyWorkAccountRepository(facts=facts), - ) diff --git a/src/scheduling/timeoffice/repositories/demand.py b/src/scheduling/timeoffice/repositories/demand.py deleted file mode 100644 index 423abbd8..00000000 --- a/src/scheduling/timeoffice/repositories/demand.py +++ /dev/null @@ -1,158 +0,0 @@ -from datetime import timedelta - -from sqlalchemy import Connection - -from scheduling.domain import ( - DemandRequirement, - PlanningMonth, - PlanningUnit, - PlanningUnitKind, - SchedulingBaseModel, - Shift, - StaffingDemandRole, -) -from scheduling.domain.employee import StaffLevel -from scheduling.timeoffice.facts import TimeOfficeDemandFact, TimeOfficeFacts - - -class DemandRepositoryResult(SchedulingBaseModel): - demand_requirements: tuple[DemandRequirement, ...] - - -class TimeOfficeDemandRepository: - """Reads TimeOffice demand/Bedarf. - - The test database currently has no usable `TBenutzerBedarf*` rows for the - selected planning units. Therefore this repository is facts-backed for now. - - Later, the internals can be replaced or extended with DB-backed reads from: - - TBenutzerBedarfsGruppen - - TBenutzerBedarf - - TBenutzerBedarfTagTypGruppe - - The output contract stays `DemandRequirement`. - """ - - def __init__(self, *, facts: TimeOfficeFacts) -> None: - self._facts = facts - - def fetch( - self, - *, - connection: Connection, - planning_month: PlanningMonth, - planning_units: tuple[PlanningUnit, ...], - shifts: tuple[Shift, ...], - ) -> DemandRepositoryResult: - # Kept in the signature because demand is architecturally source-backed. - # The current fallback implementation does not need DB rows yet. - _ = connection - - return DemandRepositoryResult( - demand_requirements=self._build_from_facts( - planning_month=planning_month, - planning_units=planning_units, - shifts=shifts, - ) - ) - - def _build_from_facts( - self, - *, - planning_month: PlanningMonth, - planning_units: tuple[PlanningUnit, ...], - shifts: tuple[Shift, ...], - ) -> tuple[DemandRequirement, ...]: - shifts_by_id = {shift.shift_id: shift for shift in shifts} - - station_planning_units = tuple( - planning_unit for planning_unit in planning_units if planning_unit.kind == PlanningUnitKind.STATION - ) - - requirements_by_key: dict[ - tuple[int, object, int, StaffLevel], - DemandRequirement, - ] = {} - - current_date = planning_month.start - while current_date <= planning_month.end: - iso_weekday = current_date.isoweekday() - - for planning_unit in station_planning_units: - for fact in self._facts.demand_facts: - if not self._applies_to_planning_unit( - fact=fact, - planning_unit_id=planning_unit.planning_unit_id, - ): - continue - - shift = shifts_by_id.get(fact.source_shift_id) - if shift is None: - raise ValueError(f"TimeOffice demand fact references unknown shift_id={fact.source_shift_id}.") - - if shift.staffing_role != StaffingDemandRole.REQUIRED_MINIMUM: - raise ValueError( - "TimeOffice demand fact must reference a REQUIRED_MINIMUM shift: " - f"shift_id={shift.shift_id} staffing_role={shift.staffing_role}." - ) - - required_count = fact.required_by_iso_weekday.get(iso_weekday) - if required_count is None: - raise ValueError( - "TimeOffice demand fact missing ISO weekday " - f"{iso_weekday}: shift_id={fact.source_shift_id} " - f"staff_level={fact.staff_level}." - ) - - if required_count <= 0: - continue - - requirement = DemandRequirement( - planning_unit_id=planning_unit.planning_unit_id, - date=current_date, - shift_id=fact.source_shift_id, - staff_level=fact.staff_level, - required_count=required_count, - ) - - key = ( - requirement.planning_unit_id, - requirement.date, - requirement.shift_id, - requirement.staff_level, - ) - - existing = requirements_by_key.get(key) - if existing is not None: - raise ValueError( - "Duplicate TimeOffice demand fact expansion: " - f"planning_unit_id={requirement.planning_unit_id} " - f"date={requirement.date} " - f"shift_id={requirement.shift_id} " - f"staff_level={requirement.staff_level}." - ) - - requirements_by_key[key] = requirement - - current_date += timedelta(days=1) - - return tuple( - requirements_by_key[key] - for key in sorted( - requirements_by_key, - key=lambda item: ( - item[0], - item[1], - item[2], - str(item[3]), - ), - ) - ) - - def _applies_to_planning_unit( - self, - *, - fact: TimeOfficeDemandFact, - planning_unit_id: int, - ) -> bool: - return fact.planning_unit_ids is None or planning_unit_id in fact.planning_unit_ids diff --git a/src/scheduling/timeoffice/repositories/monthly_work_accounts.py b/src/scheduling/timeoffice/repositories/monthly_work_accounts.py deleted file mode 100644 index fe019d04..00000000 --- a/src/scheduling/timeoffice/repositories/monthly_work_accounts.py +++ /dev/null @@ -1,137 +0,0 @@ -from sqlalchemy import Connection, bindparam, text - -from scheduling.domain import Employee, MonthlyWorkAccount, PlanningMonth, SchedulingBaseModel -from scheduling.timeoffice.facts import TimeOfficeFacts -from scheduling.timeoffice.repositories.types import ( - SourceInt, - TimeOfficeSourceRow, -) - - -class _TimeOfficeMonthlyWorkAccountRow(TimeOfficeSourceRow): - employee_id: SourceInt - month: SourceInt - target_hours: float | None = None - actual_hours: float | None = None - - -class TimeOfficeMonthlyWorkAccountRepositoryResult(SchedulingBaseModel): - monthly_work_accounts: tuple[MonthlyWorkAccount, ...] - - -class TimeOfficeMonthlyWorkAccountRepository: - """Reads monthly target and actual work hours from TimeOffice account values.""" - - def __init__(self, *, facts: TimeOfficeFacts) -> None: - self._facts = facts - - def fetch( - self, - *, - connection: Connection, - employees: tuple[Employee, ...], - planning_month: PlanningMonth, - ) -> TimeOfficeMonthlyWorkAccountRepositoryResult: - if not employees: - return TimeOfficeMonthlyWorkAccountRepositoryResult(monthly_work_accounts=()) - - rows = self._fetch_rows( - connection=connection, - employee_ids=tuple(employee.employee_id for employee in employees), - month=self._timeoffice_month(planning_month), - ) - - self._validate_unique_employee_rows(rows) - - return TimeOfficeMonthlyWorkAccountRepositoryResult(monthly_work_accounts=self._map_accounts(rows)) - - def _fetch_rows( - self, - *, - connection: Connection, - employee_ids: tuple[int, ...], - month: int, - ) -> tuple[_TimeOfficeMonthlyWorkAccountRow, ...]: - query = text( - """ - SELECT - target.RefPersonal AS employee_id, - target.Monat AS month, - target.Wert2 AS target_hours, - actual.Wert2 AS actual_hours - FROM TPersonalKontenJeMonat target - LEFT JOIN TPersonalKontenJeMonat actual - ON actual.RefPersonal = target.RefPersonal - AND actual.Monat = target.Monat - AND actual.RefKonten = :actual_account_id - WHERE target.RefPersonal IN :employee_ids - AND target.Monat = :month - AND target.RefKonten = :target_account_id - ORDER BY - target.RefPersonal - """ - ).bindparams(bindparam("employee_ids", expanding=True)) - - raw_rows = ( - connection.execute( - query, - { - "employee_ids": employee_ids, - "month": month, - "target_account_id": self._facts.monthly_target_work_account_id, - "actual_account_id": self._facts.monthly_actual_work_account_id, - }, - ) - .mappings() - .all() - ) - - return tuple(_TimeOfficeMonthlyWorkAccountRow.model_validate(row) for row in raw_rows) - - def _validate_unique_employee_rows( - self, - rows: tuple[_TimeOfficeMonthlyWorkAccountRow, ...], - ) -> None: - seen_employee_ids: set[int] = set() - - for row in rows: - if row.employee_id in seen_employee_ids: - raise ValueError( - "Duplicate TimeOffice monthly work target account row for " - f"employee_id={row.employee_id} month={row.month}." - ) - - seen_employee_ids.add(row.employee_id) - - def _map_accounts( - self, - rows: tuple[_TimeOfficeMonthlyWorkAccountRow, ...], - ) -> tuple[MonthlyWorkAccount, ...]: - accounts: list[MonthlyWorkAccount] = [] - - for row in rows: - target_minutes = self._hours_to_minutes(row.target_hours) - - if target_minutes <= 0: - continue - - actual_minutes = self._hours_to_minutes(row.actual_hours) - - accounts.append( - MonthlyWorkAccount( - employee_id=row.employee_id, - target_minutes=target_minutes, - actual_minutes=actual_minutes if actual_minutes > 0 else None, - ) - ) - - return tuple(sorted(accounts, key=lambda account: account.employee_id)) - - def _timeoffice_month(self, planning_month: PlanningMonth) -> int: - return planning_month.year * 100 + planning_month.month - - def _hours_to_minutes(self, value: float | None) -> int: - if value is None: - return 0 - - return round(value * 60) diff --git a/src/scheduling/timeoffice/repositories/personnel.py b/src/scheduling/timeoffice/repositories/personnel.py deleted file mode 100644 index 728ff894..00000000 --- a/src/scheduling/timeoffice/repositories/personnel.py +++ /dev/null @@ -1,297 +0,0 @@ -from datetime import datetime -from typing import Self - -from pydantic import model_validator -from sqlalchemy import Connection, bindparam, text - -from scheduling.domain import ( - Capability, - Employee, - Plan, - PlanningMonth, - PlanningUnitMembership, - PlanParticipant, - SchedulingBaseModel, - StaffLevel, -) -from scheduling.timeoffice.facts import TimeOfficeFacts -from scheduling.timeoffice.repositories.types import CleanNullableText, SourceInt, TimeOfficeSourceRow - - -class _TimeOfficePlanPersonnelRow(TimeOfficeSourceRow): - plan_id: SourceInt - planning_unit_id: SourceInt - employee_id: SourceInt - employee_profession_id: SourceInt - first_name: CleanNullableText = None - last_name: CleanNullableText = None - - -class _TimeOfficePlanningUnitMembershipRow(TimeOfficeSourceRow): - planning_unit_id: SourceInt - employee_id: SourceInt - membership_profession_id: SourceInt - valid_from: datetime - valid_until: datetime | None = None - is_home: bool - is_replacement: bool - - @model_validator(mode="after") - def validate_interval(self) -> Self: - if self.valid_until is not None and self.valid_until < self.valid_from: - raise ValueError( - "Invalid TimeOffice planning-unit membership interval: " - f"planning_unit_id={self.planning_unit_id} " - f"employee_id={self.employee_id} " - f"valid_from={self.valid_from!r} " - f"valid_until={self.valid_until!r}." - ) - - return self - - -class PersonnelRepositoryResult(SchedulingBaseModel): - employees: tuple[Employee, ...] - plan_participants: tuple[PlanParticipant, ...] - planning_unit_memberships: tuple[PlanningUnitMembership, ...] - - -class TimeOfficePersonnelRepository: - """Reads selected plan participants and active planning-unit memberships.""" - - def __init__(self, *, facts: TimeOfficeFacts) -> None: - self._facts = facts - - def fetch( - self, - *, - connection: Connection, - plans: tuple[Plan, ...], - planning_unit_ids: tuple[int, ...], - planning_month: PlanningMonth, - ) -> PersonnelRepositoryResult: - if not plans: - return PersonnelRepositoryResult( - employees=(), - plan_participants=(), - planning_unit_memberships=(), - ) - - plan_personnel_rows = self._fetch_plan_personnel_rows( - connection=connection, - plans=plans, - ) - - employees = self._deduplicate_employees(tuple(self._map_employee(row) for row in plan_personnel_rows)) - - plan_participants = self._map_plan_participants(plan_personnel_rows) - - membership_rows = self._fetch_membership_rows( - connection=connection, - planning_unit_ids=planning_unit_ids, - employee_ids=tuple(employee.employee_id for employee in employees), - planning_month=planning_month, - ) - - return PersonnelRepositoryResult( - employees=employees, - plan_participants=plan_participants, - planning_unit_memberships=self._map_memberships(membership_rows), - ) - - def _fetch_plan_personnel_rows( - self, - *, - connection: Connection, - plans: tuple[Plan, ...], - ) -> tuple[_TimeOfficePlanPersonnelRow, ...]: - query = text( - """ - SELECT DISTINCT - pp.RefPlan AS plan_id, - p.RefPlanungseinheiten AS planning_unit_id, - pp.RefPersonal AS employee_id, - per.RefBerufe AS employee_profession_id, - per.Vorname AS first_name, - per.Name AS last_name - FROM TPlanPersonal pp - JOIN TPlan p - ON p.Prim = pp.RefPlan - JOIN TPersonal per - ON per.Prim = pp.RefPersonal - WHERE pp.RefPlan IN :plan_ids - ORDER BY - p.RefPlanungseinheiten, - per.Name, - per.Vorname, - pp.RefPersonal - """ - ).bindparams(bindparam("plan_ids", expanding=True)) - - raw_rows = ( - connection.execute( - query, - {"plan_ids": tuple(plan.plan_id for plan in plans)}, - ) - .mappings() - .all() - ) - - return tuple(_TimeOfficePlanPersonnelRow.model_validate(row) for row in raw_rows) - - def _fetch_membership_rows( - self, - *, - connection: Connection, - planning_unit_ids: tuple[int, ...], - employee_ids: tuple[int, ...], - planning_month: PlanningMonth, - ) -> tuple[_TimeOfficePlanningUnitMembershipRow, ...]: - if not planning_unit_ids or not employee_ids: - return () - - query = text( - """ - SELECT DISTINCT - pep.RefPlanungseinheiten AS planning_unit_id, - pep.RefPersonal AS employee_id, - pep.RefBerufe AS membership_profession_id, - pep.VonDat AS valid_from, - pep.BisDat AS valid_until, - pep.IstHeimat AS is_home, - pep.IstVonErsatz AS is_replacement - FROM TPlanungseinheitenPersonal pep - WHERE pep.RefPlanungseinheiten IN :planning_unit_ids - AND pep.RefPersonal IN :employee_ids - AND CONVERT(date, pep.VonDat) <= :end - AND ( - pep.BisDat IS NULL - OR CONVERT(date, pep.BisDat) >= :start - ) - AND ISNULL(pep.KeinEPlan, 0) = 0 - ORDER BY - planning_unit_id, - employee_id, - valid_from, - valid_until - """ - ).bindparams( - bindparam("planning_unit_ids", expanding=True), - bindparam("employee_ids", expanding=True), - ) - - raw_rows = ( - connection.execute( - query, - { - "planning_unit_ids": planning_unit_ids, - "employee_ids": employee_ids, - "start": planning_month.start, - "end": planning_month.end, - }, - ) - .mappings() - .all() - ) - - return tuple(_TimeOfficePlanningUnitMembershipRow.model_validate(row) for row in raw_rows) - - def _map_employee(self, row: _TimeOfficePlanPersonnelRow) -> Employee: - return Employee( - employee_id=row.employee_id, - display_name=self._display_name( - employee_id=row.employee_id, - first_name=row.first_name, - last_name=row.last_name, - ), - staff_level=self._staff_level_from_profession( - row.employee_profession_id, - context=f"TPersonal employee_id={row.employee_id}", - ), - capabilities=self._capabilities_for_employee(row.employee_id), - ) - - def _deduplicate_employees(self, employees: tuple[Employee, ...]) -> tuple[Employee, ...]: - employees_by_id: dict[int, Employee] = {} - - for employee in employees: - existing = employees_by_id.get(employee.employee_id) - - if existing is not None: - if existing != employee: - raise ValueError( - "Conflicting duplicate employee rows from TPlanPersonal/TPersonal: " - f"employee_id={employee.employee_id} " - f"existing={existing!r} new={employee!r}." - ) - - continue - - employees_by_id[employee.employee_id] = employee - - return tuple( - sorted( - employees_by_id.values(), - key=lambda employee: employee.employee_id, - ) - ) - - def _map_plan_participants(self, rows: tuple[_TimeOfficePlanPersonnelRow, ...]) -> tuple[PlanParticipant, ...]: - return tuple( - PlanParticipant( - plan_id=row.plan_id, - planning_unit_id=row.planning_unit_id, - employee_id=row.employee_id, - ) - for row in rows - ) - - def _map_memberships( - self, rows: tuple[_TimeOfficePlanningUnitMembershipRow, ...] - ) -> tuple[PlanningUnitMembership, ...]: - return tuple( - PlanningUnitMembership( - planning_unit_id=row.planning_unit_id, - employee_id=row.employee_id, - valid_from=row.valid_from.date(), - valid_until=row.valid_until.date() if row.valid_until is not None else None, - staff_level=self._staff_level_from_profession( - row.membership_profession_id, - context=( - "TPlanungseinheitenPersonal " - f"planning_unit_id={row.planning_unit_id} " - f"employee_id={row.employee_id}" - ), - ), - is_home=row.is_home, - is_replacement=row.is_replacement, - ) - for row in rows - ) - - def _staff_level_from_profession(self, profession_id: int, *, context: str) -> StaffLevel: - staff_level = self._facts.staff_level_by_profession_id_map.get(profession_id) - - if staff_level is None: - raise ValueError( - f"No StaffLevel mapping configured for TimeOffice profession_id={profession_id} in {context}." - ) - - return staff_level - - def _capabilities_for_employee(self, employee_id: int) -> tuple[Capability, ...]: - return tuple(self._facts.capabilities_by_employee_id_map.get(employee_id, ())) - - def _display_name( - self, - *, - employee_id: int, - first_name: str | None, - last_name: str | None, - ) -> str: - display_name = " ".join(part for part in (last_name, first_name) if part) - - if display_name: - return display_name - - return f"Employee {employee_id}" diff --git a/src/scheduling/timeoffice/repositories/roster.py b/src/scheduling/timeoffice/repositories/roster.py deleted file mode 100644 index 025a2f22..00000000 --- a/src/scheduling/timeoffice/repositories/roster.py +++ /dev/null @@ -1,384 +0,0 @@ -from datetime import date, datetime -from typing import Self - -from pydantic import model_validator -from sqlalchemy import Connection, bindparam, text - -from scheduling.domain import ( - Assignment, - AssignmentType, - Availability, - AvailabilityType, - Employee, - Plan, - PlanningMonth, - SchedulingBaseModel, -) -from scheduling.timeoffice.facts import TimeOfficeFacts, TimeOfficeShiftFact -from scheduling.timeoffice.repositories.types import ( - CleanNullableText, - SourceInt, - SourceNullableInt, - TimeOfficeSourceRow, -) - - -class _TimeOfficeRosterRow(TimeOfficeSourceRow): - plan_id: SourceNullableInt = None - employee_id: SourceInt - roster_date: datetime - - work_shift_id: SourceNullableInt = None - work_shift_code: CleanNullableText = None - - global_absence_shift_id: SourceNullableInt = None - absence_shift_id: SourceNullableInt = None - resolved_absence_shift_id: SourceNullableInt = None - resolved_absence_code: CleanNullableText = None - - planning_unit_id: SourceNullableInt = None - - @model_validator(mode="after") - def validate_row_kind(self) -> Self: - has_work_shift = self.work_shift_id is not None - has_absence = self.global_absence_shift_id is not None or self.absence_shift_id is not None - - if not has_work_shift and not has_absence: - raise ValueError( - "Invalid TimeOffice roster row: neither work shift nor absence is set " - f"for employee_id={self.employee_id}, roster_date={self.roster_date}." - ) - - return self - - @model_validator(mode="after") - def validate_absence_references(self) -> Self: - if ( - self.global_absence_shift_id is not None - and self.absence_shift_id is not None - and self.global_absence_shift_id != self.absence_shift_id - ): - raise ValueError( - "Conflicting TimeOffice absence references in " - "TPlanPersonalKommtGeht: " - f"RefgAbw={self.global_absence_shift_id} " - f"RefDienstAbw={self.absence_shift_id}." - ) - - return self - - -class RosterRepositoryResult(SchedulingBaseModel): - assignments: tuple[Assignment, ...] - availability: tuple[Availability, ...] - - -class TimeOfficeRosterRepository: - """Reads hard roster facts from TimeOffice TPlanPersonalKommtGeht. - - This repository emits: - - work rows as Assignment - - absence rows as Availability - - Wishes/preferences are handled by TimeOfficeWishRepository. This repository - intentionally keeps the existing roster import behavior and does not change - semantics around Wunschdienst rows in this refactor. - """ - - def __init__(self, *, facts: TimeOfficeFacts) -> None: - self._facts = facts - - def fetch( - self, - *, - connection: Connection, - plans: tuple[Plan, ...], - employees: tuple[Employee, ...], - planning_month: PlanningMonth, - ) -> RosterRepositoryResult: - if not plans or not employees: - return RosterRepositoryResult(assignments=(), availability=()) - - rows = self._fetch_rows( - connection=connection, - employees=employees, - planning_month=planning_month, - ) - - selected_plan_ids = {plan.plan_id for plan in plans} - selected_planning_unit_ids = {plan.planning_unit_id for plan in plans} - - return RosterRepositoryResult( - assignments=self._map_assignments( - rows=rows, - selected_plan_ids=selected_plan_ids, - selected_planning_unit_ids=selected_planning_unit_ids, - ), - availability=self._map_availability(rows=rows), - ) - - def _fetch_rows( - self, - *, - connection: Connection, - employees: tuple[Employee, ...], - planning_month: PlanningMonth, - ) -> tuple[_TimeOfficeRosterRow, ...]: - query = text( - """ - SELECT - pkg.RefPlan AS plan_id, - pkg.RefPersonal AS employee_id, - pkg.Datum AS roster_date, - - pkg.RefDienste AS work_shift_id, - work_d.KurzBez AS work_shift_code, - - pkg.RefgAbw AS global_absence_shift_id, - pkg.RefDienstAbw AS absence_shift_id, - - COALESCE(pkg.RefgAbw, pkg.RefDienstAbw) AS resolved_absence_shift_id, - COALESCE(global_absence_d.KurzBez, absence_d.KurzBez) AS resolved_absence_code, - - pkg.RefPlanungseinheiten AS planning_unit_id - FROM TPlanPersonalKommtGeht pkg - LEFT JOIN TDienste work_d - ON work_d.Prim = pkg.RefDienste - LEFT JOIN TDienste global_absence_d - ON global_absence_d.Prim = pkg.RefgAbw - LEFT JOIN TDienste absence_d - ON absence_d.Prim = pkg.RefDienstAbw - WHERE pkg.RefPersonal IN :employee_ids - AND CONVERT(date, pkg.Datum) BETWEEN :start AND :end - AND ( - pkg.RefDienste IS NOT NULL - OR pkg.RefgAbw IS NOT NULL - OR pkg.RefDienstAbw IS NOT NULL - ) - ORDER BY - pkg.RefPersonal, - pkg.Datum, - pkg.RefPlan, - pkg.lfdNr - """ - ).bindparams(bindparam("employee_ids", expanding=True)) - - raw_rows = ( - connection.execute( - query, - { - "employee_ids": tuple(employee.employee_id for employee in employees), - "start": planning_month.start, - "end": planning_month.end, - }, - ) - .mappings() - .all() - ) - - return tuple(_TimeOfficeRosterRow.model_validate(row) for row in raw_rows) - - def _map_assignments( - self, - *, - rows: tuple[_TimeOfficeRosterRow, ...], - selected_plan_ids: set[int], - selected_planning_unit_ids: set[int], - ) -> tuple[Assignment, ...]: - assignments: list[Assignment] = [] - unmapped_shift_ids: dict[int, int] = {} - - for row in rows: - if row.work_shift_id is None: - continue - - shift_fact = self._facts.shift_facts_by_id.get(row.work_shift_id) - - if shift_fact is None: - unmapped_shift_ids[row.work_shift_id] = unmapped_shift_ids.get(row.work_shift_id, 0) + 1 - continue - - assignments.append( - self._map_assignment( - row=row, - shift_fact=shift_fact, - selected_plan_ids=selected_plan_ids, - selected_planning_unit_ids=selected_planning_unit_ids, - ) - ) - - if unmapped_shift_ids: - details = ", ".join(f"{shift_id} count={count}" for shift_id, count in sorted(unmapped_shift_ids.items())) - raise ValueError( - "Unmapped TimeOffice work shift ids found in " - "TPlanPersonalKommtGeht. Add them to " - "TIMEOFFICE_FACTS.shift_facts_by_id or explicitly decide " - f"to exclude them. Details: {details}." - ) - - return self._deduplicate_assignments(tuple(assignments)) - - def _map_assignment( - self, - *, - row: _TimeOfficeRosterRow, - shift_fact: TimeOfficeShiftFact, - selected_plan_ids: set[int], - selected_planning_unit_ids: set[int], - ) -> Assignment: - if row.work_shift_id is None: - raise ValueError( - "Cannot map TimeOffice assignment without work_shift_id: " - f"employee_id={row.employee_id} roster_date={row.roster_date}." - ) - - self._validate_work_shift_code(row=row, fact=shift_fact) - - assignment_type = self._assignment_type( - plan_id=row.plan_id, - planning_unit_id=row.planning_unit_id, - selected_plan_ids=selected_plan_ids, - selected_planning_unit_ids=selected_planning_unit_ids, - ) - - return Assignment( - employee_id=row.employee_id, - date=row.roster_date.date(), - shift_id=row.work_shift_id, - assignment_type=assignment_type, - planning_unit_id=(row.planning_unit_id if assignment_type == AssignmentType.PLANNED else None), - ) - - def _deduplicate_assignments(self, assignments: tuple[Assignment, ...]) -> tuple[Assignment, ...]: - assignments_by_key: dict[ - tuple[int, date, int, AssignmentType, int | None], - Assignment, - ] = {} - - for assignment in assignments: - key = ( - assignment.employee_id, - assignment.date, - assignment.shift_id, - assignment.assignment_type, - assignment.planning_unit_id, - ) - assignments_by_key.setdefault(key, assignment) - - return tuple( - assignments_by_key[key] - for key in sorted( - assignments_by_key, - key=lambda item: ( - item[0], - item[1], - item[2], - item[3].value, - -1 if item[4] is None else item[4], - ), - ) - ) - - def _map_availability(self, *, rows: tuple[_TimeOfficeRosterRow, ...]) -> tuple[Availability, ...]: - availability: list[Availability] = [] - - for row in rows: - absence_shift_id = self._resolved_absence_shift_id(row) - - if absence_shift_id is None: - continue - - availability.append( - self._map_availability_row( - row=row, - absence_shift_id=absence_shift_id, - ) - ) - - return self._deduplicate_availability(tuple(availability)) - - def _map_availability_row(self, *, row: _TimeOfficeRosterRow, absence_shift_id: int) -> Availability: - if row.resolved_absence_code is None: - raise ValueError( - "Missing resolved absence code for TimeOffice roster row: " - f"absence_shift_id={absence_shift_id} " - f"employee_id={row.employee_id} " - f"roster_date={row.roster_date}." - ) - - availability_type = self._facts.availability_type_by_absence_code.get(row.resolved_absence_code) - - if availability_type is None: - raise ValueError( - "Unmapped TimeOffice absence code: " - f"absence_shift_id={absence_shift_id} " - f"absence_code={row.resolved_absence_code!r}." - ) - - return Availability( - employee_id=row.employee_id, - date=row.roster_date.date(), - availability_type=availability_type, - ) - - def _deduplicate_availability(self, availability: tuple[Availability, ...]) -> tuple[Availability, ...]: - availability_by_key: dict[ - tuple[int, date, AvailabilityType], - Availability, - ] = {} - - for item in availability: - key = ( - item.employee_id, - item.date, - item.availability_type, - ) - availability_by_key.setdefault(key, item) - - return tuple( - availability_by_key[key] - for key in sorted( - availability_by_key, - key=lambda item: ( - item[0], - item[1], - item[2].value, - ), - ) - ) - - def _assignment_type( - self, - *, - plan_id: int | None, - planning_unit_id: int | None, - selected_plan_ids: set[int], - selected_planning_unit_ids: set[int], - ) -> AssignmentType: - if plan_id in selected_plan_ids and planning_unit_id in selected_planning_unit_ids: - return AssignmentType.PLANNED - - return AssignmentType.EXTERNAL - - def _resolved_absence_shift_id(self, row: _TimeOfficeRosterRow) -> int | None: - if row.global_absence_shift_id is not None: - return row.global_absence_shift_id - - if row.absence_shift_id is not None: - return row.absence_shift_id - - return None - - def _validate_work_shift_code(self, *, row: _TimeOfficeRosterRow, fact: TimeOfficeShiftFact) -> None: - if row.work_shift_id is None: - raise ValueError("Cannot validate work shift code without work_shift_id.") - - if row.work_shift_code is None: - raise ValueError(f"Missing TDienste.KurzBez for TimeOffice work shift source_shift_id={row.work_shift_id}.") - - if row.work_shift_code != fact.expected_code: - raise ValueError( - "Unexpected TimeOffice work shift code: " - f"source_shift_id={row.work_shift_id} " - f"expected={fact.expected_code!r} actual={row.work_shift_code!r}." - ) diff --git a/src/scheduling/timeoffice/repositories/shifts.py b/src/scheduling/timeoffice/repositories/shifts.py deleted file mode 100644 index 6077eb3a..00000000 --- a/src/scheduling/timeoffice/repositories/shifts.py +++ /dev/null @@ -1,229 +0,0 @@ -from collections import defaultdict -from datetime import datetime -from typing import Self - -from pydantic import model_validator -from sqlalchemy import Connection, bindparam, text - -from scheduling.domain import SchedulingBaseModel, Shift -from scheduling.timeoffice.facts import TimeOfficeFacts, TimeOfficeShiftFact -from scheduling.timeoffice.repositories.types import CleanText, SourceInt, SourceNullableInt, TimeOfficeSourceRow - - -class _TimeOfficeShiftRow(TimeOfficeSourceRow): - shift_id: SourceInt - shift_code: CleanText - shift_type_id: SourceInt - - segment_start: datetime | None = None - segment_end: datetime | None = None - segment_minutes: SourceNullableInt = None - - @model_validator(mode="after") - def validate_segment_shape(self) -> Self: - has_start = self.segment_start is not None - has_end = self.segment_end is not None - - if has_start != has_end: - raise ValueError( - "Incomplete TimeOffice shift segment: " - f"shift_id={self.shift_id} " - f"segment_start={self.segment_start!r} " - f"segment_end={self.segment_end!r}." - ) - - if self.segment_start is not None and self.segment_end is not None: - if self.segment_end <= self.segment_start: - raise ValueError( - "Invalid TimeOffice shift segment: " - f"shift_id={self.shift_id} " - f"segment_start={self.segment_start!r} " - f"segment_end={self.segment_end!r}." - ) - - if self.segment_minutes is not None and self.segment_minutes < 0: - raise ValueError( - "Invalid negative TimeOffice shift segment minutes: " - f"shift_id={self.shift_id} " - f"segment_minutes={self.segment_minutes!r}." - ) - - return self - - -class ShiftRepositoryResult(SchedulingBaseModel): - shifts: tuple[Shift, ...] - - -class TimeOfficeShiftRepository: - """Reads TimeOffice shifts and maps them to reduced scheduling shifts.""" - - def __init__(self, *, facts: TimeOfficeFacts) -> None: - self._facts = facts - - def fetch(self, *, connection: Connection) -> ShiftRepositoryResult: - shift_ids = tuple(self._facts.shift_facts_by_id.keys()) - - if not shift_ids: - return ShiftRepositoryResult(shifts=()) - - rows = self._fetch_rows( - connection=connection, - shift_ids=shift_ids, - ) - - shifts = self._map_rows(rows) - - self._validate_requested_shifts( - requested_shift_ids=shift_ids, - shifts=shifts, - ) - - return ShiftRepositoryResult(shifts=shifts) - - def _fetch_rows( - self, - *, - connection: Connection, - shift_ids: tuple[int, ...], - ) -> tuple[_TimeOfficeShiftRow, ...]: - query = text( - """ - SELECT - d.Prim AS shift_id, - d.KurzBez AS shift_code, - d.RefDienstTypen AS shift_type_id, - - sz.Kommt AS segment_start, - sz.Geht AS segment_end, - sz.Minuten AS segment_minutes - FROM TDienste d - LEFT JOIN TDiensteSollzeiten sz - ON sz.RefDienste = d.Prim - WHERE d.Prim IN :shift_ids - ORDER BY - d.Prim, - sz.Kommt, - sz.Geht - """ - ).bindparams(bindparam("shift_ids", expanding=True)) - - raw_rows = ( - connection.execute( - query, - {"shift_ids": shift_ids}, - ) - .mappings() - .all() - ) - - return tuple(_TimeOfficeShiftRow.model_validate(row) for row in raw_rows) - - def _map_rows(self, rows: tuple[_TimeOfficeShiftRow, ...]) -> tuple[Shift, ...]: - rows_by_shift_id = self._group_rows_by_shift_id(rows) - - return tuple( - self._map_shift( - shift_id=shift_id, - rows=shift_rows, - shift_fact=self._facts.shift_facts_by_id[shift_id], - ) - for shift_id, shift_rows in sorted(rows_by_shift_id.items()) - ) - - def _group_rows_by_shift_id(self, rows: tuple[_TimeOfficeShiftRow, ...]) -> dict[int, list[_TimeOfficeShiftRow]]: - rows_by_shift_id: dict[int, list[_TimeOfficeShiftRow]] = defaultdict(list) - - for row in rows: - rows_by_shift_id[row.shift_id].append(row) - - return dict(rows_by_shift_id) - - def _map_shift( - self, - *, - shift_id: int, - rows: list[_TimeOfficeShiftRow], - shift_fact: TimeOfficeShiftFact, - ) -> Shift: - first_row = rows[0] - - source_code = first_row.shift_code - - if self._normalize_shift_code(source_code) != self._normalize_shift_code(shift_fact.expected_code): - raise ValueError( - "Unexpected TimeOffice shift code for known scheduling shift: " - f"shift_id={shift_id} expected={shift_fact.expected_code!r} actual={source_code!r}." - ) - - if first_row.shift_type_id not in self._facts.work_shift_type_ids: - raise ValueError( - "Known scheduling shift is not configured as real work shift in TimeOffice: " - f"shift_id={shift_id} shift_type_id={first_row.shift_type_id}." - ) - - segments = self._map_segments(rows=rows, shift_id=shift_id) - - start_at = segments[0][0] - end_at = segments[-1][1] - - return Shift( - shift_id=shift_id, - code=source_code, - kind=shift_fact.kind, - staffing_role=shift_fact.staffing_role, - start_minute=self._minute_of_day(start_at), - end_minute=self._minute_of_day(end_at), - net_work_minutes=self._net_work_minutes(segments), - ) - - def _map_segments( - self, *, rows: list[_TimeOfficeShiftRow], shift_id: int - ) -> tuple[tuple[datetime, datetime, int], ...]: - segments: list[tuple[datetime, datetime, int]] = [] - - for row in rows: - if row.segment_start is None and row.segment_end is None: - continue - - if row.segment_start is None or row.segment_end is None: - raise ValueError( - "Invalid TimeOffice shift row after source-row validation: " - f"shift_id={shift_id} " - f"segment_start={row.segment_start!r} " - f"segment_end={row.segment_end!r}." - ) - - segments.append( - ( - row.segment_start, - row.segment_end, - row.segment_minutes or 0, - ) - ) - - if not segments: - raise ValueError(f"No timing segments found for TimeOffice shift_id={shift_id}.") - - return tuple(segments) - - def _net_work_minutes(self, segments: tuple[tuple[datetime, datetime, int], ...]) -> int: - source_minutes = sum(segment_minutes for _, _, segment_minutes in segments) - - if source_minutes > 0: - return source_minutes - - return sum(int((end_at - start_at).total_seconds() // 60) for start_at, end_at, _ in segments) - - def _validate_requested_shifts(self, *, requested_shift_ids: tuple[int, ...], shifts: tuple[Shift, ...]) -> None: - returned_shift_ids = {shift.shift_id for shift in shifts} - missing_shift_ids = sorted(set(requested_shift_ids) - returned_shift_ids) - - if missing_shift_ids: - raise ValueError(f"Missing TimeOffice shift definitions for shift_ids={missing_shift_ids}.") - - def _minute_of_day(self, value: datetime) -> int: - return value.hour * 60 + value.minute - - def _normalize_shift_code(self, value: str) -> str: - return value.strip().casefold() diff --git a/src/scheduling/timeoffice/repositories/sunday_work_history.py b/src/scheduling/timeoffice/repositories/sunday_work_history.py deleted file mode 100644 index 1dc5ba6d..00000000 --- a/src/scheduling/timeoffice/repositories/sunday_work_history.py +++ /dev/null @@ -1,154 +0,0 @@ -from datetime import date - -from sqlalchemy import Connection, bindparam, text - -from scheduling.domain import Employee, EmployeeSundayWorkHistory, PlanningMonth, SchedulingBaseModel -from scheduling.timeoffice.repositories.types import CleanNullableText, SourceInt, TimeOfficeSourceRow - - -class _TimeOfficeSundayAccountRow(TimeOfficeSourceRow): - account_id: SourceInt - account_code: CleanNullableText = None - account_name: CleanNullableText = None - is_daily_account: bool | None = None - - -class _TimeOfficeSundayHistoryRow(TimeOfficeSourceRow): - employee_id: SourceInt - worked_sundays: SourceInt - - -class SundayWorkHistoryRepositoryResult(SchedulingBaseModel): - sunday_work_history: tuple[EmployeeSundayWorkHistory, ...] - - -class TimeOfficeSundayWorkHistoryRepository: - """Reads historical worked-Sunday counts from TimeOffice daily account rows. - - Source: - - TKonten identifies the Sunday account via BezProg = 'SONNTAG' - - TPersonalKontenJeTag contains daily account rows - - This repository intentionally does not use TimeOfficeFacts because the - account identity is inferable from the database. - """ - - LOOKBACK_YEARS = 1 - SUNDAY_ACCOUNT_CODE = "SONNTAG" - - def fetch( - self, - *, - connection: Connection, - planning_month: PlanningMonth, - employees: tuple[Employee, ...], - ) -> SundayWorkHistoryRepositoryResult: - if not employees: - return SundayWorkHistoryRepositoryResult(sunday_work_history=()) - - sunday_account_id = self._fetch_sunday_account_id(connection) - - rows = self._fetch_history_rows( - connection=connection, - employees=employees, - sunday_account_id=sunday_account_id, - lookback_start=self._subtract_years(planning_month.end, self.LOOKBACK_YEARS), - lookback_end=planning_month.end, - ) - - return SundayWorkHistoryRepositoryResult(sunday_work_history=self._map_history_rows(rows)) - - def _fetch_sunday_account_id(self, connection: Connection) -> int: - query = text(""" - SELECT - Prim AS account_id, - BezProg AS account_code, - Bez AS account_name, - IstTagesKonto AS is_daily_account - FROM TKonten - WHERE BezProg = :sunday_account_code - """) - - rows = tuple( - _TimeOfficeSundayAccountRow.model_validate(row) - for row in connection.execute(query, {"sunday_account_code": self.SUNDAY_ACCOUNT_CODE}).mappings().all() - ) - - if len(rows) != 1: - raise ValueError( - "Expected exactly one TimeOffice Sunday account with " - f"BezProg={self.SUNDAY_ACCOUNT_CODE!r}, found {len(rows)}." - ) - - row = rows[0] - - if row.is_daily_account is not True: - raise ValueError( - "TimeOffice Sunday account must be a daily account: " - f"account_id={row.account_id} " - f"code={row.account_code!r} " - f"name={row.account_name!r}." - ) - - return row.account_id - - def _fetch_history_rows( - self, - *, - connection: Connection, - employees: tuple[Employee, ...], - sunday_account_id: int, - lookback_start: date, - lookback_end: date, - ) -> tuple[_TimeOfficeSundayHistoryRow, ...]: - query = text(""" - SELECT - p.Prim AS employee_id, - COUNT(DISTINCT CAST(pkt.Datum AS date)) AS worked_sundays - FROM TPersonal p - LEFT JOIN TPersonalKontenJeTag pkt - ON pkt.RefPersonal = p.Prim - AND pkt.RefKonten = :sunday_account_id - AND CAST(pkt.Datum AS date) BETWEEN :lookback_start AND :lookback_end - AND DATEDIFF( - day, - CONVERT(date, '1900-01-07', 23), - CAST(pkt.Datum AS date) - ) % 7 = 0 - AND ISNULL(pkt.Wert, 0) > 0 - WHERE p.Prim IN :employee_ids - GROUP BY p.Prim - ORDER BY p.Prim - """).bindparams(bindparam("employee_ids", expanding=True)) - - raw_rows = ( - connection.execute( - query, - { - "employee_ids": tuple(employee.employee_id for employee in employees), - "sunday_account_id": sunday_account_id, - "lookback_start": lookback_start, - "lookback_end": lookback_end, - }, - ) - .mappings() - .all() - ) - - return tuple(_TimeOfficeSundayHistoryRow.model_validate(row) for row in raw_rows) - - def _map_history_rows(self, rows: tuple[_TimeOfficeSundayHistoryRow, ...]) -> tuple[EmployeeSundayWorkHistory, ...]: - return tuple(self._map_history_row(row) for row in rows) - - def _map_history_row(self, row: _TimeOfficeSundayHistoryRow) -> EmployeeSundayWorkHistory: - return EmployeeSundayWorkHistory( - employee_id=row.employee_id, - worked_sundays=row.worked_sundays, - ) - - def _subtract_years(self, value: date, years: int) -> date: - try: - return value.replace(year=value.year - years) - except ValueError: - # Leap day fallback. - return value.replace(year=value.year - years, day=28) diff --git a/src/scheduling/timeoffice/repositories/wishes.py b/src/scheduling/timeoffice/repositories/wishes.py deleted file mode 100644 index 8b498030..00000000 --- a/src/scheduling/timeoffice/repositories/wishes.py +++ /dev/null @@ -1,315 +0,0 @@ -from datetime import date as Date -from datetime import datetime -from typing import Self - -from pydantic import model_validator -from sqlalchemy import Connection, bindparam, text - -from scheduling.domain import Employee, Plan, PlanningMonth, SchedulingBaseModel, Shift, Wish, WishKind -from scheduling.timeoffice.facts import TimeOfficeFacts, TimeOfficeShiftFact -from scheduling.timeoffice.repositories.types import ( - CleanNullableText, - SourceInt, - SourceNullableInt, - TimeOfficeSourceRow, -) - - -class _TimeOfficeWishRow(TimeOfficeSourceRow): - employee_id: SourceInt - wish_date: datetime - plan_id: SourceInt - planning_unit_id: SourceInt - - work_shift_id: SourceNullableInt = None - work_shift_code: CleanNullableText = None - work_shift_name: CleanNullableText = None - - global_absence_shift_id: SourceNullableInt = None - global_absence_shift_code: CleanNullableText = None - global_absence_shift_name: CleanNullableText = None - - absence_shift_id: SourceNullableInt = None - absence_shift_code: CleanNullableText = None - absence_shift_name: CleanNullableText = None - - resolved_absence_shift_id: SourceNullableInt = None - resolved_absence_code: CleanNullableText = None - resolved_absence_name: CleanNullableText = None - - @model_validator(mode="after") - def validate_row_kind(self) -> Self: - has_work_shift = self.work_shift_id is not None - has_absence = self.global_absence_shift_id is not None or self.absence_shift_id is not None - - if has_work_shift and has_absence: - raise ValueError( - "Ambiguous TimeOffice wish row: both work shift and absence are set " - f"for employee_id={self.employee_id}, wish_date={self.wish_date}." - ) - - if not has_work_shift and not has_absence: - raise ValueError( - "Invalid TimeOffice wish row: neither work shift nor absence is set " - f"for employee_id={self.employee_id}, wish_date={self.wish_date}." - ) - - return self - - @model_validator(mode="after") - def validate_absence_references(self) -> Self: - if ( - self.global_absence_shift_id is not None - and self.absence_shift_id is not None - and self.global_absence_shift_id != self.absence_shift_id - ): - raise ValueError( - "Conflicting TimeOffice wish absence references in " - "TPlanPersonalKommtGeht: " - f"RefgAbw={self.global_absence_shift_id} " - f"RefDienstAbw={self.absence_shift_id}." - ) - - return self - - -class TimeOfficeWishRepositoryResult(SchedulingBaseModel): - wishes: tuple[Wish, ...] - - -class TimeOfficeWishRepository: - """Reads employee wishes from TimeOffice TPlanPersonalKommtGeht. - - Source rule: - - only rows with Wunschdienst != 0 - - work shift rows become SHIFT wishes - - mapped absence rows become FREE_DAY wishes for now - - TPersonalAntraege is intentionally not used yet because source validation did - not show usable request workflow rows for the selected planning context. - """ - - def __init__(self, *, facts: TimeOfficeFacts) -> None: - self._facts = facts - - def fetch( - self, - *, - connection: Connection, - plans: tuple[Plan, ...], - employees: tuple[Employee, ...], - shifts: tuple[Shift, ...], - planning_month: PlanningMonth, - ) -> TimeOfficeWishRepositoryResult: - if not plans or not employees: - return TimeOfficeWishRepositoryResult(wishes=()) - - rows = self._fetch_rows( - connection=connection, - plans=plans, - employees=employees, - planning_month=planning_month, - ) - - wishes = self._map_wishes(rows=rows, shifts=shifts) - - return TimeOfficeWishRepositoryResult(wishes=self._deduplicate_wishes(wishes)) - - def _fetch_rows( - self, - *, - connection: Connection, - plans: tuple[Plan, ...], - employees: tuple[Employee, ...], - planning_month: PlanningMonth, - ) -> tuple[_TimeOfficeWishRow, ...]: - query = text( - """ - SELECT - pkg.RefPersonal AS employee_id, - pkg.Datum AS wish_date, - pkg.RefPlan AS plan_id, - pkg.RefPlanungseinheiten AS planning_unit_id, - - pkg.RefDienste AS work_shift_id, - work_d.KurzBez AS work_shift_code, - work_d.Bezeichnung AS work_shift_name, - - pkg.RefgAbw AS global_absence_shift_id, - global_absence_d.KurzBez AS global_absence_shift_code, - global_absence_d.Bezeichnung AS global_absence_shift_name, - - pkg.RefDienstAbw AS absence_shift_id, - absence_d.KurzBez AS absence_shift_code, - absence_d.Bezeichnung AS absence_shift_name, - - COALESCE(pkg.RefgAbw, pkg.RefDienstAbw) AS resolved_absence_shift_id, - COALESCE(global_absence_d.KurzBez, absence_d.KurzBez) AS resolved_absence_code, - COALESCE(global_absence_d.Bezeichnung, absence_d.Bezeichnung) AS resolved_absence_name - FROM TPlanPersonalKommtGeht pkg - LEFT JOIN TDienste work_d - ON work_d.Prim = pkg.RefDienste - LEFT JOIN TDienste global_absence_d - ON global_absence_d.Prim = pkg.RefgAbw - LEFT JOIN TDienste absence_d - ON absence_d.Prim = pkg.RefDienstAbw - WHERE pkg.RefPersonal IN :employee_ids - AND pkg.RefPlan IN :plan_ids - AND pkg.RefPlanungseinheiten IN :planning_unit_ids - AND CONVERT(date, pkg.Datum) BETWEEN :start AND :end - AND ISNULL(pkg.Wunschdienst, 0) <> 0 - AND ( - pkg.RefDienste IS NOT NULL - OR pkg.RefgAbw IS NOT NULL - OR pkg.RefDienstAbw IS NOT NULL - ) - ORDER BY - pkg.RefPersonal, - pkg.Datum, - pkg.RefPlan, - pkg.RefPlanungseinheiten, - pkg.RefDienste, - pkg.RefgAbw, - pkg.RefDienstAbw - """ - ).bindparams( - bindparam("employee_ids", expanding=True), - bindparam("plan_ids", expanding=True), - bindparam("planning_unit_ids", expanding=True), - ) - - raw_rows = tuple( - connection.execute( - query, - { - "plan_ids": tuple(plan.plan_id for plan in plans), - "planning_unit_ids": tuple(plan.planning_unit_id for plan in plans), - "employee_ids": tuple(employee.employee_id for employee in employees), - "start": planning_month.start, - "end": planning_month.end, - }, - ) - .mappings() - .all() - ) - - return tuple(_TimeOfficeWishRow.model_validate(row) for row in raw_rows) - - def _map_wishes(self, *, rows: tuple[_TimeOfficeWishRow, ...], shifts: tuple[Shift, ...]) -> tuple[Wish, ...]: - known_shift_ids = {shift.shift_id for shift in shifts} - - return tuple( - self._map_wish( - row=row, - known_shift_ids=known_shift_ids, - ) - for row in rows - ) - - def _map_wish(self, *, row: _TimeOfficeWishRow, known_shift_ids: set[int]) -> Wish: - if row.work_shift_id is not None: - return self._map_shift_wish(row=row, known_shift_ids=known_shift_ids) - - return self._map_absence_wish(row=row) - - def _map_shift_wish(self, *, row: _TimeOfficeWishRow, known_shift_ids: set[int]) -> Wish: - if row.work_shift_id is None: - raise ValueError("Cannot map shift wish without work_shift_id.") - - fact = self._facts.shift_facts_by_id.get(row.work_shift_id) - if fact is None or row.work_shift_id not in known_shift_ids: - raise ValueError( - "Unmapped TimeOffice wish work shift id found in " - "TPlanPersonalKommtGeht. Add it to TIMEOFFICE_FACTS.shift_facts_by_id " - f"or explicitly decide to exclude it. Details: shift_id={row.work_shift_id}." - ) - - self._validate_work_shift_code(row=row, fact=fact) - - return Wish( - employee_id=row.employee_id, - planning_unit_id=row.planning_unit_id, - date=row.wish_date.date(), - kind=WishKind.SHIFT, - shift_id=row.work_shift_id, - ) - - def _map_absence_wish(self, *, row: _TimeOfficeWishRow) -> Wish: - absence_shift_id = self._resolved_absence_shift_id(row) - - if row.resolved_absence_code is None: - raise ValueError( - "Missing resolved absence code for TimeOffice wish row: " - f"absence_shift_id={absence_shift_id} " - f"employee_id={row.employee_id} " - f"wish_date={row.wish_date}." - ) - - wish_kind = self._facts.wish_kind_by_absence_code.get(row.resolved_absence_code) - if wish_kind is None: - raise ValueError( - "Unmapped TimeOffice wish absence code: " - f"absence_shift_id={absence_shift_id} " - f"absence_code={row.resolved_absence_code!r} " - f"absence_name={row.resolved_absence_name!r}." - ) - - return Wish( - employee_id=row.employee_id, - planning_unit_id=row.planning_unit_id, - date=row.wish_date.date(), - kind=wish_kind, - ) - - def _validate_work_shift_code(self, *, row: _TimeOfficeWishRow, fact: TimeOfficeShiftFact) -> None: - if row.work_shift_code is None: - raise ValueError( - f"Missing TDienste.KurzBez for TimeOffice wish work shift source_shift_id={fact.source_shift_id}." - ) - - if row.work_shift_code != fact.expected_code: - raise ValueError( - "Unexpected TimeOffice wish work shift code: " - f"source_shift_id={fact.source_shift_id} " - f"expected={fact.expected_code!r} actual={row.work_shift_code!r}." - ) - - def _resolved_absence_shift_id(self, row: _TimeOfficeWishRow) -> int: - if row.global_absence_shift_id is not None: - return row.global_absence_shift_id - - if row.absence_shift_id is not None: - return row.absence_shift_id - - raise ValueError( - "Invalid TimeOffice wish row after source-row validation: " - f"missing absence shift id for employee_id={row.employee_id}, " - f"wish_date={row.wish_date}." - ) - - def _deduplicate_wishes(self, wishes: tuple[Wish, ...]) -> tuple[Wish, ...]: - wishes_by_key: dict[tuple[int, int, Date, WishKind, int | None], Wish] = {} - - for wish in wishes: - key = ( - wish.employee_id, - wish.planning_unit_id, - wish.date, - wish.kind, - wish.shift_id, - ) - wishes_by_key.setdefault(key, wish) - - return tuple( - wishes_by_key[key] - for key in sorted( - wishes_by_key, - key=lambda item: ( - item[0], - item[1], - item[2], - item[3], - item[4] or -1, - ), - ) - ) diff --git a/src/scheduling/timeoffice/service.py b/src/scheduling/timeoffice/service.py index e79994c7..9b99233d 100644 --- a/src/scheduling/timeoffice/service.py +++ b/src/scheduling/timeoffice/service.py @@ -1,64 +1,90 @@ import logging -from scheduling.domain import AssignmentType, PlanningMonth -from scheduling.solver.models import Solution, SolutionStatus -from scheduling.timeoffice.database import TimeOfficeDatabase +from sqlalchemy import Engine + +from scheduling.domain import PlanningMonth, SchedulingDataset +from scheduling.solver.models import Solution from scheduling.timeoffice.facts import TimeOfficeFacts -from scheduling.validation.dataset import ValidatedSchedulingDataset +from scheduling.timeoffice.mapping import map_scheduling_dataset +from scheduling.timeoffice.reading.container import TimeOfficeReaders +from scheduling.timeoffice.writing.solution import TimeOfficeSolutionWriter +from scheduling.validation import validate_scheduling_dataset logger = logging.getLogger(__name__) class TimeOfficeService: - """Application-facing service for loading scheduling data from TimeOffice.""" + """Application-facing service for TimeOffice reads and allowed writebacks.""" def __init__( self, *, facts: TimeOfficeFacts, - database: TimeOfficeDatabase, + engine: Engine, + readers: TimeOfficeReaders, + solution_writer: TimeOfficeSolutionWriter, ) -> None: self._facts = facts - self._database = database + self._engine = engine + self._readers = readers + self._solution_writer = solution_writer def fetch_dataset( self, *, planning_unit_ids: tuple[int, ...], planning_month: PlanningMonth, - ) -> ValidatedSchedulingDataset: + ) -> SchedulingDataset: selected_planning_unit_ids = self._normalize_planning_unit_ids(planning_unit_ids) logger.info( - "Fetching TimeOffice dataset: planning_units=%s planning_month=%s", - planning_unit_ids, + "Fetching TimeOffice sources: planning_units=%s planning_month=%s", + selected_planning_unit_ids, planning_month.label, ) - dataset = self._database.fetch_dataset( - selected_planning_unit_ids=selected_planning_unit_ids, - planning_month=planning_month, + + with self._engine.connect() as connection: + sources = self._readers.read_sources( + connection=connection, + selected_planning_unit_ids=selected_planning_unit_ids, + planning_month=planning_month, + ) + + dataset = map_scheduling_dataset( + sources=sources, + facts=self._facts, ) + + validated_dataset = validate_scheduling_dataset(dataset) + logger.info( "Fetched TimeOffice dataset: planning_units=%s plans=%s employees=%s " "memberships=%s shifts=%s assignments=%s availability=%s " - "minimum_staffing_requirements=%s wishes=%s", - len(dataset.planning_units), - len(dataset.plans), - len(dataset.employees), - len(dataset.planning_unit_memberships), - len(dataset.shifts), - len(dataset.assignments), - len(dataset.availability), - len(dataset.demand_requirements), - len(dataset.wishes), + "minimum_staffing_requirements=%s wishes=%s monthly_work_accounts=%s " + "source_plan_personnel_rows=%s", + len(validated_dataset.planning_units), + len(validated_dataset.plans), + len(validated_dataset.employees), + len(validated_dataset.planning_unit_memberships), + len(validated_dataset.shifts), + len(validated_dataset.assignments), + len(validated_dataset.availability), + len(validated_dataset.demand_requirements), + len(validated_dataset.wishes), + len(validated_dataset.monthly_work_accounts), + len(sources.plan_personnel_rows), ) - return dataset + + return validated_dataset + + def write_solution_dry_run(self, solution: Solution) -> None: + self._solution_writer.write_dry_run(solution) def _normalize_planning_unit_ids( self, planning_unit_ids: tuple[int, ...], ) -> tuple[int, ...]: - normalized = tuple(dict.fromkeys(int(value) for value in planning_unit_ids)) + normalized = tuple(dict.fromkeys(planning_unit_ids)) if not normalized: raise ValueError("At least one planning unit must be selected.") @@ -66,46 +92,12 @@ def _normalize_planning_unit_ids( unknown_ids = sorted( planning_unit_id for planning_unit_id in normalized - if planning_unit_id not in self._facts.planning_unit_kind_map + if planning_unit_id not in self._facts.planning_unit_kind_by_id ) if unknown_ids: + known_ids = sorted(self._facts.planning_unit_kind_by_id) raise ValueError( - "Unknown TimeOffice planning_unit_ids requested: " - f"{unknown_ids}. Add them to TIMEOFFICE_FACTS.planning_unit_kind_map " - "or fix the request." + f"Unknown TimeOffice planning_unit_ids requested: {unknown_ids}. Known planning_unit_ids={known_ids}." ) return normalized - - def write_solution_dry_run(self, solution: Solution) -> None: - """Log generated assignments that would later be written to TimeOffice.""" - if solution.status not in {SolutionStatus.OPTIMAL, SolutionStatus.FEASIBLE}: - logger.info( - "Skipping TimeOffice writeback dry-run because solution is not feasible: status=%s", - solution.status.value, - ) - return - - generated_assignments = [ - assignment for assignment in solution.assignments if assignment.assignment_type == AssignmentType.GENERATED - ] - - logger.info( - "Running TimeOffice writeback dry-run: generated_assignments=%s", - len(generated_assignments), - ) - - for assignment in generated_assignments: - logger.debug( - "Generated assignment for TimeOffice writeback dry-run: " - "employee_id=%s planning_unit_id=%s date=%s shift_id=%s", - assignment.employee_id, - assignment.planning_unit_id, - assignment.date.isoformat(), - assignment.shift_id, - ) - - logger.info( - "Finished TimeOffice writeback dry-run: generated_assignments=%s written=0", - len(generated_assignments), - ) diff --git a/src/scheduling/timeoffice/writing/__init__.py b/src/scheduling/timeoffice/writing/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/scheduling/timeoffice/writing/solution.py b/src/scheduling/timeoffice/writing/solution.py new file mode 100644 index 00000000..b756f7f3 --- /dev/null +++ b/src/scheduling/timeoffice/writing/solution.py @@ -0,0 +1,46 @@ +import logging + +from scheduling.domain import Assignment, AssignmentType +from scheduling.solver.models import Solution, SolutionStatus + +logger = logging.getLogger(__name__) + + +class TimeOfficeSolutionWriter: + """Allowed TimeOffice write surface for solver-generated assignments.""" + + def write_dry_run(self, solution: Solution) -> None: + if solution.status not in {SolutionStatus.OPTIMAL, SolutionStatus.FEASIBLE}: + logger.info( + "Skipping TimeOffice writeback dry-run because solution is not feasible: status=%s", + solution.status.value, + ) + return + + generated_assignments = _generated_assignments(solution) + + logger.info( + "Running TimeOffice writeback dry-run: generated_assignments=%s", + len(generated_assignments), + ) + + for assignment in generated_assignments: + logger.debug( + "Generated assignment for TimeOffice writeback dry-run: " + "employee_id=%s planning_unit_id=%s date=%s shift_id=%s", + assignment.employee_id, + assignment.planning_unit_id, + assignment.date.isoformat(), + assignment.shift_id, + ) + + logger.info( + "Finished TimeOffice writeback dry-run: generated_assignments=%s written=0", + len(generated_assignments), + ) + + +def _generated_assignments(solution: Solution) -> tuple[Assignment, ...]: + return tuple( + assignment for assignment in solution.assignments if assignment.assignment_type == AssignmentType.GENERATED + ) diff --git a/src/scheduling/validation/__init__.py b/src/scheduling/validation/__init__.py index e69de29b..5bd49536 100644 --- a/src/scheduling/validation/__init__.py +++ b/src/scheduling/validation/__init__.py @@ -0,0 +1,3 @@ +from scheduling.validation.dataset import validate_scheduling_dataset + +__all__ = ["validate_scheduling_dataset"] diff --git a/src/scheduling/validation/context.py b/src/scheduling/validation/context.py index 0cf70075..3094c40a 100644 --- a/src/scheduling/validation/context.py +++ b/src/scheduling/validation/context.py @@ -1,18 +1,8 @@ -from collections.abc import Mapping +from collections.abc import Iterable, Mapping from dataclasses import dataclass from types import MappingProxyType -from scheduling.domain import ( - EmployeeId, - Plan, - PlanId, - PlanningUnitId, - PlanningUnitKind, - SchedulingDataset, - Shift, - ShiftId, -) -from scheduling.validation.helpers import ensure_unique +from scheduling.domain import EmployeeId, PlanId, PlanningUnitId, PlanningUnitKind, SchedulingDataset, Shift, ShiftId @dataclass(frozen=True, slots=True) @@ -22,25 +12,24 @@ class DatasetValidationContext: plan_ids: frozenset[PlanId] shift_ids: frozenset[ShiftId] - plans_by_id: Mapping[PlanId, Plan] planning_unit_kind_by_id: Mapping[PlanningUnitId, PlanningUnitKind] shifts_by_id: Mapping[ShiftId, Shift] @classmethod def from_dataset(cls, dataset: SchedulingDataset) -> "DatasetValidationContext": - employee_ids = ensure_unique( + employee_ids = _ensure_unique( (employee.employee_id for employee in dataset.employees), "employee_id", ) - planning_unit_ids = ensure_unique( + planning_unit_ids = _ensure_unique( (unit.planning_unit_id for unit in dataset.planning_units), "planning_unit_id", ) - plan_ids = ensure_unique( + plan_ids = _ensure_unique( (plan.plan_id for plan in dataset.plans), "plan_id", ) - shift_ids = ensure_unique( + shift_ids = _ensure_unique( (shift.shift_id for shift in dataset.shifts), "shift_id", ) @@ -50,9 +39,24 @@ def from_dataset(cls, dataset: SchedulingDataset) -> "DatasetValidationContext": planning_unit_ids=planning_unit_ids, plan_ids=plan_ids, shift_ids=shift_ids, - plans_by_id=MappingProxyType({plan.plan_id: plan for plan in dataset.plans}), planning_unit_kind_by_id=MappingProxyType( {unit.planning_unit_id: unit.kind for unit in dataset.planning_units} ), shifts_by_id=MappingProxyType({shift.shift_id: shift for shift in dataset.shifts}), ) + + +def _ensure_unique[T](values: Iterable[T], field_name: str) -> frozenset[T]: + seen: set[T] = set() + duplicates: set[T] = set() + + for value in values: + if value in seen: + duplicates.add(value) + seen.add(value) + + if duplicates: + duplicate_values = ", ".join(sorted(str(value) for value in duplicates)) + raise ValueError(f"Duplicate {field_name} values: {duplicate_values}.") + + return frozenset(seen) diff --git a/src/scheduling/validation/dataset.py b/src/scheduling/validation/dataset.py index fd77f3ab..248fd070 100644 --- a/src/scheduling/validation/dataset.py +++ b/src/scheduling/validation/dataset.py @@ -1,6 +1,4 @@ -from typing import Self - -from pydantic import model_validator +from collections.abc import Callable from scheduling.domain import SchedulingDataset from scheduling.validation.context import DatasetValidationContext @@ -9,27 +7,32 @@ validate_availability, validate_demand_requirements, validate_monthly_work_accounts, - validate_plan_participants, validate_planning_unit_memberships, validate_plans, validate_sunday_work_history, validate_wishes, ) +type DatasetValidator = Callable[[SchedulingDataset, DatasetValidationContext], None] + + +_DATASET_VALIDATORS: tuple[DatasetValidator, ...] = ( + validate_plans, + validate_planning_unit_memberships, + validate_assignments, + validate_availability, + validate_demand_requirements, + validate_sunday_work_history, + validate_wishes, + validate_monthly_work_accounts, +) + -class ValidatedSchedulingDataset(SchedulingDataset): - @model_validator(mode="after") - def validate_cross_references(self) -> Self: - context = DatasetValidationContext.from_dataset(self) +def validate_scheduling_dataset(dataset: SchedulingDataset) -> SchedulingDataset: + """Validate cross-references and consistency of a canonical scheduling dataset.""" + context = DatasetValidationContext.from_dataset(dataset) - validate_plans(self, context) - validate_plan_participants(self, context) - validate_planning_unit_memberships(self, context) - validate_assignments(self, context) - validate_availability(self, context) - validate_demand_requirements(self, context) - validate_sunday_work_history(self, context) - validate_wishes(self, context) - validate_monthly_work_accounts(self, context) + for validate in _DATASET_VALIDATORS: + validate(dataset, context) - return self + return dataset diff --git a/src/scheduling/validation/helpers.py b/src/scheduling/validation/helpers.py deleted file mode 100644 index 1f39f81d..00000000 --- a/src/scheduling/validation/helpers.py +++ /dev/null @@ -1,17 +0,0 @@ -from collections.abc import Iterable - - -def ensure_unique[T](values: Iterable[T], field_name: str) -> frozenset[T]: - seen: set[T] = set() - duplicates: set[T] = set() - - for value in values: - if value in seen: - duplicates.add(value) - seen.add(value) - - if duplicates: - duplicate_values = ", ".join(sorted(str(value) for value in duplicates)) - raise ValueError(f"Duplicate {field_name} values: {duplicate_values}.") - - return frozenset(seen) diff --git a/src/scheduling/validation/validators.py b/src/scheduling/validation/validators.py index 02354479..87f3b0bf 100644 --- a/src/scheduling/validation/validators.py +++ b/src/scheduling/validation/validators.py @@ -4,7 +4,6 @@ AssignmentType, AvailabilityType, EmployeeId, - PlanId, PlanningMonth, PlanningUnitId, PlanningUnitKind, @@ -30,37 +29,6 @@ def validate_plans(dataset: SchedulingDataset, context: DatasetValidationContext seen_planning_units.add(plan.planning_unit_id) -def validate_plan_participants(dataset: SchedulingDataset, context: DatasetValidationContext) -> None: - seen: set[tuple[PlanId, EmployeeId]] = set() - - for participant in dataset.plan_participants: - if participant.plan_id not in context.plan_ids: - raise ValueError(f"PlanParticipant references unknown plan_id={participant.plan_id}.") - - if participant.planning_unit_id not in context.planning_unit_ids: - raise ValueError(f"PlanParticipant references unknown planning_unit_id={participant.planning_unit_id}.") - - if participant.employee_id not in context.employee_ids: - raise ValueError(f"PlanParticipant references unknown employee_id={participant.employee_id}.") - - plan = context.plans_by_id[participant.plan_id] - if participant.planning_unit_id != plan.planning_unit_id: - raise ValueError( - "PlanParticipant planning_unit_id does not match its Plan: " - f"plan_id={participant.plan_id} " - f"participant_planning_unit_id={participant.planning_unit_id} " - f"plan_planning_unit_id={plan.planning_unit_id}." - ) - - key = (participant.plan_id, participant.employee_id) - if key in seen: - raise ValueError( - f"Duplicate PlanParticipant plan_id={participant.plan_id} employee_id={participant.employee_id}." - ) - - seen.add(key) - - def validate_planning_unit_memberships(dataset: SchedulingDataset, context: DatasetValidationContext) -> None: seen: set[tuple[PlanningUnitId, EmployeeId, date, date | None]] = set() @@ -95,6 +63,12 @@ def validate_assignments(dataset: SchedulingDataset, context: DatasetValidationC seen: set[tuple[EmployeeId, date, ShiftId, AssignmentType, PlanningUnitId | None]] = set() for assignment in dataset.assignments: + if assignment.assignment_type == AssignmentType.GENERATED: + raise ValueError( + "SchedulingDataset must not contain generated assignments. " + "Generated assignments belong to Solution, not imported dataset facts." + ) + _validate_date_in_planning_month( planning_month=dataset.planning_month, value=assignment.date, @@ -108,17 +82,8 @@ def validate_assignments(dataset: SchedulingDataset, context: DatasetValidationC if assignment.shift_id not in context.shift_ids: raise ValueError(f"Assignment references unknown shift_id={assignment.shift_id}.") - # Assignment shape invariants belong in Assignment itself. - # Dataset validation only checks references. - if ( - assignment.assignment_type in {AssignmentType.PLANNED, AssignmentType.GENERATED} - and assignment.planning_unit_id is not None - and assignment.planning_unit_id not in context.planning_unit_ids - ): - raise ValueError( - f"{assignment.assignment_type.value} assignment references unknown " - f"planning_unit_id={assignment.planning_unit_id}." - ) + if assignment.planning_unit_id is not None and assignment.planning_unit_id not in context.planning_unit_ids: + raise ValueError(f"Assignment references unknown planning_unit_id={assignment.planning_unit_id}.") key = ( assignment.employee_id, @@ -152,8 +117,10 @@ def validate_availability(dataset: SchedulingDataset, context: DatasetValidation if availability.employee_id not in context.employee_ids: raise ValueError(f"Availability references unknown employee_id={availability.employee_id}.") + normalized_shift_ids = None if availability.shift_ids is not None: - unknown_shift_ids = sorted(set(availability.shift_ids) - context.shift_ids) + normalized_shift_ids = tuple(sorted(availability.shift_ids)) + unknown_shift_ids = sorted(set(normalized_shift_ids) - context.shift_ids) if unknown_shift_ids: raise ValueError(f"Availability references unknown shift_ids={unknown_shift_ids}.") @@ -161,7 +128,7 @@ def validate_availability(dataset: SchedulingDataset, context: DatasetValidation availability.employee_id, availability.date, availability.availability_type, - availability.shift_ids, + normalized_shift_ids, ) if key in seen: raise ValueError( @@ -294,9 +261,4 @@ def _validate_date_in_planning_month( if planning_month.start <= value <= planning_month.end: return - raise ValueError( - f"{label} outside planning month: " - f"{details} " - f"date={value} " - f"planning_month={planning_month.year:04d}-{planning_month.month:02d}." - ) + raise ValueError(f"{label} outside planning month: {details} date={value} planning_month={planning_month.label}.") From d76f2ba8715d3fb5c866559766b542ca573ffbc0 Mon Sep 17 00:00:00 2001 From: Tom Wawerek Date: Wed, 24 Jun 2026 00:47:43 +0200 Subject: [PATCH 12/18] refactor: make solver solvable --- src/SELECT.sql | 43 +++-- src/scheduling/domain/__init__.py | 12 +- src/scheduling/domain/planning_unit.py | 6 +- src/scheduling/domain/shift.py | 6 +- src/scheduling/domain/wish.py | 16 +- .../cp_sat/constraints/employee_daily.py | 18 -- .../cp_sat/constraints/minimum_staffing.py | 21 ++- src/scheduling/solver/cp_sat/context.py | 25 ++- src/scheduling/solver/cp_sat/eligibility.py | 89 ++++++---- src/scheduling/solver/cp_sat/index.py | 57 ++++--- src/scheduling/solver/cp_sat/inspection.py | 78 +++++++++ .../balance_generated_assignments.py | 29 ++-- src/scheduling/solver/cp_sat/variables.py | 109 +++++++----- src/scheduling/solver/service.py | 56 +++++-- src/scheduling/timeoffice/facts.py | 51 +++--- src/scheduling/timeoffice/mapping/dataset.py | 1 + src/scheduling/timeoffice/mapping/demand.py | 4 +- src/scheduling/timeoffice/mapping/planning.py | 14 +- src/scheduling/timeoffice/mapping/shifts.py | 2 +- src/scheduling/timeoffice/mapping/wishes.py | 155 ++++++++++++++---- src/scheduling/timeoffice/reading/roster.py | 2 +- src/scheduling/timeoffice/reading/wishes.py | 2 +- src/scheduling/timeoffice/service.py | 4 +- src/scheduling/validation/context.py | 8 +- src/scheduling/validation/validators.py | 12 +- 25 files changed, 545 insertions(+), 275 deletions(-) delete mode 100644 src/scheduling/solver/cp_sat/constraints/employee_daily.py create mode 100644 src/scheduling/solver/cp_sat/inspection.py diff --git a/src/SELECT.sql b/src/SELECT.sql index 79db3b7a..415cbc98 100644 --- a/src/SELECT.sql +++ b/src/SELECT.sql @@ -1,24 +1,23 @@ SELECT - pkg.RefPlan AS plan_id, - p.RefPlanungseinheiten AS plan_planning_unit_id, - pkg.RefPlanungseinheiten AS row_planning_unit_id, - pkg.RefPersonal AS employee_id, - pkg.Datum AS roster_date, - pkg.RefDienste AS work_shift_id, - d.KurzBez AS work_shift_code, - d.Bezeichnung AS work_shift_name, - pkg.RefgAbw AS global_absence_shift_id, - pkg.RefDienstAbw AS absence_shift_id, - pkg.lfdNr AS line_number -FROM TPlanPersonalKommtGeht pkg -LEFT JOIN TPlan p - ON p.Prim = pkg.RefPlan -LEFT JOIN TDienste d - ON d.Prim = pkg.RefDienste -WHERE pkg.RefPersonal = 803 - AND CONVERT(date, pkg.Datum) = '2024-11-04' + pep.RefPlanungseinheiten AS planning_unit_id, + b.KurzBez AS profession_code, + b.Bezeichnung AS profession_name, + COUNT(DISTINCT pep.RefPersonal) AS employee_count, + STRING_AGG(CONVERT(varchar(20), pep.RefPersonal), ', ') AS employee_ids +FROM TPlanungseinheitenPersonal pep +JOIN TBerufe b + ON b.Prim = pep.RefBerufe +WHERE pep.RefPlanungseinheiten IN (77, 78) + AND CONVERT(date, pep.VonDat) <= '2024-11-30' + AND ( + pep.BisDat IS NULL + OR CONVERT(date, pep.BisDat) >= '2024-11-01' + ) + AND ISNULL(pep.KeinEPlan, 0) = 0 +GROUP BY + pep.RefPlanungseinheiten, + b.KurzBez, + b.Bezeichnung ORDER BY - pkg.RefPlan, - pkg.RefPlanungseinheiten, - pkg.RefDienste, - pkg.lfdNr; + pep.RefPlanungseinheiten, + b.KurzBez; diff --git a/src/scheduling/domain/__init__.py b/src/scheduling/domain/__init__.py index e1c07f31..a22e1b50 100644 --- a/src/scheduling/domain/__init__.py +++ b/src/scheduling/domain/__init__.py @@ -6,10 +6,10 @@ from scheduling.domain.employee import Capability, Employee, EmployeeId, StaffLevel from scheduling.domain.monthly_work_account import MonthlyWorkAccount from scheduling.domain.plan import Plan, PlanId -from scheduling.domain.planning_unit import PlanningUnit, PlanningUnitId, PlanningUnitKind, PlanningUnitMembership -from scheduling.domain.shift import Shift, ShiftId, ShiftKind, StaffingDemandRole +from scheduling.domain.planning_unit import PlanningUnit, PlanningUnitId, PlanningUnitMembership, PlanningUnitType +from scheduling.domain.shift import Shift, ShiftId, ShiftType, StaffingDemandRole from scheduling.domain.sunday_work_history import EmployeeSundayWorkHistory -from scheduling.domain.wish import Wish, WishKind +from scheduling.domain.wish import Wish, WishType __all__ = [ "PositiveId", @@ -23,7 +23,7 @@ "PlanId", "PlanningUnit", "PlanningUnitId", - "PlanningUnitKind", + "PlanningUnitType", "PlanningUnitMembership", "EmployeeId", "Employee", @@ -35,11 +35,11 @@ "AvailabilityType", "Shift", "ShiftId", - "ShiftKind", + "ShiftType", "StaffingDemandRole", "DemandRequirement", "EmployeeSundayWorkHistory", "Wish", - "WishKind", + "WishType", "MonthlyWorkAccount", ] diff --git a/src/scheduling/domain/planning_unit.py b/src/scheduling/domain/planning_unit.py index a5efa729..ec6686ce 100644 --- a/src/scheduling/domain/planning_unit.py +++ b/src/scheduling/domain/planning_unit.py @@ -10,8 +10,8 @@ PlanningUnitId = PositiveId -class PlanningUnitKind(StrEnum): - """Kind of planning unit used by the scheduling pipeline. +class PlanningUnitType(StrEnum): + """Type of planning unit used by the scheduling pipeline. STATION: Planning unit with staffing demand. The solver may assign employees @@ -37,7 +37,7 @@ class PlanningUnit(SchedulingBaseModel): planning_unit_id: PlanningUnitId display_name: NonEmptyStr - kind: PlanningUnitKind + type: PlanningUnitType class PlanningUnitMembership(SchedulingBaseModel): diff --git a/src/scheduling/domain/shift.py b/src/scheduling/domain/shift.py index 1221239b..52a7b3fb 100644 --- a/src/scheduling/domain/shift.py +++ b/src/scheduling/domain/shift.py @@ -13,8 +13,8 @@ ShiftId = PositiveId -class ShiftKind(StrEnum): - """Reduced shift kind used by scheduling rules. +class ShiftType(StrEnum): + """Reduced shift type used by scheduling rules. This is not a full TimeOffice shift taxonomy. It only contains categories relevant for demand, rest rules, night rules, and project-specific work. @@ -46,7 +46,7 @@ class Shift(SchedulingBaseModel): shift_id: ShiftId code: NonEmptyStr - kind: ShiftKind + type: ShiftType staffing_role: StaffingDemandRole start_minute: MinuteOfDay diff --git a/src/scheduling/domain/wish.py b/src/scheduling/domain/wish.py index dd317d41..dfde8d07 100644 --- a/src/scheduling/domain/wish.py +++ b/src/scheduling/domain/wish.py @@ -10,9 +10,11 @@ from scheduling.domain.shift import ShiftId -class WishKind(StrEnum): - SHIFT = "shift" +class WishType(StrEnum): FREE_DAY = "free_day" + FREE_SHIFT = "free_shift" + PREFERRED_DAY = "preferred_day" + PREFERRED_SHIFT = "preferred_shift" class Wish(SchedulingBaseModel): @@ -20,15 +22,15 @@ class Wish(SchedulingBaseModel): planning_unit_id: PlanningUnitId date: Date - kind: WishKind + type: WishType shift_id: ShiftId | None = None @model_validator(mode="after") def validate_wish(self) -> Self: - if self.kind == WishKind.SHIFT and self.shift_id is None: - raise ValueError("SHIFT wish requires shift_id.") + if self.type in {WishType.FREE_SHIFT, WishType.PREFERRED_SHIFT} and self.shift_id is None: + raise ValueError(f"{self.type} wish requires shift_id.") - if self.kind == WishKind.FREE_DAY and self.shift_id is not None: - raise ValueError("FREE_DAY wish must not define shift_id.") + if self.type in {WishType.FREE_DAY, WishType.PREFERRED_DAY} and self.shift_id is not None: + raise ValueError(f"{self.type} wish must not define shift_id.") return self diff --git a/src/scheduling/solver/cp_sat/constraints/employee_daily.py b/src/scheduling/solver/cp_sat/constraints/employee_daily.py deleted file mode 100644 index dd2ef963..00000000 --- a/src/scheduling/solver/cp_sat/constraints/employee_daily.py +++ /dev/null @@ -1,18 +0,0 @@ -from collections import defaultdict - -from ortools.sat.python import cp_model - -from scheduling.solver.cp_sat.context import SolverContext -from scheduling.solver.cp_sat.keys import EmployeeDateKey - - -def add_one_assignment_per_employee_day_constraints(ctx: SolverContext) -> None: - """Prevent more than one generated assignment per employee and day.""" - vars_by_employee_date: defaultdict[EmployeeDateKey, list[cp_model.IntVar]] = defaultdict(list) - - for key, variable in ctx.assignment_variables.items(): - employee_id, _, assignment_date, _, _ = key - vars_by_employee_date[(employee_id, assignment_date)].append(variable) - - for variables in vars_by_employee_date.values(): - ctx.model.add(sum(variables) <= 1) diff --git a/src/scheduling/solver/cp_sat/constraints/minimum_staffing.py b/src/scheduling/solver/cp_sat/constraints/minimum_staffing.py index cd8e2eaf..6f93cdc4 100644 --- a/src/scheduling/solver/cp_sat/constraints/minimum_staffing.py +++ b/src/scheduling/solver/cp_sat/constraints/minimum_staffing.py @@ -7,7 +7,11 @@ def add_minimum_staffing_constraints(ctx: SolverContext) -> None: - """Cover minimum staffing requirements per planning unit, date, shift, and staff level.""" + """Cover minimum staffing requirements from SchedulingDataset demand. + + This is the primary dataset-driven hard constraint. It should remain part of + the final solver model. + """ vars_by_demand = _group_vars_by_demand(ctx) for demand_key, required_count in ctx.index.required_count_by_demand_key.items(): @@ -35,7 +39,20 @@ def add_minimum_staffing_constraints(ctx: SolverContext) -> None: ) ) - ctx.model.add(sum(variables) >= remaining_required) + constraint_name = _minimum_staffing_constraint_name(demand_key) + ctx.model.add(sum(variables) >= remaining_required).with_name(constraint_name) + + +def _minimum_staffing_constraint_name(demand_key: DemandKey) -> str: + planning_unit_id, demand_date, shift_id, staff_level = demand_key + + return ( + "minimum_staffing" + f"__unit_{planning_unit_id}" + f"__date_{demand_date:%Y%m%d}" + f"__shift_{shift_id}" + f"__level_{staff_level.value}" + ) def _group_vars_by_demand( diff --git a/src/scheduling/solver/cp_sat/context.py b/src/scheduling/solver/cp_sat/context.py index 74520639..575c58ad 100644 --- a/src/scheduling/solver/cp_sat/context.py +++ b/src/scheduling/solver/cp_sat/context.py @@ -7,20 +7,13 @@ from scheduling.solver.cp_sat.keys import AssignmentVariableKey -@dataclass(frozen=True, slots=True) -class ObjectiveTerm: - name: str - expression: cp_model.LinearExpr - weight: int - - @dataclass(slots=True) class SolverContext: dataset: SchedulingDataset index: SolverIndex model: cp_model.CpModel assignment_variables: dict[AssignmentVariableKey, cp_model.IntVar] - objective_terms: list[ObjectiveTerm] + objective_terms: list[cp_model.LinearExpr] diagnostics: list[str] @@ -33,6 +26,20 @@ def create_context(dataset: SchedulingDataset) -> SolverContext: index=index, model=cp_model.CpModel(), assignment_variables={}, - diagnostics=[], objective_terms=[], + diagnostics=[], ) + + +def add_objective_term( + ctx: SolverContext, + *, + name: str, + expression: cp_model.LinearExpr, + weight: int = 1, +) -> None: + """Register one weighted objective term for minimization.""" + if weight == 0: + return + + ctx.objective_terms.append(expression * weight) diff --git a/src/scheduling/solver/cp_sat/eligibility.py b/src/scheduling/solver/cp_sat/eligibility.py index 76df5fe1..007e8b1c 100644 --- a/src/scheduling/solver/cp_sat/eligibility.py +++ b/src/scheduling/solver/cp_sat/eligibility.py @@ -1,44 +1,75 @@ from datetime import date from scheduling.domain.availability import AvailabilityType -from scheduling.domain.demand import DemandRequirement -from scheduling.domain.employee import Employee +from scheduling.domain.employee import Employee, StaffLevel +from scheduling.domain.planning_unit import PlanningUnitId +from scheduling.domain.shift import ShiftId from scheduling.solver.cp_sat.index import SolverIndex -def is_employee_eligible_for_demand( +def eligible_staff_levels_for_assignment_slot( *, employee: Employee, - demand: DemandRequirement, + planning_unit_id: PlanningUnitId, + assignment_date: date, + shift_id: ShiftId, index: SolverIndex, -) -> bool: - return ( - _has_active_membership_for_demand(employee=employee, demand=demand, index=index) - and not _has_existing_assignment_on_date(employee=employee, demand=demand, index=index) - and not _is_blocked_by_availability(employee=employee, demand=demand, index=index) +) -> tuple[StaffLevel, ...]: + """Return staff levels under which an employee may work one generated slot. + + Temporary migration behavior: + imported TimeOffice assignments are intentionally ignored by the solver for now. + They do not block generated assignments and do not count as fixed coverage. + + Current hard eligibility rules: + - employee must have an active membership in the planning unit + - employee must not be blocked by hard availability + - AVAILABLE_ONLY restrictions must include the target shift + + Future hard rules belong here too: + - shared/jump-pool eligibility + - qualifications + - legal hard constraints that can safely pre-filter slots + """ + if _is_blocked_by_availability( + employee=employee, + assignment_date=assignment_date, + shift_id=shift_id, + index=index, + ): + return () + + return _active_membership_staff_levels( + employee=employee, + planning_unit_id=planning_unit_id, + assignment_date=assignment_date, + index=index, ) -def _has_active_membership_for_demand( +def _active_membership_staff_levels( *, employee: Employee, - demand: DemandRequirement, + planning_unit_id: PlanningUnitId, + assignment_date: date, index: SolverIndex, -) -> bool: +) -> tuple[StaffLevel, ...]: memberships = index.memberships_by_employee_unit.get( - (employee.employee_id, demand.planning_unit_id), + (employee.employee_id, planning_unit_id), [], ) - return any( - membership.staff_level == demand.staff_level - and _date_in_interval( - demand.date, + staff_levels = { + membership.staff_level + for membership in memberships + if _date_in_interval( + assignment_date, valid_from=membership.valid_from, valid_until=membership.valid_until, ) - for membership in memberships - ) + } + + return tuple(sorted(staff_levels, key=lambda staff_level: staff_level.value)) def _date_in_interval( @@ -50,23 +81,15 @@ def _date_in_interval( return target_date >= valid_from and (valid_until is None or target_date <= valid_until) -def _has_existing_assignment_on_date( - *, - employee: Employee, - demand: DemandRequirement, - index: SolverIndex, -) -> bool: - return bool(index.assignments_by_employee_date.get((employee.employee_id, demand.date))) - - def _is_blocked_by_availability( *, employee: Employee, - demand: DemandRequirement, + assignment_date: date, + shift_id: ShiftId, index: SolverIndex, ) -> bool: availability_items = index.availability_by_employee_date.get( - (employee.employee_id, demand.date), + (employee.employee_id, assignment_date), [], ) @@ -87,6 +110,8 @@ def _is_blocked_by_availability( if not available_only_items: return False - allowed_shift_ids = {shift_id for item in available_only_items for shift_id in (item.shift_ids or ())} + allowed_shift_ids = { + allowed_shift_id for item in available_only_items for allowed_shift_id in (item.shift_ids or ()) + } - return demand.shift_id not in allowed_shift_ids + return shift_id not in allowed_shift_ids diff --git a/src/scheduling/solver/cp_sat/index.py b/src/scheduling/solver/cp_sat/index.py index 569e1f03..3ee9f12a 100644 --- a/src/scheduling/solver/cp_sat/index.py +++ b/src/scheduling/solver/cp_sat/index.py @@ -3,7 +3,6 @@ from scheduling.domain import ( Assignment, - AssignmentType, Availability, DemandRequirement, Employee, @@ -41,10 +40,7 @@ def build_schedule_index(dataset: SchedulingDataset) -> SolverIndex: assignments_by_employee_date=_group_assignments_by_employee_date(dataset.assignments), availability_by_employee_date=_group_availability_by_employee_date(dataset.availability), required_count_by_demand_key=_count_required_demand_by_key(dataset.demand_requirements), - fixed_planned_count_by_demand_key=_count_fixed_planned_assignments_by_demand_key( - assignments=dataset.assignments, - employees_by_id=employees_by_id, - ), + fixed_planned_count_by_demand_key=_ignore_fixed_planned_assignments(), ) @@ -98,27 +94,38 @@ def _count_required_demand_by_key( return dict(required) -def _count_fixed_planned_assignments_by_demand_key( - *, - assignments: tuple[Assignment, ...], - employees_by_id: dict[EmployeeId, Employee], -) -> dict[DemandKey, int]: - fixed: defaultdict[DemandKey, int] = defaultdict(int) +def _ignore_fixed_planned_assignments() -> dict[DemandKey, int]: + """Ignore imported TimeOffice assignments during solver migration. - for assignment in assignments: - if assignment.assignment_type != AssignmentType.PLANNED: - continue + Temporary migration behavior: + existing assignments are still part of the imported SchedulingDataset, but + the CP-SAT model currently generates a fresh schedule and does not treat + imported assignments as fixed coverage. + """ + return {} - if assignment.planning_unit_id is None: - continue - employee = employees_by_id[assignment.employee_id] - key = ( - assignment.planning_unit_id, - assignment.date, - assignment.shift_id, - employee.staff_level, - ) - fixed[key] += 1 +# def _count_fixed_planned_assignments_by_demand_key( +# *, +# assignments: tuple[Assignment, ...], +# employees_by_id: dict[EmployeeId, Employee], +# ) -> dict[DemandKey, int]: +# fixed: defaultdict[DemandKey, int] = defaultdict(int) + +# for assignment in assignments: +# if assignment.assignment_type != AssignmentType.PLANNED: +# continue + +# if assignment.planning_unit_id is None: +# continue + +# employee = employees_by_id[assignment.employee_id] +# key = ( +# assignment.planning_unit_id, +# assignment.date, +# assignment.shift_id, +# employee.staff_level, +# ) +# fixed[key] += 1 - return dict(fixed) +# return dict(fixed) diff --git a/src/scheduling/solver/cp_sat/inspection.py b/src/scheduling/solver/cp_sat/inspection.py new file mode 100644 index 00000000..35f18c7d --- /dev/null +++ b/src/scheduling/solver/cp_sat/inspection.py @@ -0,0 +1,78 @@ +import re +from collections.abc import Sequence +from dataclasses import dataclass +from typing import Protocol, cast + +from ortools.sat.python import cp_model + + +class _NamedProto(Protocol): + name: str + + +class _CpModelProtoView(Protocol): + variables: Sequence[object] + constraints: Sequence[_NamedProto] + + +@dataclass(frozen=True, slots=True) +class CpSatInspection: + proto_variable_count: int + proto_constraint_count: int + constraint_type_counts: dict[str, int] + constraint_names: tuple[str, ...] + unnamed_constraint_count: int + model_stats: str + validation_error: str | None + + @property + def is_valid(self) -> bool: + return self.validation_error is None + + +def inspect_cp_sat_model(*, model: cp_model.CpModel) -> CpSatInspection: + """Inspect a built CP-SAT model without logging or mutating it. + + OR-Tools exposes CP-SAT protos through cp_model_helper pybind types. + Those objects do not expose normal protobuf reflection methods like + WhichOneof(), so constraint type counts are derived from model_stats(). + """ + proto = _model_proto_view(model) + model_stats = model.model_stats() + + constraint_names = tuple(_constraint_name(constraint) for constraint in proto.constraints) + + return CpSatInspection( + proto_variable_count=len(proto.variables), + proto_constraint_count=len(proto.constraints), + constraint_type_counts=_constraint_type_counts_from_model_stats(model_stats), + constraint_names=constraint_names, + unnamed_constraint_count=sum(name == "" for name in constraint_names), + model_stats=model_stats, + validation_error=model.validate() or None, + ) + + +def _model_proto_view(model: cp_model.CpModel) -> _CpModelProtoView: + raw_proto: object = model.proto + return cast(_CpModelProtoView, raw_proto) + + +def _constraint_name(constraint: _NamedProto) -> str: + return constraint.name or "" + + +def _constraint_type_counts_from_model_stats( + model_stats: str, +) -> dict[str, int]: + counts: dict[str, int] = {} + + for line in model_stats.splitlines(): + match = re.compile(r"^\s*#(k[A-Za-z0-9_]+):\s+([0-9']+)").match(line) + if match is None: + continue + + constraint_type, count = match.groups() + counts[constraint_type] = int(count.replace("'", "")) + + return counts diff --git a/src/scheduling/solver/cp_sat/objectives/balance_generated_assignments.py b/src/scheduling/solver/cp_sat/objectives/balance_generated_assignments.py index 051c18ca..fca72628 100644 --- a/src/scheduling/solver/cp_sat/objectives/balance_generated_assignments.py +++ b/src/scheduling/solver/cp_sat/objectives/balance_generated_assignments.py @@ -2,11 +2,16 @@ from ortools.sat.python import cp_model -from scheduling.solver.cp_sat.context import ObjectiveTerm, SolverContext +from scheduling.solver.cp_sat.context import SolverContext, add_objective_term -def add_balance_generated_assignments_objective(ctx: SolverContext) -> None: - """Prefer distributing generated assignments across employees.""" +def add_balance_assignments_objective(ctx: SolverContext) -> None: + """Prefer distributing generated assignments across employees. + + Temporary migration objective: + this is a placeholder while the real wish/fairness model is not migrated to + SchedulingDataset yet. Remove or replace this before final solver evaluation. + """ if not ctx.assignment_variables: return @@ -21,15 +26,17 @@ def add_balance_generated_assignments_objective(ctx: SolverContext) -> None: max_generated_assignments = ctx.model.new_int_var( 0, len(ctx.assignment_variables), - "max_generated_assignments_per_employee", + "temporary_balance_generated_assignments__max_per_employee", ) - ctx.model.add_max_equality(max_generated_assignments, generated_counts) + ctx.model.add_max_equality( + max_generated_assignments, + generated_counts, + ).with_name("temporary_balance_generated_assignments__define_max_per_employee") - ctx.objective_terms.append( - ObjectiveTerm( - name="balance_generated_assignments", - expression=max_generated_assignments, - weight=1, - ) + add_objective_term( + ctx, + name="temporary_balance_generated_assignments", + expression=max_generated_assignments, + weight=1, ) diff --git a/src/scheduling/solver/cp_sat/variables.py b/src/scheduling/solver/cp_sat/variables.py index 40a5b4b6..7dab6c6a 100644 --- a/src/scheduling/solver/cp_sat/variables.py +++ b/src/scheduling/solver/cp_sat/variables.py @@ -1,52 +1,79 @@ -from scheduling.domain.demand import DemandRequirement +from datetime import date, timedelta + +from scheduling.domain import PlanningUnitType, StaffingDemandRole from scheduling.solver.cp_sat.context import SolverContext -from scheduling.solver.cp_sat.eligibility import is_employee_eligible_for_demand -from scheduling.solver.cp_sat.keys import AssignmentVariableKey, DemandKey +from scheduling.solver.cp_sat.eligibility import eligible_staff_levels_for_assignment_slot +from scheduling.solver.cp_sat.keys import AssignmentVariableKey def create_assignment_variables(ctx: SolverContext) -> None: - """Create one boolean variable for each valid generated assignment.""" - for demand in ctx.dataset.demand_requirements: - if _remaining_required(ctx, demand) <= 0: - continue - - for employee in ctx.dataset.employees: - if not is_employee_eligible_for_demand( - employee=employee, - demand=demand, - index=ctx.index, - ): - continue - - key: AssignmentVariableKey = ( - employee.employee_id, - demand.planning_unit_id, - demand.date, - demand.shift_id, - demand.staff_level, - ) - - if key in ctx.assignment_variables: - continue - - ctx.assignment_variables[key] = ctx.model.new_bool_var(_variable_name(key)) - - -def _remaining_required(ctx: SolverContext, demand: DemandRequirement) -> int: - demand_key: DemandKey = ( - demand.planning_unit_id, - demand.date, - demand.shift_id, - demand.staff_level, + """Create one boolean variable for every feasible generated assignment slot. + + Long-term model direction: + variables describe the possible schedule space. Demand, wishes, fairness, + and workload rules are separate constraints/objectives over that space. + """ + for planning_unit_id in _assignable_planning_unit_ids(ctx): + for assignment_date in _planning_dates(ctx): + for shift_id in _assignable_shift_ids(ctx): + for employee in ctx.dataset.employees: + staff_levels = eligible_staff_levels_for_assignment_slot( + employee=employee, + planning_unit_id=planning_unit_id, + assignment_date=assignment_date, + shift_id=shift_id, + index=ctx.index, + ) + + for staff_level in staff_levels: + key: AssignmentVariableKey = ( + employee.employee_id, + planning_unit_id, + assignment_date, + shift_id, + staff_level, + ) + + ctx.assignment_variables[key] = ctx.model.new_bool_var(_assignment_variable_name(key)) + + +def _assignable_planning_unit_ids(ctx: SolverContext) -> tuple[int, ...]: + return tuple( + sorted( + planning_unit.planning_unit_id + for planning_unit in ctx.dataset.planning_units + if planning_unit.type == PlanningUnitType.STATION + ) ) - required_count = ctx.index.required_count_by_demand_key.get(demand_key, 0) - fixed_count = ctx.index.fixed_planned_count_by_demand_key.get(demand_key, 0) - return max(required_count - fixed_count, 0) +def _assignable_shift_ids(ctx: SolverContext) -> tuple[int, ...]: + return tuple( + sorted( + shift.shift_id for shift in ctx.dataset.shifts if shift.staffing_role != StaffingDemandRole.NON_MINIMUM_WORK + ) + ) + + +def _planning_dates(ctx: SolverContext) -> tuple[date, ...]: + dates: list[date] = [] + current_date = ctx.dataset.planning_month.start + while current_date <= ctx.dataset.planning_month.end: + dates.append(current_date) + current_date += timedelta(days=1) -def _variable_name(key: AssignmentVariableKey) -> str: + return tuple(dates) + + +def _assignment_variable_name(key: AssignmentVariableKey) -> str: employee_id, planning_unit_id, assignment_date, shift_id, staff_level = key - return f"assign_e{employee_id}_p{planning_unit_id}_d{assignment_date.isoformat()}_s{shift_id}_l{staff_level.value}" + return ( + "assign" + f"__employee_{employee_id}" + f"__unit_{planning_unit_id}" + f"__date_{assignment_date:%Y%m%d}" + f"__shift_{shift_id}" + f"__level_{staff_level.value}" + ) diff --git a/src/scheduling/solver/service.py b/src/scheduling/solver/service.py index b865aed7..21801e73 100644 --- a/src/scheduling/solver/service.py +++ b/src/scheduling/solver/service.py @@ -5,12 +5,10 @@ from scheduling.domain import Assignment, AssignmentType, SchedulingDataset from scheduling.settings import Settings -from scheduling.solver.cp_sat.constraints.employee_daily import add_one_assignment_per_employee_day_constraints from scheduling.solver.cp_sat.constraints.minimum_staffing import add_minimum_staffing_constraints from scheduling.solver.cp_sat.context import SolverContext, create_context -from scheduling.solver.cp_sat.objectives.balance_generated_assignments import ( - add_balance_generated_assignments_objective, -) +from scheduling.solver.cp_sat.inspection import CpSatInspection, inspect_cp_sat_model +from scheduling.solver.cp_sat.objectives.balance_generated_assignments import add_balance_assignments_objective from scheduling.solver.cp_sat.variables import create_assignment_variables from scheduling.solver.models import Solution, SolutionStatus @@ -19,12 +17,9 @@ type ModelStepFunction = Callable[[SolverContext], None] -CONSTRAINTS: tuple[ModelStepFunction, ...] = ( - add_minimum_staffing_constraints, - add_one_assignment_per_employee_day_constraints, -) +CONSTRAINTS: tuple[ModelStepFunction, ...] = (add_minimum_staffing_constraints,) -OBJECTIVES: tuple[ModelStepFunction, ...] = (add_balance_generated_assignments_objective,) +OBJECTIVES: tuple[ModelStepFunction, ...] = (add_balance_assignments_objective,) class SolverService: @@ -35,6 +30,19 @@ def __init__(self, settings: Settings) -> None: def solve(self, dataset: SchedulingDataset) -> Solution: ctx = self._build_model(dataset) + inspection = self._inspect_model(ctx) + + if not inspection.is_valid: + diagnostics = ( + *ctx.diagnostics, + f"CP-SAT model validation failed: {inspection.validation_error}", + ) + return Solution( + status=SolutionStatus.MODEL_INVALID, + assignments=(), + diagnostics=diagnostics, + ) + solver = self._create_solver() logger.info( @@ -93,20 +101,38 @@ def _build_model(self, dataset: SchedulingDataset) -> SolverContext: add_objective(ctx) if ctx.objective_terms: - objective = sum(term.weight * term.expression for term in ctx.objective_terms) - ctx.model.minimize(objective) + ctx.model.minimize(sum(ctx.objective_terms)) else: logger.debug("No solver objective terms registered.") + return ctx + + def _inspect_model(self, ctx: SolverContext) -> CpSatInspection: + inspection = inspect_cp_sat_model(model=ctx.model) + logger.info( - "Built CP-SAT model: variables=%s constraints=%s objective_terms=%s diagnostics=%s", + "Built CP-SAT model: assignment_variables=%s proto_variables=%s " + "proto_constraints=%s constraint_types=%s objective_terms=%s diagnostics=%s", len(ctx.assignment_variables), - len(CONSTRAINTS), - tuple(term.name for term in ctx.objective_terms), + inspection.proto_variable_count, + inspection.proto_constraint_count, + inspection.constraint_type_counts, + len(ctx.objective_terms), len(ctx.diagnostics), ) - return ctx + if inspection.unnamed_constraint_count: + logger.warning( + "CP-SAT model contains unnamed constraints: unnamed_constraints=%s", + inspection.unnamed_constraint_count, + ) + + if not inspection.is_valid: + logger.error("CP-SAT model validation failed: %s", inspection.validation_error) + + logger.debug("CP-SAT constraint names: names=%s", inspection.constraint_names) + + return inspection def _create_solver(self) -> cp_model.CpSolver: solver = cp_model.CpSolver() diff --git a/src/scheduling/timeoffice/facts.py b/src/scheduling/timeoffice/facts.py index ff0d0ef6..f73527ae 100644 --- a/src/scheduling/timeoffice/facts.py +++ b/src/scheduling/timeoffice/facts.py @@ -4,9 +4,9 @@ from scheduling.domain.availability import AvailabilityType from scheduling.domain.employee import Capability, StaffLevel -from scheduling.domain.planning_unit import PlanningUnitId, PlanningUnitKind -from scheduling.domain.shift import ShiftId, ShiftKind, StaffingDemandRole -from scheduling.domain.wish import WishKind +from scheduling.domain.planning_unit import PlanningUnitId, PlanningUnitType +from scheduling.domain.shift import ShiftId, ShiftType, StaffingDemandRole +from scheduling.domain.wish import WishType # TPlan.RefPlanungsIntervalle value for monthly planning. MONTHLY_PLANNING_INTERVAL_ID = 1 @@ -54,7 +54,7 @@ class TimeOfficeReferenceShiftFact: """ expected_code: str - kind: ShiftKind + type: ShiftType staffing_role: StaffingDemandRole @@ -74,7 +74,7 @@ class TimeOfficeFacts: monthly_planning_interval_id: int target_planning_status_id: int - planning_unit_kind_by_id: Mapping[PlanningUnitId, PlanningUnitKind] + planning_unit_type_by_id: Mapping[PlanningUnitId, PlanningUnitType] work_shift_type_id: int @@ -94,7 +94,7 @@ class TimeOfficeFacts: capabilities_by_employee_id: Mapping[int, tuple[Capability, ...]] availability_type_by_absence_code: Mapping[str, AvailabilityType] - wish_kind_by_absence_code: Mapping[str, WishKind] + wish_type_by_absence_code: Mapping[str, WishType] monthly_target_work_account_id: int monthly_actual_work_account_id: int @@ -104,27 +104,27 @@ class TimeOfficeFacts: { EARLY_F2_SHIFT_ID: TimeOfficeReferenceShiftFact( expected_code="F2_", - kind=ShiftKind.EARLY, + type=ShiftType.EARLY, staffing_role=StaffingDemandRole.REQUIRED_MINIMUM, ), LATE_S2_SHIFT_ID: TimeOfficeReferenceShiftFact( expected_code="S2_", - kind=ShiftKind.LATE, + type=ShiftType.LATE, staffing_role=StaffingDemandRole.REQUIRED_MINIMUM, ), NIGHT_N2_SHIFT_ID: TimeOfficeReferenceShiftFact( expected_code="N2_", - kind=ShiftKind.NIGHT, + type=ShiftType.NIGHT, staffing_role=StaffingDemandRole.REQUIRED_MINIMUM, ), INTERMEDIATE_T75_SHIFT_ID: TimeOfficeReferenceShiftFact( expected_code="T75_", - kind=ShiftKind.INTERMEDIATE, + type=ShiftType.INTERMEDIATE, staffing_role=StaffingDemandRole.OPTIONAL_COVERAGE, ), MANAGEMENT_Z60_SHIFT_ID: TimeOfficeReferenceShiftFact( expected_code="Z60", - kind=ShiftKind.MANAGEMENT, + type=ShiftType.MANAGEMENT, staffing_role=StaffingDemandRole.NON_MINIMUM_WORK, ), } @@ -190,7 +190,12 @@ class TimeOfficeFacts: } ) -DEFAULT_STATION_DEMAND: PlanningUnitDemandMatrix = MappingProxyType( +# Minimal default while solver constraints are being migrated to SchedulingDataset. +# Empty demand means: read/map/validate the station, but do not create artificial +# fallback coverage requirements for it yet. +DEFAULT_STATION_DEMAND: PlanningUnitDemandMatrix = MappingProxyType({}) + +STATION_77_DEMAND: PlanningUnitDemandMatrix = MappingProxyType( { # Weekday tuple order: Mo, Di, Mi, Do, Fr, Sa, So. StaffLevel.PROFESSIONAL: MappingProxyType( @@ -221,15 +226,15 @@ class TimeOfficeFacts: TIMEOFFICE_FACTS = TimeOfficeFacts( monthly_planning_interval_id=MONTHLY_PLANNING_INTERVAL_ID, target_planning_status_id=TARGET_PLANNING_STATUS_ID, - planning_unit_kind_by_id=MappingProxyType( + planning_unit_type_by_id=MappingProxyType( { - STATION_77_ID: PlanningUnitKind.STATION, - STATION_78_ID: PlanningUnitKind.STATION, - STATION_79_ID: PlanningUnitKind.STATION, - STATION_85_ID: PlanningUnitKind.STATION, - STATION_239_ID: PlanningUnitKind.STATION, - STATION_337_ID: PlanningUnitKind.STATION, - SHARED_POOL_408_ID: PlanningUnitKind.SHARED_POOL, + STATION_77_ID: PlanningUnitType.STATION, + STATION_78_ID: PlanningUnitType.STATION, + STATION_79_ID: PlanningUnitType.STATION, + STATION_85_ID: PlanningUnitType.STATION, + STATION_239_ID: PlanningUnitType.STATION, + STATION_337_ID: PlanningUnitType.STATION, + SHARED_POOL_408_ID: PlanningUnitType.SHARED_POOL, } ), work_shift_type_id=WORK_SHIFT_TYPE_ID, @@ -238,7 +243,7 @@ class TimeOfficeFacts: staff_level_by_profession_code=STAFF_LEVEL_BY_PROFESSION_CODE, fallback_demand_by_planning_unit=MappingProxyType( { - STATION_77_ID: DEFAULT_STATION_DEMAND, + STATION_77_ID: STATION_77_DEMAND, STATION_78_ID: DEFAULT_STATION_DEMAND, STATION_79_ID: DEFAULT_STATION_DEMAND, STATION_85_ID: DEFAULT_STATION_DEMAND, @@ -273,9 +278,9 @@ class TimeOfficeFacts: "FI": AvailabilityType.UNAVAILABLE, } ), - wish_kind_by_absence_code=MappingProxyType( + wish_type_by_absence_code=MappingProxyType( { - "FR": WishKind.FREE_DAY, + "FR": WishType.FREE_DAY, } ), monthly_target_work_account_id=MONTHLY_TARGET_WORK_ACCOUNT_ID, diff --git a/src/scheduling/timeoffice/mapping/dataset.py b/src/scheduling/timeoffice/mapping/dataset.py index 83131f3d..dc01fd92 100644 --- a/src/scheduling/timeoffice/mapping/dataset.py +++ b/src/scheduling/timeoffice/mapping/dataset.py @@ -44,6 +44,7 @@ def map_scheduling_dataset(*, sources: TimeOfficeSources, facts: TimeOfficeFacts sunday_work_history=map_sunday_work_history(sources.sunday_history_rows), wishes=map_wishes( rows=sources.wish_rows, + shifts=shifts, facts=facts, ), monthly_work_accounts=map_monthly_work_accounts(sources.monthly_work_account_rows), diff --git a/src/scheduling/timeoffice/mapping/demand.py b/src/scheduling/timeoffice/mapping/demand.py index aa85eb75..a8c791c5 100644 --- a/src/scheduling/timeoffice/mapping/demand.py +++ b/src/scheduling/timeoffice/mapping/demand.py @@ -1,6 +1,6 @@ from datetime import timedelta -from scheduling.domain import DemandRequirement, PlanningMonth, PlanningUnit, PlanningUnitKind, StaffingDemandRole +from scheduling.domain import DemandRequirement, PlanningMonth, PlanningUnit, PlanningUnitType, StaffingDemandRole from scheduling.domain.shift import ShiftId from scheduling.timeoffice.facts import PlanningUnitDemandMatrix, TimeOfficeFacts @@ -14,7 +14,7 @@ def map_demand_requirements( requirements: list[DemandRequirement] = [] selected_station_ids = sorted( - unit.planning_unit_id for unit in planning_units if unit.kind == PlanningUnitKind.STATION + unit.planning_unit_id for unit in planning_units if unit.type == PlanningUnitType.STATION ) for planning_unit_id in selected_station_ids: diff --git a/src/scheduling/timeoffice/mapping/planning.py b/src/scheduling/timeoffice/mapping/planning.py index f693796f..a8595df7 100644 --- a/src/scheduling/timeoffice/mapping/planning.py +++ b/src/scheduling/timeoffice/mapping/planning.py @@ -1,4 +1,4 @@ -from scheduling.domain import Plan, PlanningUnit, PlanningUnitKind +from scheduling.domain import Plan, PlanningUnit, PlanningUnitType from scheduling.timeoffice.facts import TimeOfficeFacts from scheduling.timeoffice.reading.planning_units import TimeOfficePlanningUnitRow @@ -10,7 +10,7 @@ def map_planning_units( PlanningUnit( planning_unit_id=row.planning_unit_id, display_name=f"Planning Unit {row.planning_unit_id}", - kind=_planning_unit_kind(row.planning_unit_id, facts=facts), + type=_planning_unit_type(row.planning_unit_id, facts=facts), ) for row in rows ) @@ -26,10 +26,10 @@ def map_plans(rows: tuple[TimeOfficePlanningUnitRow, ...]) -> tuple[Plan, ...]: ) -def _planning_unit_kind(planning_unit_id: int, *, facts: TimeOfficeFacts) -> PlanningUnitKind: - kind = facts.planning_unit_kind_by_id.get(planning_unit_id) +def _planning_unit_type(planning_unit_id: int, *, facts: TimeOfficeFacts) -> PlanningUnitType: + type = facts.planning_unit_type_by_id.get(planning_unit_id) - if kind is None: - raise ValueError(f"No PlanningUnitKind configured for TimeOffice planning_unit_id={planning_unit_id}.") + if type is None: + raise ValueError(f"No PlanningUnitType configured for TimeOffice planning_unit_id={planning_unit_id}.") - return kind + return type diff --git a/src/scheduling/timeoffice/mapping/shifts.py b/src/scheduling/timeoffice/mapping/shifts.py index 2b025a9e..3a9c9f1b 100644 --- a/src/scheduling/timeoffice/mapping/shifts.py +++ b/src/scheduling/timeoffice/mapping/shifts.py @@ -97,7 +97,7 @@ def _map_reference_shift( return Shift( shift_id=shift_id, code=first_row.shift_code, - kind=shift_fact.kind, + type=shift_fact.type, staffing_role=shift_fact.staffing_role, start_minute=_minute_of_day(segments[0][0]), end_minute=_minute_of_day(segments[-1][1]), diff --git a/src/scheduling/timeoffice/mapping/wishes.py b/src/scheduling/timeoffice/mapping/wishes.py index d5265526..2649a907 100644 --- a/src/scheduling/timeoffice/mapping/wishes.py +++ b/src/scheduling/timeoffice/mapping/wishes.py @@ -1,71 +1,158 @@ -from datetime import datetime +from datetime import date as Date -from scheduling.domain import Wish, WishKind +from scheduling.domain import Shift, Wish, WishType from scheduling.timeoffice.facts import TimeOfficeFacts from scheduling.timeoffice.mapping.shifts import reference_shift_id_for_source_shift from scheduling.timeoffice.reading.wishes import TimeOfficeWishRow -def map_wishes(*, rows: tuple[TimeOfficeWishRow, ...], facts: TimeOfficeFacts) -> tuple[Wish, ...]: - return tuple(_map_wish(row=row, facts=facts) for row in rows) +def map_wishes( + rows: tuple[TimeOfficeWishRow, ...], + *, + shifts: tuple[Shift, ...], + facts: TimeOfficeFacts, +) -> tuple[Wish, ...]: + known_shift_ids = {shift.shift_id for shift in shifts} + + wishes = tuple( + _map_wish( + row=row, + known_shift_ids=known_shift_ids, + facts=facts, + ) + for row in rows + ) + return _deduplicate_wishes(wishes) -def _map_wish(*, row: TimeOfficeWishRow, facts: TimeOfficeFacts) -> Wish: + +def _map_wish( + *, + row: TimeOfficeWishRow, + known_shift_ids: set[int], + facts: TimeOfficeFacts, +) -> Wish: if row.work_shift_id is not None: - return _map_shift_wish(row=row, facts=facts) + return _map_preferred_shift_wish( + row=row, + known_shift_ids=known_shift_ids, + facts=facts, + ) return _map_absence_wish(row=row, facts=facts) -def _map_shift_wish(*, row: TimeOfficeWishRow, facts: TimeOfficeFacts) -> Wish: +def _map_preferred_shift_wish( + *, + row: TimeOfficeWishRow, + known_shift_ids: set[int], + facts: TimeOfficeFacts, +) -> Wish: reference_shift_id = reference_shift_id_for_source_shift( source_shift_id=row.work_shift_id, source_shift_code=row.work_shift_code, facts=facts, - context=(f"TimeOffice wish work row employee_id={row.employee_id} date={row.wish_date.date()}"), + context=(f"TimeOffice preferred-shift wish employee_id={row.employee_id} date={row.wish_date.date()}"), ) + if reference_shift_id not in known_shift_ids: + raise ValueError( + "TimeOffice preferred-shift wish references shift that is not part " + "of the mapped SchedulingDataset: " + f"source_shift_id={row.work_shift_id} " + f"reference_shift_id={reference_shift_id} " + f"employee_id={row.employee_id} " + f"wish_date={row.wish_date}." + ) + return Wish( employee_id=row.employee_id, planning_unit_id=row.planning_unit_id, date=row.wish_date.date(), - kind=WishKind.SHIFT, + type=WishType.PREFERRED_SHIFT, shift_id=reference_shift_id, ) -def _map_absence_wish(*, row: TimeOfficeWishRow, facts: TimeOfficeFacts) -> Wish: +def _map_absence_wish( + *, + row: TimeOfficeWishRow, + facts: TimeOfficeFacts, +) -> Wish: + absence_shift_id = _resolved_absence_shift_id(row) + + if row.resolved_absence_code is None: + raise ValueError( + "Missing resolved absence code for TimeOffice wish row: " + f"absence_shift_id={absence_shift_id} " + f"employee_id={row.employee_id} " + f"wish_date={row.wish_date}." + ) + + wish_type = facts.wish_type_by_absence_code.get(row.resolved_absence_code) + if wish_type is None: + raise ValueError( + "Unmapped TimeOffice wish absence code: " + f"absence_shift_id={absence_shift_id} " + f"absence_code={row.resolved_absence_code!r} " + f"absence_name={row.resolved_absence_name!r}." + ) + + if wish_type in {WishType.FREE_SHIFT, WishType.PREFERRED_SHIFT}: + raise ValueError( + "TimeOffice absence wish was mapped to a shift-scoped wish type, " + "but the current TimeOffice wish row does not provide a canonical " + "target work shift_id. " + f"absence_shift_id={absence_shift_id} " + f"absence_code={row.resolved_absence_code!r} " + f"wish_type={wish_type}." + ) + return Wish( employee_id=row.employee_id, planning_unit_id=row.planning_unit_id, date=row.wish_date.date(), - kind=_wish_kind_for_absence_code( - row.resolved_absence_code, - facts=facts, - employee_id=row.employee_id, - wish_date=row.wish_date, - ), + type=wish_type, ) -def _wish_kind_for_absence_code( - absence_code: str | None, - *, - facts: TimeOfficeFacts, - employee_id: int, - wish_date: datetime, -) -> WishKind: - if absence_code is None: - raise ValueError( - f"Missing resolved absence code for TimeOffice wish row: employee_id={employee_id} wish_date={wish_date}." - ) +def _resolved_absence_shift_id(row: TimeOfficeWishRow) -> int: + if row.global_absence_shift_id is not None: + return row.global_absence_shift_id - wish_kind = facts.wish_kind_by_absence_code.get(absence_code) - if wish_kind is None: - raise ValueError( - "Unmapped TimeOffice absence code for wish: " - f"employee_id={employee_id} wish_date={wish_date} " - f"absence_code={absence_code!r}." + if row.absence_shift_id is not None: + return row.absence_shift_id + + raise ValueError( + "Invalid TimeOffice wish row after source-row validation: " + f"missing absence shift id for employee_id={row.employee_id}, " + f"wish_date={row.wish_date}." + ) + + +def _deduplicate_wishes(wishes: tuple[Wish, ...]) -> tuple[Wish, ...]: + wishes_by_key: dict[tuple[int, int, Date, WishType, int | None], Wish] = {} + + for wish in wishes: + key = ( + wish.employee_id, + wish.planning_unit_id, + wish.date, + wish.type, + wish.shift_id, ) + wishes_by_key.setdefault(key, wish) - return wish_kind + return tuple( + wishes_by_key[key] + for key in sorted( + wishes_by_key, + key=lambda item: ( + item[0], + item[1], + item[2], + item[3].value, + item[4] or -1, + ), + ) + ) diff --git a/src/scheduling/timeoffice/reading/roster.py b/src/scheduling/timeoffice/reading/roster.py index ab5e3d98..bf061d7f 100644 --- a/src/scheduling/timeoffice/reading/roster.py +++ b/src/scheduling/timeoffice/reading/roster.py @@ -24,7 +24,7 @@ class TimeOfficeRosterRow(TimeOfficeSourceRow): planning_unit_id: SourceNullableInt = None @model_validator(mode="after") - def validate_row_kind(self) -> Self: + def validate_row_type(self) -> Self: has_work_shift = self.work_shift_id is not None has_absence = self.global_absence_shift_id is not None or self.absence_shift_id is not None diff --git a/src/scheduling/timeoffice/reading/wishes.py b/src/scheduling/timeoffice/reading/wishes.py index aaa60270..5ff31745 100644 --- a/src/scheduling/timeoffice/reading/wishes.py +++ b/src/scheduling/timeoffice/reading/wishes.py @@ -31,7 +31,7 @@ class TimeOfficeWishRow(TimeOfficeSourceRow): resolved_absence_name: CleanNullableText = None @model_validator(mode="after") - def validate_row_kind(self) -> Self: + def validate_row_type(self) -> Self: has_work_shift = self.work_shift_id is not None has_absence = self.global_absence_shift_id is not None or self.absence_shift_id is not None diff --git a/src/scheduling/timeoffice/service.py b/src/scheduling/timeoffice/service.py index 9b99233d..ac8eba67 100644 --- a/src/scheduling/timeoffice/service.py +++ b/src/scheduling/timeoffice/service.py @@ -92,10 +92,10 @@ def _normalize_planning_unit_ids( unknown_ids = sorted( planning_unit_id for planning_unit_id in normalized - if planning_unit_id not in self._facts.planning_unit_kind_by_id + if planning_unit_id not in self._facts.planning_unit_type_by_id ) if unknown_ids: - known_ids = sorted(self._facts.planning_unit_kind_by_id) + known_ids = sorted(self._facts.planning_unit_type_by_id) raise ValueError( f"Unknown TimeOffice planning_unit_ids requested: {unknown_ids}. Known planning_unit_ids={known_ids}." ) diff --git a/src/scheduling/validation/context.py b/src/scheduling/validation/context.py index 3094c40a..5eb476ad 100644 --- a/src/scheduling/validation/context.py +++ b/src/scheduling/validation/context.py @@ -2,7 +2,7 @@ from dataclasses import dataclass from types import MappingProxyType -from scheduling.domain import EmployeeId, PlanId, PlanningUnitId, PlanningUnitKind, SchedulingDataset, Shift, ShiftId +from scheduling.domain import EmployeeId, PlanId, PlanningUnitId, PlanningUnitType, SchedulingDataset, Shift, ShiftId @dataclass(frozen=True, slots=True) @@ -12,7 +12,7 @@ class DatasetValidationContext: plan_ids: frozenset[PlanId] shift_ids: frozenset[ShiftId] - planning_unit_kind_by_id: Mapping[PlanningUnitId, PlanningUnitKind] + planning_unit_type_by_id: Mapping[PlanningUnitId, PlanningUnitType] shifts_by_id: Mapping[ShiftId, Shift] @classmethod @@ -39,8 +39,8 @@ def from_dataset(cls, dataset: SchedulingDataset) -> "DatasetValidationContext": planning_unit_ids=planning_unit_ids, plan_ids=plan_ids, shift_ids=shift_ids, - planning_unit_kind_by_id=MappingProxyType( - {unit.planning_unit_id: unit.kind for unit in dataset.planning_units} + planning_unit_type_by_id=MappingProxyType( + {unit.planning_unit_id: unit.type for unit in dataset.planning_units} ), shifts_by_id=MappingProxyType({shift.shift_id: shift for shift in dataset.shifts}), ) diff --git a/src/scheduling/validation/validators.py b/src/scheduling/validation/validators.py index 87f3b0bf..96e0f166 100644 --- a/src/scheduling/validation/validators.py +++ b/src/scheduling/validation/validators.py @@ -6,12 +6,12 @@ EmployeeId, PlanningMonth, PlanningUnitId, - PlanningUnitKind, + PlanningUnitType, SchedulingDataset, ShiftId, StaffingDemandRole, StaffLevel, - WishKind, + WishType, ) from scheduling.validation.context import DatasetValidationContext @@ -155,7 +155,7 @@ def validate_demand_requirements(dataset: SchedulingDataset, context: DatasetVal if demand.planning_unit_id not in context.planning_unit_ids: raise ValueError(f"DemandRequirement references unknown planning_unit_id={demand.planning_unit_id}.") - if context.planning_unit_kind_by_id[demand.planning_unit_id] != PlanningUnitKind.STATION: + if context.planning_unit_type_by_id[demand.planning_unit_id] != PlanningUnitType.STATION: raise ValueError( f"DemandRequirement must target a station planning unit: planning_unit_id={demand.planning_unit_id}." ) @@ -199,7 +199,7 @@ def validate_sunday_work_history(dataset: SchedulingDataset, context: DatasetVal def validate_wishes(dataset: SchedulingDataset, context: DatasetValidationContext) -> None: - seen: set[tuple[EmployeeId, PlanningUnitId, date, WishKind, ShiftId | None]] = set() + seen: set[tuple[EmployeeId, PlanningUnitId, date, WishType, ShiftId | None]] = set() for wish in dataset.wishes: if wish.employee_id not in context.employee_ids: @@ -222,7 +222,7 @@ def validate_wishes(dataset: SchedulingDataset, context: DatasetValidationContex wish.employee_id, wish.planning_unit_id, wish.date, - wish.kind, + wish.type, wish.shift_id, ) if key in seen: @@ -231,7 +231,7 @@ def validate_wishes(dataset: SchedulingDataset, context: DatasetValidationContex f"employee_id={wish.employee_id} " f"planning_unit_id={wish.planning_unit_id} " f"date={wish.date} " - f"kind={wish.kind} " + f"type={wish.type} " f"shift_id={wish.shift_id}." ) From 07014341054222aea4470127f8690214d391cfbf Mon Sep 17 00:00:00 2001 From: Tom Wawerek Date: Wed, 24 Jun 2026 11:29:41 +0200 Subject: [PATCH 13/18] feat: api options endpoint --- src/scheduling/api/solve/router.py | 22 +++++---- src/scheduling/api/solve/schemas.py | 6 ++- src/scheduling/solver/cp_sat/context.py | 8 +--- .../balance_generated_assignments.py | 7 +-- src/scheduling/timeoffice/mapping/dataset.py | 13 +++-- src/scheduling/timeoffice/mapping/options.py | 30 ++++++++++++ .../timeoffice/reading/container.py | 11 ++--- src/scheduling/timeoffice/reading/options.py | 48 +++++++++++++++++++ src/scheduling/timeoffice/service.py | 18 +++++++ 9 files changed, 128 insertions(+), 35 deletions(-) create mode 100644 src/scheduling/timeoffice/mapping/options.py create mode 100644 src/scheduling/timeoffice/reading/options.py diff --git a/src/scheduling/api/solve/router.py b/src/scheduling/api/solve/router.py index 791e86f0..742b20ba 100644 --- a/src/scheduling/api/solve/router.py +++ b/src/scheduling/api/solve/router.py @@ -6,25 +6,27 @@ from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, status -from scheduling.api.dependencies import ( - get_solve_job_store, - get_solve_lock, - get_solver_service, - get_timeoffice_service, -) +from scheduling.api.dependencies import get_solve_job_store, get_solve_lock, get_solver_service, get_timeoffice_service from scheduling.api.solve.job_models import SolveCommand, SolveJob from scheduling.api.solve.job_store import InMemorySolveJobStore -from scheduling.api.solve.schemas import SolveAcceptedResponse, SolveRequest +from scheduling.api.solve.schemas import SolveAcceptedResponse, SolveOptions, SolveRequest from scheduling.solver.models import Solution from scheduling.solver.service import SolverService from scheduling.timeoffice.service import TimeOfficeService logger = logging.getLogger(__name__) -solve_router = APIRouter() +solve_router = APIRouter(prefix="/solve") -@solve_router.post("/solve", status_code=status.HTTP_202_ACCEPTED) +@solve_router.get("/options") +def get_solve_options( + timeoffice: Annotated[TimeOfficeService, Depends(get_timeoffice_service)], +) -> SolveOptions: + return timeoffice.get_solve_options() + + +@solve_router.post("/", status_code=status.HTTP_202_ACCEPTED) async def create_solve_task( request: SolveRequest, timeoffice: Annotated[TimeOfficeService, Depends(get_timeoffice_service)], @@ -119,7 +121,7 @@ async def task() -> None: ) -@solve_router.get("/solve/jobs/{job_id}") +@solve_router.get("/jobs/{job_id}") async def check_solve_task( job_id: uuid.UUID, job_store: Annotated[InMemorySolveJobStore, Depends(get_solve_job_store)], diff --git a/src/scheduling/api/solve/schemas.py b/src/scheduling/api/solve/schemas.py index b060318d..48b7f907 100644 --- a/src/scheduling/api/solve/schemas.py +++ b/src/scheduling/api/solve/schemas.py @@ -4,7 +4,11 @@ from pydantic import Field, model_validator from scheduling.api.solve.job_models import SolveJobStatus -from scheduling.domain import PlanningMonth, SchedulingBaseModel +from scheduling.domain import PlanningMonth, PlanningUnit, SchedulingBaseModel + + +class SolveOptions(SchedulingBaseModel): + planning_units: tuple[PlanningUnit, ...] class SolveRequest(SchedulingBaseModel): diff --git a/src/scheduling/solver/cp_sat/context.py b/src/scheduling/solver/cp_sat/context.py index 575c58ad..5a2d5233 100644 --- a/src/scheduling/solver/cp_sat/context.py +++ b/src/scheduling/solver/cp_sat/context.py @@ -31,13 +31,7 @@ def create_context(dataset: SchedulingDataset) -> SolverContext: ) -def add_objective_term( - ctx: SolverContext, - *, - name: str, - expression: cp_model.LinearExpr, - weight: int = 1, -) -> None: +def add_objective_term(ctx: SolverContext, *, expression: cp_model.LinearExpr, weight: int = 1) -> None: """Register one weighted objective term for minimization.""" if weight == 0: return diff --git a/src/scheduling/solver/cp_sat/objectives/balance_generated_assignments.py b/src/scheduling/solver/cp_sat/objectives/balance_generated_assignments.py index fca72628..57d9daf5 100644 --- a/src/scheduling/solver/cp_sat/objectives/balance_generated_assignments.py +++ b/src/scheduling/solver/cp_sat/objectives/balance_generated_assignments.py @@ -34,9 +34,4 @@ def add_balance_assignments_objective(ctx: SolverContext) -> None: generated_counts, ).with_name("temporary_balance_generated_assignments__define_max_per_employee") - add_objective_term( - ctx, - name="temporary_balance_generated_assignments", - expression=max_generated_assignments, - weight=1, - ) + add_objective_term(ctx, expression=max_generated_assignments, weight=1) diff --git a/src/scheduling/timeoffice/mapping/dataset.py b/src/scheduling/timeoffice/mapping/dataset.py index dc01fd92..1a3a0b9c 100644 --- a/src/scheduling/timeoffice/mapping/dataset.py +++ b/src/scheduling/timeoffice/mapping/dataset.py @@ -1,4 +1,4 @@ -from scheduling.domain import SchedulingDataset +from scheduling.domain import PlanningMonth, SchedulingDataset from scheduling.timeoffice.facts import TimeOfficeFacts from scheduling.timeoffice.mapping.demand import map_demand_requirements from scheduling.timeoffice.mapping.personnel import map_employees, map_planning_unit_memberships @@ -11,13 +11,18 @@ from scheduling.timeoffice.reading.container import TimeOfficeSources -def map_scheduling_dataset(*, sources: TimeOfficeSources, facts: TimeOfficeFacts) -> SchedulingDataset: +def map_scheduling_dataset( + *, + sources: TimeOfficeSources, + facts: TimeOfficeFacts, + planning_month: PlanningMonth, +) -> SchedulingDataset: planning_units = map_planning_units(sources.planning_unit_rows, facts=facts) plans = map_plans(sources.planning_unit_rows) shifts = map_shifts(sources.shift_rows, facts=facts) return SchedulingDataset( - planning_month=sources.planning_month, + planning_month=planning_month, planning_units=planning_units, plans=plans, employees=map_employees(sources.employee_rows, facts=facts), @@ -37,7 +42,7 @@ def map_scheduling_dataset(*, sources: TimeOfficeSources, facts: TimeOfficeFacts facts=facts, ), demand_requirements=map_demand_requirements( - planning_month=sources.planning_month, + planning_month=planning_month, planning_units=planning_units, facts=facts, ), diff --git a/src/scheduling/timeoffice/mapping/options.py b/src/scheduling/timeoffice/mapping/options.py new file mode 100644 index 00000000..eb6e2b03 --- /dev/null +++ b/src/scheduling/timeoffice/mapping/options.py @@ -0,0 +1,30 @@ +from scheduling.api.solve.schemas import SolveOptions +from scheduling.domain.planning_unit import PlanningUnit, PlanningUnitType +from scheduling.timeoffice.facts import TimeOfficeFacts +from scheduling.timeoffice.reading.options import TimeOfficePlanningUnitOptionRow + + +def map_solve_options(*, rows: tuple[TimeOfficePlanningUnitOptionRow, ...], facts: TimeOfficeFacts) -> SolveOptions: + rows_by_planning_unit_id = {row.planning_unit_id: row for row in rows} + + planning_units: list[PlanningUnit] = [] + + for planning_unit_id, planning_unit_type in sorted(facts.planning_unit_type_by_id.items()): + if planning_unit_type != PlanningUnitType.STATION: + continue + + row = rows_by_planning_unit_id.get(planning_unit_id) + if row is None: + raise ValueError( + f"Configured planning unit does not exist in TimeOffice: planning_unit_id={planning_unit_id}." + ) + + planning_units.append( + PlanningUnit( + planning_unit_id=planning_unit_id, + display_name=row.planning_unit_code or f"Planning unit {planning_unit_id}", + type=planning_unit_type, + ) + ) + + return SolveOptions(planning_units=tuple(planning_units)) diff --git a/src/scheduling/timeoffice/reading/container.py b/src/scheduling/timeoffice/reading/container.py index 5b6f7511..cf923a5d 100644 --- a/src/scheduling/timeoffice/reading/container.py +++ b/src/scheduling/timeoffice/reading/container.py @@ -4,6 +4,7 @@ from scheduling.domain import PlanningMonth from scheduling.timeoffice.facts import TimeOfficeFacts +from scheduling.timeoffice.reading.options import TimeOfficeOptionsReader from scheduling.timeoffice.reading.personnel import ( TimeOfficeEmployeeRow, TimeOfficePersonnelReader, @@ -13,10 +14,7 @@ from scheduling.timeoffice.reading.planning_units import TimeOfficePlanningUnitReader, TimeOfficePlanningUnitRow from scheduling.timeoffice.reading.roster import TimeOfficeRosterReader, TimeOfficeRosterRow from scheduling.timeoffice.reading.shifts import TimeOfficeShiftReader, TimeOfficeShiftRow -from scheduling.timeoffice.reading.sunday_work import ( - TimeOfficeSundayHistoryRow, - TimeOfficeSundayWorkHistoryReader, -) +from scheduling.timeoffice.reading.sunday_work import TimeOfficeSundayHistoryRow, TimeOfficeSundayWorkHistoryReader from scheduling.timeoffice.reading.wishes import TimeOfficeWishReader, TimeOfficeWishRow from scheduling.timeoffice.reading.work_accounts import ( TimeOfficeMonthlyWorkAccountReader, @@ -28,8 +26,6 @@ class TimeOfficeSources: """TimeOffice source rows for one selected planning month.""" - planning_month: PlanningMonth - planning_unit_rows: tuple[TimeOfficePlanningUnitRow, ...] # TimeOffice plan artifact. Not canonical solver input. @@ -47,6 +43,7 @@ class TimeOfficeSources: @dataclass(frozen=True, slots=True) class TimeOfficeReaders: + options: TimeOfficeOptionsReader planning_units: TimeOfficePlanningUnitReader personnel: TimeOfficePersonnelReader shifts: TimeOfficeShiftReader @@ -58,6 +55,7 @@ class TimeOfficeReaders: @classmethod def create(cls, *, facts: TimeOfficeFacts) -> "TimeOfficeReaders": return cls( + options=TimeOfficeOptionsReader(facts=facts), planning_units=TimeOfficePlanningUnitReader(facts=facts), personnel=TimeOfficePersonnelReader(), shifts=TimeOfficeShiftReader(facts=facts), @@ -133,7 +131,6 @@ def read_sources( ) return TimeOfficeSources( - planning_month=planning_month, planning_unit_rows=planning_unit_rows, plan_personnel_rows=plan_personnel_rows, employee_rows=employee_rows, diff --git a/src/scheduling/timeoffice/reading/options.py b/src/scheduling/timeoffice/reading/options.py new file mode 100644 index 00000000..e9dcacd8 --- /dev/null +++ b/src/scheduling/timeoffice/reading/options.py @@ -0,0 +1,48 @@ +from sqlalchemy import bindparam, text +from sqlalchemy.engine import Connection + +from scheduling.timeoffice.facts import TimeOfficeFacts +from scheduling.timeoffice.reading.types import CleanNullableText, SourceInt, TimeOfficeSourceRow + + +class TimeOfficePlanningUnitOptionRow(TimeOfficeSourceRow): + planning_unit_id: SourceInt + planning_unit_code: CleanNullableText = None + + +class TimeOfficeOptionsReader: + def __init__(self, *, facts: TimeOfficeFacts) -> None: + self._facts = facts + + def read_planning_unit_option_rows( + self, + *, + connection: Connection, + ) -> tuple[TimeOfficePlanningUnitOptionRow, ...]: + planning_unit_ids = tuple(sorted(self._facts.planning_unit_type_by_id)) + + if not planning_unit_ids: + return () + + query = text( + """ + SELECT + pe.Prim AS planning_unit_id, + pe.KurzBez AS planning_unit_code + FROM TPlanungseinheiten pe + WHERE pe.Prim IN :planning_unit_ids + ORDER BY + pe.Prim + """ + ).bindparams(bindparam("planning_unit_ids", expanding=True)) + + raw_rows = ( + connection.execute( + query, + {"planning_unit_ids": planning_unit_ids}, + ) + .mappings() + .all() + ) + + return tuple(TimeOfficePlanningUnitOptionRow.model_validate(row) for row in raw_rows) diff --git a/src/scheduling/timeoffice/service.py b/src/scheduling/timeoffice/service.py index ac8eba67..fb3c759b 100644 --- a/src/scheduling/timeoffice/service.py +++ b/src/scheduling/timeoffice/service.py @@ -2,10 +2,12 @@ from sqlalchemy import Engine +from scheduling.api.solve.schemas import SolveOptions from scheduling.domain import PlanningMonth, SchedulingDataset from scheduling.solver.models import Solution from scheduling.timeoffice.facts import TimeOfficeFacts from scheduling.timeoffice.mapping import map_scheduling_dataset +from scheduling.timeoffice.mapping.options import map_solve_options from scheduling.timeoffice.reading.container import TimeOfficeReaders from scheduling.timeoffice.writing.solution import TimeOfficeSolutionWriter from scheduling.validation import validate_scheduling_dataset @@ -29,6 +31,21 @@ def __init__( self._readers = readers self._solution_writer = solution_writer + def get_solve_options(self) -> SolveOptions: + logger.info("Fetching TimeOffice solve options") + + with self._engine.connect() as connection: + rows = self._readers.options.read_planning_unit_option_rows(connection=connection) + + options = map_solve_options(rows=rows, facts=self._facts) + + logger.info( + "Fetched TimeOffice solve options: planning_units=%s", + len(options.planning_units), + ) + + return options + def fetch_dataset( self, *, @@ -53,6 +70,7 @@ def fetch_dataset( dataset = map_scheduling_dataset( sources=sources, facts=self._facts, + planning_month=planning_month, ) validated_dataset = validate_scheduling_dataset(dataset) From b851e94f9e6d19309d8ac1995a06a5347f5d207d Mon Sep 17 00:00:00 2001 From: Tom Wawerek Date: Wed, 24 Jun 2026 14:10:03 +0200 Subject: [PATCH 14/18] refactor: constraint and objective classes --- legacy/src/main.py | 67 ++--- src/SELECT.sql | 27 +- src/scheduling/api/app.py | 8 +- src/scheduling/solver/audit.py | 30 +++ src/scheduling/solver/config.py | 44 ++++ src/scheduling/solver/cp_sat/builder.py | 231 ++++++++++++++++++ src/scheduling/solver/cp_sat/constraint.py | 29 +++ .../solver/cp_sat/constraints/__init__.py | 0 .../cp_sat/constraints/minimum_staffing.py | 151 +++++++----- src/scheduling/solver/cp_sat/context.py | 19 +- src/scheduling/solver/cp_sat/eligibility.py | 2 +- src/scheduling/solver/cp_sat/inspection.py | 4 +- src/scheduling/solver/cp_sat/keys.py | 15 +- src/scheduling/solver/cp_sat/objective.py | 62 +++++ .../solver/cp_sat/objectives/__init__.py | 0 .../balance_generated_assignments.py | 37 --- ...temporary_balance_generated_assignments.py | 61 +++++ src/scheduling/solver/diagnostics.py | 22 ++ src/scheduling/solver/{cp_sat => }/index.py | 39 --- src/scheduling/solver/models.py | 7 +- src/scheduling/solver/service.py | 132 ++++++---- src/scheduling/timeoffice/facts.py | 12 +- src/scheduling/timeoffice/mapping/roster.py | 64 +++-- 23 files changed, 766 insertions(+), 297 deletions(-) create mode 100644 src/scheduling/solver/audit.py create mode 100644 src/scheduling/solver/config.py create mode 100644 src/scheduling/solver/cp_sat/builder.py create mode 100644 src/scheduling/solver/cp_sat/constraint.py delete mode 100644 src/scheduling/solver/cp_sat/constraints/__init__.py create mode 100644 src/scheduling/solver/cp_sat/objective.py delete mode 100644 src/scheduling/solver/cp_sat/objectives/__init__.py delete mode 100644 src/scheduling/solver/cp_sat/objectives/balance_generated_assignments.py create mode 100644 src/scheduling/solver/cp_sat/objectives/temporary_balance_generated_assignments.py create mode 100644 src/scheduling/solver/diagnostics.py rename src/scheduling/solver/{cp_sat => }/index.py (70%) diff --git a/legacy/src/main.py b/legacy/src/main.py index bd560f1a..df8be579 100644 --- a/legacy/src/main.py +++ b/legacy/src/main.py @@ -2,57 +2,17 @@ import click -from legacy.src.db.import_main import main as inserter -from legacy.src.loader import FSLoader -from legacy.src.services.solve_service import execute_solve, execute_solve_multiple -from legacy.src.web import App -from src.scheduling.timeoffice.models import PlanningPeriod -from src.scheduling.timeoffice.service import get_timeoffice_service +from src.db.export_main import main as fetcher +from src.db.import_main import main as inserter +from src.loader import FSLoader +from src.services.solve_service import execute_solve, execute_solve_multiple +from src.web import App @click.group() -@click.pass_context -def cli(ctx: click.Context): +def cli(): """Staff Scheduling CLI""" - - -@cli.command() -@click.option( - "--station", - "stations", - multiple=True, - type=int, - required=True, - help="Planning unit/station to fetch. Can be passed multiple times.", -) -@click.argument("start", type=click.DateTime(formats=["%d.%m.%Y"])) -@click.argument("end", type=click.DateTime(formats=["%d.%m.%Y"])) -def fetch( - stations: tuple[int, ...], - start: datetime, - end: datetime, -): - """Fetch TimeOffice data and update the local cache.""" - - timeoffice = get_timeoffice_service() - dataset = timeoffice.fetch_dataset( - selected_station_ids=stations, - period=PlanningPeriod( - start=start.date(), - end=end.date(), - ), - ) - - print( - "[timeoffice] dataset " - f"stations={len(dataset.stations)} " - f"pools={len(dataset.pools)} " - f"pool_memberships={len(dataset.pool_memberships)} " - f"employees={len(dataset.employees)} " - f"shifts={len(dataset.shifts)} " - f"assignments={len(dataset.assignments)} " - f"availability={len(dataset.availability)}" - ) + pass @cli.command() @@ -140,6 +100,19 @@ def plot(case: int, debug: bool): app.run(debug=debug) +@cli.command() +@click.argument("unit", type=click.INT) +@click.argument("start", type=click.DateTime(formats=["%d.%m.%Y"])) +@click.argument("end", type=click.DateTime(formats=["%d.%m.%Y"])) +def fetch(unit: int, start: datetime, end: datetime): + """ + Fetch data from the DB and write Json Files + """ + start_date = start.date() # convert datetime.datetime to datetime.date + end_date = end.date() + fetcher(planning_unit=unit, from_date=start_date, till_date=end_date) + + @cli.command() @click.argument("unit", type=click.INT) @click.argument("start", type=click.DateTime(formats=["%d.%m.%Y"])) diff --git a/src/SELECT.sql b/src/SELECT.sql index 415cbc98..4c0b68e4 100644 --- a/src/SELECT.sql +++ b/src/SELECT.sql @@ -1,23 +1,6 @@ SELECT - pep.RefPlanungseinheiten AS planning_unit_id, - b.KurzBez AS profession_code, - b.Bezeichnung AS profession_name, - COUNT(DISTINCT pep.RefPersonal) AS employee_count, - STRING_AGG(CONVERT(varchar(20), pep.RefPersonal), ', ') AS employee_ids -FROM TPlanungseinheitenPersonal pep -JOIN TBerufe b - ON b.Prim = pep.RefBerufe -WHERE pep.RefPlanungseinheiten IN (77, 78) - AND CONVERT(date, pep.VonDat) <= '2024-11-30' - AND ( - pep.BisDat IS NULL - OR CONVERT(date, pep.BisDat) >= '2024-11-01' - ) - AND ISNULL(pep.KeinEPlan, 0) = 0 -GROUP BY - pep.RefPlanungseinheiten, - b.KurzBez, - b.Bezeichnung -ORDER BY - pep.RefPlanungseinheiten, - b.KurzBez; + COLUMN_NAME, + DATA_TYPE +FROM INFORMATION_SCHEMA.COLUMNS +WHERE TABLE_NAME = 'TDienstCodes' +ORDER BY ORDINAL_POSITION; diff --git a/src/scheduling/api/app.py b/src/scheduling/api/app.py index 7597b121..24610114 100644 --- a/src/scheduling/api/app.py +++ b/src/scheduling/api/app.py @@ -11,6 +11,7 @@ from scheduling.api.web.router import web_router from scheduling.logging import configure_logging from scheduling.settings import get_settings +from scheduling.solver.cp_sat.builder import create_cp_sat_model_builder from scheduling.solver.service import SolverService from scheduling.timeoffice.database import create_db_engine from scheduling.timeoffice.facts import TIMEOFFICE_FACTS @@ -29,6 +30,8 @@ async def lifespan(app: FastAPI) -> AsyncIterator[None]: engine = create_db_engine(settings=settings) facts = TIMEOFFICE_FACTS + model_builder = create_cp_sat_model_builder() + app.state.runtime = ApiRuntime( timeoffice_service=TimeOfficeService( facts=facts, @@ -36,7 +39,10 @@ async def lifespan(app: FastAPI) -> AsyncIterator[None]: readers=TimeOfficeReaders.create(facts=facts), solution_writer=TimeOfficeSolutionWriter(), ), - solver_service=SolverService(settings=settings), + solver_service=SolverService( + settings=settings, + model_builder=model_builder, + ), solve_job_store=InMemorySolveJobStore(), solve_lock=asyncio.Lock(), ) diff --git a/src/scheduling/solver/audit.py b/src/scheduling/solver/audit.py new file mode 100644 index 00000000..4b253b97 --- /dev/null +++ b/src/scheduling/solver/audit.py @@ -0,0 +1,30 @@ +from datetime import date as Date +from enum import StrEnum + +from pydantic import Field + +from scheduling.domain import SchedulingBaseModel + + +class AuditSeverity(StrEnum): + INFO = "info" + WARNING = "warning" + ERROR = "error" + + +class AuditFinding(SchedulingBaseModel): + """Post-solve finding about the generated schedule.""" + + code: str + severity: AuditSeverity + message: str + source_id: str + planning_unit_id: int | None = None + employee_id: int | None = None + date: Date | None = None + shift_id: int | None = None + staff_level: str | None = None + + +class AuditReport(SchedulingBaseModel): + findings: tuple[AuditFinding, ...] = Field(default_factory=tuple) diff --git a/src/scheduling/solver/config.py b/src/scheduling/solver/config.py new file mode 100644 index 00000000..8acb5c2a --- /dev/null +++ b/src/scheduling/solver/config.py @@ -0,0 +1,44 @@ +from typing import Any + +from pydantic import Field + +from scheduling.domain import SchedulingBaseModel +from scheduling.solver.cp_sat.constraints.minimum_staffing import MinimumStaffing +from scheduling.solver.cp_sat.objectives.temporary_balance_generated_assignments import ( + TemporaryBalanceGeneratedAssignments, +) + + +class ConstraintConfig(SchedulingBaseModel): + enabled: bool + params: dict[str, Any] = Field(default_factory=dict) + + +class ObjectiveConfig(SchedulingBaseModel): + enabled: bool + weight: int + params: dict[str, Any] = Field(default_factory=dict) + + +class SolverConfig(SchedulingBaseModel): + constraints: dict[str, ConstraintConfig] + objectives: dict[str, ObjectiveConfig] + + +def create_base_solver_config() -> SolverConfig: + """Create the deliberately configured baseline solver setup. + + This is the current stable solver behavior. It is explicit on purpose: + every registered constraint/objective must appear here. + """ + return SolverConfig( + constraints={ + MinimumStaffing.id: ConstraintConfig(enabled=True), + }, + objectives={ + TemporaryBalanceGeneratedAssignments.id: ObjectiveConfig( + enabled=True, + weight=1, + ), + }, + ) diff --git a/src/scheduling/solver/cp_sat/builder.py b/src/scheduling/solver/cp_sat/builder.py new file mode 100644 index 00000000..eec7296d --- /dev/null +++ b/src/scheduling/solver/cp_sat/builder.py @@ -0,0 +1,231 @@ +from collections.abc import Iterable, Mapping +from dataclasses import dataclass +from typing import Any + +from scheduling.domain import SchedulingDataset +from scheduling.solver.config import SolverConfig, create_base_solver_config +from scheduling.solver.cp_sat.constraint import Constraint +from scheduling.solver.cp_sat.constraints.minimum_staffing import MinimumStaffing +from scheduling.solver.cp_sat.context import SolverContext, create_context +from scheduling.solver.cp_sat.objective import Objective, WeightedPenalty, minimize_weighted_penalties +from scheduling.solver.cp_sat.objectives.temporary_balance_generated_assignments import ( + TemporaryBalanceGeneratedAssignments, +) +from scheduling.solver.cp_sat.variables import create_assignment_variables + +CP_SAT_CONSTRAINTS: tuple[Constraint, ...] = (MinimumStaffing(),) + +CP_SAT_OBJECTIVES: tuple[Objective, ...] = (TemporaryBalanceGeneratedAssignments(),) + + +@dataclass(frozen=True, slots=True) +class ResolvedConstraint: + constraint: Constraint + enabled: bool + params: Mapping[str, Any] + + +@dataclass(frozen=True, slots=True) +class ResolvedObjective: + objective: Objective + enabled: bool + weight: int + params: Mapping[str, Any] + + +@dataclass(frozen=True, slots=True) +class CpSatBuildResult: + ctx: SolverContext + constraints: tuple[ResolvedConstraint, ...] + objectives: tuple[ResolvedObjective, ...] + weighted_penalty_count: int + has_objective: bool + + @property + def applied_constraint_ids(self) -> tuple[str, ...]: + return tuple(resolved.constraint.id for resolved in self.constraints if resolved.enabled) + + @property + def applied_objective_ids(self) -> tuple[str, ...]: + return tuple(resolved.objective.id for resolved in self.objectives if resolved.enabled) + + +@dataclass(frozen=True, slots=True) +class CpSatModelBuilder: + """Build CP-SAT models from configured stateless constraints and objectives.""" + + constraints: tuple[Constraint, ...] + objectives: tuple[Objective, ...] + config: SolverConfig + + def build(self, dataset: SchedulingDataset) -> CpSatBuildResult: + ctx = create_context(dataset=dataset) + + create_assignment_variables(ctx) + + resolved_constraints = resolve_constraints( + constraints=self.constraints, + config=self.config, + ) + resolved_objectives = resolve_objectives( + objectives=self.objectives, + config=self.config, + ) + + weighted_penalties: list[WeightedPenalty] = [] + + for resolved in resolved_constraints: + if not resolved.enabled: + continue + + diagnostics = resolved.constraint.add_to_model(ctx, params=resolved.params) + ctx.diagnostics.extend(diagnostics) + + for resolved in resolved_objectives: + if not resolved.enabled: + continue + + penalties = resolved.objective.add_to_model(ctx, params=resolved.params) + + weighted_penalties.extend( + WeightedPenalty( + penalty=penalty, + weight=resolved.weight, + ) + for penalty in penalties + ) + + has_objective = minimize_weighted_penalties( + model=ctx.model, + penalties=tuple(weighted_penalties), + ) + + return CpSatBuildResult( + ctx=ctx, + constraints=resolved_constraints, + objectives=resolved_objectives, + weighted_penalty_count=len(weighted_penalties), + has_objective=has_objective, + ) + + +def create_cp_sat_model_builder() -> CpSatModelBuilder: + config = create_base_solver_config() + + return CpSatModelBuilder( + constraints=CP_SAT_CONSTRAINTS, + objectives=CP_SAT_OBJECTIVES, + config=config, + ) + + +def resolve_constraints( + *, + constraints: tuple[Constraint, ...], + config: SolverConfig, +) -> tuple[ResolvedConstraint, ...]: + constraints_by_id = _constraints_by_id(constraints) + + _validate_config_keys( + configured_ids=config.constraints.keys(), + registered_ids=constraints_by_id.keys(), + kind="constraint", + ) + + resolved: list[ResolvedConstraint] = [] + + for constraint in constraints: + raw_config = config.constraints[constraint.id] + + if not raw_config.enabled and constraint.required: + raise ValueError(f"Required constraint cannot be disabled: {constraint.id}") + + resolved.append( + ResolvedConstraint( + constraint=constraint, + enabled=raw_config.enabled, + params=raw_config.params, + ) + ) + + return tuple(resolved) + + +def resolve_objectives( + *, + objectives: tuple[Objective, ...], + config: SolverConfig, +) -> tuple[ResolvedObjective, ...]: + objectives_by_id = _objectives_by_id(objectives) + + _validate_config_keys( + configured_ids=config.objectives.keys(), + registered_ids=objectives_by_id.keys(), + kind="objective", + ) + + resolved: list[ResolvedObjective] = [] + + for objective in objectives: + raw_config = config.objectives[objective.id] + + if raw_config.enabled and raw_config.weight <= 0: + raise ValueError( + f"Enabled objective must have a positive weight: {objective.id} weight={raw_config.weight}" + ) + + resolved.append( + ResolvedObjective( + objective=objective, + enabled=raw_config.enabled, + weight=raw_config.weight, + params=raw_config.params, + ) + ) + + return tuple(resolved) + + +def _constraints_by_id(constraints: tuple[Constraint, ...]) -> dict[str, Constraint]: + by_id: dict[str, Constraint] = {} + + for constraint in constraints: + if constraint.id in by_id: + raise ValueError(f"Duplicate constraint id registered: {constraint.id}") + + by_id[constraint.id] = constraint + + return by_id + + +def _objectives_by_id(objectives: tuple[Objective, ...]) -> dict[str, Objective]: + by_id: dict[str, Objective] = {} + + for objective in objectives: + if objective.id in by_id: + raise ValueError(f"Duplicate objective id registered: {objective.id}") + + by_id[objective.id] = objective + + return by_id + + +def _validate_config_keys( + *, + configured_ids: Iterable[str], + registered_ids: Iterable[str], + kind: str, +) -> None: + configured = set(configured_ids) + registered = set(registered_ids) + + unknown_ids = configured - registered + missing_ids = registered - configured + + if unknown_ids: + unknown = ", ".join(sorted(unknown_ids)) + raise ValueError(f"Unknown solver {kind} config id(s): {unknown}") + + if missing_ids: + missing = ", ".join(sorted(missing_ids)) + raise ValueError(f"Missing solver {kind} config id(s): {missing}") diff --git a/src/scheduling/solver/cp_sat/constraint.py b/src/scheduling/solver/cp_sat/constraint.py new file mode 100644 index 00000000..9065fb48 --- /dev/null +++ b/src/scheduling/solver/cp_sat/constraint.py @@ -0,0 +1,29 @@ +from collections.abc import Mapping +from typing import Any, ClassVar, Protocol + +from scheduling.solver.audit import AuditFinding +from scheduling.solver.cp_sat.context import AuditContext, SolverContext +from scheduling.solver.diagnostics import SolverDiagnostic + + +class Constraint(Protocol): + """Hard CP-SAT model component. + + Constraints define what schedules are allowed. They may be configurable via + params, but they do not have weights. + """ + + id: ClassVar[str] + required: ClassVar[bool] + + def add_to_model( + self, + ctx: SolverContext, + params: Mapping[str, Any], + ) -> tuple[SolverDiagnostic, ...]: ... + + def audit( + self, + ctx: AuditContext, + params: Mapping[str, Any], + ) -> tuple[AuditFinding, ...]: ... diff --git a/src/scheduling/solver/cp_sat/constraints/__init__.py b/src/scheduling/solver/cp_sat/constraints/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/src/scheduling/solver/cp_sat/constraints/minimum_staffing.py b/src/scheduling/solver/cp_sat/constraints/minimum_staffing.py index 6f93cdc4..c75adb1b 100644 --- a/src/scheduling/solver/cp_sat/constraints/minimum_staffing.py +++ b/src/scheduling/solver/cp_sat/constraints/minimum_staffing.py @@ -1,46 +1,82 @@ from collections import defaultdict +from collections.abc import Mapping +from typing import Any, ClassVar from ortools.sat.python import cp_model -from scheduling.solver.cp_sat.context import SolverContext +from scheduling.solver.audit import AuditFinding, AuditSeverity +from scheduling.solver.cp_sat.context import AuditContext, SolverContext from scheduling.solver.cp_sat.keys import AssignmentVariableKey, DemandKey +from scheduling.solver.diagnostics import DiagnosticSeverity, SolverDiagnostic -def add_minimum_staffing_constraints(ctx: SolverContext) -> None: +class MinimumStaffing: """Cover minimum staffing requirements from SchedulingDataset demand. - This is the primary dataset-driven hard constraint. It should remain part of - the final solver model. + Demand describes the absolute minimum. Overstaffing is intentionally allowed. """ - vars_by_demand = _group_vars_by_demand(ctx) - for demand_key, required_count in ctx.index.required_count_by_demand_key.items(): - fixed_count = ctx.index.fixed_planned_count_by_demand_key.get(demand_key, 0) - remaining_required = required_count - fixed_count + id: ClassVar[str] = "minimum_staffing" + required: ClassVar[bool] = True - if remaining_required <= 0: - if fixed_count > required_count: - ctx.diagnostics.append( - _overcovered_message( + def add_to_model( + self, + ctx: SolverContext, + params: Mapping[str, Any], + ) -> tuple[SolverDiagnostic, ...]: + diagnostics: list[SolverDiagnostic] = [] + vars_by_demand = _group_vars_by_demand(ctx) + + for demand_key, required_count in ctx.index.required_count_by_demand_key.items(): + variables = vars_by_demand.get(demand_key, []) + + if len(variables) < required_count: + diagnostics.append( + _not_enough_candidates_diagnostic( demand_key=demand_key, required_count=required_count, - fixed_count=fixed_count, + candidate_count=len(variables), ) ) - continue - variables = vars_by_demand.get(demand_key, []) - - if not variables: - ctx.diagnostics.append( - _missing_candidates_message( - demand_key=demand_key, - remaining_required=remaining_required, + ctx.model.add(sum(variables) >= required_count).with_name(_minimum_staffing_constraint_name(demand_key)) + + return tuple(diagnostics) + + def audit( + self, + ctx: AuditContext, + params: Mapping[str, Any], + ) -> tuple[AuditFinding, ...]: + del params + + findings: list[AuditFinding] = [] + actual_by_demand = _count_actual_assignments_by_demand(ctx) + + for demand_key, required_count in ctx.index.required_count_by_demand_key.items(): + actual_count = actual_by_demand.get(demand_key, 0) + + if actual_count >= required_count: + continue + + planning_unit_id, demand_date, shift_id, staff_level = demand_key + findings.append( + AuditFinding( + code="minimum_staffing.uncovered", + severity=AuditSeverity.ERROR, + source_id=self.id, + message=( + "Minimum staffing demand is not covered " + f"required_count={required_count} actual_count={actual_count}." + ), + planning_unit_id=planning_unit_id, + date=demand_date, + shift_id=shift_id, + staff_level=staff_level.value, ) ) - constraint_name = _minimum_staffing_constraint_name(demand_key) - ctx.model.add(sum(variables) >= remaining_required).with_name(constraint_name) + return tuple(findings) def _minimum_staffing_constraint_name(demand_key: DemandKey) -> str: @@ -68,46 +104,49 @@ def _group_vars_by_demand( def _demand_key_from_assignment_key(key: AssignmentVariableKey) -> DemandKey: _, planning_unit_id, assignment_date, shift_id, staff_level = key - - return ( - planning_unit_id, - assignment_date, - shift_id, - staff_level, - ) + return planning_unit_id, assignment_date, shift_id, staff_level -def _missing_candidates_message( +def _not_enough_candidates_diagnostic( *, demand_key: DemandKey, - remaining_required: int, -) -> str: + required_count: int, + candidate_count: int, +) -> SolverDiagnostic: planning_unit_id, demand_date, shift_id, staff_level = demand_key - return ( - "No eligible candidates for demand " - f"planning_unit_id={planning_unit_id} " - f"date={demand_date.isoformat()} " - f"shift_id={shift_id} " - f"staff_level={staff_level.value} " - f"remaining_required={remaining_required}." + return SolverDiagnostic( + code="minimum_staffing.not_enough_candidates", + severity=DiagnosticSeverity.ERROR, + message=( + "Minimum staffing demand has too few eligible candidates " + f"planning_unit_id={planning_unit_id} " + f"date={demand_date.isoformat()} " + f"shift_id={shift_id} " + f"staff_level={staff_level.value} " + f"required_count={required_count} " + f"candidate_count={candidate_count}." + ), ) -def _overcovered_message( - *, - demand_key: DemandKey, - required_count: int, - fixed_count: int, -) -> str: - planning_unit_id, demand_date, shift_id, staff_level = demand_key +def _count_actual_assignments_by_demand(ctx: AuditContext) -> dict[DemandKey, int]: + actual: defaultdict[DemandKey, int] = defaultdict(int) - return ( - "Existing planned assignments exceed demand " - f"planning_unit_id={planning_unit_id} " - f"date={demand_date.isoformat()} " - f"shift_id={shift_id} " - f"staff_level={staff_level.value} " - f"required_count={required_count} " - f"fixed_count={fixed_count}." - ) + for assignment in ctx.assignments: + if assignment.planning_unit_id is None: + continue + + employee = ctx.index.employees_by_id.get(assignment.employee_id) + if employee is None: + continue + + key = ( + assignment.planning_unit_id, + assignment.date, + assignment.shift_id, + employee.staff_level, + ) + actual[key] += 1 + + return dict(actual) diff --git a/src/scheduling/solver/cp_sat/context.py b/src/scheduling/solver/cp_sat/context.py index 5a2d5233..68fd390a 100644 --- a/src/scheduling/solver/cp_sat/context.py +++ b/src/scheduling/solver/cp_sat/context.py @@ -3,8 +3,10 @@ from ortools.sat.python import cp_model from scheduling.domain import SchedulingDataset -from scheduling.solver.cp_sat.index import SolverIndex, build_schedule_index +from scheduling.domain.assignment import Assignment from scheduling.solver.cp_sat.keys import AssignmentVariableKey +from scheduling.solver.diagnostics import SolverDiagnostic +from scheduling.solver.index import SolverIndex, build_schedule_index @dataclass(slots=True) @@ -13,8 +15,7 @@ class SolverContext: index: SolverIndex model: cp_model.CpModel assignment_variables: dict[AssignmentVariableKey, cp_model.IntVar] - objective_terms: list[cp_model.LinearExpr] - diagnostics: list[str] + diagnostics: list[SolverDiagnostic] def create_context(dataset: SchedulingDataset) -> SolverContext: @@ -26,14 +27,14 @@ def create_context(dataset: SchedulingDataset) -> SolverContext: index=index, model=cp_model.CpModel(), assignment_variables={}, - objective_terms=[], diagnostics=[], ) -def add_objective_term(ctx: SolverContext, *, expression: cp_model.LinearExpr, weight: int = 1) -> None: - """Register one weighted objective term for minimization.""" - if weight == 0: - return +@dataclass(frozen=True, slots=True) +class AuditContext: + """Post-solve context passed to constraints and objectives for audit.""" - ctx.objective_terms.append(expression * weight) + dataset: SchedulingDataset + index: SolverIndex + assignments: tuple[Assignment, ...] diff --git a/src/scheduling/solver/cp_sat/eligibility.py b/src/scheduling/solver/cp_sat/eligibility.py index 007e8b1c..8b3aad9c 100644 --- a/src/scheduling/solver/cp_sat/eligibility.py +++ b/src/scheduling/solver/cp_sat/eligibility.py @@ -4,7 +4,7 @@ from scheduling.domain.employee import Employee, StaffLevel from scheduling.domain.planning_unit import PlanningUnitId from scheduling.domain.shift import ShiftId -from scheduling.solver.cp_sat.index import SolverIndex +from scheduling.solver.index import SolverIndex def eligible_staff_levels_for_assignment_slot( diff --git a/src/scheduling/solver/cp_sat/inspection.py b/src/scheduling/solver/cp_sat/inspection.py index 35f18c7d..da68e5b9 100644 --- a/src/scheduling/solver/cp_sat/inspection.py +++ b/src/scheduling/solver/cp_sat/inspection.py @@ -62,9 +62,7 @@ def _constraint_name(constraint: _NamedProto) -> str: return constraint.name or "" -def _constraint_type_counts_from_model_stats( - model_stats: str, -) -> dict[str, int]: +def _constraint_type_counts_from_model_stats(model_stats: str) -> dict[str, int]: counts: dict[str, int] = {} for line in model_stats.splitlines(): diff --git a/src/scheduling/solver/cp_sat/keys.py b/src/scheduling/solver/cp_sat/keys.py index 6a63c392..9783cd08 100644 --- a/src/scheduling/solver/cp_sat/keys.py +++ b/src/scheduling/solver/cp_sat/keys.py @@ -4,20 +4,9 @@ from scheduling.domain.planning_unit import PlanningUnitId from scheduling.domain.shift import ShiftId -type AssignmentVariableKey = tuple[ - EmployeeId, - PlanningUnitId, - date, - ShiftId, - StaffLevel, -] +type AssignmentVariableKey = tuple[EmployeeId, PlanningUnitId, date, ShiftId, StaffLevel] -type DemandKey = tuple[ - PlanningUnitId, - date, - ShiftId, - StaffLevel, -] +type DemandKey = tuple[PlanningUnitId, date, ShiftId, StaffLevel] type EmployeeDateKey = tuple[EmployeeId, date] type MembershipKey = tuple[EmployeeId, PlanningUnitId] diff --git a/src/scheduling/solver/cp_sat/objective.py b/src/scheduling/solver/cp_sat/objective.py new file mode 100644 index 00000000..924be50b --- /dev/null +++ b/src/scheduling/solver/cp_sat/objective.py @@ -0,0 +1,62 @@ +from collections.abc import Mapping +from dataclasses import dataclass +from typing import Any, ClassVar, Protocol + +from ortools.sat.python import cp_model + +from scheduling.solver.audit import AuditFinding +from scheduling.solver.cp_sat.context import AuditContext, SolverContext + + +@dataclass(frozen=True, slots=True) +class Penalty: + """Raw penalty produced by one objective. + + The objective creates the expression. The model builder applies the global + objective weight centrally so objectives do not read global configuration. + """ + + objective_id: str + name: str + expression: cp_model.LinearExpr + multiplier: int = 1 + + +class Objective(Protocol): + """Soft CP-SAT model component. + + Objectives may add helper variables/constraints, but they return raw + penalties. Global weights are applied centrally by the model builder. + """ + + id: ClassVar[str] + + def add_to_model( + self, + ctx: SolverContext, + params: Mapping[str, Any], + ) -> tuple[Penalty, ...]: ... + + def audit( + self, + ctx: AuditContext, + params: Mapping[str, Any], + ) -> tuple[AuditFinding, ...]: ... + + +@dataclass(frozen=True, slots=True) +class WeightedPenalty: + penalty: Penalty + weight: int + + +def minimize_weighted_penalties( + *, + model: cp_model.CpModel, + penalties: tuple[WeightedPenalty, ...], +) -> bool: + if not penalties: + return False + + model.minimize(sum(item.weight * item.penalty.multiplier * item.penalty.expression for item in penalties)) + return True diff --git a/src/scheduling/solver/cp_sat/objectives/__init__.py b/src/scheduling/solver/cp_sat/objectives/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/src/scheduling/solver/cp_sat/objectives/balance_generated_assignments.py b/src/scheduling/solver/cp_sat/objectives/balance_generated_assignments.py deleted file mode 100644 index 57d9daf5..00000000 --- a/src/scheduling/solver/cp_sat/objectives/balance_generated_assignments.py +++ /dev/null @@ -1,37 +0,0 @@ -from collections import defaultdict - -from ortools.sat.python import cp_model - -from scheduling.solver.cp_sat.context import SolverContext, add_objective_term - - -def add_balance_assignments_objective(ctx: SolverContext) -> None: - """Prefer distributing generated assignments across employees. - - Temporary migration objective: - this is a placeholder while the real wish/fairness model is not migrated to - SchedulingDataset yet. Remove or replace this before final solver evaluation. - """ - if not ctx.assignment_variables: - return - - variables_by_employee: defaultdict[int, list[cp_model.IntVar]] = defaultdict(list) - - for key, variable in ctx.assignment_variables.items(): - employee_id, _, _, _, _ = key - variables_by_employee[employee_id].append(variable) - - generated_counts = [sum(variables) for variables in variables_by_employee.values()] - - max_generated_assignments = ctx.model.new_int_var( - 0, - len(ctx.assignment_variables), - "temporary_balance_generated_assignments__max_per_employee", - ) - - ctx.model.add_max_equality( - max_generated_assignments, - generated_counts, - ).with_name("temporary_balance_generated_assignments__define_max_per_employee") - - add_objective_term(ctx, expression=max_generated_assignments, weight=1) diff --git a/src/scheduling/solver/cp_sat/objectives/temporary_balance_generated_assignments.py b/src/scheduling/solver/cp_sat/objectives/temporary_balance_generated_assignments.py new file mode 100644 index 00000000..5743a219 --- /dev/null +++ b/src/scheduling/solver/cp_sat/objectives/temporary_balance_generated_assignments.py @@ -0,0 +1,61 @@ +from collections import defaultdict +from collections.abc import Mapping +from typing import Any, ClassVar + +from ortools.sat.python import cp_model + +from scheduling.solver.audit import AuditFinding +from scheduling.solver.cp_sat.context import AuditContext, SolverContext +from scheduling.solver.cp_sat.objective import Penalty + + +class TemporaryBalanceGeneratedAssignments: + """Temporary migration objective. + + This is intentionally named as temporary. It should be deleted once the real + target-working-time objective exists. + """ + + id: ClassVar[str] = "temporary_balance_generated_assignments" + + def add_to_model( + self, + ctx: SolverContext, + params: Mapping[str, Any], + ) -> tuple[Penalty, ...]: + if not ctx.assignment_variables: + return () + + variables_by_employee: defaultdict[int, list[cp_model.IntVar]] = defaultdict(list) + + for key, variable in ctx.assignment_variables.items(): + employee_id, _, _, _, _ = key + variables_by_employee[employee_id].append(variable) + + generated_counts = [sum(variables) for variables in variables_by_employee.values()] + + max_generated_assignments = ctx.model.new_int_var( + 0, + len(ctx.assignment_variables), + "temporary_balance_generated_assignments__max_per_employee", + ) + + ctx.model.add_max_equality( + max_generated_assignments, + generated_counts, + ).with_name("temporary_balance_generated_assignments__define_max_per_employee") + + return ( + Penalty( + objective_id=self.id, + name="max_generated_assignments_per_employee", + expression=max_generated_assignments, + ), + ) + + def audit( + self, + ctx: AuditContext, + params: Mapping[str, Any], + ) -> tuple[AuditFinding, ...]: + return () diff --git a/src/scheduling/solver/diagnostics.py b/src/scheduling/solver/diagnostics.py new file mode 100644 index 00000000..69c6a5ab --- /dev/null +++ b/src/scheduling/solver/diagnostics.py @@ -0,0 +1,22 @@ +from enum import StrEnum + +from scheduling.domain import SchedulingBaseModel + + +class DiagnosticSeverity(StrEnum): + INFO = "info" + WARNING = "warning" + ERROR = "error" + + +class SolverDiagnostic(SchedulingBaseModel): + """Build/solve-time diagnostic. + + Diagnostics are not the same as post-solve audit findings. They describe + model construction, infeasibility hints, configuration issues, or CP-SAT + validation problems. + """ + + code: str + severity: DiagnosticSeverity + message: str diff --git a/src/scheduling/solver/cp_sat/index.py b/src/scheduling/solver/index.py similarity index 70% rename from src/scheduling/solver/cp_sat/index.py rename to src/scheduling/solver/index.py index 3ee9f12a..2f727a04 100644 --- a/src/scheduling/solver/cp_sat/index.py +++ b/src/scheduling/solver/index.py @@ -26,7 +26,6 @@ class SolverIndex: assignments_by_employee_date: dict[EmployeeDateKey, list[Assignment]] availability_by_employee_date: dict[EmployeeDateKey, list[Availability]] required_count_by_demand_key: dict[DemandKey, int] - fixed_planned_count_by_demand_key: dict[DemandKey, int] def build_schedule_index(dataset: SchedulingDataset) -> SolverIndex: @@ -40,7 +39,6 @@ def build_schedule_index(dataset: SchedulingDataset) -> SolverIndex: assignments_by_employee_date=_group_assignments_by_employee_date(dataset.assignments), availability_by_employee_date=_group_availability_by_employee_date(dataset.availability), required_count_by_demand_key=_count_required_demand_by_key(dataset.demand_requirements), - fixed_planned_count_by_demand_key=_ignore_fixed_planned_assignments(), ) @@ -92,40 +90,3 @@ def _count_required_demand_by_key( required[key] += demand.required_count return dict(required) - - -def _ignore_fixed_planned_assignments() -> dict[DemandKey, int]: - """Ignore imported TimeOffice assignments during solver migration. - - Temporary migration behavior: - existing assignments are still part of the imported SchedulingDataset, but - the CP-SAT model currently generates a fresh schedule and does not treat - imported assignments as fixed coverage. - """ - return {} - - -# def _count_fixed_planned_assignments_by_demand_key( -# *, -# assignments: tuple[Assignment, ...], -# employees_by_id: dict[EmployeeId, Employee], -# ) -> dict[DemandKey, int]: -# fixed: defaultdict[DemandKey, int] = defaultdict(int) - -# for assignment in assignments: -# if assignment.assignment_type != AssignmentType.PLANNED: -# continue - -# if assignment.planning_unit_id is None: -# continue - -# employee = employees_by_id[assignment.employee_id] -# key = ( -# assignment.planning_unit_id, -# assignment.date, -# assignment.shift_id, -# employee.staff_level, -# ) -# fixed[key] += 1 - -# return dict(fixed) diff --git a/src/scheduling/solver/models.py b/src/scheduling/solver/models.py index e9608b69..3fa98d61 100644 --- a/src/scheduling/solver/models.py +++ b/src/scheduling/solver/models.py @@ -1,7 +1,11 @@ from enum import StrEnum +from pydantic import Field + from scheduling.domain import SchedulingBaseModel from scheduling.domain.assignment import Assignment +from scheduling.solver.audit import AuditReport +from scheduling.solver.diagnostics import SolverDiagnostic class SolutionStatus(StrEnum): @@ -16,4 +20,5 @@ class SolutionStatus(StrEnum): class Solution(SchedulingBaseModel): status: SolutionStatus assignments: tuple[Assignment, ...] = () - diagnostics: tuple[str, ...] = () + diagnostics: tuple[SolverDiagnostic, ...] = () + audit: AuditReport = Field(default_factory=AuditReport) diff --git a/src/scheduling/solver/service.py b/src/scheduling/solver/service.py index 21801e73..228b6c51 100644 --- a/src/scheduling/solver/service.py +++ b/src/scheduling/solver/service.py @@ -1,46 +1,38 @@ import logging -from collections.abc import Callable from ortools.sat.python import cp_model from scheduling.domain import Assignment, AssignmentType, SchedulingDataset from scheduling.settings import Settings -from scheduling.solver.cp_sat.constraints.minimum_staffing import add_minimum_staffing_constraints -from scheduling.solver.cp_sat.context import SolverContext, create_context +from scheduling.solver.audit import AuditFinding, AuditReport +from scheduling.solver.cp_sat.builder import CpSatBuildResult, CpSatModelBuilder +from scheduling.solver.cp_sat.context import AuditContext, SolverContext from scheduling.solver.cp_sat.inspection import CpSatInspection, inspect_cp_sat_model -from scheduling.solver.cp_sat.objectives.balance_generated_assignments import add_balance_assignments_objective -from scheduling.solver.cp_sat.variables import create_assignment_variables +from scheduling.solver.diagnostics import DiagnosticSeverity, SolverDiagnostic from scheduling.solver.models import Solution, SolutionStatus logger = logging.getLogger(__name__) -type ModelStepFunction = Callable[[SolverContext], None] - - -CONSTRAINTS: tuple[ModelStepFunction, ...] = (add_minimum_staffing_constraints,) - -OBJECTIVES: tuple[ModelStepFunction, ...] = (add_balance_assignments_objective,) - class SolverService: - """Builds and solves the CP-SAT scheduling model.""" + """Build, solve, map, audit, and report CP-SAT scheduling solutions.""" - def __init__(self, settings: Settings) -> None: + def __init__(self, settings: Settings, model_builder: CpSatModelBuilder) -> None: self._settings = settings + self._model_builder = model_builder def solve(self, dataset: SchedulingDataset) -> Solution: - ctx = self._build_model(dataset) + build_result = self._build_model(dataset) + ctx = build_result.ctx + inspection = self._inspect_model(ctx) if not inspection.is_valid: - diagnostics = ( - *ctx.diagnostics, - f"CP-SAT model validation failed: {inspection.validation_error}", - ) return Solution( status=SolutionStatus.MODEL_INVALID, assignments=(), - diagnostics=diagnostics, + diagnostics=tuple(ctx.diagnostics), + audit=AuditReport(), ) solver = self._create_solver() @@ -61,23 +53,38 @@ def solve(self, dataset: SchedulingDataset) -> Solution: else () ) + audit = ( + self._audit_solution( + build_result=build_result, + dataset=dataset, + assignments=assignments, + ) + if status in {SolutionStatus.OPTIMAL, SolutionStatus.FEASIBLE} + else AuditReport() + ) + self._log_solve_result( status=status, assignment_count=len(assignments), diagnostic_count=len(ctx.diagnostics), + audit_finding_count=len(audit.findings), solver=solver, ) if ctx.diagnostics: - logger.debug("Solver diagnostics: diagnostics=%s", tuple(ctx.diagnostics)) + logger.debug( + "Solver diagnostics: diagnostics=%s", + tuple(diagnostic.message for diagnostic in ctx.diagnostics), + ) return Solution( status=status, assignments=assignments, diagnostics=tuple(ctx.diagnostics), + audit=audit, ) - def _build_model(self, dataset: SchedulingDataset) -> SolverContext: + def _build_model(self, dataset: SchedulingDataset) -> CpSatBuildResult: logger.info( "Building CP-SAT model: employees=%s planning_units=%s shifts=%s " "existing_assignments=%s demand_requirements=%s", @@ -88,36 +95,28 @@ def _build_model(self, dataset: SchedulingDataset) -> SolverContext: len(dataset.demand_requirements), ) - ctx = create_context(dataset=dataset) - - create_assignment_variables(ctx) - - for add_constraint in CONSTRAINTS: - logger.debug("Applying solver constraint: name=%s", add_constraint.__name__) - add_constraint(ctx) - - for add_objective in OBJECTIVES: - logger.debug("Applying solver objective: name=%s", add_objective.__name__) - add_objective(ctx) + build_result = self._model_builder.build(dataset) - if ctx.objective_terms: - ctx.model.minimize(sum(ctx.objective_terms)) - else: - logger.debug("No solver objective terms registered.") + logger.debug( + "Applied solver components: constraints=%s objectives=%s weighted_penalties=%s has_objective=%s", + build_result.applied_constraint_ids, + build_result.applied_objective_ids, + build_result.weighted_penalty_count, + build_result.has_objective, + ) - return ctx + return build_result def _inspect_model(self, ctx: SolverContext) -> CpSatInspection: inspection = inspect_cp_sat_model(model=ctx.model) logger.info( "Built CP-SAT model: assignment_variables=%s proto_variables=%s " - "proto_constraints=%s constraint_types=%s objective_terms=%s diagnostics=%s", + "proto_constraints=%s constraint_types=%s diagnostics=%s", len(ctx.assignment_variables), inspection.proto_variable_count, inspection.proto_constraint_count, inspection.constraint_type_counts, - len(ctx.objective_terms), len(ctx.diagnostics), ) @@ -129,6 +128,13 @@ def _inspect_model(self, ctx: SolverContext) -> CpSatInspection: if not inspection.is_valid: logger.error("CP-SAT model validation failed: %s", inspection.validation_error) + ctx.diagnostics.append( + SolverDiagnostic( + code="cp_sat.model_invalid", + severity=DiagnosticSeverity.ERROR, + message=f"CP-SAT model validation failed: {inspection.validation_error}", + ) + ) logger.debug("CP-SAT constraint names: names=%s", inspection.constraint_names) @@ -199,19 +205,63 @@ def _extract_assignments( ) ) + def _audit_solution( + self, + *, + build_result: CpSatBuildResult, + dataset: SchedulingDataset, + assignments: tuple[Assignment, ...], + ) -> AuditReport: + audit_ctx = AuditContext( + dataset=dataset, + index=build_result.ctx.index, + assignments=assignments, + ) + + findings: list[AuditFinding] = [] + + for resolved in build_result.constraints: + if not resolved.enabled: + continue + + findings.extend( + resolved.constraint.audit( + audit_ctx, + params=resolved.params, + ) + ) + + for resolved in build_result.objectives: + if not resolved.enabled: + continue + + findings.extend( + resolved.objective.audit( + audit_ctx, + params=resolved.params, + ) + ) + + return AuditReport(findings=tuple(findings)) + def _log_solve_result( self, *, status: SolutionStatus, assignment_count: int, diagnostic_count: int, + audit_finding_count: int, solver: cp_model.CpSolver, ) -> None: - message = "Solved CP-SAT model: status=%s generated_assignments=%s diagnostics=%s wall_time_seconds=%.3f" + message = ( + "Solved CP-SAT model: status=%s generated_assignments=%s diagnostics=%s " + "audit_findings=%s wall_time_seconds=%.3f" + ) args = ( status.value, assignment_count, diagnostic_count, + audit_finding_count, solver.wall_time, ) diff --git a/src/scheduling/timeoffice/facts.py b/src/scheduling/timeoffice/facts.py index f73527ae..c315ba0c 100644 --- a/src/scheduling/timeoffice/facts.py +++ b/src/scheduling/timeoffice/facts.py @@ -94,6 +94,8 @@ class TimeOfficeFacts: capabilities_by_employee_id: Mapping[int, tuple[Capability, ...]] availability_type_by_absence_code: Mapping[str, AvailabilityType] + ignored_availability_absence_codes: frozenset[str] + wish_type_by_absence_code: Mapping[str, WishType] monthly_target_work_account_id: int @@ -269,8 +271,6 @@ class TimeOfficeFacts: { "U": AvailabilityType.VACATION, "ZU": AvailabilityType.VACATION, - "FR": AvailabilityType.FREE_DAY, - "AZV": AvailabilityType.FREE_DAY, # Conservative hard blockers until TimeOffice/domain semantics are confirmed. "SC": AvailabilityType.UNAVAILABLE, "EZ": AvailabilityType.UNAVAILABLE, @@ -278,6 +278,14 @@ class TimeOfficeFacts: "FI": AvailabilityType.UNAVAILABLE, } ), + ignored_availability_absence_codes=frozenset( + { + # Existing roster free/reduction markers. + # They must not block solve-from-scratch. + "FR", + "AZV", + } + ), wish_type_by_absence_code=MappingProxyType( { "FR": WishType.FREE_DAY, diff --git a/src/scheduling/timeoffice/mapping/roster.py b/src/scheduling/timeoffice/mapping/roster.py index 802e166b..311edaec 100644 --- a/src/scheduling/timeoffice/mapping/roster.py +++ b/src/scheduling/timeoffice/mapping/roster.py @@ -54,6 +54,34 @@ def map_assignments( return tuple(assignments) +def map_availability(*, rows: tuple[TimeOfficeRosterRow, ...], facts: TimeOfficeFacts) -> tuple[Availability, ...]: + availability_items: list[Availability] = [] + + for row in rows: + if not _has_absence(row): + continue + + availability_type = _availability_type_for_absence_code( + row.resolved_absence_code, + facts=facts, + employee_id=row.employee_id, + roster_date=row.roster_date, + ) + + if availability_type is None: + continue + + availability_items.append( + Availability( + employee_id=row.employee_id, + date=row.roster_date.date(), + availability_type=availability_type, + ) + ) + + return tuple(availability_items) + + def _assignment_key(assignment: Assignment) -> AssignmentKey: return ( assignment.employee_id, @@ -64,23 +92,6 @@ def _assignment_key(assignment: Assignment) -> AssignmentKey: ) -def map_availability(*, rows: tuple[TimeOfficeRosterRow, ...], facts: TimeOfficeFacts) -> tuple[Availability, ...]: - return tuple( - Availability( - employee_id=row.employee_id, - date=row.roster_date.date(), - availability_type=_availability_type_for_absence_code( - row.resolved_absence_code, - facts=facts, - employee_id=row.employee_id, - roster_date=row.roster_date, - ), - ) - for row in rows - if _has_absence(row) - ) - - def _assignment_type( *, plan_id: int | None, @@ -104,7 +115,7 @@ def _availability_type_for_absence_code( facts: TimeOfficeFacts, employee_id: int, roster_date: datetime, -) -> AvailabilityType: +) -> AvailabilityType | None: if absence_code is None: raise ValueError( "Missing resolved absence code for TimeOffice roster row: " @@ -113,11 +124,14 @@ def _availability_type_for_absence_code( availability_type = facts.availability_type_by_absence_code.get(absence_code) - if availability_type is None: - raise ValueError( - "Unmapped TimeOffice absence code for availability: " - f"employee_id={employee_id} roster_date={roster_date} " - f"absence_code={absence_code!r}." - ) + if availability_type is not None: + return availability_type + + if absence_code in facts.ignored_availability_absence_codes: + return None - return availability_type + raise ValueError( + "Unmapped TimeOffice absence code for availability: " + f"employee_id={employee_id} roster_date={roster_date} " + f"absence_code={absence_code!r}." + ) From 5d826863437ad4a4a5b8c51e9d542285027ae48c Mon Sep 17 00:00:00 2001 From: Fengwu Lu Date: Wed, 24 Jun 2026 15:54:03 +0200 Subject: [PATCH 15/18] add api for employees --- src/scheduling/api/app.py | 2 +- src/scheduling/api/web/router.py | 36 ++++++++++++++++++++++++++++---- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/src/scheduling/api/app.py b/src/scheduling/api/app.py index 7597b121..98aef711 100644 --- a/src/scheduling/api/app.py +++ b/src/scheduling/api/app.py @@ -52,6 +52,6 @@ async def lifespan(app: FastAPI) -> AsyncIterator[None]: app.include_router(web_router) -@app.get("/health") +@app.get("/status") async def healthcheck(): return {"status": "healthy"} diff --git a/src/scheduling/api/web/router.py b/src/scheduling/api/web/router.py index 3a9c073e..be3968dc 100644 --- a/src/scheduling/api/web/router.py +++ b/src/scheduling/api/web/router.py @@ -1,6 +1,12 @@ import logging +from datetime import date +from typing import Annotated, Any -from fastapi import APIRouter +from fastapi import APIRouter, Depends + +from scheduling.api.dependencies import get_timeoffice_service +from scheduling.domain import Employee, PlanningMonth +from scheduling.timeoffice.service import TimeOfficeService logger = logging.getLogger(__name__) @@ -8,6 +14,28 @@ web_router = APIRouter() -@web_router.get("/employee") -async def get_employees(): - pass +@web_router.get("/employees") +async def get_employees( + planning_unit: int, + from_date: date, + timeoffice: Annotated[TimeOfficeService, Depends(get_timeoffice_service)], +) -> dict[str, list[dict[str, Any]]]: + month = PlanningMonth(year=from_date.year, month=from_date.month) + employees = timeoffice.fetch_dataset(planning_unit_ids=(planning_unit,), planning_month=month).employees + return {"employees": [_employee_to_frontend(employee) for employee in employees]} + + +def _employee_to_frontend(employee: Employee) -> dict[str, Any]: + name, firstname = _split_display_name(employee.display_name) + + return { + "key": employee.employee_id, + "name": name, + "firstname": firstname, + "type": employee.staff_level.value, + } + + +def _split_display_name(display_name: str) -> tuple[str, str]: + name, separator, firstname = display_name.partition(" ") + return name, firstname From bd9e8b3b6732362e9040a90f3846550b9d2147f5 Mon Sep 17 00:00:00 2001 From: Tom Wawerek Date: Wed, 24 Jun 2026 17:21:57 +0200 Subject: [PATCH 16/18] fix: remove sttale sources --- src/scheduling/timeoffice/reading/sources.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/scheduling/timeoffice/reading/sources.py diff --git a/src/scheduling/timeoffice/reading/sources.py b/src/scheduling/timeoffice/reading/sources.py deleted file mode 100644 index e69de29b..00000000 From 3c12861e18cf71b554cbb149b0d76003a1c26f15 Mon Sep 17 00:00:00 2001 From: Tom Wawerek Date: Thu, 25 Jun 2026 10:56:50 +0200 Subject: [PATCH 17/18] chore: exclude legacy directory from quality checks --- .pre-commit-config.yaml | 2 ++ pyproject.toml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 239c2819..cbb4ad09 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,3 +1,5 @@ +exclude: ^legacy/ + repos: - repo: https://github.com/astral-sh/uv-pre-commit rev: 0.7.2 diff --git a/pyproject.toml b/pyproject.toml index 484121e6..c9b6490b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,6 +39,7 @@ build-backend = "uv_build" [tool.ruff] line-length = 120 +extend-exclude = ["legacy"] [tool.ruff.lint] select = ["E", "F", "B", "W", "I", "C4", "ISC", "PT", "Q", "UP"] # Plan to add 'N' and 'ANN' later @@ -46,6 +47,7 @@ select = ["E", "F", "B", "W", "I", "C4", "ISC", "PT", "Q", "UP"] # Plan to add ' [tool.pyright] typeCheckingMode = "strict" reportUnusedVariable = "warning" +exclude = ["legacy"] [tool.pytest.ini_options] addopts = "-m 'not integration'" From ac55055108660d8f5ec17ba52672c1241b4b2f1d Mon Sep 17 00:00:00 2001 From: Tom Wawerek Date: Thu, 25 Jun 2026 12:31:34 +0200 Subject: [PATCH 18/18] chore: exclude legacy directory from quality checks --- {tests => legacy/tests}/__init__.py | 0 {tests => legacy/tests}/cp/conftest.py | 0 .../cp/constraints/mass_test_results/000.txt | 63 +++++++++ .../cp/constraints/mass_test_results/001.txt | 63 +++++++++ .../cp/constraints/mass_test_results/002.txt | 63 +++++++++ .../cp/constraints/mass_test_results/003.txt | 63 +++++++++ .../cp/constraints/mass_test_results/004.txt | 63 +++++++++ .../cp/constraints/mass_test_results/005.txt | 63 +++++++++ .../cp/constraints/mass_test_results/006.txt | 63 +++++++++ .../cp/constraints/mass_test_results/007.txt | 63 +++++++++ .../cp/constraints/mass_test_results/008.txt | 63 +++++++++ .../cp/constraints/mass_test_results/009.txt | 63 +++++++++ .../cp/constraints/mass_test_results/010.txt | 63 +++++++++ .../cp/constraints/mass_test_results/011.txt | 63 +++++++++ .../cp/constraints/mass_test_results/012.txt | 63 +++++++++ .../cp/constraints/mass_test_results/013.txt | 63 +++++++++ .../cp/constraints/mass_test_results/014.txt | 63 +++++++++ .../cp/constraints/mass_test_results/015.txt | 63 +++++++++ .../cp/constraints/mass_test_results/016.txt | 63 +++++++++ .../mass_test_results/_overview.txt | 17 +++ .../cp/constraints/test_all_constraints.py | 0 .../test_free_day_after_night_shift_phase.py | 0 .../test_hierarchy_of_intermediate_shifts.py | 0 .../constraints/test_max_one_shift_per_day.py | 0 .../cp/constraints/test_min_rest_time.py | 0 .../cp/constraints/test_min_staffing.py | 0 .../cp/constraints/test_planned_shifts.py | 0 .../test_rounds_in_early_shifts.py | 0 .../constraints/test_target_working_time.py | 0 .../test_vaction_days_and_shifts.py | 0 pyproject.toml | 4 +- src/scheduling/api/app.py | 4 +- src/scheduling/api/web/router.py | 2 +- .../{integration/__init__.py => conftest.py} | 0 tests/integration/helpers/__init__.py | 0 tests/integration/helpers/smoke_fixtures.py | 124 ------------------ tests/integration/smoke_test.py | 62 --------- uv.lock | 8 +- 38 files changed, 1097 insertions(+), 195 deletions(-) rename {tests => legacy/tests}/__init__.py (100%) rename {tests => legacy/tests}/cp/conftest.py (100%) create mode 100644 legacy/tests/cp/constraints/mass_test_results/000.txt create mode 100644 legacy/tests/cp/constraints/mass_test_results/001.txt create mode 100644 legacy/tests/cp/constraints/mass_test_results/002.txt create mode 100644 legacy/tests/cp/constraints/mass_test_results/003.txt create mode 100644 legacy/tests/cp/constraints/mass_test_results/004.txt create mode 100644 legacy/tests/cp/constraints/mass_test_results/005.txt create mode 100644 legacy/tests/cp/constraints/mass_test_results/006.txt create mode 100644 legacy/tests/cp/constraints/mass_test_results/007.txt create mode 100644 legacy/tests/cp/constraints/mass_test_results/008.txt create mode 100644 legacy/tests/cp/constraints/mass_test_results/009.txt create mode 100644 legacy/tests/cp/constraints/mass_test_results/010.txt create mode 100644 legacy/tests/cp/constraints/mass_test_results/011.txt create mode 100644 legacy/tests/cp/constraints/mass_test_results/012.txt create mode 100644 legacy/tests/cp/constraints/mass_test_results/013.txt create mode 100644 legacy/tests/cp/constraints/mass_test_results/014.txt create mode 100644 legacy/tests/cp/constraints/mass_test_results/015.txt create mode 100644 legacy/tests/cp/constraints/mass_test_results/016.txt create mode 100644 legacy/tests/cp/constraints/mass_test_results/_overview.txt rename {tests => legacy/tests}/cp/constraints/test_all_constraints.py (100%) rename {tests => legacy/tests}/cp/constraints/test_free_day_after_night_shift_phase.py (100%) rename {tests => legacy/tests}/cp/constraints/test_hierarchy_of_intermediate_shifts.py (100%) rename {tests => legacy/tests}/cp/constraints/test_max_one_shift_per_day.py (100%) rename {tests => legacy/tests}/cp/constraints/test_min_rest_time.py (100%) rename {tests => legacy/tests}/cp/constraints/test_min_staffing.py (100%) rename {tests => legacy/tests}/cp/constraints/test_planned_shifts.py (100%) rename {tests => legacy/tests}/cp/constraints/test_rounds_in_early_shifts.py (100%) rename {tests => legacy/tests}/cp/constraints/test_target_working_time.py (100%) rename {tests => legacy/tests}/cp/constraints/test_vaction_days_and_shifts.py (100%) rename tests/{integration/__init__.py => conftest.py} (100%) delete mode 100644 tests/integration/helpers/__init__.py delete mode 100644 tests/integration/helpers/smoke_fixtures.py delete mode 100644 tests/integration/smoke_test.py diff --git a/tests/__init__.py b/legacy/tests/__init__.py similarity index 100% rename from tests/__init__.py rename to legacy/tests/__init__.py diff --git a/tests/cp/conftest.py b/legacy/tests/cp/conftest.py similarity index 100% rename from tests/cp/conftest.py rename to legacy/tests/cp/conftest.py diff --git a/legacy/tests/cp/constraints/mass_test_results/000.txt b/legacy/tests/cp/constraints/mass_test_results/000.txt new file mode 100644 index 00000000..946a5738 --- /dev/null +++ b/legacy/tests/cp/constraints/mass_test_results/000.txt @@ -0,0 +1,63 @@ + +|free_day_after_night_shift_phase_violations| = 0 +|hierarchy_of_intermediate_shifts_violations| = 0 +|max_one_shift_per_day_violations| = 0 +|min_rest_time_violations| = 0 +|min_staffing_violations| = 0 +|planned_shifts_violations| = 0 +|rounds_in_early_shifts_violations| = 0 +|target_working_time_violations| = 0 +|vaction_days_and_shifts_violations| = 0 + + +####################################### + +--------- |free_day_after_night_shift_phase_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |hierarchy_of_intermediate_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |max_one_shift_per_day_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |min_rest_time_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |min_staffing_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |planned_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |rounds_in_early_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |target_working_time_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |vaction_days_and_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +####################################### + + + +|free_day_after_night_shift_phase_violations| = 0 +|hierarchy_of_intermediate_shifts_violations| = 0 +|max_one_shift_per_day_violations| = 0 +|min_rest_time_violations| = 0 +|min_staffing_violations| = 0 +|planned_shifts_violations| = 0 +|rounds_in_early_shifts_violations| = 0 +|target_working_time_violations| = 0 +|vaction_days_and_shifts_violations| = 0 \ No newline at end of file diff --git a/legacy/tests/cp/constraints/mass_test_results/001.txt b/legacy/tests/cp/constraints/mass_test_results/001.txt new file mode 100644 index 00000000..946a5738 --- /dev/null +++ b/legacy/tests/cp/constraints/mass_test_results/001.txt @@ -0,0 +1,63 @@ + +|free_day_after_night_shift_phase_violations| = 0 +|hierarchy_of_intermediate_shifts_violations| = 0 +|max_one_shift_per_day_violations| = 0 +|min_rest_time_violations| = 0 +|min_staffing_violations| = 0 +|planned_shifts_violations| = 0 +|rounds_in_early_shifts_violations| = 0 +|target_working_time_violations| = 0 +|vaction_days_and_shifts_violations| = 0 + + +####################################### + +--------- |free_day_after_night_shift_phase_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |hierarchy_of_intermediate_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |max_one_shift_per_day_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |min_rest_time_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |min_staffing_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |planned_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |rounds_in_early_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |target_working_time_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |vaction_days_and_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +####################################### + + + +|free_day_after_night_shift_phase_violations| = 0 +|hierarchy_of_intermediate_shifts_violations| = 0 +|max_one_shift_per_day_violations| = 0 +|min_rest_time_violations| = 0 +|min_staffing_violations| = 0 +|planned_shifts_violations| = 0 +|rounds_in_early_shifts_violations| = 0 +|target_working_time_violations| = 0 +|vaction_days_and_shifts_violations| = 0 \ No newline at end of file diff --git a/legacy/tests/cp/constraints/mass_test_results/002.txt b/legacy/tests/cp/constraints/mass_test_results/002.txt new file mode 100644 index 00000000..946a5738 --- /dev/null +++ b/legacy/tests/cp/constraints/mass_test_results/002.txt @@ -0,0 +1,63 @@ + +|free_day_after_night_shift_phase_violations| = 0 +|hierarchy_of_intermediate_shifts_violations| = 0 +|max_one_shift_per_day_violations| = 0 +|min_rest_time_violations| = 0 +|min_staffing_violations| = 0 +|planned_shifts_violations| = 0 +|rounds_in_early_shifts_violations| = 0 +|target_working_time_violations| = 0 +|vaction_days_and_shifts_violations| = 0 + + +####################################### + +--------- |free_day_after_night_shift_phase_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |hierarchy_of_intermediate_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |max_one_shift_per_day_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |min_rest_time_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |min_staffing_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |planned_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |rounds_in_early_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |target_working_time_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |vaction_days_and_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +####################################### + + + +|free_day_after_night_shift_phase_violations| = 0 +|hierarchy_of_intermediate_shifts_violations| = 0 +|max_one_shift_per_day_violations| = 0 +|min_rest_time_violations| = 0 +|min_staffing_violations| = 0 +|planned_shifts_violations| = 0 +|rounds_in_early_shifts_violations| = 0 +|target_working_time_violations| = 0 +|vaction_days_and_shifts_violations| = 0 \ No newline at end of file diff --git a/legacy/tests/cp/constraints/mass_test_results/003.txt b/legacy/tests/cp/constraints/mass_test_results/003.txt new file mode 100644 index 00000000..946a5738 --- /dev/null +++ b/legacy/tests/cp/constraints/mass_test_results/003.txt @@ -0,0 +1,63 @@ + +|free_day_after_night_shift_phase_violations| = 0 +|hierarchy_of_intermediate_shifts_violations| = 0 +|max_one_shift_per_day_violations| = 0 +|min_rest_time_violations| = 0 +|min_staffing_violations| = 0 +|planned_shifts_violations| = 0 +|rounds_in_early_shifts_violations| = 0 +|target_working_time_violations| = 0 +|vaction_days_and_shifts_violations| = 0 + + +####################################### + +--------- |free_day_after_night_shift_phase_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |hierarchy_of_intermediate_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |max_one_shift_per_day_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |min_rest_time_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |min_staffing_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |planned_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |rounds_in_early_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |target_working_time_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |vaction_days_and_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +####################################### + + + +|free_day_after_night_shift_phase_violations| = 0 +|hierarchy_of_intermediate_shifts_violations| = 0 +|max_one_shift_per_day_violations| = 0 +|min_rest_time_violations| = 0 +|min_staffing_violations| = 0 +|planned_shifts_violations| = 0 +|rounds_in_early_shifts_violations| = 0 +|target_working_time_violations| = 0 +|vaction_days_and_shifts_violations| = 0 \ No newline at end of file diff --git a/legacy/tests/cp/constraints/mass_test_results/004.txt b/legacy/tests/cp/constraints/mass_test_results/004.txt new file mode 100644 index 00000000..946a5738 --- /dev/null +++ b/legacy/tests/cp/constraints/mass_test_results/004.txt @@ -0,0 +1,63 @@ + +|free_day_after_night_shift_phase_violations| = 0 +|hierarchy_of_intermediate_shifts_violations| = 0 +|max_one_shift_per_day_violations| = 0 +|min_rest_time_violations| = 0 +|min_staffing_violations| = 0 +|planned_shifts_violations| = 0 +|rounds_in_early_shifts_violations| = 0 +|target_working_time_violations| = 0 +|vaction_days_and_shifts_violations| = 0 + + +####################################### + +--------- |free_day_after_night_shift_phase_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |hierarchy_of_intermediate_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |max_one_shift_per_day_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |min_rest_time_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |min_staffing_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |planned_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |rounds_in_early_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |target_working_time_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |vaction_days_and_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +####################################### + + + +|free_day_after_night_shift_phase_violations| = 0 +|hierarchy_of_intermediate_shifts_violations| = 0 +|max_one_shift_per_day_violations| = 0 +|min_rest_time_violations| = 0 +|min_staffing_violations| = 0 +|planned_shifts_violations| = 0 +|rounds_in_early_shifts_violations| = 0 +|target_working_time_violations| = 0 +|vaction_days_and_shifts_violations| = 0 \ No newline at end of file diff --git a/legacy/tests/cp/constraints/mass_test_results/005.txt b/legacy/tests/cp/constraints/mass_test_results/005.txt new file mode 100644 index 00000000..946a5738 --- /dev/null +++ b/legacy/tests/cp/constraints/mass_test_results/005.txt @@ -0,0 +1,63 @@ + +|free_day_after_night_shift_phase_violations| = 0 +|hierarchy_of_intermediate_shifts_violations| = 0 +|max_one_shift_per_day_violations| = 0 +|min_rest_time_violations| = 0 +|min_staffing_violations| = 0 +|planned_shifts_violations| = 0 +|rounds_in_early_shifts_violations| = 0 +|target_working_time_violations| = 0 +|vaction_days_and_shifts_violations| = 0 + + +####################################### + +--------- |free_day_after_night_shift_phase_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |hierarchy_of_intermediate_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |max_one_shift_per_day_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |min_rest_time_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |min_staffing_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |planned_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |rounds_in_early_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |target_working_time_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |vaction_days_and_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +####################################### + + + +|free_day_after_night_shift_phase_violations| = 0 +|hierarchy_of_intermediate_shifts_violations| = 0 +|max_one_shift_per_day_violations| = 0 +|min_rest_time_violations| = 0 +|min_staffing_violations| = 0 +|planned_shifts_violations| = 0 +|rounds_in_early_shifts_violations| = 0 +|target_working_time_violations| = 0 +|vaction_days_and_shifts_violations| = 0 \ No newline at end of file diff --git a/legacy/tests/cp/constraints/mass_test_results/006.txt b/legacy/tests/cp/constraints/mass_test_results/006.txt new file mode 100644 index 00000000..946a5738 --- /dev/null +++ b/legacy/tests/cp/constraints/mass_test_results/006.txt @@ -0,0 +1,63 @@ + +|free_day_after_night_shift_phase_violations| = 0 +|hierarchy_of_intermediate_shifts_violations| = 0 +|max_one_shift_per_day_violations| = 0 +|min_rest_time_violations| = 0 +|min_staffing_violations| = 0 +|planned_shifts_violations| = 0 +|rounds_in_early_shifts_violations| = 0 +|target_working_time_violations| = 0 +|vaction_days_and_shifts_violations| = 0 + + +####################################### + +--------- |free_day_after_night_shift_phase_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |hierarchy_of_intermediate_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |max_one_shift_per_day_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |min_rest_time_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |min_staffing_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |planned_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |rounds_in_early_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |target_working_time_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |vaction_days_and_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +####################################### + + + +|free_day_after_night_shift_phase_violations| = 0 +|hierarchy_of_intermediate_shifts_violations| = 0 +|max_one_shift_per_day_violations| = 0 +|min_rest_time_violations| = 0 +|min_staffing_violations| = 0 +|planned_shifts_violations| = 0 +|rounds_in_early_shifts_violations| = 0 +|target_working_time_violations| = 0 +|vaction_days_and_shifts_violations| = 0 \ No newline at end of file diff --git a/legacy/tests/cp/constraints/mass_test_results/007.txt b/legacy/tests/cp/constraints/mass_test_results/007.txt new file mode 100644 index 00000000..946a5738 --- /dev/null +++ b/legacy/tests/cp/constraints/mass_test_results/007.txt @@ -0,0 +1,63 @@ + +|free_day_after_night_shift_phase_violations| = 0 +|hierarchy_of_intermediate_shifts_violations| = 0 +|max_one_shift_per_day_violations| = 0 +|min_rest_time_violations| = 0 +|min_staffing_violations| = 0 +|planned_shifts_violations| = 0 +|rounds_in_early_shifts_violations| = 0 +|target_working_time_violations| = 0 +|vaction_days_and_shifts_violations| = 0 + + +####################################### + +--------- |free_day_after_night_shift_phase_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |hierarchy_of_intermediate_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |max_one_shift_per_day_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |min_rest_time_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |min_staffing_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |planned_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |rounds_in_early_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |target_working_time_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |vaction_days_and_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +####################################### + + + +|free_day_after_night_shift_phase_violations| = 0 +|hierarchy_of_intermediate_shifts_violations| = 0 +|max_one_shift_per_day_violations| = 0 +|min_rest_time_violations| = 0 +|min_staffing_violations| = 0 +|planned_shifts_violations| = 0 +|rounds_in_early_shifts_violations| = 0 +|target_working_time_violations| = 0 +|vaction_days_and_shifts_violations| = 0 \ No newline at end of file diff --git a/legacy/tests/cp/constraints/mass_test_results/008.txt b/legacy/tests/cp/constraints/mass_test_results/008.txt new file mode 100644 index 00000000..946a5738 --- /dev/null +++ b/legacy/tests/cp/constraints/mass_test_results/008.txt @@ -0,0 +1,63 @@ + +|free_day_after_night_shift_phase_violations| = 0 +|hierarchy_of_intermediate_shifts_violations| = 0 +|max_one_shift_per_day_violations| = 0 +|min_rest_time_violations| = 0 +|min_staffing_violations| = 0 +|planned_shifts_violations| = 0 +|rounds_in_early_shifts_violations| = 0 +|target_working_time_violations| = 0 +|vaction_days_and_shifts_violations| = 0 + + +####################################### + +--------- |free_day_after_night_shift_phase_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |hierarchy_of_intermediate_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |max_one_shift_per_day_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |min_rest_time_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |min_staffing_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |planned_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |rounds_in_early_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |target_working_time_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |vaction_days_and_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +####################################### + + + +|free_day_after_night_shift_phase_violations| = 0 +|hierarchy_of_intermediate_shifts_violations| = 0 +|max_one_shift_per_day_violations| = 0 +|min_rest_time_violations| = 0 +|min_staffing_violations| = 0 +|planned_shifts_violations| = 0 +|rounds_in_early_shifts_violations| = 0 +|target_working_time_violations| = 0 +|vaction_days_and_shifts_violations| = 0 \ No newline at end of file diff --git a/legacy/tests/cp/constraints/mass_test_results/009.txt b/legacy/tests/cp/constraints/mass_test_results/009.txt new file mode 100644 index 00000000..946a5738 --- /dev/null +++ b/legacy/tests/cp/constraints/mass_test_results/009.txt @@ -0,0 +1,63 @@ + +|free_day_after_night_shift_phase_violations| = 0 +|hierarchy_of_intermediate_shifts_violations| = 0 +|max_one_shift_per_day_violations| = 0 +|min_rest_time_violations| = 0 +|min_staffing_violations| = 0 +|planned_shifts_violations| = 0 +|rounds_in_early_shifts_violations| = 0 +|target_working_time_violations| = 0 +|vaction_days_and_shifts_violations| = 0 + + +####################################### + +--------- |free_day_after_night_shift_phase_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |hierarchy_of_intermediate_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |max_one_shift_per_day_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |min_rest_time_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |min_staffing_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |planned_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |rounds_in_early_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |target_working_time_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |vaction_days_and_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +####################################### + + + +|free_day_after_night_shift_phase_violations| = 0 +|hierarchy_of_intermediate_shifts_violations| = 0 +|max_one_shift_per_day_violations| = 0 +|min_rest_time_violations| = 0 +|min_staffing_violations| = 0 +|planned_shifts_violations| = 0 +|rounds_in_early_shifts_violations| = 0 +|target_working_time_violations| = 0 +|vaction_days_and_shifts_violations| = 0 \ No newline at end of file diff --git a/legacy/tests/cp/constraints/mass_test_results/010.txt b/legacy/tests/cp/constraints/mass_test_results/010.txt new file mode 100644 index 00000000..946a5738 --- /dev/null +++ b/legacy/tests/cp/constraints/mass_test_results/010.txt @@ -0,0 +1,63 @@ + +|free_day_after_night_shift_phase_violations| = 0 +|hierarchy_of_intermediate_shifts_violations| = 0 +|max_one_shift_per_day_violations| = 0 +|min_rest_time_violations| = 0 +|min_staffing_violations| = 0 +|planned_shifts_violations| = 0 +|rounds_in_early_shifts_violations| = 0 +|target_working_time_violations| = 0 +|vaction_days_and_shifts_violations| = 0 + + +####################################### + +--------- |free_day_after_night_shift_phase_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |hierarchy_of_intermediate_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |max_one_shift_per_day_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |min_rest_time_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |min_staffing_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |planned_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |rounds_in_early_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |target_working_time_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |vaction_days_and_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +####################################### + + + +|free_day_after_night_shift_phase_violations| = 0 +|hierarchy_of_intermediate_shifts_violations| = 0 +|max_one_shift_per_day_violations| = 0 +|min_rest_time_violations| = 0 +|min_staffing_violations| = 0 +|planned_shifts_violations| = 0 +|rounds_in_early_shifts_violations| = 0 +|target_working_time_violations| = 0 +|vaction_days_and_shifts_violations| = 0 \ No newline at end of file diff --git a/legacy/tests/cp/constraints/mass_test_results/011.txt b/legacy/tests/cp/constraints/mass_test_results/011.txt new file mode 100644 index 00000000..946a5738 --- /dev/null +++ b/legacy/tests/cp/constraints/mass_test_results/011.txt @@ -0,0 +1,63 @@ + +|free_day_after_night_shift_phase_violations| = 0 +|hierarchy_of_intermediate_shifts_violations| = 0 +|max_one_shift_per_day_violations| = 0 +|min_rest_time_violations| = 0 +|min_staffing_violations| = 0 +|planned_shifts_violations| = 0 +|rounds_in_early_shifts_violations| = 0 +|target_working_time_violations| = 0 +|vaction_days_and_shifts_violations| = 0 + + +####################################### + +--------- |free_day_after_night_shift_phase_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |hierarchy_of_intermediate_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |max_one_shift_per_day_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |min_rest_time_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |min_staffing_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |planned_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |rounds_in_early_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |target_working_time_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |vaction_days_and_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +####################################### + + + +|free_day_after_night_shift_phase_violations| = 0 +|hierarchy_of_intermediate_shifts_violations| = 0 +|max_one_shift_per_day_violations| = 0 +|min_rest_time_violations| = 0 +|min_staffing_violations| = 0 +|planned_shifts_violations| = 0 +|rounds_in_early_shifts_violations| = 0 +|target_working_time_violations| = 0 +|vaction_days_and_shifts_violations| = 0 \ No newline at end of file diff --git a/legacy/tests/cp/constraints/mass_test_results/012.txt b/legacy/tests/cp/constraints/mass_test_results/012.txt new file mode 100644 index 00000000..946a5738 --- /dev/null +++ b/legacy/tests/cp/constraints/mass_test_results/012.txt @@ -0,0 +1,63 @@ + +|free_day_after_night_shift_phase_violations| = 0 +|hierarchy_of_intermediate_shifts_violations| = 0 +|max_one_shift_per_day_violations| = 0 +|min_rest_time_violations| = 0 +|min_staffing_violations| = 0 +|planned_shifts_violations| = 0 +|rounds_in_early_shifts_violations| = 0 +|target_working_time_violations| = 0 +|vaction_days_and_shifts_violations| = 0 + + +####################################### + +--------- |free_day_after_night_shift_phase_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |hierarchy_of_intermediate_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |max_one_shift_per_day_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |min_rest_time_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |min_staffing_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |planned_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |rounds_in_early_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |target_working_time_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |vaction_days_and_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +####################################### + + + +|free_day_after_night_shift_phase_violations| = 0 +|hierarchy_of_intermediate_shifts_violations| = 0 +|max_one_shift_per_day_violations| = 0 +|min_rest_time_violations| = 0 +|min_staffing_violations| = 0 +|planned_shifts_violations| = 0 +|rounds_in_early_shifts_violations| = 0 +|target_working_time_violations| = 0 +|vaction_days_and_shifts_violations| = 0 \ No newline at end of file diff --git a/legacy/tests/cp/constraints/mass_test_results/013.txt b/legacy/tests/cp/constraints/mass_test_results/013.txt new file mode 100644 index 00000000..946a5738 --- /dev/null +++ b/legacy/tests/cp/constraints/mass_test_results/013.txt @@ -0,0 +1,63 @@ + +|free_day_after_night_shift_phase_violations| = 0 +|hierarchy_of_intermediate_shifts_violations| = 0 +|max_one_shift_per_day_violations| = 0 +|min_rest_time_violations| = 0 +|min_staffing_violations| = 0 +|planned_shifts_violations| = 0 +|rounds_in_early_shifts_violations| = 0 +|target_working_time_violations| = 0 +|vaction_days_and_shifts_violations| = 0 + + +####################################### + +--------- |free_day_after_night_shift_phase_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |hierarchy_of_intermediate_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |max_one_shift_per_day_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |min_rest_time_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |min_staffing_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |planned_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |rounds_in_early_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |target_working_time_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |vaction_days_and_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +####################################### + + + +|free_day_after_night_shift_phase_violations| = 0 +|hierarchy_of_intermediate_shifts_violations| = 0 +|max_one_shift_per_day_violations| = 0 +|min_rest_time_violations| = 0 +|min_staffing_violations| = 0 +|planned_shifts_violations| = 0 +|rounds_in_early_shifts_violations| = 0 +|target_working_time_violations| = 0 +|vaction_days_and_shifts_violations| = 0 \ No newline at end of file diff --git a/legacy/tests/cp/constraints/mass_test_results/014.txt b/legacy/tests/cp/constraints/mass_test_results/014.txt new file mode 100644 index 00000000..946a5738 --- /dev/null +++ b/legacy/tests/cp/constraints/mass_test_results/014.txt @@ -0,0 +1,63 @@ + +|free_day_after_night_shift_phase_violations| = 0 +|hierarchy_of_intermediate_shifts_violations| = 0 +|max_one_shift_per_day_violations| = 0 +|min_rest_time_violations| = 0 +|min_staffing_violations| = 0 +|planned_shifts_violations| = 0 +|rounds_in_early_shifts_violations| = 0 +|target_working_time_violations| = 0 +|vaction_days_and_shifts_violations| = 0 + + +####################################### + +--------- |free_day_after_night_shift_phase_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |hierarchy_of_intermediate_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |max_one_shift_per_day_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |min_rest_time_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |min_staffing_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |planned_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |rounds_in_early_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |target_working_time_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |vaction_days_and_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +####################################### + + + +|free_day_after_night_shift_phase_violations| = 0 +|hierarchy_of_intermediate_shifts_violations| = 0 +|max_one_shift_per_day_violations| = 0 +|min_rest_time_violations| = 0 +|min_staffing_violations| = 0 +|planned_shifts_violations| = 0 +|rounds_in_early_shifts_violations| = 0 +|target_working_time_violations| = 0 +|vaction_days_and_shifts_violations| = 0 \ No newline at end of file diff --git a/legacy/tests/cp/constraints/mass_test_results/015.txt b/legacy/tests/cp/constraints/mass_test_results/015.txt new file mode 100644 index 00000000..946a5738 --- /dev/null +++ b/legacy/tests/cp/constraints/mass_test_results/015.txt @@ -0,0 +1,63 @@ + +|free_day_after_night_shift_phase_violations| = 0 +|hierarchy_of_intermediate_shifts_violations| = 0 +|max_one_shift_per_day_violations| = 0 +|min_rest_time_violations| = 0 +|min_staffing_violations| = 0 +|planned_shifts_violations| = 0 +|rounds_in_early_shifts_violations| = 0 +|target_working_time_violations| = 0 +|vaction_days_and_shifts_violations| = 0 + + +####################################### + +--------- |free_day_after_night_shift_phase_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |hierarchy_of_intermediate_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |max_one_shift_per_day_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |min_rest_time_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |min_staffing_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |planned_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |rounds_in_early_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |target_working_time_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |vaction_days_and_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +####################################### + + + +|free_day_after_night_shift_phase_violations| = 0 +|hierarchy_of_intermediate_shifts_violations| = 0 +|max_one_shift_per_day_violations| = 0 +|min_rest_time_violations| = 0 +|min_staffing_violations| = 0 +|planned_shifts_violations| = 0 +|rounds_in_early_shifts_violations| = 0 +|target_working_time_violations| = 0 +|vaction_days_and_shifts_violations| = 0 \ No newline at end of file diff --git a/legacy/tests/cp/constraints/mass_test_results/016.txt b/legacy/tests/cp/constraints/mass_test_results/016.txt new file mode 100644 index 00000000..946a5738 --- /dev/null +++ b/legacy/tests/cp/constraints/mass_test_results/016.txt @@ -0,0 +1,63 @@ + +|free_day_after_night_shift_phase_violations| = 0 +|hierarchy_of_intermediate_shifts_violations| = 0 +|max_one_shift_per_day_violations| = 0 +|min_rest_time_violations| = 0 +|min_staffing_violations| = 0 +|planned_shifts_violations| = 0 +|rounds_in_early_shifts_violations| = 0 +|target_working_time_violations| = 0 +|vaction_days_and_shifts_violations| = 0 + + +####################################### + +--------- |free_day_after_night_shift_phase_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |hierarchy_of_intermediate_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |max_one_shift_per_day_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |min_rest_time_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |min_staffing_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |planned_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |rounds_in_early_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |target_working_time_violations| = 0 ----------- + +---------------------------------------------------- + +--------- |vaction_days_and_shifts_violations| = 0 ----------- + +---------------------------------------------------- + +####################################### + + + +|free_day_after_night_shift_phase_violations| = 0 +|hierarchy_of_intermediate_shifts_violations| = 0 +|max_one_shift_per_day_violations| = 0 +|min_rest_time_violations| = 0 +|min_staffing_violations| = 0 +|planned_shifts_violations| = 0 +|rounds_in_early_shifts_violations| = 0 +|target_working_time_violations| = 0 +|vaction_days_and_shifts_violations| = 0 \ No newline at end of file diff --git a/legacy/tests/cp/constraints/mass_test_results/_overview.txt b/legacy/tests/cp/constraints/mass_test_results/_overview.txt new file mode 100644 index 00000000..264f70be --- /dev/null +++ b/legacy/tests/cp/constraints/mass_test_results/_overview.txt @@ -0,0 +1,17 @@ +Number Violations for result 000: 0 +Number Violations for result 001: 0 +Number Violations for result 002: 0 +Number Violations for result 003: 0 +Number Violations for result 004: 0 +Number Violations for result 005: 0 +Number Violations for result 006: 0 +Number Violations for result 007: 0 +Number Violations for result 008: 0 +Number Violations for result 009: 0 +Number Violations for result 010: 0 +Number Violations for result 011: 0 +Number Violations for result 012: 0 +Number Violations for result 013: 0 +Number Violations for result 014: 0 +Number Violations for result 015: 0 +Number Violations for result 016: 0 diff --git a/tests/cp/constraints/test_all_constraints.py b/legacy/tests/cp/constraints/test_all_constraints.py similarity index 100% rename from tests/cp/constraints/test_all_constraints.py rename to legacy/tests/cp/constraints/test_all_constraints.py diff --git a/tests/cp/constraints/test_free_day_after_night_shift_phase.py b/legacy/tests/cp/constraints/test_free_day_after_night_shift_phase.py similarity index 100% rename from tests/cp/constraints/test_free_day_after_night_shift_phase.py rename to legacy/tests/cp/constraints/test_free_day_after_night_shift_phase.py diff --git a/tests/cp/constraints/test_hierarchy_of_intermediate_shifts.py b/legacy/tests/cp/constraints/test_hierarchy_of_intermediate_shifts.py similarity index 100% rename from tests/cp/constraints/test_hierarchy_of_intermediate_shifts.py rename to legacy/tests/cp/constraints/test_hierarchy_of_intermediate_shifts.py diff --git a/tests/cp/constraints/test_max_one_shift_per_day.py b/legacy/tests/cp/constraints/test_max_one_shift_per_day.py similarity index 100% rename from tests/cp/constraints/test_max_one_shift_per_day.py rename to legacy/tests/cp/constraints/test_max_one_shift_per_day.py diff --git a/tests/cp/constraints/test_min_rest_time.py b/legacy/tests/cp/constraints/test_min_rest_time.py similarity index 100% rename from tests/cp/constraints/test_min_rest_time.py rename to legacy/tests/cp/constraints/test_min_rest_time.py diff --git a/tests/cp/constraints/test_min_staffing.py b/legacy/tests/cp/constraints/test_min_staffing.py similarity index 100% rename from tests/cp/constraints/test_min_staffing.py rename to legacy/tests/cp/constraints/test_min_staffing.py diff --git a/tests/cp/constraints/test_planned_shifts.py b/legacy/tests/cp/constraints/test_planned_shifts.py similarity index 100% rename from tests/cp/constraints/test_planned_shifts.py rename to legacy/tests/cp/constraints/test_planned_shifts.py diff --git a/tests/cp/constraints/test_rounds_in_early_shifts.py b/legacy/tests/cp/constraints/test_rounds_in_early_shifts.py similarity index 100% rename from tests/cp/constraints/test_rounds_in_early_shifts.py rename to legacy/tests/cp/constraints/test_rounds_in_early_shifts.py diff --git a/tests/cp/constraints/test_target_working_time.py b/legacy/tests/cp/constraints/test_target_working_time.py similarity index 100% rename from tests/cp/constraints/test_target_working_time.py rename to legacy/tests/cp/constraints/test_target_working_time.py diff --git a/tests/cp/constraints/test_vaction_days_and_shifts.py b/legacy/tests/cp/constraints/test_vaction_days_and_shifts.py similarity index 100% rename from tests/cp/constraints/test_vaction_days_and_shifts.py rename to legacy/tests/cp/constraints/test_vaction_days_and_shifts.py diff --git a/pyproject.toml b/pyproject.toml index c9b6490b..a19dc778 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,7 +16,7 @@ dependencies = [ [dependency-groups] dev = [ "pre-commit>=4.6.0", - "pyright>=1.1.410", + "pyright>=1.1.411", "ruff>=0.15.17", "pytest>=9.1.0", "debugpy>=1.8.21", @@ -47,7 +47,7 @@ select = ["E", "F", "B", "W", "I", "C4", "ISC", "PT", "Q", "UP"] # Plan to add ' [tool.pyright] typeCheckingMode = "strict" reportUnusedVariable = "warning" -exclude = ["legacy"] +ignore = ["legacy"] [tool.pytest.ini_options] addopts = "-m 'not integration'" diff --git a/src/scheduling/api/app.py b/src/scheduling/api/app.py index fee0c1c5..12d1a836 100644 --- a/src/scheduling/api/app.py +++ b/src/scheduling/api/app.py @@ -1,6 +1,6 @@ import asyncio import logging -from collections.abc import AsyncIterator +from collections.abc import AsyncGenerator from contextlib import asynccontextmanager from fastapi import FastAPI @@ -26,7 +26,7 @@ @asynccontextmanager -async def lifespan(app: FastAPI) -> AsyncIterator[None]: +async def lifespan(app: FastAPI) -> AsyncGenerator[None]: engine = create_db_engine(settings=settings) facts = TIMEOFFICE_FACTS diff --git a/src/scheduling/api/web/router.py b/src/scheduling/api/web/router.py index be3968dc..80d8e90a 100644 --- a/src/scheduling/api/web/router.py +++ b/src/scheduling/api/web/router.py @@ -37,5 +37,5 @@ def _employee_to_frontend(employee: Employee) -> dict[str, Any]: def _split_display_name(display_name: str) -> tuple[str, str]: - name, separator, firstname = display_name.partition(" ") + name, _separator, firstname = display_name.partition(" ") return name, firstname diff --git a/tests/integration/__init__.py b/tests/conftest.py similarity index 100% rename from tests/integration/__init__.py rename to tests/conftest.py diff --git a/tests/integration/helpers/__init__.py b/tests/integration/helpers/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/integration/helpers/smoke_fixtures.py b/tests/integration/helpers/smoke_fixtures.py deleted file mode 100644 index edd8a5c6..00000000 --- a/tests/integration/helpers/smoke_fixtures.py +++ /dev/null @@ -1,124 +0,0 @@ -from dataclasses import dataclass -from datetime import date, timedelta - -from legacy.src.employee import Employee -from legacy.src.shift import Shift - -type WeekdayAbbreviation = str -type EmployeeLevel = str -type ShiftAbbreviation = str -type MinStaffing = dict[EmployeeLevel, dict[WeekdayAbbreviation, dict[ShiftAbbreviation, int]]] - - -SMOKE_TEST_WEIGHTS: dict[str, int] = { - "free_weekend": 1, - "consecutive_nights": 1, - "hidden": 1, - "overtime": 1, - "consecutive_days": 1, - "rotate": 1, - "wishes": 1, - "after_night": 1, - "second_weekend": 1, - "preferred_block": 1, -} - - -@dataclass(frozen=True) -class SmokeSolveFixture: - unit: int - start_date: date - end_date: date - days: list[date] - shifts: list[Shift] - employees: list[Employee] - min_staffing: MinStaffing - - -def make_smoke_solve_fixture() -> SmokeSolveFixture: - """Small clean fixture for one full service-level solver run.""" - start_date = date(2024, 11, 2) # Saturday - days = [start_date + timedelta(days=offset) for offset in range(2)] - - return SmokeSolveFixture( - unit=999, - start_date=start_date, - end_date=days[-1], - days=days, - shifts=make_solver_compatible_shifts(), - employees=[ - make_employee( - key=1, - name="Alice", - level="Fachkraft", - target_working_time=460, - ), - make_employee( - key=2, - name="Bob", - level="Fachkraft", - target_working_time=460, - ), - ], - min_staffing={ - "Fachkraft": make_weekend_early_staffing(), - }, - ) - - -def make_solver_compatible_shifts() -> list[Shift]: - return [ - Shift(Shift.EARLY, "Früh", 360, 820), - Shift(Shift.INTERMEDIATE, "Zwischen", 480, 940), - Shift(Shift.LATE, "Spät", 805, 1265), - Shift(Shift.NIGHT, "Nacht", 1250, 375), - Shift(Shift.MANAGEMENT, "Z60", 480, 840), - Shift(5, "F2_", 360, 820), - Shift(6, "S2_", 805, 1265), - Shift(7, "N5", 1250, 375), - ] - - -def make_employee( - *, - key: int, - name: str, - level: str, - target_working_time: int, -) -> Employee: - return Employee( - key=key, - surname="Smoke", - name=name, - level=level, - type=f"Test-{level}", - target_working_time=target_working_time, - actual_working_time=0, - forbidden_days=[], - forbidden_shifts=[], - vacation_days=[], - vacation_shifts=[], - wish_days=[], - wish_shifts=[], - planned_shifts=[], - qualifications=[], - ) - - -def make_weekend_early_staffing() -> dict[str, dict[str, int]]: - staffing = make_empty_week_staffing() - staffing["Sa"]["F"] = 1 - staffing["So"]["F"] = 1 - return staffing - - -def make_empty_week_staffing() -> dict[str, dict[str, int]]: - return { - "Mo": {"F": 0, "Z": 0, "S": 0, "N": 0}, - "Di": {"F": 0, "Z": 0, "S": 0, "N": 0}, - "Mi": {"F": 0, "Z": 0, "S": 0, "N": 0}, - "Do": {"F": 0, "Z": 0, "S": 0, "N": 0}, - "Fr": {"F": 0, "Z": 0, "S": 0, "N": 0}, - "Sa": {"F": 0, "Z": 0, "S": 0, "N": 0}, - "So": {"F": 0, "Z": 0, "S": 0, "N": 0}, - } diff --git a/tests/integration/smoke_test.py b/tests/integration/smoke_test.py deleted file mode 100644 index 7ded4f32..00000000 --- a/tests/integration/smoke_test.py +++ /dev/null @@ -1,62 +0,0 @@ -from typing import Any - -import pytest - -from legacy.src.services.solve_service import execute_solve -from tests.integration.helpers.smoke_fixtures import SMOKE_TEST_WEIGHTS, SmokeSolveFixture, make_smoke_solve_fixture - - -def inject_smoke_fixture( - monkeypatch: pytest.MonkeyPatch, - fixture: SmokeSolveFixture, -) -> None: - """Replace data loading with fixed sanitized data.""" - monkeypatch.setattr("src.solve.FSLoader.get_days", lambda self, start_date, end_date: fixture.days) - monkeypatch.setattr("src.solve.FSLoader.get_shifts", lambda self: fixture.shifts) - monkeypatch.setattr("src.solve.FSLoader.get_employees", lambda self, start=0: fixture.employees) - monkeypatch.setattr("src.solve.FSLoader.get_min_staffing", lambda self: fixture.min_staffing) - monkeypatch.setattr("src.solve.FSLoader.write_solution", lambda self, solution, solution_name: None) - - monkeypatch.setattr( - "src.services.solve_service.load_weights", - lambda unit, start_date: SMOKE_TEST_WEIGHTS, - ) - - -@pytest.mark.integration -def test_solve_service_generates_output_for_clean_smoke_fixture( - monkeypatch: pytest.MonkeyPatch, -) -> None: - fixture = make_smoke_solve_fixture() - inject_smoke_fixture(monkeypatch, fixture) - - generated_outputs: list[dict[str, Any]] = [] - - def fake_process_solution( - *, - loader: Any, - employees: Any, - output_filename: str, - solution_file_name: str, - ) -> dict[str, Any]: - generated_outputs.append( - { - "output_filename": output_filename, - "solution_file_name": solution_file_name, - "employee_count": len(employees), - } - ) - return {"generated": True} - - monkeypatch.setattr("src.services.solve_service.process_solution", fake_process_solution) - - result = execute_solve( - unit=fixture.unit, - start_date=fixture.start_date, - end_date=fixture.end_date, - timeout=10, - ) - - assert result["status"] in {"FEASIBLE", "OPTIMAL"} - assert result["solution_data"] == {"generated": True} - assert len(generated_outputs) == 1 diff --git a/uv.lock b/uv.lock index 17dc53b1..b86667a0 100644 --- a/uv.lock +++ b/uv.lock @@ -1215,15 +1215,15 @@ wheels = [ [[package]] name = "pyright" -version = "1.1.410" +version = "1.1.411" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "nodeenv" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/10/53/e4d8ea1391bd4355231be6f91bf239479aa0014260ed3fb5526eeb12a1f2/pyright-1.1.410.tar.gz", hash = "sha256:07a073b8ba6749826773c1269773efa11b93440d9a6aa60419d9a3172d6dc488", size = 4062013, upload-time = "2026-06-01T17:35:48.894Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/ab/265f7dc69d28113ebba19092e57b075f41543b2ed048429c5f56e2b88eac/pyright-1.1.411.tar.gz", hash = "sha256:d885a0551f2e763b089a02702174e7f4ba77548cddabc972ab86d1f7f1b0f998", size = 4112861, upload-time = "2026-06-25T02:14:06.37Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d7/33/288b5868fa00846dacf249633719d747893e54aebd196b9968ac1878a5d3/pyright-1.1.410-py3-none-any.whl", hash = "sha256:5e961bed37cacf96b3f7cd7b1da39b350a9239aa2e69138d0e88f728cfaf296c", size = 6082448, upload-time = "2026-06-01T17:35:46.387Z" }, + { url = "https://files.pythonhosted.org/packages/0a/49/385be530a6a5b78d1cbcd5c2e38debc8959a2fc6bdb716f4e581002979fc/pyright-1.1.411-py3-none-any.whl", hash = "sha256:dc7c72a8e2700c55baa127554040e067041ea53ccfd50bf96308cc4291c7d5d9", size = 6181526, upload-time = "2026-06-25T02:14:04.691Z" }, ] [[package]] @@ -1529,7 +1529,7 @@ provides-extras = ["docs"] dev = [ { name = "debugpy", specifier = ">=1.8.21" }, { name = "pre-commit", specifier = ">=4.6.0" }, - { name = "pyright", specifier = ">=1.1.410" }, + { name = "pyright", specifier = ">=1.1.411" }, { name = "pytest", specifier = ">=9.1.0" }, { name = "ruff", specifier = ">=0.15.17" }, ]