# Path: socceraction/data/base.py
# class MissingDataError(Exception):
#     """Exception raised when a field is missing in the input data."""
# 
# Path: socceraction/data/opta/parsers/base.py
# class OptaParser(ABC):
#     """Extract data from an Opta data stream.
# 
#     Parameters
#     ----------
#     path : str
#         Path of the data file.
#     """
# 
#     def __init__(self, path: str, **kwargs: Any) -> None:
#         raise NotImplementedError
# 
#     def extract_competitions(self) -> Dict[Tuple[Any, Any], Dict[str, Any]]:
#         """Return a dictionary with all available competitions.
# 
#         Returns
#         -------
#         dict
#             A mapping between (competion ID, season ID) tuples and the
#             information available about each competition in the data stream.
#         """
#         return {}
# 
#     def extract_games(self) -> Dict[Any, Dict[str, Any]]:
#         """Return a dictionary with all available games.
# 
#         Returns
#         -------
#         dict
#             A mapping between game IDs and the information available about
#             each game in the data stream.
#         """
#         return {}
# 
#     def extract_teams(self) -> Dict[Any, Dict[str, Any]]:
#         """Return a dictionary with all available teams.
# 
#         Returns
#         -------
#         dict
#             A mapping between team IDs and the information available about
#             each team in the data stream.
#         """
#         return {}
# 
#     def extract_players(self) -> Dict[Tuple[Any, Any], Dict[str, Any]]:
#         """Return a dictionary with all available players.
# 
#         Returns
#         -------
#         dict
#             A mapping between (game ID, player ID) tuples and the information
#             available about each player in the data stream.
#         """
#         return {}
# 
#     def extract_lineups(self) -> Dict[Any, Dict[str, Any]]:
#         """Return a dictionary with the lineup of each team.
# 
#         Returns
#         -------
#         dict
#             A mapping between team IDs and the information available about
#             each team's lineup in the data stream.
#         """
#         return {}
# 
#     def extract_events(self) -> Dict[Tuple[Any, Any], Dict[str, Any]]:
#         """Return a dictionary with all available events.
# 
#         Returns
#         -------
#         dict
#             A mapping between (game ID, event ID) tuples and the information
#             available about each event in the data stream.
#         """
#         return {}
# 
# def _get_end_x(qualifiers: Dict[int, Any]) -> Optional[float]:
#     try:
#         # pass
#         if 140 in qualifiers:
#             return float(qualifiers[140])
#         # blocked shot
#         if 146 in qualifiers:
#             return float(qualifiers[146])
#         # passed the goal line
#         if 102 in qualifiers:
#             return float(100)
#         return None
#     except ValueError:
#         return None
# 
# def _get_end_y(qualifiers: Dict[int, Any]) -> Optional[float]:
#     try:
#         # pass
#         if 141 in qualifiers:
#             return float(qualifiers[141])
#         # blocked shot
#         if 147 in qualifiers:
#             return float(qualifiers[147])
#         # passed the goal line
#         if 102 in qualifiers:
#             return float(qualifiers[102])
#         return None
#     except ValueError:
#         return None
# 
# def assertget(dictionary: Dict[str, Any], key: str) -> Any:
#     """Return the value of the item with the specified key.
# 
#     In contrast to the default `get` method, this version will raise an
#     assertion error if the given key is not present in the dict.
# 
#     Parameters
#     ----------
#     dictionary : dict
#         A Python dictionary.
#     key : str
#         A key in the dictionary.
# 
#     Returns
#     -------
#     Any
#         Returns the value for the specified key if the key is in the dictionary.
# 
#     Raises
#     ------
#     AssertionError
#         If the given key could not be found in the dictionary.
#     """
#     value = dictionary.get(key)
#     assert value is not None, 'KeyError: ' + key + ' not found in ' + str(dictionary)
#     return value


