Skip to content

Commit

Permalink
Merge branch 'master' into feature/add-keeper-actions
Browse files Browse the repository at this point in the history
  • Loading branch information
koenvo authored Sep 18, 2023
2 parents 6e5bdf7 + 8b4b5a1 commit bcf08da
Show file tree
Hide file tree
Showing 11 changed files with 59 additions and 9 deletions.
17 changes: 17 additions & 0 deletions kloppy/domain/models/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ class EventType(Enum):
PLAYER_ON (EventType):
PLAYER_OFF (EventType):
RECOVERY (EventType):
MISCONTROL (EventType):
BALL_OUT (EventType):
FOUL_COMMITTED (EventType):
GOALKEEPER (EventType):
Expand All @@ -205,6 +206,7 @@ class EventType(Enum):
PLAYER_ON = "PLAYER_ON"
PLAYER_OFF = "PLAYER_OFF"
RECOVERY = "RECOVERY"
MISCONTROL = "MISCONTROL"
BALL_OUT = "BALL_OUT"
FOUL_COMMITTED = "FOUL_COMMITTED"
GOALKEEPER = "GOALKEEPER"
Expand Down Expand Up @@ -911,6 +913,20 @@ class BallOutEvent(Event):
event_name: str = "ball_out"


@dataclass(repr=False)
@docstring_inherit_attributes(Event)
class MiscontrolEvent(Event):
"""
MiscontrolEvent
Attributes:
event_type (EventType): `EventType.MISCONTROL` (See [`EventType`][kloppy.domain.models.event.EventType])
event_name (str): "miscontrol"
"""

event_type: EventType = EventType.MISCONTROL
event_name: str = "miscontrol"


@dataclass(repr=False)
@docstring_inherit_attributes(Event)
class FoulCommittedEvent(Event):
Expand Down Expand Up @@ -1038,6 +1054,7 @@ def generic_record_converter(event: Event):
"FormationChangeEvent",
"EventDataset",
"RecoveryEvent",
"MiscontrolEvent",
"FoulCommittedEvent",
"BallOutEvent",
"SetPieceType",
Expand Down
4 changes: 4 additions & 0 deletions kloppy/domain/services/event_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
GenericEvent,
TakeOnEvent,
RecoveryEvent,
MiscontrolEvent,
CarryEvent,
DuelEvent,
ClearanceEvent,
Expand Down Expand Up @@ -79,6 +80,9 @@ def build_generic(self, **kwargs) -> GenericEvent:
def build_recovery(self, **kwargs) -> RecoveryEvent:
return create_event(RecoveryEvent, **kwargs)

def build_miscontrol(self, **kwargs) -> MiscontrolEvent:
return create_event(MiscontrolEvent, **kwargs)

def build_take_on(self, **kwargs) -> TakeOnEvent:
return create_event(TakeOnEvent, **kwargs)

Expand Down
6 changes: 6 additions & 0 deletions kloppy/infra/serializers/event/opta/deserializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
EVENT_TYPE_CARD = 17
EVENT_TYPE_RECOVERY = 49
EVENT_TYPE_FORMATION_CHANGE = 40
EVENT_TYPE_BALL_TOUCH = 61

EVENT_TYPE_SAVE = 10
EVENT_TYPE_CLAIM = 11
Expand All @@ -100,6 +101,7 @@
EVENT_TYPE_SHOT_SAVED,
EVENT_TYPE_SHOT_GOAL,
EVENT_TYPE_RECOVERY,
EVENT_TYPE_BALL_TOUCH,
)

