Commit 010593be by Arthur Barrett

Merge branch 'master' into feature/abarrett/annotatable_xmodule

parents 949603e3 9b6cbd6d
......@@ -27,10 +27,12 @@ from xmodule.contentstore.django import contentstore
from xmodule.templates import update_templates
from xmodule.modulestore.xml_exporter import export_to_xml
from xmodule.modulestore.xml_importer import import_from_xml
from xmodule.templates import update_templates
from xmodule.capa_module import CapaDescriptor
from xmodule.course_module import CourseDescriptor
from xmodule.seq_module import SequenceDescriptor
from xmodule.modulestore.exceptions import ItemNotFoundError
TEST_DATA_MODULESTORE = copy.deepcopy(settings.MODULESTORE)
TEST_DATA_MODULESTORE['default']['OPTIONS']['fs_root'] = path('common/test/data')
......@@ -409,3 +411,32 @@ class ContentStoreTest(ModuleStoreTestCase):
self.assertIn('markdown', context, "markdown is missing from context")
self.assertIn('markdown', problem.metadata, "markdown is missing from metadata")
self.assertNotIn('markdown', problem.editable_metadata_fields, "Markdown slipped into the editable metadata fields")
class TemplateTestCase(ModuleStoreTestCase):
def test_template_cleanup(self):
ms = modulestore('direct')
# insert a bogus template in the store
bogus_template_location = Location('i4x', 'edx', 'templates', 'html', 'bogus')
source_template_location = Location('i4x', 'edx', 'templates', 'html', 'Empty')
ms.clone_item(source_template_location, bogus_template_location)
verify_create = ms.get_item(bogus_template_location)
self.assertIsNotNone(verify_create)
# now run cleanup
update_templates()
# now try to find dangling template, it should not be in DB any longer
asserted = False
try:
verify_create = ms.get_item(bogus_template_location)
except ItemNotFoundError:
asserted = True
self.assertTrue(asserted)
......@@ -249,7 +249,7 @@ class CourseGradingTest(CourseTestCase):
altered_grader = CourseGradingModel.update_from_json(test_grader.__dict__)
self.assertDictEqual(test_grader.__dict__, altered_grader.__dict__, "cutoff add D")
test_grader.grace_period = {'hours' : '4'}
test_grader.grace_period = {'hours' : 4, 'minutes' : 5, 'seconds': 0}
altered_grader = CourseGradingModel.update_from_json(test_grader.__dict__)
self.assertDictEqual(test_grader.__dict__, altered_grader.__dict__, "4 hour grace period")
......
......@@ -85,7 +85,6 @@ class ContentStoreTestCase(ModuleStoreTestCase):
# Now make sure that the user is now actually activated
self.assertTrue(user(email).is_active)
class AuthTestCase(ContentStoreTestCase):
"""Check that various permissions-related things work"""
......
......@@ -155,7 +155,8 @@ class CourseGradingModel(object):
if 'grace_period' in graceperiodjson:
graceperiodjson = graceperiodjson['grace_period']
grace_rep = " ".join(["%s %s" % (value, key) for (key, value) in graceperiodjson.iteritems()])
# lms requires these to be in a fixed order
grace_rep = "{0[hours]:d} hours {0[minutes]:d} minutes {0[seconds]:d} seconds".format(graceperiodjson)
descriptor = get_modulestore(course_location).get_item(course_location)
descriptor.metadata['graceperiod'] = grace_rep
......@@ -234,10 +235,10 @@ class CourseGradingModel(object):
@staticmethod
def convert_set_grace_period(descriptor):
# 5 hours 59 minutes 59 seconds => converted to iso format
# 5 hours 59 minutes 59 seconds => { hours: 5, minutes : 59, seconds : 59}
rawgrace = descriptor.metadata.get('graceperiod', None)
if rawgrace:
parsedgrace = {str(key): val for (val, key) in re.findall('\s*(\d+)\s*(\w+)', rawgrace)}
parsedgrace = {str(key): int(val) for (val, key) in re.findall('\s*(\d+)\s*(\w+)', rawgrace)}
return parsedgrace
else: return None
......
......@@ -227,7 +227,7 @@ CMS.Views.Settings.Details = CMS.Views.ValidatingView.extend({
time = 0;
}
var newVal = new Date(date.getTime() + time * 1000);
if (cacheModel.get(fieldName).getTime() !== newVal.getTime()) {
if (!cacheModel.has(fieldName) || cacheModel.get(fieldName).getTime() !== newVal.getTime()) {
cacheModel.save(fieldName, newVal, { error: CMS.ServerError});
}
}
......
......@@ -632,8 +632,12 @@ class MultipleChoiceResponse(LoncapaResponse):
# define correct choices (after calling secondary setup)
xml = self.xml
cxml = xml.xpath('//*[@id=$id]//choice[@correct="true"]', id=xml.get('id'))
self.correct_choices = [contextualize_text(choice.get('name'), self.context) for choice in cxml]
cxml = xml.xpath('//*[@id=$id]//choice', id=xml.get('id'))
# contextualize correct attribute and then select ones for which
# correct = "true"
self.correct_choices = [contextualize_text(choice.get('name'), self.context)
for choice in cxml
if contextualize_text(choice.get('correct'), self.context) == "true"]
def mc_setup_response(self):
'''
......
......@@ -446,7 +446,7 @@ class CourseDescriptor(SequenceDescriptor):
# utility function to get datetime objects for dates used to
# compute the is_new flag and the sorting_score
def to_datetime(timestamp):
return datetime.fromtimestamp(time.mktime(timestamp))
return datetime(*timestamp[:6])
def get_date(field):
timetuple = self._try_parse_time(field)
......
......@@ -56,6 +56,10 @@ def update_templates():
available from the installed plugins
"""
# cdodge: build up a list of all existing templates. This will be used to determine which
# templates have been removed from disk - and thus we need to remove from the DB
templates_to_delete = modulestore('direct').get_items(['i4x', 'edx', 'templates', None, None, None])
for category, templates in all_templates().items():
for template in templates:
if 'display_name' not in template.metadata:
......@@ -85,3 +89,12 @@ def update_templates():
modulestore('direct').update_item(template_location, template.data)
modulestore('direct').update_children(template_location, template.children)
modulestore('direct').update_metadata(template_location, template.metadata)
# remove template from list of templates to delete
templates_to_delete = [t for t in templates_to_delete if t.location != template_location]
# now remove all templates which appear to have removed from disk
if len(templates_to_delete) > 0:
logging.debug('deleting dangling templates = {0}'.format(templates_to_delete))
for template in templates_to_delete:
modulestore('direct').delete_item(template.location)
......@@ -86,8 +86,8 @@ class TimeLimitModule(XModule):
modified_duration = self._get_accommodated_duration(duration)
self.ending_at = self.beginning_at + modified_duration
def get_end_time_in_ms(self):
return int(self.ending_at * 1000)
def get_remaining_time_in_ms(self):
return int((self.ending_at - time()) * 1000)
def get_instance_state(self):
state = {}
......
......@@ -177,10 +177,10 @@ def check_for_active_timelimit_module(request, course_id, course):
raise Http404("No {0} metadata at this location: {1} ".format('time_expired_redirect_url', location))
time_expired_redirect_url = timelimit_descriptor.metadata.get('time_expired_redirect_url')
context['time_expired_redirect_url'] = time_expired_redirect_url
# Fetch the end time (in GMT) as stored in the module when it was started.
# This value should be UTC time as number of milliseconds since epoch.
end_date = timelimit_module.get_end_time_in_ms()
context['timer_expiration_datetime'] = end_date
# Fetch the remaining time relative to the end time as stored in the module when it was started.
# This value should be in milliseconds.
remaining_time = timelimit_module.get_remaining_time_in_ms()
context['timer_expiration_duration'] = remaining_time
if 'suppress_toplevel_navigation' in timelimit_descriptor.metadata:
context['suppress_toplevel_navigation'] = timelimit_descriptor.metadata['suppress_toplevel_navigation']
return_url = reverse('jump_to', kwargs={'course_id':course_id, 'location':location})
......@@ -191,7 +191,7 @@ def update_timelimit_module(user, course_id, student_module_cache, timelimit_des
'''
Updates the state of the provided timing module, starting it if it hasn't begun.
Returns dict with timer-related values to enable display of time remaining.
Returns 'timer_expiration_datetime' in dict if timer is still active, and not if timer has expired.
Returns 'timer_expiration_duration' in dict if timer is still active, and not if timer has expired.
'''
context = {}
# determine where to go when the exam ends:
......@@ -215,10 +215,9 @@ def update_timelimit_module(user, course_id, student_module_cache, timelimit_des
instance_module.save()
# the exam has been started, either because the student is returning to the
# exam page, or because they have just visited it. Fetch the end time (in GMT) as stored
# in the module when it was started.
# This value should be UTC time as number of milliseconds since epoch.
context['timer_expiration_datetime'] = timelimit_module.get_end_time_in_ms()
# exam page, or because they have just visited it. Fetch the remaining time relative to the
# end time as stored in the module when it was started.
context['timer_expiration_duration'] = timelimit_module.get_remaining_time_in_ms()
# also use the timed module to determine whether top-level navigation is visible:
if 'suppress_toplevel_navigation' in timelimit_descriptor.metadata:
context['suppress_toplevel_navigation'] = timelimit_descriptor.metadata['suppress_toplevel_navigation']
......@@ -325,7 +324,7 @@ def index(request, course_id, chapter=None, section=None,
if section_module.category == 'timelimit':
timer_context = update_timelimit_module(request.user, course_id, student_module_cache,
section_descriptor, section_module)
if 'timer_expiration_datetime' in timer_context:
if 'timer_expiration_duration' in timer_context:
context.update(timer_context)
else:
# if there is no expiration defined, then we know the timer has expired:
......
......@@ -200,7 +200,6 @@ COURSE_TITLE = "Circuits and Electronics"
### Dark code. Should be enabled in local settings for devel.
ENABLE_MULTICOURSE = False # set to False to disable multicourse display (see lib.util.views.mitxhome)
QUICKEDIT = False
WIKI_ENABLED = False
......
......@@ -34,7 +34,6 @@ EDX4EDX_ROOT = ENV_ROOT / "data/edx4edx"
DEBUG = True
ENABLE_MULTICOURSE = True # set to False to disable multicourse display (see lib.util.views.mitxhome)
QUICKEDIT = True
MAKO_TEMPLATES['course'] = [DATA_DIR, EDX4EDX_ROOT]
......
......@@ -6,7 +6,6 @@ COURSE_TITLE = "edx4edx: edX Author Course"
EDX4EDX_ROOT = ENV_ROOT / "data/edx4edx"
### Dark code. Should be enabled in local settings for devel.
QUICKEDIT = True
ENABLE_MULTICOURSE = True # set to False to disable multicourse display (see lib.util.views.mitxhome)
###
PIPELINE_CSS_COMPRESSOR = None
......
This is a library for edx4edx, allowing users to practice writing problems.
#!/usr/bin/python
from random import choice
import string
import traceback
from django.conf import settings
import capa.capa_problem as lcp
from dogfood.views import update_problem
def GenID(length=8, chars=string.letters + string.digits):
return ''.join([choice(chars) for i in range(length)])
randomid = GenID()
def check_problem_code(ans, the_lcp, correct_answers, false_answers):
"""
ans = student's answer
the_lcp = LoncapaProblem instance
returns dict {'ok':is_ok,'msg': message with iframe}
"""
pfn = "dog%s" % randomid
pfn += the_lcp.problem_id.replace('filename', '') # add problem ID to dogfood problem name
update_problem(pfn, ans, filestore=the_lcp.system.filestore)
msg = '<hr width="100%"/>'
msg += '<iframe src="%s/dogfood/filename%s" width="95%%" height="400" frameborder="1">No iframe support!</iframe>' % (settings.MITX_ROOT_URL, pfn)
msg += '<hr width="100%"/>'
endmsg = """<p><font size="-1" color="purple">Note: if the code text box disappears after clicking on "Check",
please type something in the box to make it refresh properly. This is a
bug with Chrome; it does not happen with Firefox. It is being fixed.
</font></p>"""
is_ok = True
if (not correct_answers) or (not false_answers):
ret = {'ok': is_ok,
'msg': msg + endmsg,
}
return ret
try:
# check correctness
fp = the_lcp.system.filestore.open('problems/%s.xml' % pfn)
test_lcp = lcp.LoncapaProblem(fp, '1', system=the_lcp.system)
if not (test_lcp.grade_answers(correct_answers).get_correctness('1_2_1') == 'correct'):
is_ok = False
if (test_lcp.grade_answers(false_answers).get_correctness('1_2_1') == 'correct'):
is_ok = False
except Exception, err:
is_ok = False
msg += "<p>Error: %s</p>" % str(err).replace('<', '&#60;')
msg += "<p><pre>%s</pre></p>" % traceback.format_exc().replace('<', '&#60;')
ret = {'ok': is_ok,
'msg': msg + endmsg,
}
return ret
'''
dogfood.py
For using mitx / edX / i4x in checking itself.
df_capa_problem: accepts an XML file for a problem, and renders it.
'''
import logging
import datetime
import re
import os # FIXME - use OSFS instead
from fs.osfs import OSFS
from django.conf import settings
from django.contrib.auth.models import User
from django.core.context_processors import csrf
from django.core.mail import send_mail
from django.http import Http404
from django.http import HttpResponse
from django.shortcuts import redirect
from mitxmako.shortcuts import render_to_response, render_to_string
import track.views
from lxml import etree
from courseware.module_render import make_track_function, ModuleSystem, get_module
from courseware.models import StudentModule
from multicourse import multicourse_settings
from student.models import UserProfile
from util.cache import cache
from util.views import accepts
import courseware.content_parser as content_parser
#import courseware.modules
import xmodule
log = logging.getLogger("mitx.courseware")
etree.set_default_parser(etree.XMLParser(dtd_validation=False, load_dtd=False,
remove_comments=True))
DOGFOOD_COURSENAME = 'edx_dogfood' # FIXME - should not be here; maybe in settings
def update_problem(pfn, pxml, coursename=None, overwrite=True, filestore=None):
'''
update problem with filename pfn, and content (xml) pxml.
'''
if not filestore:
if not coursename: coursename = DOGFOOD_COURSENAME
xp = multicourse_settings.get_course_xmlpath(coursename) # path to XML for the course
pfn2 = settings.DATA_DIR + xp + 'problems/%s.xml' % pfn
fp = open(pfn2, 'w')
else:
pfn2 = 'problems/%s.xml' % pfn
fp = filestore.open(pfn2, 'w')
log.debug('[dogfood.update_problem] pfn2=%s' % pfn2)
if os.path.exists(pfn2) and not overwrite: return # don't overwrite if already exists and overwrite=False
pxmls = pxml if type(pxml) in [str, unicode] else etree.tostring(pxml, pretty_print=True)
fp.write(pxmls)
fp.close()
def df_capa_problem(request, id=None):
'''
dogfood capa problem.
Accepts XML for a problem, inserts it into the dogfood course.xml.
Returns rendered problem.
'''
# "WARNING: UNDEPLOYABLE CODE. FOR DEV USE ONLY."
if settings.DEBUG:
log.debug('[lib.dogfood.df_capa_problem] id=%s' % id)
if not 'coursename' in request.session:
coursename = DOGFOOD_COURSENAME
else:
coursename = request.session['coursename']
xp = multicourse_settings.get_course_xmlpath(coursename) # path to XML for the course
# Grab the XML corresponding to the request from course.xml
module = 'problem'
try:
xml = content_parser.module_xml(request.user, module, 'id', id, coursename)
except Exception, err:
log.error("[lib.dogfood.df_capa_problem] error in calling content_parser: %s" % err)
xml = None
# if problem of given ID does not exist, then create it
# do this only if course.xml has a section named "DogfoodProblems"
if not xml:
m = re.match('filename([A-Za-z0-9_]+)$', id) # extract problem filename from ID given
if not m:
raise Exception, '[lib.dogfood.df_capa_problem] Illegal problem id %s' % id
pfn = m.group(1)
log.debug('[lib.dogfood.df_capa_problem] creating new problem pfn=%s' % pfn)
# add problem to course.xml
fn = settings.DATA_DIR + xp + 'course.xml'
xml = etree.parse(fn)
seq = xml.find('chapter/section[@name="DogfoodProblems"]/sequential') # assumes simplistic course.xml structure!
if seq == None:
raise Exception, "[lib.dogfood.views.df_capa_problem] missing DogfoodProblems section in course.xml!"
newprob = etree.Element('problem')
newprob.set('type', 'lecture')
newprob.set('showanswer', 'attempted')
newprob.set('rerandomize', 'never')
newprob.set('title', pfn)
newprob.set('filename', pfn)
newprob.set('name', pfn)
seq.append(newprob)
fp = open(fn, 'w')
fp.write(etree.tostring(xml, pretty_print=True)) # write new XML
fp.close()
# now create new problem file
# update_problem(pfn,'<problem>\n<text>\nThis is a new problem\n</text>\n</problem>\n',coursename,overwrite=False)
# reset cache entry
user = request.user
groups = content_parser.user_groups(user)
options = {'dev_content': settings.DEV_CONTENT,
'groups': groups}
filename = xp + 'course.xml'
cache_key = filename + "_processed?dev_content:" + str(options['dev_content']) + "&groups:" + str(sorted(groups))
log.debug('[lib.dogfood.df_capa_problem] cache_key = %s' % cache_key)
#cache.delete(cache_key)
tree = content_parser.course_xml_process(xml) # add ID tags
cache.set(cache_key, etree.tostring(tree), 60)
# settings.DEFAULT_GROUPS.append('dev') # force content_parser.course_file to not use cache
xml = content_parser.module_xml(request.user, module, 'id', id, coursename)
if not xml:
log.debug("[lib.dogfood.df_capa_problem] problem xml not found!")
# add problem ID to list so that is_staff check can be bypassed
request.session['dogfood_id'] = id
# hand over to quickedit to do the rest
return quickedit(request, id=id, qetemplate='dogfood.html', coursename=coursename)
def quickedit(request, id=None, qetemplate='quickedit.html', coursename=None):
'''
quick-edit capa problem.
Maybe this should be moved into capa/views.py
Or this should take a "module" argument, and the quickedit moved into capa_module.
id is passed in from url resolution
qetemplate is used by dogfood.views.dj_capa_problem, to override normal template
'''
print "WARNING: UNDEPLOYABLE CODE. FOR DEV USE ONLY."
print "In deployed use, this will only edit on one server"
print "We need a setting to disable for production where there is"
print "a load balanacer"
if not request.user.is_staff:
if not ('dogfood_id' in request.session and request.session['dogfood_id'] == id):
return redirect('/')
if id == 'course.xml':
return quickedit_git_reload(request)
# get coursename if stored
if not coursename:
coursename = multicourse_settings.get_coursename_from_request(request)
xp = multicourse_settings.get_course_xmlpath(coursename) # path to XML for the course
def get_lcp(coursename, id):
# Grab the XML corresponding to the request from course.xml
# create empty student state for this problem, if not previously existing
s = StudentModule.objects.filter(student=request.user,
module_id=id)
student_module_cache = list(s) if s is not None else []
#if len(s) == 0 or s is None:
# smod=StudentModule(student=request.user,
# module_type = 'problem',
# module_id=id,
# state=instance.get_state())
# smod.save()
# student_module_cache = [smod]
module = 'problem'
module_xml = etree.XML(content_parser.module_xml(request.user, module, 'id', id, coursename))
module_id = module_xml.get('id')
log.debug("module_id = %s" % module_id)
(instance, smod, module_type) = get_module(request.user, request, module_xml, student_module_cache, position=None)
log.debug('[dogfood.views] instance=%s' % instance)
lcp = instance.lcp
log.debug('[dogfood.views] lcp=%s' % lcp)
pxml = lcp.tree
pxmls = etree.tostring(pxml, pretty_print=True)
return instance, pxmls
def old_get_lcp(coursename, id):
# Grab the XML corresponding to the request from course.xml
module = 'problem'
xml = content_parser.module_xml(request.user, module, 'id', id, coursename)
ajax_url = settings.MITX_ROOT_URL + '/modx/' + id + '/'
# Create the module (instance of capa_module.Module)
system = ModuleSystem(track_function=make_track_function(request),
render_function=None,
render_template=render_to_string,
ajax_url=ajax_url,
filestore=OSFS(settings.DATA_DIR + xp),
)
instance = xmodule.get_module_class(module)(system,
xml,
id,
state=None)
log.info('ajax_url = ' + instance.ajax_url)
# create empty student state for this problem, if not previously existing
s = StudentModule.objects.filter(student=request.user,
module_state_key=id)
if len(s) == 0 or s is None:
smod = StudentModule(student=request.user,
module_type='problem',
module_state_key=id,
state=instance.get_instance_state())
smod.save()
lcp = instance.lcp
pxml = lcp.tree
pxmls = etree.tostring(pxml, pretty_print=True)
return instance, pxmls
instance, pxmls = get_lcp(coursename, id)
# if there was a POST, then process it
msg = ''
if 'qesubmit' in request.POST:
action = request.POST['qesubmit']
if "Revert" in action:
msg = "Reverted to original"
elif action == 'Change Problem':
key = 'quickedit_%s' % id
if not key in request.POST:
msg = "oops, missing code key=%s" % key
else:
newcode = request.POST[key]
# see if code changed
if str(newcode) == str(pxmls) or '<?xml version="1.0"?>\n' + str(newcode) == str(pxmls):
msg = "No changes"
else:
# check new code
isok = False
try:
newxml = etree.fromstring(newcode)
isok = True
except Exception, err:
msg = "Failed to change problem: XML error \"<font color=red>%s</font>\"" % err
if isok:
filename = instance.lcp.fileobject.name
fp = open(filename, 'w') # TODO - replace with filestore call?
fp.write(newcode)
fp.close()
msg = "<font color=green>Problem changed!</font> (<tt>%s</tt>)" % filename
instance, pxmls = get_lcp(coursename, id)
lcp = instance.lcp
# get the rendered problem HTML
phtml = instance.get_html()
# phtml = instance.get_problem_html()
context = {'id': id,
'msg': msg,
'lcp': lcp,
'filename': lcp.fileobject.name,
'pxmls': pxmls,
'phtml': phtml,
"destroy_js": '',
'init_js': '',
'csrf': csrf(request)['csrf_token'],
}
result = render_to_response(qetemplate, context)
return result
def quickedit_git_reload(request):
'''
reload course.xml and all courseware files for this course, from the git repo.
assumes the git repo has already been setup.
staff only.
'''
if not request.user.is_staff:
return redirect('/')
# get coursename if stored
coursename = multicourse_settings.get_coursename_from_request(request)
xp = multicourse_settings.get_course_xmlpath(coursename) # path to XML for the course
msg = ""
if 'cancel' in request.POST:
return redirect("/courseware")
if 'gitupdate' in request.POST:
import os # FIXME - put at top?
#cmd = "cd ../data%s; git reset --hard HEAD; git pull origin %s" % (xp,xp.replace('/',''))
cmd = "cd ../data%s; ./GITRELOAD '%s'" % (xp, xp.replace('/', ''))
msg += '<p>cmd: %s</p>' % cmd
ret = os.popen(cmd).read()
msg += '<p><pre>%s</pre></p>' % ret.replace('<', '&lt;')
msg += "<p>git update done!</p>"
context = {'id': id,
'msg': msg,
'coursename': coursename,
'csrf': csrf(request)['csrf_token'],
}
result = render_to_response("gitupdate.html", context)
return result
......@@ -60,14 +60,12 @@
});
</script>
% if timer_expiration_datetime:
% if timer_expiration_duration:
<script type="text/javascript">
var timer = {
timer_inst : null,
get_remaining_secs : function() {
// set the end time when the template is rendered.
// This value should be UTC time as number of milliseconds since epoch.
var endTime = new Date(${timer_expiration_datetime});
end_time : null,
get_remaining_secs : function(endTime) {
var currentTime = new Date();
var remaining_secs = Math.floor((endTime - currentTime)/1000);
return remaining_secs;
......@@ -87,13 +85,16 @@
return remainingTimeString;
},
update_time : function(self) {
remaining_secs = self.get_remaining_secs();
remaining_secs = self.get_remaining_secs(self.end_time);
if (remaining_secs <= 0) {
self.end(self);
}
$('#exam_timer').text(self.get_time_string(remaining_secs));
},
start : function() { var that = this;
// set the end time when the template is rendered.
// This value should be UTC time as number of milliseconds since epoch.
this.end_time = new Date((new Date()).getTime() + ${timer_expiration_duration});
this.timer_inst = setInterval(function(){ that.update_time(that); }, 1000);
},
end : function(self) {
......@@ -109,7 +110,7 @@
</%block>
% if timer_expiration_datetime:
% if timer_expiration_duration:
<div class="timer-main">
<div id="timer_wrapper">
% if timer_navigation_return_url:
......
<%namespace name='static' file='static_content.html'/>
<!DOCTYPE html>
<html>
## -----------------------------------------------------------------------------
## Template for lib.dogfood.views.dj_capa_problem
##
## Used for viewing assesment problems in "dogfood" self-evaluation mode
## -----------------------------------------------------------------------------
<head>
<link rel="stylesheet" href="${static.url('css/vendor/jquery.treeview.css')}" type="text/css" media="all" />
## <link rel="stylesheet" href="${ settings.LIB_URL }jquery.treeview.css" type="text/css" media="all" />
## <link rel="stylesheet" href="/static/sass/application.css" type="text/css" media="all" / >
% if settings.MITX_FEATURES['USE_DJANGO_PIPELINE']:
## <%static:css group='application'/>
% endif
% if not settings.MITX_FEATURES['USE_DJANGO_PIPELINE']:
## <link rel="stylesheet" href="/static/sass/application.css" type="text/css" media="all" / >
% endif
<script type="text/javascript" src="${static.url('js/jquery.min.js')}"></script>
<script type="text/javascript" src="${static.url('js/jquery-ui.min.js')}"></script>
<script type="text/javascript" src="${static.url('js/swfobject/swfobject.js')}"></script>
% if settings.MITX_FEATURES['USE_DJANGO_PIPELINE']:
<%static:js group='application'/>
% endif
% if not settings.MITX_FEATURES['USE_DJANGO_PIPELINE']:
% for jsfn in [ '/static/%s' % x.replace('.coffee','.js') for x in settings.PIPELINE_JS['application']['source_filenames'] ]:
<script type="text/javascript" src="${jsfn}"></script>
% endfor
% endif
## codemirror
<link rel="stylesheet" href="/static/css/codemirror.css" type="text/css" media="all" />
<script type="text/javascript" src="${ settings.LIB_URL }codemirror-compressed.js"></script>
## alternate codemirror
## <script type="text/javascript" src="/static/js/CodeMirror-2.25/lib/codemirror.js"></script>
## <script type="text/javascript" src="/static/js/CodeMirror-2.25/mode/xml/xml.js"></script>
## <script type="text/javascript" src="/static/js/CodeMirror-2.25/mode/python/python.js"></script>
## image input: for clicking on images (see imageinput.html)
<script type="text/javascript" src="/static/js/imageinput.js"></script>
<%include file="mathjax_include.html" />
</head>
<body class="courseware">
<!--[if lt IE 9]>
<script src="/static/js/html5shiv.js"></script>
<![endif]-->
<div class="courseware"></div>
## -----------------------------------------------------------------------------
## information
## <hr width="100%">
## <h2>Rendition of your problem code</h2>
## <hr width="100%">
## -----------------------------------------------------------------------------
## rendered problem display
<script>
${init_js}
</script>
<style type="text/css">
.problem-header {display:none;}
.staff {display:none;}
.correct { display: -moz-inline-box;
-moz-box-orient: vertical;
display: inline-block;
vertical-align: baseline;
zoom: 1;
*display: inline;
*vertical-align: auto;
background: url("/static/images/correct-icon.png") center center no-repeat;
height: 20px;
position: relative;
top: 6px;
width: 25px; }
.incorrect{
display: -moz-inline-box;
-moz-box-orient: vertical;
display: inline-block;
vertical-align: baseline;
zoom: 1;
*display: inline;
*vertical-align: auto;
background: url("/static/images/incorrect-icon.png") center center no-repeat;
height: 20px;
width: 20px;
position: relative;
top: 6px; }
.unanswered {
display: -moz-inline-box;
-moz-box-orient: vertical;
display: inline-block;
vertical-align: baseline;
zoom: 1;
*display: inline;
*vertical-align: auto;
background: url("/static/images/unanswered-icon.png") center center no-repeat;
height: 14px;
position: relative;
top: 4px;
width: 14px; }
}
</style>
<meta name="path_prefix" content="${MITX_ROOT_URL}">
<section class="course-content">
<form>
${phtml}
</form>
</section>
<script type="text/javascript" src="${static.url('js/jquery.treeview.js')}"></script>
<script type="text/javascript" src="${static.url('js/jquery.leanModal.min.js')}"></script>
<script type="text/javascript" src="${static.url('js/vendor/jquery.qtip.min.js')}"></script>
<script type="text/javascript" src="${static.url('js/jquery.cookie.js')}"></script>
## <script type="text/javascript" src="${static.url('js/video_player.js')}"></script>
<script type="text/javascript" src="${static.url('js/schematic.js')}"></script>
<script type="text/javascript" src="${static.url('js/cktsim.js')}"></script>
## image input: for clicking on images (see imageinput.html)
<script type="text/javascript" src="/static/js/imageinput.js"></script>
<script type="text/javascript" >
var codemirror_set= {}; // associative array of codemirror objects
</script>
<%block name="js_extra"/>
</body>
</html>
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html> <head>
<title>edX gitupdate</title>
</head>
<body>
<hr>
<h1>edX gitupdate</h1>
<hr>
<h2>Coursename: ${coursename}</h2>
% if msg:
${msg}
% else:
<p>
Do you REALLY want to overwrite all the course.xml + problems + html
files with version from the main git repository?
</p>
<form method="post">
<input type="submit" value="Do git update" name="gitupdate" />
## <input type="submit" value="Cancel" name="cancel" />
<input type="hidden" name="csrfmiddlewaretoken" value="${csrf}"/>
</form>
% endif
<p><a href="${MITX_ROOT_URL}/courseware/">Return to site</a></p>
</body> </html>
<%namespace name='static' file='static_content.html'/>
<!DOCTYPE html>
<html>
## -----------------------------------------------------------------------------
## Template for courseware.views.quickedit
##
## Used for quick-edit link present when viewing capa-format assesment problems.
## -----------------------------------------------------------------------------
<head>
<link rel="stylesheet" href="${static.url('css/vendor/jquery.treeview.css')}" type="text/css" media="all" />
## <link rel="stylesheet" href="${ settings.LIB_URL }jquery.treeview.css" type="text/css" media="all" />
## <link rel="stylesheet" href="/static/sass/application.css" type="text/css" media="all" / >
% if settings.MITX_FEATURES['USE_DJANGO_PIPELINE']:
<%static:css group='application'/>
% endif
% if not settings.MITX_FEATURES['USE_DJANGO_PIPELINE']:
## <link rel="stylesheet" href="/static/sass/application.css" type="text/css" media="all" / >
% endif
<script type="text/javascript" src="${static.url('js/jquery.min.js')}"></script>
<script type="text/javascript" src="${static.url('js/jquery-ui.min.js')}"></script>
<script type="text/javascript" src="${static.url('js/swfobject/swfobject.js')}"></script>
% if settings.MITX_FEATURES['USE_DJANGO_PIPELINE']:
<%static:js group='application'/>
% endif
% if not settings.MITX_FEATURES['USE_DJANGO_PIPELINE']:
% for jsfn in [ '/static/%s' % x.replace('.coffee','.js') for x in settings.PIPELINE_JS['application']['source_filenames'] ]:
<script type="text/javascript" src="${jsfn}"></script>
% endfor
% endif
## codemirror
<link rel="stylesheet" href="/static/css/codemirror.css" type="text/css" media="all" />
<script type="text/javascript" src="${ settings.LIB_URL }codemirror-compressed.js"></script>
## alternate codemirror
## <script type="text/javascript" src="/static/js/CodeMirror-2.25/lib/codemirror.js"></script>
## <script type="text/javascript" src="/static/js/CodeMirror-2.25/mode/xml/xml.js"></script>
## <script type="text/javascript" src="/static/js/CodeMirror-2.25/mode/python/python.js"></script>
## image input: for clicking on images (see imageinput.html)
<script type="text/javascript" src="/static/js/imageinput.js"></script>
## <script type="text/javascript">
## var codemirror_set = {}; // track all codemirror textareas, so they can be refreshed on page changes
## </script>
<!--[if lt IE 9]>
<script src="${static.url('js/html5shiv.js')}"></script>
<![endif]-->
<%block name="headextra"/>
<!-- This must appear after all mathjax-config blocks, so it is after the imports from the other templates.
It can't be run through static.url because MathJax uses crazy url introspection to do lazy loading of
MathJax extension libraries -->
<%include file="mathjax_include.html" />
</head>
<body class="courseware" style="text-align:left;" >
<style type="text/css">
.CodeMirror {border-style: solid;
border-width: 1px;}
.CodeMirror-scroll {
height: 500;
width: 100%
}
</style>
## -----------------------------------------------------------------------------
## information and i4x PSL code
<hr width="100%">
<h2>QuickEdit</h2>
<hr width="100%">
<ul>
<li>File = ${filename}</li>
<li>ID = ${id}</li>
</ul>
<form method="post">
<textarea rows="40" cols="160" name="quickedit_${id}" id="quickedit_${id}">${pxmls|h}</textarea>
<br/>
<input type="submit" value="Change Problem" name="qesubmit" />
<input type="submit" value="Revert to original" name="qesubmit" />
<input type="hidden" name="csrfmiddlewaretoken" value="${csrf}"/>
</form>
<span>${msg|n}</span>
## -----------------------------------------------------------------------------
## rendered problem display
<script>
// height: auto;
// overflow-y: hidden;
// overflow-x: auto;
$(function(){
var cm = CodeMirror.fromTextArea(document.getElementById("quickedit_${id}"),
{ 'mode': {name: "xml", alignCDATA: true},
lineNumbers: true
});
// $('.my-wymeditor').wymeditor();
});
</script>
<hr width="100%">
<script>
${init_js}
</script>
<style type="text/css">
.staff {display:none;}
.correct { display: -moz-inline-box;
-moz-box-orient: vertical;
display: inline-block;
vertical-align: baseline;
zoom: 1;
*display: inline;
*vertical-align: auto;
background: url("/static/images/correct-icon.png") center center no-repeat;
height: 20px;
position: relative;
top: 6px;
width: 25px; }
.incorrect{
display: -moz-inline-box;
-moz-box-orient: vertical;
display: inline-block;
vertical-align: baseline;
zoom: 1;
*display: inline;
*vertical-align: auto;
background: url("/static/images/incorrect-icon.png") center center no-repeat;
height: 20px;
width: 20px;
position: relative;
top: 6px; }
}
</style>
<meta name="path_prefix" content="${MITX_ROOT_URL}">
<section class="course-content">
<div id="seq_content">
<form>
${phtml}
</form>
</div>
</section>
<script type="text/javascript" src="${static.url('js/jquery.treeview.js')}"></script>
<script type="text/javascript" src="${static.url('js/jquery.leanModal.min.js')}"></script>
<script type="text/javascript" src="${static.url('js/vendor/jquery.qtip.min.js')}"></script>
<script type="text/javascript" src="${static.url('js/jquery.cookie.js')}"></script>
## <script type="text/javascript" src="${static.url('js/video_player.js')}"></script>
<script type="text/javascript" src="${static.url('js/schematic.js')}"></script>
<script type="text/javascript" src="${static.url('js/cktsim.js')}"></script>
<script type="text/javascript" >
var codemirror_set= {}; // associative array of codemirror objects
</script>
<script type="text/javascript" src="${static.url('js/jquery.scrollTo-1.4.2-min.js')}"></script>
<%block name="js_extra"/>
</body>
</html>
......@@ -320,10 +320,6 @@ if settings.COURSEWARE_ENABLED:
'courseware.views.static_tab', name="static_tab"),
)
if settings.QUICKEDIT:
urlpatterns += (url(r'^quickedit/(?P<id>[^/]*)$', 'dogfood.views.quickedit'),)
urlpatterns += (url(r'^dogfood/(?P<id>[^/]*)$', 'dogfood.views.df_capa_problem'),)
if settings.ENABLE_JASMINE:
urlpatterns += (url(r'^_jasmine/', include('django_jasmine.urls')),)
......
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