# 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 OptaJSONParser(OptaParser):
#     """Extract data from an Opta JSON data stream.
# 
#     Parameters
#     ----------
#     path : str
#         Path of the data file.
#     """
# 
#     def __init__(self, path: str, **kwargs: Any) -> None:
#         with open(path, 'rt', encoding='utf-8') as fh:
#             self.root = json.load(fh)
# 
# 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


from datetime import datetime
from typing import Any, Dict, List, Optional, Tuple
from ...base import MissingDataError
from .base import OptaJSONParser, assertget
                            players[(game_id, player_id)]["minutes_played"] = (
                                sub_out[0]["minute"] - sub_in[0]["minute"]
                            )
                        else:
                            players[(game_id, player_id)]["minutes_played"] = 0
        return players

    def extract_substitutions(self) -> Dict[Tuple[int, int], Dict[str, Any]]:
        """Return a dictionary with all substitution events.

        Returns
        -------
        dict
            A mapping between (game ID, player ID) tuples and the information
            available about each substitution in the data stream.
        """
        subs = {}
        for match in self._get_matches():
            match_info = self._get_match_info(match)
            game_id = assertget(match_info, 'id')
            live_data = self._get_live_data(match)
            if "substitute" not in live_data:
                continue
            for e in assertget(live_data, 'substitute'):
                sub_id = assertget(e, "playerOnId")
                subs[(game_id, sub_id)] = dict(
                    game_id=game_id,
                    team_id=assertget(e, "contestantId"),
                    period_id=int(assertget(e, "periodId")),
                    minute=int(assertget(e, "timeMin")),

# 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 OptaJSONParser(OptaParser):
#     """Extract data from an Opta JSON data stream.
# 
#     Parameters
#     ----------
#     path : str
#         Path of the data file.
#     """
# 
#     def __init__(self, path: str, **kwargs: Any) -> None:
#         with open(path, 'rt', encoding='utf-8') as fh:
#             self.root = json.load(fh)
# 
# 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/ma1_json.py
from datetime import datetime
from typing import Any, Dict, List, Optional, Tuple
from ...base import MissingDataError
from .base import OptaJSONParser, assertget

                            players[(game_id, player_id)]["minutes_played"] = (
                                sub_out[0]["minute"] - sub_in[0]["minute"]
                            )
                        else:
                            players[(game_id, player_id)]["minutes_played"] = 0
        return players

    def extract_substitutions(self) -> Dict[Tuple[int, int], Dict[str, Any]]:
        """Return a dictionary with all substitution events.

        Returns
        -------
        dict
            A mapping between (game ID, player ID) tuples and the information
            available about each substitution in the data stream.
        """
        subs = {}
        for match in self._get_matches():
            match_info = self._get_match_info(match)
            game_id = assertget(match_info, 'id')
            live_data = self._get_live_data(match)
            if "substitute" not in live_data:
                continue
            for e in assertget(live_data, 'substitute'):
                sub_id = assertget(e, "playerOnId")
                subs[(game_id, sub_id)] = dict(
                    game_id=game_id,
                    team_id=assertget(e, "contestantId"),
                    period_id=int(assertget(e, "periodId")),
                    minute=int(assertget(e, "timeMin")),

                    player_in_id=assertget(e, "playerOnId"),