API

Arenas

Arena and corner loading routines.

class sr.comp.arenas.Arena(name, display_name, colour)[source]

Bases: NamedTuple

colour: sr.comp.types.Colour

Alias for field number 2

display_name: str

Alias for field number 1

name: sr.comp.types.ArenaName

Alias for field number 0

class sr.comp.arenas.Corner(number, colour)[source]

Bases: NamedTuple

colour: sr.comp.types.Colour

Alias for field number 1

number: sr.comp.arenas.CornerNumber

Alias for field number 0

sr.comp.arenas.load_arenas(filename: pathlib.Path) dict[sr.comp.types.ArenaName, sr.comp.arenas.Arena][source]

Load arenas from a YAML file.

Parameters

filename (str) – The filename of the YAML file to load arenas from.

Returns

A mapping of arena names to Arena objects.

Return type

collections.OrderedDict

sr.comp.arenas.load_corners(filename: pathlib.Path) dict[sr.comp.arenas.CornerNumber, sr.comp.arenas.Corner][source]

Load corner colours from a YAML file.

Parameters

filename (str) – The filename of the YAML file to load corners from.

Returns

A mapping of corner numbers to Corner objects.

Return type

collections.OrderedDict

Competition

Core competition functions.

class sr.comp.comp.SRComp(root: str | pathlib.Path)[source]

Bases: object

A class containing all the various parts of a competition.

Parameters

root (Path) – The root path of the compstate repo.

arenas

A collections.OrderedDict mapping arena names to sr.comp.arenas.Arena objects.

awards

A dict mapping sr.comp.winners.Award objects to a list of teams.

corners

A collections.OrderedDict mapping corner numbers to sr.comp.arenas.Corner objects.

schedule

A sr.comp.matches.MatchSchedule instance.

scores

A sr.comp.scores.Scores instance.

state

The current commit of the Compstate repository.

teams

A mapping of TLAs to sr.comp.teams.Team objects.

timezone

The timezone of the competition.

venue

A sr.comp.venue.Venue instance.

sr.comp.comp.load_scorer(root: pathlib.Path) Type[Union[sr.comp.types.ValidatingScorer, sr.comp.types.SimpleScorer]][source]

Load the scorer module from Compstate repo.

Parameters

root (Path) – The path to the compstate repo.

Knockout Schedulers

Knockout schedule generation.

class sr.comp.knockout_scheduler.base_scheduler.BaseKnockoutScheduler(schedule: MatchSchedule, scores: Scores, arenas: Iterable[ArenaName], num_teams_per_arena: int, teams: Mapping[TLA, Team], config: YAMLData)[source]

Bases: object

Base class for knockout schedulers offering common functionality.

Parameters
  • schedule – The league schedule.

  • scores – The scores.

  • arenas (dict) – The arenas.

  • teams (dict) – The teams.

  • config – Custom configuration for the knockout scheduler.

add_knockouts() None[source]

Add the knockouts to the schedule.

Derived classes must override this method.

static get_match_display_name(rounds_remaining: int, round_num: int, global_num: sr.comp.types.MatchNumber) str[source]

Get a human-readable match display name.

Parameters
  • rounds_remaining – The number of knockout rounds remaining.

  • knockout_num – The match number within the knockout round.

  • global_num – The global match number.

get_ranking(game: sr.comp.match_period.Match) list[sr.comp.types.TLA][source]

Get a ranking of the given match’s teams.

Parameters

game – A game.

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: MatchSchedule, scores: Scores, arenas: Iterable[ArenaName], num_teams_per_arena: int, teams: Mapping[TLA, Team], config: YAMLData)[source]

Bases: sr.comp.knockout_scheduler.base_scheduler.BaseKnockoutScheduler

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
  • schedule – The league schedule.

  • scores – The scores.

  • arenas (dict) – The arenas.

  • num_teams_per_arena (int) – The usual number of teams per arena.

  • teams (dict) – The teams.

  • config – Custom configuration for the knockout scheduler.

add_knockouts() None[source]

Add the knockouts to the schedule.

Derived classes must override this method.

