datafc 2.7.2__tar.gz → 2.9.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 (97) hide show
  1. datafc-2.9.0/PKG-INFO +193 -0
  2. datafc-2.9.0/README.md +166 -0
  3. {datafc-2.7.2 → datafc-2.9.0}/datafc/__init__.py +5 -1
  4. {datafc-2.7.2 → datafc-2.9.0}/datafc/sofascore/__init__.py +2 -0
  5. {datafc-2.7.2 → datafc-2.9.0}/datafc/sofascore/_core.py +42 -0
  6. {datafc-2.7.2 → datafc-2.9.0}/datafc/sofascore/aio.py +51 -0
  7. datafc-2.9.0/datafc/sofascore/fetch_rating_breakdown_data.py +66 -0
  8. datafc-2.9.0/datafc/tff/__init__.py +40 -0
  9. datafc-2.9.0/datafc/tff/_client.py +396 -0
  10. datafc-2.9.0/datafc/tff/_parsers.py +1134 -0
  11. datafc-2.9.0/datafc/tff/fetch_fixtures_data.py +100 -0
  12. datafc-2.9.0/datafc/tff/fetch_match_cards_data.py +30 -0
  13. datafc-2.9.0/datafc/tff/fetch_match_details_data.py +28 -0
  14. datafc-2.9.0/datafc/tff/fetch_match_goals_data.py +31 -0
  15. datafc-2.9.0/datafc/tff/fetch_match_lineups_data.py +31 -0
  16. datafc-2.9.0/datafc/tff/fetch_match_staff_data.py +28 -0
  17. datafc-2.9.0/datafc/tff/fetch_player_cards_data.py +48 -0
  18. datafc-2.9.0/datafc/tff/fetch_player_career_data.py +33 -0
  19. datafc-2.9.0/datafc/tff/fetch_player_data.py +33 -0
  20. datafc-2.9.0/datafc/tff/fetch_player_goals_data.py +45 -0
  21. datafc-2.9.0/datafc/tff/fetch_player_matches_data.py +49 -0
  22. datafc-2.9.0/datafc/tff/fetch_player_national_matches_data.py +51 -0
  23. datafc-2.9.0/datafc/tff/fetch_standings_data.py +60 -0
  24. datafc-2.9.0/datafc/tff/fetch_team_data.py +28 -0
  25. {datafc-2.7.2 → datafc-2.9.0}/datafc/utils/_config.py +25 -0
  26. datafc-2.9.0/datafc.egg-info/PKG-INFO +193 -0
  27. {datafc-2.7.2 → datafc-2.9.0}/datafc.egg-info/SOURCES.txt +18 -0
  28. {datafc-2.7.2 → datafc-2.9.0}/datafc.egg-info/requires.txt +1 -0
  29. {datafc-2.7.2 → datafc-2.9.0}/pyproject.toml +2 -1
  30. datafc-2.7.2/PKG-INFO +0 -1186
  31. datafc-2.7.2/README.md +0 -1160
  32. datafc-2.7.2/datafc.egg-info/PKG-INFO +0 -1186
  33. {datafc-2.7.2 → datafc-2.9.0}/LICENSE +0 -0
  34. {datafc-2.7.2 → datafc-2.9.0}/datafc/clubelo/__init__.py +0 -0
  35. {datafc-2.7.2 → datafc-2.9.0}/datafc/clubelo/_client.py +0 -0
  36. {datafc-2.7.2 → datafc-2.9.0}/datafc/clubelo/_parsers.py +0 -0
  37. {datafc-2.7.2 → datafc-2.9.0}/datafc/clubelo/fetch_club_history_data.py +0 -0
  38. {datafc-2.7.2 → datafc-2.9.0}/datafc/clubelo/fetch_daily_ranking_data.py +0 -0
  39. {datafc-2.7.2 → datafc-2.9.0}/datafc/clubelo/fetch_fixtures_data.py +0 -0
  40. {datafc-2.7.2 → datafc-2.9.0}/datafc/eloratings/__init__.py +0 -0
  41. {datafc-2.7.2 → datafc-2.9.0}/datafc/eloratings/_client.py +0 -0
  42. {datafc-2.7.2 → datafc-2.9.0}/datafc/eloratings/_parsers.py +0 -0
  43. {datafc-2.7.2 → datafc-2.9.0}/datafc/eloratings/fetch_country_codes_data.py +0 -0
  44. {datafc-2.7.2 → datafc-2.9.0}/datafc/eloratings/fetch_country_matches_data.py +0 -0
  45. {datafc-2.7.2 → datafc-2.9.0}/datafc/eloratings/fetch_teams_data.py +0 -0
  46. {datafc-2.7.2 → datafc-2.9.0}/datafc/eloratings/fetch_tournament_codes_data.py +0 -0
  47. {datafc-2.7.2 → datafc-2.9.0}/datafc/eloratings/fetch_tournament_editions_data.py +0 -0
  48. {datafc-2.7.2 → datafc-2.9.0}/datafc/eloratings/fetch_tournament_groups_data.py +0 -0
  49. {datafc-2.7.2 → datafc-2.9.0}/datafc/eloratings/fetch_world_ranking_data.py +0 -0
  50. {datafc-2.7.2 → datafc-2.9.0}/datafc/exceptions.py +0 -0
  51. {datafc-2.7.2 → datafc-2.9.0}/datafc/sofascore/_parsers.py +0 -0
  52. {datafc-2.7.2 → datafc-2.9.0}/datafc/sofascore/fetch_average_positions_data.py +0 -0
  53. {datafc-2.7.2 → datafc-2.9.0}/datafc/sofascore/fetch_coordinates_data.py +0 -0
  54. {datafc-2.7.2 → datafc-2.9.0}/datafc/sofascore/fetch_formations_data.py +0 -0
  55. {datafc-2.7.2 → datafc-2.9.0}/datafc/sofascore/fetch_goal_networks_data.py +0 -0
  56. {datafc-2.7.2 → datafc-2.9.0}/datafc/sofascore/fetch_incidents_data.py +0 -0
  57. {datafc-2.7.2 → datafc-2.9.0}/datafc/sofascore/fetch_league_player_stats_data.py +0 -0
  58. {datafc-2.7.2 → datafc-2.9.0}/datafc/sofascore/fetch_lineups_data.py +0 -0
  59. {datafc-2.7.2 → datafc-2.9.0}/datafc/sofascore/fetch_match_data.py +0 -0
  60. {datafc-2.7.2 → datafc-2.9.0}/datafc/sofascore/fetch_match_details_data.py +0 -0
  61. {datafc-2.7.2 → datafc-2.9.0}/datafc/sofascore/fetch_match_h2h_data.py +0 -0
  62. {datafc-2.7.2 → datafc-2.9.0}/datafc/sofascore/fetch_match_odds_data.py +0 -0
  63. {datafc-2.7.2 → datafc-2.9.0}/datafc/sofascore/fetch_match_stats_data.py +0 -0
  64. {datafc-2.7.2 → datafc-2.9.0}/datafc/sofascore/fetch_momentum_data.py +0 -0
  65. {datafc-2.7.2 → datafc-2.9.0}/datafc/sofascore/fetch_past_matches_data.py +0 -0
  66. {datafc-2.7.2 → datafc-2.9.0}/datafc/sofascore/fetch_player_attribute_overviews_data.py +0 -0
  67. {datafc-2.7.2 → datafc-2.9.0}/datafc/sofascore/fetch_player_career_stats_data.py +0 -0
  68. {datafc-2.7.2 → datafc-2.9.0}/datafc/sofascore/fetch_player_data.py +0 -0
  69. {datafc-2.7.2 → datafc-2.9.0}/datafc/sofascore/fetch_player_match_log_data.py +0 -0
  70. {datafc-2.7.2 → datafc-2.9.0}/datafc/sofascore/fetch_player_national_team_data.py +0 -0
  71. {datafc-2.7.2 → datafc-2.9.0}/datafc/sofascore/fetch_player_stats_data.py +0 -0
  72. {datafc-2.7.2 → datafc-2.9.0}/datafc/sofascore/fetch_player_transfers_data.py +0 -0
  73. {datafc-2.7.2 → datafc-2.9.0}/datafc/sofascore/fetch_pregame_form_data.py +0 -0
  74. {datafc-2.7.2 → datafc-2.9.0}/datafc/sofascore/fetch_referee_stats_data.py +0 -0
  75. {datafc-2.7.2 → datafc-2.9.0}/datafc/sofascore/fetch_search_data.py +0 -0
  76. {datafc-2.7.2 → datafc-2.9.0}/datafc/sofascore/fetch_season_rounds_data.py +0 -0
  77. {datafc-2.7.2 → datafc-2.9.0}/datafc/sofascore/fetch_seasons_data.py +0 -0
  78. {datafc-2.7.2 → datafc-2.9.0}/datafc/sofascore/fetch_shots_data.py +0 -0
  79. {datafc-2.7.2 → datafc-2.9.0}/datafc/sofascore/fetch_squad_data.py +0 -0
  80. {datafc-2.7.2 → datafc-2.9.0}/datafc/sofascore/fetch_standings_data.py +0 -0
  81. {datafc-2.7.2 → datafc-2.9.0}/datafc/sofascore/fetch_substitutions_data.py +0 -0
  82. {datafc-2.7.2 → datafc-2.9.0}/datafc/sofascore/fetch_team_data.py +0 -0
  83. {datafc-2.7.2 → datafc-2.9.0}/datafc/sofascore/fetch_team_match_history_data.py +0 -0
  84. {datafc-2.7.2 → datafc-2.9.0}/datafc/sofascore/fetch_team_stats_data.py +0 -0
  85. {datafc-2.7.2 → datafc-2.9.0}/datafc/sofascore/fetch_team_transfers_data.py +0 -0
  86. {datafc-2.7.2 → datafc-2.9.0}/datafc/sofascore/fetch_upcoming_matches_data.py +0 -0
  87. {datafc-2.7.2 → datafc-2.9.0}/datafc/utils/__init__.py +0 -0
  88. {datafc-2.7.2 → datafc-2.9.0}/datafc/utils/_async_client.py +0 -0
  89. {datafc-2.7.2 → datafc-2.9.0}/datafc/utils/_cache.py +0 -0
  90. {datafc-2.7.2 → datafc-2.9.0}/datafc/utils/_client.py +0 -0
  91. {datafc-2.7.2 → datafc-2.9.0}/datafc/utils/_helpers.py +0 -0
  92. {datafc-2.7.2 → datafc-2.9.0}/datafc/utils/_save_files.py +0 -0
  93. {datafc-2.7.2 → datafc-2.9.0}/datafc/utils/_tournament_info.py +0 -0
  94. {datafc-2.7.2 → datafc-2.9.0}/datafc/utils/_validate.py +0 -0
  95. {datafc-2.7.2 → datafc-2.9.0}/datafc.egg-info/dependency_links.txt +0 -0
  96. {datafc-2.7.2 → datafc-2.9.0}/datafc.egg-info/top_level.txt +0 -0
  97. {datafc-2.7.2 → datafc-2.9.0}/setup.cfg +0 -0
