diff --git a/courseware/capa_module.py b/courseware/capa_module.py index b829c9c305..7e5384feb3 100644 --- a/courseware/capa_module.py +++ b/courseware/capa_module.py @@ -4,26 +4,48 @@ import random, numpy, math, scipy, sys, StringIO, os, struct, json from x_module import XModule +from capa_problem import LoncapaProblem + from xml.dom.minidom import parse, parseString -class LoncapaProblem(XModule): +## TODO: Abstract out from Django +from django.conf import settings +from djangomako.shortcuts import render_to_response, render_to_string + +class LoncapaModule(XModule): + xml_tags=["problem"] + id_attribute="filename" + def get_state(self): + print "got" + return self.lcp.get_state() def get_score(self): + return self.lcp.get_score() def max_score(self): + return len(lcp.questions) def get_html(self): - - def handle_ajax(self, json): - - def __init__(self, filename, id=None, state=None): + inner_html=self.lcp.get_html() + content={'name':self.name, + 'html':inner_html} + return render_to_string('problem.html', + {'problem':content, 'id':self.filename}) -if __name__=='__main__': - p=LoncapaProblem('') - print p.getHtml() - print p.getContext() - print p.getSeed() - # Chef and puppot + def __init__(self, xml, item_id, ajax_url=None, track_url=None, state=None): + XModule.__init__(self, xml, item_id, ajax_url, track_url, state) + dom=parseString(xml) + node=dom.childNodes[0] + self.filename=node.getAttribute("filename") + filename=settings.DATA_DIR+self.filename+".xml" + self.name=node.getAttribute("name") + print state + self.lcp=LoncapaProblem(filename, item_id, state) + + # Temporary + + def check_problem(self, get): + pass diff --git a/courseware/capa_problem.py b/courseware/capa_problem.py index f17be7a62b..31162cc0da 100644 --- a/courseware/capa_problem.py +++ b/courseware/capa_problem.py @@ -26,16 +26,10 @@ class LoncapaProblem(): return {'score':correct, 'total':len(self.questions)} - def max_score(self): - pass - def get_html(self): ''' Return the HTML of the question ''' return self.text - def handle_ajax(self, json): - pass - def __init__(self, filename, id=None, state=None): ''' Create a new problem of the type defined in filename. By default, this will generate a random problem. Passing @@ -71,7 +65,7 @@ class LoncapaProblem(): ot=False ## Are we in an outtext context? for e in dom.childNodes: - if e.localName=='script' and context==None: + if e.localName=='script': exec e.childNodes[0].data in g,self.context if e.localName=='endouttext': ot=False @@ -85,7 +79,7 @@ class LoncapaProblem(): self.text=buf.getvalue() self.text=self.contextualize_text(self.text) - + self.filename=filename text="" context={} # Execution context from loncapa/python diff --git a/courseware/content_parser.py b/courseware/content_parser.py new file mode 100644 index 0000000000..83d597ab2c --- /dev/null +++ b/courseware/content_parser.py @@ -0,0 +1,25 @@ +from django.conf import settings +from xml.dom.minidom import parse, parseString + + +def toc_from_xml(active_chapter,active_section): + dom=parse(settings.DATA_DIR+'course.xml') + + course = dom.getElementsByTagName('course')[0] + name=course.getAttribute("name") + chapters = course.getElementsByTagName('chapter') + ch=list() + for c in chapters: + sections=list() + for s in c.getElementsByTagName('section'): + sections.append({'name':s.getAttribute("name"), + 'time':s.getAttribute("time"), + 'format':s.getAttribute("format"), + 'due':s.getAttribute("due"), + 'active':(c.getAttribute("name")==active_chapter and \ + s.getAttribute("name")==active_section)}) + ch.append({'name':c.getAttribute("name"), + 'sections':sections, + 'active':(c.getAttribute("name")==active_chapter)}) + return ch + diff --git a/courseware/models.py b/courseware/models.py index 3dbf7c46e3..867cfc06d1 100644 --- a/courseware/models.py +++ b/courseware/models.py @@ -44,10 +44,13 @@ class StudentModule(models.Model): state = models.TextField(null=True, blank=True) grade = models.FloatField(null=True, blank=True) student = models.ForeignKey(User) - MODULE_TYPES = (('hw','homework'),) - module_type = models.CharField(max_length=32, choices=MODULE_TYPES, default='hw') - module_id = models.CharField(max_length=255) # Filename + MODULE_TYPES = (('problem','problem'),) + module_type = models.CharField(max_length=32, choices=MODULE_TYPES, default='problem') + module_id = models.CharField(max_length=255) # Filename for homeworks, etc. created = models.DateTimeField(auto_now_add=True) modified = models.DateTimeField(auto_now=True) class Meta: - unique_together = (('student', 'module_id'),) + unique_together = (('student', 'module_id', 'module_type'),) + + def __unicode__(self): + return self.module_type+'/'+self.student.username+"/"+self.module_id+'/'+str(self.state)[:20] diff --git a/courseware/views.py b/courseware/views.py index b5913b1982..416cf3f0ce 100644 --- a/courseware/views.py +++ b/courseware/views.py @@ -12,9 +12,11 @@ from django.shortcuts import redirect import StringIO +from django.http import Http404 + import urllib -import capa_problem +import capa_module, capa_problem from models import StudentModule @@ -22,6 +24,8 @@ import urllib from django.conf import settings +from content_parser import * + template_imports={'urllib':urllib} def profile(request): @@ -49,9 +53,7 @@ def profile(request): correct=response.grade else: correct=0 - # CRITICAL TODO: Benchmark, and probably cache. We shouldn't recalculate this - # every time. By modularity, this also belongs inside LoncapaProblem. - total=len(capa_problem.LoncapaProblem(settings.DATA_DIR+id+'.xml', id=id).questions) + total=capa_module.LoncapaModule(p, id=id).max_score() scores.append((int(correct),total)) score={'course':course.getAttribute('name'), 'section':s.getAttribute("name"), @@ -71,27 +73,6 @@ def profile(request): } return render_to_response('profile.html', context) -def toc_from_xml(active_chapter,active_section): - dom=parse(settings.DATA_DIR+'course.xml') - - course = dom.getElementsByTagName('course')[0] - name=course.getAttribute("name") - chapters = course.getElementsByTagName('chapter') - ch=list() - for c in chapters: - sections=list() - for s in c.getElementsByTagName('section'): - sections.append({'name':s.getAttribute("name"), - 'time':s.getAttribute("time"), - 'format':s.getAttribute("format"), - 'due':s.getAttribute("due"), - 'active':(c.getAttribute("name")==active_chapter and \ - s.getAttribute("name")==active_section)}) - ch.append({'name':c.getAttribute("name"), - 'sections':sections, - 'active':(c.getAttribute("name")==active_chapter)}) - return ch - def render_accordion(request,course,chapter,section): ''' Draws accordion. Takes current position in accordion as parameter. Returns (initialization_javascript, content)''' @@ -138,28 +119,43 @@ def vertical_module(request, module): return {'js':js, 'content':render_to_string('vert_module.html',{'items':contents})} -def render_problem(request, filename): +def render_x_module(request, xml_module): # Check if problem has an instance in DB - s = StudentModule.objects.filter(student=request.user, module_id=filename) + module_id=xml_module.getAttribute(capa_module.LoncapaModule.id_attribute) + s = StudentModule.objects.filter(student=request.user, module_id=module_id) if len(s) == 0: - # If not, create one, and return it - problem=capa_problem.LoncapaProblem(settings.DATA_DIR+filename+'.xml', id=filename) + # If not, create one, and save it + problem=capa_module.LoncapaModule(xml_module.toxml(), module_id) smod=StudentModule(student=request.user, - module_id=filename, + module_id=module_id, state=problem.get_state()) smod.save() elif len(s) == 1: # If so, render it - problem=capa_problem.LoncapaProblem(settings.DATA_DIR+filename+'.xml', - id=filename, - state=s[0].state) + problem=capa_module.LoncapaModule(xml_module.toxml(), + module_id, + state=s[0].state) else: raise Exception("Database is inconsistent (1).") - - return problem.get_html() -def reset_problem(request): - s = StudentModule.objects.filter(student=request.user, module_id=request.GET['id']) + return {'content':problem.get_html()} + +def modx_dispatch(request, dispatch=None, id=None): + s = StudentModule.objects.filter(student=request.user, module_id=id) + if len(s) == 0: + raise Http404 + + if dispatch=='problem_check': + return check_problem(request) + elif dispatch=='problem_reset': + return reset_problem(request,id) + else: + print "AAA" + raise Http404 + + +def reset_problem(request,id): + s = StudentModule.objects.filter(student=request.user, module_id=id) s[0].delete() return HttpResponse(json.dumps({}), mimetype="application/json") @@ -194,32 +190,11 @@ def check_problem(request): return HttpResponse(js, mimetype="application/json") -def problem_module(request, module): - filename=module.getAttribute('filename') - content={'name':module.getAttribute('name'), - 'html':render_problem(request, filename)} - return {'content':render_to_string('problem.html', {'problem':content, 'id':filename})} - -def homework_module(request, module): - content={'name':module.getAttribute('name'), - 'problems':[]} - filename=module.getAttribute('filename') - dom=parse(settings.DATA_DIR+filename) - homework=dom.getElementsByTagName('homework')[0] - for e in homework.childNodes: - if e.nodeType==1: - problem={'name':e.getAttribute('name'), - 'html':render_problem(request, e.getAttribute('filename'))} - content['problems'].append(problem) - - return {'content':render_to_string('homework.html', {'homework':content})} - module_types={'video':video_module, 'html':html_module, 'tab':tab_module, 'vertical':vertical_module, - 'homework':homework_module, - 'problem':problem_module} + 'problem':render_x_module} #'lab':lab_module, def render_module(request, module): diff --git a/courseware/x_module.py b/courseware/x_module.py index 7aedf0db1c..f9b1f5fac2 100644 --- a/courseware/x_module.py +++ b/courseware/x_module.py @@ -3,6 +3,14 @@ class XModule: Initialized on access with __init__, first time with state=None, and then with state ''' + def get_xml_tags(): + ''' Tags in the courseware file guaranteed to correspond to the module ''' + return [] + + def get_id_attribute(): + ''' An attribute in the XML scheme that is guaranteed unique. ''' + return "name" + def get_state(self): return "" diff --git a/urls.py b/urls.py index 4748cb3bf1..808d383fd8 100644 --- a/urls.py +++ b/urls.py @@ -9,8 +9,8 @@ urlpatterns = patterns('', url(r'^courseware/(?P[^/]*)/(?P[^/]*)/(?P
[^/]*)/$', 'courseware.views.index'), url(r'^courseware/(?P[^/]*)/(?P[^/]*)/$', 'courseware.views.index'), url(r'^courseware/(?P[^/]*)/$', 'courseware.views.index'), - url(r'^courseware/problem_check$', 'courseware.views.check_problem'), - url(r'^courseware/problem_reset$', 'courseware.views.reset_problem'), +# url(r'^courseware/modx/(?P[^/]*)/problem_check$', 'courseware.views.check_problem'), + url(r'^courseware/modx/(?P[^/]*)/(?P[^/]*)$', 'courseware.views.modx_dispatch'), #reset_problem'), url(r'^courseware/$', 'courseware.views.index'), url(r'^profile$', 'courseware.views.profile'), # url(r'^admin/', include('django.contrib.admin.urls')),