static get_rounds_remaining(prev_matches: Sized) int[source]
get_winners(game: sr.comp.match_period.Match) list[sr.comp.types.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(*args: Any, **kwargs: Any)[source]

Bases: sr.comp.knockout_scheduler.base_scheduler.BaseKnockoutScheduler

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

add_knockouts() None[source]

Add the knockouts to the schedule.

Derived classes must override this method.

get_team(team_ref: Optional[sr.comp.knockout_scheduler.static_scheduler.StaticMatchTeamReference]) Optional[sr.comp.types.TLA][source]

Stable Random

A stable random number generator implementation.

class sr.comp.knockout_scheduler.stable_random.Random[source]

Bases: object

Our 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.Random since 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.

getrandbits(n: int) int[source]
random() float[source]
seed(s: bytes | bytearray | memoryview) None[source]
shuffle(x: MutableSequence[sr.comp.knockout_scheduler.stable_random.T]) None[source]

Match Period

Classes that are useful for dealing with match periods.

class sr.comp.match_period.Delay(delay, time)[source]

Bases: NamedTuple

delay: datetime.timedelta

Alias for field number 0

time: datetime.datetime

Alias for field number 1

class sr.comp.match_period.Match(num, display_name, arena, teams, start_time, end_time, type, use_resolved_ranking)[source]

Bases: NamedTuple

arena: sr.comp.types.ArenaName

Alias for field number 2

display_name: str

Alias for field number 1

end_time: datetime.datetime

Alias for field number 5

num: sr.comp.types.MatchNumber

Alias for field number 0

start_time: datetime.datetime

Alias for field number 4

teams: list[typing.Optional[sr.comp.types.TLA]]

Alias for field number 3

type: sr.comp.match_period.MatchType

Alias for field number 6

use_resolved_ranking: bool

Alias for field number 7

class sr.comp.match_period.MatchPeriod(start_time, end_time, max_end_time, description, matches, type)[source]

Bases: NamedTuple

description: str

Alias for field number 3

end_time: datetime.datetime

Alias for field number 1

matches: list[sr.comp.match_period.MatchSlot]

Alias for field number 4

max_end_time: datetime.datetime

Alias for field number 2

start_time: datetime.datetime

Alias for field number 0

type: sr.comp.match_period.MatchType

Alias for field number 5

class sr.comp.match_period.MatchType(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)[source]

Bases: enum.Enum

knockout = 'knockout'
league = 'league'
tiebreaker = 'tiebreaker'

Match Period Clock

A clock to manage match periods.

class sr.comp.match_period_clock.MatchPeriodClock(period: sr.comp.match_period.MatchPeriod, delays: Iterable[sr.comp.match_period.Delay])[source]

Bases: object

A clock for use in scheduling matches within a MatchPeriod.

It is generally expected that the time information here will be in the form of datetime and timedelta instances, 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 time is reached.

advance_time(duration: datetime.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.

property current_time: datetime.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 OutOfTimeException if either:

  • the end of the period has been reached (i.e: the sum of durations passed to advance_time has exceeded the planned duration of the period), or

  • the 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: sr.comp.match_period.MatchPeriod, delays: Iterable[sr.comp.match_period.Delay]) list[sr.comp.match_period.Delay][source]

Filter and sort a list of all possible delays to include only those which occur after the start of the given period.

Parameters
  • period (MatchPeriod) – The period to get the delays for.

  • delays (list) – The list of Delays to consider.

Returns

A sorted list of delays which occur after the start of the period.

iterslots(slot_duration: datetime.timedelta) Iterator[datetime.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_time with the given duration. As a result, it is safe to call advance_time between iterations if additional gaps between slots are needed.

exception sr.comp.match_period_clock.OutOfTimeException[source]

Bases: Exception

An exception representing no more time available at the competition to run matches.

Matches

Match schedule library.

class sr.comp.matches.MatchSchedule(y: Any, league: sr.comp.types.LeagueMatches, teams: Mapping[sr.comp.types.TLA, sr.comp.teams.Team], num_teams_per_arena: int)[source]

Bases: object

A match schedule.

add_tiebreaker(scores: sr.comp.scores.Scores, time: datetime.datetime) None[source]

Add a tie breaker to the league if required. Also set a tiebreaker attribute if necessary.

Parameters
  • scores (Scores) – The scores for the competition.

  • time (datetime.datetime) – The time to have the tiebreaker match.

classmethod create(config_fname: pathlib.Path, league_fname: pathlib.Path, scores: sr.comp.scores.Scores, arenas: Mapping[sr.comp.types.ArenaName, sr.comp.arenas.Arena], num_teams_per_arena: int, teams: Mapping[sr.comp.types.TLA, sr.comp.teams.Team]) sr.comp.matches.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.

  • arenas (dict) – A mapping of arena ids to Arena instances.

  • num_teams_per_arena (int) – The usual number of teams per arena.

  • teams (dict) – A mapping of TLAs to Team instances.

property datetime_now: datetime.datetime

Get the current date and time, with the correct timezone.

delay_at(date: datetime.datetime) 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 a MatchPeriodClock instead.

Parameters

date (datetime) – The date to find the delay for.

Returns

A datetime.timedelta specifying the active delay.

property final_match: sr.comp.match_period.Match

Get the Match for 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: sr.comp.match_period.Match) sr.comp.matches.StagingTimes[source]
knockout_rounds: list[list[sr.comp.match_period.Match]]

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[sr.comp.match_period.MatchPeriod]

A list of the MatchPeriods which contain the matches for the competition.

matches: list[sr.comp.match_period.MatchSlot]

A list of match slots in the schedule. Each match slot is a dict of arena to the Match occurring in that arena.

matches_at(date: datetime.datetime) Iterator[sr.comp.match_period.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_matches() int[source]

Get the number of matches.

Returns

The number of matches.

n_planned_league_matches

The number of planned league matches.

period_at(date: datetime.datetime) sr.comp.match_period.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.

remove_drop_outs(teams: Iterable[Optional[sr.comp.types.TLA]], since_match: sr.comp.types.MatchNumber) list[typing.Optional[sr.comp.types.TLA]][source]

Take a list of TLAs and replace the teams that have dropped out with None values.

Parameters
  • teams (list) – A list of TLAs.

  • since_match (int) – The match number to check for drop outs from.

Returns

A new list containing the appropriate teams.

teams

A mapping of TLAs to Team instances.

class sr.comp.matches.StagingOffsets[source]

Bases: typing_extensions.TypedDict

closes: datetime.timedelta
duration: datetime.timedelta
opens: datetime.timedelta
signal_shepherds: Mapping[sr.comp.types.ShepherdName, datetime.timedelta]
signal_teams: datetime.timedelta
class sr.comp.matches.StagingTimes[source]

Bases: typing_extensions.TypedDict

closes: datetime.datetime
duration: datetime.timedelta
opens: datetime.datetime
signal_shepherds: Mapping[sr.comp.types.ShepherdName, datetime.datetime]
signal_teams: datetime.datetime
exception sr.comp.matches.WrongNumberOfTeams(match_n: int, arena_name: str, teams: Sequence[Optional[sr.comp.types.TLA]], num_teams_per_arena: int)[source]

Bases: Exception

sr.comp.matches.get_timezone(name: str) datetime.tzinfo[source]
sr.comp.matches.parse_ranges(ranges: str) set[int][source]

Parse a comma separated list of numbers which may include ranges specified as hyphen-separated numbers.

From https://stackoverflow.com/questions/6405208

Raw Compstate

Utilities for working with raw Compstate repositories.

class sr.comp.raw_compstate.RawCompstate(path: str | pathlib.Path, local_only: bool)[source]

Bases: object

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

checkout(what: str) None[source]
commit(commit_msg: str, allow_empty: bool = False) None[source]
commit_and_push(commit_msg: str, allow_empty: bool = False) None[source]
property deployments: list[str]
fetch(where: str = 'origin', refspecs: Collection[str] = (), quiet: bool = False) None[source]
get_default_branch() str[source]
get_score_path(match: sr.comp.match_period.Match) str[source]

Get the path to the score file for the given match.

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
has_ancestor(commit: str) bool[source]
property has_changes: bool

Whether or not there are any changes to files in the state, including untracked files.

has_commit(commit: str) bool[source]

Whether or not the given commit is known to this repository.

has_descendant(commit: str) bool[source]
is_parent(parent: str, child: str) bool[source]
property layout: sr.comp.types.LayoutData
load() sr.comp.comp.SRComp[source]

Load the state as an SRComp instance.

load_score(match: sr.comp.match_period.Match) sr.comp.types.ScoreData[source]

Load raw score data for the given match.

load_shepherds() list[sr.comp.raw_compstate.ShepherdInfo][source]

Load the shepherds’ state.

pull_fast_forward() None[source]
push(where: str, revspec: str, err_msg: str = '', force: bool = False) None[source]
reset_and_fast_forward() None[source]
reset_hard() None[source]
rev_parse(revision: str) str[source]
save_score(match: sr.comp.match_period.Match, score: sr.comp.types.ScoreData) None[source]

Save raw score data for the given match.

property shepherding: sr.comp.types.ShepherdingData

Provides access to the raw shepherding data. Most consumers actually want to use load_shepherds instead.

show_changes() None[source]
show_remotes() None[source]
stage(file_path: str) None[source]

Stage the given file.

Parameters

file_path (Path) – A path to the file to stage. This should either be an absolute path, or one relative to the compstate.

class sr.comp.raw_compstate.ShepherdInfo[source]

Bases: typing_extensions.TypedDict

colour: sr.comp.types.Colour
name: sr.comp.types.ShepherdName
regions: list[sr.comp.types.RegionName]
teams: list[sr.comp.types.TLA]

Scores

Utilities for working with scores.

class sr.comp.scores.BaseScores(scores_data: Iterable[sr.comp.types.ScoreData], teams: Iterable[sr.comp.types.TLA], scorer: Type[Union[sr.comp.types.ValidatingScorer, sr.comp.types.SimpleScorer]], num_teams_per_arena: int)[source]

Bases: object

A generic class that holds scores.

Parameters
  • scores_data (iterable) – A collection of loaded score sheet data.

  • teams (dict) – The teams in the competition.

  • scorer (dict) – The scorer logic.

  • num_teams_per_arena (int) – The usual number of teams per arena.

game_points: dict[typing.Tuple[sr.comp.types.ArenaName, sr.comp.types.MatchNumber], typing.Mapping[sr.comp.types.TLA, sr.comp.types.GamePoints]]

Game points data for each match. Keys are tuples of the form (arena_id, match_num), values are dicts mapping TLAs to the number of game points they scored.

game_positions: dict[typing.Tuple[sr.comp.types.ArenaName, sr.comp.types.MatchNumber], typing.Mapping[league_ranker.RankedPosition, set[sr.comp.types.TLA]]]

Game position data for each match. Keys are tuples of the form (arena_id, match_num), values are dicts 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: sr.comp.match_period.Match) Mapping[sr.comp.types.TLA, league_ranker.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_scores instead.

property last_scored_match: Optional[sr.comp.types.MatchNumber]

The most match with the highest id for which we have score data.

ranked_points: dict[typing.Tuple[sr.comp.types.ArenaName, sr.comp.types.MatchNumber], dict[sr.comp.types.TLA, league_ranker.LeaguePoints]]

Normalised (aka ‘league’) points earned in each match. Keys are tuples of the form (arena_id, match_num), values are dicts mapping TLAs to the number of normalised points they would earn for that match.

teams: Mapping[sr.comp.types.TLA, sr.comp.scores.TeamScore]

Points for each team earned during this portion of the competition. Maps TLAs to TeamScore instances.

exception sr.comp.scores.DuplicateScoresheet(match_id: Tuple[sr.comp.types.ArenaName, sr.comp.types.MatchNumber])[source]

Bases: Exception

An exception that occurs if two scoresheets for the same match have been entered.

exception sr.comp.scores.InvalidTeam(tla: sr.comp.types.TLA, context: str)[source]

Bases: Exception

An exception that occurs when a score contains an invalid team.

class sr.comp.scores.KnockoutScores(scores_data: Iterable[sr.comp.types.ScoreData], teams: Iterable[sr.comp.types.TLA], scorer: Type[Union[sr.comp.types.ValidatingScorer, sr.comp.types.SimpleScorer]], num_teams_per_arena: int, league_positions: Mapping[sr.comp.types.TLA, sr.comp.scores.LeaguePosition])[source]

Bases: sr.comp.scores.BaseScores

A class which holds knockout scores.

static calculate_ranking(match_points: Mapping[sr.comp.types.TLA, league_ranker.LeaguePoints], league_positions: Mapping[sr.comp.types.TLA, sr.comp.scores.LeaguePosition]) dict[sr.comp.types.TLA, league_ranker.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.

get_rankings(match: sr.comp.match_period.Match) Mapping[sr.comp.types.TLA, league_ranker.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_scores instead.

resolved_positions

Position data for each match which includes adjustment for ties. Keys are tuples of the form (arena_id, match_num), values are OrderedDicts mapping TLAs to the ranked position (i.e: first is 1, etc.) of that team, with the winning team in the start of the list of keys. Tie resolution is done by league position.

class sr.comp.scores.LeagueScores(scores_data: Iterable[sr.comp.types.ScoreData], teams: Iterable[sr.comp.types.TLA], scorer: Type[Union[sr.comp.types.ValidatingScorer, sr.comp.types.SimpleScorer]], num_teams_per_arena: int, extra: Optional[Mapping[sr.comp.types.TLA, sr.comp.scores.TeamScore]] = None)[source]

Bases: sr.comp.scores.BaseScores

A class which holds league scores.

positions

An OrderedDict of TLAs to sr.comp.scores.LeaguePositions.

static rank_league(team_scores: Mapping[sr.comp.types.TLA, sr.comp.scores.TeamScore]) Mapping[sr.comp.types.TLA, sr.comp.scores.LeaguePosition][source]

Given a mapping of TLA to TeamScore, returns a mapping of TLA to league position which both allows for ties and enables their resolution deterministically.

class sr.comp.scores.MatchScore(match_id: 'MatchId', game: 'Mapping[TLA, GamePoints]', normalised: 'Mapping[TLA, LeaguePoints]', ranking: 'Mapping[TLA, RankedPosition]')[source]

Bases: object

game: Mapping[sr.comp.types.TLA, sr.comp.types.GamePoints]
match_id: Tuple[sr.comp.types.ArenaName, sr.comp.types.MatchNumber]
normalised: Mapping[sr.comp.types.TLA, league_ranker.LeaguePoints]
ranking: Mapping[sr.comp.types.TLA, league_ranker.RankedPosition]
class sr.comp.scores.Scores(league: sr.comp.scores.LeagueScores, knockout: sr.comp.scores.KnockoutScores, tiebreaker: sr.comp.scores.TiebreakerScores)[source]

Bases: object

A simple class which stores references to the league and knockout scores.

get_scores(match: sr.comp.match_period.Match) sr.comp.scores.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 KnockoutScores for the competition.

last_scored_match

The match with the highest id for which we have score data.

league

The LeagueScores for the competition.

classmethod load(root: pathlib.Path, teams: Iterable[sr.comp.types.TLA], scorer: Type[Union[sr.comp.types.ValidatingScorer, sr.comp.types.SimpleScorer]], num_teams_per_arena: int) sr.comp.scores.Scores[source]
tiebreaker

The TiebreakerScores for the competition.

class sr.comp.scores.TeamScore(league: league_ranker.LeaguePoints = 0, game: sr.comp.types.GamePoints = 0)[source]

Bases: object

A team score.

Parameters
  • league (int) – The league points.

  • game (int) – The game points.

add_game_points(score: sr.comp.types.GamePoints) sr.comp.types.GamePoints[source]
add_league_points(points: league_ranker.LeaguePoints) league_ranker.LeaguePoints[source]
class sr.comp.scores.TiebreakerScores(scores_data: Iterable[sr.comp.types.ScoreData], teams: Iterable[sr.comp.types.TLA], scorer: Type[Union[sr.comp.types.ValidatingScorer, sr.comp.types.SimpleScorer]], num_teams_per_arena: int, league_positions: Mapping[sr.comp.types.TLA, sr.comp.scores.LeaguePosition])[source]

Bases: sr.comp.scores.KnockoutScores

sr.comp.scores.degroup(grouped_positions: Mapping[sr.comp.scores.T, Iterable[sr.comp.types.TLA]]) collections.OrderedDict[sr.comp.types.TLA, sr.comp.scores.T][source]

Given a mapping of positions to collections of teams at that position, returns an OrderedDict of 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[Union[sr.comp.types.ValidatingScorer, sr.comp.types.SimpleScorer]], input_data: sr.comp.types.ScoreData) Mapping[sr.comp.types.TLA, sr.comp.types.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.

sr.comp.scores.load_external_scores(scores_data: Iterable[sr.comp.types.ExternalScoreData], teams: Iterable[sr.comp.types.TLA]) Mapping[sr.comp.types.TLA, sr.comp.scores.TeamScore][source]

Mechanism to import additional scores from an external source.

This provides flexibility in the sources of score data.

sr.comp.scores.load_external_scores_data(result_dir: pathlib.Path) Iterator[sr.comp.types.ExternalScoreData][source]
sr.comp.scores.load_scores_data(result_dir: pathlib.Path) Iterator[sr.comp.types.ScoreData][source]
sr.comp.scores.results_finder(root: pathlib.Path) Iterator[pathlib.Path][source]

An iterator that finds score sheet files.

Teams

Team metadata library.

class sr.comp.teams.Team(tla, name, rookie, dropped_out_after)[source]

Bases: NamedTuple

dropped_out_after: Optional[sr.comp.types.MatchNumber]

Alias for field number 3

is_still_around(match_number: sr.comp.types.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

True if the team is still playing.

name: str

Alias for field number 1

rookie: bool

Alias for field number 2

tla: sr.comp.types.TLA

Alias for field number 0

sr.comp.teams.load_teams(filename: pathlib.Path) dict[sr.comp.types.TLA, sr.comp.teams.Team][source]

Load teams from a YAML file.

Parameters

filename (Path) – The filename of the YAML file to load.

Returns

A dictionary mapping TLAs to Team objects.

Validation

Compstate validation routines.

class sr.comp.validation.NaiveValidationError(message: 'str', code: 'str', level: 'ErrorLevel' = 'error')[source]

Bases: object

code: str
level: Literal['error', 'warning', 'hint'] = 'error'
message: str
with_source(error_type: sr.comp.validation.ErrorType, id_: object) sr.comp.validation.ValidationError[source]
exception sr.comp.validation.ValidationError(message: 'str', code: 'str', source: 'tuple[ErrorType, object] | None', level: 'ErrorLevel' = 'error')[source]

Bases: Exception

code: str
level: Literal['error', 'warning', 'hint'] = 'error'
message: str
source: tuple[sr.comp.validation.ErrorType, object] | None
sr.comp.validation.find_missing_scores(match_type: sr.comp.match_period.MatchType, match_ids: Iterable[Tuple[sr.comp.types.ArenaName, sr.comp.types.MatchNumber]], last_match: int | None, schedule: Iterable[sr.comp.match_period.MatchSlot]) Sequence[tuple[sr.comp.types.MatchNumber, set[sr.comp.types.ArenaName]]][source]

Given a collection of match_ids for which we have scores, the match_type currently under consideration, the number of the last_match which 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[sr.comp.match_period.MatchSlot], possible_teams: Iterable[sr.comp.types.TLA]) set[sr.comp.types.TLA][source]

Find teams that don’t have league matches.

Parameters
  • matches (list) – A list of matches.

  • possible_teams – A list of possible teams.

Returns

A set of teams without matches.

sr.comp.validation.join_and(items: Iterable[object]) str[source]
sr.comp.validation.report_errors(error_type: sr.comp.validation.ErrorType, id_: object, errors: list[str]) None[source]

Print out errors nicely formatted.

Parameters
  • type (str) – The human-readable ‘type’.

  • id (str) – The human-readable ‘ID’.

  • errors (list) – A list of string errors.

sr.comp.validation.report_validation_errors(errors: Sequence[sr.comp.validation.ValidationError]) None[source]
sr.comp.validation.validate(comp: sr.comp.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: sr.comp.match_period.MatchSlot, possible_teams: Iterable[sr.comp.types.TLA]) Iterator[sr.comp.validation.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: sr.comp.match_period.MatchType, match_score: Mapping[sr.comp.types.TLA, object], scheduled_match: sr.comp.match_period.Match) Iterator[sr.comp.validation.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: sr.comp.matches.MatchSchedule, possible_teams: Iterable[sr.comp.types.TLA], possible_arenas: Container[sr.comp.types.ArenaName]) Iterator[sr.comp.validation.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[sr.comp.match_period.MatchSlot], possible_arenas: Container[sr.comp.types.ArenaName]) Iterator[sr.comp.validation.ValidationError][source]

Check that any arena referenced by a match actually exists.

sr.comp.validation.validate_schedule_count(schedule: sr.comp.matches.MatchSchedule) Iterator[sr.comp.validation.ValidationError][source]
sr.comp.validation.validate_schedule_timings(scheduled_matches: Iterable[sr.comp.match_period.MatchSlot], match_duration: datetime.timedelta) Iterator[sr.comp.validation.ValidationError][source]
sr.comp.validation.validate_scores(match_type: sr.comp.match_period.MatchType, scores: sr.comp.scores.BaseScores, schedule: Sequence[sr.comp.match_period.MatchSlot]) Iterator[sr.comp.validation.ValidationError][source]

Validate that the scores are sane.

sr.comp.validation.validate_scores_inner(match_type: sr.comp.match_period.MatchType, scores: sr.comp.scores.BaseScores, schedule: Sequence[sr.comp.match_period.MatchSlot]) Iterator[sr.comp.validation.ValidationError][source]

Validate that scores are sane.

sr.comp.validation.validate_team_matches(matches: Iterable[sr.comp.match_period.MatchSlot], possible_teams: Iterable[sr.comp.types.TLA]) Iterator[sr.comp.validation.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: sr.comp.match_period.MatchType, scores: sr.comp.scores.BaseScores, schedule: Iterable[sr.comp.match_period.MatchSlot]) Iterator[sr.comp.validation.ValidationError][source]

Check that the scores up to the most recent are all present.

sr.comp.validation.with_source(naive_errors: Iterable[sr.comp.validation.NaiveValidationError], source: tuple[sr.comp.validation.ErrorType, object]) Iterator[sr.comp.validation.ValidationError][source]

Venue

Venue layout metadata library.

exception sr.comp.venue.InvalidRegionException(region: sr.comp.types.RegionName, area: str)[source]

Bases: Exception

An exception that occurs when there are invalid regions mentioned in the shepherding data.

exception sr.comp.venue.LayoutTeamsException(duplicate_teams: Iterable[sr.comp.types.TLA], extra_teams: Iterable[sr.comp.types.TLA], missing_teams: Iterable[sr.comp.types.TLA])[source]

Bases: sr.comp.venue.MismatchException[sr.comp.types.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[sr.comp.venue.T_str], extras: Iterable[sr.comp.venue.T_str], missing: Iterable[sr.comp.venue.T_str])[source]

Bases: Exception, Generic[sr.comp.venue.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: sr.comp.venue.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[sr.comp.types.TLA], layout_file: pathlib.Path, shepherding_file: pathlib.Path)[source]

Bases: object

A class providing information about the layout within the venue.

check_staging_times(staging_times: sr.comp.matches.StagingOffsets) None[source]
classmethod check_teams(teams: Iterable[sr.comp.types.TLA], teams_layout: list[sr.comp.types.RegionData]) None[source]

Check that the given layout of teams contains the same set of teams as the reference.

Will throw a LayoutTeamsException if there are any missing, extra or duplicate teams found.

Parameters
  • teams (list) – The reference list of teams in the competition.

  • teams_layout (list) – A list of maps with a list of teams under the teams key.

get_team_location(team: sr.comp.types.TLA) sr.comp.types.RegionName[source]

Get the name of the location allocated to the given team within the venue.

Parameters

team (str) – The TLA of the team in question.

Returns

The name of the location allocated to the team.

locations

A dict of location names (from the layout file) to location information, including which teams are in that location and the shepherding region which contains that location.

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, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)[source]

Bases: enum.Enum

Award 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: sr.comp.scores.Scores, final_match: sr.comp.match_period.Match, teams: Mapping[sr.comp.types.TLA, sr.comp.teams.Team], path: pathlib.Path | None = None) Mapping[sr.comp.winners.Award, List[sr.comp.types.TLA]][source]

Compute the awards handed out from configuration.

Parameters
Returns

A dictionary of Award types 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.

sr.comp.yaml_loader.add_time_constructor(loader: type[yaml.cyaml.CLoader]) None[source]
sr.comp.yaml_loader.load(file_path: pathlib.Path) Any[source]

Load a YAML fie and return the results.

Parameters

file_path (Path) – The path to the YAML file.

Returns

The parsed contents.

sr.comp.yaml_loader.time_constructor(_: Any, node: yaml.nodes.Node) datetime.datetime[source]