Commit 2a9eba38 by Calen Pennington

Removing content_parser

parent c7f95695
'''
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/'
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,9 +31,32 @@ 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)
......@@ -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()
......
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