Skip to content

Commit

Permalink
refactor: add better api types
Browse files Browse the repository at this point in the history
  • Loading branch information
tbmc committed May 25, 2024
1 parent 44ebdba commit 1daaf9a
Show file tree
Hide file tree
Showing 15 changed files with 387 additions and 56 deletions.
3 changes: 1 addition & 2 deletions calendar_connector/consts.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from typing import Any
from collections import namedtuple
import pytz

Expand All @@ -18,7 +17,7 @@
route_change_presence = "/api/change_my_presence"

PLAYED_WORDS = "played", "present", "available"
EVENT_TYPE = dict[str, int | str | Any | list[Any] | dict[str, Any]]

TIMEZONE = pytz.timezone("UTC")

ORDER_PRESENT = {
Expand Down
10 changes: 4 additions & 6 deletions calendar_connector/event_convertor.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
from datetime import datetime
from typing import Any, cast, Optional
from typing import Optional

from icalendar import Event

from calendar_connector.consts import EVENT_TYPE
from calendar_connector.datetime_utils import get_current_timestamp
from calendar_connector.event_utils.date import extract_event_dates
from calendar_connector.event_utils.description import (
Expand All @@ -13,12 +12,13 @@
from calendar_connector.event_utils.location import extract_event_location
from calendar_connector.event_utils.summary import extract_event_summary
from calendar_connector.normalize import normalize
from calendar_connector.types.event_type import EventType


def event_to_calendar_event(
team_id: int,
team_name: str,
event_data: EVENT_TYPE,
event_data: EventType,
links_data: Optional[GenerateLinksData],
team_web_url: str,
) -> Event:
Expand All @@ -38,8 +38,6 @@ def event_to_calendar_event(
event.add("created", datetime(2020, 1, 1, 1, 1, 1))
event.add("last-modified", datetime(2020, 1, 1, 1, 1, 1))

category_name = normalize(
cast(dict[str, str | Any], event_data["category"])["localized_name"]
)
category_name = normalize(event_data["category"]["localized_name"])

return event
10 changes: 5 additions & 5 deletions calendar_connector/event_utils/date.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
from datetime import datetime, timedelta
from typing import cast

from icalendar import Event

from calendar_connector.consts import EVENT_TYPE, TIMEZONE
from calendar_connector.consts import TIMEZONE
from calendar_connector.types.event_type import EventType


def extract_event_dates(event_data: EVENT_TYPE, event: Event) -> None:
start_date = datetime.fromisoformat(cast(str, event_data["start_at"]))
def extract_event_dates(event_data: EventType, event: Event) -> None:
start_date = datetime.fromisoformat(event_data["start_at"])
start_date = start_date.astimezone(TIMEZONE)
end_date_str = cast(str | None, event_data["end_at"])
end_date_str = event_data["end_at"]
if end_date_str is not None:
end_date = datetime.fromisoformat(end_date_str)
else:
Expand Down
20 changes: 9 additions & 11 deletions calendar_connector/event_utils/description.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
from icalendar import Event

from calendar_connector.consts import (
EVENT_TYPE,
ORDER_PRESENT,
route_change_presence,
PRESENCE,
)
from calendar_connector.datetime_utils import get_formated_current_time
from calendar_connector.event_utils.score import extract_scores
from calendar_connector.cryptography import generate_hash
from calendar_connector.types.event_type import EventType


@dataclasses.dataclass
Expand All @@ -23,20 +23,18 @@ class GenerateLinksData:
url_root: str


def _extract_attendee_description(event_data: EVENT_TYPE) -> str:
attendance_groups = cast(
list[dict[str, int | str]] | None, event_data["attendance_groups"]
)
def _extract_attendee_description(event_data: EventType) -> str:
attendance_groups = event_data["attendance_groups"]
attendee = ""
if attendance_groups is not None:
attendance_group_list: list[tuple[int, str, int]] = []
for ppc in attendance_groups:
slug_sort_value = ORDER_PRESENT.get(cast(str, ppc["slug_name"]), 0)
slug_sort_value = ORDER_PRESENT.get(ppc["slug_name"], 0)
attendance_group_list.append(
(
slug_sort_value,
cast(str, ppc["localized_name"]),
cast(int, ppc["count"]),
ppc["localized_name"],
ppc["count"],
)
)
attendance_group_list.sort(reverse=True)
Expand Down Expand Up @@ -84,14 +82,14 @@ def _generate_response_links(
return f"{present} | {absent}"


def _generate_link_to_sporteasy(team_web_url: str, event_data: EVENT_TYPE) -> str:
def _generate_link_to_sporteasy(team_web_url: str, event_data: EventType) -> str:
id_ = event_data["id"]
return f'<a href="{team_web_url[:-1]}/event/{id_}/">SportEasy event</a>'


def extract_event_description(
team_id: int,
event_data: EVENT_TYPE,
event_data: EventType,
event: Event,
links_data: Optional[GenerateLinksData],
team_web_url: str,
Expand All @@ -104,7 +102,7 @@ def extract_event_description(
description += f"{attendee}\n"

if links_data:
event_id = cast(int, event_data["id"])
event_id = event_data["id"]
response_links = _generate_response_links(team_id, event_id, links_data)
description += f"{response_links}\n"

Expand Down
13 changes: 4 additions & 9 deletions calendar_connector/event_utils/location.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
from typing import cast, Any
from typing import Optional

from icalendar import Event

from calendar_connector.consts import EVENT_TYPE
from calendar_connector.normalize import normalize
from calendar_connector.types.event_type import EventType


def extract_event_location(event_data: EVENT_TYPE, event: Event) -> None:
def extract_event_location(event_data: Optional[EventType], event: Event) -> None:
if event_data is None or event_data["location"] is None:
return

location = normalize(
cast(
dict[str, str | Any],
event_data["location"],
)["formatted_address"]
)
location = normalize(event_data["location"]["formatted_address"])
event.add("location", location)
22 changes: 13 additions & 9 deletions calendar_connector/event_utils/score.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
from typing import Literal, cast, Any
from typing import Literal

from calendar_connector.consts import EVENT_TYPE
from calendar_connector.types.event_type import EventType


def _extract_scores_for_opponent(
event_data: EVENT_TYPE, left_or_right: Literal["left", "right"]
event_data: EventType, left_or_right: Literal["left", "right"]
) -> tuple[str, int] | None:
opponent = event_data.get(f"opponent_{left_or_right}")
if left_or_right == "left":
opponent = event_data.get("opponent_left")
else:
opponent = event_data.get("opponent_right")

if opponent is None:
return None
opponent = cast(dict[str, Any], opponent)
score_str = opponent.get("score")

score_str = opponent["score"]
if score_str is None:
return None
name = cast(str, opponent.get("short_name"))
score = int(cast(str, score_str))
name = opponent["short_name"]
score = int(score_str)
return name, score


def extract_scores(event_data: EVENT_TYPE) -> str | None:
def extract_scores(event_data: EventType) -> str | None:
left = _extract_scores_for_opponent(event_data, "left")
right = _extract_scores_for_opponent(event_data, "right")

Expand Down
12 changes: 5 additions & 7 deletions calendar_connector/event_utils/summary.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
from typing import cast, Any

from icalendar import Event

from calendar_connector.consts import EVENT_TYPE, MY_PRESENCE
from calendar_connector.consts import MY_PRESENCE
from calendar_connector.custom_exceptions import AttributeNotFoundException
from calendar_connector.normalize import normalize
from calendar_connector.types.event_type import EventType


def _status_to_ics_status(sp_status: str) -> str:
Expand All @@ -18,8 +17,8 @@ def _status_to_ics_status(sp_status: str) -> str:
return sp_status


def extract_event_summary(event_data: EVENT_TYPE, event: Event, team_name: str) -> None:
name = normalize(cast(str, event_data["name"]))
def extract_event_summary(event_data: EventType, event: Event, team_name: str) -> None:
name = normalize(event_data["name"])
if team_name not in name:
summary = f"{team_name} - {name}"
else:
Expand All @@ -37,9 +36,8 @@ def extract_event_summary(event_data: EVENT_TYPE, event: Event, team_name: str)
# summary += f" - contre {opponent_name}"

me_object = event_data.get("me", {})
me_object = cast(dict[str, Any], me_object)
if me_object is not None and me_object.get("group") is not None:
group = cast(dict[str, str], me_object.get("group", {}))
group = me_object.get("group", {})
localized_name_presence = group.get("localized_name")
if localized_name_presence is not None:
summary += f" - {normalize(localized_name_presence)}"
Expand Down
18 changes: 11 additions & 7 deletions calendar_connector/sporteasy_connector.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
from typing import cast
import collections

import requests

from calendar_connector.consts import (
url_authenticate,
url_list_teams,
EVENT_TYPE,
url_list_events,
url_put_event_presence,
url_me,
url_csrf,
)
from calendar_connector.normalize import normalize
from calendar_connector.types.event_type import EventType
from calendar_connector.types.me_type import MeType
from calendar_connector.types.request_type import RequestType, CsrfType
from calendar_connector.types.team_type import TeamType

team_namedtuple = collections.namedtuple("team_namedtuple", ["id", "name", "web_url"])

Expand All @@ -37,28 +39,30 @@ def login(self, username: str, password: str) -> str:

def list_teams(self) -> list[team_namedtuple]:
response = self.session_requests.get(url_list_teams)
data = response.json()
data: RequestType[TeamType] = response.json()
return [
team_namedtuple(d["id"], normalize(d["name"]), d["web_url"])
for d in data["results"]
]

def list_events(self, team_id: int) -> list[EVENT_TYPE]:
def list_events(self, team_id: int) -> list[EventType]:
response = self.session_requests.get(
url_list_events.format(team_id=team_id),
headers={"Accept-Language": "fr-FR"},
)
data: list[EVENT_TYPE] = response.json()["results"]
data: list[EventType] = response.json()["results"]
return data

def get_profile_id(self) -> int:
response = self.session_requests.get(url_me)
profile_id = cast(int, response.json()["id"])
data: MeType = response.json()
profile_id = data["id"]
return profile_id

def get_csrf_token(self, team_id: int) -> tuple[str, str]:
response = self.session_requests.get(url_csrf)
csrf = response.json()["csrf_token"]
data: CsrfType = response.json()
csrf = data["csrf_token"]

teams = self.session_requests.get(url_list_teams).json()["results"]
current_team = [t for t in teams if t["id"] == team_id][0]
Expand Down
Empty file.
86 changes: 86 additions & 0 deletions calendar_connector/types/event_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
from typing import TypedDict, Any, Optional

from calendar_connector.types.season_type import SeasonType
from calendar_connector.types.shared_type import CountryType


class LocationType(TypedDict):
id: int
is_stadium: bool
lat: float
lng: float
name: str
url: str
formatted_address: str
country: CountryType


class GroupType(TypedDict):
attendance_status: str
importance_level: int
localized_name: str
slug_name: str
target_groups: list[str]


class OpponentType(TypedDict):
id: str
full_name: str
is_current_team: bool
is_home: bool
jersey_color: Optional[Any]
match_outcome: str
score: int
short_name: str


class AttendanceGroupType(TypedDict):
attendance_status: str
count: int
importance_level: int
localized_name: str
slug_name: str


class CategoryType(TypedDict):
championship_day: int
id: int
localized_name: str
slug_name: str
type: str


class RegistrationParametersType(TypedDict):
hide_attendance: bool


class MeType(TypedDict):
group: GroupType
opponent: str
url: str


class EventType(TypedDict):
id: int
name: str
start_at: str
end_at: str
url: str
thread_id: int
location: LocationType
available_threshold_reached: bool
can_see_attendance: bool
is_cancelled: bool
is_date_unknown: bool
is_past: bool
is_recurring: bool
is_sportive: bool
me: MeType
opponent_left: Optional[OpponentType]
opponent_right: Optional[OpponentType]
registration_open_at: str
step: str
attendance_groups: list[AttendanceGroupType]
category: CategoryType
registration_parameters: RegistrationParametersType
season: SeasonType
Loading

0 comments on commit 1daaf9a

Please sign in to comment.