datafc 2.2.0__tar.gz → 2.3.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.
- {datafc-2.2.0 → datafc-2.3.0}/PKG-INFO +9 -2
- {datafc-2.2.0 → datafc-2.3.0}/README.md +8 -1
- {datafc-2.2.0 → datafc-2.3.0}/datafc/__init__.py +1 -1
- {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/aio.py +28 -3
- {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_match_data.py +23 -1
- {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_standings_data.py +6 -3
- {datafc-2.2.0 → datafc-2.3.0}/datafc/utils/_config.py +17 -6
- {datafc-2.2.0 → datafc-2.3.0}/datafc.egg-info/PKG-INFO +9 -2
- {datafc-2.2.0 → datafc-2.3.0}/pyproject.toml +1 -1
- {datafc-2.2.0 → datafc-2.3.0}/LICENSE +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc/exceptions.py +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/__init__.py +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/_parsers.py +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_average_positions_data.py +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_coordinates_data.py +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_goal_networks_data.py +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_incidents_data.py +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_league_player_stats_data.py +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_lineups_data.py +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_match_details_data.py +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_match_h2h_data.py +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_match_odds_data.py +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_match_stats_data.py +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_momentum_data.py +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_past_matches_data.py +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_player_career_stats_data.py +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_player_data.py +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_player_match_log_data.py +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_player_national_team_data.py +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_player_stats_data.py +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_player_transfers_data.py +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_pregame_form_data.py +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_referee_stats_data.py +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_search_data.py +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_season_rounds_data.py +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_seasons_data.py +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_shots_data.py +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_squad_data.py +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_substitutions_data.py +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_team_data.py +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_team_match_history_data.py +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_team_stats_data.py +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_team_transfers_data.py +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_upcoming_matches_data.py +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc/utils/__init__.py +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc/utils/_async_client.py +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc/utils/_cache.py +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc/utils/_client.py +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc/utils/_helpers.py +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc/utils/_save_files.py +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc/utils/_tournament_info.py +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc/utils/_validate.py +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc.egg-info/SOURCES.txt +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc.egg-info/dependency_links.txt +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc.egg-info/requires.txt +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/datafc.egg-info/top_level.txt +0 -0
- {datafc-2.2.0 → datafc-2.3.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: datafc
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.0
|
|
4
4
|
Summary: Fetch, process, and export structured football data.
|
|
5
5
|
Author-email: Uraz Akgül <urazdev@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -24,7 +24,7 @@ Requires-Dist: pytest>=8.0; extra == "dev"
|
|
|
24
24
|
Requires-Dist: pytest-mock>=3.12; extra == "dev"
|
|
25
25
|
Dynamic: license-file
|
|
26
26
|
|
|
27
|
-
# datafc v2.
|
|
27
|
+
# datafc v2.3.0
|
|
28
28
|
|
|
29
29
|
## Overview
|
|
30
30
|
|
|
@@ -916,6 +916,13 @@ Columns: `referee_id`, `referee_name`, `tournament_id`, `tournament_name`, `stat
|
|
|
916
916
|
|
|
917
917
|
## Changelog
|
|
918
918
|
|
|
919
|
+
### v2.3.0
|
|
920
|
+
|
|
921
|
+
- **Fixed `match_data` for World Cup knockout stages across all seasons.** Round numbers are now resolved automatically from the API instead of being hardcoded, so older seasons work correctly.
|
|
922
|
+
- **Fixed `standings_data` for tournament-format competitions.** Calling this function for World Cup, Euro, or similar tournaments no longer raises an error. Only the available categories are returned.
|
|
923
|
+
|
|
924
|
+
---
|
|
925
|
+
|
|
919
926
|
### v2.2.0
|
|
920
927
|
|
|
921
928
|
- Added `tournament_type="world_cup"` support to `match_data` and `past_matches_data` for FIFA World Cup competitions. Knockout stage rounds are fixed internally; only `group_stage_week` requires `week_number`.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# datafc v2.
|
|
1
|
+
# datafc v2.3.0
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
4
|
|
|
@@ -890,6 +890,13 @@ Columns: `referee_id`, `referee_name`, `tournament_id`, `tournament_name`, `stat
|
|
|
890
890
|
|
|
891
891
|
## Changelog
|
|
892
892
|
|
|
893
|
+
### v2.3.0
|
|
894
|
+
|
|
895
|
+
- **Fixed `match_data` for World Cup knockout stages across all seasons.** Round numbers are now resolved automatically from the API instead of being hardcoded, so older seasons work correctly.
|
|
896
|
+
- **Fixed `standings_data` for tournament-format competitions.** Calling this function for World Cup, Euro, or similar tournaments no longer raises an error. Only the available categories are returned.
|
|
897
|
+
|
|
898
|
+
---
|
|
899
|
+
|
|
893
900
|
### v2.2.0
|
|
894
901
|
|
|
895
902
|
- Added `tournament_type="world_cup"` support to `match_data` and `past_matches_data` for FIFA World Cup competitions. Knockout stage rounds are fixed internally; only `group_stage_week` requires `week_number`.
|
|
@@ -35,7 +35,7 @@ import pandas as pd
|
|
|
35
35
|
|
|
36
36
|
from datafc.utils._async_client import AsyncSofascoreClient
|
|
37
37
|
from datafc.utils._cache import DiskCache
|
|
38
|
-
from datafc.utils._config import API_URLS, WWW_URLS
|
|
38
|
+
from datafc.utils._config import API_URLS, WWW_URLS, WORLD_CUP_KNOCKOUT_SLUGS
|
|
39
39
|
from datafc.utils._validate import validate_source, validate_df, build_tournament_url
|
|
40
40
|
from datafc.utils._save_files import save_json, save_excel
|
|
41
41
|
from datafc.utils._tournament_info import resolve_tournament_season
|
|
@@ -89,6 +89,28 @@ async def match_data(
|
|
|
89
89
|
) -> pd.DataFrame:
|
|
90
90
|
"""Async version of match_data(). See sync docstring for full parameter docs."""
|
|
91
91
|
validate_source(data_source)
|
|
92
|
+
|
|
93
|
+
if (
|
|
94
|
+
tournament_type == "world_cup"
|
|
95
|
+
and tournament_stage in WORLD_CUP_KNOCKOUT_SLUGS
|
|
96
|
+
and week_number is None
|
|
97
|
+
):
|
|
98
|
+
target_slug = WORLD_CUP_KNOCKOUT_SLUGS[tournament_stage]
|
|
99
|
+
rounds_url = (
|
|
100
|
+
f"{API_URLS[data_source]}/api/v1/unique-tournament/{tournament_id}"
|
|
101
|
+
f"/season/{season_id}/rounds"
|
|
102
|
+
)
|
|
103
|
+
async with AsyncSofascoreClient(rate_limit=rate_limit, cache=cache) as client:
|
|
104
|
+
rounds_data = await client.get(rounds_url)
|
|
105
|
+
rounds = rounds_data.get("rounds") or rounds_data.get("currentRounds") or []
|
|
106
|
+
matched = next((r for r in rounds if r.get("slug") == target_slug), None)
|
|
107
|
+
if matched is None:
|
|
108
|
+
raise DataNotAvailableError(
|
|
109
|
+
f"Could not find round with slug '{target_slug}' for "
|
|
110
|
+
f"tournament_id={tournament_id}, season_id={season_id}."
|
|
111
|
+
)
|
|
112
|
+
week_number = matched["round"]
|
|
113
|
+
|
|
92
114
|
url = build_tournament_url(
|
|
93
115
|
API_URLS[data_source], tournament_id, season_id, week_number,
|
|
94
116
|
tournament_type, tournament_stage,
|
|
@@ -649,8 +671,11 @@ async def standings_data(
|
|
|
649
671
|
f"{API_URLS[data_source]}/api/v1/unique-tournament/{tournament_id}"
|
|
650
672
|
f"/season/{season_id}/standings/{category}"
|
|
651
673
|
)
|
|
652
|
-
|
|
653
|
-
|
|
674
|
+
try:
|
|
675
|
+
data = await client.get(url)
|
|
676
|
+
return parse_standings_rows(data, category, tournament_id, season_id)
|
|
677
|
+
except APIError:
|
|
678
|
+
return []
|
|
654
679
|
|
|
655
680
|
async with AsyncSofascoreClient(rate_limit=rate_limit, cache=cache) as client:
|
|
656
681
|
batches = await asyncio.gather(*[_fetch(client, cat) for cat in ("total", "home", "away")])
|
|
@@ -2,7 +2,7 @@ from typing import TYPE_CHECKING, Optional
|
|
|
2
2
|
import pandas as pd
|
|
3
3
|
from datafc.utils._client import SofascoreClient
|
|
4
4
|
from datafc.utils._save_files import save_json, save_excel
|
|
5
|
-
from datafc.utils._config import API_URLS
|
|
5
|
+
from datafc.utils._config import API_URLS, WORLD_CUP_KNOCKOUT_SLUGS
|
|
6
6
|
from datafc.utils._validate import validate_source, build_tournament_url
|
|
7
7
|
from datafc.sofascore._parsers import parse_match_events
|
|
8
8
|
from datafc.exceptions import DataNotAvailableError
|
|
@@ -49,6 +49,28 @@ def match_data(
|
|
|
49
49
|
APIError: On HTTP errors from the Sofascore API.
|
|
50
50
|
"""
|
|
51
51
|
validate_source(data_source)
|
|
52
|
+
|
|
53
|
+
if (
|
|
54
|
+
tournament_type == "world_cup"
|
|
55
|
+
and tournament_stage in WORLD_CUP_KNOCKOUT_SLUGS
|
|
56
|
+
and week_number is None
|
|
57
|
+
):
|
|
58
|
+
target_slug = WORLD_CUP_KNOCKOUT_SLUGS[tournament_stage]
|
|
59
|
+
rounds_url = (
|
|
60
|
+
f"{API_URLS[data_source]}/api/v1/unique-tournament/{tournament_id}"
|
|
61
|
+
f"/season/{season_id}/rounds"
|
|
62
|
+
)
|
|
63
|
+
with SofascoreClient(rate_limit=rate_limit, cache=cache) as client:
|
|
64
|
+
rounds_data = client.get(rounds_url)
|
|
65
|
+
rounds = rounds_data.get("rounds") or rounds_data.get("currentRounds") or []
|
|
66
|
+
matched = next((r for r in rounds if r.get("slug") == target_slug), None)
|
|
67
|
+
if matched is None:
|
|
68
|
+
raise DataNotAvailableError(
|
|
69
|
+
f"Could not find round with slug '{target_slug}' for "
|
|
70
|
+
f"tournament_id={tournament_id}, season_id={season_id}."
|
|
71
|
+
)
|
|
72
|
+
week_number = matched["round"]
|
|
73
|
+
|
|
52
74
|
url = build_tournament_url(
|
|
53
75
|
API_URLS[data_source], tournament_id, season_id, week_number,
|
|
54
76
|
tournament_type, tournament_stage,
|
|
@@ -6,7 +6,7 @@ from datafc.utils._config import API_URLS
|
|
|
6
6
|
from datafc.utils._validate import validate_source
|
|
7
7
|
from datafc.utils._tournament_info import resolve_tournament_season
|
|
8
8
|
from datafc.sofascore._parsers import parse_standings_rows
|
|
9
|
-
from datafc.exceptions import DataNotAvailableError
|
|
9
|
+
from datafc.exceptions import APIError, DataNotAvailableError
|
|
10
10
|
|
|
11
11
|
if TYPE_CHECKING:
|
|
12
12
|
from datafc.utils._cache import DiskCache
|
|
@@ -51,8 +51,11 @@ def standings_data(
|
|
|
51
51
|
f"{API_URLS[data_source]}/api/v1/unique-tournament/{tournament_id}"
|
|
52
52
|
f"/season/{season_id}/standings/{category}"
|
|
53
53
|
)
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
try:
|
|
55
|
+
data = client.get(url)
|
|
56
|
+
rows.extend(parse_standings_rows(data, category, tournament_id, season_id))
|
|
57
|
+
except APIError:
|
|
58
|
+
pass
|
|
56
59
|
|
|
57
60
|
result_df = pd.DataFrame(rows)
|
|
58
61
|
if result_df.empty:
|
|
@@ -42,15 +42,26 @@ TOURNAMENT_URL_PATTERNS = {
|
|
|
42
42
|
},
|
|
43
43
|
"world_cup": {
|
|
44
44
|
"group_stage_week": "{base_url}/api/v1/unique-tournament/{tournament_id}/season/{season_id}/events/round/{week_number}",
|
|
45
|
-
"round_of_32": "{base_url}/api/v1/unique-tournament/{tournament_id}/season/{season_id}/events/round/
|
|
46
|
-
"round_of_16": "{base_url}/api/v1/unique-tournament/{tournament_id}/season/{season_id}/events/round/
|
|
47
|
-
"quarterfinals": "{base_url}/api/v1/unique-tournament/{tournament_id}/season/{season_id}/events/round/
|
|
48
|
-
"semifinals": "{base_url}/api/v1/unique-tournament/{tournament_id}/season/{season_id}/events/round/
|
|
49
|
-
"match_for_3rd_place": "{base_url}/api/v1/unique-tournament/{tournament_id}/season/{season_id}/events/round/
|
|
50
|
-
"final": "{base_url}/api/v1/unique-tournament/{tournament_id}/season/{season_id}/events/round/
|
|
45
|
+
"round_of_32": "{base_url}/api/v1/unique-tournament/{tournament_id}/season/{season_id}/events/round/{week_number}/slug/round-of-32",
|
|
46
|
+
"round_of_16": "{base_url}/api/v1/unique-tournament/{tournament_id}/season/{season_id}/events/round/{week_number}/slug/round-of-16",
|
|
47
|
+
"quarterfinals": "{base_url}/api/v1/unique-tournament/{tournament_id}/season/{season_id}/events/round/{week_number}/slug/quarterfinals",
|
|
48
|
+
"semifinals": "{base_url}/api/v1/unique-tournament/{tournament_id}/season/{season_id}/events/round/{week_number}/slug/semifinals",
|
|
49
|
+
"match_for_3rd_place": "{base_url}/api/v1/unique-tournament/{tournament_id}/season/{season_id}/events/round/{week_number}/slug/match-for-3rd-place",
|
|
50
|
+
"final": "{base_url}/api/v1/unique-tournament/{tournament_id}/season/{season_id}/events/round/{week_number}/slug/final",
|
|
51
51
|
},
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
# Slug used in the API URL for each world_cup knockout stage.
|
|
55
|
+
# Used to auto-resolve the season-specific round number via the /rounds endpoint.
|
|
56
|
+
WORLD_CUP_KNOCKOUT_SLUGS: dict[str, str] = {
|
|
57
|
+
"round_of_32": "round-of-32",
|
|
58
|
+
"round_of_16": "round-of-16",
|
|
59
|
+
"quarterfinals": "quarterfinals",
|
|
60
|
+
"semifinals": "semifinals",
|
|
61
|
+
"match_for_3rd_place": "match-for-3rd-place",
|
|
62
|
+
"final": "final",
|
|
63
|
+
}
|
|
64
|
+
|
|
54
65
|
# ---------------------------------------------------------------------------
|
|
55
66
|
# Runtime-overridable pattern registry
|
|
56
67
|
# ---------------------------------------------------------------------------
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: datafc
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.0
|
|
4
4
|
Summary: Fetch, process, and export structured football data.
|
|
5
5
|
Author-email: Uraz Akgül <urazdev@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -24,7 +24,7 @@ Requires-Dist: pytest>=8.0; extra == "dev"
|
|
|
24
24
|
Requires-Dist: pytest-mock>=3.12; extra == "dev"
|
|
25
25
|
Dynamic: license-file
|
|
26
26
|
|
|
27
|
-
# datafc v2.
|
|
27
|
+
# datafc v2.3.0
|
|
28
28
|
|
|
29
29
|
## Overview
|
|
30
30
|
|
|
@@ -916,6 +916,13 @@ Columns: `referee_id`, `referee_name`, `tournament_id`, `tournament_name`, `stat
|
|
|
916
916
|
|
|
917
917
|
## Changelog
|
|
918
918
|
|
|
919
|
+
### v2.3.0
|
|
920
|
+
|
|
921
|
+
- **Fixed `match_data` for World Cup knockout stages across all seasons.** Round numbers are now resolved automatically from the API instead of being hardcoded, so older seasons work correctly.
|
|
922
|
+
- **Fixed `standings_data` for tournament-format competitions.** Calling this function for World Cup, Euro, or similar tournaments no longer raises an error. Only the available categories are returned.
|
|
923
|
+
|
|
924
|
+
---
|
|
925
|
+
|
|
919
926
|
### v2.2.0
|
|
920
927
|
|
|
921
928
|
- Added `tournament_type="world_cup"` support to `match_data` and `past_matches_data` for FIFA World Cup competitions. Knockout stage rounds are fixed internally; only `group_stage_week` requires `week_number`.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|