Source code for sr.comp.http.query_utils

"""Various utils for working with HTTP."""

from __future__ import annotations

import datetime
from typing import Callable, Mapping, overload, TypeVar, Union
from typing_extensions import TypedDict

from league_ranker import LeaguePoints, RankedPosition

from sr.comp.comp import SRComp
from sr.comp.match_period import Match, MatchType
from sr.comp.types import ArenaName, GamePoints, MatchNumber, ShepherdName, TLA


[docs]class LeagueMatchScore(TypedDict): game: Mapping[TLA, GamePoints] league: Mapping[TLA, LeaguePoints] ranking: Mapping[TLA, RankedPosition]
[docs]class KnockoutMatchScore(TypedDict): game: Mapping[TLA, GamePoints] normalised: Mapping[TLA, LeaguePoints] ranking: Mapping[TLA, RankedPosition]
MatchScoreDict = Union[LeagueMatchScore, KnockoutMatchScore]
[docs]class Times(TypedDict): start: str end: str
[docs]class StagingTimes(TypedDict): opens: str closes: str signal_teams: str signal_shepherds: dict[ShepherdName, str]
[docs]class MatchTimings(TypedDict): slot: Times game: Times staging: StagingTimes
class _MatchInfo(TypedDict): num: MatchNumber display_name: str arena: ArenaName teams: list[TLA | None] type: str # noqa:A003 times: MatchTimings
[docs]class MatchInfo(_MatchInfo, total=False): scores: MatchScoreDict
TParseable = TypeVar('TParseable', int, str, datetime.datetime)
[docs]def match_json_info(comp: SRComp, match: Match) -> MatchInfo: """ Get match JSON information. Parameters ---------- comp : sr.comp.comp.SRComp A competition instance. match : sr.comp.match_periods.Match A match. Returns ------- dict A :class:`dict` containing JSON suitable output. """ match_slot_lengths = comp.schedule.match_slot_lengths staging_times = comp.schedule.get_staging_times(match) info = MatchInfo({ 'num': match.num, 'display_name': match.display_name, 'arena': match.arena, 'teams': match.teams, 'type': match.type.value, 'times': { 'slot': { 'start': match.start_time.isoformat(), 'end': match.end_time.isoformat(), }, 'game': { 'start': ( match.start_time + match_slot_lengths['pre'] ).isoformat(), 'end': ( match.start_time + match_slot_lengths['pre'] + match_slot_lengths['match'] ).isoformat(), }, 'staging': { 'opens': staging_times['opens'].isoformat(), 'closes': staging_times['closes'].isoformat(), 'signal_teams': staging_times['signal_teams'].isoformat(), 'signal_shepherds': { area: time.isoformat() for area, time in staging_times['signal_shepherds'].items() }, }, }, }) score_info = comp.scores.get_scores(match) if score_info: # TODO: consider using 'normalised' for both, instead of 'league' below if match.type == MatchType.league: info['scores'] = { 'game': score_info.game, 'league': score_info.normalised, 'ranking': score_info.ranking, } else: info['scores'] = { 'game': score_info.game, 'normalised': score_info.normalised, 'ranking': score_info.ranking, } return info
@overload def parse_difference_string( string: str, type_converter: Callable[[str], TParseable], ) -> Callable[[TParseable], bool]: ... @overload def parse_difference_string( string: str, type_converter: Callable[[str], int] = int, ) -> Callable[[int], bool]: ...
[docs]def parse_difference_string( string: str, type_converter: Callable[[str], TParseable] = int, # type: ignore[assignment] ) -> Callable[[TParseable], bool]: """ Parse a difference string (x..x, ..x, x.., x) and return a function that accepts a single argument and returns ``True`` if it is in the difference. """ separator = '..' if string == separator: raise ValueError('Must specify at least one bound.') tokens = string.split(separator) if len(tokens) > 2: raise ValueError('Argument is not a different string.') elif len(tokens) == 1: converted_token = type_converter(tokens[0]) return lambda x: x == converted_token elif len(tokens) == 2: if not tokens[1]: lower_bound = type_converter(tokens[0]) return lambda x: x >= lower_bound elif not tokens[0]: upper_bound = type_converter(tokens[1]) return lambda x: x <= upper_bound else: lhs = type_converter(tokens[0]) rhs = type_converter(tokens[1]) if lhs > rhs: raise ValueError('Bounds are the wrong way around.') return lambda x: lhs <= x <= rhs else: raise AssertionError('Argument contains unknown input.')