diff --git a/src/base/management/commands/correct_data.py b/src/base/management/commands/correct_data.py index b2ef5531..005623d7 100644 --- a/src/base/management/commands/correct_data.py +++ b/src/base/management/commands/correct_data.py @@ -1,6 +1,6 @@ import datetime import logging -from typing import Any, Dict +from typing import Any from django.core.management import BaseCommand from django.db import transaction @@ -49,7 +49,7 @@ def rename_player(team_bhv_id, old_name, new_name): def _score(player_number: int, goals: int = 0, penalty_tries: int = 0, - penalty_goals: int = 0, **kwargs) -> Dict[str, Any]: + penalty_goals: int = 0, **kwargs) -> dict[str, Any]: return {"player_number": player_number, "goals": goals, "penalty_tries": penalty_tries, "penalty_goals": penalty_goals, **kwargs} @@ -59,8 +59,8 @@ def time(minutes: int, seconds: int = 0): @transaction.atomic -def add_scores(league__bhv_id: int, game_number: int, home_score_data: Dict[str, Dict[str, Any]], - guest_score_data: Dict[str, Dict[str, Any]]): +def add_scores(league__bhv_id: int, game_number: int, home_score_data: dict[str, dict[str, Any]], + guest_score_data: dict[str, dict[str, Any]]): LOGGER.info('add Scores %s %s', league__bhv_id, game_number) try: game = Game.objects.get(league__bhv_id=league__bhv_id, number=game_number) @@ -73,7 +73,7 @@ def add_scores(league__bhv_id: int, game_number: int, home_score_data: Dict[str, LOGGER.warning('skip Game (not found): %s %s', league__bhv_id, game_number) -def _add_scores(game: Game, team: Team, scores_data: Dict[str, Dict[str, Any]]): +def _add_scores(game: Game, team: Team, scores_data: dict[str, dict[str, Any]]): for name, score_data in scores_data.items(): player = Player(name=name, team=team) sco = Score(player=player, game=game, **score_data) diff --git a/src/base/parsing.py b/src/base/parsing.py index 51387008..c11ec309 100644 --- a/src/base/parsing.py +++ b/src/base/parsing.py @@ -1,78 +1,81 @@ import locale import re from datetime import datetime, timedelta -from typing import Match, Optional, Tuple +from typing import cast from urllib.parse import parse_qs, urlsplit from lxml import html +from lxml.etree import _Element from games.models import SportsHall +from teams.models import Team -def html_dom(html_text: str): +def html_dom(html_text: str) -> _Element: return html.fromstring(html_text) -def parse_association_urls(dom, root_url): - items = dom.xpath('//div[@id="main-content"]//table[@summary]/tbody/tr/td[1]/a/@href') +def parse_association_urls(dom: _Element, root_url: str) -> list[str]: + items = cast(list[str], dom.xpath('//div[@id="main-content"]//table[@summary]/tbody/tr/td[1]/a/@href')) return [item if item.startswith('http') else root_url + item for item in items] -def parse_association_bhv_id_from_dom(dom): - [bhv_id] = dom.xpath('//div[@id="app"]/@data-og-id') +def parse_association_bhv_id_from_dom(dom: _Element) -> int: + [bhv_id] = cast(list[str], dom.xpath('//div[@id="app"]/@data-og-id')) return int(bhv_id) -def parse_link_query_item(link, query_key): +def parse_link_query_item(link: _Element, query_key: str) -> str: href = link.get('href') - query = urlsplit(href).query + query = cast(str, urlsplit(href).query) return parse_qs(query)[query_key][0] -def parse_association_bhv_id(link): +def parse_association_bhv_id(link: _Element) -> int: return int(parse_link_query_item(link, 'orgGrpID')) -def parse_association_name(dom): - return dom.xpath('//*[@id="results"]/div/h1/text()[2]')[0] +def parse_association_name(dom: _Element) -> str: + return cast(list[str], dom.xpath('//*[@id="results"]/div/h1/text()[2]'))[0] -def parse_district_items(dom): - return dom.xpath('//select[@name="orgID"]/option[position()>1]') +def parse_district_items(dom: _Element) -> list[_Element]: + return cast(list[_Element], dom.xpath('//select[@name="orgID"]/option[position()>1]')) -def parse_district_link_date(link): +def parse_district_link_date(link: _Element) -> str: return parse_link_query_item(link, 'do') -def parse_league_links(dom): - return dom.xpath('//div[@id="results"]/div/table[2]/tr/td[1]/a') +def parse_league_links(dom: _Element) -> list[_Element]: + return cast(list[_Element], dom.xpath('//div[@id="results"]/div/table[2]/tr/td[1]/a')) -def parse_league_bhv_id(link): +def parse_league_bhv_id(link: _Element) -> int: return int(parse_link_query_item(link, 'score')) -def parse_district_season_start_year(district_season_heading): +def parse_district_season_start_year(district_season_heading: str) -> int | None: matches = re.match(r"Halle(?:nrunde)? (\d{4})/(\d{4})", district_season_heading) return int(matches.group(1)) if matches else None -def parse_league_name(dom): - heading = dom.xpath('//*[@id="results"]/div/h1/text()[2]')[0] +def parse_league_name(dom: _Element) -> str: + heading = cast(list[str], dom.xpath('//*[@id="results"]/div/h1/text()[2]'))[0] return heading.rsplit(' - ', 1)[0] -def parse_team_links(dom): - return dom.xpath('//table[@class="scoretable"]/tr[position() > 1]/td[3]/a') or \ - dom.xpath('//table[@class="scoretable"]/tr[position() > 1]/td[2]/a') +def parse_team_links(dom: _Element) -> list[_Element]: + return cast(list[_Element], dom.xpath('//table[@class="scoretable"]/tr[position() > 1]/td[3]/a')) or \ + cast(list[_Element], dom.xpath('//table[@class="scoretable"]/tr[position() > 1]/td[2]/a')) -def parse_retirements(dom): +def parse_retirements(dom: _Element) -> list[tuple[str, datetime]]: retirements = [] - paragraphs = dom.xpath('//table[@class="scoretable"]/following::div[following::table[@class="gametable"]]') + paragraphs = cast(list[html.HtmlMixin], dom.xpath( + '//table[@class="scoretable"]/following::div[following::table[@class="gametable"]]')) for paragraph in paragraphs: - text = paragraph.text_content() + text: str = cast(str, paragraph.text_content()) matches = re.match(r"(?:Der|Die) (.*) hat.* ([0123]\d\.[012]\d\.\d{2,4}).* zurückgezogen.*", text) if matches: team_name = matches.group(1) @@ -90,26 +93,26 @@ def date_from_text(text): return retirements -def parse_team_bhv_id(link): +def parse_team_bhv_id(link: _Element) -> int: return int(parse_link_query_item(link, 'teamID')) -def parse_team_names(text: str) -> Tuple[str, str]: - match: Optional[Match[str]] = re.match(r"(.+) - (.+)", text) +def parse_team_names(text: str) -> tuple[str, str]: + match: re.Match[str] | None = re.match(r"(.+) - (.+)", text) if match: return match.group(1), match.group(2) raise ValueError(f'invalid team names: {text}') -def parse_game_rows(dom): - return dom.xpath('//table[@class="gametable"]/tr[position() > 1]') +def parse_game_rows(dom: _Element) -> list[_Element]: + return cast(list[_Element], dom.xpath('//table[@class="gametable"]/tr[position() > 1]')) -def parse_team_short_names(game_rows): - return [c.text for game_row in game_rows for c in game_row.xpath('td')[4:7:2]] +def parse_team_short_names(game_rows: list[_Element]) -> list[str | None]: + return [c.text for game_row in game_rows for c in cast(list[_Element], game_row.xpath('td'))[4:7:2]] -def parse_opening_whistle(text: str) -> Optional[datetime]: +def parse_opening_whistle(text: str) -> datetime | None: if not text.strip(): return None locale.setlocale(locale.LC_ALL, "de_DE.UTF-8") @@ -120,10 +123,10 @@ def parse_opening_whistle(text: str) -> Optional[datetime]: raise ValueError(f'invalid opening whistle: {text}') -def parse_sports_hall(dom) -> SportsHall: - table = dom.xpath('//table[@class="gym"]')[0] - name = table[0][1][0].text - city = table[1][1].text +def parse_sports_hall(dom: _Element) -> SportsHall: + table = cast(list[_Element], dom.xpath('//table[@class="gym"]'))[0] + name = cast(str, table[0][1][0].text) + city = cast(str, table[1][1].text) street = table[2][1].text address = street + ", " + city if street else city phone_number = table[3][1].text @@ -135,13 +138,13 @@ def parse_sports_hall(dom) -> SportsHall: longitude=longitude) -def parse_sports_hall_bhv_id(link): +def parse_sports_hall_bhv_id(link: _Element) -> int: return int(parse_link_query_item(link, 'gymID')) -def parse_coordinates(dom) -> Tuple[Optional[str], Optional[str]]: - map_scripts = dom.xpath('//script[contains(text(),"new mxn.LatLonPoint")]') - if not map_scripts: +def parse_coordinates(dom: _Element) -> tuple[str | None, str | None]: + map_scripts = cast(list[_Element], dom.xpath('//script[contains(text(),"new mxn.LatLonPoint")]')) + if not map_scripts or not map_scripts[0].text: return (None, None) match = re.search(r"new mxn.LatLonPoint\(([.0-9]+),([.0-9]+)\)", map_scripts[0].text) if match: @@ -149,7 +152,7 @@ def parse_coordinates(dom) -> Tuple[Optional[str], Optional[str]]: raise ValueError(f'coordinates not found: {map_scripts}') -def parse_goals(game_row) -> Tuple[Optional[int], Optional[int]]: +def parse_goals(game_row: _Element) -> tuple[int | None, int | None]: home_goals = game_row[7].text guest_goals = game_row[9].text if home_goals and guest_goals: @@ -167,14 +170,14 @@ def parse_goals(game_row) -> Tuple[Optional[int], Optional[int]]: return (None, None) -def parse_report_number(cell): +def parse_report_number(cell: _Element) -> int | None: if len(cell) >= 1 and cell[0].text == 'PI': return int(parse_link_query_item(cell[0], 'sGID')) return None -def parse_forfeiting_team(cell, home_team, guest_team): +def parse_forfeiting_team(cell: _Element, home_team: Team, guest_team: Team) -> Team | None: text = str(html.tostring(cell)) if "2:0" in text: return guest_team @@ -183,7 +186,7 @@ def parse_forfeiting_team(cell, home_team, guest_team): return None -def parse_game_time(text: str) -> Optional[timedelta]: +def parse_game_time(text: str) -> timedelta | None: if not text: return None @@ -191,7 +194,7 @@ def parse_game_time(text: str) -> Optional[timedelta]: return timedelta(minutes=int(minutes), seconds=int(seconds)) -def parse_penalty_data(text: str) -> Tuple[int, int]: +def parse_penalty_data(text: str) -> tuple[int, int]: match = re.match(r"(\d+)/(\d+)", text) if match: return int(match.group(1)), int(match.group(2)) diff --git a/src/games/management/commands/import_games.py b/src/games/management/commands/import_games.py index 9eb21e36..4caeda5a 100644 --- a/src/games/management/commands/import_games.py +++ b/src/games/management/commands/import_games.py @@ -1,5 +1,5 @@ import logging -from typing import Dict +from typing import Any from django.core.management import BaseCommand @@ -19,7 +19,7 @@ class Command(BaseCommand): - options: Dict = {} + options: dict[str, Any] = {} def add_arguments(self, parser): add_default_arguments(parser) diff --git a/src/games/tests/unit/test_game_leg.py b/src/games/tests/unit/test_game_leg.py index 6a52e7b1..1af62e5c 100644 --- a/src/games/tests/unit/test_game_leg.py +++ b/src/games/tests/unit/test_game_leg.py @@ -1,5 +1,4 @@ from datetime import datetime, timedelta -from typing import Tuple from django.test import TestCase @@ -58,7 +57,7 @@ def test_four_teams(self): self.assertEqual(expected, game.leg()) -def create_test_game(number, league, home_team, guest_team, expected_leg: Leg, unscheduled=False) -> Tuple[Game, Leg]: +def create_test_game(number, league, home_team, guest_team, expected_leg: Leg, unscheduled=False) -> tuple[Game, Leg]: opening_whistle = datetime.now() + timedelta(weeks=number * 2) if not unscheduled else None game = Game.objects.create(number=number, league=league, opening_whistle=opening_whistle, home_team=home_team, guest_team=guest_team) diff --git a/src/games/urls.py b/src/games/urls.py index 68320e21..ccd2c291 100644 --- a/src/games/urls.py +++ b/src/games/urls.py @@ -1,7 +1,5 @@ -from typing import List - app_name = 'games' -urlpatterns: List = [ +urlpatterns: list = [ ] diff --git a/src/hbscorez/settings.py b/src/hbscorez/settings.py index 47fc1829..a4238070 100644 --- a/src/hbscorez/settings.py +++ b/src/hbscorez/settings.py @@ -1,5 +1,4 @@ from pathlib import Path -from typing import Optional ROOT_DIR: Path = Path.cwd() @@ -205,7 +204,7 @@ PUBLIC_NAMES = True -MATOMO_URL: Optional[str] = None +MATOMO_URL: str | None = None REPORTS_PATH = ROOT_DIR / 'reports' diff --git a/src/players/management/commands/import_reports.py b/src/players/management/commands/import_reports.py index 7ff8e9c6..0c7cc315 100644 --- a/src/players/management/commands/import_reports.py +++ b/src/players/management/commands/import_reports.py @@ -1,6 +1,6 @@ import logging from pathlib import Path -from typing import Any, Dict +from typing import Any import tabula from django.conf import settings @@ -23,7 +23,7 @@ class Command(BaseCommand): - options: Dict[str, Any] = {} + options: dict[str, Any] = {} def add_arguments(self, parser): add_default_arguments(parser) diff --git a/src/players/management/commands/parse_report.py b/src/players/management/commands/parse_report.py index 430c333f..590a99bd 100644 --- a/src/players/management/commands/parse_report.py +++ b/src/players/management/commands/parse_report.py @@ -1,7 +1,4 @@ -from typing import Optional - - -def parse_spectators(table) -> Optional[int]: +def parse_spectators(table) -> int | None: specs: str = table['data'][4][2]['text'] if specs == 'k.A.': return None diff --git a/src/sports_halls/urls.py b/src/sports_halls/urls.py index 3abe4bf1..6d658b36 100644 --- a/src/sports_halls/urls.py +++ b/src/sports_halls/urls.py @@ -1,7 +1,5 @@ -from typing import List - app_name = 'sports-halls' -urlpatterns: List = [ +urlpatterns: list = [ ]