From a320dd935fadd13ae06c24bc82cbedbe80aa0c3f Mon Sep 17 00:00:00 2001 From: PatrickDiallo23 Date: Wed, 15 Jan 2025 16:32:23 +0200 Subject: [PATCH 1/3] chore: port sports-league-scheduling quickstart example from Java to Python --- python/sports-league-scheduling/README.adoc | 79 +++++ python/sports-league-scheduling/logging.conf | 30 ++ .../sports-league-scheduling/pyproject.toml | 20 ++ .../sports-league-scheduling-screenshot.png | Bin 0 -> 126597 bytes .../src/sports_league_scheduling/__init__.py | 16 + .../sports_league_scheduling/constraints.py | 155 +++++++++ .../src/sports_league_scheduling/demo_data.py | 126 +++++++ .../src/sports_league_scheduling/domain.py | 113 ++++++ .../json_serialization.py | 71 ++++ .../src/sports_league_scheduling/rest_api.py | 111 ++++++ .../score_analysis.py | 18 + .../src/sports_league_scheduling/solver.py | 21 ++ python/sports-league-scheduling/static/app.js | 322 ++++++++++++++++++ .../static/index.html | 159 +++++++++ .../webjars/timefold/css/timefold-webui.css | 60 ++++ .../webjars/timefold/img/timefold-favicon.svg | 25 ++ .../img/timefold-logo-horizontal-negative.svg | 1 + .../img/timefold-logo-horizontal-positive.svg | 1 + .../img/timefold-logo-stacked-positive.svg | 1 + .../webjars/timefold/js/timefold-webui.js | 142 ++++++++ .../tests/test_constraints.py | 165 +++++++++ .../tests/test_feasible.py | 68 ++++ 22 files changed, 1704 insertions(+) create mode 100644 python/sports-league-scheduling/README.adoc create mode 100644 python/sports-league-scheduling/logging.conf create mode 100644 python/sports-league-scheduling/pyproject.toml create mode 100644 python/sports-league-scheduling/sports-league-scheduling-screenshot.png create mode 100644 python/sports-league-scheduling/src/sports_league_scheduling/__init__.py create mode 100644 python/sports-league-scheduling/src/sports_league_scheduling/constraints.py create mode 100644 python/sports-league-scheduling/src/sports_league_scheduling/demo_data.py create mode 100644 python/sports-league-scheduling/src/sports_league_scheduling/domain.py create mode 100644 python/sports-league-scheduling/src/sports_league_scheduling/json_serialization.py create mode 100644 python/sports-league-scheduling/src/sports_league_scheduling/rest_api.py create mode 100644 python/sports-league-scheduling/src/sports_league_scheduling/score_analysis.py create mode 100644 python/sports-league-scheduling/src/sports_league_scheduling/solver.py create mode 100644 python/sports-league-scheduling/static/app.js create mode 100644 python/sports-league-scheduling/static/index.html create mode 100644 python/sports-league-scheduling/static/webjars/timefold/css/timefold-webui.css create mode 100644 python/sports-league-scheduling/static/webjars/timefold/img/timefold-favicon.svg create mode 100644 python/sports-league-scheduling/static/webjars/timefold/img/timefold-logo-horizontal-negative.svg create mode 100644 python/sports-league-scheduling/static/webjars/timefold/img/timefold-logo-horizontal-positive.svg create mode 100644 python/sports-league-scheduling/static/webjars/timefold/img/timefold-logo-stacked-positive.svg create mode 100644 python/sports-league-scheduling/static/webjars/timefold/js/timefold-webui.js create mode 100644 python/sports-league-scheduling/tests/test_constraints.py create mode 100644 python/sports-league-scheduling/tests/test_feasible.py diff --git a/python/sports-league-scheduling/README.adoc b/python/sports-league-scheduling/README.adoc new file mode 100644 index 0000000000..c9fa4a7ede --- /dev/null +++ b/python/sports-league-scheduling/README.adoc @@ -0,0 +1,79 @@ += Sports League Scheduling (Python) + +Assign rounds to matches to produce a better schedule for league matches. + +image::./sports-league-scheduling-screenshot.png[] + +* <> +* <> +* <> + +[[prerequisites]] +== Prerequisites + +. Install https://www.python.org/downloads/[Python 3.11+] + +. Install JDK 17+, for example with https://sdkman.io[Sdkman]: ++ +---- +$ sdk install java +---- + +[[run]] +== Run the application + +. Git clone the timefold-quickstarts repo and navigate to this directory: ++ +[source, shell] +---- +$ git clone https://github.com/TimefoldAI/timefold-quickstarts.git +... +$ cd timefold-quickstarts/python/sports-league-scheduling +---- + +. Create a virtual environment ++ +[source, shell] +---- +$ python -m venv .venv +---- + +. Activate the virtual environment ++ +[source, shell] +---- +$ . .venv/bin/activate +---- + +. Install the application ++ +[source, shell] +---- +$ pip install -e . +---- + +. Run the application ++ +[source, shell] +---- +$ run-app +---- + +. Visit http://localhost:8080 in your browser. + +. Click on the *Solve* button. + + +[[test]] +== Test the application + +. Run tests ++ +[source, shell] +---- +$ pytest +---- + +== More information + +Visit https://timefold.ai[timefold.ai]. diff --git a/python/sports-league-scheduling/logging.conf b/python/sports-league-scheduling/logging.conf new file mode 100644 index 0000000000..b9dd947471 --- /dev/null +++ b/python/sports-league-scheduling/logging.conf @@ -0,0 +1,30 @@ +[loggers] +keys=root,timefold_solver + +[handlers] +keys=consoleHandler + +[formatters] +keys=simpleFormatter + +[logger_root] +level=INFO +handlers=consoleHandler + +[logger_timefold_solver] +level=INFO +qualname=timefold.solver +handlers=consoleHandler +propagate=0 + +[handler_consoleHandler] +class=StreamHandler +level=INFO +formatter=simpleFormatter +args=(sys.stdout,) + +[formatter_simpleFormatter] +class=uvicorn.logging.ColourizedFormatter +format={levelprefix:<8} @ {name} : {message} +style={ +use_colors=True diff --git a/python/sports-league-scheduling/pyproject.toml b/python/sports-league-scheduling/pyproject.toml new file mode 100644 index 0000000000..2671cd975d --- /dev/null +++ b/python/sports-league-scheduling/pyproject.toml @@ -0,0 +1,20 @@ +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +name = "sports_league_scheduling" +version = "1.0.0" +requires-python = ">=3.11" +dependencies = [ + 'timefold == 999-dev0', + 'fastapi == 0.111.0', + 'pydantic == 2.7.3', + 'uvicorn == 0.30.1', + 'pytest == 8.2.2', + 'httpx == 0.27.0', +] + + +[project.scripts] +run-app = "sports_league_scheduling:main" diff --git a/python/sports-league-scheduling/sports-league-scheduling-screenshot.png b/python/sports-league-scheduling/sports-league-scheduling-screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..cfdfb28274932b69e78a63fc789b92a69eff9d60 GIT binary patch literal 126597 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ÙsH#R2<|O7yE-sOUo%< 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 z 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 Constraint: + return (constraint_factory + .for_each_unique_pair(Match, + Joiners.equal(lambda match: match.round.index), + Joiners.filtering(are_teams_overlapping)) + .penalize(HardSoftScore.ONE_HARD) + .as_constraint("Matches on the same day")) + +def are_teams_overlapping(match1 : Match, match2:Match) -> bool: + return (match1.home_team == match2.home_team or match1.home_team == match2.away_team + or match1.away_team == match2.home_team or match1.away_team == match2.away_team) + + +def multiple_consecutive_home_matches(constraint_factory: ConstraintFactory) -> Constraint: + return (constraint_factory + .for_each(Match) + .join(Team, + Joiners.equal(lambda match: match.home_team, lambda team: team)) + .group_by(lambda match, team: team, + ConstraintCollectors.to_consecutive_sequences(lambda match, team: match.round, + lambda match_round: match_round.index)) + .flatten_last(lambda sequences: sequences.getConsecutiveSequences()) + .filter(lambda team, matches: matches.getCount() >= MAX_CONSECUTIVE_MATCHES) + .penalize(HardSoftScore.ONE_HARD, lambda team, matches: matches.getCount()) + .as_constraint("4 or more consecutive home matches")) + + +def multiple_consecutive_away_matches(constraint_factory: ConstraintFactory) -> Constraint: + return (constraint_factory + .for_each(Match) + .join(Team, + Joiners.equal(lambda match: match.away_team, lambda team: team)) + .group_by(lambda match, team: team, + ConstraintCollectors.to_consecutive_sequences(lambda match, team: match.round, + lambda match_round: match_round.index)) + .flatten_last(lambda sequences: sequences.getConsecutiveSequences()) + .filter(lambda team, matches: matches.getCount() >= MAX_CONSECUTIVE_MATCHES) + .penalize(HardSoftScore.ONE_HARD, lambda team, matches: matches.getCount()) + .as_constraint("4 or more consecutive away matches")) + + +def repeat_match_on_the_next_day(constraint_factory: ConstraintFactory) -> Constraint: + return (constraint_factory + .for_each(Match) + .if_exists(Match, + Joiners.equal(lambda match: match.home_team, lambda match: match.away_team), + Joiners.equal(lambda match: match.away_team, lambda match: match.home_team), + Joiners.equal(lambda match: match.round.index + 1, lambda match: match.round.index)) + .penalize(HardSoftScore.ONE_HARD) + .as_constraint("Repeat match on the next day")) + + +def start_to_away_hop(constraint_factory: ConstraintFactory) -> Constraint: + return (constraint_factory + .for_each(Match) + .if_not_exists(Round, Joiners.equal(lambda match: match.round.index - 1, + lambda match_round: match_round.index)) + .penalize(HardSoftScore.ONE_SOFT, away_home_match_distance_lambda) + .as_constraint("Start to away hop")) + + +def home_to_away_hop(constraint_factory: ConstraintFactory) -> Constraint: + return (constraint_factory + .for_each(Match) + .join(Match, + Joiners.equal(lambda match: match.home_team, lambda match: match.away_team), + Joiners.equal(lambda match: match.round.index + 1, lambda match: match.round.index)) + .penalize(HardSoftScore.ONE_SOFT, home_matches_distance_lambda) + .as_constraint("Home to away hop")) + + +def away_to_away_hop(constraint_factory: ConstraintFactory) -> Constraint: + return (constraint_factory + .for_each(Match) + .join(Match, + Joiners.equal(lambda match: match.away_team, lambda match: match.away_team), + Joiners.equal(lambda match: match.round.index + 1, lambda match: match.round.index)) + .penalize(HardSoftScore.ONE_SOFT, home_matches_distance_lambda) + .as_constraint("Away to away hop")) + + +def away_to_home_hop(constraint_factory: ConstraintFactory) -> Constraint: + return (constraint_factory + .for_each(Match) + .join(Match, + Joiners.equal(lambda match: match.away_team, lambda match: match.home_team), + Joiners.equal(lambda match: match.round.index + 1, lambda match: match.round.index)) + .penalize(HardSoftScore.ONE_SOFT, home_away_matches_distance_lambda) + .as_constraint("Away to home hop")) + +home_away_match_distance_lambda = lambda match: ( + match.home_team.distance_to_team.get(match.away_team.id, 0) + if isinstance(match.home_team, Team) and isinstance(match.away_team, Team) else 0 +) + + +away_home_match_distance_lambda = lambda match: ( + match.away_team.distance_to_team.get(match.home_team.id, 0) + if isinstance(match.away_team, Team) and isinstance(match.home_team, Team) else 0 +) + +home_away_matches_distance_lambda = lambda match, other_match: ( + match.home_team.distance_to_team.get(match.away_team.id, 0) + if isinstance(match.home_team, Team) and isinstance(match.away_team, Team) else 0 +) + +home_matches_distance_lambda = lambda match, other_match: ( + match.home_team.distance_to_team.get(other_match.home_team.id, 0) + if isinstance(match.home_team, Team) and isinstance(other_match.home_team, Team) else 0 +) + +def away_to_end_hop(constraint_factory: ConstraintFactory) -> Constraint: + return (constraint_factory + .for_each(Match) + .if_not_exists(Round, Joiners.equal(lambda match: match.round.index + 1, + lambda match_round: match_round.index)) + .penalize(HardSoftScore.ONE_SOFT, home_away_match_distance_lambda) + .as_constraint("Away to end hop")) + + +def classic_matches(constraint_factory: ConstraintFactory) -> Constraint: + return (constraint_factory + .for_each(Match) + .filter(is_invalid_classic_match) + .penalize(HardSoftScore.of_soft(1000)) + .as_constraint("Classic matches played on weekends or holidays")) + +def is_invalid_classic_match(match: Match) -> bool: + return match.classic_match and not match.round.weekend_or_holiday diff --git a/python/sports-league-scheduling/src/sports_league_scheduling/demo_data.py b/python/sports-league-scheduling/src/sports_league_scheduling/demo_data.py new file mode 100644 index 0000000000..9c190a0c83 --- /dev/null +++ b/python/sports-league-scheduling/src/sports_league_scheduling/demo_data.py @@ -0,0 +1,126 @@ +import json +import random +from datetime import datetime, time, timedelta +from typing import List, Callable + +from .domain import * + + +DISTANCE_IN_KM = [ + [0, 2163, 2163, 2160, 2156, 2156, 2163, 340, 1342, 512, 3038, 1526, 2054, 2054], + [2163, 0, 11, 50, 813, 813, 11, 1967, 842, 1661, 1139, 1037, 202, 202], + [2163, 11, 0, 50, 813, 813, 11, 1967, 842, 1661, 1139, 1037, 202, 202], + [2160, 50, 50, 0, 862, 862, 50, 1957, 831, 1655, 1180, 1068, 161, 161], + [2160, 813, 813, 862, 0, 1, 813, 2083, 1160, 1741, 910, 644, 600, 600], + [2160, 813, 813, 862, 1, 0, 813, 2083, 1160, 1741, 910, 644, 600, 600], + [2163, 11, 11, 50, 813, 813, 0, 1967, 842, 1661, 1139, 1037, 202, 202], + [340, 1967, 1967, 1957, 2083, 2083, 1967, 0, 1126, 341, 2926, 1490, 1836, 1836], + [1342, 842, 842, 831, 1160, 1160, 842, 1126, 0, 831, 1874, 820, 714, 714], + [512, 1661, 1661, 1655, 1741, 1741, 1661, 341, 831, 0, 2589, 1151, 1545, 1545], + [3038, 1139, 1139, 1180, 910, 910, 1139, 2926, 1874, 2589, 0, 1552, 1340, 1340], + [1526, 1037, 1037, 1068, 644, 644, 1037, 1490, 820, 1151, 1552, 0, 1077, 1077], + [2054, 202, 202, 161, 600, 600, 202, 1836, 714, 1545, 1340, 1077, 0, 14], + [2054, 202, 202, 161, 600, 600, 202, 1836, 714, 1545, 1340, 1077, 14, 0], +] + + +def id_generator(): + current = 0 + while True: + yield str(current) + current += 1 + + +def generate_rounds(count_rounds : int) -> List[Round]: + today = datetime.now() + rounds = [Round(index=i, weekend_or_holiday=False) for i in range(count_rounds)] + + # Mark weekend rounds as important + for round_obj in rounds: + future_date = today + timedelta(days=round_obj.index) + if future_date.weekday() in (5, 6): # Saturday or Sunday + round_obj.weekend_or_holiday = True + + return rounds + + +def generate_teams() -> List[Team]: + team_names = [ + "Cruzeiro", "Argentinos Jr.", "Boca Juniors", "Estudiantes", "Independente", + "Racing", "River Plate", "Flamengo", "Gremio", "Santos", + "Colo-Colo", "Olimpia", "Nacional", "Penharol" + ] + + teams = [Team(id=str(i + 1), name=name, distance_to_team={}) for i, name in enumerate(team_names)] + + # Assign distances + for i, team in enumerate(teams): + team.distance_to_team = { + teams[j].id: DISTANCE_IN_KM[i][j] + for j in range(len(teams)) + if i != j + } + + return teams + +def generate_matches(teams: List[Team]) -> List[Match]: + reciprocal_match = None + matches = [ + Match(id=f'{team1.id}-{team2.id}', home_team=team1, away_team=team2, classic_match=False, round=None) + for team1 in teams + for team2 in teams + if team1 != team2 + ] + + # 5% classic matches + apply_random_value( + count=int(len(matches) * 0.05), + values = matches, + filter_func = lambda match_league: not match_league.classic_match, + consumer_func = lambda match_league: setattr(match_league, 'classic_match', True) + ) + + # Ensure reciprocity for classic matches + for match in matches: + if match.classic_match: + reciprocal_match = next((m for m in matches if m.home_team == match.away_team and m.away_team == match.home_team), None) + if reciprocal_match: + reciprocal_match.classic_match = True + + return matches + + +def apply_random_value(count: int, values: List, filter_func: Callable, consumer_func: Callable) -> None: + filtered_values = [value for value in values if filter_func(value)] + size = len(filtered_values) + + for _ in range(count): + if size > 0: + selected_value = random.choice(filtered_values) + consumer_func(selected_value) + filtered_values.remove(selected_value) + size -= 1 + else: + break + + +def generate_demo_data_test() -> LeagueSchedule: + count_rounds = 32 + # Rounds + rounds = generate_rounds(count_rounds) + # Teams + teams = generate_teams() + # Matches + matches = generate_matches(teams) + + # Create Schedule + schedule = LeagueSchedule( + id="demo-schedule", + rounds=rounds, + teams=teams, + matches=matches, + score=None, + solver_status=None + ) + + return schedule \ No newline at end of file diff --git a/python/sports-league-scheduling/src/sports_league_scheduling/domain.py b/python/sports-league-scheduling/src/sports_league_scheduling/domain.py new file mode 100644 index 0000000000..f5b8346b6c --- /dev/null +++ b/python/sports-league-scheduling/src/sports_league_scheduling/domain.py @@ -0,0 +1,113 @@ +from timefold.solver import SolverStatus +from timefold.solver.domain import (planning_entity, planning_solution, PlanningId, PlanningVariable, + PlanningEntityCollectionProperty, + ProblemFactCollectionProperty, ValueRangeProvider, + PlanningScore) +from timefold.solver.score import HardSoftScore +from typing import Dict, List, Any, Annotated + +from .json_serialization import * + + +class Team(JsonDomainBase): + id: str + name: str + distance_to_team: Annotated[Dict[str, int], + DistanceToTeamValidator, + Field(default_factory=dict)] + + + def __eq__(self, other): + if self is other: # Check if both references are the same + return True + if not isinstance(other, Team): # Ensure the other object is a Team instance + return False + return self.id == other.id # Compare IDs for equality + + def __hash__(self): + return hash(self.id) + + def __str__(self): + return self.id + + def __repr__(self): + return f'Team({self.id}, {self.name}, {self.distance_to_team})' + + +class Round(JsonDomainBase): + index: Annotated[int, PlanningId] + # Rounds scheduled on weekends and holidays. It's common for classic matches to be scheduled on weekends or holidays. + weekend_or_holiday: bool + + + def __eq__(self, other): + if self is other: + return True + if not isinstance(other, Round): + return False + return self.index == other.index + + def __hash__(self): + return 31 * self.index + + def __str__(self): + return f'Round-{self.index}' + + def __repr__(self): + return f'Round({self.index}, {self.weekendOrHoliday})' + + +@planning_entity +class Match(JsonDomainBase): + id: Annotated[str, PlanningId] + home_team: Annotated[Team | None, + IdStrSerializer, + TeamDeserializer, + Field(default=None)] + away_team: Annotated[Team | None, + IdStrSerializer, + TeamDeserializer, + Field(default=None)] + # A classic/important match can impact aspects like revenue (e.g., derby) + classic_match: Annotated[bool, Field(default=False)] + round: Annotated[Round | None, + PlanningVariable, + IdIntSerializer, + RoundDeserializer, + Field(default=None)] + + + def __eq__(self, other): + if self is other: + return True + if not isinstance(other, Match): + return False + return self.id == other.id + + def __hash__(self): + return hash(self.id) + + def __str__(self): + return f'{self.home_team} + {self.away_team}' + + def __repr__(self): + return f'Match({self.id}, {self.home_team}, {self.away_team}, {self.classic_match})' + + +@planning_solution +class LeagueSchedule(JsonDomainBase): + id: str + rounds: Annotated[list[Round], + ProblemFactCollectionProperty, + ValueRangeProvider] + teams: Annotated[list[Team], + ProblemFactCollectionProperty, + ValueRangeProvider] + matches: Annotated[list[Match], + PlanningEntityCollectionProperty] + score: Annotated[HardSoftScore | None, + PlanningScore, + ScoreSerializer, + ScoreValidator, + Field(default=None)] + solver_status: Annotated[SolverStatus | None, Field(default=SolverStatus.NOT_SOLVING)] \ No newline at end of file diff --git a/python/sports-league-scheduling/src/sports_league_scheduling/json_serialization.py b/python/sports-league-scheduling/src/sports_league_scheduling/json_serialization.py new file mode 100644 index 0000000000..e6784cbd96 --- /dev/null +++ b/python/sports-league-scheduling/src/sports_league_scheduling/json_serialization.py @@ -0,0 +1,71 @@ +from timefold.solver.score import HardSoftScore +from pydantic import BaseModel, ConfigDict, Field, PlainSerializer, BeforeValidator, ValidationInfo +from pydantic.alias_generators import to_camel +from typing import Any, Dict + + +def make_list_item_validator(key: str): + def validator(v: Any, info: ValidationInfo) -> Any: + if v is None: + return None + + if not isinstance(v, (str, int)): + return v + + if not info.context or key not in info.context: + raise ValueError(f"Context is missing or does not contain key '{key}'.") + + context_data = info.context.get(key) + if v not in context_data: + raise ValueError(f"Value '{v}' not found in context for key '{key}'.") + + return context_data[v] + + return BeforeValidator(validator) + + +RoundDeserializer = make_list_item_validator('rounds') +TeamDeserializer = make_list_item_validator('teams') + +IdStrSerializer = PlainSerializer( + lambda item: item.id if item is not None else None, + return_type=str | None +) +IdIntSerializer = PlainSerializer( + lambda item: item.index if item is not None else None, + return_type=int | None +) +ScoreSerializer = PlainSerializer(lambda score: str(score) if score is not None else None, + return_type=str | None) + + +def validate_score(v: Any, info: ValidationInfo) -> Any: + if isinstance(v, HardSoftScore) or v is None: + return v + if isinstance(v, str): + return HardSoftScore.parse(v) + raise ValueError('"score" should be a string') + + +def validate_distance_to_team(value: Any, info: ValidationInfo) -> Dict[str, int]: + if not isinstance(value, dict): + raise ValueError("distance_to_team must be a dictionary.") + + for key, val in value.items(): + if not isinstance(key, str): + raise ValueError(f"Key {key} in distance_to_team must be a Team instance.") + if not isinstance(val, int): + raise ValueError(f"Value for {key} must be an integer.") + + return value + + +ScoreValidator = BeforeValidator(validate_score) +DistanceToTeamValidator = BeforeValidator(validate_distance_to_team) + +class JsonDomainBase(BaseModel): + model_config = ConfigDict( + alias_generator=to_camel, + populate_by_name=True, + from_attributes=True, + ) diff --git a/python/sports-league-scheduling/src/sports_league_scheduling/rest_api.py b/python/sports-league-scheduling/src/sports_league_scheduling/rest_api.py new file mode 100644 index 0000000000..dd15ccf31b --- /dev/null +++ b/python/sports-league-scheduling/src/sports_league_scheduling/rest_api.py @@ -0,0 +1,111 @@ +from fastapi import FastAPI, Depends, Request +from fastapi.staticfiles import StaticFiles +from typing import Annotated, Final +from uuid import uuid4 +from datetime import datetime +from .domain import * +from .score_analysis import * +from .demo_data import generate_demo_data_test +from .solver import solver_manager, solution_manager + +app = FastAPI(docs_url='/q/swagger-ui') +MAX_JOBS_CACHE_SIZE: Final[int] = 2 +data_sets: Dict[str, dict] = {} + + +@app.get("/demo-data") +async def get_demo_data(): + return generate_demo_data_test() + + +async def setup_context(request: Request) -> LeagueSchedule: + json = await request.json() + return LeagueSchedule.model_validate(json, + context={ + 'rounds': { + match_round['index']: Round.model_validate(match_round) for + match_round in json.get('rounds', []) + }, + 'teams': { + team['id']: Team.model_validate(team) for + team in json.get('teams', []) + }, + }) + + +def clean_jobs(): + """ + The method retains only the records of the last MAX_JOBS_CACHE_SIZE completed jobs by removing the oldest ones. + """ + global data_sets + if len(data_sets) <= MAX_JOBS_CACHE_SIZE: + return + + completed_jobs = [ + (job_id, job_data) + for job_id, job_data in data_sets.items() + if job_data["schedule"] is not None + ] + + completed_jobs.sort(key=lambda job: job[1]["created_at"]) + + for job_id, _ in completed_jobs[:len(completed_jobs) - MAX_JOBS_CACHE_SIZE]: + del data_sets[job_id] + + +def update_league_schedule(problem_id: str, league_schedule: LeagueSchedule): + global data_sets + data_sets[problem_id]["schedule"] = league_schedule + + +@app.post("/schedules") +async def solve_schedule(league_schedule: Annotated[LeagueSchedule, Depends(setup_context)]) -> str: + job_id = str(uuid4()) + data_sets[job_id] = { + "schedule": league_schedule, + "created_at": datetime.now(), + "exception": None, + } + solver_manager.solve_and_listen(job_id, league_schedule, + lambda solution: update_league_schedule(job_id, solution)) + clean_jobs() + return job_id + + +@app.get("/schedules/{problem_id}") +async def get_league_schedule(problem_id: str) -> LeagueSchedule: + league_schedule = data_sets[problem_id]["schedule"] + return league_schedule.model_copy(update={ + 'solver_status': solver_manager.get_solver_status(problem_id) + }) + + +@app.get("/schedules/{job_id}/status") +async def get_schedule_status(job_id: str) -> dict: + league_schedule = data_sets[job_id]["schedule"] + return {"solver_status": league_schedule.solver_status} + + +@app.put("/schedules/analyze") +async def analyze_timetable(league_schedule: Annotated[LeagueSchedule, Depends(setup_context)]) -> dict: + return {'constraints': [ConstraintAnalysisDTO( + name=constraint.constraint_name, + weight=constraint.weight, + score=constraint.score, + matches=[ + MatchAnalysisDTO( + name=match.constraint_ref.constraint_name, + score=match.score, + justification=match.justification + ) + for match in constraint.matches + ] + ) for constraint in solution_manager.analyze(league_schedule).constraint_analyses]} + + +@app.delete("/schedules/{problem_id}") +async def stop_solving(problem_id: str) -> None: + solver_manager.terminate_early(problem_id) + + +app.mount("/", StaticFiles(directory="static", html=True), name="static") diff --git a/python/sports-league-scheduling/src/sports_league_scheduling/score_analysis.py b/python/sports-league-scheduling/src/sports_league_scheduling/score_analysis.py new file mode 100644 index 0000000000..3884952fe7 --- /dev/null +++ b/python/sports-league-scheduling/src/sports_league_scheduling/score_analysis.py @@ -0,0 +1,18 @@ +from timefold.solver.score import ConstraintJustification +from dataclasses import dataclass, field + +from .json_serialization import * +from .domain import * + + +class MatchAnalysisDTO(JsonDomainBase): + name: str + score: Annotated[HardSoftScore, ScoreSerializer] + justification: object + + +class ConstraintAnalysisDTO(JsonDomainBase): + name: str + weight: Annotated[HardSoftScore, ScoreSerializer] + matches: list[MatchAnalysisDTO] + score: Annotated[HardSoftScore, ScoreSerializer] diff --git a/python/sports-league-scheduling/src/sports_league_scheduling/solver.py b/python/sports-league-scheduling/src/sports_league_scheduling/solver.py new file mode 100644 index 0000000000..96275ece1a --- /dev/null +++ b/python/sports-league-scheduling/src/sports_league_scheduling/solver.py @@ -0,0 +1,21 @@ +from timefold.solver import SolverManager, SolverFactory, SolutionManager +from timefold.solver.config import (SolverConfig, ScoreDirectorFactoryConfig, + TerminationConfig, Duration) + +from .domain import * +from .constraints import define_constraints + + +solver_config = SolverConfig( + solution_class=LeagueSchedule, + entity_class_list=[Match], + score_director_factory_config=ScoreDirectorFactoryConfig( + constraint_provider_function=define_constraints + ), + termination_config=TerminationConfig( + spent_limit=Duration(seconds=30) + ) +) + +solver_manager = SolverManager.create(SolverFactory.create(solver_config)) +solution_manager = SolutionManager.create(solver_manager) diff --git a/python/sports-league-scheduling/static/app.js b/python/sports-league-scheduling/static/app.js new file mode 100644 index 0000000000..fea847c161 --- /dev/null +++ b/python/sports-league-scheduling/static/app.js @@ -0,0 +1,322 @@ +let autoRefreshIntervalId = null; +const formatter = JSJoda.DateTimeFormatter.ofPattern("MM/dd/YYYY HH:mm").withLocale(JSJodaLocale.Locale.ENGLISH); + +const zoomMin = 1000 * 60 * 60 * 24 // 1 day in milliseconds +const zoomMax = 1000 * 60 * 60 * 24 * 7 * 4 // 2 weeks in milliseconds + +const byTimelineOptions = { + timeAxis: {scale: "day"}, + orientation: {axis: "top"}, + stack: false, + xss: {disabled: true}, // Items are XSS safe through JQuery + zoomMin: zoomMin, + showCurrentTime: false, +}; + +const byTeamPanel = document.getElementById("byTeamPanel"); +let byTeamGroupData = new vis.DataSet(); +let byTeamItemData = new vis.DataSet(); +let byTeamTimeline = new vis.Timeline(byTeamPanel, byTeamItemData, byTeamGroupData, byTimelineOptions); + +let scheduleId = null; +let loadedSchedule = null; +let viewType = "T"; + +$(document).ready(function () { + replaceQuickstartTimefoldAutoHeaderFooter(); + + $("#solveButton").click(function () { + solve(); + }); + $("#stopSolvingButton").click(function () { + stopSolving(); + }); + $("#analyzeButton").click(function () { + analyze(); + }); + $("#byTeamTab").click(function () { + viewType = "T"; + refreshSchedule(); + }); + + setupAjax(); + refreshSchedule(); +}); + +function setupAjax() { + $.ajaxSetup({ + headers: { + 'Content-Type': 'application/json', 'Accept': 'application/json,text/plain', // plain text is required by solve() returning UUID of the solver job + } + }); + + // Extend jQuery to support $.put() and $.delete() + jQuery.each(["put", "delete"], function (i, method) { + jQuery[method] = function (url, data, callback, type) { + if (jQuery.isFunction(data)) { + type = type || callback; + callback = data; + data = undefined; + } + return jQuery.ajax({ + url: url, type: method, dataType: type, data: data, success: callback + }); + }; + }); +} + +function refreshSchedule() { + let path = "/schedules/" + scheduleId; + if (scheduleId === null) { + path = "/demo-data"; + } + + $.getJSON(path, function (schedule) { + loadedSchedule = schedule; + $('#exportData').attr('href', 'data:text/plain;charset=utf-8,' + JSON.stringify(loadedSchedule)); + renderSchedule(schedule); + }) + .fail(function (xhr, ajaxOptions, thrownError) { + showError("Getting the schedule has failed.", xhr); + refreshSolvingButtons(false); + }); +} + +function renderSchedule(schedule) { + refreshSolvingButtons(schedule.solverStatus != null && schedule.solverStatus !== "NOT_SOLVING"); + $("#score").text("Score: " + (schedule.score == null ? "?" : schedule.score)); + + if (viewType === "T") { + renderScheduleByTeam(schedule); + } +} + +function renderScheduleByTeam(schedule) { + const unassigned = $("#unassigned"); + unassigned.children().remove(); + byTeamGroupData.clear(); + byTeamItemData.clear(); + + const teamMap = new Map(); + $.each(schedule.teams.sort((t1, t2) => t1.name.localeCompare(t2.name)), (_, team) => { + teamMap.set(team.id, team); + let content = `

${team.name}
`; + byTeamGroupData.add({ + id: team.id, + content: content, + }); + }); + + + const currentDate = JSJoda.LocalDate.now(); + $.each(schedule.matches, (_, match) => { + const homeTeam = teamMap.get(match.homeTeam); + const awayTeam = teamMap.get(match.awayTeam); + if (match.round == null) { + const unassignedElement = $(`
`) + .append($(`
`).text(`${homeTeam.name} x ${awayTeam.name}`)); + + unassigned.append($(`
`).append($(`
`).append(unassignedElement))); + } else { + const byHomeTeamElement = $("
").append($("
").append($(`
`).text(awayTeam.name))).append($(``).append("")); + const byAwayTeamElement = $("
").append($("
").append($(`
`).text(homeTeam.name))).append($(``).append("")); + byTeamItemData.add({ + id: `${match.id}-1`, + group: homeTeam.id, + content: byHomeTeamElement.html(), + start: currentDate.plusDays(match.round).toString(), + end: currentDate.plusDays(match.round + 1).toString(), + style: `background-color: ${match.classicMatch ? '#198754CF' : '#97b0f8'}` + }); + byTeamItemData.add({ + id: `${match.id}-2`, + group: awayTeam.id, + content: byAwayTeamElement.html(), + start: currentDate.plusDays(match.round).toString(), + end: currentDate.plusDays(match.round + 1).toString(), + style: `background-color: ${match.classicMatch ? '#198754CF' : '#97b0f8'}` + }); + } + }); + + byTeamTimeline.setWindow(JSJoda.LocalDate.now().toString(), JSJoda.LocalDate.now().plusDays(7).toString()); +} + +function solve() { + $.post("/schedules", JSON.stringify(loadedSchedule), function (data) { + scheduleId = data; + refreshSolvingButtons(true); + }).fail(function (xhr, ajaxOptions, thrownError) { + showError("Start solving failed.", xhr); + refreshSolvingButtons(false); + }, "text"); +} + +function analyze() { + new bootstrap.Modal("#scoreAnalysisModal").show() + const scoreAnalysisModalContent = $("#scoreAnalysisModalContent"); + scoreAnalysisModalContent.children().remove(); + if (loadedSchedule.score == null || loadedSchedule.score.indexOf('init') != -1) { + scoreAnalysisModalContent.text("No score to analyze yet, please first press the 'solve' button."); + } else { + $('#scoreAnalysisScoreLabel').text(`(${loadedSchedule.score})`); + $.put("/schedules/analyze", JSON.stringify(loadedSchedule), function (scoreAnalysis) { + let constraints = scoreAnalysis.constraints; + constraints.sort((a, b) => { + let aComponents = getScoreComponents(a.score), bComponents = getScoreComponents(b.score); + if (aComponents.hard < 0 && bComponents.hard > 0) return -1; + if (aComponents.hard > 0 && bComponents.soft < 0) return 1; + if (Math.abs(aComponents.hard) > Math.abs(bComponents.hard)) { + return -1; + } else { + if (aComponents.medium < 0 && bComponents.medium > 0) return -1; + if (aComponents.medium > 0 && bComponents.medium < 0) return 1; + if (Math.abs(aComponents.medium) > Math.abs(bComponents.medium)) { + return -1; + } else { + if (aComponents.soft < 0 && bComponents.soft > 0) return -1; + if (aComponents.soft > 0 && bComponents.soft < 0) return 1; + + return Math.abs(bComponents.soft) - Math.abs(aComponents.soft); + } + } + }); + constraints.map((e) => { + let components = getScoreComponents(e.weight); + e.type = components.hard != 0 ? 'hard' : (components.medium != 0 ? 'medium' : 'soft'); + e.weight = components[e.type]; + let scores = getScoreComponents(e.score); + e.implicitScore = scores.hard != 0 ? scores.hard : (scores.medium != 0 ? scores.medium : scores.soft); + }); + scoreAnalysis.constraints = constraints; + + scoreAnalysisModalContent.children().remove(); + scoreAnalysisModalContent.text(""); + + const analysisTable = $(``).css({textAlign: 'center'}); + const analysisTHead = $(``).append($(``) + .append($(``)) + .append($(``).css({textAlign: 'left'})) + .append($(``)) + .append($(``)) + .append($(``)) + .append($(``)) + .append($(``))); + analysisTable.append(analysisTHead); + const analysisTBody = $(``) + $.each(scoreAnalysis.constraints, (index, constraintAnalysis) => { + let icon = constraintAnalysis.type == "hard" && constraintAnalysis.implicitScore < 0 ? '' : ''; + if (!icon) icon = constraintAnalysis.matches.length == 0 ? '' : ''; + + let row = $(``); + row.append($(`
ConstraintType# MatchesWeightScore
`).html(icon)) + .append($(``).text(constraintAnalysis.name).css({textAlign: 'left'})) + .append($(``).text(constraintAnalysis.type)) + .append($(``).html(`${constraintAnalysis.matches.length}`)) + .append($(``).text(constraintAnalysis.weight)) + .append($(``).text(constraintAnalysis.implicitScore)); + analysisTBody.append(row); + row.append($(``)); + }); + analysisTable.append(analysisTBody); + scoreAnalysisModalContent.append(analysisTable); + }).fail(function (xhr, ajaxOptions, thrownError) { + showError("Analyze failed.", xhr); + }, "text"); + } +} + +function getScoreComponents(score) { + let components = {hard: 0, medium: 0, soft: 0}; + + $.each([...score.matchAll(/(-?[0-9]+)(hard|medium|soft)/g)], (i, parts) => { + components[parts[2]] = parseInt(parts[1], 10); + }); + + return components; +} + +function refreshSolvingButtons(solving) { + if (solving) { + $("#solveButton").hide(); + $("#stopSolvingButton").show(); + if (autoRefreshIntervalId == null) { + autoRefreshIntervalId = setInterval(refreshSchedule, 2000); + } + } else { + $("#solveButton").show(); + $("#stopSolvingButton").hide(); + if (autoRefreshIntervalId != null) { + clearInterval(autoRefreshIntervalId); + autoRefreshIntervalId = null; + } + } +} + +function stopSolving() { + $.delete("/schedules/" + scheduleId, function () { + refreshSolvingButtons(false); + refreshSchedule(); + }).fail(function (xhr, ajaxOptions, thrownError) { + showError("Stop solving failed.", xhr); + }); +} + +function copyTextToClipboard(id) { + const text = $("#" + id).text().trim(); + + const dummy = document.createElement("textarea"); + document.body.appendChild(dummy); + dummy.value = text; + dummy.select(); + document.execCommand("copy"); + document.body.removeChild(dummy); +} + +// TODO: move to the webjar +function replaceQuickstartTimefoldAutoHeaderFooter() { + const timefoldHeader = $("header#timefold-auto-header"); + if (timefoldHeader != null) { + timefoldHeader.addClass("bg-black") + timefoldHeader.append($(`
+ +
`)); + } + + const timefoldFooter = $("footer#timefold-auto-footer"); + if (timefoldFooter != null) { + timefoldFooter.append($(``)); + } +} diff --git a/python/sports-league-scheduling/static/index.html b/python/sports-league-scheduling/static/index.html new file mode 100644 index 0000000000..1d17ecf73a --- /dev/null +++ b/python/sports-league-scheduling/static/index.html @@ -0,0 +1,159 @@ + + + + + + Sports League Scheduling - Timefold Solver on Python + + + + + + + + +
+ +
+
+
+
+
+
+

Sports League Scheduling Solver

+

Generate the optimal schedule for your sports league matches.

+ +
+ + + Score: ? + + +
+ +
+
+
+
+
+
+
+ +

Unassigned

+
+
+ +
+

REST API Guide

+ +

Sports League Scheduling solver integration via cURL

+ +

1. Download demo data

+
+            
+            curl -X GET -H 'Accept:application/json' http://localhost:8080/demo-data -o sample.json
+    
+ +

2. Post the sample data for solving

+

The POST operation returns a jobId that should be used in subsequent commands.

+
+            
+            curl -X POST -H 'Content-Type:application/json' http://localhost:8080/schedules -d@sample.json
+    
+ +

3. Get the current status and score

+
+            
+            curl -X GET -H 'Accept:application/json' http://localhost:8080/schedules/{jobId}/status
+    
+ +

4. Get the complete solution

+
+            
+            curl -X GET -H 'Accept:application/json' http://localhost:8080/schedules/{jobId} -o solution.json
+    
+ +

5. Fetch the analysis of the solution

+
+            
+            curl -X PUT -H 'Content-Type:application/json' http://localhost:8080/schedules/analyze -d@solution.json
+    
+ +

6. Terminate solving early

+
+            
+            curl -X DELETE -H 'Accept:application/json' http://localhost:8080/schedules/{jobId}
+    
+
+ +
+

REST API Reference

+
+ + +
+
+
+
+ + + + + + + + + + + + diff --git a/python/sports-league-scheduling/static/webjars/timefold/css/timefold-webui.css b/python/sports-league-scheduling/static/webjars/timefold/css/timefold-webui.css new file mode 100644 index 0000000000..0d729db03d --- /dev/null +++ b/python/sports-league-scheduling/static/webjars/timefold/css/timefold-webui.css @@ -0,0 +1,60 @@ +:root { + /* Keep in sync with .navbar height on a large screen. */ + --ts-navbar-height: 109px; + + --ts-violet-1-rgb: #3E00FF; + --ts-violet-2-rgb: #3423A6; + --ts-violet-3-rgb: #2E1760; + --ts-violet-4-rgb: #200F4F; + --ts-violet-5-rgb: #000000; /* TODO FIXME */ + --ts-violet-dark-1-rgb: #b6adfd; + --ts-violet-dark-2-rgb: #c1bbfd; + --ts-gray-rgb: #666666; + --ts-white-rgb: #FFFFFF; + --ts-light-rgb: #F2F2F2; + --ts-gray-border: #c5c5c5; + + --tf-light-rgb-transparent: rgb(242,242,242,0.5); /* #F2F2F2 = rgb(242,242,242) */ + --bs-body-bg: var(--ts-light-rgb); /* link to html bg */ + --bs-link-color: var(--ts-violet-1-rgb); + --bs-link-hover-color: var(--ts-violet-2-rgb); + + --bs-navbar-color: var(--ts-white-rgb); + --bs-navbar-hover-color: var(--ts-white-rgb); + --bs-nav-link-font-size: 18px; + --bs-nav-link-font-weight: 400; + --bs-nav-link-color: var(--ts-white-rgb); + --ts-nav-link-hover-border-color: var(--ts-violet-1-rgb); +} +.btn { + --bs-btn-border-radius: 1.5rem; +} +.btn-primary { + --bs-btn-bg: var(--ts-violet-1-rgb); + --bs-btn-border-color: var(--ts-violet-1-rgb); + --bs-btn-hover-bg: var(--ts-violet-2-rgb); + --bs-btn-hover-border-color: var(--ts-violet-2-rgb); + --bs-btn-active-bg: var(--ts-violet-2-rgb); + --bs-btn-active-border-bg: var(--ts-violet-2-rgb); + --bs-btn-disabled-bg: var(--ts-violet-1-rgb); + --bs-btn-disabled-border-color: var(--ts-violet-1-rgb); +} +.btn-outline-primary { + --bs-btn-color: var(--ts-violet-1-rgb); + --bs-btn-border-color: var(--ts-violet-1-rgb); + --bs-btn-hover-bg: var(--ts-violet-1-rgb); + --bs-btn-hover-border-color: var(--ts-violet-1-rgb); + --bs-btn-active-bg: var(--ts-violet-1-rgb); + --bs-btn-active-border-color: var(--ts-violet-1-rgb); + --bs-btn-disabled-color: var(--ts-violet-1-rgb); + --bs-btn-disabled-border-color: var(--ts-violet-1-rgb); +} +.navbar-dark { + --bs-link-color: var(--ts-violet-dark-1-rgb); + --bs-link-hover-color: var(--ts-violet-dark-2-rgb); + --bs-navbar-color: var(--ts-white-rgb); + --bs-navbar-hover-color: var(--ts-white-rgb); +} +.nav-pills { + --bs-nav-pills-link-active-bg: var(--ts-violet-1-rgb); +} diff --git a/python/sports-league-scheduling/static/webjars/timefold/img/timefold-favicon.svg b/python/sports-league-scheduling/static/webjars/timefold/img/timefold-favicon.svg new file mode 100644 index 0000000000..f5bece2d39 --- /dev/null +++ b/python/sports-league-scheduling/static/webjars/timefold/img/timefold-favicon.svg @@ -0,0 +1,25 @@ + + + + +Created by potrace 1.14, written by Peter Selinger 2001-2017 + + + + + diff --git a/python/sports-league-scheduling/static/webjars/timefold/img/timefold-logo-horizontal-negative.svg b/python/sports-league-scheduling/static/webjars/timefold/img/timefold-logo-horizontal-negative.svg new file mode 100644 index 0000000000..26aa96ab2f --- /dev/null +++ b/python/sports-league-scheduling/static/webjars/timefold/img/timefold-logo-horizontal-negative.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/python/sports-league-scheduling/static/webjars/timefold/img/timefold-logo-horizontal-positive.svg b/python/sports-league-scheduling/static/webjars/timefold/img/timefold-logo-horizontal-positive.svg new file mode 100644 index 0000000000..12cf1da644 --- /dev/null +++ b/python/sports-league-scheduling/static/webjars/timefold/img/timefold-logo-horizontal-positive.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/python/sports-league-scheduling/static/webjars/timefold/img/timefold-logo-stacked-positive.svg b/python/sports-league-scheduling/static/webjars/timefold/img/timefold-logo-stacked-positive.svg new file mode 100644 index 0000000000..7c871643b2 --- /dev/null +++ b/python/sports-league-scheduling/static/webjars/timefold/img/timefold-logo-stacked-positive.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/python/sports-league-scheduling/static/webjars/timefold/js/timefold-webui.js b/python/sports-league-scheduling/static/webjars/timefold/js/timefold-webui.js new file mode 100644 index 0000000000..dc8853c3f4 --- /dev/null +++ b/python/sports-league-scheduling/static/webjars/timefold/js/timefold-webui.js @@ -0,0 +1,142 @@ +function replaceTimefoldAutoHeaderFooter() { + const timefoldHeader = $("header#timefold-auto-header"); + if (timefoldHeader != null) { + timefoldHeader.addClass("bg-black") + timefoldHeader.append( + $(`
+ +
`)); + } + const timefoldFooter = $("footer#timefold-auto-footer"); + if (timefoldFooter != null) { + timefoldFooter.append( + $(``)); + + applicationInfo(); + } + +} + +function showSimpleError(title) { + const notification = $(`