Newer
Older
Calen Pennington
committed
import json
from fs.osfs import OSFS
from django.core.context_processors import csrf
from django.contrib.auth.models import User
Calen Pennington
committed
from django.contrib.auth.decorators import login_required
from django.http import Http404, HttpResponse
Piotr Mitros
committed
from mitxmako.shortcuts import render_to_response, render_to_string
#from django.views.decorators.csrf import ensure_csrf_cookie
from django.views.decorators.cache import cache_control
Piotr Mitros
committed
from module_render import render_module, make_track_function, I4xSystem
David Ormsbee
committed
from models import StudentModule
from student.models import UserProfile
from util.views import accepts
from multicourse import multicourse_settings
David Ormsbee
committed
import courseware.content_parser as content_parser
Calen Pennington
committed
import courseware.modules
import courseware.grades as grades
log = logging.getLogger("mitx.courseware")
Piotr Mitros
committed
etree.set_default_parser(etree.XMLParser(dtd_validation=False, load_dtd=False,
remove_comments = True))
@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):
raise Http404
coursename = multicourse_settings.get_coursename_from_request(request)
student_objects = User.objects.all()[:100]
student_info = [{'username' :s.username,
'id' : s.id,
'email': s.email,
'grade_info' : grades.grade_sheet(s,coursename),
'realname' : UserProfile.objects.get(user = s).name
} for s in student_objects]
return render_to_response('gradebook.html',{'students':student_info})
Calen Pennington
committed
@login_required
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
def profile(request, student_id = None):
''' User profile. Show username, location, etc, as well as grades .
We need to allow the user to change some of these settings .'''
if student_id == None:
student = request.user
else:
print content_parser.user_groups(request.user)
if 'course_admin' not in content_parser.user_groups(request.user):
raise Http404
student = User.objects.get( id = int(student_id))
user_info = UserProfile.objects.get(user=student) # request.user.profile_cache #
coursename = multicourse_settings.get_coursename_from_request(request)
context={'name':user_info.name,
'username':student.username,
'location':user_info.location,
'language':user_info.language,
'email':student.email,
'format_url_params' : content_parser.format_url_params,
'csrf':csrf(request)['csrf_token']
}
context.update(grades.grade_sheet(student,coursename))
return render_to_response('profile.html', context)
def render_accordion(request,course,chapter,section):
''' Draws navigation bar. Takes current position in accordion as
if not course:
course = "6.002 Spring 2012"
toc=content_parser.toc_from_xml(content_parser.course_file(request.user,course), chapter, section)
active_chapter=1
for i in range(len(toc)):
if toc[i]['active']:
active_chapter=i
context=dict([['active_chapter',active_chapter],
['toc',toc],
['course_name',course],
['format_url_params',content_parser.format_url_params],
return render_to_string('accordion.html',context)
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
def render_section(request, section):
''' TODO: Consolidate with index
'''
user = request.user
Calen Pennington
committed
if not settings.COURSEWARE_ENABLED:
coursename = multicourse_settings.get_coursename_from_request(request)
Calen Pennington
committed
try:
dom = content_parser.section_file(user, section, coursename)
Calen Pennington
committed
except:
log.exception("Unable to parse courseware xml")
Calen Pennington
committed
return render_to_response('courseware-error.html', {})
Calen Pennington
committed
context = {
'csrf': csrf(request)['csrf_token'],
'accordion': render_accordion(request, '', '', '')
}
module_ids = dom.xpath("//@id")
Calen Pennington
committed
if user.is_authenticated():
module_object_preload = list(StudentModule.objects.filter(student=user,
module_id__in=module_ids))
else:
module_object_preload = []
Calen Pennington
committed
try:
module = render_module(user, request, dom, module_object_preload)
except:
log.exception("Unable to load module")
Calen Pennington
committed
context.update({
'init': '',
'content': render_to_string("module-error.html", {}),
})
return render_to_response('courseware.html', context)
context.update({
'init':module.get('init_js', ''),
'content':module['content'],
})
result = render_to_response('courseware.html', context)
return result
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
def index(request, course=None, chapter="Using the System", section="Hints"):
''' Displays courseware accordion, and any associated content.
'''
Piotr Mitros
committed
user = request.user
Calen Pennington
committed
if not settings.COURSEWARE_ENABLED:
if course==None:
if not settings.ENABLE_MULTICOURSE:
course = "6.002 Spring 2012"
elif 'coursename' in request.session:
course = request.session['coursename']
else:
course = settings.COURSE_DEFAULT
# Fixes URLs -- we don't get funny encoding characters from spaces
# so they remain readable
Piotr Mitros
committed
## TODO: Properly replace underscores
course=course.replace("_"," ")
chapter=chapter.replace("_"," ")
section=section.replace("_"," ")
# use multicourse module to determine if "course" is valid
#if course!=settings.COURSE_NAME.replace('_',' '):
if not multicourse_settings.is_valid_course(course):
request.session['coursename'] = course # keep track of current course being viewed in django's request.session
Calen Pennington
committed
try:
dom = content_parser.course_file(user,course) # also pass course to it, for course-specific XML path
Calen Pennington
committed
except:
log.exception("Unable to parse courseware xml")
Calen Pennington
committed
return render_to_response('courseware-error.html', {})
Piotr Mitros
committed
dom_module = dom.xpath("//course[@name=$course]/chapter[@name=$chapter]//section[@name=$section]/*[1]",
course=course, chapter=chapter, section=section)
Calen Pennington
committed
Piotr Mitros
committed
if len(dom_module) == 0:
module = None
else:
module = dom_module[0]
Piotr Mitros
committed
module_ids = dom.xpath("//course[@name=$course]/chapter[@name=$chapter]//section[@name=$section]//@id",
course=course, chapter=chapter, section=section)
Calen Pennington
committed
if user.is_authenticated():
module_object_preload = list(StudentModule.objects.filter(student=user,
module_id__in=module_ids))
else:
module_object_preload = []
Piotr Mitros
committed
Calen Pennington
committed
context = {
'csrf': csrf(request)['csrf_token'],
'accordion': render_accordion(request, course, chapter, section),
'COURSE_TITLE':multicourse_settings.get_course_title(course),
Calen Pennington
committed
}
try:
module = render_module(user, request, module, module_object_preload)
except:
log.exception("Unable to load module")
Calen Pennington
committed
context.update({
'init': '',
'content': render_to_string("module-error.html", {}),
})
return render_to_response('courseware.html', context)
context.update({
'init': module.get('init_js', ''),
'content': module['content'],
})
Piotr Mitros
committed
Piotr Mitros
committed
result = render_to_response('courseware.html', context)
return result
Calen Pennington
committed
def modx_dispatch(request, module=None, dispatch=None, id=None):
''' Generic view for extensions. This is where AJAX calls go.'''
if not request.user.is_authenticated():
return redirect('/')
Calen Pennington
committed
# Grab the student information for the module from the database
s = StudentModule.objects.filter(student=request.user,
module_id=id)
#s = StudentModule.get_with_caching(request.user, id)
if len(s) == 0 or s is None:
log.debug("Couldnt find module for user and id " + str(module) + " " + str(request.user) + " "+ str(id))
raise Http404
s = s[0]
Calen Pennington
committed
oldgrade = s.grade
oldstate = s.state
Calen Pennington
committed
dispatch=dispatch.split('?')[0]
ajax_url = settings.MITX_ROOT_URL + '/modx/'+module+'/'+id+'/'
Calen Pennington
committed
# get coursename if stored
coursename = multicourse_settings.get_coursename_from_request(request)
if coursename and settings.ENABLE_MULTICOURSE:
xp = multicourse_settings.get_course_xmlpath(coursename) # path to XML for the course
data_root = settings.DATA_DIR + xp
else:
data_root = settings.DATA_DIR
Calen Pennington
committed
# Grab the XML corresponding to the request from course.xml
Calen Pennington
committed
try:
xml = content_parser.module_xml(request.user, module, 'id', id, coursename)
Calen Pennington
committed
except:
log.exception("Unable to load module during ajax call")
if accepts(request, 'text/html'):
Calen Pennington
committed
return render_to_response("module-error.html", {})
else:
response = HttpResponse(json.dumps({'success': "We're sorry, this module is temporarily unavailable. Our staff is working to fix it as soon as possible"}))
return response
Calen Pennington
committed
# Create the module
system = I4xSystem(track_function = make_track_function(request),
render_function = None,
ajax_url = ajax_url,
filestore = OSFS(data_root),
Calen Pennington
committed
try:
instance=courseware.modules.get_module_class(module)(system,
xml,
id,
state=oldstate)
except:
log.exception("Unable to load module instance during ajax call")
log.exception('module=%s, dispatch=%s, id=%s' % (module,dispatch,id))
# log.exception('xml = %s' % xml)
if accepts(request, 'text/html'):
Calen Pennington
committed
return render_to_response("module-error.html", {})
else:
response = HttpResponse(json.dumps({'success': "We're sorry, this module is temporarily unavailable. Our staff is working to fix it as soon as possible"}))
Calen Pennington
committed
return response
Calen Pennington
committed
# Let the module handle the AJAX
ajax_return=instance.handle_ajax(dispatch, request.POST)
# Save the state back to the database
s.state=instance.get_state()
if instance.get_score():
s.grade=instance.get_score()['score']
if s.grade != oldgrade or s.state != oldstate:
s.save()
Calen Pennington
committed
# Return whatever the module wanted to return to the client/caller
return HttpResponse(ajax_return)
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"
ichuang
committed
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
module = 'problem'
xml = content_parser.module_xml(request.user, module, 'id', id, coursename)
ajax_url = settings.MITX_ROOT_URL + '/modx/'+module+'/'+id+'/'
# Create the module (instance of capa_module.Module)
system = I4xSystem(track_function = make_track_function(request),
render_function = None,
ajax_url = ajax_url,
filestore = OSFS(settings.DATA_DIR + xp),
#role = 'staff' if request.user.is_staff else 'student', # TODO: generalize this
)
instance=courseware.modules.get_module_class(module)(system,
xml,
id,
state=None)
ichuang
committed
# create empty student state for this problem, if not previously existing
s = StudentModule.objects.filter(student=request.user,
module_id=id)
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()
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
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_problem_html()
init_js = instance.get_init_js()
destory_js = instance.get_destroy_js()
context = {'id':id,
'msg' : msg,
'lcp' : lcp,
'filename' : lcp.fileobject.name,
'pxmls' : pxmls,
'phtml' : phtml,
"destroy_js":destory_js,
'init_js':init_js,
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('/',''))
msg += '<p>cmd: %s</p>' % cmd
ret = os.popen(cmd).read()
msg += '<p><pre>%s</pre></p>' % ret.replace('<','<')
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