datafc-2.9.0/PKG-INFO ADDED
@@ -0,0 +1,193 @@
1
+ Metadata-Version: 2.4
2
+ Name: datafc
3
+ Version: 2.9.0
4
+ Summary: Fetch, process, and export structured football data.
5
+ Author-email: Uraz Akgül <urazdev@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/urazakgul/datafc
8
+ Project-URL: Repository, https://github.com/urazakgul/datafc
9
+ Project-URL: Issues, https://github.com/urazakgul/datafc/issues
10
+ Keywords: football,soccer,data,analytics,sofascore,clubelo,eloratings,elo
11
+ Requires-Python: >=3.8
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: pandas>=1.5
15
+ Requires-Dist: curl_cffi>=0.7
16
+ Requires-Dist: openpyxl>=3.0
17
+ Requires-Dist: beautifulsoup4>=4.11
18
+ Provides-Extra: parquet
19
+ Requires-Dist: pyarrow>=12.0; extra == "parquet"
20
+ Provides-Extra: dev
21
+ Requires-Dist: ruff>=0.4; extra == "dev"
22
+ Requires-Dist: mypy>=1.0; extra == "dev"
23
+ Requires-Dist: pandas-stubs>=2.0; extra == "dev"
24
+ Requires-Dist: pytest>=8.0; extra == "dev"
25
+ Requires-Dist: pytest-mock>=3.12; extra == "dev"
26
+ Dynamic: license-file
27
+
28
+ # datafc v2.9.0
29
+
30
+ Football data from **Sofascore**, **ClubElo**, **eloratings.net** (Elo since 1872) and **tff.org** (Turkish federation) as pandas DataFrames. 60 functions total, each with an async mirror under `datafc.aio`, optional `DiskCache`.
31
+
32
+ ```bash
33
+ pip install datafc
34
+ ```
35
+
36
+ ---
37
+
38
+ ## Sofascore — `from datafc import ...`
39
+
40
+ Grouped by the argument each function needs. Chain them: id → root frame → derived frame.
41
+
42
+ ### Discovery
43
+ | Function | Purpose |
44
+ | --- | --- |
45
+ | `search_data(query, entity_type=None)` | search teams, players, tournaments, managers |
46
+ | `seasons_data(tournament_id)` | every season of a tournament |
47
+ | `season_rounds_data(tournament_id, season_id)` | defined matchweeks |
48
+
49
+ ### Root (id-driven)
50
+ | Function | Purpose |
51
+ | --- | --- |
52
+ | `standings_data(tournament_id, season_id)` | Total / Home / Away table |
53
+ | `match_data(tournament_id, season_id, week_number=None, tournament_type=None, tournament_stage=None)` | fixtures + scores |
54
+ | `past_matches_data(...)` | same signature; H2H history per pair |
55
+ | `team_match_history_data(team_id)` | full match history for one team |
56
+ | `league_player_stats_data(tournament_id, season_id, order='-rating', accumulation='total', fields=None, position=None, max_players=100)` | wide-format player leaderboard |
57
+ | `referee_stats_data(referee_id)` | per-tournament career stats |
58
+
59
+ `tournament_type` ∈ `{"uefa", "world_cup", "league_playoff"}` with matching `tournament_stage` (`group_stage_week`, `round_of_16`, `quarterfinals`, `semifinals`, `final`, …).
60
+
61
+ ### From `standings_df`
62
+ | Function | Purpose |
63
+ | --- | --- |
64
+ | `team_data(standings_df)` | profile, kits, manager, venue |
65
+ | `team_stats_data(standings_df, tournament_id, season_id, season=None)` | season team stats (long) |
66
+ | `team_transfers_data(standings_df)` | team transfer log |
67
+ | `player_stats_data(standings_df, tournament_id, season_id)` | top players per team |
68
+ | `squad_data(standings_df)` | full roster |
69
+ | `upcoming_matches_data(standings_df)` | upcoming fixtures |
70
+
71
+ ### From `match_df`
72
+ | Function | Purpose |
73
+ | --- | --- |
74
+ | `match_details_data` | referee + venue |
75
+ | `formations_data` | formation strings |
76
+ | `match_stats_data` | team stats per match |
77
+ | `match_odds_data` | 1/X/2 odds |
78
+ | `match_h2h_data` | head-to-head record |
79
+ | `momentum_data` | minute-by-minute momentum |
80
+ | `pregame_form_data` | last-5 form, rating, position, market value |
81
+ | `shots_data` | every shot (xG, xGOT, body part, situation) |
82
+ | `lineups_data` | XI + subs + per-match player stats |
83
+ | `substitutions_data` | substitutions |
84
+ | `incidents_data` | goals, cards, VAR |
85
+ | `average_positions_data` | average pitch position (X/Y) |
86
+ | `goal_networks_data` | coordinates for every action in a goal sequence |
87
+
88
+ ### From `lineups_df`
89
+ | Function | Purpose |
90
+ | --- | --- |
91
+ | `coordinates_data(lineups_df)` | touch heatmap per player |
92
+ | `rating_breakdown_data(lineups_df)` | every pass / dribble / defensive action / ball-carry with start-end coordinates and outcome flags |
93
+
94
+ ### From `squad_df`
95
+ | Function | Purpose |
96
+ | --- | --- |
97
+ | `player_data` | profile |
98
+ | `player_attribute_overviews_data` | radar attributes + baselines |
99
+ | `player_transfers_data` | transfer history |
100
+ | `player_career_stats_data` | season-by-season career stats |
101
+ | `player_national_team_data` | international career |
102
+ | `player_match_log_data` | match-by-match in-game stats (wide) |
103
+
104
+ ---
105
+
106
+ ## ClubElo — `from datafc import clubelo`
107
+
108
+ Club names use ClubElo spelling (`Man City`, `Real Madrid`, `Paris SG`).
109
+
110
+ | Function | Purpose |
111
+ | --- | --- |
112
+ | `daily_ranking_data(date)` | full ranking on a calendar day (`"YYYY-MM-DD"` or `date`) |
113
+ | `club_history_data(club)` | Elo history for one club (pre-1960 values are provisional) |
114
+ | `fixtures_data()` | probability grid for upcoming matches |
115
+
116
+ ---
117
+
118
+ ## EloRatings — `from datafc import eloratings`
119
+
120
+ Country arguments are page slugs (`Czech_Republic`, `United_States`).
121
+
122
+ | Function | Purpose |
123
+ | --- | --- |
124
+ | `world_ranking_data()` | current national-team ranking |
125
+ | `country_matches_data(country)` | full international match history |
126
+ | `country_codes_data()` | code → country |
127
+ | `tournament_codes_data()` | code → tournament |
128
+ | `tournament_editions_data()` | edition list with dates + slugs |
129
+ | `tournament_groups_data()` | subcode → group code (e.g. WC qualifiers → `WQT`) |
130
+ | `teams_data()` | legacy country code → ISO |
131
+
132
+ ---
133
+
134
+ ## TFF — `from datafc import tff`
135
+
136
+ Süper Lig, 1. Lig, 2. Lig, 3. Lig.
137
+
138
+ ### Discovery / league
139
+ | Function | Purpose |
140
+ | --- | --- |
141
+ | `fixtures_data(page_id, week=None, group=None, playoff=False, round=None)` | weekly fixtures or play-off bracket |
142
+ | `standings_data(page_id, week, group=None)` | league table (`group` required for 2. Lig / 3. Lig) |
143
+ | `team_data(club_id)` | club profile |
144
+
145
+ ### From `match_id`
146
+ | Function | Purpose |
147
+ | --- | --- |
148
+ | `match_details_data` | teams, score, officials |
149
+ | `match_lineups_data` | XI + bench |
150
+ | `match_staff_data` | head coaches |
151
+ | `match_goals_data` | goals with type (`F`/`H`/`P`/`K`/`KG`) |
152
+ | `match_cards_data` | bookings (yellow / red / second_yellow) |
153
+
154
+ ### From `player_id`
155
+ | Function | Purpose |
156
+ | --- | --- |
157
+ | `player_data` | profile (licence, current club) |
158
+ | `player_career_data` | licence history |
159
+ | `player_national_matches_data` | national-team appearances |
160
+ | `player_goals_data` | every recorded goal |
161
+ | `player_matches_data(player_id, season)` | club matches in a season (`"2025-2026"`) |
162
+ | `player_cards_data(player_id, season)` | bookings in a season |
163
+
164
+ ---
165
+
166
+ ## Async
167
+
168
+ Every function has a mirror in `datafc.aio` with the same signature:
169
+
170
+ ```python
171
+ import asyncio
172
+ from datafc import aio
173
+
174
+ async def main():
175
+ tasks = [aio.match_data(52, 63814, week_number=w) for w in range(1, 39)]
176
+ return await asyncio.gather(*tasks, return_exceptions=True)
177
+
178
+ asyncio.run(main())
179
+ ```
180
+
181
+ ## Cache
182
+
183
+ ```python
184
+ from datafc import DiskCache
185
+
186
+ cache = DiskCache(cache_dir=".datafc_cache", ttl_hours=24)
187
+ ```
188
+
189
+ Pass `cache=cache` to any function. `ttl_hours=0` disables expiry; `cache.clear()` empties it.
190
+
191
+ ## Exceptions
192
+
193
+ `DataFCError` (base), `InvalidParameterError`, `APIError`, `RateLimitError`, `ServerError`, `DataNotAvailableError`.
datafc-2.9.0/README.md ADDED
@@ -0,0 +1,166 @@
1
+ # datafc v2.9.0
2
+
3
+ Football data from **Sofascore**, **ClubElo**, **eloratings.net** (Elo since 1872) and **tff.org** (Turkish federation) as pandas DataFrames. 60 functions total, each with an async mirror under `datafc.aio`, optional `DiskCache`.
4
+
5
+ ```bash
6
+ pip install datafc
7
+ ```
8
+
9
+ ---
10
+
11
+ ## Sofascore — `from datafc import ...`
12
+
13
+ Grouped by the argument each function needs. Chain them: id → root frame → derived frame.
14
+
15
+ ### Discovery
16
+ | Function | Purpose |
17
+ | --- | --- |
18
+ | `search_data(query, entity_type=None)` | search teams, players, tournaments, managers |
19
+ | `seasons_data(tournament_id)` | every season of a tournament |
20
+ | `season_rounds_data(tournament_id, season_id)` | defined matchweeks |
21
+
22
+ ### Root (id-driven)
23
+ | Function | Purpose |
24
+ | --- | --- |
25
+ | `standings_data(tournament_id, season_id)` | Total / Home / Away table |
26
+ | `match_data(tournament_id, season_id, week_number=None, tournament_type=None, tournament_stage=None)` | fixtures + scores |
27
+ | `past_matches_data(...)` | same signature; H2H history per pair |
28
+ | `team_match_history_data(team_id)` | full match history for one team |
29
+ | `league_player_stats_data(tournament_id, season_id, order='-rating', accumulation='total', fields=None, position=None, max_players=100)` | wide-format player leaderboard |
30
+ | `referee_stats_data(referee_id)` | per-tournament career stats |
31
+
32
+ `tournament_type` ∈ `{"uefa", "world_cup", "league_playoff"}` with matching `tournament_stage` (`group_stage_week`, `round_of_16`, `quarterfinals`, `semifinals`, `final`, …).
33
+
34
+ ### From `standings_df`
35
+ | Function | Purpose |
36
+ | --- | --- |
37
+ | `team_data(standings_df)` | profile, kits, manager, venue |
38
+ | `team_stats_data(standings_df, tournament_id, season_id, season=None)` | season team stats (long) |
39
+ | `team_transfers_data(standings_df)` | team transfer log |
40
+ | `player_stats_data(standings_df, tournament_id, season_id)` | top players per team |
41
+ | `squad_data(standings_df)` | full roster |
42
+ | `upcoming_matches_data(standings_df)` | upcoming fixtures |
43
+
44
+ ### From `match_df`
45
+ | Function | Purpose |
46
+ | --- | --- |
47
+ | `match_details_data` | referee + venue |
48
+ | `formations_data` | formation strings |
49
+ | `match_stats_data` | team stats per match |
50
+ | `match_odds_data` | 1/X/2 odds |
51
+ | `match_h2h_data` | head-to-head record |
52
+ | `momentum_data` | minute-by-minute momentum |
53
+ | `pregame_form_data` | last-5 form, rating, position, market value |
54
+ | `shots_data` | every shot (xG, xGOT, body part, situation) |
55
+ | `lineups_data` | XI + subs + per-match player stats |
56
+ | `substitutions_data` | substitutions |
57
+ | `incidents_data` | goals, cards, VAR |
58
+ | `average_positions_data` | average pitch position (X/Y) |
59
+ | `goal_networks_data` | coordinates for every action in a goal sequence |
60
+
61
+ ### From `lineups_df`
62
+ | Function | Purpose |
63
+ | --- | --- |
64
+ | `coordinates_data(lineups_df)` | touch heatmap per player |
65
+ | `rating_breakdown_data(lineups_df)` | every pass / dribble / defensive action / ball-carry with start-end coordinates and outcome flags |
66
+
67
+ ### From `squad_df`
68
+ | Function | Purpose |
69
+ | --- | --- |
70
+ | `player_data` | profile |
71
+ | `player_attribute_overviews_data` | radar attributes + baselines |
72
+ | `player_transfers_data` | transfer history |
73
+ | `player_career_stats_data` | season-by-season career stats |
74
+ | `player_national_team_data` | international career |
75
+ | `player_match_log_data` | match-by-match in-game stats (wide) |
76
+
77
+ ---
78
+
79
+ ## ClubElo — `from datafc import clubelo`
80
+
81
+ Club names use ClubElo spelling (`Man City`, `Real Madrid`, `Paris SG`).
82
+
83
+ | Function | Purpose |
84
+ | --- | --- |
85
+ | `daily_ranking_data(date)` | full ranking on a calendar day (`"YYYY-MM-DD"` or `date`) |
86
+ | `club_history_data(club)` | Elo history for one club (pre-1960 values are provisional) |
87
+ | `fixtures_data()` | probability grid for upcoming matches |
88
+
89
+ ---
90
+
91
+ ## EloRatings — `from datafc import eloratings`
92
+
93
+ Country arguments are page slugs (`Czech_Republic`, `United_States`).
94
+
95
+ | Function | Purpose |
96
+ | --- | --- |
97
+ | `world_ranking_data()` | current national-team ranking |
98
+ | `country_matches_data(country)` | full international match history |
99
+ | `country_codes_data()` | code → country |
100
+ | `tournament_codes_data()` | code → tournament |
101
+ | `tournament_editions_data()` | edition list with dates + slugs |
102
+ | `tournament_groups_data()` | subcode → group code (e.g. WC qualifiers → `WQT`) |
103
+ | `teams_data()` | legacy country code → ISO |
104
+
105
+ ---
106
+
107
+ ## TFF — `from datafc import tff`
108
+
109
+ Süper Lig, 1. Lig, 2. Lig, 3. Lig.
110
+
111
+ ### Discovery / league
112
+ | Function | Purpose |
113
+ | --- | --- |
114
+ | `fixtures_data(page_id, week=None, group=None, playoff=False, round=None)` | weekly fixtures or play-off bracket |
115
+ | `standings_data(page_id, week, group=None)` | league table (`group` required for 2. Lig / 3. Lig) |
116
+ | `team_data(club_id)` | club profile |
117
+
118
+ ### From `match_id`
119
+ | Function | Purpose |
120
+ | --- | --- |
121
+ | `match_details_data` | teams, score, officials |
122
+ | `match_lineups_data` | XI + bench |
123
+ | `match_staff_data` | head coaches |
124
+ | `match_goals_data` | goals with type (`F`/`H`/`P`/`K`/`KG`) |
125
+ | `match_cards_data` | bookings (yellow / red / second_yellow) |
126
+
127
+ ### From `player_id`
128
+ | Function | Purpose |
129
+ | --- | --- |
130
+ | `player_data` | profile (licence, current club) |
131
+ | `player_career_data` | licence history |
132
+ | `player_national_matches_data` | national-team appearances |
133
+ | `player_goals_data` | every recorded goal |
134
+ | `player_matches_data(player_id, season)` | club matches in a season (`"2025-2026"`) |
135
+ | `player_cards_data(player_id, season)` | bookings in a season |
136
+
137
+ ---
138
+
139
+ ## Async
140
+
141
+ Every function has a mirror in `datafc.aio` with the same signature:
142
+
143
+ ```python
144
+ import asyncio
145
+ from datafc import aio
146
+
147
+ async def main():
148
+ tasks = [aio.match_data(52, 63814, week_number=w) for w in range(1, 39)]
149
+ return await asyncio.gather(*tasks, return_exceptions=True)
150
+
151
+ asyncio.run(main())
152
+ ```
153
+
154
+ ## Cache
155
+
156
+ ```python
157
+ from datafc import DiskCache
158
+
159
+ cache = DiskCache(cache_dir=".datafc_cache", ttl_hours=24)
160
+ ```
161
+
162
+ Pass `cache=cache` to any function. `ttl_hours=0` disables expiry; `cache.clear()` empties it.
163
+
164
+ ## Exceptions
165
+
166
+ `DataFCError` (base), `InvalidParameterError`, `APIError`, `RateLimitError`, `ServerError`, `DataNotAvailableError`.
@@ -1,8 +1,9 @@
1
- __version__ = "2.7.2"
1
+ __version__ = "2.9.0"
2
2
 
