Commit 97fb0544 by ichuang

add export grades to remote gradebook to instructor dashboard

parent 200493a5
...@@ -8,6 +8,8 @@ import os ...@@ -8,6 +8,8 @@ import os
import requests import requests
import urllib import urllib
from StringIO import StringIO
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import User, Group from django.contrib.auth.models import User, Group
from django.http import HttpResponse from django.http import HttpResponse
...@@ -77,9 +79,12 @@ def instructor_dashboard(request, course_id): ...@@ -77,9 +79,12 @@ def instructor_dashboard(request, course_id):
data.append(['metadata', escape(str(course.metadata))]) data.append(['metadata', escape(str(course.metadata))])
datatable['data'] = data datatable['data'] = data
def return_csv(fn, datatable): def return_csv(fn, datatable, fp=None):
response = HttpResponse(mimetype='text/csv') if fp is None:
response['Content-Disposition'] = 'attachment; filename={0}'.format(fn) response = HttpResponse(mimetype='text/csv')
response['Content-Disposition'] = 'attachment; filename={0}'.format(fn)
else:
response = fp
writer = csv.writer(response, dialect='excel', quotechar='"', quoting=csv.QUOTE_ALL) writer = csv.writer(response, dialect='excel', quotechar='"', quoting=csv.QUOTE_ALL)
writer.writerow(datatable['header']) writer.writerow(datatable['header'])
for datarow in datatable['data']: for datarow in datatable['data']:
...@@ -161,6 +166,65 @@ def instructor_dashboard(request, course_id): ...@@ -161,6 +166,65 @@ def instructor_dashboard(request, course_id):
return return_csv('answer_dist_{0}.csv'.format(course_id), get_answers_distribution(request, course_id)) return return_csv('answer_dist_{0}.csv'.format(course_id), get_answers_distribution(request, course_id))
#---------------------------------------- #----------------------------------------
# export grades to remote gradebook
elif action=='List assignments available in remote gradebook':
msg2, datatable = _do_remote_gradebook(request.user, course, 'get-assignments')
msg += msg2
elif action=='List assignments available for this course':
log.debug(action)
allgrades = get_student_grade_summary_data(request, course, course_id, get_grades=True)
assignments = [[x] for x in allgrades['assignments']]
datatable = {'header': ['Assignment Name']}
datatable['data'] = assignments
datatable['title'] = action
msg += 'assignments=<pre>%s</pre>' % assignments
elif action=='List enrolled students matching remote gradebook':
stud_data = get_student_grade_summary_data(request, course, course_id, get_grades=False)
msg2, rg_stud_data = _do_remote_gradebook(request.user, course, 'get-membership')
datatable = {'header': ['Student email', 'Match?']}
rg_students = [ x['email'] for x in rg_stud_data['retdata'] ]
def domatch(x):
return '<font color="green">yes</font>' if x.email in rg_students else '<font color="red">No</font>'
datatable['data'] = [[x.email, domatch(x)] for x in stud_data['students']]
datatable['title'] = action
elif action in ['Display grades for assignment', 'Export grades for assignment to remote gradebook',
'Export CSV file of grades for assignment']:
log.debug(action)
datatable = {}
aname = request.POST.get('assignment_name','')
if not aname:
msg += "<font color='red'>Please enter an assignment name</font>"
else:
allgrades = get_student_grade_summary_data(request, course, course_id, get_grades=True)
if aname not in allgrades['assignments']:
msg += "<font color='red'>Invalid assignment name '%s'</font>" % aname
else:
aidx = allgrades['assignments'].index(aname)
datatable = {'header': ['External email', aname]}
datatable['data'] = [[x.email, x.grades[aidx]] for x in allgrades['students']]
datatable['title'] = 'Grades for assignment "%s"' % aname
if 'Export CSV' in action:
# generate and return CSV file
return return_csv('grades %s.csv' % aname, datatable)
elif 'remote gradebook' in action:
fp = StringIO()
return_csv('', datatable, fp=fp)
fp.seek(0)
files = {'datafile': fp}
msg2, dataset = _do_remote_gradebook(request.user, course, 'post-grades', files=files)
msg += msg2
#----------------------------------------
# Admin # Admin
elif 'List course staff' in action: elif 'List course staff' in action:
...@@ -305,7 +369,7 @@ def instructor_dashboard(request, course_id): ...@@ -305,7 +369,7 @@ def instructor_dashboard(request, course_id):
elif action == 'List sections available in remote gradebook': elif action == 'List sections available in remote gradebook':
msg2, datatable = _do_remote_gradebook(course, 'get-sections') msg2, datatable = _do_remote_gradebook(request.user, course, 'get-sections')
msg += msg2 msg += msg2
elif action in ['List students in section in remote gradebook', elif action in ['List students in section in remote gradebook',
...@@ -313,7 +377,7 @@ def instructor_dashboard(request, course_id): ...@@ -313,7 +377,7 @@ def instructor_dashboard(request, course_id):
'Merge enrollment list with remote gradebook']: 'Merge enrollment list with remote gradebook']:
section = request.POST.get('gradebook_section','') section = request.POST.get('gradebook_section','')
msg2, datatable = _do_remote_gradebook(course, 'get-membership', dict(section=section) ) msg2, datatable = _do_remote_gradebook(request.user, course, 'get-membership', dict(section=section) )
msg += msg2 msg += msg2
if not 'List' in action: if not 'List' in action:
...@@ -357,7 +421,7 @@ def instructor_dashboard(request, course_id): ...@@ -357,7 +421,7 @@ def instructor_dashboard(request, course_id):
return render_to_response('courseware/instructor_dashboard.html', context) return render_to_response('courseware/instructor_dashboard.html', context)
def _do_remote_gradebook(course, action, args=None): def _do_remote_gradebook(user, course, action, args=None, files=None):
''' '''
Perform remote gradebook action. Returns msg, datatable. Perform remote gradebook action. Returns msg, datatable.
''' '''
...@@ -378,11 +442,11 @@ def _do_remote_gradebook(course, action, args=None): ...@@ -378,11 +442,11 @@ def _do_remote_gradebook(course, action, args=None):
if args is None: if args is None:
args = {} args = {}
data = dict(submit=action, gradebook=rgname) data = dict(submit=action, gradebook=rgname, user=user.email)
data.update(args) data.update(args)
try: try:
resp = requests.post(rgurl, data=data, verify=False) resp = requests.post(rgurl, data=data, verify=False, files=files)
retdict = json.loads(resp.content) retdict = json.loads(resp.content)
except Exception as err: except Exception as err:
msg = "Failed to communicate with gradebook server at %s<br/>" % rgurl msg = "Failed to communicate with gradebook server at %s<br/>" % rgurl
...@@ -392,7 +456,7 @@ def _do_remote_gradebook(course, action, args=None): ...@@ -392,7 +456,7 @@ def _do_remote_gradebook(course, action, args=None):
return msg, {} return msg, {}
msg = '<pre>%s</pre>' % retdict['msg'].replace('\n','<br/>') msg = '<pre>%s</pre>' % retdict['msg'].replace('\n','<br/>')
retdata = retdict['data'] retdata = retdict['data'] # a list of dicts
if retdata: if retdata:
datatable = {'header': retdata[0].keys()} datatable = {'header': retdata[0].keys()}
...@@ -495,16 +559,18 @@ def get_student_grade_summary_data(request, course, course_id, get_grades=True, ...@@ -495,16 +559,18 @@ def get_student_grade_summary_data(request, course, course_id, get_grades=True,
enrolled_students = User.objects.filter(courseenrollment__course_id=course_id).prefetch_related("groups").order_by('username') enrolled_students = User.objects.filter(courseenrollment__course_id=course_id).prefetch_related("groups").order_by('username')
header = ['ID', 'Username', 'Full Name', 'edX email', 'External email'] header = ['ID', 'Username', 'Full Name', 'edX email', 'External email']
assignments = []
if get_grades and enrolled_students.count() > 0: if get_grades and enrolled_students.count() > 0:
# just to construct the header # just to construct the header
gradeset = grades.grade(enrolled_students[0], request, course, keep_raw_scores=get_raw_scores) gradeset = grades.grade(enrolled_students[0], request, course, keep_raw_scores=get_raw_scores)
# log.debug('student {0} gradeset {1}'.format(enrolled_students[0], gradeset)) # log.debug('student {0} gradeset {1}'.format(enrolled_students[0], gradeset))
if get_raw_scores: if get_raw_scores:
header += [score.section for score in gradeset['raw_scores']] assignments += [score.section for score in gradeset['raw_scores']]
else: else:
header += [x['label'] for x in gradeset['section_breakdown']] assignments += [x['label'] for x in gradeset['section_breakdown']]
header += assignments
datatable = {'header': header} datatable = {'header': header, 'assignments': assignments, 'students': enrolled_students}
data = [] data = []
for student in enrolled_students: for student in enrolled_students:
...@@ -518,9 +584,11 @@ def get_student_grade_summary_data(request, course, course_id, get_grades=True, ...@@ -518,9 +584,11 @@ def get_student_grade_summary_data(request, course, course_id, get_grades=True,
gradeset = grades.grade(student, request, course, keep_raw_scores=get_raw_scores) gradeset = grades.grade(student, request, course, keep_raw_scores=get_raw_scores)
# log.debug('student={0}, gradeset={1}'.format(student,gradeset)) # log.debug('student={0}, gradeset={1}'.format(student,gradeset))
if get_raw_scores: if get_raw_scores:
datarow += [score.earned for score in gradeset['raw_scores']] student_grades = [score.earned for score in gradeset['raw_scores']]
else: else:
datarow += [x['percent'] for x in gradeset['section_breakdown']] student_grades = [x['percent'] for x in gradeset['section_breakdown']]
datarow += student_grades
student.grades = student_grades # store in student object
data.append(datarow) data.append(datarow)
datatable['data'] = data datatable['data'] = data
......
...@@ -96,6 +96,42 @@ function goto( mode) ...@@ -96,6 +96,42 @@ function goto( mode)
<p> <p>
<input type="submit" name="action" value="Download CSV of answer distributions"> <input type="submit" name="action" value="Download CSV of answer distributions">
</p> </p>
<hr width="40%" style="align:left">
%if settings.MITX_FEATURES.get('REMOTE_GRADEBOOK_URL','') and instructor_access:
<%
rg = course.metadata.get('remote_gradebook',{})
%>
<h3>Export grades to remote gradebook</h3>
<p>The assignments defined for this course should match the ones
stored in the gradebook, for this to work properly!</p>
<ul>
<li>Gradebook name: <font color="green">${rg.get('name','None defined!')}</font>
<br/>
<br/>
<input type="submit" name="action" value="List assignments available in remote gradebook">
<input type="submit" name="action" value="List enrolled students matching remote gradebook">
<br/>
<br/>
</li>
<li><input type="submit" name="action" value="List assignments available for this course">
<br/>
<br/>
</li>
<li>Assignment name: <input type="text" name="assignment_name" size=40 >
<br/>
<br/>
<input type="submit" name="action" value="Display grades for assignment">
<input type="submit" name="action" value="Export grades for assignment to remote gradebook">
<input type="submit" name="action" value="Export CSV file of grades for assignment">
</li>
</ul>
%endif
%endif %endif
##----------------------------------------------------------------------------- ##-----------------------------------------------------------------------------
...@@ -187,7 +223,7 @@ function goto( mode) ...@@ -187,7 +223,7 @@ function goto( mode)
<p>Pull enrollment from remote gradebook</p> <p>Pull enrollment from remote gradebook</p>
<ul> <ul>
<li>Gradebook name: <input type="text" name="gradebook_name" size=40 value="${rg.get('name','')}"></li> <li>Gradebook name: <font color="green">${rg.get('name','None defined!')}</font>
<li>Section: <input type="text" name="gradebook_section" size=40 value="${rg.get('section','')}"></li> <li>Section: <input type="text" name="gradebook_section" size=40 value="${rg.get('section','')}"></li>
</ul> </ul>
<input type="submit" name="action" value="List sections available in remote gradebook"> <input type="submit" name="action" value="List sections available in remote gradebook">
......
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