-
Notifications
You must be signed in to change notification settings - Fork 3.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Waheed/openended problems commands #1946
Changes from 4 commits
ef1f55a
b140d66
0d31772
1c13360
f47e7cc
c599dfd
9891acb
627cb9e
fce9c45
0b50613
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -348,6 +348,29 @@ def fix_invalid_state(self): | |
last_completed_child = next((i for i, child in reversed(list(enumerate(children))) if child['child_state'] == self.DONE), 0) | ||
self.current_task_number = min(last_completed_child + 1, len(best_task_states) - 1) | ||
|
||
def create_task(self, task_state, task_xml): | ||
"""Create task object for given task state and task xml.""" | ||
|
||
tag_name = self.get_tag_name(task_xml) | ||
children = self.child_modules() | ||
task_descriptor = children['descriptors'][tag_name](self.system) | ||
task_parsed_xml = task_descriptor.definition_from_xml(etree.fromstring(task_xml), self.system) | ||
task = children['modules'][tag_name]( | ||
self.system, | ||
self.location, | ||
task_parsed_xml, | ||
task_descriptor, | ||
self.static_data, | ||
instance_state=task_state, | ||
) | ||
return task | ||
|
||
def get_latest_task(self): | ||
"""Return latest task object.""" | ||
|
||
last_task_state = self.task_states[-1] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is it ever possible that self.task_states is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, this is done wrong. |
||
last_task_xml = self.task_xml[-1] | ||
return self.create_task(last_task_state, last_task_xml) | ||
|
||
def reset_task_state(self, message=""): | ||
""" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
#!/usr/bin/python | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be: |
||
# | ||
# django management command: dump grades to csv files | ||
# for use by batch processes | ||
|
||
from ...utils import get_descriptor, read_csv, get_affected_students_from_ids, get_module_for_student | ||
from django.core.management.base import BaseCommand | ||
|
||
|
||
class Command(BaseCommand): | ||
help = "Usage: posts problem to xqueue \n" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This doesn't explain what the arguments are. |
||
|
||
def handle(self, *args, **options): | ||
|
||
print "args = ", args | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems like a debugging leave-behind. |
||
|
||
if len(args) > 0: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this be if len(args) > 2, since you're using args[0], args[1], args[2]? |
||
course_id = args[0] | ||
location = args[1] | ||
affected_students_ids = read_csv(args[2]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. args is checked for more than none, but then we read 3 arguments for it, which won't be a good way to handle wrong input. There are a variety of ways to parse input that will be more robust. |
||
else: | ||
print self.help | ||
return | ||
|
||
descriptor = get_descriptor(course_id, location) | ||
if descriptor is None: | ||
print "Location not found in course" | ||
return | ||
|
||
affected_students = get_affected_students_from_ids(affected_students_ids) | ||
|
||
for student in affected_students: | ||
try: | ||
module = get_module_for_student(student, course_id, location) | ||
latest_task = module._xmodule.child_module.get_latest_task() | ||
latest_task.send_to_grader(latest_task.latest_answer(), latest_task.system) | ||
except Exception as err: | ||
print err | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it appropriate to continue with more students if an exception happens? Are you sure that |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
#!/usr/bin/python | ||
# | ||
# Admin command for open ended problems. | ||
|
||
from xmodule.open_ended_grading_classes.openendedchild import OpenEndedChild | ||
from ...utils import get_descriptor, get_module_for_student, get_enrolled_students | ||
from django.core.management.base import BaseCommand | ||
|
||
|
||
class Command(BaseCommand): | ||
"""Admin command for open ended problems.""" | ||
|
||
help = "Usage: openended_stats course_id location \n" | ||
|
||
def handle(self, *args, **options): | ||
"""Handler for command.""" | ||
|
||
print "args = ", args | ||
|
||
if len(args) > 0: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same as before. |
||
course_id = args[0] | ||
location = args[1] | ||
else: | ||
print self.help | ||
return | ||
|
||
descriptor = get_descriptor(course_id, location) | ||
if descriptor is None: | ||
print "Location not found in course" | ||
return | ||
|
||
enrolled_students = get_enrolled_students(course_id) | ||
print "Total students enrolled: {0}".format(enrolled_students.count()) | ||
|
||
self.get_state_counts(enrolled_students, course_id, location) | ||
|
||
def get_state_counts(self, students, course_id, location): | ||
"""Print stats of students.""" | ||
|
||
stats = { | ||
OpenEndedChild.INITIAL: 0, | ||
OpenEndedChild.ASSESSING: 0, | ||
OpenEndedChild.POST_ASSESSMENT: 0, | ||
OpenEndedChild.DONE: 0 | ||
} | ||
|
||
for index, student in enumerate(students): | ||
if index % 100 == 0: | ||
print "{0} students processed".format(index) | ||
|
||
module = get_module_for_student(student, course_id, location) | ||
latest_task = module._xmodule.child_module.get_latest_task() | ||
stats[latest_task.child_state] += 1 | ||
|
||
print stats |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
|
||
from courseware.model_data import FieldDataCache | ||
from courseware.module_render import get_module_for_descriptor | ||
from courseware.courses import get_course_by_id | ||
import csv | ||
from django.contrib.auth.models import User | ||
|
||
|
||
def get_descriptor(course_id, location): | ||
"""Find descriptor for the location in the course.""" | ||
|
||
course = get_course_by_id(course_id) | ||
grading_context = course.grading_context | ||
descriptor = None | ||
for section_format, sections in grading_context['graded_sections'].iteritems(): | ||
try: | ||
for section in sections: | ||
section_descriptor = section['section_descriptor'] | ||
descriptor = find_descriptor_in_children(section_descriptor, location) | ||
if descriptor: | ||
break | ||
|
||
if descriptor: | ||
break | ||
|
||
except Exception as err: | ||
print err.message | ||
|
||
return descriptor | ||
|
||
|
||
def find_descriptor_in_children(descriptor, location): | ||
"""Recursively look for location in descriptors children.""" | ||
|
||
try: | ||
if descriptor.id == location: | ||
return descriptor | ||
children = descriptor.get_children() | ||
except: | ||
children = [] | ||
for module_descriptor in children: | ||
child_descriptor = find_descriptor_in_children(module_descriptor, location) | ||
if child_descriptor: | ||
return child_descriptor | ||
return None | ||
|
||
|
||
def create_module(student, course, descriptor, request): | ||
"""Create module for student from descriptor.""" | ||
|
||
field_data_cache = FieldDataCache([descriptor], course.id, student) | ||
return get_module_for_descriptor(student, request, descriptor, field_data_cache, course.id) | ||
|
||
|
||
def get_module_for_student(student, course, location, request=None): | ||
"""Return module for student from location.""" | ||
|
||
if isinstance(student, str): | ||
try: | ||
student = User.objects.get(username=student) | ||
except User.DoesNotExist: | ||
return None | ||
|
||
if request is None: | ||
request = DummyRequest() | ||
request.user = student | ||
request.session = {} | ||
if isinstance(course, str): | ||
course = get_course_by_id(course) | ||
if course is None: | ||
return None | ||
descriptor = get_descriptor(course.id, location) | ||
module = create_module(student, course, descriptor, request) | ||
return module | ||
|
||
|
||
def get_enrolled_students(course_id): | ||
"""Return enrolled students for course.""" | ||
|
||
enrolled_students = User.objects.filter( | ||
courseenrollment__course_id=course_id, | ||
courseenrollment__is_active=1 | ||
).prefetch_related("groups").order_by('username') | ||
return enrolled_students | ||
|
||
|
||
def get_affected_students_from_ids(affected_students_ids): | ||
"""Return affected students form ids list.""" | ||
|
||
affected_students = User.objects.filter( | ||
id__in=affected_students_ids, | ||
courseenrollment__is_active=1 | ||
).prefetch_related("groups").order_by('username') | ||
return affected_students | ||
|
||
|
||
def read_csv(path_to_csv): | ||
""" | ||
reads a csv and returns a list | ||
""" | ||
|
||
affected_students_ids = [] | ||
with open(path_to_csv) as csv_file: | ||
csv_reader = csv.reader(csv_file) | ||
for row in csv_reader: | ||
affected_students_ids.append(row[0]) | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A list comprehension would be good here: |
||
return affected_students_ids | ||
|
||
|
||
class DummyRequest(object): | ||
"""Dummy request""" | ||
|
||
META = {} | ||
|
||
def __init__(self): | ||
return | ||
|
||
def get_host(self): | ||
return 'edx.mit.edu' | ||
|
||
def is_secure(self): | ||
return False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we call this variable
definition
for more clarity?