impectPy 2.5.9__tar.gz → 2.5.10__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.
- {impectpy-2.5.9 → impectpy-2.5.10}/PKG-INFO +16 -4
- {impectpy-2.5.9 → impectpy-2.5.10}/README.md +16 -4
- {impectpy-2.5.9 → impectpy-2.5.10}/impectPy/__init__.py +2 -1
- impectpy-2.5.10/impectPy/data.py +54 -0
- {impectpy-2.5.9 → impectpy-2.5.10}/impectPy/impect.py +27 -2
- {impectpy-2.5.9 → impectpy-2.5.10}/impectPy.egg-info/PKG-INFO +16 -4
- {impectpy-2.5.9 → impectpy-2.5.10}/impectPy.egg-info/SOURCES.txt +3 -1
- {impectpy-2.5.9 → impectpy-2.5.10}/setup.py +1 -1
- impectpy-2.5.10/tests/test_package.py +291 -0
- {impectpy-2.5.9 → impectpy-2.5.10}/LICENSE.md +0 -0
- {impectpy-2.5.9 → impectpy-2.5.10}/impectPy/access_token.py +0 -0
- {impectpy-2.5.9 → impectpy-2.5.10}/impectPy/config.py +0 -0
- {impectpy-2.5.9 → impectpy-2.5.10}/impectPy/events.py +0 -0
- {impectpy-2.5.9 → impectpy-2.5.10}/impectPy/formations.py +0 -0
- {impectpy-2.5.9 → impectpy-2.5.10}/impectPy/generate_xml.py +0 -0
- {impectpy-2.5.9 → impectpy-2.5.10}/impectPy/helpers.py +0 -0
- {impectpy-2.5.9 → impectpy-2.5.10}/impectPy/iterations.py +0 -0
- {impectpy-2.5.9 → impectpy-2.5.10}/impectPy/matches.py +0 -0
- {impectpy-2.5.9 → impectpy-2.5.10}/impectPy/player_iteration_averages.py +0 -0
- {impectpy-2.5.9 → impectpy-2.5.10}/impectPy/player_iteration_scores.py +0 -0
- {impectpy-2.5.9 → impectpy-2.5.10}/impectPy/player_match_scores.py +0 -0
- {impectpy-2.5.9 → impectpy-2.5.10}/impectPy/player_matchsums.py +0 -0
- {impectpy-2.5.9 → impectpy-2.5.10}/impectPy/player_profile_scores.py +0 -0
- {impectpy-2.5.9 → impectpy-2.5.10}/impectPy/set_pieces.py +0 -0
- {impectpy-2.5.9 → impectpy-2.5.10}/impectPy/squad_coefficients.py +0 -0
- {impectpy-2.5.9 → impectpy-2.5.10}/impectPy/squad_iteration_averages.py +0 -0
- {impectpy-2.5.9 → impectpy-2.5.10}/impectPy/squad_iteration_scores.py +0 -0
- {impectpy-2.5.9 → impectpy-2.5.10}/impectPy/squad_match_scores.py +0 -0
- {impectpy-2.5.9 → impectpy-2.5.10}/impectPy/squad_matchsums.py +0 -0
- {impectpy-2.5.9 → impectpy-2.5.10}/impectPy/squad_ratings.py +0 -0
- {impectpy-2.5.9 → impectpy-2.5.10}/impectPy/starting_positions.py +0 -0
- {impectpy-2.5.9 → impectpy-2.5.10}/impectPy/substitutions.py +0 -0
- {impectpy-2.5.9 → impectpy-2.5.10}/impectPy.egg-info/dependency_links.txt +0 -0
- {impectpy-2.5.9 → impectpy-2.5.10}/impectPy.egg-info/requires.txt +0 -0
- {impectpy-2.5.9 → impectpy-2.5.10}/impectPy.egg-info/top_level.txt +0 -0
- {impectpy-2.5.9 → impectpy-2.5.10}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: impectPy
|
|
3
|
-
Version: 2.5.
|
|
3
|
+
Version: 2.5.10
|
|
4
4
|
Summary: A Python package to facilitate interaction with the Impect customer API
|
|
5
5
|
Home-page: https://github.com/ImpectAPI/impectPy
|
|
6
6
|
Author: Impect
|
|
@@ -25,9 +25,9 @@ Dynamic: summary
|
|
|
25
25
|
|
|
26
26
|
A package provided by: Impect GmbH
|
|
27
27
|
|
|
28
|
-
Version: v2.5.
|
|
28
|
+
Version: v2.5.10
|
|
29
29
|
|
|
30
|
-
**Updated: April
|
|
30
|
+
**Updated: April 13th 2026**
|
|
31
31
|
|
|
32
32
|
---
|
|
33
33
|
|
|
@@ -58,7 +58,7 @@ pip install impectPy
|
|
|
58
58
|
You can also install it from [GitHub](https://github.com/) with:
|
|
59
59
|
|
|
60
60
|
```cmd
|
|
61
|
-
pip install git+https://github.com/ImpectAPI/impectPy.git@v2.5.
|
|
61
|
+
pip install git+https://github.com/ImpectAPI/impectPy.git@v2.5.10
|
|
62
62
|
```
|
|
63
63
|
|
|
64
64
|
## Usage
|
|
@@ -302,6 +302,15 @@ calls made on the client side already. The rate limit is read from the first lim
|
|
|
302
302
|
policy sent back by the API, so if this limit increases over time, this package will
|
|
303
303
|
act accordingly.
|
|
304
304
|
|
|
305
|
+
### Generic API Call
|
|
306
|
+
You can also use this package to make individual API calls querying a specific endpoint
|
|
307
|
+
such as the "squads" endpoint.
|
|
308
|
+
|
|
309
|
+
```python
|
|
310
|
+
# query any IMPECT customer API endpoint
|
|
311
|
+
data = ip.getData(url=f"https://api.impect.com/v5/customerapi/iterations/{iteration}/squads", token=token)
|
|
312
|
+
```
|
|
313
|
+
|
|
305
314
|
### SportsCodeXML
|
|
306
315
|
|
|
307
316
|
It is also possible to convert a dataframe containing event data into an XML file,
|
|
@@ -449,6 +458,9 @@ squadIterationScores = api.getSquadIterationScores(iteration=iteration)
|
|
|
449
458
|
|
|
450
459
|
# get player profile scores
|
|
451
460
|
playerProfileScores = api.getPlayerProfileScores(iteration=iteration, positions=positions)
|
|
461
|
+
|
|
462
|
+
# query single API endpoint (e.g. squads for iteration 518)
|
|
463
|
+
data = api.getData(url=f"https://api.impect.com/v5/customerapi/iterations/{iteration}/squads")
|
|
452
464
|
```
|
|
453
465
|
|
|
454
466
|
## Final Notes
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
A package provided by: Impect GmbH
|
|
4
4
|
|
|
5
|
-
Version: v2.5.
|
|
5
|
+
Version: v2.5.10
|
|
6
6
|
|
|
7
|
-
**Updated: April
|
|
7
|
+
**Updated: April 13th 2026**
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
@@ -35,7 +35,7 @@ pip install impectPy
|
|
|
35
35
|
You can also install it from [GitHub](https://github.com/) with:
|
|
36
36
|
|
|
37
37
|
```cmd
|
|
38
|
-
pip install git+https://github.com/ImpectAPI/impectPy.git@v2.5.
|
|
38
|
+
pip install git+https://github.com/ImpectAPI/impectPy.git@v2.5.10
|
|
39
39
|
```
|
|
40
40
|
|
|
41
41
|
## Usage
|
|
@@ -279,6 +279,15 @@ calls made on the client side already. The rate limit is read from the first lim
|
|
|
279
279
|
policy sent back by the API, so if this limit increases over time, this package will
|
|
280
280
|
act accordingly.
|
|
281
281
|
|
|
282
|
+
### Generic API Call
|
|
283
|
+
You can also use this package to make individual API calls querying a specific endpoint
|
|
284
|
+
such as the "squads" endpoint.
|
|
285
|
+
|
|
286
|
+
```python
|
|
287
|
+
# query any IMPECT customer API endpoint
|
|
288
|
+
data = ip.getData(url=f"https://api.impect.com/v5/customerapi/iterations/{iteration}/squads", token=token)
|
|
289
|
+
```
|
|
290
|
+
|
|
282
291
|
### SportsCodeXML
|
|
283
292
|
|
|
284
293
|
It is also possible to convert a dataframe containing event data into an XML file,
|
|
@@ -426,9 +435,12 @@ squadIterationScores = api.getSquadIterationScores(iteration=iteration)
|
|
|
426
435
|
|
|
427
436
|
# get player profile scores
|
|
428
437
|
playerProfileScores = api.getPlayerProfileScores(iteration=iteration, positions=positions)
|
|
438
|
+
|
|
439
|
+
# query single API endpoint (e.g. squads for iteration 518)
|
|
440
|
+
data = api.getData(url=f"https://api.impect.com/v5/customerapi/iterations/{iteration}/squads")
|
|
429
441
|
```
|
|
430
442
|
|
|
431
443
|
## Final Notes
|
|
432
444
|
|
|
433
445
|
Further documentation on the data and explanations of variables can be
|
|
434
|
-
found in our [Glossary](https://glossary.impect.com/).
|
|
446
|
+
found in our [Glossary](https://glossary.impect.com/).
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# define version attribute
|
|
2
|
-
__version__ = "2.5.
|
|
2
|
+
__version__ = "2.5.10"
|
|
3
3
|
|
|
4
4
|
# import modules
|
|
5
5
|
from .access_token import getAccessToken
|
|
@@ -22,5 +22,6 @@ from .squad_coefficients import getSquadCoefficients
|
|
|
22
22
|
from .formations import getFormations
|
|
23
23
|
from .substitutions import getSubstitutions
|
|
24
24
|
from .starting_positions import getStartingPositions
|
|
25
|
+
from .data import getData
|
|
25
26
|
from .config import Config as Config
|
|
26
27
|
from .impect import Impect as Impect
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# load packages
|
|
2
|
+
import pandas as pd
|
|
3
|
+
from typing import Optional, Dict, Any
|
|
4
|
+
from impectPy.helpers import RateLimitedAPI, ImpectSession
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
######
|
|
8
|
+
#
|
|
9
|
+
# This function returns a dataframe from any Impect API endpoint
|
|
10
|
+
#
|
|
11
|
+
######
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def getData(
|
|
15
|
+
url: str, token: str, method: str = "GET", data: Optional[Dict[str, Any]] = None,
|
|
16
|
+
session: Optional[ImpectSession] = None
|
|
17
|
+
) -> pd.DataFrame:
|
|
18
|
+
"""Returns a processed DataFrame from any Impect API endpoint.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
url (str): Full URL of the API endpoint.
|
|
22
|
+
token (str): Bearer token for authentication.
|
|
23
|
+
method (str): HTTP method. Defaults to "GET".
|
|
24
|
+
data (Optional[Dict[str, Any]]): Optional request body. Defaults to None.
|
|
25
|
+
session (Optional[ImpectSession]): The session object to use for the API calls. Defaults to a new ImpectSession.
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
pd.DataFrame: Processed response data.
|
|
29
|
+
"""
|
|
30
|
+
# create an instance of RateLimitedAPI
|
|
31
|
+
connection = RateLimitedAPI(session or ImpectSession())
|
|
32
|
+
|
|
33
|
+
# set auth header
|
|
34
|
+
connection.session.headers.update({"Authorization": f"Bearer {token}"})
|
|
35
|
+
|
|
36
|
+
return getDataFromHost(url=url, method=method, connection=connection, data=data)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def getDataFromHost(
|
|
40
|
+
url: str, method: str, connection: RateLimitedAPI, data: Optional[Dict[str, Any]] = None
|
|
41
|
+
) -> pd.DataFrame:
|
|
42
|
+
"""Core implementation: executes a rate-limited API call and returns a processed DataFrame.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
url (str): Full URL of the API endpoint.
|
|
46
|
+
method (str): HTTP method.
|
|
47
|
+
connection (RateLimitedAPI): Authenticated connection object.
|
|
48
|
+
data (Optional[Dict[str, Any]]): Optional request body. Defaults to None.
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
pd.DataFrame: Processed response data.
|
|
52
|
+
"""
|
|
53
|
+
response = connection.make_api_request_limited(url=url, method=method, data=data)
|
|
54
|
+
return response.process_response(endpoint=url)
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
+
from typing import Optional, Dict, Any
|
|
2
|
+
from xml.etree import ElementTree as ET
|
|
3
|
+
|
|
4
|
+
import pandas as pd
|
|
5
|
+
|
|
1
6
|
from impectPy.config import Config
|
|
7
|
+
|
|
2
8
|
from .helpers import RateLimitedAPI
|
|
3
9
|
from .access_token import getAccessTokenFromUrl
|
|
4
10
|
from .iterations import getIterationsFromHost
|
|
@@ -20,8 +26,7 @@ from .squad_coefficients import getSquadCoefficientsFromHost
|
|
|
20
26
|
from .formations import getFormationsFromHost
|
|
21
27
|
from .substitutions import getSubstitutionsFromHost
|
|
22
28
|
from .starting_positions import getStartingPositionsFromHost
|
|
23
|
-
|
|
24
|
-
from xml.etree import ElementTree as ET
|
|
29
|
+
from .data import getDataFromHost
|
|
25
30
|
|
|
26
31
|
|
|
27
32
|
class Impect:
|
|
@@ -130,6 +135,26 @@ class Impect:
|
|
|
130
135
|
matches, self.connection, self.__config.HOST
|
|
131
136
|
)
|
|
132
137
|
|
|
138
|
+
def getData(
|
|
139
|
+
self, url: str, method: str = "GET", data: Optional[Dict[str, Any]] = None
|
|
140
|
+
) -> pd.DataFrame:
|
|
141
|
+
"""Returns a processed DataFrame from any Impect API endpoint.
|
|
142
|
+
|
|
143
|
+
Accepts a full URL or a path. If a path is provided (does not start
|
|
144
|
+
with 'http'), the configured host is prepended automatically.
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
url (str): Full URL or path of the API endpoint.
|
|
148
|
+
method (str): HTTP method. Defaults to "GET".
|
|
149
|
+
data (Optional[Dict[str, Any]]): Optional request body. Defaults to None.
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
pd.DataFrame: Processed response data.
|
|
153
|
+
"""
|
|
154
|
+
if not url.startswith("http"):
|
|
155
|
+
url = f"{self.__config.HOST}{url}"
|
|
156
|
+
return getDataFromHost(url=url, method=method, connection=self.connection, data=data)
|
|
157
|
+
|
|
133
158
|
@staticmethod
|
|
134
159
|
def generateXML(
|
|
135
160
|
events: pd.DataFrame,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: impectPy
|
|
3
|
-
Version: 2.5.
|
|
3
|
+
Version: 2.5.10
|
|
4
4
|
Summary: A Python package to facilitate interaction with the Impect customer API
|
|
5
5
|
Home-page: https://github.com/ImpectAPI/impectPy
|
|
6
6
|
Author: Impect
|
|
@@ -25,9 +25,9 @@ Dynamic: summary
|
|
|
25
25
|
|
|
26
26
|
A package provided by: Impect GmbH
|
|
27
27
|
|
|
28
|
-
Version: v2.5.
|
|
28
|
+
Version: v2.5.10
|
|
29
29
|
|
|
30
|
-
**Updated: April
|
|
30
|
+
**Updated: April 13th 2026**
|
|
31
31
|
|
|
32
32
|
---
|
|
33
33
|
|
|
@@ -58,7 +58,7 @@ pip install impectPy
|
|
|
58
58
|
You can also install it from [GitHub](https://github.com/) with:
|
|
59
59
|
|
|
60
60
|
```cmd
|
|
61
|
-
pip install git+https://github.com/ImpectAPI/impectPy.git@v2.5.
|
|
61
|
+
pip install git+https://github.com/ImpectAPI/impectPy.git@v2.5.10
|
|
62
62
|
```
|
|
63
63
|
|
|
64
64
|
## Usage
|
|
@@ -302,6 +302,15 @@ calls made on the client side already. The rate limit is read from the first lim
|
|
|
302
302
|
policy sent back by the API, so if this limit increases over time, this package will
|
|
303
303
|
act accordingly.
|
|
304
304
|
|
|
305
|
+
### Generic API Call
|
|
306
|
+
You can also use this package to make individual API calls querying a specific endpoint
|
|
307
|
+
such as the "squads" endpoint.
|
|
308
|
+
|
|
309
|
+
```python
|
|
310
|
+
# query any IMPECT customer API endpoint
|
|
311
|
+
data = ip.getData(url=f"https://api.impect.com/v5/customerapi/iterations/{iteration}/squads", token=token)
|
|
312
|
+
```
|
|
313
|
+
|
|
305
314
|
### SportsCodeXML
|
|
306
315
|
|
|
307
316
|
It is also possible to convert a dataframe containing event data into an XML file,
|
|
@@ -449,6 +458,9 @@ squadIterationScores = api.getSquadIterationScores(iteration=iteration)
|
|
|
449
458
|
|
|
450
459
|
# get player profile scores
|
|
451
460
|
playerProfileScores = api.getPlayerProfileScores(iteration=iteration, positions=positions)
|
|
461
|
+
|
|
462
|
+
# query single API endpoint (e.g. squads for iteration 518)
|
|
463
|
+
data = api.getData(url=f"https://api.impect.com/v5/customerapi/iterations/{iteration}/squads")
|
|
452
464
|
```
|
|
453
465
|
|
|
454
466
|
## Final Notes
|
|
@@ -4,6 +4,7 @@ setup.py
|
|
|
4
4
|
impectPy/__init__.py
|
|
5
5
|
impectPy/access_token.py
|
|
6
6
|
impectPy/config.py
|
|
7
|
+
impectPy/data.py
|
|
7
8
|
impectPy/events.py
|
|
8
9
|
impectPy/formations.py
|
|
9
10
|
impectPy/generate_xml.py
|
|
@@ -29,4 +30,5 @@ impectPy.egg-info/PKG-INFO
|
|
|
29
30
|
impectPy.egg-info/SOURCES.txt
|
|
30
31
|
impectPy.egg-info/dependency_links.txt
|
|
31
32
|
impectPy.egg-info/requires.txt
|
|
32
|
-
impectPy.egg-info/top_level.txt
|
|
33
|
+
impectPy.egg-info/top_level.txt
|
|
34
|
+
tests/test_package.py
|
|
@@ -17,7 +17,7 @@ setup(
|
|
|
17
17
|
"pandas>=2.2.0",
|
|
18
18
|
"numpy>=1.24.2"],
|
|
19
19
|
# *strongly* suggested for sharing
|
|
20
|
-
version="2.5.
|
|
20
|
+
version="2.5.10",
|
|
21
21
|
# The license can be anything you like
|
|
22
22
|
license="MIT",
|
|
23
23
|
description="A Python package to facilitate interaction with the Impect customer API",
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
# load packages
|
|
2
|
+
import sys
|
|
3
|
+
import importlib
|
|
4
|
+
import logging
|
|
5
|
+
import os
|
|
6
|
+
import re
|
|
7
|
+
import subprocess
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from tqdm import tqdm
|
|
10
|
+
from dotenv import load_dotenv
|
|
11
|
+
import pandas as pd
|
|
12
|
+
|
|
13
|
+
execute_functions = True
|
|
14
|
+
|
|
15
|
+
# branch comparison configuration
|
|
16
|
+
BRANCH_A_NAME = "main"
|
|
17
|
+
BRANCH_B_NAME = "release"
|
|
18
|
+
|
|
19
|
+
REPO_ROOT = Path(__file__).resolve().parents[1]
|
|
20
|
+
BRANCH_PATHS = {
|
|
21
|
+
BRANCH_A_NAME: REPO_ROOT,
|
|
22
|
+
BRANCH_B_NAME: REPO_ROOT,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def checkout_branch(branch_name: str):
|
|
27
|
+
repo_path = BRANCH_PATHS.get(branch_name)
|
|
28
|
+
if repo_path is None or not repo_path.exists():
|
|
29
|
+
raise ValueError(f"Unknown branch or missing repo path: '{branch_name}'")
|
|
30
|
+
|
|
31
|
+
try:
|
|
32
|
+
subprocess.run(
|
|
33
|
+
["git", "-C", str(repo_path), "fetch"],
|
|
34
|
+
check=True,
|
|
35
|
+
stdout=subprocess.DEVNULL
|
|
36
|
+
)
|
|
37
|
+
subprocess.run(
|
|
38
|
+
["git", "-C", str(repo_path), "checkout", branch_name],
|
|
39
|
+
check=True,
|
|
40
|
+
stdout=subprocess.DEVNULL
|
|
41
|
+
)
|
|
42
|
+
except subprocess.CalledProcessError as e:
|
|
43
|
+
print(f"Error checking out branch '{branch_name}' in {repo_path}: {e}")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def load_impect(env: str):
|
|
47
|
+
modules_to_remove = [m for m in sys.modules if m.startswith("impectPy")]
|
|
48
|
+
|
|
49
|
+
for module_name in modules_to_remove:
|
|
50
|
+
del sys.modules[module_name]
|
|
51
|
+
|
|
52
|
+
sys.path = [p for p in sys.path if "impectPy" not in p]
|
|
53
|
+
|
|
54
|
+
importlib.invalidate_caches()
|
|
55
|
+
|
|
56
|
+
repo_root = BRANCH_PATHS.get(env)
|
|
57
|
+
if repo_root is None:
|
|
58
|
+
raise ValueError(f"Unknown environment '{env}'. Check BRANCH_PATHS.")
|
|
59
|
+
|
|
60
|
+
sys.path.insert(0, str(repo_root))
|
|
61
|
+
return importlib.import_module("impectPy")
|
|
62
|
+
|
|
63
|
+
if execute_functions:
|
|
64
|
+
|
|
65
|
+
# define login credentials
|
|
66
|
+
load_dotenv()
|
|
67
|
+
username = os.getenv("USERNAME")
|
|
68
|
+
password = os.getenv("PASSWORD")
|
|
69
|
+
|
|
70
|
+
# define logger
|
|
71
|
+
logging.basicConfig(
|
|
72
|
+
level=logging.ERROR,
|
|
73
|
+
format="%(asctime)s - %(name)s - %(levelname)s - ID=%(id)s - URL=%(url)s - %(message)s"
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# define object to be passed onto functions
|
|
77
|
+
iteration = 1385
|
|
78
|
+
|
|
79
|
+
matches = [
|
|
80
|
+
232434,
|
|
81
|
+
202485
|
|
82
|
+
]
|
|
83
|
+
|
|
84
|
+
positions = [
|
|
85
|
+
"GOALKEEPER",
|
|
86
|
+
"LEFT_WINGBACK_DEFENDER",
|
|
87
|
+
"RIGHT_WINGBACK_DEFENDER",
|
|
88
|
+
"CENTRAL_DEFENDER",
|
|
89
|
+
"DEFENSE_MIDFIELD",
|
|
90
|
+
"CENTRAL_MIDFIELD",
|
|
91
|
+
"ATTACKING_MIDFIELD",
|
|
92
|
+
"LEFT_WINGER",
|
|
93
|
+
"RIGHT_WINGER",
|
|
94
|
+
"CENTER_FORWARD"
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
# define branches
|
|
98
|
+
branches = [BRANCH_A_NAME, BRANCH_B_NAME]
|
|
99
|
+
|
|
100
|
+
# iterate over envs
|
|
101
|
+
for branch in branches:
|
|
102
|
+
|
|
103
|
+
os.makedirs(f"files/{branch}", exist_ok=True)
|
|
104
|
+
|
|
105
|
+
checkout_branch(branch)
|
|
106
|
+
impectPy = load_impect(branch)
|
|
107
|
+
print(f"impectPy Version: {impectPy.__version__}")
|
|
108
|
+
|
|
109
|
+
config = impectPy.Config()
|
|
110
|
+
api = impectPy.Impect(config=config)
|
|
111
|
+
api.login(username, password)
|
|
112
|
+
|
|
113
|
+
with tqdm(total=21, desc=f"{branch}: Executing functions...", unit="chunk") as pbar:
|
|
114
|
+
|
|
115
|
+
# get iterations
|
|
116
|
+
iterations = api.getIterations()
|
|
117
|
+
iterations.to_csv(f"files/{branch}/iterations.csv")
|
|
118
|
+
pbar.update()
|
|
119
|
+
|
|
120
|
+
# get squad ratings
|
|
121
|
+
ratings = api.getSquadRatings(iteration)
|
|
122
|
+
ratings.to_csv(f"files/{branch}/ratings.csv")
|
|
123
|
+
pbar.update()
|
|
124
|
+
|
|
125
|
+
# get squad coefficients
|
|
126
|
+
coefficients = api.getSquadCoefficients(iteration)
|
|
127
|
+
coefficients.to_csv(f"files/{branch}/coefficients.csv")
|
|
128
|
+
pbar.update()
|
|
129
|
+
|
|
130
|
+
# get matches
|
|
131
|
+
matchplan = api.getMatches(iteration)
|
|
132
|
+
matchplan.to_csv(f"files/{branch}/matchplan.csv")
|
|
133
|
+
pbar.update()
|
|
134
|
+
|
|
135
|
+
# get match info
|
|
136
|
+
formations = api.getFormations(matches)
|
|
137
|
+
formations.to_csv(f"files/{branch}/formations.csv")
|
|
138
|
+
pbar.update()
|
|
139
|
+
substitutions = api.getSubstitutions(matches)
|
|
140
|
+
substitutions.to_csv(f"files/{branch}/substitutions.csv")
|
|
141
|
+
pbar.update()
|
|
142
|
+
startingPositions = api.getStartingPositions(matches)
|
|
143
|
+
startingPositions.to_csv(f"files/{branch}/startingPositions.csv")
|
|
144
|
+
pbar.update()
|
|
145
|
+
|
|
146
|
+
# get match events
|
|
147
|
+
events = api.getEvents(matches, include_kpis=True, include_set_pieces=True)
|
|
148
|
+
events.to_csv(f"files/{branch}/events.csv")
|
|
149
|
+
pbar.update()
|
|
150
|
+
|
|
151
|
+
# get set pieces
|
|
152
|
+
set_pieces = api.getSetPieces(matches)
|
|
153
|
+
set_pieces.to_csv(f"files/{branch}/set_pieces.csv")
|
|
154
|
+
pbar.update()
|
|
155
|
+
|
|
156
|
+
# get player iteration averages
|
|
157
|
+
playerIterationAverages = api.getPlayerIterationAverages(iteration)
|
|
158
|
+
playerIterationAverages.to_csv(f"files/{branch}/playerIterationAverages.csv")
|
|
159
|
+
pbar.update()
|
|
160
|
+
|
|
161
|
+
# get player matchsums
|
|
162
|
+
playerMatchsums = api.getPlayerMatchsums(matches)
|
|
163
|
+
playerMatchsums.to_csv(f"files/{branch}/playerMatchsums.csv")
|
|
164
|
+
pbar.update()
|
|
165
|
+
|
|
166
|
+
# get squad iteration averages
|
|
167
|
+
squadIterationAverages = api.getSquadIterationAverages(iteration)
|
|
168
|
+
squadIterationAverages.to_csv(f"files/{branch}/squadIterationAverages.csv")
|
|
169
|
+
pbar.update()
|
|
170
|
+
|
|
171
|
+
# get squad matchsums
|
|
172
|
+
squadMatchsums = api.getSquadMatchsums(matches)
|
|
173
|
+
squadMatchsums.to_csv(f"files/{branch}/squadMatchsums.csv")
|
|
174
|
+
pbar.update()
|
|
175
|
+
|
|
176
|
+
# get player match scores
|
|
177
|
+
playerMatchScores = api.getPlayerMatchScores(matches)
|
|
178
|
+
playerMatchScores.to_csv(f"files/{branch}/playerMatchScores.csv")
|
|
179
|
+
pbar.update()
|
|
180
|
+
playerMatchScores_2 = api.getPlayerMatchScores(matches, positions)
|
|
181
|
+
playerMatchScores_2.to_csv(f"files/{branch}/playerMatchScores_2.csv")
|
|
182
|
+
pbar.update()
|
|
183
|
+
|
|
184
|
+
# get squad match scores
|
|
185
|
+
squadMatchScores = api.getSquadMatchScores(matches)
|
|
186
|
+
squadMatchScores.to_csv(f"files/{branch}/squadMatchScores.csv")
|
|
187
|
+
pbar.update()
|
|
188
|
+
|
|
189
|
+
# get player iteration scores
|
|
190
|
+
playerIterationScores = api.getPlayerIterationScores(iteration)
|
|
191
|
+
playerIterationScores.to_csv(f"files/{branch}/playerIterationScores.csv")
|
|
192
|
+
pbar.update()
|
|
193
|
+
playerIterationScores_2 = api.getPlayerIterationScores(iteration, positions)
|
|
194
|
+
playerIterationScores_2.to_csv(f"files/{branch}/playerIterationScores_2.csv")
|
|
195
|
+
pbar.update()
|
|
196
|
+
|
|
197
|
+
# get squad iteration scores
|
|
198
|
+
squadIterationScores = api.getSquadIterationScores(iteration)
|
|
199
|
+
squadIterationScores.to_csv(f"files/{branch}/squadIterationScores.csv")
|
|
200
|
+
pbar.update()
|
|
201
|
+
|
|
202
|
+
# get player profile scores
|
|
203
|
+
playerProfileScores = api.getPlayerProfileScores(iteration, positions)
|
|
204
|
+
playerProfileScores.to_csv(f"files/{branch}/playerProfileScores.csv")
|
|
205
|
+
pbar.update()
|
|
206
|
+
|
|
207
|
+
# # get data using generic getData() method
|
|
208
|
+
# pbar.set_description("Get data using generic getData() method...")
|
|
209
|
+
# data = api.getData(f"https://api.impect.com/v5/customerapi/iterations/{iteration}/players")
|
|
210
|
+
# pbar.update()
|
|
211
|
+
|
|
212
|
+
print("\nRunning auto-diff between source and test outputs...\n")
|
|
213
|
+
|
|
214
|
+
base_path = Path(__file__).parent / "files"
|
|
215
|
+
source_path = base_path / BRANCH_A_NAME
|
|
216
|
+
test_path = base_path / BRANCH_B_NAME
|
|
217
|
+
|
|
218
|
+
excluded_columns = [
|
|
219
|
+
# "playerCountry"
|
|
220
|
+
]
|
|
221
|
+
|
|
222
|
+
failed = False
|
|
223
|
+
assertion_error_patterns = {
|
|
224
|
+
"column": re.compile(r'column name="([^"]+)"'),
|
|
225
|
+
"percentage": re.compile(r'values are different \(([\d.]+)\s*%\)'),
|
|
226
|
+
"first_diff": re.compile(
|
|
227
|
+
r'At positional index (\d+), first diff:\s*(.*?)\s*!=\s*(.*)'
|
|
228
|
+
),
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
for src_file in source_path.glob("*.csv"):
|
|
232
|
+
test_file = test_path / src_file.name
|
|
233
|
+
|
|
234
|
+
if not test_file.exists():
|
|
235
|
+
print(f"[MISSING] {src_file.name} not found in test/")
|
|
236
|
+
failed = True
|
|
237
|
+
continue
|
|
238
|
+
|
|
239
|
+
src_df = pd.read_csv(src_file, low_memory=False)
|
|
240
|
+
test_df = pd.read_csv(test_file, low_memory=False)
|
|
241
|
+
|
|
242
|
+
src_df = src_df[[col for col in src_df.columns if col not in excluded_columns]]
|
|
243
|
+
test_df = test_df[[col for col in test_df.columns if col not in excluded_columns]]
|
|
244
|
+
|
|
245
|
+
try:
|
|
246
|
+
pd.testing.assert_frame_equal(
|
|
247
|
+
src_df,
|
|
248
|
+
test_df,
|
|
249
|
+
check_dtype=False,
|
|
250
|
+
check_like=True
|
|
251
|
+
)
|
|
252
|
+
print(f"[OK] {src_file.name}")
|
|
253
|
+
except AssertionError as e:
|
|
254
|
+
parsed_error = str(e)
|
|
255
|
+
|
|
256
|
+
column = assertion_error_patterns["column"].search(parsed_error).group(1)
|
|
257
|
+
percentage = float(assertion_error_patterns["percentage"].search(parsed_error).group(1))
|
|
258
|
+
|
|
259
|
+
first_diff_match = assertion_error_patterns["first_diff"].search(parsed_error)
|
|
260
|
+
if first_diff_match:
|
|
261
|
+
first_diff = {
|
|
262
|
+
"index": int(first_diff_match.group(1)),
|
|
263
|
+
"left": first_diff_match.group(2),
|
|
264
|
+
"right": first_diff_match.group(3),
|
|
265
|
+
}
|
|
266
|
+
else:
|
|
267
|
+
# pandas uses [index]/[left]/[right] array format when all values differ
|
|
268
|
+
idx_match = re.search(r'\[index\]:\s*\[([^]]+)', parsed_error)
|
|
269
|
+
left_match = re.search(r'\[left\]:\s*\[([^]]+)', parsed_error)
|
|
270
|
+
right_match = re.search(r'\[right\]:\s*\[([^]]+)', parsed_error)
|
|
271
|
+
first_diff = {
|
|
272
|
+
"index": idx_match.group(1).split(",")[0].strip() if idx_match else "?",
|
|
273
|
+
"left": left_match.group(1).split(",")[0].strip() if left_match else "?",
|
|
274
|
+
"right": right_match.group(1).split(",")[0].strip() if right_match else "?",
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
result = {
|
|
278
|
+
"column": column,
|
|
279
|
+
"percentage_diff": percentage,
|
|
280
|
+
"first_diff": first_diff,
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
print(f"[DIFF] {src_file.name} : Column '{result['column']}‘ (Delta: {result['percentage_diff']:.2f}%)') : "
|
|
284
|
+
f"First Diff at index {result['first_diff']['index']} "
|
|
285
|
+
f"({result['first_diff']['left']} != {result['first_diff']['right']})")
|
|
286
|
+
failed = True
|
|
287
|
+
|
|
288
|
+
if failed:
|
|
289
|
+
raise AssertionError("Auto-diff failed: source and test outputs differ")
|
|
290
|
+
|
|
291
|
+
print("\nAll source vs test outputs are identical ✅")
|
|
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
|