Commit 579544ec by Calen Pennington

Merge pull request #626 from MITx/feature/victor/answer-export

Feature/victor/answer export
parents 77029076 a8cd4633
......@@ -4,11 +4,14 @@ from __future__ import division
import random
import logging
from collections import defaultdict
from django.conf import settings
from django.contrib.auth.models import User
from models import StudentModuleCache
from module_render import get_module, get_instance_module
from xmodule import graders
from xmodule.capa_module import CapaModule
from xmodule.course_module import CourseDescriptor
from xmodule.graders import Score
from models import StudentModule
......@@ -24,6 +27,71 @@ def yield_module_descendents(module):
stack.extend( next_module.get_display_items() )
yield next_module
def yield_problems(request, course, student):
"""
Return an iterator over capa_modules that this student has
potentially answered. (all that student has answered will definitely be in
the list, but there may be others as well).
"""
grading_context = course.grading_context
student_module_cache = StudentModuleCache(course.id, student, grading_context['all_descriptors'])
for section_format, sections in grading_context['graded_sections'].iteritems():
for section in sections:
section_descriptor = section['section_descriptor']
# If the student hasn't seen a single problem in the section, skip it.
skip = True
for moduledescriptor in section['xmoduledescriptors']:
if student_module_cache.lookup(
course.id, moduledescriptor.category, moduledescriptor.location.url()):
skip = False
break
if skip:
continue
section_module = get_module(student, request,
section_descriptor.location, student_module_cache,
course.id)
if section_module is None:
# student doesn't have access to this module, or something else
# went wrong.
# log.debug("couldn't get module for student {0} for section location {1}"
# .format(student.username, section_descriptor.location))
continue
for problem in yield_module_descendents(section_module):
if isinstance(problem, CapaModule):
yield problem
def answer_distributions(request, course):
"""
Given a course_descriptor, compute frequencies of answers for each problem:
Format is:
dict: (problem url_name, problem display_name, problem_id) -> (dict : answer -> count)
TODO (vshnayder): this is currently doing a full linear pass through all
students and all problems. This will be just a little slow.
"""
counts = defaultdict(lambda: defaultdict(int))
enrolled_students = User.objects.filter(courseenrollment__course_id=course.id)
for student in enrolled_students:
for capa_module in yield_problems(request, course, student):
for problem_id in capa_module.lcp.student_answers:
answer = capa_module.lcp.student_answers[problem_id]
key = (capa_module.url_name, capa_module.display_name, problem_id)
counts[key][answer] += 1
return counts
def grade(student, request, course, student_module_cache=None, keep_raw_scores=False):
"""
This grades a student as quickly as possible. It retuns the
......
import csv
import json
import logging
import urllib
import itertools
import StringIO
from functools import partial
......@@ -368,5 +370,3 @@ def progress(request, course_id, student_id=None):
return render_to_response('courseware/progress.html', context)
......@@ -144,6 +144,10 @@ def instructor_dashboard(request, course_id):
return return_csv('grades_%s_raw.csv' % course_id,
get_student_grade_summary_data(request, course, course_id, get_raw_scores=True))
elif 'Download CSV of answer distributions' in action:
track.views.server_track(request, 'dump-answer-dist-csv', {}, page='idashboard')
return return_csv('answer_dist_%s.csv' % course_id, get_answers_distribution(request, course_id))
elif 'List course staff' in action:
group = get_staff_group(course)
msg += 'Staff group = %s' % group.name
......@@ -290,7 +294,7 @@ def grade_summary(request, course_id):
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
def enroll_students(request, course_id):
''' Allows a staff member to enroll students in a course.
"""Allows a staff member to enroll students in a course.
This is a short-term hack for Berkeley courses launching fall
2012. In the long term, we would like functionality like this, but
......@@ -300,7 +304,7 @@ def enroll_students(request, course_id):
It is poorly written and poorly tested, but it's designed to be
stripped out.
'''
"""
course = get_course_with_access(request.user, course_id, 'staff')
existing_students = [ce.user.email for ce in CourseEnrollment.objects.filter(course_id=course_id)]
......@@ -328,6 +332,28 @@ def enroll_students(request, course_id):
'rejected_students': rejected_students,
'debug': new_students})
def get_answers_distribution(request, course_id):
"""
Get the distribution of answers for all graded problems in the course.
Return a dict with two keys:
'header': a header row
'data': a list of rows
"""
course = get_course_with_access(request.user, course_id, 'staff')
dist = grades.answer_distributions(request, course)
d = {}
d['header'] = ['url_name', 'display name', 'answer id', 'answer', 'count']
d['data'] = [[url_name, display_name, answer_id, a, answers[a]]
for (url_name, display_name, answer_id), answers in dist.items()
for a in answers]
return d
#-----------------------------------------------------------------------------
......
......@@ -58,6 +58,9 @@ table.stat_table td {
<input type="submit" name="action" value="Dump all RAW grades for all students in this course">
<input type="submit" name="action" value="Download CSV of all RAW grades">
<p>
<input type="submit" name="action" value="Download CSV of answer distributions">
%if instructor_access:
<hr width="40%" style="align:left">
<p>
......
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