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.
Files changed (57) hide show
  1. {datafc-2.2.0 → datafc-2.3.0}/PKG-INFO +9 -2
  2. {datafc-2.2.0 → datafc-2.3.0}/README.md +8 -1
  3. {datafc-2.2.0 → datafc-2.3.0}/datafc/__init__.py +1 -1
  4. {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/aio.py +28 -3
  5. {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_match_data.py +23 -1
  6. {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_standings_data.py +6 -3
  7. {datafc-2.2.0 → datafc-2.3.0}/datafc/utils/_config.py +17 -6
  8. {datafc-2.2.0 → datafc-2.3.0}/datafc.egg-info/PKG-INFO +9 -2
  9. {datafc-2.2.0 → datafc-2.3.0}/pyproject.toml +1 -1
  10. {datafc-2.2.0 → datafc-2.3.0}/LICENSE +0 -0
  11. {datafc-2.2.0 → datafc-2.3.0}/datafc/exceptions.py +0 -0
  12. {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/__init__.py +0 -0
  13. {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/_parsers.py +0 -0
  14. {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_average_positions_data.py +0 -0
  15. {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_coordinates_data.py +0 -0
  16. {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_goal_networks_data.py +0 -0
  17. {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_incidents_data.py +0 -0
  18. {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_league_player_stats_data.py +0 -0
  19. {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_lineups_data.py +0 -0
  20. {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_match_details_data.py +0 -0
  21. {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_match_h2h_data.py +0 -0
  22. {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_match_odds_data.py +0 -0
  23. {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_match_stats_data.py +0 -0
  24. {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_momentum_data.py +0 -0
  25. {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_past_matches_data.py +0 -0
  26. {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_player_career_stats_data.py +0 -0
  27. {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_player_data.py +0 -0
  28. {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_player_match_log_data.py +0 -0
  29. {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_player_national_team_data.py +0 -0
  30. {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_player_stats_data.py +0 -0
  31. {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_player_transfers_data.py +0 -0
  32. {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_pregame_form_data.py +0 -0
  33. {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_referee_stats_data.py +0 -0
  34. {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_search_data.py +0 -0
  35. {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_season_rounds_data.py +0 -0
  36. {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_seasons_data.py +0 -0
  37. {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_shots_data.py +0 -0
  38. {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_squad_data.py +0 -0
  39. {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_substitutions_data.py +0 -0
  40. {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_team_data.py +0 -0
  41. {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_team_match_history_data.py +0 -0
  42. {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_team_stats_data.py +0 -0
  43. {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_team_transfers_data.py +0 -0
  44. {datafc-2.2.0 → datafc-2.3.0}/datafc/sofascore/fetch_upcoming_matches_data.py +0 -0
  45. {datafc-2.2.0 → datafc-2.3.0}/datafc/utils/__init__.py +0 -0
  46. {datafc-2.2.0 → datafc-2.3.0}/datafc/utils/_async_client.py +0 -0
  47. {datafc-2.2.0 → datafc-2.3.0}/datafc/utils/_cache.py +0 -0
  48. {datafc-2.2.0 → datafc-2.3.0}/datafc/utils/_client.py +0 -0
  49. {datafc-2.2.0 → datafc-2.3.0}/datafc/utils/_helpers.py +0 -0
  50. {datafc-2.2.0 → datafc-2.3.0}/datafc/utils/_save_files.py +0 -0
  51. {datafc-2.2.0 → datafc-2.3.0}/datafc/utils/_tournament_info.py +0 -0
  52. {datafc-2.2.0 → datafc-2.3.0}/datafc/utils/_validate.py +0 -0
  53. {datafc-2.2.0 → datafc-2.3.0}/datafc.egg-info/SOURCES.txt +0 -0
  54. {datafc-2.2.0 → datafc-2.3.0}/datafc.egg-info/dependency_links.txt +0 -0
  55. {datafc-2.2.0 → datafc-2.3.0}/datafc.egg-info/requires.txt +0 -0
  56. {datafc-2.2.0 → datafc-2.3.0}/datafc.egg-info/top_level.txt +0 -0
  57. {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.2.0
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.2.0
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.2.0
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`.
@@ -1,4 +1,4 @@
1
- __version__ = "2.2.0"
1
+ __version__ = "2.3.0"
2
2
 
3
3
  from .sofascore import *
4
4
  from .exceptions import (
@@ -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
- data = await client.get(url)
653
- return parse_standings_rows(data, category, tournament_id, season_id)
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
- data = client.get(url)
55
- rows.extend(parse_standings_rows(data, category, tournament_id, season_id))
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/6/slug/round-of-32",
46
- "round_of_16": "{base_url}/api/v1/unique-tournament/{tournament_id}/season/{season_id}/events/round/5/slug/round-of-16",
47
- "quarterfinals": "{base_url}/api/v1/unique-tournament/{tournament_id}/season/{season_id}/events/round/27/slug/quarterfinals",
48
- "semifinals": "{base_url}/api/v1/unique-tournament/{tournament_id}/season/{season_id}/events/round/28/slug/semifinals",
49
- "match_for_3rd_place": "{base_url}/api/v1/unique-tournament/{tournament_id}/season/{season_id}/events/round/50/slug/match-for-3rd-place",
50
- "final": "{base_url}/api/v1/unique-tournament/{tournament_id}/season/{season_id}/events/round/29/slug/final",
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.2.0
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.2.0
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`.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "datafc"
7
- version = "2.2.0"
7
+ version = "2.3.0"
8
8
  description = "Fetch, process, and export structured football data."
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes