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.
Files changed (71) hide show
  1. transfermarkt_api/__init__.py +23 -0
  2. transfermarkt_api/_version.py +3 -0
  3. transfermarkt_api/cache.py +20 -0
  4. transfermarkt_api/client.py +226 -0
  5. transfermarkt_api/exceptions.py +31 -0
  6. transfermarkt_api/py.typed +0 -0
  7. transfermarkt_api/schemas/__init__.py +24 -0
  8. transfermarkt_api/schemas/achievements.py +25 -0
  9. transfermarkt_api/schemas/base.py +17 -0
  10. transfermarkt_api/schemas/clubs/__init__.py +3 -0
  11. transfermarkt_api/schemas/clubs/listing.py +14 -0
  12. transfermarkt_api/schemas/clubs/players.py +30 -0
  13. transfermarkt_api/schemas/clubs/profile.py +82 -0
  14. transfermarkt_api/schemas/clubs/search.py +19 -0
  15. transfermarkt_api/schemas/coaches/__init__.py +31 -0
  16. transfermarkt_api/schemas/competitions/__init__.py +4 -0
  17. transfermarkt_api/schemas/competitions/clubs.py +15 -0
  18. transfermarkt_api/schemas/competitions/search.py +21 -0
  19. transfermarkt_api/schemas/competitions/seasons.py +15 -0
  20. transfermarkt_api/schemas/competitions/table.py +31 -0
  21. transfermarkt_api/schemas/countries.py +16 -0
  22. transfermarkt_api/schemas/games/__init__.py +83 -0
  23. transfermarkt_api/schemas/players/__init__.py +9 -0
  24. transfermarkt_api/schemas/players/absences.py +22 -0
  25. transfermarkt_api/schemas/players/injuries.py +20 -0
  26. transfermarkt_api/schemas/players/market_value.py +19 -0
  27. transfermarkt_api/schemas/players/matches.py +56 -0
  28. transfermarkt_api/schemas/players/national_career.py +21 -0
  29. transfermarkt_api/schemas/players/profile.py +81 -0
  30. transfermarkt_api/schemas/players/search.py +25 -0
  31. transfermarkt_api/schemas/players/stats.py +24 -0
  32. transfermarkt_api/schemas/players/transfers.py +38 -0
  33. transfermarkt_api/services/__init__.py +0 -0
  34. transfermarkt_api/services/achievements.py +50 -0
  35. transfermarkt_api/services/clubs/__init__.py +0 -0
  36. transfermarkt_api/services/clubs/achievements.py +10 -0
  37. transfermarkt_api/services/clubs/listing.py +22 -0
  38. transfermarkt_api/services/clubs/players.py +88 -0
  39. transfermarkt_api/services/clubs/profile.py +114 -0
  40. transfermarkt_api/services/clubs/search.py +33 -0
  41. transfermarkt_api/services/coaches/__init__.py +0 -0
  42. transfermarkt_api/services/coaches/profile.py +29 -0
  43. transfermarkt_api/services/coaches/search.py +18 -0
  44. transfermarkt_api/services/competitions/__init__.py +0 -0
  45. transfermarkt_api/services/competitions/clubs.py +30 -0
  46. transfermarkt_api/services/competitions/search.py +61 -0
  47. transfermarkt_api/services/competitions/seasons.py +28 -0
  48. transfermarkt_api/services/competitions/table.py +61 -0
  49. transfermarkt_api/services/countries.py +12 -0
  50. transfermarkt_api/services/games/__init__.py +0 -0
  51. transfermarkt_api/services/games/game.py +136 -0
  52. transfermarkt_api/services/players/__init__.py +0 -0
  53. transfermarkt_api/services/players/absences.py +33 -0
  54. transfermarkt_api/services/players/achievements.py +10 -0
  55. transfermarkt_api/services/players/injuries.py +42 -0
  56. transfermarkt_api/services/players/market_value.py +35 -0
  57. transfermarkt_api/services/players/matches.py +107 -0
  58. transfermarkt_api/services/players/national_career.py +31 -0
  59. transfermarkt_api/services/players/profile.py +122 -0
  60. transfermarkt_api/services/players/search.py +44 -0
  61. transfermarkt_api/services/players/stats.py +74 -0
  62. transfermarkt_api/services/players/transfers.py +89 -0
  63. transfermarkt_api/tfmkt/__init__.py +1 -0
  64. transfermarkt_api/tfmkt/client.py +249 -0
  65. transfermarkt_api/tfmkt/concurrency.py +17 -0
  66. transfermarkt_api/tfmkt/freshness.py +38 -0
  67. transfermarkt_api/tfmkt/reference.py +116 -0
  68. transfermarkt_api-5.0.0.dist-info/METADATA +234 -0
  69. transfermarkt_api-5.0.0.dist-info/RECORD +71 -0
  70. transfermarkt_api-5.0.0.dist-info/WHEEL +4 -0
  71. 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,3 @@
1
+ """Release version shared by the SDK and server."""
2
+
3
+ __version__ = "5.0.0"
@@ -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,3 @@
1
+ from transfermarkt_api.schemas.clubs.players import ClubPlayers as ClubPlayers
2
+ from transfermarkt_api.schemas.clubs.profile import ClubProfile as ClubProfile
3
+ from transfermarkt_api.schemas.clubs.search import ClubSearch as ClubSearch
@@ -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]