checklist.py 5.94 KB
Newer Older
Steve Strassmann committed
1
import json
2
import copy
Steve Strassmann committed
3

4 5
from util.json_request import JsonResponse
from django.http import HttpResponseBadRequest
6
from django.contrib.auth.decorators import login_required
7
from django.views.decorators.http import require_http_methods
8
from django.core.urlresolvers import reverse
9 10
from django_future.csrf import ensure_csrf_cookie
from mitxmako.shortcuts import render_to_response
11 12 13
from django.http import HttpResponseNotFound
from django.core.exceptions import PermissionDenied
from xmodule.modulestore.django import loc_mapper
Steve Strassmann committed
14 15 16

from xmodule.modulestore.inheritance import own_metadata

17

18
from ..utils import get_modulestore
19
from .access import has_access
20
from xmodule.course_module import CourseDescriptor
21
from xmodule.modulestore.locator import BlockUsageLocator
22

23
__all__ = ['checklists_handler']
24

25
@require_http_methods(("GET", "POST", "PUT"))
26
@login_required
27 28
@ensure_csrf_cookie
def checklists_handler(request, tag=None, course_id=None, branch=None, version_guid=None, block=None, checklist_index=None):
29
    """
30
    The restful handler for checklists.
31

32 33 34 35 36
    GET
        html: return html page for all checklists
        json: return json representing all checklists. checklist_index is not supported for GET at this time.
    POST or PUT
        json: updates the checked state for items within a particular checklist. checklist_index is required.
37
    """
38 39 40
    location = BlockUsageLocator(course_id=course_id, branch=branch, version_guid=version_guid, usage_id=block)
    if not has_access(request.user, location):
        raise PermissionDenied()
41

42
    old_location = loc_mapper().translate_locator_to_location(location)
43

44 45
    modulestore = get_modulestore(old_location)
    course_module = modulestore.get_item(old_location)
46

47 48 49 50 51 52 53 54
    json_request = 'application/json' in request.META.get('HTTP_ACCEPT', 'application/json')
    if request.method == 'GET':
        # If course was created before checklists were introduced, copy them over
        # from the template.
        if not course_module.checklists:
            course_module.checklists = CourseDescriptor.checklists.default
            course_module.save()
            modulestore.update_metadata(old_location, own_metadata(course_module))
55

56 57 58 59 60 61 62 63
        expanded_checklists = expand_all_action_urls(course_module)
        if json_request:
            return JsonResponse(expanded_checklists)
        else:
            handler_url = location.url_reverse('checklists/', '')
            return render_to_response('checklists.html',
                                      {
                                          'handler_url': handler_url,
64 65
                                          # context_course is used by analytics
                                          'context_course': course_module,
66 67 68 69
                                          'checklists': expanded_checklists
                                      })
    elif json_request:
        # Can now assume POST or PUT because GET handled above.
70 71
        if checklist_index is not None and 0 <= int(checklist_index) < len(course_module.checklists):
            index = int(checklist_index)
72 73 74 75 76 77 78
            persisted_checklist = course_module.checklists[index]
            modified_checklist = json.loads(request.body)
            # Only thing the user can modify is the "checked" state.
            # We don't want to persist what comes back from the client because it will
            # include the expanded action URLs (which are non-portable).
            for item_index, item in enumerate(modified_checklist.get('items')):
                persisted_checklist['items'][item_index]['is_checked'] = item['is_checked']
79 80
            # seeming noop which triggers kvs to record that the metadata is
            # not default
81
            course_module.checklists = course_module.checklists
82
            course_module.save()
83
            modulestore.update_metadata(old_location, own_metadata(course_module))
84 85
            expanded_checklist = expand_checklist_action_url(course_module, persisted_checklist)
            return JsonResponse(expanded_checklist)
86 87
        else:
            return HttpResponseBadRequest(
88
                ( "Could not save checklist state because the checklist index "
89
                  "was out of range or unspecified."),
90 91
                content_type="text/plain"
            )
92 93
    else:
        return HttpResponseNotFound()
94 95


cahrens committed
96
def expand_all_action_urls(course_module):
97 98 99 100 101 102 103 104 105 106
    """
    Gets the checklists out of the course module and expands their action urls.

    Returns a copy of the checklists with modified urls, without modifying the persisted version
    of the checklists.
    """
    expanded_checklists = []
    for checklist in course_module.checklists:
        expanded_checklists.append(expand_checklist_action_url(course_module, checklist))
    return expanded_checklists
107 108


109
def expand_checklist_action_url(course_module, checklist):
110
    """
111
    Expands the action URLs for a given checklist and returns the modified version.
112

113
    The method does a copy of the input checklist and does not modify the input argument.
114
    """
115
    expanded_checklist = copy.deepcopy(checklist)
116
    oldurlconf_map = {
117
        "SettingsDetails": "settings_details",
118
        "SettingsGrading": "settings_grading"
cahrens committed
119
    }
120

121 122 123 124 125
    urlconf_map = {
        "ManageUsers": "course_team",
        "CourseOutline": "course"
    }

126 127
    for item in expanded_checklist.get('items'):
        action_url = item.get('action_url')
128 129
        if action_url in urlconf_map:
            url_prefix = urlconf_map[action_url]
130 131
            ctx_loc = course_module.location
            location = loc_mapper().translate_location(ctx_loc.course_id, ctx_loc, False, True)
132 133 134
            item['action_url'] = location.url_reverse(url_prefix, '')
        elif action_url in oldurlconf_map:
            urlconf_name = oldurlconf_map[action_url]
135 136 137 138 139
            item['action_url'] = reverse(urlconf_name, kwargs={
                'org': course_module.location.org,
                'course': course_module.location.course,
                'name': course_module.location.name,
            })
140 141

    return expanded_checklist