From 386480905fe4d2d347db8685bd2e44a8817e98e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Andr=C3=A9s=20Rocha?= Date: Tue, 9 Oct 2012 17:48:46 -0400 Subject: [PATCH 1/8] Fix openid provider URL matching for usernames with non-word characters --- lms/urls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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') ) From e256b2390bf4b251b559ff27751a28336b1fe96f Mon Sep 17 00:00:00 2001 From: Bridger Maxwell Date: Wed, 10 Oct 2012 14:53:39 -0400 Subject: [PATCH 2/8] Added an indicator on Progress page when there are no problem scores in a section. --- lms/templates/courseware/progress.html | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) 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 From 6f60af71fd799f71ced3eb004ca2f1fca6a7d10a Mon Sep 17 00:00:00 2001 From: Tom Giannattasio Date: Wed, 10 Oct 2012 16:14:32 -0400 Subject: [PATCH 3/8] tweaked collapsible selector to target the proper section; functionality like this should have its own class to avoid selector conflicts, but it may be too late to fix this now --- common/lib/xmodule/xmodule/js/src/collapsible.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 00e2f093b9b3af578eb0509e026667558571dfeb Mon Sep 17 00:00:00 2001 From: Tom Giannattasio Date: Wed, 10 Oct 2012 16:19:03 -0400 Subject: [PATCH 4/8] removed extra padding on nested sections --- common/lib/xmodule/xmodule/css/capa/display.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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; } } From 778017fc3e3a8bb686e224d295212d502c3d6c24 Mon Sep 17 00:00:00 2001 From: ichuang Date: Thu, 11 Oct 2012 11:30:57 -0400 Subject: [PATCH 5/8] add django management command for dumping grades to CSV file --- .../instructor/management/__init__.py | 0 .../management/commands/__init__.py | 0 .../management/commands/dump_grades.py | 79 +++++++++++++++++++ 3 files changed, 79 insertions(+) create mode 100644 lms/djangoapps/instructor/management/__init__.py create mode 100644 lms/djangoapps/instructor/management/commands/__init__.py create mode 100644 lms/djangoapps/instructor/management/commands/dump_grades.py 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 + + + From 331f35145fe80cdd7fcde4da66813e2298c1e985 Mon Sep 17 00:00:00 2001 From: Lyla Fischer Date: Thu, 11 Oct 2012 11:59:27 -0400 Subject: [PATCH 6/8] fixed default tolerance on numericalresponse --- common/lib/capa/capa/responsetypes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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] From 0cdbd170c2c9992bab457347256af52f1e6498d2 Mon Sep 17 00:00:00 2001 From: Lyla Fischer Date: Thu, 11 Oct 2012 12:23:06 -0400 Subject: [PATCH 7/8] fixed documentation --- common/lib/capa/capa/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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('%') From a5dcdcb705c684cfcf54b091cd7d035aa0fbbf7a Mon Sep 17 00:00:00 2001 From: Victor Shnayder Date: Thu, 11 Oct 2012 13:08:05 -0400 Subject: [PATCH 8/8] Check for None to fix 3.091 progress tab crash - get_module() contract says it can return None (e.g. if access control check failed) --- lms/djangoapps/courseware/grades.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) 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