From 4a0969df9d15617b2921a4e484b70c5acf895e25 Mon Sep 17 00:00:00 2001 From: Calen Pennington Date: Thu, 7 Jun 2012 10:41:02 -0400 Subject: [PATCH 1/2] Move tests for xmodules into the xmodule library, and make them run without django --- common/lib/capa/capa_problem.py | 2 - common/lib/capa/inputtypes.py | 40 ++++++++--------- common/lib/capa/responsetypes.py | 14 +++--- common/lib/xmodule/capa_module.py | 14 +++--- .../lib/xmodule}/graders.py | 45 +++++++++---------- common/lib/xmodule/html_module.py | 5 +-- common/lib/xmodule/seq_module.py | 6 +-- common/lib/xmodule/template_module.py | 4 +- .../lib/xmodule}/test_files/imageresponse.xml | 0 .../lib/xmodule}/test_files/multi_bare.xml | 0 .../lib/xmodule}/test_files/multichoice.xml | 0 .../xmodule}/test_files/optionresponse.xml | 0 .../test_files/symbolicresponse.xml | 0 .../lib/xmodule}/test_files/truefalse.xml | 0 .../lib/xmodule}/tests.py | 5 +-- common/lib/xmodule/vertical_module.py | 6 +-- common/lib/xmodule/video_module.py | 14 +++--- common/lib/xmodule/x_module.py | 7 --- lms/djangoapps/courseware/grades.py | 27 ++--------- lms/djangoapps/courseware/module_render.py | 5 ++- lms/lib/dogfood/views.py | 1 + rakefile | 29 ++++++++++-- 22 files changed, 100 insertions(+), 124 deletions(-) rename {lms/djangoapps/courseware => common/lib/xmodule}/graders.py (88%) rename {lms/djangoapps/courseware => common/lib/xmodule}/test_files/imageresponse.xml (100%) rename {lms/djangoapps/courseware => common/lib/xmodule}/test_files/multi_bare.xml (100%) rename {lms/djangoapps/courseware => common/lib/xmodule}/test_files/multichoice.xml (100%) rename {lms/djangoapps/courseware => common/lib/xmodule}/test_files/optionresponse.xml (100%) rename {lms/djangoapps/courseware => common/lib/xmodule}/test_files/test_files/symbolicresponse.xml (100%) rename {lms/djangoapps/courseware => common/lib/xmodule}/test_files/truefalse.xml (100%) rename {lms/djangoapps/courseware => common/lib/xmodule}/tests.py (98%) diff --git a/common/lib/capa/capa_problem.py b/common/lib/capa/capa_problem.py index 5aed06fe34..a817433b49 100644 --- a/common/lib/capa/capa_problem.py +++ b/common/lib/capa/capa_problem.py @@ -21,8 +21,6 @@ from lxml import etree from lxml.etree import Element from xml.sax.saxutils import escape, unescape -from mako.template import Template - from util import contextualize_text import inputtypes diff --git a/common/lib/capa/inputtypes.py b/common/lib/capa/inputtypes.py index f4e34fad2f..de3e20c1cf 100644 --- a/common/lib/capa/inputtypes.py +++ b/common/lib/capa/inputtypes.py @@ -27,8 +27,6 @@ import shlex # for splitting quoted strings from lxml import etree -from mitxmako.shortcuts import render_to_string - def get_input_xml_tags(): ''' Eventually, this will be for all registered input types ''' return SimpleInput.get_xml_tags() @@ -54,7 +52,7 @@ class SimpleInput():# XModule return ['capa_input', 'capa_transform'] def get_html(self): - return self.xml_tags[self.tag](self.xml, self.value, self.status, self.msg) + return self.xml_tags[self.tag](self.xml, self.value, self.status, self.system.render_template, self.msg) def __init__(self, system, xml, item_id = None, track_url=None, state=None, use = 'capa_input'): self.xml = xml @@ -144,7 +142,7 @@ def register_render_function(fn, names=None, cls=SimpleInput): #----------------------------------------------------------------------------- @register_render_function -def optioninput(element, value, status, msg=''): +def optioninput(element, value, status, render_template, msg=''): ''' Select option input type. @@ -171,12 +169,12 @@ def optioninput(element, value, status, msg=''): 'options':osetdict, } - html=render_to_string("optioninput.html", context) + html = render_template("optioninput.html", context) return etree.XML(html) #----------------------------------------------------------------------------- @register_render_function -def choicegroup(element, value, status, msg=''): +def choicegroup(element, value, status, render_template, msg=''): ''' Radio button inputs: multiple choice or true/false @@ -199,11 +197,11 @@ def choicegroup(element, value, status, msg=''): ctext += choice.text # TODO: fix order? choices[choice.get("name")] = ctext context={'id':eid, 'value':value, 'state':status, 'type':type, 'choices':choices} - html=render_to_string("choicegroup.html", context) + html = render_template("choicegroup.html", context) return etree.XML(html) @register_render_function -def textline(element, value, state, msg=""): +def textline(element, value, state, render_template, msg=""): ''' Simple text line input, with optional size specification. ''' @@ -213,13 +211,13 @@ def textline(element, value, state, msg=""): count = int(eid.split('_')[-2])-1 # HACK size = element.get('size') context = {'id':eid, 'value':value, 'state':state, 'count':count, 'size': size, 'msg': msg} - html=render_to_string("textinput.html", context) + html = render_template("textinput.html", context) return etree.XML(html) #----------------------------------------------------------------------------- @register_render_function -def textline_dynamath(element, value, status, msg=''): +def textline_dynamath(element, value, status, render_template, msg=''): ''' Text line input with dynamic math display (equation rendered on client in real time during input). ''' @@ -237,13 +235,13 @@ def textline_dynamath(element, value, status, msg=''): context = {'id':eid, 'value':value, 'state':status, 'count':count, 'size': size, 'msg':msg, } - html=render_to_string("textinput_dynamath.html", context) + html = render_template("textinput_dynamath.html", context) return etree.XML(html) #----------------------------------------------------------------------------- ## TODO: Make a wrapper for @register_render_function -def textbox(element, value, status, msg=''): +def textbox(element, value, status, render_template, msg=''): ''' The textbox is used for code input. The message is the return HTML string from evaluating the code, eg error messages, and output from the code tests. @@ -261,12 +259,12 @@ def textbox(element, value, status, msg=''): 'mode':mode, 'linenumbers':linenumbers, 'rows':rows, 'cols':cols, } - html=render_to_string("textbox.html", context) + html = render_template("textbox.html", context) return etree.XML(html) #----------------------------------------------------------------------------- @register_render_function -def schematic(element, value, status, msg=''): +def schematic(element, value, status, render_template, msg=''): eid = element.get('id') height = element.get('height') width = element.get('width') @@ -285,13 +283,13 @@ def schematic(element, value, status, msg=''): 'analyses':analyses, 'submit_analyses':submit_analyses, } - html=render_to_string("schematicinput.html", context) + html = render_template("schematicinput.html", context) return etree.XML(html) #----------------------------------------------------------------------------- ### TODO: Move out of inputtypes @register_render_function -def math(element, value, status, msg=''): +def math(element, value, status, render_template, msg=''): ''' This is not really an input type. It is a convention from Lon-CAPA, used for displaying a math equation. @@ -316,7 +314,7 @@ def math(element, value, status, msg=''): # mathstr = mathstr.replace('\\displaystyle','') #else: # isinline = True - # html=render_to_string("mathstring.html",{'mathstr':mathstr,'isinline':isinline,'tail':element.tail}) + # html = render_template("mathstring.html",{'mathstr':mathstr,'isinline':isinline,'tail':element.tail}) html = '%s%s' % (mathstr,element.tail) xhtml = etree.XML(html) @@ -326,7 +324,7 @@ def math(element, value, status, msg=''): #----------------------------------------------------------------------------- @register_render_function -def solution(element, value, status, msg=''): +def solution(element, value, status, render_template, msg=''): ''' This is not really an input type. It is just a ... which is given an ID, that is used for displaying an extended answer (a problem "solution") after "show answers" @@ -341,13 +339,13 @@ def solution(element, value, status, msg=''): 'size': size, 'msg':msg, } - html=render_to_string("solutionspan.html", context) + html = render_template("solutionspan.html", context) return etree.XML(html) #----------------------------------------------------------------------------- @register_render_function -def imageinput(element, value, status, msg=''): +def imageinput(element, value, status, render_template, msg=''): ''' Clickable image as an input field. Element should specify the image source, height, and width, eg @@ -378,5 +376,5 @@ def imageinput(element, value, status, msg=''): 'state' : status, # to change 'msg': msg, # to change } - html=render_to_string("imageinput.html", context) + html = render_template("imageinput.html", context) return etree.XML(html) diff --git a/common/lib/capa/responsetypes.py b/common/lib/capa/responsetypes.py index 7fc1bbf2e5..955d2553a1 100644 --- a/common/lib/capa/responsetypes.py +++ b/common/lib/capa/responsetypes.py @@ -256,8 +256,7 @@ def sympy_check2(): self.expect = xml.get('expect') or xml.get('answer') self.myid = xml.get('id') - if settings.DEBUG: - log.info('answer_ids=%s' % self.answer_ids) + log.debug('answer_ids=%s' % self.answer_ids) # the ... stanza should be local to the current . So try looking there first. self.code = None @@ -271,7 +270,7 @@ def sympy_check2(): # ie the comparison function is defined in the stanza instead cfn = xml.get('cfn') if cfn: - if settings.DEBUG: log.info("cfn = %s" % cfn) + log.debug("cfn = %s" % cfn) if cfn in context: self.code = context[cfn] else: @@ -346,7 +345,7 @@ def sympy_check2(): # this is an interface to the Tutor2 check functions fn = self.code ret = None - if settings.DEBUG: log.info(" submission = %s" % submission) + log.debug(" submission = %s" % submission) try: answer_given = submission[0] if (len(idset)==1) else submission # handle variable number of arguments in check function, for backwards compatibility @@ -358,9 +357,8 @@ def sympy_check2(): for argname in argspec.args[nargs:]: kwargs[argname] = self.context[argname] if argname in self.context else None - if settings.DEBUG: - log.debug('[courseware.capa.responsetypes.customresponse] answer_given=%s' % answer_given) - log.info('nargs=%d, args=%s, kwargs=%s' % (nargs,args,kwargs)) + log.debug('[customresponse] answer_given=%s' % answer_given) + log.debug('nargs=%d, args=%s, kwargs=%s' % (nargs,args,kwargs)) ret = fn(*args[:nargs],**kwargs) except Exception,err: @@ -368,7 +366,7 @@ def sympy_check2(): # print "context = ",self.context log.error(traceback.format_exc()) raise Exception,"oops in customresponse (cfn) error %s" % err - if settings.DEBUG: log.info("[courseware.capa.responsetypes.customresponse.get_score] ret = %s" % ret) + log.debug("[courseware.capa.responsetypes.customresponse.get_score] ret = %s" % ret) if type(ret)==dict: correct = ['correct']*len(idset) if ret['ok'] else ['incorrect']*len(idset) msg = ret['msg'] diff --git a/common/lib/xmodule/capa_module.py b/common/lib/xmodule/capa_module.py index 6b03647cd9..33838c7ee1 100644 --- a/common/lib/xmodule/capa_module.py +++ b/common/lib/xmodule/capa_module.py @@ -5,13 +5,11 @@ import json import logging import traceback import re +import StringIO from datetime import timedelta from lxml import etree -## TODO: Abstract out from Django -from mitxmako.shortcuts import render_to_string - from x_module import XModule, XModuleDescriptor from capa.capa_problem import LoncapaProblem, StudentInputError log = logging.getLogger("mitx.courseware") @@ -72,10 +70,10 @@ class Module(XModule): return self.lcp.get_max_score() def get_html(self): - return render_to_string('problem_ajax.html', - {'id':self.item_id, - 'ajax_url':self.ajax_url, - }) + return self.system.render_template('problem_ajax.html', { + 'id': self.item_id, + 'ajax_url': self.ajax_url, + }) def get_problem_html(self, encapsulate=True): html = self.lcp.get_html() @@ -138,7 +136,7 @@ class Module(XModule): 'explain': explain, } - html=render_to_string('problem.html', context) + html = self.system.render_template('problem.html', context) if encapsulate: html = '
'.format(id=self.item_id,ajax_url=self.ajax_url)+html+"
" diff --git a/lms/djangoapps/courseware/graders.py b/common/lib/xmodule/graders.py similarity index 88% rename from lms/djangoapps/courseware/graders.py rename to common/lib/xmodule/graders.py index 94b2ca78cd..cbe1d90fd8 100644 --- a/lms/djangoapps/courseware/graders.py +++ b/common/lib/xmodule/graders.py @@ -1,8 +1,6 @@ import abc import logging -from django.conf import settings - from collections import namedtuple log = logging.getLogger("mitx.courseware") @@ -11,6 +9,26 @@ log = logging.getLogger("mitx.courseware") # Section either indicates the name of the problem or the name of the section Score = namedtuple("Score", "earned possible graded section") +def aggregate_scores(scores, section_name = "summary"): + total_correct_graded = sum(score.earned for score in scores if score.graded) + total_possible_graded = sum(score.possible for score in scores if score.graded) + + total_correct = sum(score.earned for score in scores) + total_possible = sum(score.possible for score in scores) + + #regardless of whether or not it is graded + all_total = Score(total_correct, + total_possible, + False, + section_name) + #selecting only graded things + graded_total = Score(total_correct_graded, + total_possible_graded, + True, + section_name) + + return all_total, graded_total + def grader_from_conf(conf): """ This creates a CourseGrader from a configuration (such as in course_settings.py). @@ -162,18 +180,6 @@ class SingleSectionGrader(CourseGrader): percent = 0.0 detail = "{name} - 0% (?/?)".format(name = self.name) - if settings.GENERATE_PROFILE_SCORES: - points_possible = random.randrange(50, 100) - points_earned = random.randrange(40, points_possible) - percent = points_earned / float(points_possible) - detail = "{name} - {percent:.0%} ({earned:.3n}/{possible:.3n})".format( name = self.name, - percent = percent, - earned = float(points_earned), - possible = float(points_possible)) - - - - breakdown = [{'percent': percent, 'label': self.short_label, 'detail': detail, 'category': self.category, 'prominent': True}] return {'percent' : percent, @@ -244,17 +250,6 @@ class AssignmentFormatGrader(CourseGrader): percentage = 0 summary = "{section_type} {index} Unreleased - 0% (?/?)".format(index = i+1, section_type = self.section_type) - if settings.GENERATE_PROFILE_SCORES: - points_possible = random.randrange(10, 50) - points_earned = random.randrange(5, points_possible) - percentage = points_earned / float(points_possible) - summary = "{section_type} {index} - {name} - {percent:.0%} ({earned:.3n}/{possible:.3n})".format(index = i+1, - section_type = self.section_type, - name = "Randomly Generated", - percent = percentage, - earned = float(points_earned), - possible = float(points_possible) ) - short_label = "{short_label} {index:02d}".format(index = i+1, short_label = self.short_label) breakdown.append( {'percent': percentage, 'label': short_label, 'detail': summary, 'category': self.category} ) diff --git a/common/lib/xmodule/html_module.py b/common/lib/xmodule/html_module.py index 9543107be0..8d9d0b17d6 100644 --- a/common/lib/xmodule/html_module.py +++ b/common/lib/xmodule/html_module.py @@ -1,8 +1,6 @@ import json import logging -from mitxmako.shortcuts import render_to_response, render_to_string - from x_module import XModule, XModuleDescriptor from lxml import etree @@ -34,8 +32,7 @@ class Module(XModule): except: # For backwards compatibility. TODO: Remove if self.DEBUG: log.info('[courseware.modules.html_module] filename=%s' % self.filename) - #return render_to_string(self.filename, {'id': self.item_id}) - return render_to_string(self.filename, {'id': self.item_id},namespace='course') + return self.system.render_template(self.filename, {'id': self.item_id}, namespace='course') def __init__(self, system, xml, item_id, state=None): XModule.__init__(self, system, xml, item_id, state) diff --git a/common/lib/xmodule/seq_module.py b/common/lib/xmodule/seq_module.py index de80e2bf05..f643eaab4a 100644 --- a/common/lib/xmodule/seq_module.py +++ b/common/lib/xmodule/seq_module.py @@ -2,8 +2,6 @@ import json from lxml import etree -from mitxmako.shortcuts import render_to_string - from x_module import XModule, XModuleDescriptor # HACK: This shouldn't be hard-coded to two types @@ -77,9 +75,9 @@ class Module(XModule): 'tag':self.xmltree.tag} if self.xmltree.tag in ['sequential', 'videosequence']: - self.content=render_to_string('seq_module.html',params) + self.content = self.system.render_template('seq_module.html', params) if self.xmltree.tag == 'tab': - self.content=render_to_string('tab_module.html',params) + self.content = self.system.render_template('tab_module.html', params) self.rendered = True def __init__(self, system, xml, item_id, state=None): diff --git a/common/lib/xmodule/template_module.py b/common/lib/xmodule/template_module.py index 97a2c09501..111ccda82c 100644 --- a/common/lib/xmodule/template_module.py +++ b/common/lib/xmodule/template_module.py @@ -1,7 +1,5 @@ import json -from mitxmako.shortcuts import render_to_string - from x_module import XModule, XModuleDescriptor from lxml import etree @@ -24,4 +22,4 @@ class Module(XModule): xmltree = etree.fromstring(xml) filename = xmltree[0].text params = dict(xmltree.items()) - self.html = render_to_string(filename, params, namespace = 'custom_tags') + self.html = self.system.render_template(filename, params, namespace = 'custom_tags') diff --git a/lms/djangoapps/courseware/test_files/imageresponse.xml b/common/lib/xmodule/test_files/imageresponse.xml similarity index 100% rename from lms/djangoapps/courseware/test_files/imageresponse.xml rename to common/lib/xmodule/test_files/imageresponse.xml diff --git a/lms/djangoapps/courseware/test_files/multi_bare.xml b/common/lib/xmodule/test_files/multi_bare.xml similarity index 100% rename from lms/djangoapps/courseware/test_files/multi_bare.xml rename to common/lib/xmodule/test_files/multi_bare.xml diff --git a/lms/djangoapps/courseware/test_files/multichoice.xml b/common/lib/xmodule/test_files/multichoice.xml similarity index 100% rename from lms/djangoapps/courseware/test_files/multichoice.xml rename to common/lib/xmodule/test_files/multichoice.xml diff --git a/lms/djangoapps/courseware/test_files/optionresponse.xml b/common/lib/xmodule/test_files/optionresponse.xml similarity index 100% rename from lms/djangoapps/courseware/test_files/optionresponse.xml rename to common/lib/xmodule/test_files/optionresponse.xml diff --git a/lms/djangoapps/courseware/test_files/test_files/symbolicresponse.xml b/common/lib/xmodule/test_files/test_files/symbolicresponse.xml similarity index 100% rename from lms/djangoapps/courseware/test_files/test_files/symbolicresponse.xml rename to common/lib/xmodule/test_files/test_files/symbolicresponse.xml diff --git a/lms/djangoapps/courseware/test_files/truefalse.xml b/common/lib/xmodule/test_files/truefalse.xml similarity index 100% rename from lms/djangoapps/courseware/test_files/truefalse.xml rename to common/lib/xmodule/test_files/truefalse.xml diff --git a/lms/djangoapps/courseware/tests.py b/common/lib/xmodule/tests.py similarity index 98% rename from lms/djangoapps/courseware/tests.py rename to common/lib/xmodule/tests.py index 7cdfa1ffae..69e69aa1d9 100644 --- a/lms/djangoapps/courseware/tests.py +++ b/common/lib/xmodule/tests.py @@ -13,9 +13,8 @@ import numpy import xmodule import capa.calc as calc import capa.capa_problem as lcp -import courseware.graders as graders -from courseware.graders import Score, CourseGrader, WeightedSubsectionsGrader, SingleSectionGrader, AssignmentFormatGrader -from courseware.grades import aggregate_scores +from xmodule import graders +from xmodule.graders import Score, aggregate_scores from nose.plugins.skip import SkipTest class I4xSystem(object): diff --git a/common/lib/xmodule/vertical_module.py b/common/lib/xmodule/vertical_module.py index 0819fb9f1b..7eb6dda0b8 100644 --- a/common/lib/xmodule/vertical_module.py +++ b/common/lib/xmodule/vertical_module.py @@ -1,7 +1,5 @@ import json -from mitxmako.shortcuts import render_to_response, render_to_string - from x_module import XModule, XModuleDescriptor from lxml import etree @@ -19,7 +17,9 @@ class Module(XModule): return ["vertical", "problemset"] def get_html(self): - return render_to_string('vert_module.html',{'items':self.contents}) + return self.system.render_template('vert_module.html', { + 'items': self.contents + }) def __init__(self, system, xml, item_id, state=None): XModule.__init__(self, system, xml, item_id, state) diff --git a/common/lib/xmodule/video_module.py b/common/lib/xmodule/video_module.py index ad26ede81e..8f8a2c2ffd 100644 --- a/common/lib/xmodule/video_module.py +++ b/common/lib/xmodule/video_module.py @@ -3,8 +3,6 @@ import logging from lxml import etree -from mitxmako.shortcuts import render_to_response, render_to_string - from x_module import XModule, XModuleDescriptor log = logging.getLogger("mitx.courseware.modules") @@ -38,11 +36,13 @@ class Module(XModule): return self.youtube def get_html(self): - return render_to_string('video.html',{'streams':self.video_list(), - 'id':self.item_id, - 'position':self.position, - 'name':self.name, - 'annotations':self.annotations}) + return self.system.render_template('video.html', { + 'streams': self.video_list(), + 'id': self.item_id, + 'position': self.position, + 'name': self.name, + 'annotations': self.annotations + }) def __init__(self, system, xml, item_id, state=None): XModule.__init__(self, system, xml, item_id, state) diff --git a/common/lib/xmodule/x_module.py b/common/lib/xmodule/x_module.py index 035b3819d1..d783694fee 100644 --- a/common/lib/xmodule/x_module.py +++ b/common/lib/xmodule/x_module.py @@ -1,7 +1,5 @@ from lxml import etree -import courseware.progress - def dummy_track(event_type, event): pass @@ -71,11 +69,6 @@ class XModule(object): ### Functions used in the LMS - def get_completion(self): - ''' This is mostly unimplemented. - It gives a progress indication -- e.g. 30 minutes of 1.5 hours watched. 3 of 5 problems done, etc. ''' - return courseware.progress.completion() - def get_state(self): ''' State of the object, as stored in the database ''' diff --git a/lms/djangoapps/courseware/grades.py b/lms/djangoapps/courseware/grades.py index 84efe82b95..354cf5991f 100644 --- a/lms/djangoapps/courseware/grades.py +++ b/lms/djangoapps/courseware/grades.py @@ -25,8 +25,8 @@ import types from django.conf import settings from courseware import global_course_settings -from courseware import graders -from courseware.graders import Score +from xmodule import graders +from xmodule.graders import Score from models import StudentModule import courseware.content_parser as content_parser import xmodule @@ -116,7 +116,7 @@ def grade_sheet(student,coursename=None): graded = False scores.append( Score(correct,total, graded, p.get("name")) ) - section_total, graded_total = aggregate_scores(scores, s.get("name")) + section_total, graded_total = graders.aggregate_scores(scores, s.get("name")) #Add the graded total to totaled_scores format = s.get('format', "") subtitle = s.get('subtitle', format) @@ -146,27 +146,6 @@ def grade_sheet(student,coursename=None): return {'courseware_summary' : chapters, 'grade_summary' : grade_summary} -def aggregate_scores(scores, section_name = "summary"): - total_correct_graded = sum(score.earned for score in scores if score.graded) - total_possible_graded = sum(score.possible for score in scores if score.graded) - - total_correct = sum(score.earned for score in scores) - total_possible = sum(score.possible for score in scores) - - #regardless of whether or not it is graded - all_total = Score(total_correct, - total_possible, - False, - section_name) - #selecting only graded things - graded_total = Score(total_correct_graded, - total_possible_graded, - True, - section_name) - - return all_total, graded_total - - def get_score(user, problem, cache, coursename=None): ## HACK: assumes max score is fixed per problem id = problem.get('id') diff --git a/lms/djangoapps/courseware/module_render.py b/lms/djangoapps/courseware/module_render.py index 19cff7b304..97fd1b948c 100644 --- a/lms/djangoapps/courseware/module_render.py +++ b/lms/djangoapps/courseware/module_render.py @@ -27,7 +27,7 @@ class I4xSystem(object): of the courseware (e.g. import into other types of courseware, LMS, or if we want to have a sandbox server for user-contributed content) ''' - def __init__(self, ajax_url, track_function, render_function, filestore=None): + def __init__(self, ajax_url, track_function, render_function, render_template, filestore=None): self.ajax_url = ajax_url self.track_function = track_function if not filestore: @@ -37,6 +37,7 @@ class I4xSystem(object): if settings.DEBUG: log.info("[courseware.module_render.I4xSystem] filestore path = %s" % filestore) self.render_function = render_function + self.render_template = render_template self.exception404 = Http404 self.DEBUG = settings.DEBUG @@ -117,6 +118,7 @@ def get_module(user, request, xml_module, module_object_preload, position=None): system = I4xSystem(track_function = make_track_function(request), render_function = lambda x: render_x_module(user, request, x, module_object_preload, position), + render_template = render_to_string, ajax_url = ajax_url, filestore = OSFS(data_root), ) @@ -225,6 +227,7 @@ def modx_dispatch(request, module=None, dispatch=None, id=None): # Create the module system = I4xSystem(track_function = make_track_function(request), render_function = None, + render_template = render_to_string, ajax_url = ajax_url, filestore = OSFS(data_root), ) diff --git a/lms/lib/dogfood/views.py b/lms/lib/dogfood/views.py index ca9c89c0dc..31d3b3ded2 100644 --- a/lms/lib/dogfood/views.py +++ b/lms/lib/dogfood/views.py @@ -179,6 +179,7 @@ def quickedit(request, id=None, qetemplate='quickedit.html',coursename=None): # Create the module (instance of capa_module.Module) system = I4xSystem(track_function = make_track_function(request), render_function = None, + render_template = render_to_string, ajax_url = ajax_url, filestore = OSFS(settings.DATA_DIR + xp), #role = 'staff' if request.user.is_staff else 'student', # TODO: generalize this diff --git a/rakefile b/rakefile index d7de49ccd2..8f49668506 100644 --- a/rakefile +++ b/rakefile @@ -58,10 +58,31 @@ task :pylint => LMS_REPORT_DIR do end end -desc "Run all django tests on our djangoapps" -task :test => LMS_REPORT_DIR do - ENV['NOSE_XUNIT_FILE'] = File.join(LMS_REPORT_DIR, "nosetests.xml") - sh(django_admin(:lms, :test, 'test', *Dir['lms/djangoapps'].each)) +[:lms].each do |system| + task_name = "test_#{system}" + report_dir = File.join(REPORT_DIR, task_name) + directory report_dir + + desc "Run all django tests on our djangoapps for the #{system}" + task task_name => report_dir do + ENV['NOSE_XUNIT_FILE'] = File.join(report_dir, "nosetests.xml") + sh(django_admin(:lms, :test, 'test', *Dir['lms/djangoapps'].each)) + end + task :test => task_name +end + +Dir["common/lib/*"].each do |lib| + task_name = "test_#{lib}" + + report_dir = File.join(REPORT_DIR, task_name) + directory report_dir + + desc "Run tests for common lib #{lib}" + task task_name do + ENV['NOSE_XUNIT_FILE'] = File.join(report_dir, "nosetests.xml") + sh("nosetests #{lib}") + end + task :test => task_name end desc <<-desc From a83fdc71f96ffe11428ca71208f0930d9da43bf3 Mon Sep 17 00:00:00 2001 From: Calen Pennington Date: Thu, 7 Jun 2012 10:44:16 -0400 Subject: [PATCH 2/2] Document the aggregate_scores function --- common/lib/xmodule/graders.py | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/common/lib/xmodule/graders.py b/common/lib/xmodule/graders.py index cbe1d90fd8..55dc4e4f7a 100644 --- a/common/lib/xmodule/graders.py +++ b/common/lib/xmodule/graders.py @@ -9,26 +9,34 @@ log = logging.getLogger("mitx.courseware") # Section either indicates the name of the problem or the name of the section Score = namedtuple("Score", "earned possible graded section") -def aggregate_scores(scores, section_name = "summary"): + +def aggregate_scores(scores, section_name="summary"): + """ + scores: A list of Score objects + returns: A tuple (all_total, graded_total). + all_total: A Score representing the total score summed over all input scores + graded_total: A Score representing the score summed over all graded input scores + """ total_correct_graded = sum(score.earned for score in scores if score.graded) total_possible_graded = sum(score.possible for score in scores if score.graded) - + total_correct = sum(score.earned for score in scores) total_possible = sum(score.possible for score in scores) - + #regardless of whether or not it is graded - all_total = Score(total_correct, - total_possible, - False, - section_name) + all_total = Score(total_correct, + total_possible, + False, + section_name) #selecting only graded things - graded_total = Score(total_correct_graded, - total_possible_graded, - True, + graded_total = Score(total_correct_graded, + total_possible_graded, + True, section_name) return all_total, graded_total + def grader_from_conf(conf): """ This creates a CourseGrader from a configuration (such as in course_settings.py).