ATG-lib 1.5.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. atg_lib-1.5.0/ATG/__init__.py +8 -0
  2. atg_lib-1.5.0/ATG/api/__init__.py +46 -0
  3. atg_lib-1.5.0/ATG/api/account_v1.py +29 -0
  4. atg_lib-1.5.0/ATG/api/ddragon.py +6 -0
  5. atg_lib-1.5.0/ATG/api/league_v4.py +91 -0
  6. atg_lib-1.5.0/ATG/api/match_v5.py +59 -0
  7. atg_lib-1.5.0/ATG/api/spectator_v5.py +28 -0
  8. atg_lib-1.5.0/ATG/api/summoner_v4.py +43 -0
  9. atg_lib-1.5.0/ATG/api/utils.py +73 -0
  10. atg_lib-1.5.0/ATG/database/__init__.py +3 -0
  11. atg_lib-1.5.0/ATG/database/database.py +10 -0
  12. atg_lib-1.5.0/ATG/match_lib/__init__.py +23 -0
  13. atg_lib-1.5.0/ATG/match_lib/esports_ingest.py +112 -0
  14. atg_lib-1.5.0/ATG/match_lib/ingest_match.py +175 -0
  15. atg_lib-1.5.0/ATG/models/__init__.py +33 -0
  16. atg_lib-1.5.0/ATG/models/account.py +43 -0
  17. atg_lib-1.5.0/ATG/models/base.py +5 -0
  18. atg_lib-1.5.0/ATG/models/champion.py +14 -0
  19. atg_lib-1.5.0/ATG/models/draft_event.py +26 -0
  20. atg_lib-1.5.0/ATG/models/game.py +131 -0
  21. atg_lib-1.5.0/ATG/models/game_event.py +33 -0
  22. atg_lib-1.5.0/ATG/models/participant.py +62 -0
  23. atg_lib-1.5.0/ATG/models/participant_stat.py +177 -0
  24. atg_lib-1.5.0/ATG/models/player.py +28 -0
  25. atg_lib-1.5.0/ATG/models/player_team_association.py +18 -0
  26. atg_lib-1.5.0/ATG/models/series.py +23 -0
  27. atg_lib-1.5.0/ATG/models/team.py +31 -0
  28. atg_lib-1.5.0/ATG/models/team_dto.py +29 -0
  29. atg_lib-1.5.0/ATG/models/team_stat.py +34 -0
  30. atg_lib-1.5.0/ATG/models/tournament.py +33 -0
  31. atg_lib-1.5.0/ATG/rate_limiter/README.md +44 -0
  32. atg_lib-1.5.0/ATG/rate_limiter/__init__.py +7 -0
  33. atg_lib-1.5.0/ATG/rate_limiter/rate_limiter.py +193 -0
  34. atg_lib-1.5.0/ATG/utils/__init__.py +25 -0
  35. atg_lib-1.5.0/ATG/utils/constants.py +65 -0
  36. atg_lib-1.5.0/ATG/utils/db_utils.py +38 -0
  37. atg_lib-1.5.0/ATG/utils/helpers.py +12 -0
  38. atg_lib-1.5.0/LICENSE +674 -0
  39. atg_lib-1.5.0/PKG-INFO +121 -0
  40. atg_lib-1.5.0/README.md +100 -0
  41. atg_lib-1.5.0/pyproject.toml +26 -0
