diff --git a/lms/djangoapps/courseware/content_parser.py b/lms/djangoapps/courseware/content_parser.py deleted file mode 100644 index d1d9cf3980..0000000000 --- a/lms/djangoapps/courseware/content_parser.py +++ /dev/null @@ -1,205 +0,0 @@ -''' -courseware/content_parser.py - -This file interfaces between all courseware modules and the top-level course.xml file for a course. - -Does some caching (to be explained). - -''' - -import logging -import os -import sys -import urllib - -from lxml import etree -from util.memcache import fasthash - -from django.conf import settings - -from student.models import UserProfile -from student.models import UserTestGroup -from mitxmako.shortcuts import render_to_string -from util.cache import cache -from multicourse import multicourse_settings -import xmodule - -''' This file will eventually form an abstraction layer between the -course XML file and the rest of the system. -''' - -# ==== This section has no direct dependencies on django ==================================== -# NOTE: it does still have some indirect dependencies: -# util.memcache.fasthash (which does not depend on memcache at all) -# - -log = logging.getLogger("mitx.courseware") - -def format_url_params(params): - return [ urllib.quote(string.replace(' ','_')) for string in params ] - -def id_tag(course): - ''' Tag all course elements with unique IDs ''' - default_ids = xmodule.get_default_ids() - - # Tag elements with unique IDs - elements = course.xpath("|".join('//' + c for c in default_ids)) - for elem in elements: - if elem.get('id'): - pass - elif elem.get(default_ids[elem.tag]): - new_id = elem.get(default_ids[elem.tag]) - # Convert to alphanumeric - new_id = "".join(a for a in new_id if a.isalnum()) - - # Without this, a conflict may occur between an html or youtube id - new_id = default_ids[elem.tag] + new_id - elem.set('id', new_id) - else: - elem.set('id', "id" + fasthash(etree.tostring(elem))) - -def propogate_downward_tag(element, attribute_name, parent_attribute = None): - ''' This call is to pass down an attribute to all children. If an element - has this attribute, it will be "inherited" by all of its children. If a - child (A) already has that attribute, A will keep the same attribute and - all of A's children will inherit A's attribute. This is a recursive call.''' - - if (parent_attribute is None): - #This is the entry call. Select all elements with this attribute - all_attributed_elements = element.xpath("//*[@" + attribute_name +"]") - for attributed_element in all_attributed_elements: - attribute_value = attributed_element.get(attribute_name) - for child_element in attributed_element: - propogate_downward_tag(child_element, attribute_name, attribute_value) - else: - '''The hack below is because we would get _ContentOnlyELements from the - iterator that can't have attributes set. We can't find API for it. If we - ever have an element which subclasses BaseElement, we will not tag it''' - if not element.get(attribute_name) and type(element) == etree._Element: - element.set(attribute_name, parent_attribute) - - for child_element in element: - propogate_downward_tag(child_element, attribute_name, parent_attribute) - else: - #This element would have already been found by Xpath, so we return - #for now and trust that this element will get its turn to propogate - #to its children later. - return - - -def course_xml_process(tree): - ''' Do basic pre-processing of an XML tree. Assign IDs to all - items without. Propagate due dates, grace periods, etc. to child - items. - ''' - process_includes(tree) - replace_custom_tags(tree) - id_tag(tree) - propogate_downward_tag(tree, "due") - propogate_downward_tag(tree, "graded") - propogate_downward_tag(tree, "graceperiod") - propogate_downward_tag(tree, "showanswer") - propogate_downward_tag(tree, "rerandomize") - return tree - - -def process_includes_dir(tree, dir): - """ - Process tree to replace all tags - with the contents of the file specified, relative to dir - """ - includes = tree.findall('.//include') - for inc in includes: - file = inc.get('file') - if file is not None: - try: - ifp = open(os.path.join(dir, file)) - except Exception: - log.exception('Error in problem xml include: %s' % (etree.tostring(inc, pretty_print=True))) - log.exception('Cannot find file %s in %s' % (file, dir)) - raise - try: - # read in and convert to XML - incxml = etree.XML(ifp.read()) - except Exception: - log.exception('Error in problem xml include: %s' % (etree.tostring(inc, pretty_print=True))) - log.exception('Cannot parse XML in %s' % (file)) - raise - # insert new XML into tree in place of inlcude - parent = inc.getparent() - parent.insert(parent.index(inc), incxml) - parent.remove(inc) - - -def replace_custom_tags_dir(tree, dir): - ''' - Process tree to replace all custom tags defined in dir. - ''' - tags = os.listdir(dir) - for tag in tags: - for element in tree.iter(tag): - element.tag = 'customtag' - impl = etree.SubElement(element, 'impl') - impl.text = tag - -def parse_course_file(filename, options, namespace): - ''' - Parse a course file with the given options, and return the resulting - xml tree object. - - Options should be a dictionary including keys - 'dev_content': bool, - 'groups' : [list, of, user, groups] - - namespace is used to in searching for the file. Could be e.g. 'course', - 'sections'. - ''' - xml = etree.XML(render_to_string(filename, options, namespace=namespace)) - return course_xml_process(xml) - - -# ==== All Django-specific code below ============================================= - -def user_groups(user): - if not user.is_authenticated(): - return [] - - # TODO: Rewrite in Django - key = 'user_group_names_{user.id}'.format(user=user) - cache_expiration = 60 * 60 # one hour - - # Kill caching on dev machines -- we switch groups a lot - group_names = cache.get(key) - - if group_names is None: - group_names = [u.name for u in UserTestGroup.objects.filter(users=user)] - cache.set(key, group_names, cache_expiration) - - return group_names - - -def get_options(user): - return {'dev_content': settings.DEV_CONTENT, - 'groups': user_groups(user)} - - -def process_includes(tree): - '''Replace tags with the contents from the course directory''' - process_includes_dir(tree, settings.DATA_DIR) - - -def replace_custom_tags(tree): - '''Replace custom tags defined in our custom_tags dir''' - replace_custom_tags_dir(tree, settings.DATA_DIR+'/custom_tags') - - -def sections_dir(coursename=None): - ''' Get directory where sections information is stored. - ''' - # if a specific course is specified, then use multicourse to get the - # right path to the course XML directory - xp = '' - if coursename and settings.ENABLE_MULTICOURSE: - xp = multicourse_settings.get_course_xmlpath(coursename) - - return settings.DATA_DIR + xp + '/sections/' diff --git a/lms/djangoapps/courseware/views.py b/lms/djangoapps/courseware/views.py index 444e830072..e1e1c16632 100644 --- a/lms/djangoapps/courseware/views.py +++ b/lms/djangoapps/courseware/views.py @@ -1,8 +1,6 @@ import logging import urllib -from fs.osfs import OSFS - from django.conf import settings from django.core.context_processors import csrf from django.contrib.auth.models import User @@ -22,7 +20,9 @@ from student.models import UserProfile from multicourse import multicourse_settings from keystore.django import keystore -from courseware import grades, content_parser +from util.cache import cache +from student.models import UserTestGroup +from courseware import grades log = logging.getLogger("mitx.courseware") @@ -31,16 +31,39 @@ etree.set_default_parser(etree.XMLParser(dtd_validation=False, load_dtd=False, template_imports = {'urllib': urllib} + +def user_groups(user): + if not user.is_authenticated(): + return [] + + # TODO: Rewrite in Django + key = 'user_group_names_{user.id}'.format(user=user) + cache_expiration = 60 * 60 # one hour + + # Kill caching on dev machines -- we switch groups a lot + group_names = cache.get(key) + + if group_names is None: + group_names = [u.name for u in UserTestGroup.objects.filter(users=user)] + cache.set(key, group_names, cache_expiration) + + return group_names + + +def format_url_params(params): + return [urllib.quote(string.replace(' ', '_')) for string in params] + + @cache_control(no_cache=True, no_store=True, must_revalidate=True) def gradebook(request): - if 'course_admin' not in content_parser.user_groups(request.user): + if 'course_admin' not in user_groups(request.user): raise Http404 coursename = multicourse_settings.get_coursename_from_request(request) student_objects = User.objects.all()[:100] student_info = [] - + coursename = multicourse_settings.get_coursename_from_request(request) course_location = multicourse_settings.get_course_location(coursename) @@ -52,7 +75,7 @@ def gradebook(request): 'id': student.id, 'email': student.email, 'grade_info': grades.grade_sheet(student, course, student_module_cache), - 'realname': UserProfile.objects.get(user = student).name + 'realname': UserProfile.objects.get(user=student).name }) return render_to_response('gradebook.html', {'students': student_info}) @@ -67,7 +90,7 @@ def profile(request, student_id=None): if student_id is None: student = request.user else: - if 'course_admin' not in content_parser.user_groups(request.user): + if 'course_admin' not in user_groups(request.user): raise Http404 student = User.objects.get(id=int(student_id)) @@ -83,7 +106,7 @@ def profile(request, student_id=None): 'location': user_info.location, 'language': user_info.language, 'email': student.email, - 'format_url_params': content_parser.format_url_params, + 'format_url_params': format_url_params, 'csrf': csrf(request)['csrf_token'] } context.update(grades.grade_sheet(student, course, student_module_cache)) @@ -110,7 +133,7 @@ def render_accordion(request, course, chapter, section): context = dict([('active_chapter', active_chapter), ('toc', toc), ('course_name', course), - ('format_url_params', content_parser.format_url_params), + ('format_url_params', format_url_params), ('csrf', csrf(request)['csrf_token'])] + template_imports.items()) return render_to_string('accordion.html', context) @@ -215,7 +238,8 @@ def jump_to(request, probname=None): # look for problem of given name pxml = xml.xpath('//problem[@filename="%s"]' % probname) - if pxml: pxml = pxml[0] + if pxml: + pxml = pxml[0] # get the parent element parent = pxml.getparent() @@ -224,7 +248,7 @@ def jump_to(request, probname=None): chapter = None section = None branch = parent - for k in range(4): # max depth of recursion + for k in range(4): # max depth of recursion if branch.tag == 'section': section = branch.get('name') if branch.tag == 'chapter': @@ -233,7 +257,7 @@ def jump_to(request, probname=None): position = None if parent.tag == 'sequential': - position = parent.index(pxml) + 1 # position in sequence + position = parent.index(pxml) + 1 # position in sequence return index(request, course=coursename, chapter=chapter,