EVENT_QUALIFIER_GOAL_KICK = 124
Expand Down Expand Up @@ -765,6 +767,10 @@ def deserialize(self, inputs: OptaInputs) -> EventDataset:
)
event = self.event_factory.build_goalkeeper_event(
**goalkeeper_event_kwargs,
elif (type_id == EVENT_TYPE_BALL_TOUCH) & (outcome == 0):
event = self.event_factory.build_miscontrol(
result=None,
qualifiers=None,
**generic_event_kwargs,
)
elif (type_id == EVENT_TYPE_FOUL_COMMITTED) and (
Expand Down
8 changes: 8 additions & 0 deletions kloppy/infra/serializers/event/statsbomb/deserializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
SB_EVENT_TYPE_GOALKEEPER_EVENT = 23
SB_EVENT_TYPE_PASS = 30
SB_EVENT_TYPE_50_50 = 33
SB_EVENT_TYPE_MISCONTROL = 38
SB_EVENT_TYPE_CARRY = 43

SB_EVENT_TYPE_HALF_START = 18
Expand Down Expand Up @@ -856,6 +857,13 @@ def deserialize(self, inputs: StatsBombInputs) -> EventDataset:
**generic_event_kwargs,
)
new_events.append(clearance_event)
elif event_type == SB_EVENT_TYPE_MISCONTROL:
miscontrol_event = self.event_factory.build_miscontrol(
result=None,
qualifiers=None,
**generic_event_kwargs,
)
new_events.append(miscontrol_event)
# For dribble and carry the definitions
# are flipped between StatsBomb and kloppy
elif event_type == SB_EVENT_TYPE_DRIBBLE:
Expand Down
12 changes: 12 additions & 0 deletions kloppy/infra/serializers/event/wyscout/deserializer_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,18 @@ def deserialize(self, inputs: WyscoutInputs) -> EventDataset:
**clearance_event_args,
**generic_event_args,
)
elif (
raw_event["subEventId"]
== wyscout_events.OTHERS_ON_BALL.TOUCH
) & (_has_tag(raw_event, wyscout_tags.MISSED_BALL)):
miscontrol_event_args = {
"result": None,
"qualifiers": _generic_qualifiers(raw_event),
}
event = self.event_factory.build_miscontrol(
**miscontrol_event_args,
**generic_event_args,
)
else:
recovery_event_args = _parse_recovery(raw_event)
event = self.event_factory.build_recovery(
Expand Down
7 changes: 0 additions & 7 deletions kloppy/infra/serializers/event/wyscout/deserializer_v3.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,6 @@ def _parse_team(raw_events, wyId: str, ground: Ground) -> Team:
return team


def _has_tag(raw_event, tag_id) -> bool:
for tag in raw_event["tags"]:
if tag["id"] == tag_id:
return True
return False


def _generic_qualifiers(raw_event: Dict) -> List[Qualifier]:
qualifiers: List[Qualifier] = []

Expand Down
3 changes: 3 additions & 0 deletions kloppy/tests/files/opta_f24.xml
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,9 @@
<Event id="2438594253" event_id="965" type_id="54" period_id="2" min="21" sec="35" player_id="111319" team_id="569" outcome="1" x="4.4" y="49.9" timestamp="2018-09-23T17:00:01.850" last_modified="2018-09-23T17:08:02" version="1537718881823">
<Q id="3666298279" qualifier_id="232" />
<Q id="3667330981" qualifier_id="389" />
<Event id="2509132175" event_id="50" type_id="61" period_id="1" min="22" sec="6" player_id="460842" team_id="2592" outcome="0" x="1.3" y="81.1" timestamp="2018-09-23T17:21:01.810" last_modified="2018-09-23T17:08:02" version="1537718881823">
<Q id="4042410691" qualifier_id="178" />
<Q id="4039492675" qualifier_id="56" value="Back" />
</Event>
</Game>
</Games>
2 changes: 1 addition & 1 deletion kloppy/tests/test_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,4 @@ def read_to_stream(self, url: str, output: BinaryIO):
# Asserts borrowed from `test_opta.py`
assert dataset.metadata.provider == Provider.OPTA
assert dataset.dataset_type == DatasetType.EVENT
assert len(dataset.events) == 28
assert len(dataset.events) == 29
5 changes: 4 additions & 1 deletion kloppy/tests/test_opta.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def test_correct_deserialization(self, f7_data: str, f24_data: str):
)
assert dataset.metadata.provider == Provider.OPTA
assert dataset.dataset_type == DatasetType.EVENT
assert len(dataset.events) == 28
assert len(dataset.events) == 29
assert len(dataset.metadata.periods) == 2
assert (
dataset.events[10].ball_owning_team == dataset.metadata.teams[1]
Expand Down Expand Up @@ -115,6 +115,9 @@ def test_correct_deserialization(self, f7_data: str, f24_data: str):
assert dataset.events[18].result.value == "OWN_GOAL" # 2318697001
# Check OFFSIDE pass has end_coordinates
assert dataset.events[20].receiver_coordinates.x == 89.3 # 2360555167
assert (
dataset.events[23].event_type == EventType.MISCONTROL
) # 250913217

# Check goalkeeper qualifiers
assert (
Expand Down
1 change: 1 addition & 0 deletions kloppy/tests/test_statsbomb.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ def test_correct_deserialization(
== DuelType.GROUND
)
assert dataset.events[272].event_type == EventType.CLEARANCE
assert dataset.events[68].event_type == EventType.MISCONTROL

assert (
dataset.events[761].get_qualifier_value(GoalkeeperQualifier)
Expand Down
3 changes: 3 additions & 0 deletions kloppy/tests/test_wyscout.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ def test_correct_v2_deserialization(self, event_v2_data: Path):
)
assert dataset.records[2].coordinates == Point(29.0, 6.0)
assert dataset.events[137].event_type == EventType.CLEARANCE
assert dataset.events[11].event_type == EventType.MISCONTROL
assert dataset.events[136].event_type == EventType.CLEARANCE

assert (
dataset.events[39].get_qualifier_value(DuelQualifier)
== DuelType.GROUND
Expand Down

0 comments on commit bcf08da

Please sign in to comment.