import json  # type: ignore
import re
from datetime import datetime, timedelta
from typing import Any, Dict, Optional, Tuple
from ...base import MissingDataError
from .base import OptaParser, _get_end_x, _get_end_y, assertget
        period_minute = minute
        if period_id > 1:
            period_minute = minute - period_minute_limits[str(period_id - 1)]
        period_second = period_minute * 60 + int(event.get("second", 0))
        return period_second * 1000

    def extract_games(self) -> Dict[int, Dict[str, Any]]:
        """Return a dictionary with all available games.

        Returns
        -------
        dict
            A mapping between game IDs and the information available about
            each game in the data stream.
        """
        team_home = assertget(self.root, "home")
        team_away = assertget(self.root, "away")
        game_dict = dict(
            # Fields required by the base schema
            game_id=self.game_id,
            season_id=self.season_id,
            competition_id=self.competition_id,
            game_day=None,  # Cannot be determined from the data stream
            game_date=datetime.strptime(
                assertget(self.root, "startTime"), "%Y-%m-%dT%H:%M:%S"
            ),  # Dates are UTC
            home_team_id=int(assertget(team_home, "teamId")),
            away_team_id=int(assertget(team_away, "teamId")),
            # Optional fields
            home_score=int(assertget(assertget(self.root["home"], "scores"), "running")),

# Path: socceraction/data/base.py
# class MissingDataError(Exception):
#     """Exception raised when a field is missing in the input data."""
# 
# Path: socceraction/data/opta/parsers/base.py
# class OptaParser(ABC):
#     """Extract data from an Opta data stream.
# 
#     Parameters
#     ----------
#     path : str
#         Path of the data file.
#     """
# 
#     def __init__(self, path: str, **kwargs: Any) -> None:
#         raise NotImplementedError
# 
#     def extract_competitions(self) -> Dict[Tuple[Any, Any], Dict[str, Any]]:
#         """Return a dictionary with all available competitions.
# 
#         Returns
#         -------
#         dict
#             A mapping between (competion ID, season ID) tuples and the
#             information available about each competition in the data stream.
#         """
#         return {}
# 
#     def extract_games(self) -> Dict[Any, Dict[str, Any]]:
#         """Return a dictionary with all available games.
# 
#         Returns
#         -------
#         dict
#             A mapping between game IDs and the information available about
#             each game in the data stream.
#         """
#         return {}
# 
#     def extract_teams(self) -> Dict[Any, Dict[str, Any]]:
#         """Return a dictionary with all available teams.
# 
#         Returns
#         -------
#         dict
#             A mapping between team IDs and the information available about
#             each team in the data stream.
#         """
#         return {}
# 
#     def extract_players(self) -> Dict[Tuple[Any, Any], Dict[str, Any]]:
#         """Return a dictionary with all available players.
# 
#         Returns
#         -------
#         dict
#             A mapping between (game ID, player ID) tuples and the information
#             available about each player in the data stream.
#         """
#         return {}
# 
#     def extract_lineups(self) -> Dict[Any, Dict[str, Any]]:
#         """Return a dictionary with the lineup of each team.
# 
#         Returns
#         -------
#         dict
#             A mapping between team IDs and the information available about
#             each team's lineup in the data stream.
#         """
#         return {}
# 
#     def extract_events(self) -> Dict[Tuple[Any, Any], Dict[str, Any]]:
#         """Return a dictionary with all available events.
# 
#         Returns
#         -------
#         dict
#             A mapping between (game ID, event ID) tuples and the information
#             available about each event in the data stream.
#         """
#         return {}
# 
# def _get_end_x(qualifiers: Dict[int, Any]) -> Optional[float]:
#     try:
#         # pass
#         if 140 in qualifiers:
#             return float(qualifiers[140])
#         # blocked shot
#         if 146 in qualifiers:
#             return float(qualifiers[146])
#         # passed the goal line
#         if 102 in qualifiers:
#             return float(100)
#         return None
#     except ValueError:
#         return None
# 
# def _get_end_y(qualifiers: Dict[int, Any]) -> Optional[float]:
#     try:
#         # pass
#         if 141 in qualifiers:
#             return float(qualifiers[141])
#         # blocked shot
#         if 147 in qualifiers:
#             return float(qualifiers[147])
#         # passed the goal line
#         if 102 in qualifiers:
#             return float(qualifiers[102])
#         return None
#     except ValueError:
#         return None
# 
# def assertget(dictionary: Dict[str, Any], key: str) -> Any:
#     """Return the value of the item with the specified key.
# 
#     In contrast to the default `get` method, this version will raise an
#     assertion error if the given key is not present in the dict.
# 
#     Parameters
#     ----------
#     dictionary : dict
#         A Python dictionary.
#     key : str
#         A key in the dictionary.
# 
#     Returns
#     -------
#     Any
#         Returns the value for the specified key if the key is in the dictionary.
# 
#     Raises
#     ------
#     AssertionError
#         If the given key could not be found in the dictionary.
#     """
#     value = dictionary.get(key)
#     assert value is not None, 'KeyError: ' + key + ' not found in ' + str(dictionary)
#     return value


# Path: socceraction/data/opta/parsers/whoscored.py
import json  # type: ignore
import re
from datetime import datetime, timedelta
from typing import Any, Dict, Optional, Tuple
from ...base import MissingDataError
from .base import OptaParser, _get_end_x, _get_end_y, assertget

        period_minute = minute
        if period_id > 1:
            period_minute = minute - period_minute_limits[str(period_id - 1)]
        period_second = period_minute * 60 + int(event.get("second", 0))
        return period_second * 1000

    def extract_games(self) -> Dict[int, Dict[str, Any]]:
        """Return a dictionary with all available games.

        Returns
        -------
        dict
            A mapping between game IDs and the information available about
            each game in the data stream.
        """
        team_home = assertget(self.root, "home")
        team_away = assertget(self.root, "away")
        game_dict = dict(
            # Fields required by the base schema
            game_id=self.game_id,
            season_id=self.season_id,
            competition_id=self.competition_id,
            game_day=None,  # Cannot be determined from the data stream
            game_date=datetime.strptime(
                assertget(self.root, "startTime"), "%Y-%m-%dT%H:%M:%S"
            ),  # Dates are UTC
            home_team_id=int(assertget(team_home, "teamId")),
            away_team_id=int(assertget(team_away, "teamId")),
            # Optional fields
            home_score=int(assertget(assertget(self.root["home"], "scores"), "running")),

            away_score=int(assertget(assertget(self.root["away"], "scores"), "running")),