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 4f87ba6b81..e5f7fa8ce7 100644 --- a/lms/djangoapps/courseware/tests/test_module_render.py +++ b/lms/djangoapps/courseware/tests/test_module_render.py @@ -1767,17 +1767,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

${plot['title']}

-
-

${plot['info']}

-
-
- -
-
- %endfor - -%endif - -##----------------------------------------------------------------------------- -## always show msg - - ##----------------------------------------------------------------------------- %if modeflag.get('Admin'): % if course_errors is not UNDEFINED: