diff options
| author | schneefux <schneefux+commit@schneefux.xyz> | 2017-01-14 21:19:48 +0100 |
|---|---|---|
| committer | schneefux <schneefux+commit@schneefux.xyz> | 2017-01-14 21:19:48 +0100 |
| commit | 814c7262ed1c9863ea6106840c217a69182eced3 (patch) | |
| tree | a82a9baf1a34287080808b9404069633040bee06 /gamelocker/datatypes.py | |
| parent | e3337188804e1521cc0a9ae72a172a71657c5f45 (diff) | |
| download | python-gamelocker-814c7262ed1c9863ea6106840c217a69182eced3.tar.gz python-gamelocker-814c7262ed1c9863ea6106840c217a69182eced3.zip | |
rewrite API to parse with xamoom-janus
Diffstat (limited to 'gamelocker/datatypes.py')
| -rw-r--r-- | gamelocker/datatypes.py | 177 |
1 files changed, 177 insertions, 0 deletions
diff --git a/gamelocker/datatypes.py b/gamelocker/datatypes.py new file mode 100644 index 0000000..92848ad --- /dev/null +++ b/gamelocker/datatypes.py @@ -0,0 +1,177 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# pylint: disable=missing-docstring + +import inspect +import sys +from gamelocker.janus import DataMessage, Attribute + + +def attr(typ, key, relation=False): + """Convenience function to specify an attribute or a relation. + + :param typ: Allowed data type or class. + :type typ: object + :param key: Object name, mapping name and key mapping name. + :type key: str + :param link: (optional) Whether this is a relation. + :return: `DataMessage` `Attribute` class. + :rtype: :class:`Attribute` + """ + if relation: + return Attribute(value_type=typ, name=key, + mapping=key, key_mapping=key) + else: + return Attribute(value_type=typ, name=key, + mapping=key) + + +def rel(typ, key): + """Convenience function to specify a relation. + See `attr()`. + """ + return attr(typ, key, True) + + +class Player(DataMessage): + type_name = "player" + key_id = attr(str, "id") + + name = attr(str, "name") + stats = rel(dict, "stats") + + +class Participant(DataMessage): + type_name = "participant" + key_id = attr(str, "id") + + actor = attr(str, "actor") + stats = attr(str, "stats") + + player = rel(Player, "player") + + +class Team(DataMessage): + type_name = "team" + key_id = attr(str, "id") + + name = rel(str, "name") + + +class Roster(DataMessage): + type_name = "roster" + key_id = attr(str, "id") + + stats = attr(str, "stats") + + participants = rel(Participant, "participants") + team = rel(Team, "team") + + +class Match(DataMessage): + type_name = "match" + key_id = attr(str, "id") + + createdAt = attr(str, "createdAt") + duration = attr(int, "duration") + gameMode = attr(str, "gameMode") + patchVersion = attr(str, "patchVersion") + region = attr(str, "region") + stats = attr(dict, "stats") + + rosters = rel(Roster, "rosters") + + +def modulemap(): + """Returns a dictionary where `type_name`s are mapped + to corresponding classes from `gamelocker.datatypes`. + + :return: A dictionary with keys of `type_name` + and values of :class:`DataMessage` subclasses. + :rtype: :class:`gamelocker.janus.DataMessage` + """ + typemap = dict() + classes = inspect.getmembers(sys.modules[__name__], inspect.isclass) + for name, value in classes: + if name == "Attribute" or name == "DataMessage": + continue + typemap[value.type_name] = value + + return typemap + + +def data_to_object(data): + """Given a data dictionary from the API, returns mapped objects or None. + + :param data: Dictionary from API request. + :type data: dict + :return: An object. + :rtype: :class:`gamelocker.janus.DataMessage` + """ + # find the appropriate class from /datatypes.py + typemap = modulemap() + datatype = None + for key, dataclass in typemap.items(): + if key == data["type"]: + datatype = dataclass + if datatype is None: + raise NotImplementedError( + "Data type " + data["type"] + " is not implemented yet.") + + # load data into class + element = datatype() + element.map_message(data) + + return element + + +def link_to_object(obj, relations): + """Replaces references in `obj` + by their corresponding objects from `relations`. + + :param obj: Master object to populate. + :type obj: :class:`gamelocker.janus.DataMessage` + :param relations: Object pool to populate from. + :type obj: list of :class:`gamelocker.janus.DataMessage` + :return: The populated master object. + :rtype: :class:`gamelocker.janus.DataMessage` + """ + for att in dir(obj): + # loop through all relations that object could possibly have + if not(isinstance(object.__getattribute__(obj, att), Attribute) and + issubclass( + object.__getattribute__(obj, att).value_type, DataMessage) + and not att.startswith("__")): + continue + + # try to get the objects that are being linked to + children = object.__getattribute__(obj, att).value + if children is None: + # there is no reference -> master class has object + # but it was not populated with data + # so it is (hopefully) optional. + continue + + # can either be a reference or a list of references + if isinstance(children, (list, tuple)): + newlist = [] + for child in children: + # replaces every element + for relation in relations: + # see below + if relation.id == child.id: + relation = link_to_object(relation, relations) + newlist.append(relation) + break + object.__setattr__(obj, att, newlist) + else: + for relation in relations: + if relation.id == children.id: + # replace unpopulated relation + # with object from `relations` that has data + # also, replace all references in that object (recursively) + relation = link_to_object(relation, relations) + object.__setattr__(obj, att, relation) + break + + return obj |
