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']:
- ${"{0:.3n}/{1:.3n}".format(float(score.earned),float(score.possible))}
%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