Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
0135dfb
Fix fps key handling in secondspectrum.load function
WoutPaepenUcLL Feb 21, 2025
f8a382c
Merge pull request #1 from WoutPaepenUcLL/fix-fps-key
WoutPaepenUcLL Feb 21, 2025
9a93979
Update secondspectrum.py
WoutPaepenUcLL Feb 21, 2025
ecdcf73
Update secondspectrum load function to support current metadata.json …
WoutPaepenUcLL Feb 21, 2025
067e451
Merge pull request #2 from WoutPaepenUcLL/update-secondspectrum-load
WoutPaepenUcLL Feb 21, 2025
0f5531c
Update secondspectrum.py
WoutPaepenUcLL Feb 26, 2025
ea3e5e9
Fix metadata handling in SecondSpectrumDeserializer and update game_i…
WoutPaepenUcLL Feb 26, 2025
28cbf93
Add event data loading functionality and update imports
WoutPaepenUcLL Feb 28, 2025
6fafe10
Add DeflectionEvent and DeflectionResult classes; update event factor…
WoutPaepenUcLL Mar 3, 2025
9fa3d52
Update .gitignore, refactor event factory, and improve serializer for…
WoutPaepenUcLL Mar 3, 2025
1e3a888
Merge pull request #5 from WoutPaepenUcLL:add-event-data-secondspectrum
WoutPaepenUcLL Mar 3, 2025
c3c1712
Add sample event data for testing in secondspectrum_fake_eventdata.jsonl
WoutPaepenUcLL Mar 10, 2025
5387669
Fix game_id retrieval in SecondSpectrumDeserializer and add comprehen…
WoutPaepenUcLL Mar 11, 2025
db366a3
Merge branch 'master' into add-event-data-secondspectrum
WoutPaepenUcLL Mar 11, 2025
8d81a2c
Merge branch 'PySport:master' into master
WoutPaepenUcLL Mar 11, 2025
473defd
Merge branch 'master' into add-event-data-secondspectrum
WoutPaepenUcLL Mar 11, 2025
04f3377
Merge pull request #6 from WoutPaepenUcLL/add-event-data-secondspectrum
WoutPaepenUcLL Mar 11, 2025
3a9e826
Remove accidentally created test directory
WoutPaepenUcLL Mar 11, 2025
5a79228
run black for formatting
WoutPaepenUcLL Mar 13, 2025
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
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -155,3 +155,9 @@ examples/pattern_matching/repository/*.json
!.gitignore

scratchpad

event.jsonl
Pro League - Dragon_Events_Feed_Spec_v0.2.pdf


events.jsonl
1 change: 1 addition & 0 deletions docs/create-notebook.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Creates a minimal jupyter notebook (.ipynb)
Usage: create-notebook <notebook>
"""

import sys
from notebook import transutils as _
from notebook.services.contents.filemanager import FileContentsManager as FCM
Expand Down
41 changes: 40 additions & 1 deletion kloppy/_providers/secondspectrum.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
from typing import Optional

from kloppy.domain import TrackingDataset
from kloppy.domain import TrackingDataset, EventDataset, EventFactory
from kloppy.infra.serializers.tracking.secondspectrum import (
SecondSpectrumDeserializer,
SecondSpectrumInputs,
)
from kloppy.infra.serializers.event.secondspectrum import (
SecondSpectrumEventDataDeserializer,
SecondSpectrumEventDataInputs,
)
from kloppy.io import FileLike, open_as_file, Source


Expand Down Expand Up @@ -35,3 +39,38 @@ def load(
additional_meta_data=additional_meta_data_fp,
)
)


def load_event_data(
meta_data: FileLike,
event_data: FileLike,
coordinates: Optional[str] = None,
) -> EventDataset:
"""Load SecondSpectrum event data.

Parameters
----------
meta_data: str
Path to metadata json file
event_data: str
Path to event data json file
coordinates: str, optional
Coordinate system to transform the coordinates to

Returns
-------
EventDataset
"""
deserializer = SecondSpectrumEventDataDeserializer(
coordinate_system=coordinates
)
with open_as_file(meta_data) as meta_data_fp, open_as_file(
event_data
) as event_data_fp:
return deserializer.deserialize(
inputs=SecondSpectrumEventDataInputs(
meta_data=meta_data_fp,
event_data=event_data_fp,
additional_meta_data=None,
)
)
18 changes: 8 additions & 10 deletions kloppy/domain/models/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -1252,9 +1252,9 @@ def __post_init__(self):
for i, period in enumerate(self.periods):
period.set_refs(
prev=self.periods[i - 1] if i > 0 else None,
next_=self.periods[i + 1]
if i + 1 < len(self.periods)
else None,
next_=(
self.periods[i + 1] if i + 1 < len(self.periods) else None
),
)


Expand Down Expand Up @@ -1291,9 +1291,9 @@ def __post_init__(self):
record.set_refs(
dataset=self,
prev=self.records[i - 1] if i > 0 else None,
next_=self.records[i + 1]
if i + 1 < len(self.records)
else None,
next_=(
self.records[i + 1] if i + 1 < len(self.records) else None
),
)

self._init_player_positions()
Expand Down Expand Up @@ -1412,17 +1412,15 @@ def to_records(
*columns: "Column",
as_list: Literal[True] = True,
**named_columns: "Column",
) -> List[Dict[str, Any]]:
...
) -> List[Dict[str, Any]]: ...

@overload
def to_records(
self,
*columns: "Column",
as_list: Literal[False] = False,
**named_columns: "Column",
) -> Iterable[Dict[str, Any]]:
...
) -> Iterable[Dict[str, Any]]: ...

def to_records(
self,
Expand Down
42 changes: 42 additions & 0 deletions kloppy/domain/models/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,26 @@ class CardType(Enum):
RED = "RED"


class DeflectionResult(ResultType):
"""
DeflectionResult

Attributes:
SUCCESS (DeflectionResult): Deflection successfully cleared the ball
FAILED (DeflectionResult): Deflection did not successfully clear the ball
"""

SUCCESS = "SUCCESS"
FAILED = "FAILED"

@property
def is_success(self):
"""
Returns if the deflection was successful
"""
return self == self.SUCCESS


class EventType(Enum):
"""
Attributes:
Expand All @@ -222,6 +242,7 @@ class EventType(Enum):
GOALKEEPER (EventType):
PRESSURE (EventType):
FORMATION_CHANGE (EventType):
DEFLLECTION (EventType):
"""

GENERIC = "generic"
Expand All @@ -244,6 +265,7 @@ class EventType(Enum):
GOALKEEPER = "GOALKEEPER"
PRESSURE = "PRESSURE"
FORMATION_CHANGE = "FORMATION_CHANGE"
DEFLECTION = "DEFLLECTION"

def __repr__(self):
return self.value
Expand Down Expand Up @@ -1052,6 +1074,23 @@ class PressureEvent(Event):
event_name: str = "pressure"


@dataclass(repr=False)
@docstring_inherit_attributes(Event)
class DeflectionEvent(Event):
"""
DeflectionEvent

Attributes:
event_type (EventType): `EventType.DEFLECTION` (See [`EventType`][kloppy.domain.models.event.EventType])
event_name (str): `"deflection"`
"""

result: DeflectionResult

event_type: EventType = EventType.DEFLECTION
event_name: str = "deflection"


@dataclass(repr=False)
class EventDataset(Dataset[Event]):
"""
Expand Down Expand Up @@ -1240,4 +1279,7 @@ def aggregate(self, type_: str, **aggregator_kwargs) -> List[Any]:
"DuelType",
"DuelQualifier",
"DuelResult",
"PressureEvent",
"DeflectionEvent",
"DeflectionResult",
]
68 changes: 41 additions & 27 deletions kloppy/domain/models/pitch.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,27 +168,37 @@ def convert(self, to_unit: Unit) -> "PitchDimensions":
"""
return PitchDimensions(
x_dim=Dimension(
min=self.unit.convert(to_unit, self.x_dim.min)
if self.x_dim.min is not None
else None,
max=self.unit.convert(to_unit, self.x_dim.max)
if self.x_dim.max is not None
else None,
min=(
self.unit.convert(to_unit, self.x_dim.min)
if self.x_dim.min is not None
else None
),
max=(
self.unit.convert(to_unit, self.x_dim.max)
if self.x_dim.max is not None
else None
),
),
y_dim=Dimension(
min=self.unit.convert(to_unit, self.y_dim.min)
if self.y_dim.min is not None
else None,
max=self.unit.convert(to_unit, self.y_dim.max)
if self.y_dim.max is not None
else None,
min=(
self.unit.convert(to_unit, self.y_dim.min)
if self.y_dim.min is not None
else None
),
max=(
self.unit.convert(to_unit, self.y_dim.max)
if self.y_dim.max is not None
else None
),
),
standardized=self.standardized,
unit=to_unit,
goal_width=self.unit.convert(to_unit, self.goal_width),
goal_height=self.unit.convert(to_unit, self.goal_height)
if self.goal_height is not None
else None,
goal_height=(
self.unit.convert(to_unit, self.goal_height)
if self.goal_height is not None
else None
),
six_yard_width=self.unit.convert(to_unit, self.six_yard_width),
six_yard_length=self.unit.convert(to_unit, self.six_yard_length),
penalty_area_width=self.unit.convert(
Expand Down Expand Up @@ -356,12 +366,14 @@ def transform(v, from_zones, from_length, ifab_zones, ifab_length):
pitch_width,
),
z=(
point.z * 2.44 / self.goal_height
if self.goal_height is not None
else point.z
)
if point.z is not None
else None,
(
point.z * 2.44 / self.goal_height
if self.goal_height is not None
else point.z
)
if point.z is not None
else None
),
)
else:
return Point(
Expand Down Expand Up @@ -470,12 +482,14 @@ def transform(v, to_zones, to_length, ifab_zones, ifab_length):
pitch_width,
),
z=(
point.z * self.goal_height / 2.44
if self.goal_height is not None
else point.z
)
if point.z is not None
else None,
(
point.z * self.goal_height / 2.44
if self.goal_height is not None
else point.z
)
if point.z is not None
else None
),
)
else:
return Point(
Expand Down
6 changes: 2 additions & 4 deletions kloppy/domain/models/time.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,10 @@ def from_period(
)

@overload
def __sub__(self, other: timedelta) -> "Time":
...
def __sub__(self, other: timedelta) -> "Time": ...

@overload
def __sub__(self, other: "Time") -> timedelta:
...
def __sub__(self, other: "Time") -> timedelta: ...

def __sub__(
self, other: Union["Time", timedelta]
Expand Down
5 changes: 5 additions & 0 deletions kloppy/domain/services/event_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
CardEvent,
SubstitutionEvent,
GoalkeeperEvent,
PressureEvent,
DeflectionEvent,
)
from kloppy.domain.models.event import PressureEvent

Expand Down Expand Up @@ -131,3 +133,6 @@ def build_goalkeeper_event(self, **kwargs) -> GoalkeeperEvent:

def build_pressure_event(self, **kwargs) -> PressureEvent:
return create_event(PressureEvent, **kwargs)

def build_deflection(self, **kwargs) -> DeflectionEvent:
return create_event(DeflectionEvent, **kwargs)
6 changes: 3 additions & 3 deletions kloppy/domain/services/state_builder/registered.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ def __new__(mcs, cls_name, bases, class_dict):
class_dict["name"] = name
builder_cls = super().__new__(mcs, cls_name, bases, class_dict)
if not inspect.isabstract(builder_cls):
_STATE_BUILDER_REGISTRY[
name.replace("_state_builder", "")
] = builder_cls
_STATE_BUILDER_REGISTRY[name.replace("_state_builder", "")] = (
builder_cls
)
return builder_cls


Expand Down
Loading
Loading