diff --git a/common/lib/capa/capa/responsetypes.py b/common/lib/capa/capa/responsetypes.py index ab5eaf950c..df98f62fc5 100644 --- a/common/lib/capa/capa/responsetypes.py +++ b/common/lib/capa/capa/responsetypes.py @@ -746,7 +746,7 @@ class NumericalResponse(LoncapaResponse): id=xml.get('id'))[0] self.tolerance = contextualize_text(self.tolerance_xml, context) except Exception: - self.tolerance = 0 + self.tolerance = '0' try: self.answer_id = xml.xpath('//*[@id=$id]//textline/@id', id=xml.get('id'))[0] diff --git a/common/lib/capa/capa/util.py b/common/lib/capa/capa/util.py index 75acf657e8..75bd9fb5bc 100644 --- a/common/lib/capa/capa/util.py +++ b/common/lib/capa/capa/util.py @@ -11,7 +11,7 @@ def compare_with_tolerance(v1, v2, tol): - v1 : student result (number) - v2 : instructor result (number) - - tol : tolerance (string or number) + - tol : tolerance (string representing a number) ''' relative = tol.endswith('%') diff --git a/common/lib/xmodule/xmodule/css/capa/display.scss b/common/lib/xmodule/xmodule/css/capa/display.scss index aa3f96c2e7..fd67a3804e 100644 --- a/common/lib/xmodule/xmodule/css/capa/display.scss +++ b/common/lib/xmodule/xmodule/css/capa/display.scss @@ -572,7 +572,7 @@ section.problem { } } - section { + > section { padding: 9px; } } diff --git a/common/lib/xmodule/xmodule/js/src/collapsible.coffee b/common/lib/xmodule/xmodule/js/src/collapsible.coffee index 314e7ca868..2f4b84e253 100644 --- a/common/lib/xmodule/xmodule/js/src/collapsible.coffee +++ b/common/lib/xmodule/xmodule/js/src/collapsible.coffee @@ -11,7 +11,7 @@ class @Collapsible ### el.find('.longform').hide() el.find('.shortform').append('See full output') - el.find('.collapsible section').hide() + el.find('.collapsible header + section').hide() el.find('.full').click @toggleFull el.find('.collapsible header a').click @toggleHint diff --git a/lms/djangoapps/courseware/grades.py b/lms/djangoapps/courseware/grades.py index 555f1c5f89..36932f9e42 100644 --- a/lms/djangoapps/courseware/grades.py +++ b/lms/djangoapps/courseware/grades.py @@ -329,9 +329,15 @@ def progress_summary(student, request, course, student_module_cache): def get_score(course_id, user, problem_descriptor, module_creator, student_module_cache): """ Return the score for a user on a problem, as a tuple (correct, total). + e.g. (5,7) if you got 5 out of 7 points. + + If this problem doesn't have a score, or we couldn't load it, returns (None, + None). user: a Student object - problem: an XModule + problem_descriptor: an XModuleDescriptor + module_creator: a function that takes a descriptor, and returns the corresponding XModule for this user. + Can return None if user doesn't have access, or if something else went wrong. cache: A StudentModuleCache """ if not (problem_descriptor.stores_state and problem_descriptor.has_score): @@ -339,14 +345,16 @@ def get_score(course_id, user, problem_descriptor, module_creator, student_modul return (None, None) correct = 0.0 - + instance_module = student_module_cache.lookup( course_id, problem_descriptor.category, problem_descriptor.location.url()) - + if not instance_module: # If the problem was not in the cache, we need to instantiate the problem. - # Otherwise, the max score (cached in instance_module) won't be available + # Otherwise, the max score (cached in instance_module) won't be available problem = module_creator(problem_descriptor) + if problem is None: + return (None, None) instance_module = get_instance_module(course_id, user, problem, student_module_cache) # If this problem is ungraded/ungradable, bail @@ -361,7 +369,7 @@ def get_score(course_id, user, problem_descriptor, module_creator, student_modul weight = getattr(problem_descriptor, 'weight', None) if weight is not None: if total == 0: - log.exception("Cannot reweight a problem with zero weight. Problem: " + str(instance_module)) + log.exception("Cannot reweight a problem with zero total points. Problem: " + str(instance_module)) return (correct, total) correct = correct * weight / total total = weight diff --git a/lms/djangoapps/instructor/management/__init__.py b/lms/djangoapps/instructor/management/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/lms/djangoapps/instructor/management/commands/__init__.py b/lms/djangoapps/instructor/management/commands/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/lms/djangoapps/instructor/management/commands/dump_grades.py b/lms/djangoapps/instructor/management/commands/dump_grades.py new file mode 100644 index 0000000000..65825271f3 --- /dev/null +++ b/lms/djangoapps/instructor/management/commands/dump_grades.py @@ -0,0 +1,79 @@ +#!/usr/bin/python +# +# django management command: dump grades to csv files +# for use by batch processes + +import os, sys, string +import datetime +import json + +from instructor.views import * +from courseware.courses import get_course_by_id +from xmodule.modulestore.django import modulestore + +from django.conf import settings +from django.core.management.base import BaseCommand + +class Command(BaseCommand): + help = "dump grades to CSV file. Usage: dump_grades course_id_or_dir filename dump_type\n" + help += " course_id_or_dir: either course_id or course_dir\n" + help += " filename: where the output CSV is to be stored\n" + # help += " start_date: end date as M/D/Y H:M (defaults to end of available data)" + help += " dump_type: 'all' or 'raw' (see instructor dashboard)" + + def handle(self, *args, **options): + + # current grading logic and data schema doesn't handle dates + # datetime.strptime("21/11/06 16:30", "%m/%d/%y %H:%M") + + print "args = ", args + + course_id = 'MITx/8.01rq_MW/Classical_Mechanics_Reading_Questions_Fall_2012_MW_Section' + fn = "grades.csv" + get_raw_scores = False + + if len(args)>0: + course_id = args[0] + if len(args)>1: + fn = args[1] + if len(args)>2: + get_raw_scores = args[2].lower()=='raw' + + request = self.DummyRequest() + try: + course = get_course_by_id(course_id) + except Exception as err: + if course_id in modulestore().courses: + course = modulestore().courses[course_id] + else: + print "-----------------------------------------------------------------------------" + print "Sorry, cannot find course %s" % course_id + print "Please provide a course ID or course data directory name, eg content-mit-801rq" + return + + print "-----------------------------------------------------------------------------" + print "Dumping grades from %s to file %s (get_raw_scores=%s)" % (course.id, fn, get_raw_scores) + datatable = get_student_grade_summary_data(request, course, course.id, get_raw_scores=get_raw_scores) + + fp = open(fn,'w') + + writer = csv.writer(fp, dialect='excel', quotechar='"', quoting=csv.QUOTE_ALL) + writer.writerow(datatable['header']) + for datarow in datatable['data']: + encoded_row = [unicode(s).encode('utf-8') for s in datarow] + writer.writerow(encoded_row) + + fp.close() + print "Done: %d records dumped" % len(datatable['data']) + + class DummyRequest(object): + META = {} + def __init__(self): + return + def get_host(self): + return 'edx.mit.edu' + def is_secure(self): + return False + + + diff --git a/lms/templates/courseware/progress.html b/lms/templates/courseware/progress.html index 87ac06bae6..81268ff081 100644 --- a/lms/templates/courseware/progress.html +++ b/lms/templates/courseware/progress.html @@ -65,16 +65,19 @@ ${progress_graph.body(grade_summary, course.grade_cutoffs, "grade-detail-graph") %endif

- %if len(section['scores']) > 0: -
+
+ %if len(section['scores']) > 0:

${ "Problem Scores: " if section['graded'] else "Practice Scores: "}

    %for score in section['scores']:
  1. ${"{0:.3n}/{1:.3n}".format(float(score.earned),float(score.possible))}
  2. %endfor
-
- %endif + %else: +

No problem scores in this section

+ %endif +
+ %endfor diff --git a/lms/urls.py b/lms/urls.py index 662e41235e..862621b7e1 100644 --- a/lms/urls.py +++ b/lms/urls.py @@ -244,7 +244,7 @@ if settings.MITX_FEATURES.get('AUTH_USE_OPENID'): if settings.MITX_FEATURES.get('AUTH_USE_OPENID_PROVIDER'): urlpatterns += ( url(r'^openid/provider/login/$', 'external_auth.views.provider_login', name='openid-provider-login'), - url(r'^openid/provider/login/(?:[\w%\. ]+)$', 'external_auth.views.provider_identity', name='openid-provider-login-identity'), + url(r'^openid/provider/login/(?:.+)$', 'external_auth.views.provider_identity', name='openid-provider-login-identity'), url(r'^openid/provider/identity/$', 'external_auth.views.provider_identity', name='openid-provider-identity'), url(r'^openid/provider/xrds/$', 'external_auth.views.provider_xrds', name='openid-provider-xrds') )