@@ -0,0 +1,8 @@
1
+ from . import api
2
+ from . import database
3
+ from . import match_lib
4
+ from . import models
5
+ from . import rate_limiter
6
+ from . import utils
7
+
8
+ __all__ = ["api", "database", "match_lib", "models", "rate_limiter", "utils"]
@@ -0,0 +1,46 @@
1
+ from .account_v1 import get_account_by_puuid, get_account_by_riot_id
2
+ from .ddragon import get_ddragon_versions
3
+ from .league_v4 import (
4
+ get_masters,
5
+ get_grandmasters,
6
+ get_challengers,
7
+ get_league_entries_by_puuid,
8
+ get_league_entries_by_summoner_id,
9
+ get_all_league_entries,
10
+ get_league_entries_by_league_id,
11
+ )
12
+ from .match_v5 import get_available_matches, get_match_by_id, get_match_history
13
+ from .spectator_v5 import get_active_games, get_featured_games
14
+ from .summoner_v4 import (
15
+ get_summoner_by_account_id,
16
+ get_summoner_by_puuid,
17
+ get_summoner_by_summoner_id,
18
+ )
19
+ from .utils import parse_match_id, REGIONS, QUEUES, DIVISIONS, TIERS
20
+
21
+
22
+ __all__ = [
23
+ "get_account_by_puuid",
24
+ "get_account_by_riot_id",
25
+ "get_ddragon_versions",
26
+ "get_masters",
27
+ "get_grandmasters",
28
+ "get_challengers",
29
+ "get_league_entries_by_puuid",
30
+ "get_league_entries_by_summoner_id",
31
+ "get_all_league_entries",
32
+ "get_league_entries_by_league_id",
33
+ "get_available_matches",
34
+ "get_match_by_id",
35
+ "get_match_history",
36
+ "get_active_games",
37
+ "get_featured_games",
38
+ "get_summoner_by_account_id",
39
+ "get_summoner_by_puuid",
40
+ "get_summoner_by_summoner_id",
41
+ "parse_match_id",
42
+ "REGIONS",
43
+ "QUEUES",
44
+ "DIVISIONS",
45
+ "TIERS",
46
+ ]
@@ -0,0 +1,29 @@
1
+ import requests as r
2
+ from requests import Response
3
+ from .utils import headers, RoutingLiteral
4
+ from ..rate_limiter import riot_api_limiter
5
+
6
+
7
+ @riot_api_limiter(endpoint_key="account_v1_by_riot_id")
8
+ def get_account_by_riot_id(
9
+ game_name: str, tag_line: str, api_key: str, routing: RoutingLiteral = "americas"
10
+ ) -> Response:
11
+ _headers = {"X-Riot-Token": api_key, **headers}
12
+ # There are three routing values for account-v1; americas, asia, and europe. You can query for any account in any region. We recommend using the nearest cluster.
13
+ response = r.get(
14
+ f"https://{routing}.api.riotgames.com/riot/account/v1/accounts/by-riot-id/{game_name}/{tag_line}",
15
+ headers=_headers,
16
+ )
17
+ return response
18
+
19
+
20
+ @riot_api_limiter(endpoint_key="account_v1_by_puuid")
21
+ def get_account_by_puuid(
22
+ puuid: str, api_key: str, routing: RoutingLiteral = "americas"
23
+ ) -> Response:
24
+ _headers = {"X-Riot-Token": api_key, **headers}
25
+ response = r.get(
26
+ f"https://{routing}.api.riotgames.com/riot/account/v1/accounts/by-puuid/{puuid}",
27
+ headers=_headers,
28
+ )
29
+ return response
@@ -0,0 +1,6 @@
1
+ import requests as r
2
+ from requests import Response
3
+
4
+
5
+ def get_ddragon_versions() -> Response:
6
+ return r.get("https://ddragon.leagueoflegends.com/api/versions.json")
@@ -0,0 +1,91 @@
1
+ import requests as r
2
+ from requests import Response
3
+ from .utils import headers, RegionLiteral, QueueLiteral, DivisionLiteral, TierLiteral
4
+ from ..rate_limiter import riot_api_limiter
5
+
6
+
7
+ @riot_api_limiter(endpoint_key="league_v4_get_challengers")
8
+ def get_challengers(
9
+ region: RegionLiteral, queue: QueueLiteral, api_key: str
10
+ ) -> Response:
11
+ _headers = {"X-Riot-Token": api_key, **headers}
12
+ response = r.get(
13
+ f"https://{region.lower()}.api.riotgames.com/lol/league/v4/challengerleagues/by-queue/{queue}",
14
+ headers=_headers,
15
+ )
16
+ return response
17
+
18
+
19
+ @riot_api_limiter(endpoint_key="league_v4_get_grandmasters")
20
+ def get_grandmasters(
21
+ region: RegionLiteral, queue: QueueLiteral, api_key: str
22
+ ) -> Response:
23
+ _headers = {"X-Riot-Token": api_key, **headers}
24
+ response = r.get(
25
+ f"https://{region.lower()}.api.riotgames.com/lol/league/v4/grandmasterleagues/by-queue/{queue}",
26
+ headers=_headers,
27
+ )
28
+ return response
29
+
30
+
31
+ @riot_api_limiter(endpoint_key="league_v4_get_masters")
32
+ def get_masters(region: RegionLiteral, queue: QueueLiteral, api_key: str) -> Response:
33
+ _headers = {"X-Riot-Token": api_key, **headers}
34
+ response = r.get(
35
+ f"https://{region.lower()}.api.riotgames.com/lol/league/v4/masterleagues/by-queue/{queue}",
36
+ headers=_headers,
37
+ )
38
+ return response
39
+
40
+
41
+ @riot_api_limiter(endpoint_key="league_v4_entries_puuid")
42
+ def get_league_entries_by_puuid(
43
+ region: RegionLiteral, puuid: str, api_key: str
44
+ ) -> Response:
45
+ _headers = {"X-Riot-Token": api_key, **headers}
46
+ response = r.get(
47
+ f"https://{region.lower()}.api.riotgames.com/lol/league/v4/entries/by-puuid/{puuid}",
48
+ headers=_headers,
49
+ )
50
+ return response
51
+
52
+
53
+ @riot_api_limiter(endpoint_key="league_v4_entries_summoner_id")
54
+ def get_league_entries_by_summoner_id(
55
+ region: RegionLiteral, summoner_id: str, api_key: str
56
+ ) -> Response:
57
+ _headers = {"X-Riot-Token": api_key, **headers}
58
+ response = r.get(
59
+ f"https://{region.lower()}.api.riotgames.com/lol/league/v4/entries/by-summoner/{summoner_id}",
60
+ headers=_headers,
61
+ )
62
+ return response
63
+
64
+
65
+ @riot_api_limiter(endpoint_key="league_v4_all_entries")
66
+ def get_all_league_entries(
67
+ region: RegionLiteral,
68
+ queue: QueueLiteral,
69
+ tier: TierLiteral,
70
+ division: DivisionLiteral,
71
+ api_key: str,
72
+ page: int = 1,
73
+ ) -> Response:
74
+ _headers = {"X-Riot-Token": api_key, "page": str(page), **headers}
75
+ response = r.get(
76
+ f"https://{region.lower()}.api.riotgames.com/lol/league/v4/entries/{queue}/{tier}/{division}",
77
+ headers=_headers,
78
+ )
79
+ return response
80
+
81
+
82
+ @riot_api_limiter(endpoint_key="league_v4_entries_league_id")
83
+ def get_league_entries_by_league_id(
84
+ region: RegionLiteral, league_id: str, api_key: str
85
+ ) -> Response:
86
+ _headers = {"X-Riot-Token": api_key, **headers}
87
+ response = r.get(
88
+ f"https://{region.lower()}.api.riotgames.com/lol/league/v4/leagues/{league_id}",
89
+ headers=_headers,
90
+ )
91
+ return response
@@ -0,0 +1,59 @@
1
+ import requests as r
2
+ from requests import Response
3
+ from .utils import headers, PLATFORM_ROUTING, parse_match_id, RegionLiteral
4
+ from ..rate_limiter import riot_api_limiter
5
+
6
+
7
+ @riot_api_limiter(endpoint_key="match_v5_by_id")
8
+ def get_match_by_id(match_id: str, api_key: str, timeline: bool = False) -> Response:
9
+ region, _ = parse_match_id(match_id)
10
+ is_timeline = "/timeline" if timeline else ""
11
+ _headers = {"X-Riot-Token": api_key, **headers}
12
+ response = r.get(
13
+ f"https://{PLATFORM_ROUTING[region]}.api.riotgames.com/lol/match/v5/matches/{match_id}{is_timeline}",
14
+ headers=_headers,
15
+ )
16
+ return response
17
+
18
+
19
+ @riot_api_limiter(endpoint_key="match_v5_available_matches")
20
+ def get_available_matches(
21
+ puuid: str, region: RegionLiteral, api_key: str, **kwargs
22
+ ) -> Response:
23
+ _headers = {"X-Riot-Token": api_key, **headers}
24
+ response = r.get(
25
+ f"https://{PLATFORM_ROUTING[region]}.api.riotgames.com/lol/match/v5/matches/by-puuid/{puuid}/ids",
26
+ headers=_headers,
27
+ params=kwargs,
28
+ )
29
+ return response
30
+
31
+
32
+ def get_match_history(
33
+ puuid: str, region: RegionLiteral, api_key: str, **kwargs
34
+ ) -> list[str]:
35
+ start = 0
36
+ count = 100
37
+ match_history = []
38
+ while True:
39
+ match_list = get_available_matches(
40
+ puuid=puuid,
41
+ region=region,
42
+ api_key=api_key,
43
+ start=start,
44
+ count=count,
45
+ **kwargs,
46
+ )
47
+ if match_list:
48
+ try:
49
+ match_list.raise_for_status()
50
+ match_list = match_list.json()
51
+ if len(match_list) == 0:
52
+ break
53
+ match_history.extend(match_list)
54
+ start += count
55
+ except:
56
+ # If an error occurs, we will fail.
57
+ print(f"{match_list.status_code} - Failed to retrieve match history")
58
+ return match_history
59
+ return match_history
@@ -0,0 +1,28 @@
1
+ import requests as r
2
+ from requests import Response
3
+ from .utils import headers, RegionLiteral
4
+ from ..rate_limiter import riot_api_limiter
5
+
6
+
7
+ @riot_api_limiter(endpoint_key="spectator_v5_active_games")
8
+ def get_active_games(
9
+ puuid: str, region: RegionLiteral, api_key: str, **kwargs
10
+ ) -> Response:
11
+ _headers = {"X-Riot-Token": api_key, **headers}
12
+ response = r.get(
13
+ f"https://{region.lower()}.api.riotgames.com/lol/spectator/v5/active-games/by-summoner/{puuid}",
14
+ headers=_headers,
15
+ params=kwargs,
16
+ )
17
+ return response
18
+
19
+
20
+ @riot_api_limiter(endpoint_key="spectator_v5_featured_games")
21
+ def get_featured_games(region: RegionLiteral, api_key: str, **kwargs) -> Response:
22
+ _headers = {"X-Riot-Token": api_key, **headers}
23
+ response = r.get(
24
+ f"https://{region.lower()}.api.riotgames.com/lol/spectator/v5/featured-games",
25
+ headers=_headers,
26
+ params=kwargs,
27
+ )
28
+ return response
@@ -0,0 +1,43 @@
1
+ import requests as r
2
+ from requests import Response
3
+ from .utils import headers, RegionLiteral
4
+ from ..rate_limiter import riot_api_limiter
5
+
6
+
7
+ @riot_api_limiter(endpoint_key="summoner_v4_by_account_id")
8
+ def get_summoner_by_account_id(
9
+ account_id: str, region: RegionLiteral, api_key: str, **kwargs
10
+ ) -> Response:
11
+ _headers = {"X-Riot-Token": api_key, **headers}
12
+ response = r.get(
13
+ f"https://{region.lower()}.api.riotgames.com/lol/summoner/v4/summoners/by-account/{account_id}",
14
+ headers=_headers,
15
+ params=kwargs,
16
+ )
17
+ return response
18
+
19
+
20
+ @riot_api_limiter(endpoint_key="summoner_v4_by_puuid")
21
+ def get_summoner_by_puuid(
22
+ puuid: str, region: RegionLiteral, api_key: str, **kwargs
23
+ ) -> Response:
24
+ _headers = {"X-Riot-Token": api_key, **headers}
25
+ response = r.get(
26
+ f"https://{region.lower()}.api.riotgames.com/lol/summoner/v4/summoners/by-puuid/{puuid}",
27
+ headers=_headers,
28
+ params=kwargs,
29
+ )
30
+ return response
31
+
32
+
33
+ @riot_api_limiter(endpoint_key="summoner_v4_by_summoner_id")
34
+ def get_summoner_by_summoner_id(
35
+ summoner_id: str, region: RegionLiteral, api_key: str, **kwargs
36
+ ) -> Response:
37
+ _headers = {"X-Riot-Token": api_key, **headers}
38
+ response = r.get(
39
+ f"https://{region.lower()}.api.riotgames.com/lol/summoner/v4/summoners/{summoner_id}",
40
+ headers=_headers,
41
+ params=kwargs,
42
+ )
43
+ return response
@@ -0,0 +1,73 @@
1
+ from typing import Dict, Literal
2
+ from enum import StrEnum
3
+
4
+ headers = {
5
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36",
6
+ "Accept-Language": "en-US,en-CA;q=0.9,en;q=0.8,ru-RU;q=0.7,ru;q=0.6,ko-KR;q=0.5,ko;q=0.4",
7
+ "Accept-Charset": "application/x-www-form-urlencoded; charset=UTF-8",
8
+ }
9
+
10
+ # The AMERICAS routing value serves NA, BR, LAN and LAS. The ASIA routing value serves KR and JP. The EUROPE routing value serves EUNE, EUW, ME1, TR and RU. The SEA routing value serves OCE, SG2, TW2 and VN2.
11
+ PLATFORM_ROUTING: Dict[str, str] = {
12
+ "NA1": "americas",
13
+ "BR1": "americas",
14
+ "LA1": "americas",
15
+ "LA2": "americas",
16
+ "KR": "asia",
17
+ "JP": "asia",
18
+ "ME1": "europe",
19
+ "EUN1": "europe",
20
+ "EUW1": "europe",
21
+ "TR1": "europe",
22
+ "RU": "europe",
23
+ "OC1": "sea",
24
+ "SG2": "sea",
25
+ "TW2": "sea",
26
+ "VN2": "sea",
27
+ }
28
+
29
+ RoutingLiteral = Literal["americas", "asia", "europe", "sea"]
30
+ RegionLiteral = Literal[
31
+ "NA1",
32
+ "BR1",
33
+ "LA1",
34
+ "LA2",
35
+ "KR",
36
+ "JP",
37
+ "ME1",
38
+ "EUN1",
39
+ "EUW1",
40
+ "TR1",
41
+ "RU",
42
+ "OC1",
43
+ "SG2",
44
+ "TW2",
45
+ "VN2",
46
+ ]
47
+ QueueLiteral = Literal["RANKED_SOLO_5x5", "RANKED_FLEX_SR", "RANKED_FLEX_TT"]
48
+ DivisionLiteral = Literal["I", "II", "III", "IV"]
49
+ TierLiteral = Literal[
50
+ "DIAMOND", "EMERALD", "PLATINUM", "GOLD", "SILVER", "BRONZE", "IRON"
51
+ ]
52
+
53
+ REGIONS = StrEnum("Regions", [(_, _) for _ in PLATFORM_ROUTING.keys()])
54
+ QUEUES = StrEnum(
55
+ "Queues", [(_, _) for _ in ["RANKED_SOLO_5x5", "RANKED_FLEX_SR", "RANKED_FLEX_TT"]]
56
+ )
57
+ DIVISIONS = StrEnum("Divisions", [(_, _) for _ in ["I", "II", "III", "IV"]])
58
+ TIERS = StrEnum(
59
+ "Tiers",
60
+ [
61
+ (_, _)
62
+ for _ in ["DIAMOND", "EMERALD", "PLATINUM", "GOLD", "SILVER", "BRONZE", "IRON"]
63
+ ],
64
+ )
65
+
66
+
67
+ def parse_match_id(match_id: str) -> tuple[str, str]:
68
+ split = match_id.split("_")
69
+ if len(split) != 2:
70
+ raise ValueError(f"Unable to parse {match_id}")
71
+ if split[0] not in REGIONS:
72
+ raise ValueError(f"Unknown region {split[0]} received")
73
+ return split[0], split[1]
@@ -0,0 +1,3 @@
1
+ from .database import get_session_factory
2
+
3
+ __all__ = ["get_session_factory"]
@@ -0,0 +1,10 @@
1
+ from sqlalchemy import create_engine
2
+ from sqlalchemy.orm import sessionmaker
3
+ from ..models import Base
4
+
5
+
6
+ def get_session_factory(database_url: str, read_only: bool = False):
7
+ engine = create_engine(database_url, pool_recycle=3600)
8
+ if not read_only:
9
+ Base.metadata.create_all(bind=engine)
10
+ return sessionmaker(autocommit=False, autoflush=True, bind=engine)
@@ -0,0 +1,23 @@
1
+ from .esports_ingest import (
2
+ create_account,
3
+ create_player,
4
+ create_missing_accounts,
5
+ parse_game_participants,
6
+ get_common_prefix_length
7
+ )
8
+ from .ingest_match import (
9
+ insert_match_history,
10
+ update_account_names,
11
+ process_match,
12
+ )
13
+
14
+ __all__ = [
15
+ "create_account",
16
+ "create_player",
17
+ "create_missing_accounts",
18
+ "parse_game_participants",
19
+ "get_common_prefix_length",
20
+ "insert_match_history",
21
+ "update_account_names",
22
+ "process_match",
23
+ ]
@@ -0,0 +1,112 @@
1
+ from ..models import Player, Account, Participant
2
+ from sqlalchemy.orm import Session
3
+ from sqlalchemy import select
4
+
5
+ def create_player(name: str) -> Player:
6
+ """Creates a player object
7
+
8
+ Args:
9
+ name (str): Player name
10
+
11
+ Returns:
12
+ Player: Created player object
13
+ """
14
+ return Player(name=name)
15
+
16
+ def create_account(puuid: str, name: str, tagline: str, player_id: int, region: str = "TOURNAMENT", skip_update: bool = True) -> Account:
17
+ """Creates an account object
18
+
19
+ Args:
20
+ puuid (str): Account identifier (PUUID / Account ID)
21
+ name (str): Account name
22
+ tagline (str): Account tagline
23
+ player_id (int): Player ID (foreign key)
24
+ region (str, optional): Account region. Defaults to "TOURNAMENT".
25
+ skip_update (bool, optional): Should be updated automatically?. Defaults to True.
26
+
27
+ Returns:
28
+ Account: Created account object
29
+ """
30
+ return Account(
31
+ puuid=puuid,
32
+ name=name,
33
+ tagline=tagline,
34
+ region=region,
35
+ player_id=player_id,
36
+ skip_update=skip_update)
37
+
38
+ def get_puuid(participant) -> str:
39
+ return str(participant.get("puuid", participant["accountID"]))
40
+ def get_game_name(participant) -> str:
41
+ return participant.get("riotId", {"displayName": participant.get("summonerName")})["displayName"]
42
+ def get_tagline(participant) -> str:
43
+ return participant.get("riotId", {}).get("tagLine", "")
44
+ def get_team_id(participant) -> int:
45
+ return participant.get("teamID", participant.get("teamId"))
46
+
47
+ def parse_game_participants(participants: list[dict], game_id: str, winning_team_id: int, NAME_ID_MAP: dict[str, int]) -> list[Participant]:
48
+ """Parses the participants into a list of Participant objects
49
+
50
+ Args:
51
+ participants (list[dict]): List of game participants
52
+ winning_team_id (int): 100 or 200 (blue/red winners)
53
+ NAME_ID_MAP (dict[str, int]): champion name -> champion id map
54
+
55
+ Returns:
56
+ list[Participant]: Parsed participant objects
57
+ """
58
+ game_participants = []
59
+ for participant in participants:
60
+ game_participants.append(Participant(
61
+ game_id = game_id,
62
+ puuid = get_puuid(participant),
63
+ participant_id = participant["participantID"],
64
+ riot_id_game_name = get_game_name(participant),
65
+ riot_id_tagline = get_tagline(participant),
66
+ summoner_id = participant["accountID"],
67
+ team_id = get_team_id(participant),
68
+ champion_id = NAME_ID_MAP[participant["championName"].lower()],
69
+ win = get_team_id(participant) == winning_team_id,
70
+ ))
71
+ return game_participants
72
+
73
+ def get_common_prefix_length(player_names: list[str]) -> int:
74
+ """Gets the number of charaters shared in the prefix of all items of a list
75
+
76
+ Args:
77
+ player_names (list[str]): A list of player names
78
+
79
+ Returns:
80
+ int: The length of the shared prefix
81
+ """
82
+ common_prefix_length = 0
83
+ for chars in zip(*player_names):
84
+ if len(set(chars)) == 1:
85
+ common_prefix_length += 1
86
+ else:
87
+ break
88
+ return common_prefix_length
89
+
90
+ def create_missing_accounts(session: Session, participants: dict):
91
+ """Helper function to parse a list of participants and add to the session any missing players/accounts.
92
+
93
+ Args:
94
+ session (Session): Active SQLAlchemy session object
95
+ participants (dict): A match-v5 like participant list
96
+ """
97
+ stored_puuids = session.scalars(select(Account.puuid)).all()
98
+ for participant in participants:
99
+ puuid = get_puuid(participant)
100
+ if puuid not in stored_puuids:
101
+ game_name = get_game_name(participant)
102
+ player_names = [get_game_name(p) for p in participants if get_team_id(p) == get_team_id(participant)]
103
+ player_name = game_name[get_common_prefix_length(player_names):]
104
+
105
+ db_player = session.scalar(select(Player).where(Player.name == player_name))
106
+
107
+ if db_player is None:
108
+ db_player = create_player(player_name)
109
+ session.add(db_player)
110
+ session.flush()
111
+
112
+ session.add(create_account(puuid, game_name, get_tagline(participant), db_player.id))