Commit 1fce77d8 by Brian Talbot

Merge branch 'master' into feature/btalbot/studio-checklists

parents 7b74d972 0b2226b0
...@@ -33,7 +33,11 @@ load-plugins= ...@@ -33,7 +33,11 @@ load-plugins=
# can either give multiple identifier separated by comma (,) or put this option # can either give multiple identifier separated by comma (,) or put this option
# multiple time (only on the command line, not in the configuration file where # multiple time (only on the command line, not in the configuration file where
# it should appear only once). # it should appear only once).
disable=E1102,W0142 disable=
# W0141: Used builtin function 'map'
# W0142: Used * or ** magic
# R0903: Too few public methods (1/2)
W0141,W0142,R0903
[REPORTS] [REPORTS]
...@@ -97,7 +101,7 @@ bad-functions=map,filter,apply,input ...@@ -97,7 +101,7 @@ bad-functions=map,filter,apply,input
module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
# Regular expression which should only match correct module level names # Regular expression which should only match correct module level names
const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__)|log)$ const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__)|log|urlpatterns)$
# Regular expression which should only match correct class names # Regular expression which should only match correct class names
class-rgx=[A-Z_][a-zA-Z0-9]+$ class-rgx=[A-Z_][a-zA-Z0-9]+$
...@@ -106,7 +110,7 @@ class-rgx=[A-Z_][a-zA-Z0-9]+$ ...@@ -106,7 +110,7 @@ class-rgx=[A-Z_][a-zA-Z0-9]+$
function-rgx=[a-z_][a-z0-9_]{2,30}$ function-rgx=[a-z_][a-z0-9_]{2,30}$
# Regular expression which should only match correct method names # Regular expression which should only match correct method names
method-rgx=[a-z_][a-z0-9_]{2,30}$ method-rgx=([a-z_][a-z0-9_]{2,60}|setUp|set[Uu]pClass|tearDown|tear[Dd]ownClass|assert[A-Z]\w*)$
# Regular expression which should only match correct instance attribute names # Regular expression which should only match correct instance attribute names
attr-rgx=[a-z_][a-z0-9_]{2,30}$ attr-rgx=[a-z_][a-z0-9_]{2,30}$
......
...@@ -112,7 +112,7 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase): ...@@ -112,7 +112,7 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
self.assertTrue(sequential.location.url() in chapter.definition['children']) self.assertTrue(sequential.location.url() in chapter.definition['children'])
self.client.post(reverse('delete_item'), self.client.post(reverse('delete_item'),
json.dumps({'id': sequential.location.url(), 'delete_children':'true'}), json.dumps({'id': sequential.location.url(), 'delete_children':'true', 'delete_all_versions':'true'}),
"application/json") "application/json")
found = False found = False
......
...@@ -646,15 +646,15 @@ def delete_item(request): ...@@ -646,15 +646,15 @@ def delete_item(request):
modulestore('direct').delete_item(item.location) modulestore('direct').delete_item(item.location)
# cdodge: we need to remove our parent's pointer to us so that it is no longer dangling # cdodge: we need to remove our parent's pointer to us so that it is no longer dangling
if delete_all_versions:
parent_locs = modulestore('direct').get_parent_locations(item_loc, None) parent_locs = modulestore('direct').get_parent_locations(item_loc, None)
for parent_loc in parent_locs: for parent_loc in parent_locs:
parent = modulestore('direct').get_item(parent_loc) parent = modulestore('direct').get_item(parent_loc)
item_url = item_loc.url() item_url = item_loc.url()
if item_url in parent.definition["children"]: if item_url in parent.definition["children"]:
parent.definition["children"].remove(item_url) parent.definition["children"].remove(item_url)
modulestore('direct').update_children(parent.location, parent.definition["children"]) modulestore('direct').update_children(parent.location, parent.definition["children"])
return HttpResponse() return HttpResponse()
......
...@@ -87,7 +87,7 @@ class FolditModule(XModule): ...@@ -87,7 +87,7 @@ class FolditModule(XModule):
from foldit.models import Score from foldit.models import Score
leaders = [(e['username'], e['score']) for e in Score.get_tops_n(10)] leaders = [(e['username'], e['score']) for e in Score.get_tops_n(10)]
leaders.sort(key=lambda x: x[1]) leaders.sort(key=lambda x: -x[1])
return leaders return leaders
......
...@@ -32,10 +32,11 @@ if [ ! -d /mnt/virtualenvs/"$JOB_NAME" ]; then ...@@ -32,10 +32,11 @@ if [ ! -d /mnt/virtualenvs/"$JOB_NAME" ]; then
virtualenv /mnt/virtualenvs/"$JOB_NAME" virtualenv /mnt/virtualenvs/"$JOB_NAME"
fi fi
export PIP_DOWNLOAD_CACHE=/mnt/pip-cache
source /mnt/virtualenvs/"$JOB_NAME"/bin/activate source /mnt/virtualenvs/"$JOB_NAME"/bin/activate
pip install -q -r pre-requirements.txt pip install -q -r pre-requirements.txt
pip install -q -r test-requirements.txt yes w | pip install -q -r test-requirements.txt -r requirements.txt
yes w | pip install -q -r requirements.txt
rake clobber rake clobber
rake pep8 rake pep8
......
'''
This is a one-off command aimed at fixing a temporary problem encountered where partial credit was awarded for
code problems, but the resulting score (or grade) was mistakenly set to zero because of a bug in
CorrectMap.get_npoints().
'''
import json
import logging
from optparse import make_option
from django.core.management.base import BaseCommand
from courseware.models import StudentModule
from capa.correctmap import CorrectMap
LOG = logging.getLogger(__name__)
class Command(BaseCommand):
'''
The fix here is to recalculate the score/grade based on the partial credit.
To narrow down the set of problems that might need fixing, the StudentModule
objects to be checked is filtered down to those:
created < '2013-03-08 15:45:00' (the problem must have been answered before the fix was installed,
on Prod and Edge)
modified > '2013-03-07 20:18:00' (the problem must have been visited after the bug was introduced)
state like '%"npoints": 0.%' (the problem must have some form of partial credit).
'''
num_visited = 0
num_changed = 0
option_list = BaseCommand.option_list + (
make_option('--save',
action='store_true',
dest='save_changes',
default=False,
help='Persist the changes that were encountered. If not set, no changes are saved.'), )
def fix_studentmodules(self, save_changes):
'''Identify the list of StudentModule objects that might need fixing, and then fix each one'''
modules = StudentModule.objects.filter(modified__gt='2013-03-07 20:18:00',
created__lt='2013-03-08 15:45:00',
state__contains='"npoints": 0.')
for module in modules:
self.fix_studentmodule_grade(module, save_changes)
def fix_studentmodule_grade(self, module, save_changes):
''' Fix the grade assigned to a StudentModule'''
module_state = module.state
if module_state is None:
# not likely, since we filter on it. But in general...
LOG.info("No state found for {type} module {id} for student {student} in course {course_id}"
.format(type=module.module_type, id=module.module_state_key,
student=module.student.username, course_id=module.course_id))
return
state_dict = json.loads(module_state)
self.num_visited += 1
# LoncapaProblem.get_score() checks student_answers -- if there are none, we will return a grade of 0
# Check that this is the case, but do so sooner, before we do any of the other grading work.
student_answers = state_dict['student_answers']
if (not student_answers) or len(student_answers) == 0:
# we should not have a grade here:
if module.grade != 0:
LOG.error("No answer found but grade {grade} exists for {type} module {id} for student {student} "
"in course {course_id}".format(grade=module.grade,
type=module.module_type, id=module.module_state_key,
student=module.student.username, course_id=module.course_id))
else:
LOG.debug("No answer and no grade found for {type} module {id} for student {student} "
"in course {course_id}".format(grade=module.grade,
type=module.module_type, id=module.module_state_key,
student=module.student.username, course_id=module.course_id))
return
# load into a CorrectMap, as done in LoncapaProblem.__init__():
correct_map = CorrectMap()
if 'correct_map' in state_dict:
correct_map.set_dict(state_dict['correct_map'])
# calculate score the way LoncapaProblem.get_score() works, by deferring to
# CorrectMap's get_npoints implementation.
correct = 0
for key in correct_map:
correct += correct_map.get_npoints(key)
if module.grade == correct:
# nothing to change
LOG.debug("Grade matches for {type} module {id} for student {student} in course {course_id}"
.format(type=module.module_type, id=module.module_state_key,
student=module.student.username, course_id=module.course_id))
elif save_changes:
# make the change
LOG.info("Grade changing from {0} to {1} for {type} module {id} for student {student} "
"in course {course_id}".format(module.grade, correct,
type=module.module_type, id=module.module_state_key,
student=module.student.username, course_id=module.course_id))
module.grade = correct
module.save()
self.num_changed += 1
else:
# don't make the change, but log that the change would be made
LOG.info("Grade would change from {0} to {1} for {type} module {id} for student {student} "
"in course {course_id}".format(module.grade, correct,
type=module.module_type, id=module.module_state_key,
student=module.student.username, course_id=module.course_id))
self.num_changed += 1
def handle(self, **options):
'''Handle management command request'''
save_changes = options['save_changes']
LOG.info("Starting run: save_changes = {0}".format(save_changes))
self.fix_studentmodules(save_changes)
LOG.info("Finished run: updating {0} of {1} modules".format(self.num_changed, self.num_visited))
django==1.4.3 -r repo-requirements.txt
pip
numpy==1.6.2
scipy==0.11.0
Markdown==2.2.1
pygments==1.5
lxml==3.0.1
boto==2.6.0
mako==0.7.3
python-memcached==1.48
python-openid==2.2.5
path.py
django_debug_toolbar
fs==0.4.0
beautifulsoup==3.2.1
beautifulsoup4==4.1.3 beautifulsoup4==4.1.3
feedparser==5.1.3 beautifulsoup==3.2.1
requests==0.14.2 boto==2.6.0
http://sympy.googlecode.com/files/sympy-0.7.1.tar.gz
newrelic==1.8.0.13
glob2==0.3
pymongo==2.4.1
django_nose==1.1
nosexcover==1.0.7
rednose==0.3.3
GitPython==0.3.2.RC1
mock==0.8.0
PyYAML==3.10
South==0.7.6
pytz==2012h
django-celery==3.0.11 django-celery==3.0.11
django-countries==1.5 django-countries==1.5
django-kombu==0.9.4 django-debug-toolbar-mongo
django-followit==0.0.3 django-followit==0.0.3
django-jasmine==0.3.2 django-jasmine==0.3.2
django-keyedcache==1.4-6 django-keyedcache==1.4-6
django-kombu==0.9.4
django-mako==0.1.5pre django-mako==0.1.5pre
django-masquerade==0.1.6 django-masquerade==0.1.6
django-mptt==0.5.5
django-openid-auth==0.4 django-openid-auth==0.4
django-robots==0.9.1 django-robots==0.9.1
django-sekizai==0.6.1
django-ses==0.4.1 django-ses==0.4.1
django-storages==1.1.5 django-storages==1.1.5
django-threaded-multihost==1.4-1 django-threaded-multihost==1.4-1
django-sekizai==0.6.1 django==1.4.3
django-mptt==0.5.5 django_debug_toolbar
sorl-thumbnail==11.12 django_nose==1.1
networkx==1.7 dogapi==1.2.1
pygraphviz==1.1
-r repo-requirements.txt
nltk==2.0.4
django-debug-toolbar-mongo
dogstatsd-python==0.2.1 dogstatsd-python==0.2.1
MySQL-python==1.2.4c1
sphinx==1.1.3
factory_boy factory_boy
Shapely==1.2.16 feedparser==5.1.3
fs==0.4.0
GitPython==0.3.2.RC1
glob2==0.3
http://sympy.googlecode.com/files/sympy-0.7.1.tar.gz
ipython==0.13.1 ipython==0.13.1
xmltodict==0.4.1 lxml==3.0.1
mako==0.7.3
Markdown==2.2.1
mock==0.8.0
MySQL-python==1.2.4c1
networkx==1.7
newrelic==1.8.0.13
nltk==2.0.4
nosexcover==1.0.7
numpy==1.6.2
paramiko==1.9.0 paramiko==1.9.0
path.py
Pillow==1.7.8 Pillow==1.7.8
dogapi==1.2.1 pip
pygments==1.5
pygraphviz==1.1
pymongo==2.4.1
python-memcached==1.48
python-openid==2.2.5
pytz==2012h
PyYAML==3.10
rednose==0.3.3
requests==0.14.2
scipy==0.11.0
Shapely==1.2.16
sorl-thumbnail==11.12
South==0.7.6
sphinx==1.1.3
xmltodict==0.4.1
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment