add psychometrics - grade histograms, check time diffs, and IRT plots
This commit is contained in:
@@ -509,6 +509,9 @@ class CapaModule(XModule):
|
||||
event_info['success'] = success
|
||||
self.system.track_function('save_problem_check', event_info)
|
||||
|
||||
if hasattr(self.system,'psychometrics_handler'): # update PsychometricsData using callback
|
||||
self.system.psychometrics_handler(self.get_instance_state())
|
||||
|
||||
# render problem into HTML
|
||||
html = self.get_problem_html(encapsulate=False)
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ from capa.xqueue_interface import XQueueInterface
|
||||
from courseware.access import has_access
|
||||
from mitxmako.shortcuts import render_to_string
|
||||
from models import StudentModule, StudentModuleCache
|
||||
from psychometrics.psychoanalyze import make_psychometrics_data_update_handler
|
||||
from static_replace import replace_urls
|
||||
from xmodule.errortracker import exc_info_to_str
|
||||
from xmodule.exceptions import NotFoundError
|
||||
@@ -230,6 +231,9 @@ def _get_module(user, request, location, student_module_cache, course_id, positi
|
||||
# pass position specified in URL to module through ModuleSystem
|
||||
system.set('position', position)
|
||||
system.set('DEBUG', settings.DEBUG)
|
||||
if settings.MITX_FEATURES.get('ENABLE_PSYCHOMETRICS') and instance_module is not None:
|
||||
system.set('psychometrics_handler', # set callback for updating PsychometricsData
|
||||
make_psychometrics_data_update_handler(instance_module))
|
||||
|
||||
try:
|
||||
module = descriptor.xmodule_constructor(system)(instance_state, shared_state)
|
||||
|
||||
@@ -27,6 +27,7 @@ from django.views.decorators.cache import cache_control
|
||||
from courseware import grades
|
||||
from courseware.access import has_access, get_access_group_name
|
||||
from courseware.courses import (get_course_with_access, get_courses_by_university)
|
||||
from psychometrics import psychoanalyze
|
||||
from student.models import UserProfile
|
||||
|
||||
from student.models import UserTestGroup, CourseEnrollment
|
||||
@@ -51,7 +52,18 @@ def instructor_dashboard(request, course_id):
|
||||
instructor_access = has_access(request.user, course, 'instructor') # an instructor can manage staff lists
|
||||
|
||||
msg = ''
|
||||
# msg += ('POST=%s' % dict(request.POST)).replace('<','<')
|
||||
#msg += ('POST=%s' % dict(request.POST)).replace('<','<')
|
||||
|
||||
problems = []
|
||||
plots = []
|
||||
|
||||
# the instructor dashboard page is modal: grades, psychometrics, admin
|
||||
# keep that state in request.session (defaults to grades mode)
|
||||
idash_mode = request.POST.get('idash_mode','')
|
||||
if idash_mode:
|
||||
request.session['idash_mode'] = idash_mode
|
||||
else:
|
||||
idash_mode = request.session.get('idash_mode','Grades')
|
||||
|
||||
def escape(s):
|
||||
"""escape HTML special characters in string"""
|
||||
@@ -149,6 +161,9 @@ def instructor_dashboard(request, course_id):
|
||||
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))
|
||||
|
||||
#----------------------------------------
|
||||
# Admin
|
||||
|
||||
elif 'List course staff' in action:
|
||||
group = get_staff_group(course)
|
||||
msg += 'Staff group = %s' % group.name
|
||||
@@ -187,13 +202,28 @@ def instructor_dashboard(request, course_id):
|
||||
user.groups.remove(group)
|
||||
track.views.server_track(request, 'remove-staff %s' % user, {}, page='idashboard')
|
||||
|
||||
# For now, mostly a static page
|
||||
#----------------------------------------
|
||||
# psychometrics
|
||||
|
||||
elif action == 'Generate Histogram and IRT Plot':
|
||||
problem = request.POST['Problem']
|
||||
nmsg, plots = psychoanalyze.generate_plots_for_problem(problem)
|
||||
msg += nmsg
|
||||
|
||||
if idash_mode=='Psychometrics':
|
||||
problems = psychoanalyze.problems_with_psychometric_data(course_id)
|
||||
|
||||
#----------------------------------------
|
||||
# context for rendering
|
||||
context = {'course': course,
|
||||
'staff_access': True,
|
||||
'admin_access': request.user.is_staff,
|
||||
'instructor_access': instructor_access,
|
||||
'datatable': datatable,
|
||||
'msg': msg,
|
||||
'modeflag': {idash_mode: 'selectedmode'},
|
||||
'problems': problems, # psychometrics
|
||||
'plots': plots, # psychometrics
|
||||
'course_errors': modulestore().get_item_errors(course.location),
|
||||
}
|
||||
|
||||
|
||||
@@ -71,6 +71,8 @@ MITX_FEATURES = {
|
||||
'ENABLE_DISCUSSION' : False,
|
||||
'ENABLE_DISCUSSION_SERVICE': True,
|
||||
|
||||
'ENABLE_PSYCHOMETRICS': False, # real-time psychometrics (eg item response theory analysis in instructor dashboard)
|
||||
|
||||
'ENABLE_SQL_TRACKING_LOGS': False,
|
||||
'ENABLE_LMS_MIGRATION': False,
|
||||
'ENABLE_MANUAL_GIT_RELOAD': False,
|
||||
@@ -619,6 +621,7 @@ INSTALLED_APPS = (
|
||||
'util',
|
||||
'certificates',
|
||||
'instructor',
|
||||
'psychometrics',
|
||||
|
||||
#For the wiki
|
||||
'wiki', # The new django-wiki from benjaoming
|
||||
|
||||
@@ -20,6 +20,8 @@ MITX_FEATURES['SUBDOMAIN_COURSE_LISTINGS'] = False # Enable to test subdomains-
|
||||
MITX_FEATURES['SUBDOMAIN_BRANDING'] = True
|
||||
MITX_FEATURES['FORCE_UNIVERSITY_DOMAIN'] = None # show all university courses if in dev (ie don't use HTTP_HOST)
|
||||
MITX_FEATURES['ENABLE_MANUAL_GIT_RELOAD'] = True
|
||||
MITX_FEATURES['ENABLE_PSYCHOMETRICS'] = True # real-time psychometrics (eg item response theory analysis in instructor dashboard)
|
||||
|
||||
|
||||
WIKI_ENABLED = True
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
<%block name="headextra">
|
||||
<%static:css group='course'/>
|
||||
<script type="text/javascript" src="${static.url('js/vendor/flot/jquery.flot.js')}"></script>
|
||||
<script type="text/javascript" src="${static.url('js/vendor/flot/jquery.flot.axislabels.js')}"></script>
|
||||
</%block>
|
||||
|
||||
<%include file="/courseware/course_navigation.html" args="active_page='instructor'" />
|
||||
@@ -31,37 +33,89 @@ table.stat_table td {
|
||||
border-color: #666666;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
a.selectedmode { background-color: yellow; }
|
||||
|
||||
</style>
|
||||
|
||||
<script language="JavaScript" type="text/javascript">
|
||||
function goto( mode)
|
||||
{
|
||||
document.idashform.idash_mode.value = mode;
|
||||
document.idashform.submit() ;
|
||||
}
|
||||
</script>
|
||||
|
||||
<section class="container">
|
||||
<div class="instructor-dashboard-wrapper">
|
||||
|
||||
<section class="instructor-dashboard-content">
|
||||
<h1>Instructor Dashboard</h1>
|
||||
|
||||
<form method="POST">
|
||||
<input type="hidden" name="csrfmiddlewaretoken" value="${ csrf_token }">
|
||||
<h2>[ <a href="#" onclick="goto('Grades');" class="${modeflag.get('Grades')}">Grades</a> |
|
||||
%if settings.MITX_FEATURES.get('ENABLE_PSYCHOMETRICS'):
|
||||
<a href="#" onclick="goto('Psychometrics');" class="${modeflag.get('Psychometrics')}">Psychometrics</a> |
|
||||
%endif
|
||||
<a href="#" onclick="goto('Admin');" class="${modeflag.get('Admin')}">Admin</a> ]
|
||||
</h2>
|
||||
|
||||
<form name="idashform" method="POST">
|
||||
<input type="hidden" name="csrfmiddlewaretoken" value="${ csrf_token }">
|
||||
<input type="hidden" name="idash_mode" value="">
|
||||
|
||||
##-----------------------------------------------------------------------------
|
||||
%if modeflag.get('Grades'):
|
||||
<p>
|
||||
<a href="${reverse('gradebook', kwargs=dict(course_id=course.id))}">Gradebook</a>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<a href="${reverse('grade_summary', kwargs=dict(course_id=course.id))}">Grade summary</a>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<input type="submit" name="action" value="Dump list of enrolled students">
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<input type="submit" name="action" value="Dump Grades for all students in this course">
|
||||
<input type="submit" name="action" value="Download CSV of all student grades for this course">
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<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>
|
||||
|
||||
<p>
|
||||
<input type="submit" name="action" value="Download CSV of answer distributions">
|
||||
</p>
|
||||
%endif
|
||||
|
||||
%if instructor_access:
|
||||
##-----------------------------------------------------------------------------
|
||||
%if modeflag.get('Psychometrics'):
|
||||
|
||||
<p>Select a problem and an action:
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<select name="Problem">
|
||||
%for problem, count in sorted(problems.items(), key=lambda x: x[0]):
|
||||
<option value="${problem}">${problem} [${count}]</option>
|
||||
%endfor
|
||||
</select>
|
||||
</p>
|
||||
<p>
|
||||
<input type="submit" name="action" value="Generate Histogram and IRT Plot">
|
||||
</p>
|
||||
|
||||
<p></p>
|
||||
|
||||
%endif
|
||||
|
||||
##-----------------------------------------------------------------------------
|
||||
%if modeflag.get('Admin'):
|
||||
%if instructor_access:
|
||||
<hr width="40%" style="align:left">
|
||||
<p>
|
||||
<input type="submit" name="action" value="List course staff members">
|
||||
@@ -69,16 +123,20 @@ table.stat_table td {
|
||||
<input type="text" name="staffuser"> <input type="submit" name="action" value="Remove course staff">
|
||||
<input type="submit" name="action" value="Add course staff">
|
||||
<hr width="40%" style="align:left">
|
||||
%endif
|
||||
%endif
|
||||
|
||||
%if settings.MITX_FEATURES['ENABLE_MANUAL_GIT_RELOAD'] and admin_access:
|
||||
%if settings.MITX_FEATURES['ENABLE_MANUAL_GIT_RELOAD'] and admin_access:
|
||||
<p>
|
||||
<input type="submit" name="action" value="Reload course from XML files">
|
||||
<input type="submit" name="action" value="GIT pull and Reload course">
|
||||
%endif
|
||||
%endif
|
||||
|
||||
</form>
|
||||
|
||||
##-----------------------------------------------------------------------------
|
||||
%if modeflag.get('Psychometrics') is None:
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
<p>
|
||||
@@ -99,14 +157,45 @@ table.stat_table td {
|
||||
%endfor
|
||||
</table>
|
||||
</p>
|
||||
%endif
|
||||
|
||||
##-----------------------------------------------------------------------------
|
||||
%if modeflag.get('Psychometrics'):
|
||||
|
||||
%for plot in plots:
|
||||
<br/>
|
||||
<h3>${plot['title']}</h3>
|
||||
<br/>
|
||||
<p>${plot['info']}</p>
|
||||
<br/>
|
||||
<div id="plot_${plot['id']}" style="width:600px;height:300px;"></div>
|
||||
<script type="text/javascript">
|
||||
$(function () {
|
||||
${plot['data']}
|
||||
$.plot($("#plot_${plot['id']}"), ${plot['cmd']} );
|
||||
});
|
||||
</script>
|
||||
<br/>
|
||||
<br/>
|
||||
%endfor
|
||||
|
||||
%endif
|
||||
|
||||
##-----------------------------------------------------------------------------
|
||||
## always show msg
|
||||
|
||||
%if msg:
|
||||
<p>${msg}</p>
|
||||
%endif
|
||||
|
||||
% if course_errors is not UNDEFINED:
|
||||
##-----------------------------------------------------------------------------
|
||||
%if modeflag.get('Admin'):
|
||||
% if course_errors is not UNDEFINED:
|
||||
<h2>Course errors</h2>
|
||||
<div id="course-errors">
|
||||
%if not course_errors:
|
||||
None
|
||||
%else:
|
||||
<ul>
|
||||
% for (summary, err) in course_errors:
|
||||
<li>${summary | h}
|
||||
@@ -118,8 +207,10 @@ table.stat_table td {
|
||||
</li>
|
||||
% endfor
|
||||
</ul>
|
||||
%endif
|
||||
</div>
|
||||
% endif
|
||||
% endif
|
||||
%endif
|
||||
|
||||
</section>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user