{"action":"created","comment":{"url":"https://api.github.com/repos/edx/edx-platform/pulls/comments/43257350","id":43257350,"diff_hunk":"@@ -0,0 +1,588 @@\n+\"\"\"\n+Module with family of classes for block structures.\n+    BlockStructure - responsible for block existence and relations.\n+    BlockStructureBlockData - responsible for block & transformer data.\n+    BlockStructureXBlockData - responsible for xBlock data.\n+\n+The following internal data structures are implemented:\n+    _BlockRelations - Data structure for a single block's relations.\n+    _BlockData - Data structure for a single block's data.\n+\"\"\"\n+# pylint: disable=protected-access\n+from collections import defaultdict\n+from logging import getLogger\n+\n+from openedx.core.lib.graph_traversals import traverse_topologically, traverse_post_order\n+\n+from .exceptions import TransformerException\n+\n+\n+logger = getLogger(__name__)  # pylint: disable=invalid-name\n+\n+\n+# A dictionary key value for storing a transformer's version number.\n+TRANSFORMER_VERSION_KEY = '_version'\n+\n+\n+class _BlockRelations(object):\n+    \"\"\"\n+    Data structure to encapsulate relationships for a single block.\n+\n+    Attributes:\n+        parents - a list of usage keys for this block's parent blocks\n+        children - a list of usage keys for this block's child blocks\n+    \"\"\"\n+    def __init__(self):\n+\n+        # list [UsageKey]\n+        self.parents = []\n+\n+        # list [UsageKey]\n+        self.children = []\n+\n+\n+class BlockStructure(object):\n+    \"\"\"\n+    Base class for a block structure.  BlockStructures are constructed\n+    using the BlockStructureFactory and then used as the currency across\n+    Transformers.\n+\n+    This base class keeps track of the block structure's root_block_usage_key,\n+    the existence of the blocks, and their parents and children\n+    relationships (graph nodes and edges).\n+    \"\"\"\n+    def __init__(self, root_block_usage_key):\n+\n+        # The usage key of the root block for this structure.\n+        # UsageKey\n+        self.root_block_usage_key = root_block_usage_key\n+\n+        # Map of a block's usage key to its block relations. The\n+        # existence of a block in the structure is determined by its\n+        # presence in this map.\n+        # defaultdict {UsageKey: _BlockRelations}\n+        self._block_relations = defaultdict(_BlockRelations)\n+\n+        # Add the root block.\n+        self._add_block(self._block_relations, root_block_usage_key)\n+\n+    def __iter__(self):\n+        \"\"\"\n+        The default iterator for a block structure is a topological\n+        traversal since it's the more common case and we currently\n+        need to support DAGs.\n+        \"\"\"\n+        return self.topological_traversal()\n+\n+    #--- Block structure relation methods ---#\n+\n+    def get_parents(self, usage_key):\n+        \"\"\"\n+        Returns the parents of the block identified by the given\n+        usage_key.\n+\n+        Arguments:\n+            usage_key - The usage key of the block whose parents\n+                are to be returned.\n+\n+        Returns:\n+            [UsageKey] - A list of usage keys of the block's parents.\n+        \"\"\"\n+        return self._block_relations[usage_key].parents if self.has_block(usage_key) else []\n+\n+    def get_children(self, usage_key):\n+        \"\"\"\n+        Returns the children of the block identified by the given\n+        usage_key.\n+\n+        Arguments:\n+            usage_key - The usage key of the block whose children\n+                are to be returned.\n+\n+        Returns:\n+            [UsageKey] - A list of usage keys of the block's children.\n+        \"\"\"\n+        return self._block_relations[usage_key].children if self.has_block(usage_key) else []\n+\n+    def has_block(self, usage_key):\n+        \"\"\"\n+        Returns whether a block with the given usage_key is in this\n+        block structure.\n+\n+        Arguments:\n+            usage_key - The usage key of the block whose children\n+                are to be returned.\n+\n+        Returns:\n+            bool - Whether or not a block with the given usage_key\n+                is present in this block structure.\n+        \"\"\"\n+        return usage_key in self._block_relations\n+\n+    #--- Block structure traversal methods ---#\n+\n+    def topological_traversal(\n+            self,\n+            filter_func=None,\n+            yield_descendants_of_unyielded=False,\n+    ):\n+        \"\"\"\n+        Performs a topological sort of the block structure and yields\n+        the usage_key of each block as it is encountered.\n+\n+        Arguments:\n+            See the description in\n+            openedx.core.lib.graph_traversals.traverse_topologically.\n+\n+        Returns:\n+            generator - A generator object created from the\n+                traverse_topologically method.\n+        \"\"\"\n+        return traverse_topologically(\n+            start_node=self.root_block_usage_key,\n+            get_parents=self.get_parents,\n+            get_children=self.get_children,\n+            filter_func=filter_func,\n+            yield_descendants_of_unyielded=yield_descendants_of_unyielded,\n+        )\n+\n+    def post_order_traversal(\n+            self,\n+            filter_func=None,\n+    ):\n+        \"\"\"\n+        Performs a post-order sort of the block structure and yields\n+        the usage_key of each block as it is encountered.\n+\n+        Arguments:\n+            See the description in\n+            openedx.core.lib.graph_traversals.traverse_post_order.\n+\n+        Returns:\n+            generator - A generator object created from the\n+                traverse_post_order method.\n+        \"\"\"\n+        return traverse_post_order(\n+            start_node=self.root_block_usage_key,\n+            get_children=self.get_children,\n+            filter_func=filter_func,\n+        )\n+\n+    #--- Internal methods ---#\n+    # To be used within the block_cache framework or by tests.\n+\n+    def _prune_unreachable(self):\n+        \"\"\"\n+        Mutates this block structure by removing any unreachable blocks.\n+        \"\"\"\n+\n+        # Create a new block relations map to store only those blocks\n+        # that are still linked\n+        pruned_block_relations = defaultdict(_BlockRelations)\n+        old_block_relations = self._block_relations\n+\n+        # Build the structure from the leaves up by doing a post-order\n+        # traversal of the old structure, thereby encountering only\n+        # reachable blocks.\n+        for block_key in self.post_order_traversal():\n+            # If the block is in the old structure,\n+            if block_key in old_block_relations:\n+                # Add it to the new pruned structure\n+                self._add_block(pruned_block_relations, block_key)\n+\n+                # Add a relationship to only those old children that\n+                # were also added to the new pruned structure.\n+                for child in old_block_relations[block_key].children:\n+                    if child in pruned_block_relations:\n+                        self._add_to_relations(pruned_block_relations, block_key, child)\n+\n+        # Replace this structure's relations with the newly pruned one.\n+        self._block_relations = pruned_block_relations\n+\n+    def _get_block_keys(self):\n+        \"\"\"\n+        Returns an iterator of all the block keys in the block\n+        structure.\n+        \"\"\"\n+        return self._block_relations.iterkeys()\n+\n+    def _add_relation(self, parent_key, child_key):\n+        \"\"\"\n+        Adds a parent to child relationship in this block structure.\n+\n+        Arguments:\n+            parent_key (UsageKey) - Usage key of the parent block.\n+            child_key (UsageKey) - Usage key of the child block.\n+        \"\"\"\n+        self._add_to_relations(self._block_relations, parent_key, child_key)\n+\n+    @staticmethod\n+    def _add_to_relations(block_relations, parent_key, child_key):\n+        \"\"\"\n+        Adds a parent to child relationship in the given block\n+        relations map.\n+\n+        Arguments:\n+            block_relations (defaultdict({UsageKey: _BlockRelations})) -\n+                Internal map of a block's usage key to its\n+                parents/children relations.\n+\n+            parent_key (UsageKey) - Usage key of the parent block.\n+\n+            child_key (UsageKey) - Usage key of the child block.\n+        \"\"\"\n+        block_relations[child_key].parents.append(parent_key)\n+        block_relations[parent_key].children.append(child_key)\n+\n+    @staticmethod\n+    def _add_block(block_relations, usage_key):\n+        \"\"\"\n+        Adds the given usage_key to the given block_relations map.\n+\n+        Arguments:\n+            block_relations (defaultdict({UsageKey: _BlockRelations})) -\n+                Internal map of a block's usage key to its\n+                parents/children relations.\n+\n+            usage_key (UsageKey) - Usage key of the block that is to\n+                be added to the given block_relations.\n+        \"\"\"\n+\n+        # Since block_relations is a DefaultDict, the usage_key will\n+        # automatically be added by virtue of accessing it.\n+        _ = block_relations[usage_key]\n+\n+\n+class _BlockData(object):\n+    \"\"\"\n+    Data structure to encapsulate collected data for a single block.\n+    \"\"\"\n+    def __init__(self):\n+        # Map of xblock field name to the field's value for this block.\n+        # dict {string: any picklable type}\n+        self._xblock_fields = {}\n+\n+        # Map of transformer name to the transformer's data for this\n+        # block.\n+        # defaultdict {string: dict}\n+        self._transformer_data = defaultdict(dict)","path":"openedx/core/lib/block_cache/block_structure.py","position":268,"original_position":268,"commit_id":"904f8f7db542a82ccc69cbcb6385fa24ba5c6118","original_commit_id":"904f8f7db542a82ccc69cbcb6385fa24ba5c6118","user":{"login":"jcdyer","id":77486,"avatar_url":"https://avatars.githubusercontent.com/u/77486?v=3","gravatar_id":"","url":"https://api.github.com/users/jcdyer","html_url":"https://github.com/jcdyer","followers_url":"https://api.github.com/users/jcdyer/followers","following_url":"https://api.github.com/users/jcdyer/following{/other_user}","gists_url":"https://api.github.com/users/jcdyer/gists{/gist_id}","starred_url":"https://api.github.com/users/jcdyer/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jcdyer/subscriptions","organizations_url":"https://api.github.com/users/jcdyer/orgs","repos_url":"https://api.github.com/users/jcdyer/repos","events_url":"https://api.github.com/users/jcdyer/events{/privacy}","received_events_url":"https://api.github.com/users/jcdyer/received_events","type":"User","site_admin":false},"body":"As with _BlockRelations, these comments should be part of the docstring.","created_at":"2015-10-28T13:54:17Z","updated_at":"2015-10-28T13:54:17Z","html_url":"https://github.com/edx/edx-platform/pull/10365#discussion_r43257350","pull_request_url":"https://api.github.com/repos/edx/edx-platform/pulls/10365","_links":{"self":{"href":"https://api.github.com/repos/edx/edx-platform/pulls/comments/43257350"},"html":{"href":"https://github.com/edx/edx-platform/pull/10365#discussion_r43257350"},"pull_request":{"href":"https://api.github.com/repos/edx/edx-platform/pulls/10365"}}},"pull_request":{"url":"https://api.github.com/repos/edx/edx-platform/pulls/10365","id":48755306,"html_url":"https://github.com/edx/edx-platform/pull/10365","diff_url":"https://github.com/edx/edx-platform/pull/10365.diff","patch_url":"https://github.com/edx/edx-platform/pull/10365.patch","issue_url":"https://api.github.com/repos/edx/edx-platform/issues/10365","number":10365,"state":"open","locked":false,"title":"Block Cache framework. (MA-1551)","user":{"login":"nasthagiri","id":6772826,"avatar_url":"https://avatars.githubusercontent.com/u/6772826?v=3","gravatar_id":"","url":"https://api.github.com/users/nasthagiri","html_url":"https://github.com/nasthagiri","followers_url":"https://api.github.com/users/nasthagiri/followers","following_url":"https://api.github.com/users/nasthagiri/following{/other_user}","gists_url":"https://api.github.com/users/nasthagiri/gists{/gist_id}","starred_url":"https://api.github.com/users/nasthagiri/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nasthagiri/subscriptions","organizations_url":"https://api.github.com/users/nasthagiri/orgs","repos_url":"https://api.github.com/users/nasthagiri/repos","events_url":"https://api.github.com/users/nasthagiri/events{/privacy}","received_events_url":"https://api.github.com/users/nasthagiri/received_events","type":"User","site_admin":false},"body":"For context, please read:\r\n* the module's `__init__.py` file\r\n* https://openedx.atlassian.net/wiki/display/MA/Fast+Block+Trees%3A+Tree+Transformers","created_at":"2015-10-26T15:36:07Z","updated_at":"2015-10-28T13:54:17Z","closed_at":null,"merged_at":null,"merge_commit_sha":"adfb3132b33d060529d4f8530a1392a065550985","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/edx/edx-platform/pulls/10365/commits","review_comments_url":"https://api.github.com/repos/edx/edx-platform/pulls/10365/comments","review_comment_url":"https://api.github.com/repos/edx/edx-platform/pulls/comments{/number}","comments_url":"https://api.github.com/repos/edx/edx-platform/issues/10365/comments","statuses_url":"https://api.github.com/repos/edx/edx-platform/statuses/904f8f7db542a82ccc69cbcb6385fa24ba5c6118","head":{"label":"edx:mobile/block-cache-framework","ref":"mobile/block-cache-framework","sha":"904f8f7db542a82ccc69cbcb6385fa24ba5c6118","user":{"login":"edx","id":3179841,"avatar_url":"https://avatars.githubusercontent.com/u/3179841?v=3","gravatar_id":"","url":"https://api.github.com/users/edx","html_url":"https://github.com/edx","followers_url":"https://api.github.com/users/edx/followers","following_url":"https://api.github.com/users/edx/following{/other_user}","gists_url":"https://api.github.com/users/edx/gists{/gist_id}","starred_url":"https://api.github.com/users/edx/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/edx/subscriptions","organizations_url":"https://api.github.com/users/edx/orgs","repos_url":"https://api.github.com/users/edx/repos","events_url":"https://api.github.com/users/edx/events{/privacy}","received_events_url":"https://api.github.com/users/edx/received_events","type":"Organization","site_admin":false},"repo":{"id":10391073,"name":"edx-platform","full_name":"edx/edx-platform","owner":{"login":"edx","id":3179841,"avatar_url":"https://avatars.githubusercontent.com/u/3179841?v=3","gravatar_id":"","url":"https://api.github.com/users/edx","html_url":"https://github.com/edx","followers_url":"https://api.github.com/users/edx/followers","following_url":"https://api.github.com/users/edx/following{/other_user}","gists_url":"https://api.github.com/users/edx/gists{/gist_id}","starred_url":"https://api.github.com/users/edx/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/edx/subscriptions","organizations_url":"https://api.github.com/users/edx/orgs","repos_url":"https://api.github.com/users/edx/repos","events_url":"https://api.github.com/users/edx/events{/privacy}","received_events_url":"https://api.github.com/users/edx/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/edx/edx-platform","description":"The Open edX platform, the software that powers edX!","fork":false,"url":"https://api.github.com/repos/edx/edx-platform","forks_url":"https://api.github.com/repos/edx/edx-platform/forks","keys_url":"https://api.github.com/repos/edx/edx-platform/keys{/key_id}","collaborators_url":"https://api.github.com/repos/edx/edx-platform/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/edx/edx-platform/teams","hooks_url":"https://api.github.com/repos/edx/edx-platform/hooks","issue_events_url":"https://api.github.com/repos/edx/edx-platform/issues/events{/number}","events_url":"https://api.github.com/repos/edx/edx-platform/events","assignees_url":"https://api.github.com/repos/edx/edx-platform/assignees{/user}","branches_url":"https://api.github.com/repos/edx/edx-platform/branches{/branch}","tags_url":"https://api.github.com/repos/edx/edx-platform/tags","blobs_url":"https://api.github.com/repos/edx/edx-platform/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/edx/edx-platform/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/edx/edx-platform/git/refs{/sha}","trees_url":"https://api.github.com/repos/edx/edx-platform/git/trees{/sha}","statuses_url":"https://api.github.com/repos/edx/edx-platform/statuses/{sha}","languages_url":"https://api.github.com/repos/edx/edx-platform/languages","stargazers_url":"https://api.github.com/repos/edx/edx-platform/stargazers","contributors_url":"https://api.github.com/repos/edx/edx-platform/contributors","subscribers_url":"https://api.github.com/repos/edx/edx-platform/subscribers","subscription_url":"https://api.github.com/repos/edx/edx-platform/subscription","commits_url":"https://api.github.com/repos/edx/edx-platform/commits{/sha}","git_commits_url":"https://api.github.com/repos/edx/edx-platform/git/commits{/sha}","comments_url":"https://api.github.com/repos/edx/edx-platform/comments{/number}","issue_comment_url":"https://api.github.com/repos/edx/edx-platform/issues/comments{/number}","contents_url":"https://api.github.com/repos/edx/edx-platform/contents/{+path}","compare_url":"https://api.github.com/repos/edx/edx-platform/compare/{base}...{head}","merges_url":"https://api.github.com/repos/edx/edx-platform/merges","archive_url":"https://api.github.com/repos/edx/edx-platform/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/edx/edx-platform/downloads","issues_url":"https://api.github.com/repos/edx/edx-platform/issues{/number}","pulls_url":"https://api.github.com/repos/edx/edx-platform/pulls{/number}","milestones_url":"https://api.github.com/repos/edx/edx-platform/milestones{/number}","notifications_url":"https://api.github.com/repos/edx/edx-platform/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/edx/edx-platform/labels{/name}","releases_url":"https://api.github.com/repos/edx/edx-platform/releases{/id}","created_at":"2013-05-30T20:20:38Z","updated_at":"2015-10-27T00:21:27Z","pushed_at":"2015-10-28T13:51:31Z","git_url":"git://github.com/edx/edx-platform.git","ssh_url":"git@github.com:edx/edx-platform.git","clone_url":"https://github.com/edx/edx-platform.git","svn_url":"https://github.com/edx/edx-platform","homepage":"http://open.edx.org/","size":2201824,"stargazers_count":2196,"watchers_count":2196,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":1341,"mirror_url":null,"open_issues_count":161,"forks":1341,"open_issues":161,"watchers":2196,"default_branch":"master"}},"base":{"label":"edx:master","ref":"master","sha":"c032fefc74cb35fddf99787d27c3cf7126001cf8","user":{"login":"edx","id":3179841,"avatar_url":"https://avatars.githubusercontent.com/u/3179841?v=3","gravatar_id":"","url":"https://api.github.com/users/edx","html_url":"https://github.com/edx","followers_url":"https://api.github.com/users/edx/followers","following_url":"https://api.github.com/users/edx/following{/other_user}","gists_url":"https://api.github.com/users/edx/gists{/gist_id}","starred_url":"https://api.github.com/users/edx/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/edx/subscriptions","organizations_url":"https://api.github.com/users/edx/orgs","repos_url":"https://api.github.com/users/edx/repos","events_url":"https://api.github.com/users/edx/events{/privacy}","received_events_url":"https://api.github.com/users/edx/received_events","type":"Organization","site_admin":false},"repo":{"id":10391073,"name":"edx-platform","full_name":"edx/edx-platform","owner":{"login":"edx","id":3179841,"avatar_url":"https://avatars.githubusercontent.com/u/3179841?v=3","gravatar_id":"","url":"https://api.github.com/users/edx","html_url":"https://github.com/edx","followers_url":"https://api.github.com/users/edx/followers","following_url":"https://api.github.com/users/edx/following{/other_user}","gists_url":"https://api.github.com/users/edx/gists{/gist_id}","starred_url":"https://api.github.com/users/edx/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/edx/subscriptions","organizations_url":"https://api.github.com/users/edx/orgs","repos_url":"https://api.github.com/users/edx/repos","events_url":"https://api.github.com/users/edx/events{/privacy}","received_events_url":"https://api.github.com/users/edx/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/edx/edx-platform","description":"The Open edX platform, the software that powers edX!","fork":false,"url":"https://api.github.com/repos/edx/edx-platform","forks_url":"https://api.github.com/repos/edx/edx-platform/forks","keys_url":"https://api.github.com/repos/edx/edx-platform/keys{/key_id}","collaborators_url":"https://api.github.com/repos/edx/edx-platform/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/edx/edx-platform/teams","hooks_url":"https://api.github.com/repos/edx/edx-platform/hooks","issue_events_url":"https://api.github.com/repos/edx/edx-platform/issues/events{/number}","events_url":"https://api.github.com/repos/edx/edx-platform/events","assignees_url":"https://api.github.com/repos/edx/edx-platform/assignees{/user}","branches_url":"https://api.github.com/repos/edx/edx-platform/branches{/branch}","tags_url":"https://api.github.com/repos/edx/edx-platform/tags","blobs_url":"https://api.github.com/repos/edx/edx-platform/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/edx/edx-platform/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/edx/edx-platform/git/refs{/sha}","trees_url":"https://api.github.com/repos/edx/edx-platform/git/trees{/sha}","statuses_url":"https://api.github.com/repos/edx/edx-platform/statuses/{sha}","languages_url":"https://api.github.com/repos/edx/edx-platform/languages","stargazers_url":"https://api.github.com/repos/edx/edx-platform/stargazers","contributors_url":"https://api.github.com/repos/edx/edx-platform/contributors","subscribers_url":"https://api.github.com/repos/edx/edx-platform/subscribers","subscription_url":"https://api.github.com/repos/edx/edx-platform/subscription","commits_url":"https://api.github.com/repos/edx/edx-platform/commits{/sha}","git_commits_url":"https://api.github.com/repos/edx/edx-platform/git/commits{/sha}","comments_url":"https://api.github.com/repos/edx/edx-platform/comments{/number}","issue_comment_url":"https://api.github.com/repos/edx/edx-platform/issues/comments{/number}","contents_url":"https://api.github.com/repos/edx/edx-platform/contents/{+path}","compare_url":"https://api.github.com/repos/edx/edx-platform/compare/{base}...{head}","merges_url":"https://api.github.com/repos/edx/edx-platform/merges","archive_url":"https://api.github.com/repos/edx/edx-platform/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/edx/edx-platform/downloads","issues_url":"https://api.github.com/repos/edx/edx-platform/issues{/number}","pulls_url":"https://api.github.com/repos/edx/edx-platform/pulls{/number}","milestones_url":"https://api.github.com/repos/edx/edx-platform/milestones{/number}","notifications_url":"https://api.github.com/repos/edx/edx-platform/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/edx/edx-platform/labels{/name}","releases_url":"https://api.github.com/repos/edx/edx-platform/releases{/id}","created_at":"2013-05-30T20:20:38Z","updated_at":"2015-10-27T00:21:27Z","pushed_at":"2015-10-28T13:51:31Z","git_url":"git://github.com/edx/edx-platform.git","ssh_url":"git@github.com:edx/edx-platform.git","clone_url":"https://github.com/edx/edx-platform.git","svn_url":"https://github.com/edx/edx-platform","homepage":"http://open.edx.org/","size":2201824,"stargazers_count":2196,"watchers_count":2196,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":1341,"mirror_url":null,"open_issues_count":161,"forks":1341,"open_issues":161,"watchers":2196,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/edx/edx-platform/pulls/10365"},"html":{"href":"https://github.com/edx/edx-platform/pull/10365"},"issue":{"href":"https://api.github.com/repos/edx/edx-platform/issues/10365"},"comments":{"href":"https://api.github.com/repos/edx/edx-platform/issues/10365/comments"},"review_comments":{"href":"https://api.github.com/repos/edx/edx-platform/pulls/10365/comments"},"review_comment":{"href":"https://api.github.com/repos/edx/edx-platform/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/edx/edx-platform/pulls/10365/commits"},"statuses":{"href":"https://api.github.com/repos/edx/edx-platform/statuses/904f8f7db542a82ccc69cbcb6385fa24ba5c6118"}}}}