Commit 3ed41982 by Julian Arni

Contentstore views pylint fixes

parent c65c9eb0
......@@ -26,12 +26,16 @@ def has_access(user, location, role=STAFF_ROLE_NAME):
There is a super-admin permissions if user.is_staff is set
Also, since we're unifying the user database between LMS and CAS,
I'm presuming that the course instructor (formally known as admin)
will not be in both INSTRUCTOR and STAFF groups, so we have to cascade our queries here as INSTRUCTOR
has all the rights that STAFF do
will not be in both INSTRUCTOR and STAFF groups, so we have to cascade our
queries here as INSTRUCTOR has all the rights that STAFF do
'''
course_location = get_course_location_for_item(location)
_has_access = is_user_in_course_group_role(user, course_location, role)
# if we're not in STAFF, perhaps we're in INSTRUCTOR groups
if not _has_access and role == STAFF_ROLE_NAME:
_has_access = is_user_in_course_group_role(user, course_location, INSTRUCTOR_ROLE_NAME)
_has_access = is_user_in_course_group_role(
user,
course_location,
INSTRUCTOR_ROLE_NAME
)
return _has_access
......@@ -4,6 +4,7 @@ import os
import tarfile
import shutil
import cgi
from functools import partial
from tempfile import mkdtemp
from path import path
......@@ -34,7 +35,8 @@ from .access import get_location_and_verify_access
from util.json_request import JsonResponse
__all__ = ['asset_index', 'upload_asset', 'import_course', 'generate_export_course', 'export_course']
__all__ = ['asset_index', 'upload_asset', 'import_course',
'generate_export_course', 'export_course']
def assets_to_json_dict(assets):
......@@ -58,7 +60,8 @@ def assets_to_json_dict(assets):
obj["thumbnail"] = thumbnail
id_info = asset.get("_id")
if id_info:
obj["id"] = "/{tag}/{org}/{course}/{revision}/{category}/{name}".format(
obj["id"] = "/{tag}/{org}/{course}/{revision}/{category}/{name}" \
.format(
org=id_info.get("org", ""),
course=id_info.get("course", ""),
revision=id_info.get("revision", ""),
......@@ -132,14 +135,14 @@ def asset_index(request, org, course, name):
@login_required
def upload_asset(request, org, course, coursename):
'''
This method allows for POST uploading of files into the course asset library, which will
be supported by GridFS in MongoDB.
This method allows for POST uploading of files into the course asset
library, which will be supported by GridFS in MongoDB.
'''
# construct a location from the passed in path
location = get_location_and_verify_access(request, org, course, coursename)
# Does the course actually exist?!? Get anything from it to prove its existance
# Does the course actually exist?!? Get anything from it to prove its
# existence
try:
modulestore().get_item(location)
except:
......@@ -150,9 +153,10 @@ def upload_asset(request, org, course, coursename):
if 'file' not in request.FILES:
return HttpResponseBadRequest()
# compute a 'filename' which is similar to the location formatting, we're using the 'filename'
# nomenclature since we're using a FileSystem paradigm here. We're just imposing
# the Location string formatting expectations to keep things a bit more consistent
# compute a 'filename' which is similar to the location formatting, we're
# using the 'filename' nomenclature since we're using a FileSystem paradigm
# here. We're just imposing the Location string formatting expectations to
# keep things a bit more consistent
upload_file = request.FILES['file']
filename = upload_file.name
mime_type = upload_file.content_type
......@@ -160,20 +164,25 @@ def upload_asset(request, org, course, coursename):
content_loc = StaticContent.compute_location(org, course, filename)
chunked = upload_file.multiple_chunks()
sc_partial = partial(StaticContent, content_loc, filename, mime_type)
if chunked:
content = StaticContent(content_loc, filename, mime_type, upload_file.chunks())
content = sc_partial(upload_file.chunks())
temp_filepath = upload_file.temporary_file_path()
else:
content = StaticContent(content_loc, filename, mime_type, upload_file.read())
content = sc_partial(upload_file.read())
tempfile_path = None
thumbnail_content = None
thumbnail_location = None
# first let's see if a thumbnail can be created
(thumbnail_content, thumbnail_location) = contentstore().generate_thumbnail(content,
tempfile_path=None if not chunked else
upload_file.temporary_file_path())
(thumbnail_content, thumbnail_location) = contentstore().generate_thumbnail(
content,
tempfile_path=tempfile_path
)
# delete cached thumbnail even if one couldn't be created this time (else the old thumbnail will continue to show)
# delete cached thumbnail even if one couldn't be created this time (else
# the old thumbnail will continue to show)
del_cached_content(thumbnail_location)
# now store thumbnail location only if we could create it
if thumbnail_content is not None:
......@@ -186,11 +195,13 @@ def upload_asset(request, org, course, coursename):
# readback the saved content - we need the database timestamp
readback = contentstore().find(content.location)
response_payload = {'displayname': content.name,
response_payload = {
'displayname': content.name,
'uploadDate': get_default_time_display(readback.last_modified_at),
'url': StaticContent.get_url_path_from_location(content.location),
'portable_url': StaticContent.get_static_path_from_location(content.location),
'thumb_url': StaticContent.get_url_path_from_location(thumbnail_location) if thumbnail_content is not None else None,
'thumb_url': StaticContent.get_url_path_from_location(thumbnail_location)
if thumbnail_content is not None else None,
'msg': 'Upload completed'
}
......@@ -202,8 +213,8 @@ def upload_asset(request, org, course, coursename):
@login_required
def remove_asset(request, org, course, name):
'''
This method will perform a 'soft-delete' of an asset, which is basically to copy the asset from
the main GridFS collection and into a Trashcan
This method will perform a 'soft-delete' of an asset, which is basically to
copy the asset from the main GridFS collection and into a Trashcan
'''
get_location_and_verify_access(request, org, course, name)
......
......@@ -30,7 +30,8 @@ def get_checklists(request, org, course, name):
modulestore = get_modulestore(location)
course_module = modulestore.get_item(location)
# If course was created before checklists were introduced, copy them over from the template.
# If course was created before checklists were introduced, copy them over
# from the template.
copied = False
if not course_module.checklists:
course_module.checklists = CourseDescriptor.checklists.default
......@@ -68,7 +69,8 @@ def update_checklist(request, org, course, name, checklist_index=None):
if checklist_index is not None and 0 <= int(checklist_index) < len(course_module.checklists):
index = int(checklist_index)
course_module.checklists[index] = json.loads(request.body)
# seeming noop which triggers kvs to record that the metadata is not default
# seeming noop which triggers kvs to record that the metadata is
# not default
course_module.checklists = course_module.checklists
checklists, _ = expand_checklist_action_urls(course_module)
course_module.save()
......@@ -76,10 +78,13 @@ def update_checklist(request, org, course, name, checklist_index=None):
return JsonResponse(checklists[index])
else:
return HttpResponseBadRequest(
"Could not save checklist state because the checklist index was out of range or unspecified.",
content_type="text/plain")
( "Could not save checklist state because the checklist index "
"was out of range or unspecified."),
content_type="text/plain"
)
elif request.method == 'GET':
# In the JavaScript view initialize method, we do a fetch to get all the checklists.
# In the JavaScript view initialize method, we do a fetch to get all
# the checklists.
checklists, modified = expand_checklist_action_urls(course_module)
if modified:
course_module.save()
......
......@@ -2,7 +2,8 @@ import json
import logging
from collections import defaultdict
from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseForbidden
from django.http import HttpResponse, HttpResponseBadRequest, \
HttpResponseForbidden
from django.contrib.auth.decorators import login_required
from django.views.decorators.http import require_http_methods
from django.core.exceptions import PermissionDenied
......@@ -72,10 +73,15 @@ def edit_subsection(request, location):
except ItemNotFoundError:
return HttpResponseBadRequest()
lms_link = get_lms_link_for_item(location, course_id=course.location.course_id)
preview_link = get_lms_link_for_item(location, course_id=course.location.course_id, preview=True)
lms_link = get_lms_link_for_item(
location, course_id=course.location.course_id
)
preview_link = get_lms_link_for_item(
location, course_id=course.location.course_id, preview=True
)
# make sure that location references a 'sequential', otherwise return BadRequest
# make sure that location references a 'sequential', otherwise return
# BadRequest
if item.location.category != 'sequential':
return HttpResponseBadRequest()
......@@ -83,18 +89,22 @@ def edit_subsection(request, location):
# we're for now assuming a single parent
if len(parent_locs) != 1:
logging.error('Multiple (or none) parents have been found for {0}'.format(location))
logging.error(
'Multiple (or none) parents have been found for' + location
)
# this should blow up if we don't find any parents, which would be erroneous
parent = modulestore().get_item(parent_locs[0])
# remove all metadata from the generic dictionary that is presented in a more normalized UI
# remove all metadata from the generic dictionary that is presented in a
# more normalized UI
policy_metadata = dict(
(field.name, field.read_from(item))
for field
in item.fields
if field.name not in ['display_name', 'start', 'due', 'format'] and field.scope == Scope.settings
if field.name not in ['display_name', 'start', 'due', 'format']
and field.scope == Scope.settings
)
can_view_live = False
......@@ -105,8 +115,10 @@ def edit_subsection(request, location):
can_view_live = True
break
return render_to_response('edit_subsection.html',
{'subsection': item,
return render_to_response(
'edit_subsection.html',
{
'subsection': item,
'context_course': course,
'new_unit_category': 'vertical',
'lms_link': lms_link,
......@@ -117,7 +129,8 @@ def edit_subsection(request, location):
'policy_metadata': policy_metadata,
'subsection_units': subsection_units,
'can_view_live': can_view_live
})
}
)
@login_required
......@@ -125,7 +138,7 @@ def edit_unit(request, location):
"""
Display an editing page for the specified module.
Expects a GET request with the parameter 'id'.
Expects a GET request with the parameter `id`.
id: A Location URL
"""
......@@ -141,7 +154,10 @@ def edit_unit(request, location):
item = modulestore().get_item(location, depth=1)
except ItemNotFoundError:
return HttpResponseBadRequest()
lms_link = get_lms_link_for_item(item.location, course_id=course.location.course_id)
lms_link = get_lms_link_for_item(
item.location,
course_id=course.location.course_id
)
component_templates = defaultdict(list)
for category in COMPONENT_TYPES:
......@@ -162,17 +178,19 @@ def edit_unit(request, location):
template.get('template_id')
))
# Check if there are any advanced modules specified in the course policy. These modules
# should be specified as a list of strings, where the strings are the names of the modules
# in ADVANCED_COMPONENT_TYPES that should be enabled for the course.
# Check if there are any advanced modules specified in the course policy.
# These modules should be specified as a list of strings, where the strings
# are the names of the modules in ADVANCED_COMPONENT_TYPES that should be
# enabled for the course.
course_advanced_keys = course.advanced_modules
# Set component types according to course policy file
if isinstance(course_advanced_keys, list):
for category in course_advanced_keys:
if category in ADVANCED_COMPONENT_TYPES:
# Do I need to allow for boilerplates or just defaults on the class? i.e., can an advanced
# have more than one entry in the menu? one for default and others for prefilled boilerplates?
# Do I need to allow for boilerplates or just defaults on the
# class? i.e., can an advanced have more than one entry in the
# menu? one for default and others for prefilled boilerplates?
try:
component_class = XModuleDescriptor.load_class(category)
......@@ -183,13 +201,16 @@ def edit_unit(request, location):
None # don't override default data
))
except PluginMissingError:
# dhm: I got this once but it can happen any time the course author configures
# an advanced component which does not exist on the server. This code here merely
# prevents any authors from trying to instantiate the non-existent component type
# by not showing it in the menu
# dhm: I got this once but it can happen any time the
# course author configures an advanced component which does
# not exist on the server. This code here merely
# prevents any authors from trying to instantiate the
# non-existent component type by not showing it in the menu
pass
else:
log.error("Improper format for course advanced keys! {0}".format(course_advanced_keys))
log.error(
"Improper format for course advanced keys!" + course_advanced_keys
)
components = [
component.location.url()
......@@ -201,16 +222,20 @@ def edit_unit(request, location):
# this will need to change to check permissions correctly so as
# to pick the correct parent subsection
containing_subsection_locs = modulestore().get_parent_locations(location, None)
containing_subsection_locs = modulestore().get_parent_locations(
location, None
)
containing_subsection = modulestore().get_item(containing_subsection_locs[0])
containing_section_locs = modulestore().get_parent_locations(containing_subsection.location, None)
containing_section_locs = modulestore().get_parent_locations(
containing_subsection.location, None
)
containing_section = modulestore().get_item(containing_section_locs[0])
# cdodge hack. We're having trouble previewing drafts via jump_to redirect
# so let's generate the link url here
# need to figure out where this item is in the list of children as the preview will need this
# need to figure out where this item is in the list of children as the
# preview will need this
index = 1
for child in containing_subsection.get_children():
if child.location == item.location:
......@@ -219,7 +244,10 @@ def edit_unit(request, location):
preview_lms_base = settings.MITX_FEATURES.get('PREVIEW_LMS_BASE')
preview_lms_link = '//{preview_lms_base}/courses/{org}/{course}/{course_name}/courseware/{section}/{subsection}/{index}'.format(
preview_lms_link = (
'//{preview_lms_base}/courses/{org}/{course}/'
'{course_name}/courseware/{section}/{subsection}/{index}'
).format(
preview_lms_base=preview_lms_base,
lms_base=settings.LMS_BASE,
org=course.location.org,
......@@ -227,7 +255,8 @@ def edit_unit(request, location):
course_name=course.location.name,
section=containing_section.location.name,
subsection=containing_subsection.location.name,
index=index)
index=index
)
unit_state = compute_unit_state(item)
......@@ -240,11 +269,13 @@ def edit_unit(request, location):
'draft_preview_link': preview_lms_link,
'published_preview_link': lms_link,
'subsection': containing_subsection,
'release_date': get_default_time_display(containing_subsection.lms.start) if containing_subsection.lms.start is not None else None,
'release_date': get_default_time_display(containing_subsection.lms.start)
if containing_subsection.lms.start is not None else None,
'section': containing_section,
'new_unit_category': 'vertical',
'unit_state': unit_state,
'published_date': get_default_time_display(item.cms.published_date) if item.cms.published_date is not None else None
'published_date': get_default_time_display(item.cms.published_date)
if item.cms.published_date is not None else None
})
......@@ -253,9 +284,10 @@ def edit_unit(request, location):
@require_http_methods(("GET", "POST", "PUT"))
@ensure_csrf_cookie
def assignment_type_update(request, org, course, category, name):
'''
CRUD operations on assignment types for sections and subsections and anything else gradable.
'''
"""
CRUD operations on assignment types for sections and subsections and
anything else gradable.
"""
location = Location(['i4x', org, course, category, name])
if not has_access(request.user, location):
return HttpResponseForbidden()
......@@ -263,7 +295,9 @@ def assignment_type_update(request, org, course, category, name):
if request.method == 'GET':
return JsonResponse(CourseGradingModel.get_section_grader_type(location))
elif request.method in ('POST', 'PUT'): # post or put, doesn't matter.
return JsonResponse(CourseGradingModel.update_section_grader_type(location, request.POST))
return JsonResponse(CourseGradingModel.update_section_grader_type(
location, request.POST
))
@login_required
......@@ -276,8 +310,8 @@ def create_draft(request):
if not has_access(request.user, location):
raise PermissionDenied()
# This clones the existing item location to a draft location (the draft is implicit,
# because modulestore is a Draft modulestore)
# This clones the existing item location to a draft location (the draft is
# implicit, because modulestore is a Draft modulestore)
modulestore().convert_to_draft(location)
return HttpResponse()
......@@ -286,7 +320,9 @@ def create_draft(request):
@login_required
@expect_json
def publish_draft(request):
"Publish a draft"
"""
Publish a draft
"""
location = request.POST['id']
# check permissions for this user within this course
......@@ -294,7 +330,10 @@ def publish_draft(request):
raise PermissionDenied()
item = modulestore().get_item(location)
_xmodule_recurse(item, lambda i: modulestore().publish(i.location, request.user.id))
_xmodule_recurse(
item,
lambda i: modulestore().publish(i.location, request.user.id)
)
return HttpResponse()
......@@ -328,13 +367,23 @@ def module_info(request, module_location):
raise PermissionDenied()
rewrite_static_links = request.GET.get('rewrite_url_links', 'True') in ['True', 'true']
logging.debug('rewrite_static_links = {0} {1}'.format(request.GET.get('rewrite_url_links', 'False'), rewrite_static_links))
logging.debug('rewrite_static_links = {0} {1}'.format(
request.GET.get('rewrite_url_links', 'False'),
rewrite_static_links)
)
# check that logged in user has permissions to this item
if not has_access(request.user, location):
raise PermissionDenied()
if request.method == 'GET':
return JsonResponse(get_module_info(get_modulestore(location), location, rewrite_static_links=rewrite_static_links))
return JsonResponse(get_module_info(
get_modulestore(location),
location,
rewrite_static_links=rewrite_static_links
))
elif request.method in ("POST", "PUT"):
return JsonResponse(set_module_info(get_modulestore(location), location, request.POST))
return JsonResponse(set_module_info(
get_modulestore(location),
location, request.POST
))
......@@ -82,7 +82,9 @@ def course_index(request, org, course, name):
'context_course': course,
'lms_link': lms_link,
'sections': sections,
'course_graders': json.dumps(CourseGradingModel.fetch(course.location).graders),
'course_graders': json.dumps(
CourseGradingModel.fetch(course.location).graders
),
'parent_location': course.location,
'new_section_category': 'chapter',
'new_subsection_category': 'sequential',
......@@ -120,24 +122,31 @@ def create_new_course(request):
except ItemNotFoundError:
pass
if existing_course is not None:
return JsonResponse(
{
'ErrMsg': _('There is already a course defined with the same organization, course number, and course run. Please change either organization or course number to be unique.'),
'OrgErrMsg': _('Please change either the organization or course number so that it is unique.'),
'CourseErrMsg': _('Please change either the organization or course number so that it is unique.'),
}
)
return JsonResponse({
'ErrMsg': _(('There is already a course defined with the same '
'organization, course number, and course run. Please '
'change either organization or course number to be '
'unique.')),
'OrgErrMsg': _(('Please change either the organization or '
'course number so that it is unique.')),
'CourseErrMsg': _(('Please change either the organization or '
'course number so that it is unique.')),
})
course_search_location = ['i4x', dest_location.org, dest_location.course, 'course', None]
course_search_location = ['i4x', dest_location.org, dest_location.course,
'course', None
]
courses = modulestore().get_items(course_search_location)
if len(courses) > 0:
return JsonResponse(
{
'ErrMsg': _('There is already a course defined with the same organization and course number. Please change at least one field to be unique.'),
'OrgErrMsg': _('Please change either the organization or course number so that it is unique.'),
'CourseErrMsg': _('Please change either the organization or course number so that it is unique.'),
}
)
return JsonResponse({
'ErrMsg': _(('There is already a course defined with the same '
'organization and course number. Please '
'change at least one field to be unique.')),
'OrgErrMsg': _(('Please change either the organization or '
'course number so that it is unique.')),
'CourseErrMsg': _(('Please change either the organization or '
'course number so that it is unique.')),
})
# instantiate the CourseDescriptor and then persist it
# note: no system to pass
......@@ -145,11 +154,17 @@ def create_new_course(request):
metadata = {}
else:
metadata = {'display_name': display_name}
modulestore('direct').create_and_save_xmodule(dest_location, metadata=metadata)
modulestore('direct').create_and_save_xmodule(
dest_location,
metadata=metadata
)
new_course = modulestore('direct').get_item(dest_location)
# clone a default 'about' overview module as well
dest_about_location = dest_location.replace(category='about', name='overview')
dest_about_location = dest_location.replace(
category='about',
name='overview'
)
overview_template = AboutDescriptor.get_template('overview.yaml')
modulestore('direct').create_and_save_xmodule(
dest_about_location,
......@@ -164,7 +179,8 @@ def create_new_course(request):
# seed the forums
seed_permissions_roles(new_course.location.course_id)
# auto-enroll the course creator in the course so that "View Live" will work.
# auto-enroll the course creator in the course so that "View Live" will
# work.
CourseEnrollment.enroll(request.user, new_course.location.course_id)
return JsonResponse({'id': new_course.location.url()})
......@@ -174,7 +190,8 @@ def create_new_course(request):
@ensure_csrf_cookie
def course_info(request, org, course, name, provided_id=None):
"""
Send models and views as well as html for editing the course info to the client.
Send models and views as well as html for editing the course info to the
client.
org, course, name: Attributes of the Location for the item to edit
"""
......@@ -189,7 +206,8 @@ def course_info(request, org, course, name, provided_id=None):
'context_course': course_module,
'url_base': "/" + org + "/" + course + "/",
'course_updates': json.dumps(get_course_updates(location)),
'handouts_location': Location(['i4x', org, course, 'course_info', 'handouts']).url()
'handouts_location': Location(['i4x', org, course, 'course_info',
'handouts']).url()
})
......@@ -202,14 +220,16 @@ def course_info_updates(request, org, course, provided_id=None):
restful CRUD operations on course_info updates.
org, course: Attributes of the Location for the item to edit
provided_id should be none if it's new (create) and a composite of the update db id + index otherwise.
provided_id should be none if it's new (create) and a composite of the
update db id + index otherwise.
"""
# ??? No way to check for access permission afaik
# get current updates
location = ['i4x', org, course, 'course_info', "updates"]
# Hmmm, provided_id is coming as empty string on create whereas I believe it used to be None :-(
# Possibly due to my removing the seemingly redundant pattern in urls.py
# Hmmm, provided_id is coming as empty string on create whereas I believe
# it used to be None :-( Possibly due to my removing the seemingly
# redundant pattern in urls.py
if provided_id == '':
provided_id = None
......@@ -221,13 +241,19 @@ def course_info_updates(request, org, course, provided_id=None):
return JsonResponse(get_course_updates(location))
elif request.method == 'DELETE':
try:
return JsonResponse(delete_course_update(location, request.POST, provided_id))
return JsonResponse(delete_course_update(location, request.POST,
provided_id
))
except:
return HttpResponseBadRequest("Failed to delete",
content_type="text/plain")
elif request.method in ('POST', 'PUT'): # can be either and sometimes django is rewriting one to the other
elif request.method in ('POST', 'PUT'): # can be either and sometimes
# django is rewriting one to the
# other
try:
return JsonResponse(update_course_updates(location, request.POST, provided_id))
return JsonResponse(update_course_updates(location, request.POST,
provided_id
))
except:
return HttpResponseBadRequest("Failed to save",
content_type="text/plain")
......@@ -237,7 +263,8 @@ def course_info_updates(request, org, course, provided_id=None):
@ensure_csrf_cookie
def get_course_settings(request, org, course, name):
"""
Send models and views as well as html for editing the course settings to the client.
Send models and views as well as html for editing the course settings to
the client.
org, course, name: Attributes of the Location for the item to edit
"""
......@@ -253,7 +280,9 @@ def get_course_settings(request, org, course, name):
"course": course,
"name": name,
"section": "details"}),
'about_page_editable': not settings.MITX_FEATURES.get('ENABLE_MKTG_SITE', False)
'about_page_editable': not settings.MITX_FEATURES.get(
'ENABLE_MKTG_SITE', False
)
})
......@@ -261,7 +290,8 @@ def get_course_settings(request, org, course, name):
@ensure_csrf_cookie
def course_config_graders_page(request, org, course, name):
"""
Send models and views as well as html for editing the course settings to the client.
Send models and views as well as html for editing the course settings to
the client.
org, course, name: Attributes of the Location for the item to edit
"""
......@@ -281,7 +311,8 @@ def course_config_graders_page(request, org, course, name):
@ensure_csrf_cookie
def course_config_advanced_page(request, org, course, name):
"""
Send models and views as well as html for editing the advanced course settings to the client.
Send models and views as well as html for editing the advanced course
settings to the client.
org, course, name: Attributes of the Location for the item to edit
"""
......@@ -301,8 +332,9 @@ def course_config_advanced_page(request, org, course, name):
@ensure_csrf_cookie
def course_settings_updates(request, org, course, name, section):
"""
restful CRUD operations on course settings. This differs from get_course_settings by communicating purely
through json (not rendering any html) and handles section level operations rather than whole page.
Restful CRUD operations on course settings. This differs from
get_course_settings by communicating purely through json (not rendering any
html) and handles section level operations rather than whole page.
org, course: Attributes of the Location for the item to edit
section: one of details, faculty, grading, problems, discussions
......@@ -318,9 +350,15 @@ def course_settings_updates(request, org, course, name, section):
if request.method == 'GET':
# Cannot just do a get w/o knowing the course name :-(
return JsonResponse(manager.fetch(Location(['i4x', org, course, 'course', name])), encoder=CourseSettingsEncoder)
return JsonResponse(
manager.fetch(Location(['i4x', org, course, 'course', name])),
encoder=CourseSettingsEncoder
)
elif request.method in ('POST', 'PUT'): # post or put, doesn't matter.
return JsonResponse(manager.update_from_json(request.POST), encoder=CourseSettingsEncoder)
return JsonResponse(
manager.update_from_json(request.POST),
encoder=CourseSettingsEncoder
)
@expect_json
......@@ -329,8 +367,9 @@ def course_settings_updates(request, org, course, name, section):
@ensure_csrf_cookie
def course_grader_updates(request, org, course, name, grader_index=None):
"""
restful CRUD operations on course_info updates. This differs from get_course_settings by communicating purely
through json (not rendering any html) and handles section level operations rather than whole page.
Restful CRUD operations on course_info updates. This differs from
get_course_settings by communicating purely through json (not rendering any
html) and handles section level operations rather than whole page.
org, course: Attributes of the Location for the item to edit
"""
......@@ -339,13 +378,18 @@ def course_grader_updates(request, org, course, name, grader_index=None):
if request.method == 'GET':
# Cannot just do a get w/o knowing the course name :-(
return JsonResponse(CourseGradingModel.fetch_grader(Location(location), grader_index))
return JsonResponse(CourseGradingModel.fetch_grader(
Location(location), grader_index
))
elif request.method == "DELETE":
# ??? Should this return anything? Perhaps success fail?
CourseGradingModel.delete_grader(Location(location), grader_index)
return JsonResponse()
else: # post or put, doesn't matter.
return JsonResponse(CourseGradingModel.update_grader_from_json(Location(location), request.POST))
return JsonResponse(CourseGradingModel.update_grader_from_json(
Location(location),
request.POST
))
# # NB: expect_json failed on ["key", "key2"] and json payload
......@@ -354,8 +398,9 @@ def course_grader_updates(request, org, course, name, grader_index=None):
@ensure_csrf_cookie
def course_advanced_updates(request, org, course, name):
"""
restful CRUD operations on metadata. The payload is a json rep of the metadata dicts. For delete, otoh,
the payload is either a key or a list of keys to delete.
Restful CRUD operations on metadata. The payload is a json rep of the
metadata dicts. For delete, otoh, the payload is either a key or a list of
keys to delete.
org, course: Attributes of the Location for the item to edit
"""
......@@ -364,20 +409,26 @@ def course_advanced_updates(request, org, course, name):
if request.method == 'GET':
return JsonResponse(CourseMetadata.fetch(location))
elif request.method == 'DELETE':
return JsonResponse(CourseMetadata.delete_key(location, json.loads(request.body)))
return JsonResponse(CourseMetadata.delete_key(
location,
json.loads(request.body)
))
else:
# NOTE: request.POST is messed up because expect_json
# cloned_request.POST.copy() is creating a defective entry w/ the whole payload as the key
# cloned_request.POST.copy() is creating a defective entry w/ the whole
# payload as the key
request_body = json.loads(request.body)
# Whether or not to filter the tabs key out of the settings metadata
filter_tabs = True
# Check to see if the user instantiated any advanced components. This is a hack
# that does the following :
# 1) adds/removes the open ended panel tab to a course automatically if the user
# has indicated that they want to edit the combinedopendended or peergrading module
# 2) adds/removes the notes panel tab to a course automatically if the user has
# indicated that they want the notes module enabled in their course
# Check to see if the user instantiated any advanced components. This
# is a hack that does the following :
# 1) adds/removes the open ended panel tab to a course automatically
# if the user has indicated that they want to edit the
# combinedopendended or peergrading module
# 2) adds/removes the notes panel tab to a course automatically if
# the user has indicated that they want the notes module enabled in
# their course
# TODO refactor the above into distinct advanced policy settings
if ADVANCED_COMPONENT_POLICY_KEY in request_body:
# Get the course so that we can scrape current tabs
......@@ -389,19 +440,25 @@ def course_advanced_updates(request, org, course, name):
'notes': NOTE_COMPONENT_TYPES,
}
# Check to see if the user instantiated any notes or open ended components
# Check to see if the user instantiated any notes or open ended
# components
for tab_type in tab_component_map.keys():
component_types = tab_component_map.get(tab_type)
found_ac_type = False
for ac_type in component_types:
if ac_type in request_body[ADVANCED_COMPONENT_POLICY_KEY]:
# Add tab to the course if needed
changed, new_tabs = add_extra_panel_tab(tab_type, course_module)
# If a tab has been added to the course, then send the metadata along to CourseMetadata.update_from_json
changed, new_tabs = add_extra_panel_tab(
tab_type,
course_module
)
# If a tab has been added to the course, then send the
# metadata along to CourseMetadata.update_from_json
if changed:
course_module.tabs = new_tabs
request_body.update({'tabs': new_tabs})
# Indicate that tabs should not be filtered out of the metadata
# Indicate that tabs should not be filtered out of
# the metadata
filter_tabs = False
# Set this flag to avoid the tab removal code below.
found_ac_type = True
......@@ -410,18 +467,26 @@ def course_advanced_updates(request, org, course, name):
# we may need to remove the tab from the course.
if not found_ac_type:
# Remove tab from the course if needed
changed, new_tabs = remove_extra_panel_tab(tab_type, course_module)
changed, new_tabs = remove_extra_panel_tab(
tab_type, course_module
)
if changed:
course_module.tabs = new_tabs
request_body.update({'tabs': new_tabs})
# Indicate that tabs should *not* be filtered out of the metadata
# Indicate that tabs should *not* be filtered out of
# the metadata
filter_tabs = False
try:
return JsonResponse(CourseMetadata.update_from_json(location,
return JsonResponse(CourseMetadata.update_from_json(
location,
request_body,
filter_tabs=filter_tabs))
filter_tabs=filter_tabs
))
except (TypeError, ValueError) as err:
return HttpResponseBadRequest("Incorrect setting format. " + str(err), content_type="text/plain")
return HttpResponseBadRequest(
"Incorrect setting format. " + str(err),
content_type="text/plain"
)
class TextbookValidationError(Exception):
......@@ -498,7 +563,8 @@ def textbook_index(request, org, course, name):
if request.is_ajax():
if request.method == 'GET':
return JsonResponse(course_module.pdf_textbooks)
elif request.method in ('POST', 'PUT'): # can be either and sometimes django is rewriting one to the other
elif request.method in ('POST', 'PUT'): # can be either and sometimes
# django is rewriting one to the other
try:
textbooks = validate_textbooks_json(request.body)
except TextbookValidationError as err:
......@@ -517,7 +583,10 @@ def textbook_index(request, org, course, name):
# Save the data that we've just changed to the underlying
# MongoKeyValueStore before we update the mongo datastore.
course_module.save()
store.update_metadata(course_module.location, own_metadata(course_module))
store.update_metadata(
course_module.location,
own_metadata(course_module)
)
return JsonResponse(course_module.pdf_textbooks)
else:
upload_asset_url = reverse('upload_asset', kwargs={
......@@ -599,7 +668,8 @@ def textbook_by_id(request, org, course, name, tid):
if not textbook:
return JsonResponse(status=404)
return JsonResponse(textbook)
elif request.method in ('POST', 'PUT'): # can be either and sometimes django is rewriting one to the other
elif request.method in ('POST', 'PUT'): # can be either and sometimes
# django is rewriting one to the other
try:
new_textbook = validate_textbook_json(request.body)
except TextbookValidationError as err:
......@@ -616,7 +686,10 @@ def textbook_by_id(request, org, course, name, tid):
# Save the data that we've just changed to the underlying
# MongoKeyValueStore before we update the mongo datastore.
course_module.save()
store.update_metadata(course_module.location, own_metadata(course_module))
store.update_metadata(
course_module.location,
own_metadata(course_module)
)
return JsonResponse(new_textbook, status=201)
elif request.method == 'DELETE':
if not textbook:
......@@ -626,5 +699,8 @@ def textbook_by_id(request, org, course, name, tid):
new_textbooks.extend(course_module.pdf_textbooks[i + 1:])
course_module.pdf_textbooks = new_textbooks
course_module.save()
store.update_metadata(course_module.location, own_metadata(course_module))
store.update_metadata(
course_module.location,
own_metadata(course_module)
)
return JsonResponse()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment