{"action":"created","comment":{"url":"https://api.github.com/repos/open-craft/edx-platform/pulls/comments/94093440","pull_request_review_id":14624044,"id":94093440,"diff_hunk":"@@ -371,13 +388,186 @@ def get_child_descriptors(self):\n         return list(self._get_selected_child_blocks())\n \n \n+@XBlock.wants('user')\n @XBlock.wants('library_tools')  # Only needed in studio\n-class AdaptiveLibraryContentModule(AdaptiveLibraryContentFields, LibraryContentModule):\n+class AdaptiveLibraryContentModule(AdaptiveLibraryContentFields, LibraryContentModule, AdaptiveLearningAPIMixin):\n     \"\"\"\n     A specialized version of Randomized Content Block that communicates\n     with an external service to determine when to show its children, and which ones.\n     \"\"\"\n \n+    @lazy\n+    def parent_course(self):\n+        \"\"\"\n+        Return parent course.\n+        \"\"\"\n+        return self.runtime.modulestore.get_course(self.course_id, depth=1)\n+\n+    @lazy\n+    def parent_unit_id(self):\n+        \"\"\"\n+        Return block ID of parent unit.\n+        \"\"\"\n+        parent_unit = self.get_parent()\n+        return parent_unit.scope_ids.usage_id.block_id\n+\n+    @lazy\n+    def anonymous_user_id(self):\n+        \"\"\"\n+        Return anonymous ID for current user.\n+\n+        Note that we can't use `user_service.get_anonymous_user_id` here\n+        because it only produces meaningful results for staff users.\n+        \"\"\"\n+        user_service = self.runtime.service(self, 'user')\n+        if user_service:\n+            current_user = user_service.get_current_user()\n+            user_id = current_user.opt_attrs.get('edx-platform.user_id')\n+            anonymous_user_id = self._make_anonymous_user_id(user_id)\n+        else:\n+            anonymous_user_id = None\n+        return anonymous_user_id\n+\n+    @lazy\n+    def child_block_ids(self):\n+        \"\"\"\n+        Return list of `block_id`s identifying children of this block.\n+        \"\"\"\n+        return [child.block_id for child in self.children]\n+\n+    @classmethod\n+    def make_selection(cls, selected, children, max_count, mode):\n+        \"\"\"\n+        Dynamically selects block_ids indicating which of the possible children are displayed to the current user.\n+\n+        Arguments:\n+            selected - list of (block_type, block_id) tuples assigned to this student\n+            children - children of this block\n+            max_count - number of components to display to each student\n+            mode - how content is drawn from the library\n+\n+        Returns:\n+            A dict containing the following keys:\n+\n+            'selected' (set) of (block_type, block_id) tuples assigned to this student\n+            'invalid' (set) of dropped (block_type, block_id) tuples that are no longer valid\n+            'overlimit' (set) of dropped (block_type, block_id) tuples that were previously selected\n+            'added' (set) of newly added (block_type, block_id) tuples\n+\n+        Note that this implementation ignores the `max_count` argument:\n+        The list of `selected` (block_type, block_id) tuples assigned to this student\n+        has been determined based on information from external service providing adaptive learning features,\n+        so we only remove blocks that are no longer valid here.\n+        \"\"\"\n+        selected = set(tuple(k) for k in selected)  # set of (block_type, block_id) tuples assigned to this student\n+\n+        # Determine which of our children we will show:\n+        valid_block_keys = set([(c.block_type, c.block_id) for c in children])\n+        # Remove any selected blocks that are no longer valid:\n+        invalid_block_keys = (selected - valid_block_keys)\n+        if invalid_block_keys:\n+            selected -= invalid_block_keys\n+\n+        # `overlimit_block_keys`:\n+        # External service determines number of children to show,\n+        # so no need to *drop* any blocks based on value of `max_count` argument.\n+\n+        # `added_block_keys`:\n+        # External service determines number of children to show,\n+        # so no need to *add* any blocks based on value of `max_count` argument.\n+\n+        return {\n+            'selected': selected,\n+            'invalid': invalid_block_keys,\n+            # External service determines number of children to show,\n+            # so `max_count` setting has no effect:\n+            # We never *drop* previously selected children because of it,\n+            # so the set of dropped children is empty by definition:\n+            'overlimit': set(),\n+            # External service determines number of children to show,\n+            # so `max_count` setting has no effect:\n+            # We never *add* any children because of it,\n+            # so the set of added children is empty by definition:\n+            'added': set(),\n+        }\n+\n+    def selected_children(self):\n+        \"\"\"\n+        Returns a set() of block_ids indicating which of the possible children\n+        have been selected to display to the current user.\n+        \"\"\"\n+        if hasattr(self, \"_selected_set\"):\n+            # Already done:\n+            return self._selected_set  # pylint: disable=access-member-before-definition\n+\n+        # Determine children to display based on information about pending reviews from external service.\n+        # This needs to happen here (at instance level) because we need access to course and block-specific data,\n+        # which is not available at the class level.\n+        selected = self.get_selections_current_user(self.children)\n+        block_keys = self.make_selection(selected, self.children, self.max_count, \"adaptive\")  # pylint: disable=no-member\n+","path":"common/lib/xmodule/xmodule/library_content_module.py","position":null,"original_position":214,"commit_id":"a8ccc570d444a04520e71a8c1bff2959f65846cc","original_commit_id":"b595f4a8275bf36124c76e626f58a00538d0610c","user":{"login":"pomegranited","id":7556571,"avatar_url":"https://avatars.githubusercontent.com/u/7556571?v=3","gravatar_id":"","url":"https://api.github.com/users/pomegranited","html_url":"https://github.com/pomegranited","followers_url":"https://api.github.com/users/pomegranited/followers","following_url":"https://api.github.com/users/pomegranited/following{/other_user}","gists_url":"https://api.github.com/users/pomegranited/gists{/gist_id}","starred_url":"https://api.github.com/users/pomegranited/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pomegranited/subscriptions","organizations_url":"https://api.github.com/users/pomegranited/orgs","repos_url":"https://api.github.com/users/pomegranited/repos","events_url":"https://api.github.com/users/pomegranited/events{/privacy}","received_events_url":"https://api.github.com/users/pomegranited/received_events","type":"User","site_admin":false},"body":"Ah gotcha.  Thanks for the explanation!","created_at":"2016-12-29T01:20:34Z","updated_at":"2016-12-29T01:20:34Z","html_url":"https://github.com/open-craft/edx-platform/pull/49#discussion_r94093440","pull_request_url":"https://api.github.com/repos/open-craft/edx-platform/pulls/49","_links":{"self":{"href":"https://api.github.com/repos/open-craft/edx-platform/pulls/comments/94093440"},"html":{"href":"https://github.com/open-craft/edx-platform/pull/49#discussion_r94093440"},"pull_request":{"href":"https://api.github.com/repos/open-craft/edx-platform/pulls/49"}}},"pull_request":{"url":"https://api.github.com/repos/open-craft/edx-platform/pulls/49","id":99163556,"html_url":"https://github.com/open-craft/edx-platform/pull/49","diff_url":"https://github.com/open-craft/edx-platform/pull/49.diff","patch_url":"https://github.com/open-craft/edx-platform/pull/49.patch","issue_url":"https://api.github.com/repos/open-craft/edx-platform/issues/49","number":49,"state":"open","locked":false,"title":"Adaptive Content Block: Send \"Unit viewed\" event, selectively display review questions","user":{"login":"itsjeyd","id":961441,"avatar_url":"https://avatars.githubusercontent.com/u/961441?v=3","gravatar_id":"","url":"https://api.github.com/users/itsjeyd","html_url":"https://github.com/itsjeyd","followers_url":"https://api.github.com/users/itsjeyd/followers","following_url":"https://api.github.com/users/itsjeyd/following{/other_user}","gists_url":"https://api.github.com/users/itsjeyd/gists{/gist_id}","starred_url":"https://api.github.com/users/itsjeyd/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/itsjeyd/subscriptions","organizations_url":"https://api.github.com/users/itsjeyd/orgs","repos_url":"https://api.github.com/users/itsjeyd/repos","events_url":"https://api.github.com/users/itsjeyd/events{/privacy}","received_events_url":"https://api.github.com/users/itsjeyd/received_events","type":"User","site_admin":false},"body":"This PR extends the new Adaptive Content Block in two ways:\r\n\r\n* It adds logic for notifying Domoscio that the parent unit of an ACB has been accessed by a learner.\r\n* It changes the way child blocks are selected for display: Instead of selecting `max_count` children randomly, ACBs now query Domoscio for information about pending reviews to determine which children to show (if any).\r\n\r\nThe PR also adds a new course-wide setting called \"Adaptive Learning Configuration\" which can be used to specify necessary information for communicating with the Domoscio API.\r\n\r\ncf. [OC-2150](https://tasks.opencraft.com/browse/OC-2150), [OC-2151](https://tasks.opencraft.com/browse/OC-2151)\r\n\r\n**Implementation**\r\n\r\nWhen sending a \"Unit viewed\" event, ACB assumes that the corresponding unit exists in Domoscio's database (in the form of a \"knowledge node\" whose `uid` is set to the `block_id` of the unit). This is a safe assumption to make because Domoscio staff will take care of creating entities representing relevant elements of the course structure (including units and review questions) prior to the start date of the course that will be using the new adaptive learning features.\r\n\r\nIn addition to knowledge nodes representing units, there are other entities that must exist in the Domoscio database to be able to send an event of the type that Domoscio expects: Events target objects that link students to specific knowledge nodes. This means that prior to sending a \"Unit viewed\" event, the Domoscio database must contain both a \"student\" entity corresponding to the learner viewing the unit in the LMS, and an object that links the student to the unit (knowledge node). In Domoscio terminology, an object linking a student to a knowledge node is called \"knowledge node student\". (The plural is \"knowledge node students\".) Since we can't assume that students and objects that link them to units will be created by Domoscio staff prior to the start date of the course, ACBs will create student and \"knowledge node student\" objects via the Domoscio API prior to sending \"Unit viewed\" events (if necessary).\r\n\r\nAs of this PR, ACBs will also create \"knowledge node student\" objects that link students to review questions when a learner accesses a unit containing an ACB. This is not a prerequisite for sending \"Unit viewed\" events (or for obtaining a list of review questions to display from Domoscio). However, when a learner submits an answer to a review question, we will need to notify Domoscio about the result (OC-2152). At that point, links between students and review questions need to exist so we can target them with appropriate `EventResult` events.\r\n\r\n**Test instructions**\r\n\r\n1. Follow instructions on the [epic](https://tasks.opencraft.com/browse/OC-2127) to set up a FUN box.\r\n\r\n2. Switch to this branch (`adaptive-content-block`) and install the new block via\r\n\r\n    ```sh\r\n    pip install -r requirements/edx/local.txt\r\n    ```\r\n\r\n3. Create a new content library and add a few problems to it.\r\n\r\n4. Create a new course and add `\"adaptive_library_content\"` to advanced modules.\r\n\r\n5. Add an \"Adaptive Content Block\" that references the library created previously to a unit (optionally modifying the number of components to display to each student), and publish the unit. Do **not** access the unit in the LMS yet.\r\n\r\n6. To be able to work with the Domoscio API, you'll need to specify a few course-level settings: In Studio, navigate to the Advanced Settings page for the course (\"Paramètres\" > \"Paramètres avancés\") and replace the default value for the \"Adaptive Learning Configuration\" setting with the following:\r\n\r\n    ```json\r\n    \"url\": \"<see comment on internal ticket>\",\r\n    \"instance_id\": <see comment on internal ticket>,\r\n    \"access_token\": \"<see comment on internal ticket>\",\r\n    \"api_version\": \"v1\"\r\n    ```\r\n\r\n7. In a venv that has the `requests` library installed, create a knowledge node that corresponds to the unit you created earlier (`block_id` needs to be a 32-character hash), and review the results:\r\n\r\n    ```python\r\n    import requests, json\r\n    response = requests.post(\"<url>/v1/instances/<instance_id>/knowledge_nodes\", headers={\"Authorization\": \"Token token=<access_token>\"}, data={\"knowledge_graph_id\": <see comment on internal ticket>, \"name\": \"Unit node\", \"uid\": \"<block_id of unit>\"})\r\n    json.loads(response.content)\r\n    ```\r\n\r\n    Then create a knowledge node for each of the problems that belong to the library you created earlier. Note that problems belonging to a content library are assigned unique block IDs for each Adaptive Content Block that references them; you'll need to use these context-specific IDs when creating corresponding knowledge nodes. Block IDs for problems are 20-character hashes.\r\n\r\n8. Review the list of students that exist on the Domoscio test instance:\r\n\r\n   ```python\r\n   response = requests.get(\"<url>/v1/instances/<instance_id>/students\", headers={\"Authorization\": \"Token token=<access_token>\"})\r\n   json.loads(response.content)\r\n   ```\r\n\r\n9. Review the list of links between students and units (\"knowledge node student\" objects) on the Domoscio test instance:\r\n\r\n    ```python\r\n    response = requests.get(\"<url>/v1/instances/<instance_id>/knowledge_node_students\", headers={\"Authorization\": \"Token token=<access_token>\"})\r\n    json.loads(response.content)    \r\n    ```\r\n\r\n10. Access the unit that contains the ACB in the LMS. Observe that it does not display any child blocks.\r\n\r\n11. Review the list of students and the list of \"knowledge node student\" objects as described above. Observe that the former contains a new student and the latter contains new \"knowledge node student\" objects, one for each knowledge node you created earlier. The `knowledge_node_id`/`knowledge_node_uid` and `student_id`/`student_uid` fields of the new \"knowledge node student\" objects should reference the knowledge nodes created earlier, and the student that was created when you accessed the unit for the first time.\r\n\r\n12. Review the list of events that have been recorded for the Domoscio test instance, making sure that it contains an entry that references the \"knowledge node student\" object linking the unit you created earlier to the student that you used to access the unit in the LMS:\r\n\r\n    ```python\r\n    response = requests.get(\"<url>/v1/instances/<instance_id>/events\", headers={\"Authorization\": \"Token token=<access_token>\"})\r\n    json.loads(response.content)\r\n    ```\r\n\r\n**Reviewers**\r\n\r\n- [ ] @e-kolpakov / @pomegranited\r\n\r\n**Notes**\r\n\r\n<!--\r\n- This PR implements one possible approach for fetching pending reviews from the Domoscio API. Depending on how the course structure will be represented in the Domoscio database (details still pending), we might need to adjust the approach in a follow-up PR.\r\n-->\r\n- There is no way to force Domoscio to schedule a review, so it's not possible to manually test the logic for selectively displaying review questions (except the case where no pending reviews are available).","created_at":"2016-12-22T20:15:42Z","updated_at":"2016-12-29T01:20:34Z","closed_at":null,"merged_at":null,"merge_commit_sha":"354ef96f1a526b05da4ea05b8484c7845c6d01b5","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/open-craft/edx-platform/pulls/49/commits","review_comments_url":"https://api.github.com/repos/open-craft/edx-platform/pulls/49/comments","review_comment_url":"https://api.github.com/repos/open-craft/edx-platform/pulls/comments{/number}","comments_url":"https://api.github.com/repos/open-craft/edx-platform/issues/49/comments","statuses_url":"https://api.github.com/repos/open-craft/edx-platform/statuses/a8ccc570d444a04520e71a8c1bff2959f65846cc","head":{"label":"open-craft:send-unit-viewed","ref":"send-unit-viewed","sha":"a8ccc570d444a04520e71a8c1bff2959f65846cc","user":{"login":"open-craft","id":7414786,"avatar_url":"https://avatars.githubusercontent.com/u/7414786?v=3","gravatar_id":"","url":"https://api.github.com/users/open-craft","html_url":"https://github.com/open-craft","followers_url":"https://api.github.com/users/open-craft/followers","following_url":"https://api.github.com/users/open-craft/following{/other_user}","gists_url":"https://api.github.com/users/open-craft/gists{/gist_id}","starred_url":"https://api.github.com/users/open-craft/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/open-craft/subscriptions","organizations_url":"https://api.github.com/users/open-craft/orgs","repos_url":"https://api.github.com/users/open-craft/repos","events_url":"https://api.github.com/users/open-craft/events{/privacy}","received_events_url":"https://api.github.com/users/open-craft/received_events","type":"Organization","site_admin":false},"repo":{"id":19940883,"name":"edx-platform","full_name":"open-craft/edx-platform","owner":{"login":"open-craft","id":7414786,"avatar_url":"https://avatars.githubusercontent.com/u/7414786?v=3","gravatar_id":"","url":"https://api.github.com/users/open-craft","html_url":"https://github.com/open-craft","followers_url":"https://api.github.com/users/open-craft/followers","following_url":"https://api.github.com/users/open-craft/following{/other_user}","gists_url":"https://api.github.com/users/open-craft/gists{/gist_id}","starred_url":"https://api.github.com/users/open-craft/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/open-craft/subscriptions","organizations_url":"https://api.github.com/users/open-craft/orgs","repos_url":"https://api.github.com/users/open-craft/repos","events_url":"https://api.github.com/users/open-craft/events{/privacy}","received_events_url":"https://api.github.com/users/open-craft/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/open-craft/edx-platform","description":"the edX learning management system (LMS) and course authoring tool, Studio","fork":true,"url":"https://api.github.com/repos/open-craft/edx-platform","forks_url":"https://api.github.com/repos/open-craft/edx-platform/forks","keys_url":"https://api.github.com/repos/open-craft/edx-platform/keys{/key_id}","collaborators_url":"https://api.github.com/repos/open-craft/edx-platform/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/open-craft/edx-platform/teams","hooks_url":"https://api.github.com/repos/open-craft/edx-platform/hooks","issue_events_url":"https://api.github.com/repos/open-craft/edx-platform/issues/events{/number}","events_url":"https://api.github.com/repos/open-craft/edx-platform/events","assignees_url":"https://api.github.com/repos/open-craft/edx-platform/assignees{/user}","branches_url":"https://api.github.com/repos/open-craft/edx-platform/branches{/branch}","tags_url":"https://api.github.com/repos/open-craft/edx-platform/tags","blobs_url":"https://api.github.com/repos/open-craft/edx-platform/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/open-craft/edx-platform/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/open-craft/edx-platform/git/refs{/sha}","trees_url":"https://api.github.com/repos/open-craft/edx-platform/git/trees{/sha}","statuses_url":"https://api.github.com/repos/open-craft/edx-platform/statuses/{sha}","languages_url":"https://api.github.com/repos/open-craft/edx-platform/languages","stargazers_url":"https://api.github.com/repos/open-craft/edx-platform/stargazers","contributors_url":"https://api.github.com/repos/open-craft/edx-platform/contributors","subscribers_url":"https://api.github.com/repos/open-craft/edx-platform/subscribers","subscription_url":"https://api.github.com/repos/open-craft/edx-platform/subscription","commits_url":"https://api.github.com/repos/open-craft/edx-platform/commits{/sha}","git_commits_url":"https://api.github.com/repos/open-craft/edx-platform/git/commits{/sha}","comments_url":"https://api.github.com/repos/open-craft/edx-platform/comments{/number}","issue_comment_url":"https://api.github.com/repos/open-craft/edx-platform/issues/comments{/number}","contents_url":"https://api.github.com/repos/open-craft/edx-platform/contents/{+path}","compare_url":"https://api.github.com/repos/open-craft/edx-platform/compare/{base}...{head}","merges_url":"https://api.github.com/repos/open-craft/edx-platform/merges","archive_url":"https://api.github.com/repos/open-craft/edx-platform/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/open-craft/edx-platform/downloads","issues_url":"https://api.github.com/repos/open-craft/edx-platform/issues{/number}","pulls_url":"https://api.github.com/repos/open-craft/edx-platform/pulls{/number}","milestones_url":"https://api.github.com/repos/open-craft/edx-platform/milestones{/number}","notifications_url":"https://api.github.com/repos/open-craft/edx-platform/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/open-craft/edx-platform/labels{/name}","releases_url":"https://api.github.com/repos/open-craft/edx-platform/releases{/id}","deployments_url":"https://api.github.com/repos/open-craft/edx-platform/deployments","created_at":"2014-05-19T12:28:25Z","updated_at":"2016-12-04T22:09:57Z","pushed_at":"2016-12-28T14:22:00Z","git_url":"git://github.com/open-craft/edx-platform.git","ssh_url":"git@github.com:open-craft/edx-platform.git","clone_url":"https://github.com/open-craft/edx-platform.git","svn_url":"https://github.com/open-craft/edx-platform","homepage":"http://code.edx.org/","size":743030,"stargazers_count":2,"watchers_count":2,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":4,"forks":1,"open_issues":4,"watchers":2,"default_branch":"master"}},"base":{"label":"open-craft:fun-adaptive-learning","ref":"fun-adaptive-learning","sha":"ce7e25633605f2de2a664bc4b1f6c8bd203c97f1","user":{"login":"open-craft","id":7414786,"avatar_url":"https://avatars.githubusercontent.com/u/7414786?v=3","gravatar_id":"","url":"https://api.github.com/users/open-craft","html_url":"https://github.com/open-craft","followers_url":"https://api.github.com/users/open-craft/followers","following_url":"https://api.github.com/users/open-craft/following{/other_user}","gists_url":"https://api.github.com/users/open-craft/gists{/gist_id}","starred_url":"https://api.github.com/users/open-craft/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/open-craft/subscriptions","organizations_url":"https://api.github.com/users/open-craft/orgs","repos_url":"https://api.github.com/users/open-craft/repos","events_url":"https://api.github.com/users/open-craft/events{/privacy}","received_events_url":"https://api.github.com/users/open-craft/received_events","type":"Organization","site_admin":false},"repo":{"id":19940883,"name":"edx-platform","full_name":"open-craft/edx-platform","owner":{"login":"open-craft","id":7414786,"avatar_url":"https://avatars.githubusercontent.com/u/7414786?v=3","gravatar_id":"","url":"https://api.github.com/users/open-craft","html_url":"https://github.com/open-craft","followers_url":"https://api.github.com/users/open-craft/followers","following_url":"https://api.github.com/users/open-craft/following{/other_user}","gists_url":"https://api.github.com/users/open-craft/gists{/gist_id}","starred_url":"https://api.github.com/users/open-craft/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/open-craft/subscriptions","organizations_url":"https://api.github.com/users/open-craft/orgs","repos_url":"https://api.github.com/users/open-craft/repos","events_url":"https://api.github.com/users/open-craft/events{/privacy}","received_events_url":"https://api.github.com/users/open-craft/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/open-craft/edx-platform","description":"the edX learning management system (LMS) and course authoring tool, Studio","fork":true,"url":"https://api.github.com/repos/open-craft/edx-platform","forks_url":"https://api.github.com/repos/open-craft/edx-platform/forks","keys_url":"https://api.github.com/repos/open-craft/edx-platform/keys{/key_id}","collaborators_url":"https://api.github.com/repos/open-craft/edx-platform/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/open-craft/edx-platform/teams","hooks_url":"https://api.github.com/repos/open-craft/edx-platform/hooks","issue_events_url":"https://api.github.com/repos/open-craft/edx-platform/issues/events{/number}","events_url":"https://api.github.com/repos/open-craft/edx-platform/events","assignees_url":"https://api.github.com/repos/open-craft/edx-platform/assignees{/user}","branches_url":"https://api.github.com/repos/open-craft/edx-platform/branches{/branch}","tags_url":"https://api.github.com/repos/open-craft/edx-platform/tags","blobs_url":"https://api.github.com/repos/open-craft/edx-platform/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/open-craft/edx-platform/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/open-craft/edx-platform/git/refs{/sha}","trees_url":"https://api.github.com/repos/open-craft/edx-platform/git/trees{/sha}","statuses_url":"https://api.github.com/repos/open-craft/edx-platform/statuses/{sha}","languages_url":"https://api.github.com/repos/open-craft/edx-platform/languages","stargazers_url":"https://api.github.com/repos/open-craft/edx-platform/stargazers","contributors_url":"https://api.github.com/repos/open-craft/edx-platform/contributors","subscribers_url":"https://api.github.com/repos/open-craft/edx-platform/subscribers","subscription_url":"https://api.github.com/repos/open-craft/edx-platform/subscription","commits_url":"https://api.github.com/repos/open-craft/edx-platform/commits{/sha}","git_commits_url":"https://api.github.com/repos/open-craft/edx-platform/git/commits{/sha}","comments_url":"https://api.github.com/repos/open-craft/edx-platform/comments{/number}","issue_comment_url":"https://api.github.com/repos/open-craft/edx-platform/issues/comments{/number}","contents_url":"https://api.github.com/repos/open-craft/edx-platform/contents/{+path}","compare_url":"https://api.github.com/repos/open-craft/edx-platform/compare/{base}...{head}","merges_url":"https://api.github.com/repos/open-craft/edx-platform/merges","archive_url":"https://api.github.com/repos/open-craft/edx-platform/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/open-craft/edx-platform/downloads","issues_url":"https://api.github.com/repos/open-craft/edx-platform/issues{/number}","pulls_url":"https://api.github.com/repos/open-craft/edx-platform/pulls{/number}","milestones_url":"https://api.github.com/repos/open-craft/edx-platform/milestones{/number}","notifications_url":"https://api.github.com/repos/open-craft/edx-platform/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/open-craft/edx-platform/labels{/name}","releases_url":"https://api.github.com/repos/open-craft/edx-platform/releases{/id}","deployments_url":"https://api.github.com/repos/open-craft/edx-platform/deployments","created_at":"2014-05-19T12:28:25Z","updated_at":"2016-12-04T22:09:57Z","pushed_at":"2016-12-28T14:22:00Z","git_url":"git://github.com/open-craft/edx-platform.git","ssh_url":"git@github.com:open-craft/edx-platform.git","clone_url":"https://github.com/open-craft/edx-platform.git","svn_url":"https://github.com/open-craft/edx-platform","homepage":"http://code.edx.org/","size":743030,"stargazers_count":2,"watchers_count":2,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":4,"forks":1,"open_issues":4,"watchers":2,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/open-craft/edx-platform/pulls/49"},"html":{"href":"https://github.com/open-craft/edx-platform/pull/49"},"issue":{"href":"https://api.github.com/repos/open-craft/edx-platform/issues/49"},"comments":{"href":"https://api.github.com/repos/open-craft/edx-platform/issues/49/comments"},"review_comments":{"href":"https://api.github.com/repos/open-craft/edx-platform/pulls/49/comments"},"review_comment":{"href":"https://api.github.com/repos/open-craft/edx-platform/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/open-craft/edx-platform/pulls/49/commits"},"statuses":{"href":"https://api.github.com/repos/open-craft/edx-platform/statuses/a8ccc570d444a04520e71a8c1bff2959f65846cc"}}}}
{"actor":{"display_login":"pomegranited"}}