3
3
  from .sofascore import *
4
4
  from . import clubelo
5
5
  from . import eloratings
6
+ from . import tff
6
7
  from .exceptions import (
7
8
  DataFCError,
8
9
  InvalidParameterError,
@@ -50,6 +51,7 @@ __all__ = [
50
51
  "incidents_data",
51
52
  "average_positions_data",
52
53
  "coordinates_data",
54
+ "rating_breakdown_data",
53
55
  "goal_networks_data",
54
56
  "past_matches_data",
55
57
  # Player
@@ -67,6 +69,8 @@ __all__ = [
67
69
  "clubelo",
68
70
  # EloRatings subpackage
69
71
  "eloratings",
72
+ # TFF subpackage
73
+ "tff",
70
74
  # Exceptions
71
75
  "DataFCError",
72
76
  "InvalidParameterError",
@@ -6,6 +6,7 @@ from .fetch_goal_networks_data import goal_networks_data
6
6
  from .fetch_formations_data import formations_data
7
7
  from .fetch_lineups_data import lineups_data
8
8
  from .fetch_coordinates_data import coordinates_data
9
+ from .fetch_rating_breakdown_data import rating_breakdown_data
9
10
  from .fetch_substitutions_data import substitutions_data
10
11
  from .fetch_incidents_data import incidents_data
11
12
  from .fetch_match_details_data import match_details_data
@@ -59,6 +60,7 @@ __all__ = [
59
60
  # Player-level
60
61
  "lineups_data",
61
62
  "coordinates_data",
63
+ "rating_breakdown_data",
62
64
  "player_career_stats_data",
63
65
  "player_transfers_data",
64
66
  # League / season-level
@@ -815,3 +815,45 @@ def heatmap_records(data: dict, row: pd.Series) -> list:
815
815
  for point in data.get("heatmap", [])
816
816
  if isinstance(point, dict) and "x" in point and "y" in point
817
817
  ]
818
+
819
+
820
+ _RATING_BREAKDOWN_CATEGORIES = ("passes", "dribbles", "defensive", "ball-carries")
821
+
822
+
823
+ def rating_breakdown_records(data: dict, row: pd.Series) -> list:
824
+ """rating_breakdown_data per-player action parser.
825
+
826
+ Flattens the four action buckets returned by the endpoint
827
+ (``passes``, ``dribbles``, ``defensive``, ``ball-carries``) into one row
828
+ per action, carrying start/end coordinates and outcome flags.
829
+ """
830
+ out = []
831
+ for category in _RATING_BREAKDOWN_CATEGORIES:
832
+ actions = data.get(category) or []
833
+ for action in actions:
834
+ if not isinstance(action, dict):
835
+ continue
836
+ player_coords = action.get("playerCoordinates") or {}
837
+ pass_end = action.get("passEndCoordinates") or {}
838
+ out.append({
839
+ "country": row["country"],
840
+ "tournament": row["tournament"],
841
+ "season": row["season"],
842
+ "week": row["week"],
843
+ "game_id": row["game_id"],
844
+ "team": row["team"],
845
+ "player_id": row["player_id"],
846
+ "player_name": row["player_name"],
847
+ "category": category,
848
+ "event_action_type": action.get("eventActionType"),
849
+ "is_home": action.get("isHome"),
850
+ "outcome": action.get("outcome"),
851
+ "keypass": action.get("keypass"),
852
+ "is_assist": action.get("isAssist"),
853
+ "is_long_ball": action.get("isLongBall"),
854
+ "player_x": player_coords.get("x"),
855
+ "player_y": player_coords.get("y"),
856
+ "pass_end_x": pass_end.get("x"),
857
+ "pass_end_y": pass_end.get("y"),
858
+ })
859
+ return out
@@ -37,6 +37,7 @@ from datafc.sofascore._core import (
37
37
  goal_networks_post_process,
38
38
  heatmap_records,
39
39
  iter_per_match_async,
40
+ rating_breakdown_records,
40
41
  match_log_records_from_response,
41
42
  needs_world_cup_resolution,
42
43
  past_match_record_from_event,
@@ -513,6 +514,56 @@ async def coordinates_data(
513
514
  return result_df
514
515
 
515
516
 
517
+ async def rating_breakdown_data(
518
+ lineups_df: pd.DataFrame,
519
+ data_source: str = "sofascore",
520
+ rate_limit: float = 2.0,
521
+ cache: Optional[DiskCache] = None,
522
+ enable_json_export: bool = False,
523
+ enable_excel_export: bool = False,
524
+ output_dir: str = ".",
525
+ ) -> pd.DataFrame:
526
+ """Async version of rating_breakdown_data(). Players with no data are silently skipped."""
527
+ validate_source(data_source)
528
+ validate_df(lineups_df, "lineups_df")
529
+
530
+ unique_players = lineups_df[[
531
+ "country", "tournament", "season", "week", "game_id", "team", "player_id", "player_name"
532
+ ]].drop_duplicates()
533
+ if unique_players.empty:
534
+ raise InvalidParameterError("No unique players found in lineups_df.")
535
+
536
+ sem = asyncio.Semaphore(1)
537
+ base = API_URLS[data_source]
538
+
539
+ async def _fetch(client, row):
540
+ url = f"{base}/api/v1/event/{row['game_id']}/player/{row['player_id']}/rating-breakdown"
541
+ async with sem:
542
+ try:
543
+ data = await client.get(url)
544
+ except APIError as exc:
545
+ if exc.status_code in (404, 403):
546
+ return []
547
+ raise
548
+ return rating_breakdown_records(data, row)
549
+
550
+ async with AsyncSofascoreClient(rate_limit=rate_limit, cache=cache) as client:
551
+ batches = await asyncio.gather(*[_fetch(client, row) for _, row in unique_players.iterrows()])
552
+
553
+ records = [rec for batch in batches for rec in batch]
554
+ result_df = pd.DataFrame(records)
555
+ if result_df.empty:
556
+ raise DataNotAvailableError("No rating breakdown data found for the specified players.")
557
+
558
+ export_df(
559
+ result_df, fn_name="rating_breakdown_data", data_source=data_source,
560
+ output_dir=output_dir,
561
+ first_row=lineups_df.iloc[0],
562
+ enable_json_export=enable_json_export, enable_excel_export=enable_excel_export,
563
+ )
564
+ return result_df
565
+
566
+
516
567
  async def goal_networks_data(
517
568
  match_df: pd.DataFrame,
518
569
  data_source: str = "sofascore",
@@ -0,0 +1,66 @@
1
+ from typing import TYPE_CHECKING, Optional
2
+ import pandas as pd
3
+ from datafc.utils._client import SofascoreClient
4
+ from datafc.utils._config import API_URLS
5
+ from datafc.utils._validate import validate_source, validate_df
6
+ from datafc.sofascore._core import rating_breakdown_records, export_df
7
+ from datafc.exceptions import APIError, DataNotAvailableError, InvalidParameterError
8
+
9
+ if TYPE_CHECKING:
10
+ from datafc.utils._cache import DiskCache
11
+
12
+
13
+ def rating_breakdown_data(
14
+ lineups_df: pd.DataFrame,
15
+ data_source: str = "sofascore",
16
+ rate_limit: float = 2.0,
17
+ cache: Optional["DiskCache"] = None,
18
+ enable_json_export: bool = False,
19
+ enable_excel_export: bool = False,
20
+ output_dir: str = ".",
21
+ ) -> pd.DataFrame:
22
+ """Fetches rating-breakdown action data for each player in the provided lineup dataset.
23
+
24
+ The endpoint returns four buckets — passes, dribbles, defensive actions and
25
+ ball carries — each with start (and optionally end) coordinates plus outcome
26
+ flags (``outcome``, ``keypass``, ``isAssist``, ``isLongBall``). Rows are
27
+ flattened so each action becomes a single record tagged with ``category``.
28
+
29
+ Players with no rating breakdown data (404/403) are silently skipped.
30
+ """
31
+ validate_source(data_source)
32
+ validate_df(lineups_df, "lineups_df")
33
+
34
+ unique_players = lineups_df[[
35
+ "country", "tournament", "season", "week", "game_id", "team", "player_id", "player_name"
36
+ ]].drop_duplicates()
37
+
38
+ if unique_players.empty:
39
+ raise InvalidParameterError("No unique players found in lineups_df.")
40
+
41
+ records = []
42
+ with SofascoreClient(rate_limit=rate_limit, cache=cache) as client:
43
+ for _, row in unique_players.iterrows():
44
+ url = (
45
+ f"{API_URLS[data_source]}/api/v1/event/{row['game_id']}"
46
+ f"/player/{row['player_id']}/rating-breakdown"
47
+ )
48
+ try:
49
+ data = client.get(url)
50
+ except APIError as exc:
51
+ if exc.status_code in (404, 403):
52
+ continue
53
+ raise
54
+ records.extend(rating_breakdown_records(data, row))
55
+
56
+ result_df = pd.DataFrame(records)
57
+ if result_df.empty:
58
+ raise DataNotAvailableError("No rating breakdown data found for the specified players.")
59
+
60
+ export_df(
61
+ result_df, fn_name="rating_breakdown_data", data_source=data_source,
62
+ output_dir=output_dir,
63
+ first_row=lineups_df.iloc[0],
64
+ enable_json_export=enable_json_export, enable_excel_export=enable_excel_export,
65
+ )
66
+ return result_df
@@ -0,0 +1,40 @@
1
+ from datafc.utils._config import (
2
+ get_tff_base_url,
3
+ reset_tff_base_url,
4
+ set_tff_base_url,
5
+ )
6
+
7
+ from .fetch_fixtures_data import fixtures_data
8
+ from .fetch_match_cards_data import match_cards_data
9
+ from .fetch_match_details_data import match_details_data
10
+ from .fetch_match_goals_data import match_goals_data
11
+ from .fetch_match_lineups_data import match_lineups_data
12
+ from .fetch_match_staff_data import match_staff_data
13
+ from .fetch_player_cards_data import player_cards_data
14
+ from .fetch_player_career_data import player_career_data
15
+ from .fetch_player_data import player_data
16
+ from .fetch_player_goals_data import player_goals_data
17
+ from .fetch_player_matches_data import player_matches_data
18
+ from .fetch_player_national_matches_data import player_national_matches_data
19
+ from .fetch_standings_data import standings_data
20
+ from .fetch_team_data import team_data
21
+
22
+ __all__ = [
23
+ "fixtures_data",
24
+ "standings_data",
25
+ "match_details_data",
26
+ "match_lineups_data",
27
+ "match_staff_data",
28
+ "match_goals_data",
29
+ "match_cards_data",
30
+ "player_data",
31
+ "player_career_data",
32
+ "player_national_matches_data",
33
+ "player_goals_data",
34
+ "player_matches_data",
35
+ "player_cards_data",
36
+ "team_data",
37
+ "get_tff_base_url",
38
+ "set_tff_base_url",
39
+ "reset_tff_base_url",
40
+ ]