Removing content_parser
This commit is contained in:
@@ -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 <include file=""> 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 <include> 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/'
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user