From e932632b49a4c4413a2313738978594c5a9fb088 Mon Sep 17 00:00:00 2001
From: Sarina Canelake
Date: Fri, 4 Dec 2015 11:04:25 -0500
Subject: [PATCH] Remove psychometrics app
---
common/lib/xmodule/xmodule/capa_base.py | 7 -
common/test/db_cache/bok_choy_schema.sql | 14 -
lms/djangoapps/courseware/module_render.py | 6 -
.../courseware/tests/test_module_render.py | 11 -
lms/djangoapps/instructor/views/legacy.py | 17 +-
lms/djangoapps/psychometrics/__init__.py | 0
lms/djangoapps/psychometrics/admin.py | 8 -
.../psychometrics/management/__init__.py | 0
.../management/commands/__init__.py | 0
.../management/commands/init_psychometrics.py | 66 ----
.../psychometrics/migrations/0001_initial.py | 24 --
.../psychometrics/migrations/__init__.py | 0
lms/djangoapps/psychometrics/models.py | 46 ---
lms/djangoapps/psychometrics/psychoanalyze.py | 362 ------------------
lms/envs/common.py | 3 -
lms/envs/dev.py | 1 -
.../legacy_instructor_dashboard.html | 52 +--
17 files changed, 2 insertions(+), 615 deletions(-)
delete mode 100644 lms/djangoapps/psychometrics/__init__.py
delete mode 100644 lms/djangoapps/psychometrics/admin.py
delete mode 100644 lms/djangoapps/psychometrics/management/__init__.py
delete mode 100644 lms/djangoapps/psychometrics/management/commands/__init__.py
delete mode 100644 lms/djangoapps/psychometrics/management/commands/init_psychometrics.py
delete mode 100644 lms/djangoapps/psychometrics/migrations/0001_initial.py
delete mode 100644 lms/djangoapps/psychometrics/migrations/__init__.py
delete mode 100644 lms/djangoapps/psychometrics/models.py
delete mode 100644 lms/djangoapps/psychometrics/psychoanalyze.py
diff --git a/common/lib/xmodule/xmodule/capa_base.py b/common/lib/xmodule/xmodule/capa_base.py
index b5ead18bd7..a3fe7d8381 100644
--- a/common/lib/xmodule/xmodule/capa_base.py
+++ b/common/lib/xmodule/xmodule/capa_base.py
@@ -1125,9 +1125,6 @@ class CapaMixin(CapaFields):
self.attempts,
)
- if hasattr(self.runtime, 'psychometrics_handler'): # update PsychometricsData using callback
- self.runtime.psychometrics_handler(self.get_state_for_lcp())
-
# render problem into HTML
html = self.get_problem_html(encapsulate=False)
@@ -1375,10 +1372,6 @@ class CapaMixin(CapaFields):
event_info['attempts'] = self.attempts
self.track_function_unmask('problem_rescore', event_info)
- # psychometrics should be called on rescoring requests in the same way as check-problem
- if hasattr(self.runtime, 'psychometrics_handler'): # update PsychometricsData using callback
- self.runtime.psychometrics_handler(self.get_state_for_lcp())
-
return {'success': success}
def save_problem(self, data):
diff --git a/common/test/db_cache/bok_choy_schema.sql b/common/test/db_cache/bok_choy_schema.sql
index 2c83c1836c..9856d86aa3 100644
--- a/common/test/db_cache/bok_choy_schema.sql
+++ b/common/test/db_cache/bok_choy_schema.sql
@@ -2528,20 +2528,6 @@ CREATE TABLE `programs_programsapiconfig` (
CONSTRAINT `programs_programsa_changed_by_id_b7c3b49d5c0dcd3_fk_auth_user_id` FOREIGN KEY (`changed_by_id`) REFERENCES `auth_user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
-DROP TABLE IF EXISTS `psychometrics_psychometricdata`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `psychometrics_psychometricdata` (
- `id` int(11) NOT NULL AUTO_INCREMENT,
- `done` tinyint(1) NOT NULL,
- `attempts` int(11) NOT NULL,
- `checktimes` longtext,
- `studentmodule_id` int(11) NOT NULL,
- PRIMARY KEY (`id`),
- UNIQUE KEY `studentmodule_id` (`studentmodule_id`),
- CONSTRAINT `D758b867e6fa9161734bd9cb58b9a485` FOREIGN KEY (`studentmodule_id`) REFERENCES `courseware_studentmodule` (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `self_paced_selfpacedconfiguration`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
diff --git a/lms/djangoapps/courseware/module_render.py b/lms/djangoapps/courseware/module_render.py
index 2302ab5dba..24000b4b5b 100644
--- a/lms/djangoapps/courseware/module_render.py
+++ b/lms/djangoapps/courseware/module_render.py
@@ -56,7 +56,6 @@ from openedx.core.lib.xblock_utils import (
wrap_xblock,
request_token as xblock_request_token,
)
-from psychometrics.psychoanalyze import make_psychometrics_data_update_handler
from student.models import anonymous_id_for_user, user_by_anonymous_id
from student.roles import CourseBetaTesterRole
from xblock.core import XBlock
@@ -760,11 +759,6 @@ def get_module_system_for_user(user, student_data, # TODO # pylint: disable=to
position = None
system.set('position', position)
- if settings.FEATURES.get('ENABLE_PSYCHOMETRICS') and user.is_authenticated():
- system.set(
- 'psychometrics_handler', # set callback for updating PsychometricsData
- make_psychometrics_data_update_handler(course_id, user, descriptor.location)
- )
system.set(u'user_is_staff', user_is_staff)
system.set(u'user_is_admin', bool(has_access(user, u'staff', 'global')))
diff --git a/lms/djangoapps/courseware/tests/test_module_render.py b/lms/djangoapps/courseware/tests/test_module_render.py
index cd2075f92f..e6596c92fe 100644
--- a/lms/djangoapps/courseware/tests/test_module_render.py
+++ b/lms/djangoapps/courseware/tests/test_module_render.py
@@ -1766,17 +1766,6 @@ class TestRebindModule(TestSubmittingProblems):
self.assertEqual(module.scope_ids.user_id, user2.id)
self.assertEqual(module.descriptor.scope_ids.user_id, user2.id)
- @patch('courseware.module_render.make_psychometrics_data_update_handler')
- @patch.dict(settings.FEATURES, {'ENABLE_PSYCHOMETRICS': True})
- def test_psychometrics_anonymous(self, psycho_handler):
- """
- Make sure that noauth modules with anonymous users don't have
- the psychometrics callback bound.
- """
- module = self.get_module_for_user(self.anon_user)
- module.system.rebind_noauth_module_to_user(module, self.anon_user)
- self.assertFalse(psycho_handler.called)
-
@attr('shard_1')
@ddt.ddt
diff --git a/lms/djangoapps/instructor/views/legacy.py b/lms/djangoapps/instructor/views/legacy.py
index 6b30a6ee57..3d12f49759 100644
--- a/lms/djangoapps/instructor/views/legacy.py
+++ b/lms/djangoapps/instructor/views/legacy.py
@@ -48,7 +48,6 @@ from instructor_task.api import (
from instructor_task.views import get_task_completion_info
from edxmako.shortcuts import render_to_response, render_to_string
from class_dashboard import dashboard_data
-from psychometrics import psychoanalyze
from student.models import (
CourseEnrollment,
CourseEnrollmentAllowed,
@@ -97,7 +96,7 @@ def instructor_dashboard(request, course_id):
plots = []
datatable = {}
- # the instructor dashboard page is modal: grades, psychometrics, admin
+ # the instructor dashboard page is modal: grades, admin
# keep that state in request.session (defaults to grades mode)
idash_mode = request.POST.get('idash_mode', '')
idash_mode_key = u'idash_mode:{0}'.format(course_id)
@@ -319,18 +318,6 @@ def instructor_dashboard(request, course_id):
ret = _do_enroll_students(course, course_key, students, secure=secure, overload=overload)
datatable = ret['datatable']
- #----------------------------------------
- # psychometrics
-
- elif action == 'Generate Histogram and IRT Plot':
- problem = request.POST['Problem']
- nmsg, plots = psychoanalyze.generate_plots_for_problem(problem)
- msg += nmsg
- track.views.server_track(request, "psychometrics-histogram-generation", {"problem": unicode(problem)}, page="idashboard")
-
- if idash_mode == 'Psychometrics':
- problems = psychoanalyze.problems_with_psychometric_data(course_key)
-
#----------------------------------------
# analytics
def get_analytics_result(analytics_name):
@@ -435,8 +422,6 @@ def instructor_dashboard(request, course_id):
'show_email_tab': show_email_tab, # email
- 'problems': problems, # psychometrics
- 'plots': plots, # psychometrics
'course_errors': modulestore().get_course_errors(course.id),
'instructor_tasks': instructor_tasks,
'offline_grade_log': offline_grades_available(course_key),
diff --git a/lms/djangoapps/psychometrics/__init__.py b/lms/djangoapps/psychometrics/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/lms/djangoapps/psychometrics/admin.py b/lms/djangoapps/psychometrics/admin.py
deleted file mode 100644
index b7c04b5069..0000000000
--- a/lms/djangoapps/psychometrics/admin.py
+++ /dev/null
@@ -1,8 +0,0 @@
-'''
-django admin pages for courseware model
-'''
-
-from psychometrics.models import PsychometricData
-from django.contrib import admin
-
-admin.site.register(PsychometricData)
diff --git a/lms/djangoapps/psychometrics/management/__init__.py b/lms/djangoapps/psychometrics/management/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/lms/djangoapps/psychometrics/management/commands/__init__.py b/lms/djangoapps/psychometrics/management/commands/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/lms/djangoapps/psychometrics/management/commands/init_psychometrics.py b/lms/djangoapps/psychometrics/management/commands/init_psychometrics.py
deleted file mode 100644
index 6ef183bcb1..0000000000
--- a/lms/djangoapps/psychometrics/management/commands/init_psychometrics.py
+++ /dev/null
@@ -1,66 +0,0 @@
-#!/usr/bin/python
-#
-# generate pyschometrics data from tracking logs and student module data
-
-import json
-
-from courseware.models import StudentModule
-from track.models import TrackingLog
-from psychometrics.models import PsychometricData
-
-from django.conf import settings
-from django.core.management.base import BaseCommand
-
-#db = "ocwtutor" # for debugging
-#db = "default"
-
-db = getattr(settings, 'DATABASE_FOR_PSYCHOMETRICS', 'default')
-
-
-class Command(BaseCommand):
- help = "initialize PsychometricData tables from StudentModule instances (and tracking data, if in SQL)."
- help += "Note this is done for all courses for which StudentModule instances exist."
-
- def handle(self, *args, **options):
-
- # delete all pmd
-
- #PsychometricData.objects.all().delete()
- #PsychometricData.objects.using(db).all().delete()
-
- smset = StudentModule.objects.using(db).exclude(max_grade=None)
-
- for sm in smset:
- usage_key = sm.module_state_key
- if not usage_key.block_type == "problem":
- continue
- try:
- state = json.loads(sm.state)
- done = state['done']
- except:
- print "Oops, failed to eval state for %s (state=%s)" % (sm, sm.state)
- continue
-
- if done: # only keep if problem completed
- try:
- pmd = PsychometricData.objects.using(db).get(studentmodule=sm)
- except PsychometricData.DoesNotExist:
- pmd = PsychometricData(studentmodule=sm)
-
- pmd.done = done
- pmd.attempts = state['attempts']
-
- # get attempt times from tracking log
- uname = sm.student.username
- tset = TrackingLog.objects.using(db).filter(username=uname, event_type__contains='problem_check')
- tset = tset.filter(event_source='server')
- tset = tset.filter(event__contains="'%s'" % usage_key)
- checktimes = [x.dtcreated for x in tset]
- pmd.checktimes = checktimes
- if not len(checktimes) == pmd.attempts:
- print "Oops, mismatch in number of attempts and check times for %s" % pmd
-
- #print pmd
- pmd.save(using=db)
-
- print "%d PMD entries" % PsychometricData.objects.using(db).all().count()
diff --git a/lms/djangoapps/psychometrics/migrations/0001_initial.py b/lms/djangoapps/psychometrics/migrations/0001_initial.py
deleted file mode 100644
index 8cd81936d8..0000000000
--- a/lms/djangoapps/psychometrics/migrations/0001_initial.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('courseware', '0001_initial'),
- ]
-
- operations = [
- migrations.CreateModel(
- name='PsychometricData',
- fields=[
- ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
- ('done', models.BooleanField(default=False)),
- ('attempts', models.IntegerField(default=0)),
- ('checktimes', models.TextField(null=True, blank=True)),
- ('studentmodule', models.OneToOneField(to='courseware.StudentModule')),
- ],
- ),
- ]
diff --git a/lms/djangoapps/psychometrics/migrations/__init__.py b/lms/djangoapps/psychometrics/migrations/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/lms/djangoapps/psychometrics/models.py b/lms/djangoapps/psychometrics/models.py
deleted file mode 100644
index e8070ca907..0000000000
--- a/lms/djangoapps/psychometrics/models.py
+++ /dev/null
@@ -1,46 +0,0 @@
-#
-# db model for psychometrics data
-#
-# this data is collected in real time
-#
-
-from django.db import models
-from courseware.models import StudentModule
-
-
-class PsychometricData(models.Model):
- """
- This data is a table linking student, module, and module performance,
- including number of attempts, grade, max grade, and time of checks.
-
- Links to instances of StudentModule, but only those for capa problems.
-
- Note that StudentModule.module_state_key is a :class:`Location` instance.
-
- checktimes is extracted from tracking logs, or added by capa module via psychometrics callback.
- """
-
- class Meta(object):
- app_label = "psychometrics"
-
- studentmodule = models.OneToOneField(StudentModule, db_index=True) # contains student, module_state_key, course_id
-
- done = models.BooleanField(default=False)
- attempts = models.IntegerField(default=0) # extracted from studentmodule.state
- checktimes = models.TextField(null=True, blank=True) # internally stored as list of datetime objects
-
- # keep in mind
- # grade = studentmodule.grade
- # max_grade = studentmodule.max_grade
- # student = studentmodule.student
- # course_id = studentmodule.course_id
- # location = studentmodule.module_state_key
-
- def __unicode__(self):
- sm = self.studentmodule
- return "[PsychometricData] %s url=%s, grade=%s, max=%s, attempts=%s, ct=%s" % (sm.student,
- sm.module_state_key,
- sm.grade,
- sm.max_grade,
- self.attempts,
- self.checktimes)
diff --git a/lms/djangoapps/psychometrics/psychoanalyze.py b/lms/djangoapps/psychometrics/psychoanalyze.py
deleted file mode 100644
index 918edffa47..0000000000
--- a/lms/djangoapps/psychometrics/psychoanalyze.py
+++ /dev/null
@@ -1,362 +0,0 @@
-#
-# File: psychometrics/psychoanalyze.py
-#
-# generate pyschometrics plots from PsychometricData
-
-from __future__ import division
-
-import datetime
-import logging
-import json
-import math
-import numpy as np
-from opaque_keys.edx.locator import BlockUsageLocator
-from scipy.optimize import curve_fit
-
-from django.conf import settings
-from django.db.models import Sum, Max
-from psychometrics.models import PsychometricData
-from courseware.models import StudentModule
-from pytz import UTC
-
-log = logging.getLogger("edx.psychometrics")
-
-#db = "ocwtutor" # for debugging
-#db = "default"
-
-db = getattr(settings, 'DATABASE_FOR_PSYCHOMETRICS', 'default')
-
-#-----------------------------------------------------------------------------
-# fit functions
-
-
-def func_2pl(x, a, b):
- """
- 2-parameter logistic function
- """
- D = 1.7
- edax = np.exp(D * a * (x - b))
- return edax / (1 + edax)
-
-#-----------------------------------------------------------------------------
-# statistics class
-
-
-class StatVar(object):
- """
- Simple statistics on floating point numbers: avg, sdv, var, min, max
- """
- def __init__(self, unit=1):
- self.sum = 0
- self.sum2 = 0
- self.cnt = 0
- self.unit = unit
- self.min = None
- self.max = None
-
- def add(self, x):
- if x is None:
- return
- if self.min is None:
- self.min = x
- else:
- if x < self.min:
- self.min = x
- if self.max is None:
- self.max = x
- else:
- if x > self.max:
- self.max = x
- self.sum += x
- self.sum2 += x ** 2
- self.cnt += 1
-
- def avg(self):
- if self.cnt is None:
- return 0
- return self.sum / 1.0 / self.cnt / self.unit
-
- def var(self):
- if self.cnt is None:
- return 0
- return (self.sum2 / 1.0 / self.cnt / (self.unit ** 2)) - (self.avg() ** 2)
-
- def sdv(self):
- v = self.var()
- if v > 0:
- return math.sqrt(v)
- else:
- return 0
-
- def __str__(self):
- return 'cnt=%d, avg=%f, sdv=%f' % (self.cnt, self.avg(), self.sdv())
-
- def __add__(self, x):
- self.add(x)
- return self
-
-#-----------------------------------------------------------------------------
-# histogram generator
-
-
-def make_histogram(ydata, bins=None):
- '''
- Generate histogram of ydata using bins provided, or by default bins
- from 0 to 100 by 10. bins should be ordered in increasing order.
-
- returns dict with keys being bins, and values being counts.
- special: hist['bins'] = bins
- '''
- if bins is None:
- bins = range(0, 100, 10)
-
- nbins = len(bins)
- hist = dict(zip(bins, [0] * nbins))
- for y in ydata:
- for b in bins[::-1]: # in reverse order
- if y > b:
- hist[b] += 1
- break
- # hist['bins'] = bins
- return hist
-
-#-----------------------------------------------------------------------------
-
-
-def problems_with_psychometric_data(course_id):
- '''
- Return dict of {problems (location urls): count} for which psychometric data is available.
- Does this for a given course_id.
- '''
- pmdset = PsychometricData.objects.using(db).filter(studentmodule__course_id=course_id)
- plist = [p['studentmodule__module_state_key'] for p in pmdset.values('studentmodule__module_state_key').distinct()]
- problems = dict(
- (
- p,
- pmdset.filter(
- studentmodule__module_state_key=BlockUsageLocator.from_string(p)
- ).count()
- ) for p in plist
- )
-
- return problems
-
-#-----------------------------------------------------------------------------
-
-
-def generate_plots_for_problem(problem):
-
- pmdset = PsychometricData.objects.using(db).filter(
- studentmodule__module_state_key=BlockUsageLocator.from_string(problem)
- )
- nstudents = pmdset.count()
- msg = ""
- plots = []
-
- if nstudents < 2:
- msg += "%s nstudents=%d --> skipping, too few" % (problem, nstudents)
- return msg, plots
-
- max_grade = pmdset[0].studentmodule.max_grade
-
- agdat = pmdset.aggregate(Sum('attempts'), Max('attempts'))
- max_attempts = agdat['attempts__max']
- total_attempts = agdat['attempts__sum'] # not used yet
-
- msg += "max attempts = %d" % max_attempts
-
- xdat = range(1, max_attempts + 1)
- dataset = {'xdat': xdat}
-
- # compute grade statistics
- grades = [pmd.studentmodule.grade for pmd in pmdset]
- gsv = StatVar()
- for g in grades:
- gsv += g
- msg += "
Grade distribution: %s
" % gsv
-
- # generate grade histogram
- ghist = []
-
- axisopts = """{
- xaxes: [{
- axisLabel: 'Grade'
- }],
- yaxes: [{
- position: 'left',
- axisLabel: 'Count'
- }]
- }"""
-
- if gsv.max > max_grade:
- msg += "
Something is wrong: max_grade=%s, but max(grades)=%s
" % (max_grade, gsv.max)
- max_grade = gsv.max
-
- if max_grade > 1:
- ghist = make_histogram(grades, np.linspace(0, max_grade, max_grade + 1))
- ghist_json = json.dumps(ghist.items())
-
- plot = {'title': "Grade histogram for %s" % problem,
- 'id': 'histogram',
- 'info': '',
- 'data': "var dhist = %s;\n" % ghist_json,
- 'cmd': '[ {data: dhist, bars: { show: true, align: "center" }} ], %s' % axisopts,
- }
- plots.append(plot)
- else:
- msg += "
Not generating histogram: max_grade=%s" % max_grade
-
- # histogram of time differences between checks
- # Warning: this is inefficient - doesn't scale to large numbers of students
- dtset = [] # time differences in minutes
- dtsv = StatVar()
- for pmd in pmdset:
- try:
- checktimes = eval(pmd.checktimes) # update log of attempt timestamps
- except:
- continue
- if len(checktimes) < 2:
- continue
- ct0 = checktimes[0]
- for ct in checktimes[1:]:
- dt = (ct - ct0).total_seconds() / 60.0
- if dt < 20: # ignore if dt too long
- dtset.append(dt)
- dtsv += dt
- ct0 = ct
- if dtsv.cnt > 2:
- msg += "
Time differences between checks: %s
" % dtsv
- bins = np.linspace(0, 1.5 * dtsv.sdv(), 30)
- dbar = bins[1] - bins[0]
- thist = make_histogram(dtset, bins)
- thist_json = json.dumps(sorted(thist.items(), key=lambda(x): x[0]))
-
- axisopts = """{ xaxes: [{ axisLabel: 'Time (min)'}], yaxes: [{position: 'left',axisLabel: 'Count'}]}"""
-
- plot = {'title': "Histogram of time differences between checks",
- 'id': 'thistogram',
- 'info': '',
- 'data': "var thist = %s;\n" % thist_json,
- 'cmd': '[ {data: thist, bars: { show: true, align: "center", barWidth:%f }} ], %s' % (dbar, axisopts),
- }
- plots.append(plot)
-
- # one IRT plot curve for each grade received (TODO: this assumes integer grades)
- for grade in range(1, int(max_grade) + 1):
- yset = {}
- gset = pmdset.filter(studentmodule__grade=grade)
- ngset = gset.count()
- if ngset == 0:
- continue
- ydat = []
- ylast = 0
- for x in xdat:
- y = gset.filter(attempts=x).count() / ngset
- ydat.append(y + ylast)
- ylast = y + ylast
- yset['ydat'] = ydat
-
- if len(ydat) > 3: # try to fit to logistic function if enough data points
- try:
- cfp = curve_fit(func_2pl, xdat, ydat, [1.0, max_attempts / 2.0])
- yset['fitparam'] = cfp
- yset['fitpts'] = func_2pl(np.array(xdat), *cfp[0])
- yset['fiterr'] = [yd - yf for (yd, yf) in zip(ydat, yset['fitpts'])]
- fitx = np.linspace(xdat[0], xdat[-1], 100)
- yset['fitx'] = fitx
- yset['fity'] = func_2pl(np.array(fitx), *cfp[0])
- except Exception as err:
- log.debug('Error in psychoanalyze curve fitting: %s', err)
-
- dataset['grade_%d' % grade] = yset
-
- axisopts = """{
- xaxes: [{
- axisLabel: 'Number of Attempts'
- }],
- yaxes: [{
- max:1.0,
- position: 'left',
- axisLabel: 'Probability of correctness'
- }]
- }"""
-
- # generate points for flot plot
- for grade in range(1, int(max_grade) + 1):
- jsdata = ""
- jsplots = []
- gkey = 'grade_%d' % grade
- if gkey in dataset:
- yset = dataset[gkey]
- jsdata += "var d%d = %s;\n" % (grade, json.dumps(zip(xdat, yset['ydat'])))
- jsplots.append('{ data: d%d, lines: { show: false }, points: { show: true}, color: "red" }' % grade)
- if 'fitpts' in yset:
- jsdata += 'var fit = %s;\n' % (json.dumps(zip(yset['fitx'], yset['fity'])))
- jsplots.append('{ data: fit, lines: { show: true }, color: "blue" }')
- (a, b) = yset['fitparam'][0]
- irtinfo = "(2PL: D=1.7, a=%6.3f, b=%6.3f)" % (a, b)
- else:
- irtinfo = ""
-
- plots.append({'title': 'IRT Plot for grade=%s %s' % (grade, irtinfo),
- 'id': "irt%s" % grade,
- 'info': '',
- 'data': jsdata,
- 'cmd': '[%s], %s' % (','.join(jsplots), axisopts),
- })
-
- #log.debug('plots = %s' % plots)
- return msg, plots
-
-#-----------------------------------------------------------------------------
-
-
-def make_psychometrics_data_update_handler(course_id, user, module_state_key):
- """
- Construct and return a procedure which may be called to update
- the PsychometricData instance for the given StudentModule instance.
- """
- sm, status = StudentModule.objects.get_or_create(
- course_id=course_id,
- student=user,
- module_state_key=module_state_key,
- defaults={'state': '{}', 'module_type': 'problem'},
- )
-
- try:
- pmd = PsychometricData.objects.using(db).get(studentmodule=sm)
- except PsychometricData.DoesNotExist:
- pmd = PsychometricData(studentmodule=sm)
-
- def psychometrics_data_update_handler(state):
- """
- This function may be called each time a problem is successfully checked
- (eg on save_problem_check events in capa_module).
-
- state = instance state (a nice, uniform way to interface - for more future psychometric feature extraction)
- """
- try:
- state = json.loads(sm.state)
- done = state['done']
- except:
- log.exception("Oops, failed to eval state for %s (state=%s)", sm, sm.state)
- return
-
- pmd.done = done
- try:
- pmd.attempts = state.get('attempts', 0)
- except:
- log.exception("no attempts for %s (state=%s)", sm, sm.state)
-
- try:
- checktimes = eval(pmd.checktimes) # update log of attempt timestamps
- except:
- checktimes = []
- checktimes.append(datetime.datetime.now(UTC))
- pmd.checktimes = checktimes
- try:
- pmd.save()
- except:
- log.exception("Error in updating psychometrics data for %s", sm)
-
- return psychometrics_data_update_handler
diff --git a/lms/envs/common.py b/lms/envs/common.py
index 2e5f9c9173..66f47211f7 100644
--- a/lms/envs/common.py
+++ b/lms/envs/common.py
@@ -116,8 +116,6 @@ FEATURES = {
# in their emails, and they will have no way to resubscribe.
'ENABLE_DISCUSSION_EMAIL_DIGEST': False,
- 'ENABLE_PSYCHOMETRICS': False, # real-time psychometrics (eg item response theory analysis in instructor dashboard)
-
'ENABLE_DJANGO_ADMIN_SITE': True, # set true to enable django's admin site, even on prod (e.g. for course ops)
'ENABLE_SQL_TRACKING_LOGS': False,
'ENABLE_LMS_MIGRATION': False,
@@ -1833,7 +1831,6 @@ INSTALLED_APPS = (
'instructor',
'instructor_task',
'open_ended_grading',
- 'psychometrics',
'licenses',
'openedx.core.djangoapps.course_groups',
'bulk_email',
diff --git a/lms/envs/dev.py b/lms/envs/dev.py
index 2b5f4229be..1c1f7bcc5c 100644
--- a/lms/envs/dev.py
+++ b/lms/envs/dev.py
@@ -24,7 +24,6 @@ FEATURES['SUBDOMAIN_COURSE_LISTINGS'] = False # Enable to test subdomains--othe
FEATURES['SUBDOMAIN_BRANDING'] = True
FEATURES['FORCE_UNIVERSITY_DOMAIN'] = None # show all university courses if in dev (ie don't use HTTP_HOST)
FEATURES['ENABLE_MANUAL_GIT_RELOAD'] = True
-FEATURES['ENABLE_PSYCHOMETRICS'] = False # real-time psychometrics (eg item response theory analysis in instructor dashboard)
FEATURES['ENABLE_SERVICE_STATUS'] = True
FEATURES['ENABLE_INSTRUCTOR_EMAIL'] = True # Enable email for all Studio courses
FEATURES['REQUIRE_COURSE_EMAIL_AUTH'] = False # Give all courses email (don't require django-admin perms)
diff --git a/lms/templates/courseware/legacy_instructor_dashboard.html b/lms/templates/courseware/legacy_instructor_dashboard.html
index 28724da62d..2ec867f363 100644
--- a/lms/templates/courseware/legacy_instructor_dashboard.html
+++ b/lms/templates/courseware/legacy_instructor_dashboard.html
@@ -148,9 +148,6 @@ function goto( mode)
%endif
[ Grades |
- %if settings.FEATURES.get('ENABLE_PSYCHOMETRICS'):
- ${_("Psychometrics")} |
- %endif
${_("Admin")} |
${_("Forum Admin")} |
${_("Enrollment")} |
@@ -267,27 +264,6 @@ function goto( mode)
%endif
-##-----------------------------------------------------------------------------
-%if modeflag.get('Psychometrics'):
-
-
${_("Select a problem and an action:")}
-
-
-
-
-
-
-
-
-
-
-
-%endif
-
##-----------------------------------------------------------------------------
%if modeflag.get('Admin'):
@@ -398,7 +374,7 @@ function goto( mode)
##-----------------------------------------------------------------------------
-%if datatable and modeflag.get('Psychometrics') is None:
+%if datatable:
@@ -491,32 +467,6 @@ function goto( mode)
%endif
-##-----------------------------------------------------------------------------
-%if modeflag.get('Psychometrics'):
-
- %for plot in plots:
-
- ${plot['title']}
-
- ${plot['info']}
-
-
-
-
-
- %endfor
-
-%endif
-
-##-----------------------------------------------------------------------------
-## always show msg
-
-
##-----------------------------------------------------------------------------
%if modeflag.get('Admin'):
% if course_errors is not UNDEFINED: