API¶
Arenas¶
Arena and corner loading routines.
- class sr.comp.arenas.Arena(name, display_name, colour)[source]¶
Bases:
NamedTuple- colour: Colour¶
Alias for field number 2
- name: ArenaName¶
Alias for field number 0
- class sr.comp.arenas.Corner(number, colour)[source]¶
Bases:
NamedTuple- colour: Colour¶
Alias for field number 1
- number: CornerNumber¶
Alias for field number 0
Competition¶
Core competition functions.
- class sr.comp.comp.SRComp(root: str | Path)[source]¶
Bases:
objectA class containing all the various parts of a competition.
- Parameters:
root (Path) – The root path of the
compstaterepo.
- arenas¶
A
collections.OrderedDictmapping arena names tosr.comp.arenas.Arenaobjects.
- awards¶
A
dictmappingsr.comp.winners.Awardobjects to alistof teams.
- corners¶
A
collections.OrderedDictmapping corner numbers tosr.comp.arenas.Cornerobjects.
- schedule¶
A
sr.comp.matches.MatchScheduleinstance.
- scores¶
A
sr.comp.scores.Scoresinstance.
- state¶
The current commit of the Compstate repository.
- teams¶
A mapping of TLAs to
sr.comp.teams.Teamobjects.
- timezone¶
The timezone of the competition.
- venue¶
A
sr.comp.venue.Venueinstance.
Knockout Schedulers¶
Knockout schedule generation.
- class sr.comp.knockout_scheduler.base_scheduler.BaseKnockoutScheduler(schedule: ScheduleHost, scores: Scores, arenas: Iterable[ArenaName], num_teams_per_arena: int, teams: Mapping[TLA, Team], config: TConfig)[source]¶
Bases:
Generic[TConfig]Base class for knockout schedulers offering common functionality.
- Parameters:
- add_knockouts() None[source]¶
Add the knockouts to the schedule.
Derived classes must override this method.
- static get_match_display_name(rounds_remaining: int, num_within_round: int, global_num: MatchNumber) str[source]¶
Get a human-readable match display name.
- Parameters:
rounds_remaining – The number of knockout rounds remaining.
num_within_round – The match number within the knockout round.
global_num – The global match number.
- get_ranking(game: Match) list[TLA][source]¶
Get a ranking of the given match’s teams.
- Parameters:
game – A game.
- static get_round_display_name(round_number: int, rounds_remaining: int) str[source]¶
Get a human-readable knockout round display name.
- Parameters:
round_number – The round number within the knockouts. This should be a 0-indexed number.
rounds_remaining – The number of knockout rounds remaining.
- knockout_brackets¶
Brackets which make up the knockout.
This currently has no bearing on the actual matches and is purely a display consideration.
- num_teams_per_arena¶
The number of spaces for teams in an arena.
This is used in building matches where we don’t yet know which teams will actually be playing, and for filling in when there aren’t enough teams to fill the arena.
- class sr.comp.knockout_scheduler.KnockoutScheduler(schedule: ScheduleHost, scores: Scores, arenas: Iterable[ArenaName], num_teams_per_arena: int, teams: Mapping[TLA, Team], config: AutoKnockoutScheduleData)[source]¶
Bases:
BaseKnockoutScheduler[AutoKnockoutScheduleData]A class that can be used to generate a knockout schedule based on seeding.
Due to the way the seeding logic works, this class is suitable only when games feature four slots for competitors, with the top two progressing to the next round.
- Parameters:
- add_knockouts() None[source]¶
Add the knockouts to the schedule.
Derived classes must override this method.
- get_winners(game: Match) list[TLA][source]¶
Find the parent match’s winners.
- Parameters:
game – A game.
- num_teams_per_arena = 4¶
Constant value due to the way the automatic seeding works.
- class sr.comp.knockout_scheduler.StaticScheduler(schedule: ScheduleHost, scores: Scores, arenas: Iterable[ArenaName], num_teams_per_arena: int, teams: Mapping[TLA, Team], config: StaticKnockoutScheduleData)[source]¶
Bases:
BaseKnockoutScheduler[StaticKnockoutScheduleData]A knockout scheduler which loads almost fixed data from the config. Assumes only a single arena.
Due to the nature of its interaction with the seedings, this scheduler has a very limited handling of dropped-out teams: it only adjusts its scheduling for dropouts before the knockouts.
- The practical results of this dropout behaviour are:
the schedule is stable when teams drop out, as this either affects the entire knockout or none of it
dropping out a team such that there are no longer enough seeds requires manual changes to the schedule to remove the seeds which cannot be filled
Stable Random¶
A stable random number generator implementation.
- class sr.comp.knockout_scheduler.stable_random.Random[source]¶
Bases:
objectOur own random number generator that is guaranteed to be stable.
Python’s random number generator’s stability across Python versions is complicated. Different versions will produce different results. It’s easier right now to just have our own random number generator that’s not as good, but is definitely stable between machines.
Note
This class is deliberately not a sub-class of
random.Randomsince any of the functionality provided by the class (i.e. not just the generation portion) could change between Python versions. Instead, any additionally required functionality should be added below as needed and _importantly_ tests for the functionality to ensure that the output is the same on all supported platforms.- shuffle(x: MutableSequence[T]) None[source]¶
Match Period¶
Classes that are useful for dealing with match periods.
- class sr.comp.match_period.Delay(delay: 'datetime.timedelta', time: 'datetime.datetime')[source]¶
Bases:
object
- class sr.comp.match_period.KnockoutMatch(num: 'MatchNumber', display_name: 'str', arena: 'ArenaName', teams: 'list[TLA | None]', start_time: 'datetime.datetime', end_time: 'datetime.datetime', type: 'Literal[MatchType.knockout]', use_resolved_ranking: 'bool', knockout_bracket: 'str')[source]¶
Bases:
Match
- class sr.comp.match_period.Match(num: 'MatchNumber', display_name: 'str', arena: 'ArenaName', teams: 'list[TLA | None]', start_time: 'datetime.datetime', end_time: 'datetime.datetime', type: 'MatchType', use_resolved_ranking: 'bool')[source]¶
Bases:
object- arena: ArenaName¶
- num: MatchNumber¶
Match Period Clock¶
A clock to manage match periods.
- class sr.comp.match_period_clock.MatchPeriodClock(period: MatchPeriod, delays: Iterable[Delay])[source]¶
Bases:
objectA clock for use in scheduling matches within a
MatchPeriod.It is generally expected that the time information here will be in the form of
datetimeandtimedeltainstances, though any data which can be compared and added appropriately should work.Delay rules:
Only delays which are scheduled after the start of the given period will be considered.
Delays are cumulative.
Delays take effect as soon as their
timeis reached.
- advance_time(duration: timedelta) None[source]¶
Make a given amount of time pass. This is expected to be called after scheduling some matches in order to move to the next timeslot.
Note
It is assumed that the duration value will always be ‘positive’, i.e. that time will not go backwards. The results of the duration value being ‘negative’ are undefined.
- apply_spacing(spacing: Spacing, recover_time: Callable[[Delay], None]) None[source]¶
Apply a given spacing to make some time pass. This is expected to be called when inserting a variable length gap between matches.
The
recover_timeparameter should be a callable which updates the central view of delays in the system and will be called with a negative delay when time is recovered.
- property current_time: datetime¶
Get the apparent current time. This is a combination of the time which has passed (through calls to
advance_time) and the delays which have occurred.Will raise an
OutOfTimeExceptionif either:the end of the period has been reached (i.e: the sum of durations passed to
advance_timehas exceeded the planned duration of the period), orthe maximum end of the period has been reached (i.e: the current time would be after the period’s
max_end_time).
- static delays_for_period(period: MatchPeriod, delays: Iterable[Delay]) list[Delay][source]¶
Filter and sort a list of all possible delays to include only those which occur after the start of the given period.
- iterslots(slot_duration: timedelta) Iterator[datetime][source]¶
Iterate through all the available timeslots of the given size within the
MatchPeriod, taking into account delays.This is equivalent to checking the current_time and repeatedly calling
advance_timewith the given duration. As a result, it is safe to calladvance_timebetween iterations if additional gaps between slots are needed.
- exception sr.comp.match_period_clock.OutOfTimeException[source]¶
Bases:
ExceptionAn exception representing no more time available at the competition to run matches.
Matches¶
Match schedule library.
- class sr.comp.matches.MatchSchedule(y: ScheduleData, league: LeagueMatches, teams: Mapping[TLA, Team], num_teams_per_arena: int)[source]¶
Bases:
objectA match schedule.
- add_tiebreaker(scores: Scores, time: datetime) None[source]¶
Add a tie breaker to the league if required. Also set a
tiebreakerattribute if necessary.- Parameters:
scores (.Scores) – The scores for the competition.
time (datetime.datetime) – The time to have the tiebreaker match.
- classmethod create(config_fname: Path, league_fname: Path, knockout_fname: Path, scores: Scores, arenas: Mapping[ArenaName, Arena], num_teams_per_arena: int, teams: Mapping[TLA, Team]) TSchedule[source]¶
Create a new match schedule around the given config data.
- Parameters:
config_fname (Path) – The filename of the main config file.
league_fname (Path) – The filename of the file containing the league matches.
scores (.Scores) – The scores for the competition.
num_teams_per_arena (int) – The usual number of teams per arena.
- delay_at(date: datetime) timedelta[source]¶
Calculates the active delay at a given
date. Intended for use only in exposing the current delay value – scheduling should be done using aMatchPeriodClockinstead.- Parameters:
date (datetime) – The date to find the delay for.
- Returns:
A
datetime.timedeltaspecifying the active delay.
- property final_match: Match¶
Get the
Matchfor the last match of the competition.This is the info for the ‘finals’ of the competition (i.e: the last of the knockout matches) unless there is a tiebreaker.
- get_staging_times(match: Match) StagingTimes[source]¶
- knockout_brackets: Sequence[KnockoutBracket]¶
A list of brackets which make up the knockout.
This currently has no bearing on the actual matches and is purely a display consideration.
- knockout_rounds: Sequence[KnockoutRound]¶
A list of the knockout matches by round. Each entry in the list represents a round of knockout matches, such that knockout_rounds[-1] contains a list with only one match – the final.
- match_periods: list[MatchPeriod]¶
A list of the
MatchPeriods which contain the matches for the competition.
- matches: list[MatchSlot]¶
A list of match slots in the schedule. Each match slot is a dict of arena to the
Matchoccurring in that arena.
- matches_at(date: datetime) Iterator[Match][source]¶
Get all the matches that occur around a specific
date.- Parameters:
date (datetime) – The date at which matches occur.
- Returns:
An iterable list of matches.
- n_planned_league_matches¶
The number of planned league matches.
- period_at(date: datetime) MatchPeriod | None[source]¶
Get the match period that occur around a specific
date.- Parameters:
date (datetime) – The date at which period occurs.
- Returns:
The period at that time or
None.
Raw Compstate¶
Utilities for working with raw Compstate repositories.
- class sr.comp.raw_compstate.RawCompstate(path: str | Path, local_only: bool)[source]¶
Bases:
objectHelper class to interact with a Compstate as raw files in a Git repository on disk.
- Parameters:
path (Path) – The path to the Compstate repository.
local_only (bool) – If true, this disabled the pulling, committing and pushing functionality.
- git(command_pieces: Iterable[str], err_msg: str = '', *, return_output: Literal[True]) str[source]¶
- git(command_pieces: Iterable[str], err_msg: str = '', return_output: Literal[False] = False) int
- git(command_pieces: Iterable[str], err_msg: str = '', return_output: bool = False) str | int
- property has_changes: bool¶
Whether or not there are any changes to files in the state, including untracked files.
- property layout: LayoutData¶
- load_shepherds() list[ShepherdInfo][source]¶
Load the shepherds’ state.
- property shepherding: ShepherdingData¶
Provides access to the raw shepherding data. Most consumers actually want to use
load_shepherdsinstead.
Scores¶
Utilities for working with scores.
- class sr.comp.scores.BaseScores(scores_data: Iterable[ScoreData], teams: Iterable[TLA], scorer: type[ValidatingScorer | SimpleScorer], ranker: type[Ranker], num_teams_per_arena: int)[source]¶
Bases:
objectA generic class that holds scores.
- Parameters:
- game_points: dict[tuple[ArenaName, MatchNumber], Mapping[TLA, GamePoints]]¶
Game points data for each match. Keys are tuples of the form
(arena_id, match_num), values aredicts mapping TLAs to the number of game points they scored.
- game_positions: dict[tuple[ArenaName, MatchNumber], Mapping[RankedPosition, set[TLA]]]¶
Game position data for each match. Keys are tuples of the form
(arena_id, match_num), values aredicts mapping ranked positions (i.e: first is 1, etc.) to an iterable of TLAs which have that position. Based solely on teams’ game points.
- get_rankings(match: Match) Mapping[TLA, RankedPosition][source]¶
Return a mapping of TLAs to ranked positions for the given match.
This is an internal API – most consumers should use
Scores.get_scoresinstead.
- property last_scored_match: MatchNumber | None¶
The match with the highest id for which we have score data.
This is intended primarily for display purposes and should not be relied upon as a measure of the completeness of scores up to the identified match.
Warning: this does not account for there possibly being earlier matches which are missing score data. For example if matches 0, 1 and 3 have scores while match 2 does not then this will return 3.
- exception sr.comp.scores.DuplicateScoresheet(match_id: tuple[ArenaName, MatchNumber])[source]¶
Bases:
ExceptionAn exception that occurs if two scoresheets for the same match have been entered.
- exception sr.comp.scores.InvalidTeam(tla: TLA, context: str)[source]¶
Bases:
ExceptionAn exception that occurs when a score contains an invalid team.
- class sr.comp.scores.KnockoutScores(scores_data: Iterable[ScoreData], teams: Iterable[TLA], scorer: type[ValidatingScorer | SimpleScorer], ranker: type[Ranker], num_teams_per_arena: int, league_positions: Mapping[TLA, LeaguePosition])[source]¶
Bases:
BaseScoresA class which holds knockout scores.
- static calculate_ranking(match_points: Mapping[TLA, LeaguePoints], league_positions: Mapping[TLA, LeaguePosition]) dict[TLA, RankedPosition][source]¶
Get a ranking of the given match’s teams.
- Parameters:
match_points – A map of TLAs to (normalised) scores.
league_positions – A map of TLAs to league positions.
- class sr.comp.scores.LeagueScores(scores_data: Iterable[ScoreData], teams: Iterable[TLA], scorer: type[ValidatingScorer | SimpleScorer], ranker: type[Ranker], num_teams_per_arena: int, extra: Mapping[TLA, TeamScore] | None = None)[source]¶
Bases:
BaseScoresA class which holds league scores.
- positions¶
An
OrderedDictof TLAs tosr.comp.scores.LeaguePositions.
- class sr.comp.scores.MatchScore(match_id: 'MatchId', game: 'Mapping[TLA, GamePoints]', normalised: 'Mapping[TLA, LeaguePoints]', ranking: 'Mapping[TLA, RankedPosition]')[source]¶
Bases:
object
- class sr.comp.scores.Scores(league: LeagueScores, knockout: KnockoutScores, tiebreaker: TiebreakerScores)[source]¶
Bases:
objectA simple class which stores references to the league and knockout scores.
- get_scores(match: Match) MatchScore | None[source]¶
Get the scores for a given match.
- Parameters:
match (sr.comp.match_period.Match) – A match.
- Returns:
An object describing the scores for the match, if scores have been recorded yet. Otherwise None.
- Return type:
MatchScore | None
- knockout¶
The
KnockoutScoresfor the competition.
- last_scored_match¶
The match with the highest id for which we have score data.
This is intended primarily for display purposes and should not be relied upon as a measure of the completeness of scores up to the identified match.
Warning: this does not account for there possibly being earlier matches which are missing score data. For example if matches 0, 1 and 3 have scores while match 2 does not then this will return 3.
- league¶
The
LeagueScoresfor the competition.
- classmethod load(root: Path, teams: Iterable[TLA], scorer: type[ValidatingScorer | SimpleScorer], ranker: type[Ranker], num_teams_per_arena: int) Scores[source]¶
- tiebreaker¶
The
TiebreakerScoresfor the competition.
- class sr.comp.scores.TeamScore(league: LeaguePoints = 0, game: GamePoints = 0)[source]¶
Bases:
objectA team score.
- class sr.comp.scores.TiebreakerScores(scores_data: Iterable[ScoreData], teams: Iterable[TLA], scorer: type[ValidatingScorer | SimpleScorer], ranker: type[Ranker], num_teams_per_arena: int, league_positions: Mapping[TLA, LeaguePosition])[source]¶
Bases:
KnockoutScores
- sr.comp.scores.degroup(grouped_positions: Mapping[T, Iterable[TLA]]) OrderedDict[TLA, T][source]¶
Given a mapping of positions to collections of teams at that position, returns an
OrderedDictof teams to their positions.Where more than one team has a given position, they are sorted before being inserted.
- sr.comp.scores.get_validated_scores(scorer_cls: type[ValidatingScorer | SimpleScorer], input_data: ScoreData) Mapping[TLA, GamePoints][source]¶
Helper function which mimics the behaviour from libproton.
Given a libproton 3.0 (Proton 3.0.0-rc2) compatible class this will calculate the scores and validate the input.
Teams¶
Team metadata library.
- class sr.comp.teams.Team(tla, name, rookie, dropped_out_after)[source]¶
Bases:
NamedTuple- is_still_around(match_number: MatchNumber) bool[source]¶
Check if this team is still around at a certain match.
- Parameters:
match_number (int) – The number of the match to check.
- Returns:
Trueif the team is still playing.
- tla: TLA¶
Alias for field number 0
Validation¶
Compstate validation routines.
- class sr.comp.validation.NaiveValidationError(message: 'str', code: 'str', level: 'ErrorLevel' = 'error')[source]¶
Bases:
object- with_source(error_type: ErrorType, id_: object) ValidationError[source]¶
- exception sr.comp.validation.ValidationError(message: 'str', code: 'str', source: 'tuple[ErrorType, object] | None', level: 'ErrorLevel' = 'error')[source]¶
Bases:
Exception
- sr.comp.validation.find_missing_scores(match_type: MatchType, match_ids: Iterable[tuple[ArenaName, MatchNumber]], last_match: int | None, schedule: Iterable[MatchSlot]) Sequence[tuple[MatchNumber, set[ArenaName]]][source]¶
Given a collection of
match_idsfor which we have scores, thematch_typecurrently under consideration, the number of thelast_matchwhich was scored and the list of all known matches determine which scores should be present but aren’t.
- sr.comp.validation.find_teams_without_league_matches(matches: Iterable[MatchSlot], possible_teams: Iterable[TLA]) set[TLA][source]¶
Find teams that don’t have league matches.
- sr.comp.validation.report_errors(error_type: ErrorType, id_: object, errors: list[str]) None[source]¶
Print out errors nicely formatted.
- sr.comp.validation.report_validation_errors(errors: Sequence[ValidationError]) None[source]¶
- sr.comp.validation.validate(comp: SRComp) int[source]¶
Validate a Compstate repo.
- Parameters:
comp (sr.comp.SRComp) – A competition instance.
- Returns:
The number of errors that have occurred.
- sr.comp.validation.validate_match(match: MatchSlot, possible_teams: Iterable[TLA]) Iterator[NaiveValidationError][source]¶
Check that the teams featuring in a match exist and are only required in one arena at a time.
- sr.comp.validation.validate_match_score(match_type: MatchType, match_score: Mapping[TLA, object], scheduled_match: Match) Iterator[NaiveValidationError][source]¶
Check that the match awards points to the right teams, by checking that the teams with points were scheduled to appear in the match.
- sr.comp.validation.validate_schedule(schedule: MatchSchedule, possible_teams: Iterable[TLA], possible_arenas: Container[ArenaName]) Iterator[ValidationError][source]¶
Check that the schedule contains enough time for all the matches, and that the matches themselves are valid.
- sr.comp.validation.validate_schedule_arenas(matches: Iterable[MatchSlot], possible_arenas: Container[ArenaName]) Iterator[ValidationError][source]¶
Check that any arena referenced by a match actually exists.
- sr.comp.validation.validate_schedule_count(schedule: MatchSchedule) Iterator[ValidationError][source]¶
- sr.comp.validation.validate_schedule_timings(scheduled_matches: Iterable[MatchSlot], match_duration: timedelta) Iterator[ValidationError][source]¶
- sr.comp.validation.validate_scores(match_type: MatchType, scores: BaseScores, schedule: Sequence[MatchSlot]) Iterator[ValidationError][source]¶
Validate that the scores are sane.
- sr.comp.validation.validate_scores_inner(match_type: MatchType, scores: BaseScores, schedule: Sequence[MatchSlot]) Iterator[ValidationError][source]¶
Validate that scores are sane.
- sr.comp.validation.validate_team_matches(matches: Iterable[MatchSlot], possible_teams: Iterable[TLA]) Iterator[ValidationError][source]¶
Check that all teams have been assigned league matches. We don’t need (or want) to check the knockouts, since those are scheduled dynamically based on the list of teams.
- sr.comp.validation.warn_missing_scores(match_type: MatchType, scores: BaseScores, schedule: Iterable[MatchSlot]) Iterator[ValidationError][source]¶
Check that the scores up to the most recent are all present.
- sr.comp.validation.with_source(naive_errors: Iterable[NaiveValidationError], source: tuple[ErrorType, object]) Iterator[ValidationError][source]¶
Venue¶
Venue layout metadata library.
- exception sr.comp.venue.InvalidRegionException(region: RegionName, area: str)[source]¶
Bases:
ExceptionAn exception that occurs when there are invalid regions mentioned in the shepherding data.
- exception sr.comp.venue.LayoutTeamsException(duplicate_teams: Iterable[TLA], extra_teams: Iterable[TLA], missing_teams: Iterable[TLA])[source]¶
Bases:
MismatchException[TLA]An exception that occurs when there are duplicate, extra or missing teams in a layout.
- exception sr.comp.venue.MismatchException(tpl: str, duplicates: Iterable[T_str], extras: Iterable[T_str], missing: Iterable[T_str])[source]¶
Bases:
Exception,Generic[T_str]An exception that occurs when there are duplicate, extra or missing items.
- exception sr.comp.venue.ShepherdingAreasException(where: str, duplicate: Iterable[str], extra: Iterable[str], missing: Iterable[str])[source]¶
Bases:
MismatchException[str]An exception that occurs when there are duplicate, extra or missing shepherding areas in the staging times.
- class sr.comp.venue.Venue(teams: Iterable[TLA], layout_file: Path, shepherding_file: Path)[source]¶
Bases:
objectA class providing information about the layout within the venue.
- check_staging_times(staging_times: StagingOffsets) None[source]¶
- classmethod check_teams(teams: Iterable[TLA], teams_layout: list[RegionData]) None[source]¶
Check that the given layout of teams contains the same set of teams as the reference.
Will throw a
LayoutTeamsExceptionif there are any missing, extra or duplicate teams found.
Winners¶
Calculation of winners of awards.
The awards calculated are:
1st place,
2nd place,
3rd place,
Rookie award (rookie team with highest league position).
- class sr.comp.winners.Award(value)[source]¶
Bases:
EnumAward types.
These correspond with awards as specified in the rulebook.
- committee = 'committee'¶
- first = 'first'¶
- image = 'image'¶
- movement = 'movement'¶
- rookie = 'rookie'¶
- second = 'second'¶
- third = 'third'¶
- web = 'web'¶
- sr.comp.winners.compute_awards(scores: Scores, final_match: Match, teams: Mapping[TLA, Team], path: Path | None = None) Mapping[Award, list[TLA]][source]¶
Compute the awards handed out from configuration.
- Parameters:
scores (sr.comp.scores.Scores) – The scores.
final_match (Match) – The match to use as the final.
teams (dict) – A mapping from TLAs to
sr.comp.teams.Teamobjects.
- Returns:
A dictionary of
Awardtypes to TLAs is returned. This may not have a key for any award type that has not yet been determined.
YAML Loader¶
YAML loading routines.
This includes parsing of dates and times properly, and also ensures the C YAML loader is used which is necessary for optimum performance.