{"action":"created","comment":{"url":"https://api.github.com/repos/edx/edx-platform/pulls/comments/23398109","id":23398109,"diff_hunk":"@@ -0,0 +1,206 @@\n+\"\"\"\n+Entrance Exams view module -- handles all requests related to entrance exam management via Studio\n+Intended to be utilized as an AJAX callback handler, versus a proper view/screen\n+\"\"\"\n+import json\n+from uuid import uuid4\n+\n+from django.contrib.auth.decorators import login_required\n+from django_future.csrf import ensure_csrf_cookie\n+from django.http import HttpResponse\n+from django.test import RequestFactory\n+\n+from contentstore.views.item import create_item, delete_item\n+from milestones import api as milestones_api\n+from models.settings.course_metadata import CourseMetadata\n+from opaque_keys.edx.keys import CourseKey, UsageKey\n+from opaque_keys import InvalidKeyError\n+from student.auth import has_course_author_access\n+from xmodule.modulestore.django import modulestore\n+from xmodule.modulestore.exceptions import ItemNotFoundError\n+from django.conf import settings\n+\n+__all__ = ['entrance_exam', ]\n+\n+\n+@login_required\n+@ensure_csrf_cookie\n+def entrance_exam(request, course_key_string):\n+    \"\"\"\n+    The restful handler for entrance exams.\n+    It allows retrieval of all the assets (as an HTML page), as well as uploading new assets,\n+    deleting assets, and changing the \"locked\" state of an asset.\n+\n+    GET\n+        Retrieves the entrance exam module (metadata) for the specified course\n+    POST\n+        Adds an entrance exam module to the specified course.\n+    DELETE\n+        Removes the entrance exam from the course\n+    \"\"\"\n+    course_key = CourseKey.from_string(course_key_string)\n+\n+    # Deny access if the user is valid, but they lack the proper object access privileges\n+    if not has_course_author_access(request.user, course_key):\n+        return HttpResponse(status=403)\n+\n+    # Retrieve the entrance exam module for the specified course (returns 404 if none found)\n+    if request.method == 'GET':\n+        return _get_entrance_exam(request, course_key)\n+\n+    # Create a new entrance exam for the specified course (returns 201 if created)\n+    elif request.method == 'POST':\n+        response_format = request.REQUEST.get('format', 'html')\n+        http_accept = request.META.get('http_accept')\n+        if response_format == 'json' or 'application/json' in http_accept:\n+            ee_min_score = request.POST.get('entrance_exam_minimum_score_pct', None)\n+\n+            # if request contains empty value or none then save the default one.\n+            entrance_exam_minimum_score_pct = float(settings.ENTRANCE_EXAM_MIN_SCORE_PCT)\n+            if ee_min_score != '' and ee_min_score is not None:\n+                entrance_exam_minimum_score_pct = float(ee_min_score)\n+            return create_entrance_exam(request, course_key, entrance_exam_minimum_score_pct)\n+        return HttpResponse(status=400)\n+\n+    # Remove the entrance exam module for the specified course (returns 204 regardless of existence)\n+    elif request.method == 'DELETE':\n+        return delete_entrance_exam(request, course_key)\n+\n+    # No other HTTP verbs/methods are supported at this time\n+    else:\n+        return HttpResponse(status=405)\n+\n+\n+def create_entrance_exam(request, course_key, entrance_exam_minimum_score_pct):\n+    \"\"\"\n+    api method to create an entrance exam.\n+    First clean out any old entrance exams.\n+    \"\"\"\n+    _delete_entrance_exam(request, course_key)\n+    return _create_entrance_exam(request=request, course_key=course_key, entrance_exam_minimum_score_pct=entrance_exam_minimum_score_pct)\n+\n+\n+def _create_entrance_exam(request, course_key, entrance_exam_minimum_score_pct=50.0):\n+    \"\"\"\n+    Internal workflow operation to create an entrance exam\n+    \"\"\"\n+\n+    # Confirm the course exists\n+    course = modulestore().get_course(course_key)\n+    if course is None:\n+        return HttpResponse(status=400)\n+\n+    # Create the entrance exam item (currently it's just a chapter)\n+    payload = {\n+        'category': \"chapter\",\n+        'display_name': \"Entrance Exam\",\n+        'parent_locator': unicode(course.location),\n+        'is_entrance_exam': True,\n+    }\n+    factory = RequestFactory()\n+    internal_request = factory.post('/', json.dumps(payload), content_type=\"application/json\")\n+    internal_request.user = request.user\n+    created_item = json.loads(create_item(internal_request).content)","path":"cms/djangoapps/contentstore/views/entrance_exam.py","position":114,"original_position":103,"commit_id":"3c669e3838a38d9eed071c0625903a4a43adb62b","original_commit_id":"6641dfb080913624eefd78a8317128759dbc2cc3","user":{"login":"cpennington","id":30236,"avatar_url":"https://avatars.githubusercontent.com/u/30236?v=3","gravatar_id":"","url":"https://api.github.com/users/cpennington","html_url":"https://github.com/cpennington","followers_url":"https://api.github.com/users/cpennington/followers","following_url":"https://api.github.com/users/cpennington/following{/other_user}","gists_url":"https://api.github.com/users/cpennington/gists{/gist_id}","starred_url":"https://api.github.com/users/cpennington/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cpennington/subscriptions","organizations_url":"https://api.github.com/users/cpennington/orgs","repos_url":"https://api.github.com/users/cpennington/repos","events_url":"https://api.github.com/users/cpennington/events{/privacy}","received_events_url":"https://api.github.com/users/cpennington/received_events","type":"User","site_admin":false},"body":"I mentioned this over hipchat, but I would have preferred to extract the data-only portions of this (perhaps into an `api.py` module), and then call that directly, rather than manufacturing a fake request.","created_at":"2015-01-22T18:38:38Z","updated_at":"2015-01-22T18:38:38Z","html_url":"https://github.com/edx/edx-platform/pull/6455#discussion_r23398109","pull_request_url":"https://api.github.com/repos/edx/edx-platform/pulls/6455","_links":{"self":{"href":"https://api.github.com/repos/edx/edx-platform/pulls/comments/23398109"},"html":{"href":"https://github.com/edx/edx-platform/pull/6455#discussion_r23398109"},"pull_request":{"href":"https://api.github.com/repos/edx/edx-platform/pulls/6455"}}},"pull_request":{"url":"https://api.github.com/repos/edx/edx-platform/pulls/6455","id":26854501,"html_url":"https://github.com/edx/edx-platform/pull/6455","diff_url":"https://github.com/edx/edx-platform/pull/6455.diff","patch_url":"https://github.com/edx/edx-platform/pull/6455.patch","issue_url":"https://api.github.com/repos/edx/edx-platform/issues/6455","number":6455,"state":"closed","locked":false,"title":"(WIP) Course Entrance Exams","user":{"login":"mattdrayer","id":4520595,"avatar_url":"https://avatars.githubusercontent.com/u/4520595?v=3","gravatar_id":"","url":"https://api.github.com/users/mattdrayer","html_url":"https://github.com/mattdrayer","followers_url":"https://api.github.com/users/mattdrayer/followers","following_url":"https://api.github.com/users/mattdrayer/following{/other_user}","gists_url":"https://api.github.com/users/mattdrayer/gists{/gist_id}","starred_url":"https://api.github.com/users/mattdrayer/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mattdrayer/subscriptions","organizations_url":"https://api.github.com/users/mattdrayer/orgs","repos_url":"https://api.github.com/users/mattdrayer/repos","events_url":"https://api.github.com/users/mattdrayer/events{/privacy}","received_events_url":"https://api.github.com/users/mattdrayer/received_events","type":"User","site_admin":false},"body":"Up for review is our new \"Entrance Exams\" feature!  There are a lot of parts comprising this functionality, so we'll try to keep track of the main points of reference in this first post.\r\n\r\n*JIRA Epic*: https://openedx.atlassian.net/browse/SOL-40\r\n*Solutions Review*: @chrisndodge \r\n*T&L Review*: @andy-armstrong \r\n*Sandbox*: http://studio.entexams.m.sandbox.edx.org/course/edX/DemoX/Demo_Course\r\n*InvisionApp*: http://invis.io/T61UAL5UX\r\n*A11y Review*:  @cptvitamin \r\n*HTML/CSS Review*: @marcotuts\r\n","created_at":"2015-01-05T22:01:52Z","updated_at":"2015-01-22T18:38:38Z","closed_at":"2015-01-16T02:43:08Z","merged_at":"2015-01-16T02:43:08Z","merge_commit_sha":"3f5c1e7fdad554293bf98183d2049bb3044c4abf","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/edx/edx-platform/pulls/6455/commits","review_comments_url":"https://api.github.com/repos/edx/edx-platform/pulls/6455/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/6455/comments","statuses_url":"https://api.github.com/repos/edx/edx-platform/statuses/3c669e3838a38d9eed071c0625903a4a43adb62b","head":{"label":"edx:mattdrayer/entrance-exams","ref":"mattdrayer/entrance-exams","sha":"3c669e3838a38d9eed071c0625903a4a43adb62b","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":"Open edX, the edX platform that powers http://edx.org","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-01-22T15:13:44Z","pushed_at":"2015-01-22T18:33:41Z","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://openedx.org/","size":1582008,"stargazers_count":1697,"watchers_count":1697,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":967,"mirror_url":null,"open_issues_count":130,"forks":967,"open_issues":130,"watchers":1697,"default_branch":"master"}},"base":{"label":"edx:master","ref":"master","sha":"27fee6203982413c02779a946ede1e45c3ecef9c","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":"Open edX, the edX platform that powers http://edx.org","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-01-22T15:13:44Z","pushed_at":"2015-01-22T18:33:41Z","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://openedx.org/","size":1582008,"stargazers_count":1697,"watchers_count":1697,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":967,"mirror_url":null,"open_issues_count":130,"forks":967,"open_issues":130,"watchers":1697,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/edx/edx-platform/pulls/6455"},"html":{"href":"https://github.com/edx/edx-platform/pull/6455"},"issue":{"href":"https://api.github.com/repos/edx/edx-platform/issues/6455"},"comments":{"href":"https://api.github.com/repos/edx/edx-platform/issues/6455/comments"},"review_comments":{"href":"https://api.github.com/repos/edx/edx-platform/pulls/6455/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/6455/commits"},"statuses":{"href":"https://api.github.com/repos/edx/edx-platform/statuses/3c669e3838a38d9eed071c0625903a4a43adb62b"}}}}