datafc 2.1.0__tar.gz → 2.2.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.1.0 → datafc-2.2.0}/PKG-INFO +34 -8
- {datafc-2.1.0 → datafc-2.2.0}/README.md +33 -7
- {datafc-2.1.0 → datafc-2.2.0}/datafc/__init__.py +1 -1
- {datafc-2.1.0 → datafc-2.2.0}/datafc/sofascore/aio.py +2 -2
- {datafc-2.1.0 → datafc-2.2.0}/datafc/sofascore/fetch_match_data.py +2 -2
- {datafc-2.1.0 → datafc-2.2.0}/datafc/sofascore/fetch_past_matches_data.py +2 -2
- {datafc-2.1.0 → datafc-2.2.0}/datafc/sofascore/fetch_team_match_history_data.py +108 -108
- {datafc-2.1.0 → datafc-2.2.0}/datafc/utils/_config.py +9 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc/utils/_validate.py +8 -7
- {datafc-2.1.0 → datafc-2.2.0}/datafc.egg-info/PKG-INFO +34 -8
- {datafc-2.1.0 → datafc-2.2.0}/pyproject.toml +1 -1
- {datafc-2.1.0 → datafc-2.2.0}/LICENSE +0 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc/exceptions.py +0 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc/sofascore/__init__.py +0 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc/sofascore/_parsers.py +0 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc/sofascore/fetch_average_positions_data.py +0 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc/sofascore/fetch_coordinates_data.py +0 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc/sofascore/fetch_goal_networks_data.py +0 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc/sofascore/fetch_incidents_data.py +0 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc/sofascore/fetch_league_player_stats_data.py +0 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc/sofascore/fetch_lineups_data.py +0 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc/sofascore/fetch_match_details_data.py +0 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc/sofascore/fetch_match_h2h_data.py +0 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc/sofascore/fetch_match_odds_data.py +0 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc/sofascore/fetch_match_stats_data.py +0 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc/sofascore/fetch_momentum_data.py +0 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc/sofascore/fetch_player_career_stats_data.py +0 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc/sofascore/fetch_player_data.py +0 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc/sofascore/fetch_player_match_log_data.py +0 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc/sofascore/fetch_player_national_team_data.py +0 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc/sofascore/fetch_player_stats_data.py +0 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc/sofascore/fetch_player_transfers_data.py +0 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc/sofascore/fetch_pregame_form_data.py +0 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc/sofascore/fetch_referee_stats_data.py +0 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc/sofascore/fetch_search_data.py +0 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc/sofascore/fetch_season_rounds_data.py +0 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc/sofascore/fetch_seasons_data.py +0 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc/sofascore/fetch_shots_data.py +0 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc/sofascore/fetch_squad_data.py +0 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc/sofascore/fetch_standings_data.py +0 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc/sofascore/fetch_substitutions_data.py +0 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc/sofascore/fetch_team_data.py +0 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc/sofascore/fetch_team_stats_data.py +0 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc/sofascore/fetch_team_transfers_data.py +0 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc/sofascore/fetch_upcoming_matches_data.py +0 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc/utils/__init__.py +0 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc/utils/_async_client.py +0 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc/utils/_cache.py +0 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc/utils/_client.py +0 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc/utils/_helpers.py +0 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc/utils/_save_files.py +0 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc/utils/_tournament_info.py +0 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc.egg-info/SOURCES.txt +0 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc.egg-info/dependency_links.txt +0 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc.egg-info/requires.txt +0 -0
- {datafc-2.1.0 → datafc-2.2.0}/datafc.egg-info/top_level.txt +0 -0
- {datafc-2.1.0 → datafc-2.2.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.2.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.2.0
|
|
28
28
|
|
|
29
29
|
## Overview
|
|
30
30
|
|
|
@@ -530,15 +530,34 @@ ucl_df = match_data(
|
|
|
530
530
|
tournament_type="uefa",
|
|
531
531
|
tournament_stage="round_of_16",
|
|
532
532
|
)
|
|
533
|
+
|
|
534
|
+
# World Cup knockout stages — week_number not needed:
|
|
535
|
+
wc_df = match_data(
|
|
536
|
+
tournament_id=16,
|
|
537
|
+
season_id=58210,
|
|
538
|
+
tournament_type="world_cup",
|
|
539
|
+
tournament_stage="round_of_16",
|
|
540
|
+
)
|
|
541
|
+
|
|
542
|
+
# World Cup group stage — week_number required:
|
|
543
|
+
wc_group_df = match_data(
|
|
544
|
+
tournament_id=16,
|
|
545
|
+
season_id=58210,
|
|
546
|
+
week_number=1,
|
|
547
|
+
tournament_type="world_cup",
|
|
548
|
+
tournament_stage="group_stage_week",
|
|
549
|
+
)
|
|
533
550
|
```
|
|
534
551
|
|
|
535
552
|
Parameters:
|
|
536
553
|
|
|
537
554
|
- `tournament_id` (int)
|
|
538
555
|
- `season_id` (int)
|
|
539
|
-
- `week_number` (int)
|
|
540
|
-
- `tournament_type` (str, optional): `"uefa"` for UEFA competitions. `None` assumes a domestic league.
|
|
541
|
-
- `tournament_stage` (str, optional): Required when `tournament_type
|
|
556
|
+
- `week_number` (int, optional): Required for league rounds, UEFA stages, and `world_cup` + `group_stage_week`. Not needed for other `world_cup` stages.
|
|
557
|
+
- `tournament_type` (str, optional): `"uefa"` for UEFA competitions, `"world_cup"` for FIFA World Cup. `None` assumes a domestic league.
|
|
558
|
+
- `tournament_stage` (str, optional): Required when `tournament_type` is set.
|
|
559
|
+
- `"uefa"` options: `preliminary_semifinals`, `preliminary_final`, `qualification_round`, `qualification_playoff`, `group_stage_week`, `playoff_round`, `round_of_16`, `quarterfinals`, `semifinals`, `match_for_3rd_place`, `final`.
|
|
560
|
+
- `"world_cup"` options: `group_stage_week`, `round_of_32`, `round_of_16`, `quarterfinals`, `semifinals`, `match_for_3rd_place`, `final`.
|
|
542
561
|
|
|
543
562
|
Columns: `country`, `tournament`, `season`, `week`, `game_id`, `home_team`, `home_team_id`, `away_team`, `away_team_id`, `injury_time_1`, `injury_time_2`, `start_timestamp`, `status`, `home_score_current`, `home_score_display`, `home_score_period1`, `home_score_period2`, `home_score_normaltime`, `away_score_current`, `away_score_display`, `away_score_period1`, `away_score_period2`, `away_score_normaltime`.
|
|
544
563
|
|
|
@@ -775,9 +794,9 @@ Parameters:
|
|
|
775
794
|
|
|
776
795
|
- `tournament_id` (int)
|
|
777
796
|
- `season_id` (int)
|
|
778
|
-
- `week_number` (int)
|
|
779
|
-
- `tournament_type` (str, optional): `"uefa"` for UEFA competitions.
|
|
780
|
-
- `tournament_stage` (str, optional): Required when `tournament_type
|
|
797
|
+
- `week_number` (int, optional): Required for league rounds, UEFA stages, and `world_cup` + `group_stage_week`. Not needed for other `world_cup` stages.
|
|
798
|
+
- `tournament_type` (str, optional): `"uefa"` for UEFA competitions, `"world_cup"` for FIFA World Cup.
|
|
799
|
+
- `tournament_stage` (str, optional): Required when `tournament_type` is set. Same options as `match_data`.
|
|
781
800
|
|
|
782
801
|
Same columns as `match_data`.
|
|
783
802
|
|
|
@@ -897,6 +916,13 @@ Columns: `referee_id`, `referee_name`, `tournament_id`, `tournament_name`, `stat
|
|
|
897
916
|
|
|
898
917
|
## Changelog
|
|
899
918
|
|
|
919
|
+
### v2.2.0
|
|
920
|
+
|
|
921
|
+
- 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`.
|
|
922
|
+
- `week_number` is now optional (`None` by default). It is required for league rounds, UEFA stages, and `world_cup` + `group_stage_week`. Omitting it when required raises `InvalidParameterError`.
|
|
923
|
+
|
|
924
|
+
---
|
|
925
|
+
|
|
900
926
|
### v2.1.0
|
|
901
927
|
|
|
902
928
|
- Added `team_match_history_data`: fetches the complete match history for a single team across all competitions using `team_id` directly (no standings dependency).
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# datafc v2.
|
|
1
|
+
# datafc v2.2.0
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
4
|
|
|
@@ -504,15 +504,34 @@ ucl_df = match_data(
|
|
|
504
504
|
tournament_type="uefa",
|
|
505
505
|
tournament_stage="round_of_16",
|
|
506
506
|
)
|
|
507
|
+
|
|
508
|
+
# World Cup knockout stages — week_number not needed:
|
|
509
|
+
wc_df = match_data(
|
|
510
|
+
tournament_id=16,
|
|
511
|
+
season_id=58210,
|
|
512
|
+
tournament_type="world_cup",
|
|
513
|
+
tournament_stage="round_of_16",
|
|
514
|
+
)
|
|
515
|
+
|
|
516
|
+
# World Cup group stage — week_number required:
|
|
517
|
+
wc_group_df = match_data(
|
|
518
|
+
tournament_id=16,
|
|
519
|
+
season_id=58210,
|
|
520
|
+
week_number=1,
|
|
521
|
+
tournament_type="world_cup",
|
|
522
|
+
tournament_stage="group_stage_week",
|
|
523
|
+
)
|
|
507
524
|
```
|
|
508
525
|
|
|
509
526
|
Parameters:
|
|
510
527
|
|
|
511
528
|
- `tournament_id` (int)
|
|
512
529
|
- `season_id` (int)
|
|
513
|
-
- `week_number` (int)
|
|
514
|
-
- `tournament_type` (str, optional): `"uefa"` for UEFA competitions. `None` assumes a domestic league.
|
|
515
|
-
- `tournament_stage` (str, optional): Required when `tournament_type
|
|
530
|
+
- `week_number` (int, optional): Required for league rounds, UEFA stages, and `world_cup` + `group_stage_week`. Not needed for other `world_cup` stages.
|
|
531
|
+
- `tournament_type` (str, optional): `"uefa"` for UEFA competitions, `"world_cup"` for FIFA World Cup. `None` assumes a domestic league.
|
|
532
|
+
- `tournament_stage` (str, optional): Required when `tournament_type` is set.
|
|
533
|
+
- `"uefa"` options: `preliminary_semifinals`, `preliminary_final`, `qualification_round`, `qualification_playoff`, `group_stage_week`, `playoff_round`, `round_of_16`, `quarterfinals`, `semifinals`, `match_for_3rd_place`, `final`.
|
|
534
|
+
- `"world_cup"` options: `group_stage_week`, `round_of_32`, `round_of_16`, `quarterfinals`, `semifinals`, `match_for_3rd_place`, `final`.
|
|
516
535
|
|
|
517
536
|
Columns: `country`, `tournament`, `season`, `week`, `game_id`, `home_team`, `home_team_id`, `away_team`, `away_team_id`, `injury_time_1`, `injury_time_2`, `start_timestamp`, `status`, `home_score_current`, `home_score_display`, `home_score_period1`, `home_score_period2`, `home_score_normaltime`, `away_score_current`, `away_score_display`, `away_score_period1`, `away_score_period2`, `away_score_normaltime`.
|
|
518
537
|
|
|
@@ -749,9 +768,9 @@ Parameters:
|
|
|
749
768
|
|
|
750
769
|
- `tournament_id` (int)
|
|
751
770
|
- `season_id` (int)
|
|
752
|
-
- `week_number` (int)
|
|
753
|
-
- `tournament_type` (str, optional): `"uefa"` for UEFA competitions.
|
|
754
|
-
- `tournament_stage` (str, optional): Required when `tournament_type
|
|
771
|
+
- `week_number` (int, optional): Required for league rounds, UEFA stages, and `world_cup` + `group_stage_week`. Not needed for other `world_cup` stages.
|
|
772
|
+
- `tournament_type` (str, optional): `"uefa"` for UEFA competitions, `"world_cup"` for FIFA World Cup.
|
|
773
|
+
- `tournament_stage` (str, optional): Required when `tournament_type` is set. Same options as `match_data`.
|
|
755
774
|
|
|
756
775
|
Same columns as `match_data`.
|
|
757
776
|
|
|
@@ -871,6 +890,13 @@ Columns: `referee_id`, `referee_name`, `tournament_id`, `tournament_name`, `stat
|
|
|
871
890
|
|
|
872
891
|
## Changelog
|
|
873
892
|
|
|
893
|
+
### v2.2.0
|
|
894
|
+
|
|
895
|
+
- 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`.
|
|
896
|
+
- `week_number` is now optional (`None` by default). It is required for league rounds, UEFA stages, and `world_cup` + `group_stage_week`. Omitting it when required raises `InvalidParameterError`.
|
|
897
|
+
|
|
898
|
+
---
|
|
899
|
+
|
|
874
900
|
### v2.1.0
|
|
875
901
|
|
|
876
902
|
- Added `team_match_history_data`: fetches the complete match history for a single team across all competitions using `team_id` directly (no standings dependency).
|
|
@@ -77,7 +77,7 @@ logger = logging.getLogger(__name__)
|
|
|
77
77
|
async def match_data(
|
|
78
78
|
tournament_id: int,
|
|
79
79
|
season_id: int,
|
|
80
|
-
week_number: int,
|
|
80
|
+
week_number: Optional[int] = None,
|
|
81
81
|
tournament_type: Optional[str] = None,
|
|
82
82
|
tournament_stage: Optional[str] = None,
|
|
83
83
|
data_source: str = "sofascore",
|
|
@@ -1173,7 +1173,7 @@ async def goal_networks_data(
|
|
|
1173
1173
|
async def past_matches_data(
|
|
1174
1174
|
tournament_id: int,
|
|
1175
1175
|
season_id: int,
|
|
1176
|
-
week_number: int,
|
|
1176
|
+
week_number: Optional[int] = None,
|
|
1177
1177
|
tournament_type: Optional[str] = None,
|
|
1178
1178
|
tournament_stage: Optional[str] = None,
|
|
1179
1179
|
data_source: str = "sofascore",
|
|
@@ -14,7 +14,7 @@ if TYPE_CHECKING:
|
|
|
14
14
|
def match_data(
|
|
15
15
|
tournament_id: int,
|
|
16
16
|
season_id: int,
|
|
17
|
-
week_number: int,
|
|
17
|
+
week_number: Optional[int] = None,
|
|
18
18
|
tournament_type: Optional[str] = None,
|
|
19
19
|
tournament_stage: Optional[str] = None,
|
|
20
20
|
data_source: str = "sofascore",
|
|
@@ -31,7 +31,7 @@ def match_data(
|
|
|
31
31
|
tournament_id: The unique identifier for the tournament.
|
|
32
32
|
season_id: The unique identifier for the season.
|
|
33
33
|
week_number: The matchweek number within the season.
|
|
34
|
-
tournament_type: The tournament type ('uefa'). If None, assumes league format.
|
|
34
|
+
tournament_type: The tournament type ('uefa', 'world_cup'). If None, assumes league format.
|
|
35
35
|
tournament_stage: The specific stage of the tournament (e.g., 'group_stage_week', 'round_of_16').
|
|
36
36
|
data_source: The data source ('sofavpn' or 'sofascore'). Defaults to 'sofascore'.
|
|
37
37
|
rate_limit: Maximum requests per second. Defaults to 2.0.
|
|
@@ -13,7 +13,7 @@ if TYPE_CHECKING:
|
|
|
13
13
|
def past_matches_data(
|
|
14
14
|
tournament_id: int,
|
|
15
15
|
season_id: int,
|
|
16
|
-
week_number: int,
|
|
16
|
+
week_number: Optional[int] = None,
|
|
17
17
|
tournament_type: Optional[str] = None,
|
|
18
18
|
tournament_stage: Optional[str] = None,
|
|
19
19
|
data_source: str = "sofascore",
|
|
@@ -30,7 +30,7 @@ def past_matches_data(
|
|
|
30
30
|
tournament_id: The unique identifier for the tournament.
|
|
31
31
|
season_id: The unique identifier for the season.
|
|
32
32
|
week_number: The matchweek number within the season.
|
|
33
|
-
tournament_type: The tournament type ('uefa'). If None, assumes league format.
|
|
33
|
+
tournament_type: The tournament type ('uefa', 'world_cup'). If None, assumes league format.
|
|
34
34
|
tournament_stage: The specific stage of the tournament (e.g., 'group_stage_week', 'round_of_16').
|
|
35
35
|
data_source: The data source ('sofavpn' or 'sofascore'). Defaults to 'sofascore'.
|
|
36
36
|
rate_limit: Maximum requests per second. Defaults to 2.0.
|
|
@@ -1,108 +1,108 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
from typing import TYPE_CHECKING, Optional
|
|
3
|
-
import pandas as pd
|
|
4
|
-
from datafc.utils._client import SofascoreClient
|
|
5
|
-
from datafc.utils._save_files import save_json, save_excel
|
|
6
|
-
from datafc.utils._config import API_URLS
|
|
7
|
-
from datafc.utils._validate import validate_source
|
|
8
|
-
from datafc.utils._helpers import _cast_int_cols
|
|
9
|
-
from datafc.sofascore._parsers import parse_team_match_history_records
|
|
10
|
-
from datafc.exceptions import APIError, DataNotAvailableError
|
|
11
|
-
|
|
12
|
-
if TYPE_CHECKING:
|
|
13
|
-
from datafc.utils._cache import DiskCache
|
|
14
|
-
|
|
15
|
-
logger = logging.getLogger(__name__)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def team_match_history_data(
|
|
19
|
-
team_id: int,
|
|
20
|
-
data_source: str = "sofascore",
|
|
21
|
-
rate_limit: float = 2.0,
|
|
22
|
-
cache: Optional["DiskCache"] = None,
|
|
23
|
-
enable_json_export: bool = False,
|
|
24
|
-
enable_excel_export: bool = False,
|
|
25
|
-
output_dir: str = ".",
|
|
26
|
-
) -> pd.DataFrame:
|
|
27
|
-
"""
|
|
28
|
-
Fetches the complete match history for a single team across all competitions.
|
|
29
|
-
|
|
30
|
-
Paginates through all available history pages until no further pages exist.
|
|
31
|
-
The team_id can be obtained from standings_data(), squad_data(), or search_data().
|
|
32
|
-
|
|
33
|
-
Args:
|
|
34
|
-
team_id: The unique Sofascore identifier for the team.
|
|
35
|
-
data_source: The data source ('sofavpn' or 'sofascore'). Defaults to 'sofascore'.
|
|
36
|
-
rate_limit: Maximum requests per second. Defaults to 2.0.
|
|
37
|
-
cache: Optional DiskCache instance. Cached responses skip the API call.
|
|
38
|
-
enable_json_export: If True, saves output as JSON. Defaults to False.
|
|
39
|
-
enable_excel_export: If True, saves output as Excel. Defaults to False.
|
|
40
|
-
output_dir: Directory for exported files. Defaults to current directory.
|
|
41
|
-
|
|
42
|
-
Returns:
|
|
43
|
-
Past matches with country, tournament, season, week, home/away team names,
|
|
44
|
-
IDs, scores, start timestamp and status; sorted by start_timestamp ascending.
|
|
45
|
-
|
|
46
|
-
Raises:
|
|
47
|
-
InvalidParameterError: If an invalid data_source is given.
|
|
48
|
-
DataNotAvailableError: If no historical match data is found for the team.
|
|
49
|
-
APIError: On HTTP errors from the Sofascore API.
|
|
50
|
-
"""
|
|
51
|
-
validate_source(data_source)
|
|
52
|
-
|
|
53
|
-
seen_game_ids: set = set()
|
|
54
|
-
records = []
|
|
55
|
-
page = 0
|
|
56
|
-
|
|
57
|
-
with SofascoreClient(rate_limit=rate_limit, cache=cache) as client:
|
|
58
|
-
while True:
|
|
59
|
-
url = f"{API_URLS[data_source]}/api/v1/team/{team_id}/events/last/{page}"
|
|
60
|
-
try:
|
|
61
|
-
data = client.get(url)
|
|
62
|
-
except APIError as exc:
|
|
63
|
-
logger.warning(
|
|
64
|
-
"Failed to fetch match history for team_id=%s page=%s: %s",
|
|
65
|
-
team_id, page, exc,
|
|
66
|
-
)
|
|
67
|
-
break
|
|
68
|
-
|
|
69
|
-
batch = parse_team_match_history_records(data, seen_game_ids)
|
|
70
|
-
records.extend(batch)
|
|
71
|
-
|
|
72
|
-
if not data.get("hasNextPage", False):
|
|
73
|
-
break
|
|
74
|
-
page += 1
|
|
75
|
-
|
|
76
|
-
result_df = pd.DataFrame(records)
|
|
77
|
-
if result_df.empty:
|
|
78
|
-
raise DataNotAvailableError(
|
|
79
|
-
f"No historical match data found for team_id={team_id}."
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
result_df = result_df.sort_values("start_timestamp").reset_index(drop=True)
|
|
83
|
-
result_df = _cast_int_cols(
|
|
84
|
-
result_df,
|
|
85
|
-
"week",
|
|
86
|
-
"home_team_id", "away_team_id",
|
|
87
|
-
"home_score_period1", "home_score_period2", "home_score_normaltime",
|
|
88
|
-
"home_score_display", "home_score_current",
|
|
89
|
-
"away_score_period1", "away_score_period2", "away_score_normaltime",
|
|
90
|
-
"away_score_display", "away_score_current",
|
|
91
|
-
"start_timestamp",
|
|
92
|
-
)
|
|
93
|
-
|
|
94
|
-
if enable_json_export or enable_excel_export:
|
|
95
|
-
first = result_df.iloc[0]
|
|
96
|
-
kwargs = dict(
|
|
97
|
-
fn_name="team_match_history_data",
|
|
98
|
-
data_source=data_source,
|
|
99
|
-
country=first.get("country", ""),
|
|
100
|
-
tournament=first.get("tournament", ""),
|
|
101
|
-
season=first.get("season"),
|
|
102
|
-
)
|
|
103
|
-
if enable_json_export:
|
|
104
|
-
save_json(data=result_df, **kwargs, output_dir=output_dir)
|
|
105
|
-
if enable_excel_export:
|
|
106
|
-
save_excel(data=result_df, **kwargs, output_dir=output_dir)
|
|
107
|
-
|
|
108
|
-
return result_df
|
|
1
|
+
import logging
|
|
2
|
+
from typing import TYPE_CHECKING, Optional
|
|
3
|
+
import pandas as pd
|
|
4
|
+
from datafc.utils._client import SofascoreClient
|
|
5
|
+
from datafc.utils._save_files import save_json, save_excel
|
|
6
|
+
from datafc.utils._config import API_URLS
|
|
7
|
+
from datafc.utils._validate import validate_source
|
|
8
|
+
from datafc.utils._helpers import _cast_int_cols
|
|
9
|
+
from datafc.sofascore._parsers import parse_team_match_history_records
|
|
10
|
+
from datafc.exceptions import APIError, DataNotAvailableError
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from datafc.utils._cache import DiskCache
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def team_match_history_data(
|
|
19
|
+
team_id: int,
|
|
20
|
+
data_source: str = "sofascore",
|
|
21
|
+
rate_limit: float = 2.0,
|
|
22
|
+
cache: Optional["DiskCache"] = None,
|
|
23
|
+
enable_json_export: bool = False,
|
|
24
|
+
enable_excel_export: bool = False,
|
|
25
|
+
output_dir: str = ".",
|
|
26
|
+
) -> pd.DataFrame:
|
|
27
|
+
"""
|
|
28
|
+
Fetches the complete match history for a single team across all competitions.
|
|
29
|
+
|
|
30
|
+
Paginates through all available history pages until no further pages exist.
|
|
31
|
+
The team_id can be obtained from standings_data(), squad_data(), or search_data().
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
team_id: The unique Sofascore identifier for the team.
|
|
35
|
+
data_source: The data source ('sofavpn' or 'sofascore'). Defaults to 'sofascore'.
|
|
36
|
+
rate_limit: Maximum requests per second. Defaults to 2.0.
|
|
37
|
+
cache: Optional DiskCache instance. Cached responses skip the API call.
|
|
38
|
+
enable_json_export: If True, saves output as JSON. Defaults to False.
|
|
39
|
+
enable_excel_export: If True, saves output as Excel. Defaults to False.
|
|
40
|
+
output_dir: Directory for exported files. Defaults to current directory.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
Past matches with country, tournament, season, week, home/away team names,
|
|
44
|
+
IDs, scores, start timestamp and status; sorted by start_timestamp ascending.
|
|
45
|
+
|
|
46
|
+
Raises:
|
|
47
|
+
InvalidParameterError: If an invalid data_source is given.
|
|
48
|
+
DataNotAvailableError: If no historical match data is found for the team.
|
|
49
|
+
APIError: On HTTP errors from the Sofascore API.
|
|
50
|
+
"""
|
|
51
|
+
validate_source(data_source)
|
|
52
|
+
|
|
53
|
+
seen_game_ids: set = set()
|
|
54
|
+
records = []
|
|
55
|
+
page = 0
|
|
56
|
+
|
|
57
|
+
with SofascoreClient(rate_limit=rate_limit, cache=cache) as client:
|
|
58
|
+
while True:
|
|
59
|
+
url = f"{API_URLS[data_source]}/api/v1/team/{team_id}/events/last/{page}"
|
|
60
|
+
try:
|
|
61
|
+
data = client.get(url)
|
|
62
|
+
except APIError as exc:
|
|
63
|
+
logger.warning(
|
|
64
|
+
"Failed to fetch match history for team_id=%s page=%s: %s",
|
|
65
|
+
team_id, page, exc,
|
|
66
|
+
)
|
|
67
|
+
break
|
|
68
|
+
|
|
69
|
+
batch = parse_team_match_history_records(data, seen_game_ids)
|
|
70
|
+
records.extend(batch)
|
|
71
|
+
|
|
72
|
+
if not data.get("hasNextPage", False):
|
|
73
|
+
break
|
|
74
|
+
page += 1
|
|
75
|
+
|
|
76
|
+
result_df = pd.DataFrame(records)
|
|
77
|
+
if result_df.empty:
|
|
78
|
+
raise DataNotAvailableError(
|
|
79
|
+
f"No historical match data found for team_id={team_id}."
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
result_df = result_df.sort_values("start_timestamp").reset_index(drop=True)
|
|
83
|
+
result_df = _cast_int_cols(
|
|
84
|
+
result_df,
|
|
85
|
+
"week",
|
|
86
|
+
"home_team_id", "away_team_id",
|
|
87
|
+
"home_score_period1", "home_score_period2", "home_score_normaltime",
|
|
88
|
+
"home_score_display", "home_score_current",
|
|
89
|
+
"away_score_period1", "away_score_period2", "away_score_normaltime",
|
|
90
|
+
"away_score_display", "away_score_current",
|
|
91
|
+
"start_timestamp",
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
if enable_json_export or enable_excel_export:
|
|
95
|
+
first = result_df.iloc[0]
|
|
96
|
+
kwargs = dict(
|
|
97
|
+
fn_name="team_match_history_data",
|
|
98
|
+
data_source=data_source,
|
|
99
|
+
country=first.get("country", ""),
|
|
100
|
+
tournament=first.get("tournament", ""),
|
|
101
|
+
season=first.get("season"),
|
|
102
|
+
)
|
|
103
|
+
if enable_json_export:
|
|
104
|
+
save_json(data=result_df, **kwargs, output_dir=output_dir)
|
|
105
|
+
if enable_excel_export:
|
|
106
|
+
save_excel(data=result_df, **kwargs, output_dir=output_dir)
|
|
107
|
+
|
|
108
|
+
return result_df
|
|
@@ -40,6 +40,15 @@ TOURNAMENT_URL_PATTERNS = {
|
|
|
40
40
|
"match_for_3rd_place": "{base_url}/api/v1/unique-tournament/{tournament_id}/season/{season_id}/events/round/{week_number}/slug/match-for-3rd-place",
|
|
41
41
|
"final": "{base_url}/api/v1/unique-tournament/{tournament_id}/season/{season_id}/events/round/{week_number}/slug/final",
|
|
42
42
|
},
|
|
43
|
+
"world_cup": {
|
|
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",
|
|
51
|
+
},
|
|
43
52
|
}
|
|
44
53
|
|
|
45
54
|
# ---------------------------------------------------------------------------
|
|
@@ -75,7 +75,7 @@ def build_tournament_url(
|
|
|
75
75
|
base_url: str,
|
|
76
76
|
tournament_id: int,
|
|
77
77
|
season_id: int,
|
|
78
|
-
week_number: int,
|
|
78
|
+
week_number: Optional[int],
|
|
79
79
|
tournament_type: Optional[str],
|
|
80
80
|
tournament_stage: Optional[str],
|
|
81
81
|
) -> str:
|
|
@@ -84,13 +84,14 @@ def build_tournament_url(
|
|
|
84
84
|
if tournament_type is not None:
|
|
85
85
|
validate_tournament_type(tournament_type)
|
|
86
86
|
validate_tournament_stage(tournament_type, tournament_stage)
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
87
|
+
template = patterns[tournament_type][tournament_stage]
|
|
88
|
+
else:
|
|
89
|
+
template = patterns["default"]
|
|
90
|
+
if "{week_number}" in template and week_number is None:
|
|
91
|
+
raise InvalidParameterError(
|
|
92
|
+
"week_number is required for this tournament_type/tournament_stage combination."
|
|
92
93
|
)
|
|
93
|
-
return
|
|
94
|
+
return template.format(
|
|
94
95
|
base_url=base_url,
|
|
95
96
|
tournament_id=tournament_id,
|
|
96
97
|
season_id=season_id,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: datafc
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.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.2.0
|
|
28
28
|
|
|
29
29
|
## Overview
|
|
30
30
|
|
|
@@ -530,15 +530,34 @@ ucl_df = match_data(
|
|
|
530
530
|
tournament_type="uefa",
|
|
531
531
|
tournament_stage="round_of_16",
|
|
532
532
|
)
|
|
533
|
+
|
|
534
|
+
# World Cup knockout stages — week_number not needed:
|
|
535
|
+
wc_df = match_data(
|
|
536
|
+
tournament_id=16,
|
|
537
|
+
season_id=58210,
|
|
538
|
+
tournament_type="world_cup",
|
|
539
|
+
tournament_stage="round_of_16",
|
|
540
|
+
)
|
|
541
|
+
|
|
542
|
+
# World Cup group stage — week_number required:
|
|
543
|
+
wc_group_df = match_data(
|
|
544
|
+
tournament_id=16,
|
|
545
|
+
season_id=58210,
|
|
546
|
+
week_number=1,
|
|
547
|
+
tournament_type="world_cup",
|
|
548
|
+
tournament_stage="group_stage_week",
|
|
549
|
+
)
|
|
533
550
|
```
|
|
534
551
|
|
|
535
552
|
Parameters:
|
|
536
553
|
|
|
537
554
|
- `tournament_id` (int)
|
|
538
555
|
- `season_id` (int)
|
|
539
|
-
- `week_number` (int)
|
|
540
|
-
- `tournament_type` (str, optional): `"uefa"` for UEFA competitions. `None` assumes a domestic league.
|
|
541
|
-
- `tournament_stage` (str, optional): Required when `tournament_type
|
|
556
|
+
- `week_number` (int, optional): Required for league rounds, UEFA stages, and `world_cup` + `group_stage_week`. Not needed for other `world_cup` stages.
|
|
557
|
+
- `tournament_type` (str, optional): `"uefa"` for UEFA competitions, `"world_cup"` for FIFA World Cup. `None` assumes a domestic league.
|
|
558
|
+
- `tournament_stage` (str, optional): Required when `tournament_type` is set.
|
|
559
|
+
- `"uefa"` options: `preliminary_semifinals`, `preliminary_final`, `qualification_round`, `qualification_playoff`, `group_stage_week`, `playoff_round`, `round_of_16`, `quarterfinals`, `semifinals`, `match_for_3rd_place`, `final`.
|
|
560
|
+
- `"world_cup"` options: `group_stage_week`, `round_of_32`, `round_of_16`, `quarterfinals`, `semifinals`, `match_for_3rd_place`, `final`.
|
|
542
561
|
|
|
543
562
|
Columns: `country`, `tournament`, `season`, `week`, `game_id`, `home_team`, `home_team_id`, `away_team`, `away_team_id`, `injury_time_1`, `injury_time_2`, `start_timestamp`, `status`, `home_score_current`, `home_score_display`, `home_score_period1`, `home_score_period2`, `home_score_normaltime`, `away_score_current`, `away_score_display`, `away_score_period1`, `away_score_period2`, `away_score_normaltime`.
|
|
544
563
|
|
|
@@ -775,9 +794,9 @@ Parameters:
|
|
|
775
794
|
|
|
776
795
|
- `tournament_id` (int)
|
|
777
796
|
- `season_id` (int)
|
|
778
|
-
- `week_number` (int)
|
|
779
|
-
- `tournament_type` (str, optional): `"uefa"` for UEFA competitions.
|
|
780
|
-
- `tournament_stage` (str, optional): Required when `tournament_type
|
|
797
|
+
- `week_number` (int, optional): Required for league rounds, UEFA stages, and `world_cup` + `group_stage_week`. Not needed for other `world_cup` stages.
|
|
798
|
+
- `tournament_type` (str, optional): `"uefa"` for UEFA competitions, `"world_cup"` for FIFA World Cup.
|
|
799
|
+
- `tournament_stage` (str, optional): Required when `tournament_type` is set. Same options as `match_data`.
|
|
781
800
|
|
|
782
801
|
Same columns as `match_data`.
|
|
783
802
|
|
|
@@ -897,6 +916,13 @@ Columns: `referee_id`, `referee_name`, `tournament_id`, `tournament_name`, `stat
|
|
|
897
916
|
|
|
898
917
|
## Changelog
|
|
899
918
|
|
|
919
|
+
### v2.2.0
|
|
920
|
+
|
|
921
|
+
- 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`.
|
|
922
|
+
- `week_number` is now optional (`None` by default). It is required for league rounds, UEFA stages, and `world_cup` + `group_stage_week`. Omitting it when required raises `InvalidParameterError`.
|
|
923
|
+
|
|
924
|
+
---
|
|
925
|
+
|
|
900
926
|
### v2.1.0
|
|
901
927
|
|
|
902
928
|
- Added `team_match_history_data`: fetches the complete match history for a single team across all competitions using `team_id` directly (no standings dependency).
|
|
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
|