Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion impectPy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from .player_scores import getPlayerMatchScores, getPlayerIterationScores
from .squad_scores import getSquadMatchScores, getSquadIterationScores
from .player_profile_scores import getPlayerProfileScores
from .xml import generateXML
from .generate_xml import generateXML
from .set_pieces import getSetPieces
from .squad_ratings import getSquadRatings
from .match_info import getFormations, getSubstitutions, getStartingPositions
Expand Down
70 changes: 57 additions & 13 deletions impectPy/xml.py → impectPy/generate_xml.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
#
######

#define allowed KPIs, labels and codes
# define allowed KPIs, labels and codes
allowed_labels = [
{"order": "00 | ", "name": "eventId"},
{"order": "01 | ", "name": "matchId"},
Expand Down Expand Up @@ -69,11 +69,16 @@

allowed_codes = [
"playerName",
"squadName",
"team",
"actionType",
"action"
]

allowed_perspectives = [
"teamName",
"teamFocus"
]

# define allowed label/code combinations
combinations = {
"eventId": {"playerName": True, "team": False, "action": True, "actionType": True},
Expand Down Expand Up @@ -125,18 +130,43 @@ def generateXML(
p4Start: int,
p5Start: int,
codeTag: str,
squad: None,
perspective: None,
labels=None,
kpis=None,
labelSorting: bool = True,
sequencing: bool = True,
buckets: bool = True
) -> ET.ElementTree:

# handle kpis and labels defaults
# periodId check
period_start_times = {
1: p1Start,
2: p2Start,
3: p3Start,
4: p4Start,
5: p5Start
}
existing_period_ids = events['periodId'].unique()
# Validate the start time for each period that exists in the data
for periodId in existing_period_ids:
if periodId in period_start_times:
start_time = period_start_times[periodId]
# Verify the start time is an integer and within the valid range
if not isinstance(start_time, int) or not start_time > 0:
raise ValueError(
f"Invalid start time for periodId {periodId}. "
f"A valid integer is required, but got: '{start_time}'."
)

# handle kpis, labels, squad and perspective defaults
if labels is None:
labels = [label["name"] for label in allowed_labels if combinations.get(label.get("name")).get(codeTag)]
if kpis is None:
kpis = [kpi["name"] for kpi in allowed_kpis]
if squad is None or perspective is None:
perspective = "teamName"
elif squad not in events["squadId"].unique():
raise ValueError(f"Provided squad ID '{squad}' not found in event data.")

# check for invalid kpis
invalid_kpis = [kpi for kpi in kpis if kpi not in [kpi["name"] for kpi in allowed_kpis]]
Expand All @@ -152,14 +182,18 @@ def generateXML(
if not codeTag in allowed_codes:
raise ValueError(f"Invalid Code: {codeTag}")

if not perspective in allowed_perspectives:
raise ValueError(f"Invalid perspective: {perspective}")

# keep only :
# - if KPI in kpis
# - if Label in labels
# - if code matches legend
labels_and_kpis = []
invalid_labels = []
for label in allowed_labels:
if label.get("name") in labels and label.get("name") != codeTag: # ensure code attribute is not repeated as a label
if label.get("name") in labels and label.get(
"name") != codeTag: # ensure code attribute is not repeated as a label
if combinations.get(label.get("name")).get(codeTag):
labels_and_kpis.append(label)
else:
Expand All @@ -177,7 +211,6 @@ def generateXML(
if labelSorting:
labels_and_kpis = sorted(labels_and_kpis, key=lambda x: x["order"])


# compile periods start times into dict
offsets = {
"p1": p1Start,
Expand Down Expand Up @@ -646,20 +679,31 @@ def generateXML(
# reset index
phases.reset_index(inplace=True)

# merge phase and squadName into one column to later pass into code tag
phases["teamPhase"] = phases["squadName"] + " - " + phases["phase"].str.replace("_", " ")
# Determine how to label team phases: by squadName or by role (home/away)

if perspective == "teamName":
phases["teamPhase"] = phases["squadName"] + " - " + phases["phase"].str.replace("_", " ")
elif perspective == "teamFocus":
my_squad_id = squad
phases["teamPhase"] = np.where(
phases["squadId"] == my_squad_id,
"mySquad - " + phases["phase"].str.replace("_", " "),
"opponent - " + phases["phase"].str.replace("_", " ")
)
else:
raise ValueError(f"Invalid value for perspective: {perspective}")

# get period starts

# filter for kick off events of each period
kickoffs = events.copy()[
(events.actionType == "KICK_OFF") & ((events.gameTimeInSec - (events.periodId - 1) * 10000) < 10)
].reset_index()
].reset_index()

# check for penalty shootout
penalty_shootout = events.copy()[
events.periodId == 5
]
]

# add row for start of penalty shootout
if len(penalty_shootout) > 0:
Expand Down Expand Up @@ -786,7 +830,7 @@ def get_bucket(bucket, value, zero_value, error_value):
seq_id_current = None

# If the selected code attribute is "squadName", generate XML entries from the `phases` DataFrame
if codeTag == "squadName":
if codeTag == "team":
for index, phase in phases.iterrows():
# Create a new XML instance for each team phase
instance = ET.SubElement(instances, "instance")
Expand Down Expand Up @@ -873,7 +917,7 @@ def get_bucket(bucket, value, zero_value, error_value):
seq_id_current = seq_id_new
else:
# Same logic as above, but without sequencing (i.e., one clip per row)
if codeTag == "squadName":
if codeTag == "team":
for index, phase in phases.iterrows():
instance = ET.SubElement(instances, "instance")
event_id = ET.SubElement(instance, "ID")
Expand Down Expand Up @@ -968,7 +1012,7 @@ def row(value, colors):
# call function
row(player, home_colors)

elif codeTag == "squadName":
elif codeTag == "team":
# add entries for away team phases
for phase in away_phases:
# call function
Expand Down
4 changes: 2 additions & 2 deletions impectPy/impect.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@
from .player_scores import getPlayerMatchScoresFromHost, getPlayerIterationScoresFromHost
from .squad_scores import getSquadMatchScoresFromHost, getSquadIterationScoresFromHost
from .player_profile_scores import getPlayerProfileScoresFromHost
from .xml import generateXML
from .generate_xml import generateXML
from .set_pieces import getSetPiecesFromHost
from .squad_ratings import getSquadRatingsFromHost
from .match_info import getFormationsFromHost, getSubstitutionsFromHost, getStartingPositionsFromHost
import pandas as pd
from xml.etree import ElementTree as ET
from generate_xml.etree import ElementTree as ET


class Impect:
Expand Down