transfermarkt-api 5.0.0__py3-none-any.whl
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.
- transfermarkt_api/__init__.py +23 -0
- transfermarkt_api/_version.py +3 -0
- transfermarkt_api/cache.py +20 -0
- transfermarkt_api/client.py +226 -0
- transfermarkt_api/exceptions.py +31 -0
- transfermarkt_api/py.typed +0 -0
- transfermarkt_api/schemas/__init__.py +24 -0
- transfermarkt_api/schemas/achievements.py +25 -0
- transfermarkt_api/schemas/base.py +17 -0
- transfermarkt_api/schemas/clubs/__init__.py +3 -0
- transfermarkt_api/schemas/clubs/listing.py +14 -0
- transfermarkt_api/schemas/clubs/players.py +30 -0
- transfermarkt_api/schemas/clubs/profile.py +82 -0
- transfermarkt_api/schemas/clubs/search.py +19 -0
- transfermarkt_api/schemas/coaches/__init__.py +31 -0
- transfermarkt_api/schemas/competitions/__init__.py +4 -0
- transfermarkt_api/schemas/competitions/clubs.py +15 -0
- transfermarkt_api/schemas/competitions/search.py +21 -0
- transfermarkt_api/schemas/competitions/seasons.py +15 -0
- transfermarkt_api/schemas/competitions/table.py +31 -0
- transfermarkt_api/schemas/countries.py +16 -0
- transfermarkt_api/schemas/games/__init__.py +83 -0
- transfermarkt_api/schemas/players/__init__.py +9 -0
- transfermarkt_api/schemas/players/absences.py +22 -0
- transfermarkt_api/schemas/players/injuries.py +20 -0
- transfermarkt_api/schemas/players/market_value.py +19 -0
- transfermarkt_api/schemas/players/matches.py +56 -0
- transfermarkt_api/schemas/players/national_career.py +21 -0
- transfermarkt_api/schemas/players/profile.py +81 -0
- transfermarkt_api/schemas/players/search.py +25 -0
- transfermarkt_api/schemas/players/stats.py +24 -0
- transfermarkt_api/schemas/players/transfers.py +38 -0
- transfermarkt_api/services/__init__.py +0 -0
- transfermarkt_api/services/achievements.py +50 -0
- transfermarkt_api/services/clubs/__init__.py +0 -0
- transfermarkt_api/services/clubs/achievements.py +10 -0
- transfermarkt_api/services/clubs/listing.py +22 -0
- transfermarkt_api/services/clubs/players.py +88 -0
- transfermarkt_api/services/clubs/profile.py +114 -0
- transfermarkt_api/services/clubs/search.py +33 -0
- transfermarkt_api/services/coaches/__init__.py +0 -0
- transfermarkt_api/services/coaches/profile.py +29 -0
- transfermarkt_api/services/coaches/search.py +18 -0
- transfermarkt_api/services/competitions/__init__.py +0 -0
- transfermarkt_api/services/competitions/clubs.py +30 -0
- transfermarkt_api/services/competitions/search.py +61 -0
- transfermarkt_api/services/competitions/seasons.py +28 -0
- transfermarkt_api/services/competitions/table.py +61 -0
- transfermarkt_api/services/countries.py +12 -0
- transfermarkt_api/services/games/__init__.py +0 -0
- transfermarkt_api/services/games/game.py +136 -0
- transfermarkt_api/services/players/__init__.py +0 -0
- transfermarkt_api/services/players/absences.py +33 -0
- transfermarkt_api/services/players/achievements.py +10 -0
- transfermarkt_api/services/players/injuries.py +42 -0
- transfermarkt_api/services/players/market_value.py +35 -0
- transfermarkt_api/services/players/matches.py +107 -0
- transfermarkt_api/services/players/national_career.py +31 -0
- transfermarkt_api/services/players/profile.py +122 -0
- transfermarkt_api/services/players/search.py +44 -0
- transfermarkt_api/services/players/stats.py +74 -0
- transfermarkt_api/services/players/transfers.py +89 -0
- transfermarkt_api/tfmkt/__init__.py +1 -0
- transfermarkt_api/tfmkt/client.py +249 -0
- transfermarkt_api/tfmkt/concurrency.py +17 -0
- transfermarkt_api/tfmkt/freshness.py +38 -0
- transfermarkt_api/tfmkt/reference.py +116 -0
- transfermarkt_api-5.0.0.dist-info/METADATA +234 -0
- transfermarkt_api-5.0.0.dist-info/RECORD +71 -0
- transfermarkt_api-5.0.0.dist-info/WHEEL +4 -0
- transfermarkt_api-5.0.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Async Python access to Transfermarkt data."""
|
|
2
|
+
|
|
3
|
+
from transfermarkt_api._version import __version__ as __version__
|
|
4
|
+
from transfermarkt_api.cache import CacheConfig as CacheConfig
|
|
5
|
+
from transfermarkt_api.client import Transfermarkt as Transfermarkt
|
|
6
|
+
from transfermarkt_api.exceptions import (
|
|
7
|
+
EndpointUnavailable as EndpointUnavailable,
|
|
8
|
+
)
|
|
9
|
+
from transfermarkt_api.exceptions import (
|
|
10
|
+
NotFound as NotFound,
|
|
11
|
+
)
|
|
12
|
+
from transfermarkt_api.exceptions import (
|
|
13
|
+
TransfermarktError as TransfermarktError,
|
|
14
|
+
)
|
|
15
|
+
from transfermarkt_api.exceptions import (
|
|
16
|
+
UpstreamBlocked as UpstreamBlocked,
|
|
17
|
+
)
|
|
18
|
+
from transfermarkt_api.exceptions import (
|
|
19
|
+
UpstreamError as UpstreamError,
|
|
20
|
+
)
|
|
21
|
+
from transfermarkt_api.exceptions import (
|
|
22
|
+
UpstreamTimeout as UpstreamTimeout,
|
|
23
|
+
)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""Configuration for the SDK's per-client response caches."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from math import isfinite
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass(frozen=True)
|
|
8
|
+
class CacheConfig:
|
|
9
|
+
"""Successful response TTLs (seconds) and cache capacity."""
|
|
10
|
+
|
|
11
|
+
ttl: float = 600
|
|
12
|
+
reference_ttl: float = 86_400
|
|
13
|
+
max_entries: int = 2_000
|
|
14
|
+
|
|
15
|
+
def __post_init__(self) -> None:
|
|
16
|
+
"""Reject unusable cache settings before allocating resources."""
|
|
17
|
+
if not isinstance(self.max_entries, int) or isinstance(self.max_entries, bool) or self.max_entries < 1:
|
|
18
|
+
raise ValueError("max_entries must be a positive integer")
|
|
19
|
+
if not all(isfinite(value) and value > 0 for value in (self.ttl, self.reference_ttl)):
|
|
20
|
+
raise ValueError("cache TTLs must be finite and positive")
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
"""Typed async facade over the upstream assembly services."""
|
|
2
|
+
|
|
3
|
+
from collections.abc import Awaitable, Callable
|
|
4
|
+
from types import TracebackType
|
|
5
|
+
from typing import Any, NoReturn, Self, TypeVar
|
|
6
|
+
|
|
7
|
+
import httpx
|
|
8
|
+
from pydantic import BaseModel, ValidationError
|
|
9
|
+
|
|
10
|
+
from transfermarkt_api.cache import CacheConfig
|
|
11
|
+
from transfermarkt_api.exceptions import EndpointUnavailable, UpstreamError
|
|
12
|
+
from transfermarkt_api.schemas import (
|
|
13
|
+
Achievements,
|
|
14
|
+
ClubListing,
|
|
15
|
+
ClubPlayers,
|
|
16
|
+
ClubProfile,
|
|
17
|
+
ClubSearch,
|
|
18
|
+
CoachProfile,
|
|
19
|
+
CoachSearch,
|
|
20
|
+
CompetitionClubs,
|
|
21
|
+
CompetitionSearch,
|
|
22
|
+
CompetitionSeasons,
|
|
23
|
+
CompetitionTable,
|
|
24
|
+
Countries,
|
|
25
|
+
Game,
|
|
26
|
+
PlayerAbsences,
|
|
27
|
+
PlayerInjuries,
|
|
28
|
+
PlayerMarketValue,
|
|
29
|
+
PlayerMatches,
|
|
30
|
+
PlayerNationalCareer,
|
|
31
|
+
PlayerProfile,
|
|
32
|
+
PlayerSearch,
|
|
33
|
+
PlayerStats,
|
|
34
|
+
PlayerTransfers,
|
|
35
|
+
)
|
|
36
|
+
from transfermarkt_api.services import countries
|
|
37
|
+
from transfermarkt_api.services.clubs import achievements as club_achievements
|
|
38
|
+
from transfermarkt_api.services.clubs import listing as club_listing
|
|
39
|
+
from transfermarkt_api.services.clubs import players as club_players
|
|
40
|
+
from transfermarkt_api.services.clubs import profile as club_profile
|
|
41
|
+
from transfermarkt_api.services.clubs import search as club_search
|
|
42
|
+
from transfermarkt_api.services.coaches import profile as coach_profile
|
|
43
|
+
from transfermarkt_api.services.coaches import search as coach_search
|
|
44
|
+
from transfermarkt_api.services.competitions import clubs as competition_clubs
|
|
45
|
+
from transfermarkt_api.services.competitions import search as competition_search
|
|
46
|
+
from transfermarkt_api.services.competitions import seasons as competition_seasons
|
|
47
|
+
from transfermarkt_api.services.competitions import table as competition_table
|
|
48
|
+
from transfermarkt_api.services.games import game
|
|
49
|
+
from transfermarkt_api.services.players import absences as player_absences
|
|
50
|
+
from transfermarkt_api.services.players import achievements as player_achievements
|
|
51
|
+
from transfermarkt_api.services.players import injuries as player_injuries
|
|
52
|
+
from transfermarkt_api.services.players import market_value as player_market_value
|
|
53
|
+
from transfermarkt_api.services.players import matches as player_matches
|
|
54
|
+
from transfermarkt_api.services.players import national_career as player_national_career
|
|
55
|
+
from transfermarkt_api.services.players import profile as player_profile
|
|
56
|
+
from transfermarkt_api.services.players import search as player_search
|
|
57
|
+
from transfermarkt_api.services.players import stats as player_stats
|
|
58
|
+
from transfermarkt_api.services.players import transfers as player_transfers
|
|
59
|
+
from transfermarkt_api.tfmkt import TfmktClient
|
|
60
|
+
from transfermarkt_api.tfmkt.freshness import fetch_scope
|
|
61
|
+
|
|
62
|
+
Model = TypeVar("Model", bound=BaseModel)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class Transfermarkt:
|
|
66
|
+
"""Async data client; owns its transport and supports one event loop per instance."""
|
|
67
|
+
|
|
68
|
+
def __init__(
|
|
69
|
+
self,
|
|
70
|
+
*,
|
|
71
|
+
base_url: str = "https://tmapi.transfermarkt.technology",
|
|
72
|
+
timeout: float | httpx.Timeout | None = 15.0,
|
|
73
|
+
max_concurrency: int = 10,
|
|
74
|
+
transport: httpx.AsyncBaseTransport | None = None,
|
|
75
|
+
cache: CacheConfig | None = CacheConfig(),
|
|
76
|
+
) -> None:
|
|
77
|
+
"""Configure upstream access; cache=None disables ordinary and reference caching."""
|
|
78
|
+
self._client = TfmktClient(
|
|
79
|
+
base_url=base_url,
|
|
80
|
+
timeout=timeout,
|
|
81
|
+
max_concurrency=max_concurrency,
|
|
82
|
+
transport=transport,
|
|
83
|
+
cache=cache,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
async def __aenter__(self) -> Self:
|
|
87
|
+
"""Enter this client's lifetime without making a request."""
|
|
88
|
+
if self._client.http.is_closed:
|
|
89
|
+
raise RuntimeError("Transfermarkt client is closed")
|
|
90
|
+
return self
|
|
91
|
+
|
|
92
|
+
async def __aexit__(
|
|
93
|
+
self,
|
|
94
|
+
exc_type: type[BaseException] | None,
|
|
95
|
+
exc: BaseException | None,
|
|
96
|
+
traceback: TracebackType | None,
|
|
97
|
+
) -> None:
|
|
98
|
+
"""Close the owned transport, including when the body raises."""
|
|
99
|
+
await self.aclose()
|
|
100
|
+
|
|
101
|
+
async def aclose(self) -> None:
|
|
102
|
+
"""Close the owned HTTP client and transport; safe to call more than once."""
|
|
103
|
+
await self._client.aclose()
|
|
104
|
+
|
|
105
|
+
async def _call(self, model: type[Model], service: Callable[..., Awaitable[dict]], *args: Any) -> Model:
|
|
106
|
+
with fetch_scope():
|
|
107
|
+
data = await service(self._client, *args)
|
|
108
|
+
try:
|
|
109
|
+
return model.model_validate(data)
|
|
110
|
+
except ValidationError as error:
|
|
111
|
+
raise UpstreamError(f"Unexpected upstream payload for {model.__name__}") from error
|
|
112
|
+
|
|
113
|
+
async def countries(self) -> Countries:
|
|
114
|
+
"""List country metadata, including historical entries."""
|
|
115
|
+
return await self._call(Countries, countries.list_countries)
|
|
116
|
+
|
|
117
|
+
async def clubs(self, country_id: int) -> ClubListing:
|
|
118
|
+
"""List the available clubs for a country."""
|
|
119
|
+
_positive_integer(country_id, "country_id")
|
|
120
|
+
return await self._call(ClubListing, club_listing.list_clubs, country_id)
|
|
121
|
+
|
|
122
|
+
async def search_clubs(self, club_name: str, *, page_number: int = 1) -> ClubSearch:
|
|
123
|
+
"""Search clubs, returning one page."""
|
|
124
|
+
_positive_integer(page_number, "page_number")
|
|
125
|
+
return await self._call(ClubSearch, club_search.search_clubs, club_name, page_number)
|
|
126
|
+
|
|
127
|
+
async def club_profile(self, club_id: str) -> ClubProfile:
|
|
128
|
+
"""Get a club's profile."""
|
|
129
|
+
return await self._call(ClubProfile, club_profile.get_club_profile, club_id)
|
|
130
|
+
|
|
131
|
+
async def club_players(self, club_id: str, *, season_id: str | None = None) -> ClubPlayers:
|
|
132
|
+
"""Get the current or historical squad of a club."""
|
|
133
|
+
return await self._call(ClubPlayers, club_players.get_club_players, club_id, season_id)
|
|
134
|
+
|
|
135
|
+
async def club_achievements(self, club_id: str) -> Achievements:
|
|
136
|
+
"""Get a club's titles."""
|
|
137
|
+
return await self._call(Achievements, club_achievements.get_club_achievements, club_id)
|
|
138
|
+
|
|
139
|
+
async def search_players(self, player_name: str, *, page_number: int = 1) -> PlayerSearch:
|
|
140
|
+
"""Search players, returning one page."""
|
|
141
|
+
_positive_integer(page_number, "page_number")
|
|
142
|
+
return await self._call(PlayerSearch, player_search.search_players, player_name, page_number)
|
|
143
|
+
|
|
144
|
+
async def player_profile(self, player_id: str) -> PlayerProfile:
|
|
145
|
+
"""Get a player's profile."""
|
|
146
|
+
return await self._call(PlayerProfile, player_profile.get_player_profile, player_id)
|
|
147
|
+
|
|
148
|
+
async def player_market_value(self, player_id: str) -> PlayerMarketValue:
|
|
149
|
+
"""Get current and historical player valuations."""
|
|
150
|
+
return await self._call(PlayerMarketValue, player_market_value.get_player_market_value, player_id)
|
|
151
|
+
|
|
152
|
+
async def player_transfers(self, player_id: str) -> PlayerTransfers:
|
|
153
|
+
"""Get a player's transfer history."""
|
|
154
|
+
return await self._call(PlayerTransfers, player_transfers.get_player_transfers, player_id)
|
|
155
|
+
|
|
156
|
+
async def player_stats(self, player_id: str) -> PlayerStats:
|
|
157
|
+
"""Get a player's club statistics by season and competition."""
|
|
158
|
+
return await self._call(PlayerStats, player_stats.get_player_stats, player_id)
|
|
159
|
+
|
|
160
|
+
async def player_matches(
|
|
161
|
+
self, player_id: str, *, season_id: str | None = None, page_number: int = 1
|
|
162
|
+
) -> PlayerMatches:
|
|
163
|
+
"""Get one page of past matches, optionally filtered by season."""
|
|
164
|
+
_positive_integer(page_number, "page_number")
|
|
165
|
+
return await self._call(PlayerMatches, player_matches.get_player_matches, player_id, page_number, season_id)
|
|
166
|
+
|
|
167
|
+
async def player_injuries(self, player_id: str, *, page_number: int = 1) -> PlayerInjuries:
|
|
168
|
+
"""Get one page of a player's injuries."""
|
|
169
|
+
_positive_integer(page_number, "page_number")
|
|
170
|
+
return await self._call(PlayerInjuries, player_injuries.get_player_injuries, player_id, page_number)
|
|
171
|
+
|
|
172
|
+
async def player_absences(self, player_id: str, *, page_number: int = 1) -> PlayerAbsences:
|
|
173
|
+
"""Get one page of a player's non-injury absences."""
|
|
174
|
+
_positive_integer(page_number, "page_number")
|
|
175
|
+
return await self._call(PlayerAbsences, player_absences.get_player_absences, player_id, page_number)
|
|
176
|
+
|
|
177
|
+
async def player_achievements(self, player_id: str) -> Achievements:
|
|
178
|
+
"""Get a player's titles and awards."""
|
|
179
|
+
return await self._call(Achievements, player_achievements.get_player_achievements, player_id)
|
|
180
|
+
|
|
181
|
+
async def player_national_career(self, player_id: str) -> PlayerNationalCareer:
|
|
182
|
+
"""Get a player's senior and youth national teams."""
|
|
183
|
+
return await self._call(PlayerNationalCareer, player_national_career.get_player_national_career, player_id)
|
|
184
|
+
|
|
185
|
+
async def search_coaches(self, coach_name: str, *, page_number: int = 1) -> CoachSearch:
|
|
186
|
+
"""Search coaches, returning one page."""
|
|
187
|
+
_positive_integer(page_number, "page_number")
|
|
188
|
+
return await self._call(CoachSearch, coach_search.search_coaches, coach_name, page_number)
|
|
189
|
+
|
|
190
|
+
async def coach_profile(self, coach_id: str) -> CoachProfile:
|
|
191
|
+
"""Get a coach's profile."""
|
|
192
|
+
return await self._call(CoachProfile, coach_profile.get_coach_profile, coach_id)
|
|
193
|
+
|
|
194
|
+
async def search_competitions(self, competition_name: str, *, page_number: int = 1) -> CompetitionSearch:
|
|
195
|
+
"""Search competitions, returning one page."""
|
|
196
|
+
_positive_integer(page_number, "page_number")
|
|
197
|
+
return await self._call(
|
|
198
|
+
CompetitionSearch, competition_search.search_competitions, competition_name, page_number
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
async def competition_clubs(self, competition_id: str, *, season_id: str | None = None) -> CompetitionClubs:
|
|
202
|
+
"""Get the clubs in a competition season."""
|
|
203
|
+
return await self._call(CompetitionClubs, competition_clubs.get_competition_clubs, competition_id, season_id)
|
|
204
|
+
|
|
205
|
+
async def competition_table(self, competition_id: str, *, season_id: str | None = None) -> CompetitionTable:
|
|
206
|
+
"""Get the league or group tables in a competition season."""
|
|
207
|
+
return await self._call(CompetitionTable, competition_table.get_competition_table, competition_id, season_id)
|
|
208
|
+
|
|
209
|
+
async def competition_seasons(self, competition_id: str) -> CompetitionSeasons:
|
|
210
|
+
"""Get the available seasons for a competition."""
|
|
211
|
+
return await self._call(CompetitionSeasons, competition_seasons.get_competition_seasons, competition_id)
|
|
212
|
+
|
|
213
|
+
async def game(self, game_id: str) -> Game:
|
|
214
|
+
"""Get match details, lineups, statistics and events."""
|
|
215
|
+
return await self._call(Game, game.get_game, game_id)
|
|
216
|
+
|
|
217
|
+
async def player_jersey_numbers(self, player_id: str) -> NoReturn:
|
|
218
|
+
"""Raise EndpointUnavailable: upstream provides no jersey-number history."""
|
|
219
|
+
raise EndpointUnavailable(
|
|
220
|
+
"Jersey number history is not available: Transfermarkt's JSON API has no equivalent data."
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def _positive_integer(value: int, name: str) -> None:
|
|
225
|
+
if not isinstance(value, int) or isinstance(value, bool) or value < 1:
|
|
226
|
+
raise ValueError(f"{name} must be a positive integer")
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Failures exposed to SDK consumers, independent of the HTTP server."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class TransfermarktError(Exception):
|
|
5
|
+
"""Base class for failures fetching or assembling Transfermarkt data."""
|
|
6
|
+
|
|
7
|
+
def __init__(self, message: str, *, url: str | None = None, status_code: int | None = None) -> None:
|
|
8
|
+
"""Attach upstream request metadata when available."""
|
|
9
|
+
super().__init__(message)
|
|
10
|
+
self.url = url
|
|
11
|
+
self.status_code = status_code
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class NotFound(TransfermarktError):
|
|
15
|
+
"""The requested entity does not exist upstream."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class UpstreamBlocked(TransfermarktError):
|
|
19
|
+
"""Upstream challenged or refused the request."""
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class UpstreamTimeout(TransfermarktError):
|
|
23
|
+
"""An upstream request timed out."""
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class UpstreamError(TransfermarktError):
|
|
27
|
+
"""An upstream connection, response or assembled model was invalid."""
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class EndpointUnavailable(TransfermarktError):
|
|
31
|
+
"""There is no upstream data source for this endpoint."""
|
|
File without changes
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Public typed response models; nested models also live in their resource modules."""
|
|
2
|
+
|
|
3
|
+
from transfermarkt_api.schemas.achievements import Achievements as Achievements
|
|
4
|
+
from transfermarkt_api.schemas.clubs import ClubPlayers as ClubPlayers
|
|
5
|
+
from transfermarkt_api.schemas.clubs import ClubProfile as ClubProfile
|
|
6
|
+
from transfermarkt_api.schemas.clubs import ClubSearch as ClubSearch
|
|
7
|
+
from transfermarkt_api.schemas.clubs.listing import ClubListing as ClubListing
|
|
8
|
+
from transfermarkt_api.schemas.coaches import CoachProfile as CoachProfile
|
|
9
|
+
from transfermarkt_api.schemas.coaches import CoachSearch as CoachSearch
|
|
10
|
+
from transfermarkt_api.schemas.competitions import CompetitionClubs as CompetitionClubs
|
|
11
|
+
from transfermarkt_api.schemas.competitions import CompetitionSearch as CompetitionSearch
|
|
12
|
+
from transfermarkt_api.schemas.competitions import CompetitionSeasons as CompetitionSeasons
|
|
13
|
+
from transfermarkt_api.schemas.competitions import CompetitionTable as CompetitionTable
|
|
14
|
+
from transfermarkt_api.schemas.countries import Countries as Countries
|
|
15
|
+
from transfermarkt_api.schemas.games import Game as Game
|
|
16
|
+
from transfermarkt_api.schemas.players import PlayerAbsences as PlayerAbsences
|
|
17
|
+
from transfermarkt_api.schemas.players import PlayerInjuries as PlayerInjuries
|
|
18
|
+
from transfermarkt_api.schemas.players import PlayerMarketValue as PlayerMarketValue
|
|
19
|
+
from transfermarkt_api.schemas.players import PlayerMatches as PlayerMatches
|
|
20
|
+
from transfermarkt_api.schemas.players import PlayerNationalCareer as PlayerNationalCareer
|
|
21
|
+
from transfermarkt_api.schemas.players import PlayerProfile as PlayerProfile
|
|
22
|
+
from transfermarkt_api.schemas.players import PlayerSearch as PlayerSearch
|
|
23
|
+
from transfermarkt_api.schemas.players import PlayerStats as PlayerStats
|
|
24
|
+
from transfermarkt_api.schemas.players import PlayerTransfers as PlayerTransfers
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from transfermarkt_api.schemas.base import AuditMixin, TransfermarktBaseModel
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class AchievementEntity(TransfermarktBaseModel):
|
|
7
|
+
id: Optional[str] = None
|
|
8
|
+
name: Optional[str] = None
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class AchievementDetail(TransfermarktBaseModel):
|
|
12
|
+
season: AchievementEntity
|
|
13
|
+
club: Optional[AchievementEntity] = None
|
|
14
|
+
competition: Optional[AchievementEntity] = None
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Achievement(TransfermarktBaseModel):
|
|
18
|
+
title: str
|
|
19
|
+
count: int
|
|
20
|
+
details: list[AchievementDetail]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Achievements(TransfermarktBaseModel, AuditMixin):
|
|
24
|
+
id: str
|
|
25
|
+
achievements: list[Achievement]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
4
|
+
from pydantic.alias_generators import to_camel
|
|
5
|
+
|
|
6
|
+
from transfermarkt_api.tfmkt.freshness import data_fetched_at
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class AuditMixin(BaseModel):
|
|
10
|
+
updated_at: datetime = Field(
|
|
11
|
+
default_factory=data_fetched_at,
|
|
12
|
+
description="When the upstream data in this response was fetched (oldest fetch if served from cache).",
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class TransfermarktBaseModel(BaseModel):
|
|
17
|
+
model_config = ConfigDict(alias_generator=to_camel)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from transfermarkt_api.schemas.base import AuditMixin, TransfermarktBaseModel
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ListedClub(TransfermarktBaseModel):
|
|
7
|
+
id: str
|
|
8
|
+
name: Optional[str] = None
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ClubListing(TransfermarktBaseModel, AuditMixin):
|
|
12
|
+
country_id: int
|
|
13
|
+
country_name: str
|
|
14
|
+
clubs: list[ListedClub]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from datetime import date
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
from pydantic import HttpUrl
|
|
5
|
+
|
|
6
|
+
from transfermarkt_api.schemas.base import AuditMixin, TransfermarktBaseModel
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ClubPlayer(TransfermarktBaseModel):
|
|
10
|
+
id: str
|
|
11
|
+
name: Optional[str] = None
|
|
12
|
+
image_url: Optional[HttpUrl] = None
|
|
13
|
+
position: Optional[str] = None
|
|
14
|
+
shirt_number: Optional[int] = None
|
|
15
|
+
is_captain: Optional[bool] = None
|
|
16
|
+
date_of_birth: Optional[date] = None
|
|
17
|
+
age: Optional[int] = None
|
|
18
|
+
nationality: list[str]
|
|
19
|
+
current_club: Optional[str] = None
|
|
20
|
+
height: Optional[int] = None
|
|
21
|
+
foot: Optional[str] = None
|
|
22
|
+
joined_on: Optional[date] = None
|
|
23
|
+
signed_from: Optional[str] = None
|
|
24
|
+
contract: Optional[date] = None
|
|
25
|
+
market_value: Optional[int] = None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ClubPlayers(TransfermarktBaseModel, AuditMixin):
|
|
29
|
+
id: str
|
|
30
|
+
players: list[ClubPlayer]
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
from datetime import date
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
from transfermarkt_api.schemas.base import AuditMixin, TransfermarktBaseModel
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ClubSquad(TransfermarktBaseModel):
|
|
8
|
+
size: Optional[int] = None
|
|
9
|
+
average_age: Optional[float] = None
|
|
10
|
+
foreigners: Optional[int] = None
|
|
11
|
+
national_team_players: Optional[int] = None
|
|
12
|
+
domestic_players: Optional[int] = None
|
|
13
|
+
average_market_value: Optional[int] = None
|
|
14
|
+
acquisition_value: Optional[int] = None
|
|
15
|
+
top_18_players_market_value: Optional[int] = None
|
|
16
|
+
top_18_share_percentage: Optional[float] = None
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ClubCoach(TransfermarktBaseModel):
|
|
20
|
+
id: str
|
|
21
|
+
name: Optional[str] = None
|
|
22
|
+
since: Optional[date] = None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ClubLeague(TransfermarktBaseModel):
|
|
26
|
+
id: Optional[str] = None
|
|
27
|
+
name: Optional[str] = None
|
|
28
|
+
country_id: Optional[str] = None
|
|
29
|
+
country_name: Optional[str] = None
|
|
30
|
+
tier: Optional[str] = None
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class ClubHistoricalName(TransfermarktBaseModel):
|
|
34
|
+
name: Optional[str] = None
|
|
35
|
+
short_name: Optional[str] = None
|
|
36
|
+
abbreviation: Optional[str] = None
|
|
37
|
+
season_id: Optional[str] = None
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class ClubStadium(TransfermarktBaseModel):
|
|
41
|
+
id: Optional[str] = None
|
|
42
|
+
name: Optional[str] = None
|
|
43
|
+
capacity: Optional[int] = None
|
|
44
|
+
international_capacity: Optional[int] = None
|
|
45
|
+
website: Optional[str] = None
|
|
46
|
+
address_line_1: Optional[str] = None
|
|
47
|
+
address_line_2: Optional[str] = None
|
|
48
|
+
country_id: Optional[str] = None
|
|
49
|
+
country_name: Optional[str] = None
|
|
50
|
+
latitude: Optional[float] = None
|
|
51
|
+
longitude: Optional[float] = None
|
|
52
|
+
build_year: Optional[int] = None
|
|
53
|
+
renovation_year: Optional[int] = None
|
|
54
|
+
field_length: Optional[int] = None
|
|
55
|
+
field_width: Optional[int] = None
|
|
56
|
+
field_surface: Optional[str] = None
|
|
57
|
+
images: list[str] = []
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class ClubProfile(TransfermarktBaseModel, AuditMixin):
|
|
61
|
+
id: str
|
|
62
|
+
url: Optional[str] = None
|
|
63
|
+
name: str
|
|
64
|
+
official_name: Optional[str] = None
|
|
65
|
+
short_name: Optional[str] = None
|
|
66
|
+
abbreviation: Optional[str] = None
|
|
67
|
+
club_code: Optional[str] = None
|
|
68
|
+
image: Optional[str] = None
|
|
69
|
+
address_line_1: Optional[str] = None
|
|
70
|
+
address_line_2: Optional[str] = None
|
|
71
|
+
address_line_3: Optional[str] = None
|
|
72
|
+
colors: list[str] = []
|
|
73
|
+
stadium_name: Optional[str] = None
|
|
74
|
+
stadium_seats: Optional[int] = None
|
|
75
|
+
stadium: Optional[ClubStadium] = None
|
|
76
|
+
current_market_value: Optional[int] = None
|
|
77
|
+
confederation: Optional[str] = None
|
|
78
|
+
coach: Optional[ClubCoach] = None
|
|
79
|
+
squad: ClubSquad
|
|
80
|
+
league: ClubLeague
|
|
81
|
+
historical_crests: list[str] = []
|
|
82
|
+
historical_names: list[ClubHistoricalName] = []
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from transfermarkt_api.schemas.base import AuditMixin, TransfermarktBaseModel
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ClubSearchResult(TransfermarktBaseModel):
|
|
7
|
+
id: str
|
|
8
|
+
url: Optional[str] = None
|
|
9
|
+
name: Optional[str] = None
|
|
10
|
+
country: Optional[str] = None
|
|
11
|
+
squad: Optional[int] = None
|
|
12
|
+
market_value: Optional[int] = None
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ClubSearch(TransfermarktBaseModel, AuditMixin):
|
|
16
|
+
query: str
|
|
17
|
+
page_number: int
|
|
18
|
+
last_page_number: int
|
|
19
|
+
results: list[ClubSearchResult]
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from datetime import date
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
from pydantic import HttpUrl
|
|
5
|
+
|
|
6
|
+
from transfermarkt_api.schemas.base import AuditMixin, TransfermarktBaseModel
|
|
7
|
+
from transfermarkt_api.schemas.players.profile import PlayerPlaceOfBirth
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Coach(TransfermarktBaseModel):
|
|
11
|
+
id: str
|
|
12
|
+
url: Optional[HttpUrl] = None
|
|
13
|
+
name: Optional[str] = None
|
|
14
|
+
name_in_home_country: Optional[str] = None
|
|
15
|
+
image_url: Optional[HttpUrl] = None
|
|
16
|
+
date_of_birth: Optional[date] = None
|
|
17
|
+
age: Optional[int] = None
|
|
18
|
+
place_of_birth: PlayerPlaceOfBirth
|
|
19
|
+
citizenship: list[str]
|
|
20
|
+
license: Optional[str] = None
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class CoachProfile(Coach, AuditMixin):
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class CoachSearch(TransfermarktBaseModel, AuditMixin):
|
|
28
|
+
query: str
|
|
29
|
+
page_number: int
|
|
30
|
+
last_page_number: int
|
|
31
|
+
results: list[Coach]
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
from transfermarkt_api.schemas.competitions.clubs import CompetitionClubs as CompetitionClubs
|
|
2
|
+
from transfermarkt_api.schemas.competitions.search import CompetitionSearch as CompetitionSearch
|
|
3
|
+
from transfermarkt_api.schemas.competitions.seasons import CompetitionSeasons as CompetitionSeasons
|
|
4
|
+
from transfermarkt_api.schemas.competitions.table import CompetitionTable as CompetitionTable
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from transfermarkt_api.schemas.base import AuditMixin, TransfermarktBaseModel
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class CompetitionClub(TransfermarktBaseModel):
|
|
7
|
+
id: str
|
|
8
|
+
name: Optional[str] = None
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CompetitionClubs(TransfermarktBaseModel, AuditMixin):
|
|
12
|
+
id: str
|
|
13
|
+
name: str
|
|
14
|
+
season_id: str
|
|
15
|
+
clubs: list[CompetitionClub]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from transfermarkt_api.schemas.base import AuditMixin, TransfermarktBaseModel
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class CompetitionSearchResult(TransfermarktBaseModel):
|
|
7
|
+
id: str
|
|
8
|
+
name: Optional[str] = None
|
|
9
|
+
country: Optional[str] = None
|
|
10
|
+
clubs: Optional[int] = None
|
|
11
|
+
players: Optional[int] = None
|
|
12
|
+
total_market_value: Optional[int] = None
|
|
13
|
+
mean_market_value: Optional[int] = None
|
|
14
|
+
continent: Optional[str] = None
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class CompetitionSearch(TransfermarktBaseModel, AuditMixin):
|
|
18
|
+
query: str
|
|
19
|
+
page_number: int
|
|
20
|
+
last_page_number: int
|
|
21
|
+
results: list[CompetitionSearchResult]
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from transfermarkt_api.schemas.base import AuditMixin, TransfermarktBaseModel
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Season(TransfermarktBaseModel):
|
|
7
|
+
id: str
|
|
8
|
+
name: Optional[str] = None
|
|
9
|
+
match_days: Optional[int] = None
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class CompetitionSeasons(TransfermarktBaseModel, AuditMixin):
|
|
13
|
+
id: str
|
|
14
|
+
name: Optional[str] = None
|
|
15
|
+
seasons: list[Season]
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from transfermarkt_api.schemas.base import AuditMixin, TransfermarktBaseModel
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class TableRow(TransfermarktBaseModel):
|
|
7
|
+
position: Optional[int] = None
|
|
8
|
+
previous_position: Optional[int] = None
|
|
9
|
+
club_id: str
|
|
10
|
+
club_name: Optional[str] = None
|
|
11
|
+
matches: Optional[int] = None
|
|
12
|
+
wins: Optional[int] = None
|
|
13
|
+
draws: Optional[int] = None
|
|
14
|
+
losses: Optional[int] = None
|
|
15
|
+
goals_for: Optional[int] = None
|
|
16
|
+
goals_against: Optional[int] = None
|
|
17
|
+
goal_difference: Optional[int] = None
|
|
18
|
+
points: Optional[int] = None
|
|
19
|
+
zone: Optional[str] = None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Table(TransfermarktBaseModel):
|
|
23
|
+
name: Optional[str] = None
|
|
24
|
+
rows: list[TableRow]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class CompetitionTable(TransfermarktBaseModel, AuditMixin):
|
|
28
|
+
id: str
|
|
29
|
+
name: Optional[str] = None
|
|
30
|
+
season_id: str
|
|
31
|
+
tables: list[Table]
|