summaryrefslogtreecommitdiff
path: root/gamelocker/datatypes.py
diff options
context:
space:
mode:
authorschneefux <schneefux+commit@schneefux.xyz>2017-01-14 21:19:48 +0100
committerschneefux <schneefux+commit@schneefux.xyz>2017-01-14 21:19:48 +0100
commit814c7262ed1c9863ea6106840c217a69182eced3 (patch)
treea82a9baf1a34287080808b9404069633040bee06 /gamelocker/datatypes.py
parente3337188804e1521cc0a9ae72a172a71657c5f45 (diff)
downloadpython-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.py177
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