diff --git a/lms/djangoapps/courseware/migrations/0010_add_courseware_coursetasklog.py b/lms/djangoapps/courseware/migrations/0010_add_courseware_coursetasklog.py
deleted file mode 100644
index ac933b140a..0000000000
--- a/lms/djangoapps/courseware/migrations/0010_add_courseware_coursetasklog.py
+++ /dev/null
@@ -1,164 +0,0 @@
-# -*- coding: utf-8 -*-
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-
-class Migration(SchemaMigration):
-
- def forwards(self, orm):
- # Adding model 'CourseTask'
- db.create_table('courseware_coursetask', (
- ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
- ('task_type', self.gf('django.db.models.fields.CharField')(max_length=50, db_index=True)),
- ('course_id', self.gf('django.db.models.fields.CharField')(max_length=255, db_index=True)),
- ('task_key', self.gf('django.db.models.fields.CharField')(max_length=255, db_index=True)),
- ('task_input', self.gf('django.db.models.fields.CharField')(max_length=255)),
- ('task_id', self.gf('django.db.models.fields.CharField')(max_length=255, db_index=True)),
- ('task_state', self.gf('django.db.models.fields.CharField')(max_length=50, null=True, db_index=True)),
- ('task_output', self.gf('django.db.models.fields.CharField')(max_length=1024, null=True)),
- ('requester', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])),
- ('created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, null=True, blank=True)),
- ('updated', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, blank=True)),
- ))
- db.send_create_signal('courseware', ['CourseTask'])
-
-
- def backwards(self, orm):
- # Deleting model 'CourseTask'
- db.delete_table('courseware_coursetask')
-
-
- models = {
- 'auth.group': {
- 'Meta': {'object_name': 'Group'},
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
- 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
- },
- 'auth.permission': {
- 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
- 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
- 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
- },
- 'auth.user': {
- 'Meta': {'object_name': 'User'},
- 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
- 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
- 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
- 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
- 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
- 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
- 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
- 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
- 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
- },
- 'contenttypes.contenttype': {
- 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
- 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
- },
- 'courseware.coursetask': {
- 'Meta': {'object_name': 'CourseTask'},
- 'course_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
- 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'requester': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
- 'task_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
- 'task_input': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
- 'task_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
- 'task_output': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True'}),
- 'task_state': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'db_index': 'True'}),
- 'task_type': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}),
- 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
- },
- 'courseware.offlinecomputedgrade': {
- 'Meta': {'unique_together': "(('user', 'course_id'),)", 'object_name': 'OfflineComputedGrade'},
- 'course_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
- 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'db_index': 'True', 'blank': 'True'}),
- 'gradeset': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}),
- 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
- },
- 'courseware.offlinecomputedgradelog': {
- 'Meta': {'ordering': "['-created']", 'object_name': 'OfflineComputedGradeLog'},
- 'course_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
- 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'db_index': 'True', 'blank': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'nstudents': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
- 'seconds': ('django.db.models.fields.IntegerField', [], {'default': '0'})
- },
- 'courseware.studentmodule': {
- 'Meta': {'unique_together': "(('student', 'module_state_key', 'course_id'),)", 'object_name': 'StudentModule'},
- 'course_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
- 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}),
- 'done': ('django.db.models.fields.CharField', [], {'default': "'na'", 'max_length': '8', 'db_index': 'True'}),
- 'grade': ('django.db.models.fields.FloatField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'max_grade': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}),
- 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}),
- 'module_state_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_column': "'module_id'", 'db_index': 'True'}),
- 'module_type': ('django.db.models.fields.CharField', [], {'default': "'problem'", 'max_length': '32', 'db_index': 'True'}),
- 'state': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
- 'student': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
- },
- 'courseware.studentmodulehistory': {
- 'Meta': {'object_name': 'StudentModuleHistory'},
- 'created': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
- 'grade': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'max_grade': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}),
- 'state': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
- 'student_module': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['courseware.StudentModule']"}),
- 'version': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'null': 'True', 'blank': 'True'})
- },
- 'courseware.xmodulecontentfield': {
- 'Meta': {'unique_together': "(('definition_id', 'field_name'),)", 'object_name': 'XModuleContentField'},
- 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}),
- 'definition_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
- 'field_name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}),
- 'value': ('django.db.models.fields.TextField', [], {'default': "'null'"})
- },
- 'courseware.xmodulesettingsfield': {
- 'Meta': {'unique_together': "(('usage_id', 'field_name'),)", 'object_name': 'XModuleSettingsField'},
- 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}),
- 'field_name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}),
- 'usage_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
- 'value': ('django.db.models.fields.TextField', [], {'default': "'null'"})
- },
- 'courseware.xmodulestudentinfofield': {
- 'Meta': {'unique_together': "(('student', 'field_name'),)", 'object_name': 'XModuleStudentInfoField'},
- 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}),
- 'field_name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}),
- 'student': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
- 'value': ('django.db.models.fields.TextField', [], {'default': "'null'"})
- },
- 'courseware.xmodulestudentprefsfield': {
- 'Meta': {'unique_together': "(('student', 'module_type', 'field_name'),)", 'object_name': 'XModuleStudentPrefsField'},
- 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}),
- 'field_name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}),
- 'module_type': ('django.db.models.fields.CharField', [], {'max_length': '64', 'db_index': 'True'}),
- 'student': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
- 'value': ('django.db.models.fields.TextField', [], {'default': "'null'"})
- }
- }
-
- complete_apps = ['courseware']
diff --git a/lms/djangoapps/courseware/tests/factories.py b/lms/djangoapps/courseware/tests/factories.py
index 75be060366..26df68ca7e 100644
--- a/lms/djangoapps/courseware/tests/factories.py
+++ b/lms/djangoapps/courseware/tests/factories.py
@@ -10,8 +10,6 @@ from student.tests.factories import CourseEnrollmentAllowedFactory as StudentCou
from student.tests.factories import RegistrationFactory as StudentRegistrationFactory
from courseware.models import StudentModule, XModuleContentField, XModuleSettingsField
from courseware.models import XModuleStudentInfoField, XModuleStudentPrefsField
-from courseware.models import CourseTask
-from celery.states import PENDING
from xmodule.modulestore import Location
from pytz import UTC
@@ -86,16 +84,3 @@ class StudentInfoFactory(DjangoModelFactory):
field_name = 'existing_field'
value = json.dumps('old_value')
student = SubFactory(UserFactory)
-
-
-class CourseTaskFactory(DjangoModelFactory):
- FACTORY_FOR = CourseTask
-
- task_type = 'rescore_problem'
- course_id = "MITx/999/Robot_Super_Course"
- task_input = json.dumps({})
- task_key = None
- task_id = None
- task_state = PENDING
- task_output = None
- requester = SubFactory(UserFactory)
diff --git a/lms/djangoapps/instructor/views.py b/lms/djangoapps/instructor/views.py
index d94d31700b..0f9c7e9593 100644
--- a/lms/djangoapps/instructor/views.py
+++ b/lms/djangoapps/instructor/views.py
@@ -25,7 +25,6 @@ from xmodule.modulestore.django import modulestore
from xmodule.modulestore.exceptions import ItemNotFoundError
from courseware import grades
-from instructor_task import api as task_api
from courseware.access import (has_access, get_access_group_name,
course_beta_test_group_name)
from courseware.courses import get_course_with_access
@@ -36,6 +35,13 @@ from django_comment_common.models import (Role,
FORUM_ROLE_COMMUNITY_TA)
from django_comment_client.utils import has_forum_access
from instructor.offline_gradecalc import student_grades, offline_grades_available
+from instructor_task.api import (get_running_instructor_tasks,
+ get_instructor_task_history,
+ submit_rescore_problem_for_all_students,
+ submit_rescore_problem_for_student,
+ submit_reset_problem_attempts_for_all_students,
+ submit_delete_problem_state_for_all_students)
+from instructor_task.views import get_task_completion_info
from mitxmako.shortcuts import render_to_response
from psychometrics import psychoanalyze
from student.models import CourseEnrollment, CourseEnrollmentAllowed
@@ -69,7 +75,7 @@ def instructor_dashboard(request, course_id):
msg = ''
problems = []
plots = []
- datatable = None
+ datatable = {}
# the instructor dashboard page is modal: grades, psychometrics, admin
# keep that state in request.session (defaults to grades mode)
@@ -250,8 +256,8 @@ def instructor_dashboard(request, course_id):
problem_urlname = request.POST.get('problem_for_all_students', '')
problem_url = get_module_url(problem_urlname)
try:
- course_task = task_api.submit_rescore_problem_for_all_students(request, course_id, problem_url)
- if course_task is None:
+ instructor_task = submit_rescore_problem_for_all_students(request, course_id, problem_url)
+ if instructor_task is None:
msg += 'Failed to create a background task for rescoring "{0}".'.format(problem_url)
else:
track_msg = 'rescore problem {problem} for all students in {course}'.format(problem=problem_url, course=course_id)
@@ -266,8 +272,8 @@ def instructor_dashboard(request, course_id):
problem_urlname = request.POST.get('problem_for_all_students', '')
problem_url = get_module_url(problem_urlname)
try:
- course_task = task_api.submit_reset_problem_attempts_for_all_students(request, course_id, problem_url)
- if course_task is None:
+ instructor_task = submit_reset_problem_attempts_for_all_students(request, course_id, problem_url)
+ if instructor_task is None:
msg += 'Failed to create a background task for resetting "{0}".'.format(problem_url)
else:
track_msg = 'reset problem {problem} for all students in {course}'.format(problem=problem_url, course=course_id)
@@ -288,18 +294,14 @@ def instructor_dashboard(request, course_id):
else:
problem_urlname = request.POST.get('problem_for_student', '')
problem_url = get_module_url(problem_urlname)
- message, task_datatable = get_background_task_table(course_id, problem_url, student)
+ message, datatable = get_background_task_table(course_id, problem_url, student)
msg += message
- if task_datatable is not None:
- datatable = task_datatable
elif "Show Background Task History" in action:
problem_urlname = request.POST.get('problem_for_all_students', '')
problem_url = get_module_url(problem_urlname)
- message, task_datatable = get_background_task_table(course_id, problem_url)
+ message, datatable = get_background_task_table(course_id, problem_url)
msg += message
- if task_datatable is not None:
- datatable = task_datatable
elif ("Reset student's attempts" in action or
"Delete student state for module" in action or
@@ -357,8 +359,8 @@ def instructor_dashboard(request, course_id):
else:
# "Rescore student's problem submission" case
try:
- course_task = task_api.submit_rescore_problem_for_student(request, course_id, module_state_key, student)
- if course_task is None:
+ instructor_task = submit_rescore_problem_for_student(request, course_id, module_state_key, student)
+ if instructor_task is None:
msg += 'Failed to create a background task for rescoring "{0}" for student {1}.'.format(module_state_key, unique_student_identifier)
else:
track_msg = 'rescore problem {problem} for student {student} in {course}'.format(problem=module_state_key, student=unique_student_identifier, course=course_id)
@@ -721,13 +723,14 @@ def instructor_dashboard(request, course_id):
msg += "
Grades from %s" % offline_grades_available(course_id)
# generate list of pending background tasks
- if settings.MITX_FEATURES.get('ENABLE_COURSE_BACKGROUND_TASKS'):
- course_tasks = task_api.get_running_course_tasks(course_id)
+ if settings.MITX_FEATURES.get('ENABLE_INSTRUCTOR_BACKGROUND_TASKS'):
+ instructor_tasks = get_running_instructor_tasks(course_id)
else:
- course_tasks = None
+ instructor_tasks = None
+ # display course stats only if there is no other table to display:
course_stats = None
- if datatable is None:
+ if not datatable:
course_stats = get_course_stats_table()
#----------------------------------------
# context for rendering
@@ -744,7 +747,7 @@ def instructor_dashboard(request, course_id):
'problems': problems, # psychometrics
'plots': plots, # psychometrics
'course_errors': modulestore().get_item_errors(course.location),
- 'course_tasks': course_tasks,
+ 'instructor_tasks': instructor_tasks,
'djangopid': os.getpid(),
'mitx_version': getattr(settings, 'MITX_VERSION_STRING', ''),
'offline_grade_log': offline_grades_available(course_id),
@@ -1299,8 +1302,8 @@ def get_background_task_table(course_id, problem_url, student=None):
Returns a tuple of (msg, datatable), where the msg is a possible error message,
and the datatable is the datatable to be used for display.
"""
- history_entries = task_api.get_instructor_task_history(course_id, problem_url, student)
- datatable = None
+ history_entries = get_instructor_task_history(course_id, problem_url, student)
+ datatable = {}
msg = ""
# first check to see if there is any history at all
# (note that we don't have to check that the arguments are valid; it
@@ -1312,7 +1315,6 @@ def get_background_task_table(course_id, problem_url, student=None):
else:
msg += 'Failed to find any background tasks for course "{course}" and module "{problem}".'.format(course=course_id, problem=problem_url)
else:
- datatable = {}
datatable['header'] = ["Task Type",
"Task Id",
"Requester",
@@ -1323,23 +1325,23 @@ def get_background_task_table(course_id, problem_url, student=None):
"Task Output"]
datatable['data'] = []
- for course_task in history_entries:
+ for instructor_task in history_entries:
# get duration info, if known:
duration_ms = 'unknown'
- if hasattr(course_task, 'task_output'):
- task_output = json.loads(course_task.task_output)
+ if hasattr(instructor_task, 'task_output'):
+ task_output = json.loads(instructor_task.task_output)
if 'duration_ms' in task_output:
duration_ms = task_output['duration_ms']
# get progress status message:
- success, task_message = task_submit.get_task_completion_info(course_task)
+ success, task_message = get_task_completion_info(instructor_task)
status = "Complete" if success else "Incomplete"
# generate row for this task:
- row = [str(course_task.task_type),
- str(course_task.task_id),
- str(course_task.requester),
- course_task.created.isoformat(' '),
+ row = [str(instructor_task.task_type),
+ str(instructor_task.task_id),
+ str(instructor_task.requester),
+ instructor_task.created.isoformat(' '),
duration_ms,
- str(course_task.task_state),
+ str(instructor_task.task_state),
status,
task_message]
datatable['data'].append(row)
diff --git a/lms/djangoapps/instructor_task/tests/test_tasks.py b/lms/djangoapps/instructor_task/tests/test_integration.py
similarity index 89%
rename from lms/djangoapps/instructor_task/tests/test_tasks.py
rename to lms/djangoapps/instructor_task/tests/test_integration.py
index b3552f0239..704cc265a8 100644
--- a/lms/djangoapps/instructor_task/tests/test_tasks.py
+++ b/lms/djangoapps/instructor_task/tests/test_integration.py
@@ -1,5 +1,9 @@
"""
-Test for LMS courseware background tasks
+Integration Test for LMS instructor-initiated background tasks
+
+Runs tasks on answers to course problems to validate that code
+paths actually work.
+
"""
import logging
import json
@@ -197,22 +201,8 @@ class TestRescoringBase(LoginEnrollmentTestCase, ModuleStoreTestCase):
TestRescoringBase.problem_location(problem_url_name),
student)
- def _create_course_task(self, task_state="QUEUED", task_input=None, student=None):
- """Creates a InstructorTask entry for testing."""
- task_id = str(uuid4())
- task_key = "dummy value"
- course_task = InstructorTaskFactory.create(requester=self.instructor,
- task_input=json.dumps(task_input),
- task_key=task_key,
- task_id=task_id,
- task_state=task_state)
- return course_task
-
def rescore_all_student_answers(self, instructor, problem_url_name):
"""Runs the task to rescore the current problem"""
-#TODO: fix this...
-# task_input = {'problem_url': TestRescoringBase.problem_location(problem_url_name)}
-# rescore_problem(entry_id, self.course_id, task_input, xmodule_instance_args)
return submit_rescore_problem_for_all_students(self.create_task_request(instructor), self.course.id,
TestRescoringBase.problem_location(problem_url_name))
@@ -244,6 +234,13 @@ class TestRescoringBase(LoginEnrollmentTestCase, ModuleStoreTestCase):
self.assertGreater(len(state['correct_map']), 0)
self.assertGreater(len(state['student_answers']), 0)
+ def get_task_status(self, task_id):
+ mock_request = Mock()
+ mock_request.REQUEST = {'task_id': task_id}
+ response = instructor_task_status(mock_request)
+ status = json.loads(response.content)
+ return status
+
class TestRescoring(TestRescoringBase):
"""Test rescoring problems in a background task."""
@@ -306,24 +303,21 @@ class TestRescoring(TestRescoringBase):
expected_message = "bad things happened"
with patch('capa.capa_problem.LoncapaProblem.rescore_existing_answers') as mock_rescore:
mock_rescore.side_effect = ZeroDivisionError(expected_message)
- course_task = self.submit_rescore_all_student_answers('instructor', problem_url_name)
+ instructor_task = self.submit_rescore_all_student_answers('instructor', problem_url_name)
- # check task_log returned
- self.assertEqual(course_task.task_state, 'FAILURE')
- self.assertEqual(course_task.requester.username, 'instructor')
- self.assertEqual(course_task.task_type, 'rescore_problem')
- task_input = json.loads(course_task.task_input)
+ # check instructor_task returned
+ self.assertEqual(instructor_task.task_state, 'FAILURE')
+ self.assertEqual(instructor_task.requester.username, 'instructor')
+ self.assertEqual(instructor_task.task_type, 'rescore_problem')
+ task_input = json.loads(instructor_task.task_input)
self.assertFalse('student' in task_input)
self.assertEqual(task_input['problem_url'], TestRescoring.problem_location(problem_url_name))
- status = json.loads(course_task.task_output)
+ status = json.loads(instructor_task.task_output)
self.assertEqual(status['exception'], 'ZeroDivisionError')
self.assertEqual(status['message'], expected_message)
# check status returned:
- mock_request = Mock()
- mock_request.REQUEST = {'task_id': course_task.task_id}
- response = instructor_task_status(mock_request)
- status = json.loads(response.content)
+ status = self.get_task_status(instructor_task.task_id)
self.assertEqual(status['message'], expected_message)
def test_rescoring_non_problem(self):
@@ -364,16 +358,13 @@ class TestRescoring(TestRescoringBase):
mock_send_to_queue.return_value = (0, "Successfully queued")
self.submit_student_answer('u1', problem_url_name, ["answer1", "answer2"])
- course_task = self.submit_rescore_all_student_answers('instructor', problem_url_name)
- self.assertEqual(course_task.task_state, FAILURE)
- status = json.loads(course_task.task_output)
+ instructor_task = self.submit_rescore_all_student_answers('instructor', problem_url_name)
+ self.assertEqual(instructor_task.task_state, FAILURE)
+ status = json.loads(instructor_task.task_output)
self.assertEqual(status['exception'], 'NotImplementedError')
self.assertEqual(status['message'], "Problem's definition does not support rescoring")
- mock_request = Mock()
- mock_request.REQUEST = {'task_id': course_task.task_id}
- response = instructor_task_status(mock_request)
- status = json.loads(response.content)
+ status = self.get_task_status(instructor_task.task_id)
self.assertEqual(status['message'], "Problem's definition does not support rescoring")
def define_randomized_custom_response_problem(self, problem_url_name, redefine=False):
@@ -517,31 +508,28 @@ class TestResetAttempts(TestRescoringBase):
expected_message = "bad things happened"
with patch('courseware.models.StudentModule.save') as mock_save:
mock_save.side_effect = ZeroDivisionError(expected_message)
- course_task = self.reset_problem_attempts('instructor', problem_url_name)
+ instructor_task = self.reset_problem_attempts('instructor', problem_url_name)
- # check task_log returned
- self.assertEqual(course_task.task_state, FAILURE)
- self.assertEqual(course_task.requester.username, 'instructor')
- self.assertEqual(course_task.task_type, 'reset_problem_attempts')
- task_input = json.loads(course_task.task_input)
+ # check instructor_task returned
+ self.assertEqual(instructor_task.task_state, FAILURE)
+ self.assertEqual(instructor_task.requester.username, 'instructor')
+ self.assertEqual(instructor_task.task_type, 'reset_problem_attempts')
+ task_input = json.loads(instructor_task.task_input)
self.assertFalse('student' in task_input)
self.assertEqual(task_input['problem_url'], TestRescoring.problem_location(problem_url_name))
- status = json.loads(course_task.task_output)
+ status = json.loads(instructor_task.task_output)
self.assertEqual(status['exception'], 'ZeroDivisionError')
self.assertEqual(status['message'], expected_message)
# check status returned:
- mock_request = Mock()
- mock_request.REQUEST = {'task_id': course_task.task_id}
- response = instructor_task_status(mock_request)
- status = json.loads(response.content)
+ status = self.get_task_status(instructor_task.task_id)
self.assertEqual(status['message'], expected_message)
def test_reset_non_problem(self):
"""confirm that a non-problem can still be successfully reset"""
problem_url_name = self.problem_section.location.url()
- course_task = self.reset_problem_attempts('instructor', problem_url_name)
- self.assertEqual(course_task.task_state, SUCCESS)
+ instructor_task = self.reset_problem_attempts('instructor', problem_url_name)
+ self.assertEqual(instructor_task.task_state, SUCCESS)
def test_reset_nonexistent_problem(self):
"""confirm that a non-existent problem will not submit"""
@@ -595,31 +583,28 @@ class TestDeleteProblem(TestRescoringBase):
expected_message = "bad things happened"
with patch('courseware.models.StudentModule.delete') as mock_delete:
mock_delete.side_effect = ZeroDivisionError(expected_message)
- course_task = self.delete_problem_state('instructor', problem_url_name)
+ instructor_task = self.delete_problem_state('instructor', problem_url_name)
- # check task_log returned
- self.assertEqual(course_task.task_state, FAILURE)
- self.assertEqual(course_task.requester.username, 'instructor')
- self.assertEqual(course_task.task_type, 'delete_problem_state')
- task_input = json.loads(course_task.task_input)
+ # check instructor_task returned
+ self.assertEqual(instructor_task.task_state, FAILURE)
+ self.assertEqual(instructor_task.requester.username, 'instructor')
+ self.assertEqual(instructor_task.task_type, 'delete_problem_state')
+ task_input = json.loads(instructor_task.task_input)
self.assertFalse('student' in task_input)
self.assertEqual(task_input['problem_url'], TestRescoring.problem_location(problem_url_name))
- status = json.loads(course_task.task_output)
+ status = json.loads(instructor_task.task_output)
self.assertEqual(status['exception'], 'ZeroDivisionError')
self.assertEqual(status['message'], expected_message)
# check status returned:
- mock_request = Mock()
- mock_request.REQUEST = {'task_id': course_task.task_id}
- response = instructor_task_status(mock_request)
- status = json.loads(response.content)
+ status = self.get_task_status(instructor_task.task_id)
self.assertEqual(status['message'], expected_message)
def test_delete_non_problem(self):
"""confirm that a non-problem can still be successfully deleted"""
problem_url_name = self.problem_section.location.url()
- course_task = self.delete_problem_state('instructor', problem_url_name)
- self.assertEqual(course_task.task_state, SUCCESS)
+ instructor_task = self.delete_problem_state('instructor', problem_url_name)
+ self.assertEqual(instructor_task.task_state, SUCCESS)
def test_delete_nonexistent_module(self):
"""confirm that a non-existent module will not submit"""
diff --git a/lms/templates/courseware/instructor_dashboard.html b/lms/templates/courseware/instructor_dashboard.html
index 10b902b904..4c9b4200a2 100644
--- a/lms/templates/courseware/instructor_dashboard.html
+++ b/lms/templates/courseware/instructor_dashboard.html
@@ -10,13 +10,13 @@
-%if course_tasks is not None:
+%if instructor_tasks is not None:
@@ -302,7 +302,7 @@ function goto( mode)
%endif
- %if settings.MITX_FEATURES.get('ENABLE_COURSE_BACKGROUND_TASKS'):
+ %if settings.MITX_FEATURES.get('ENABLE_INSTRUCTOR_BACKGROUND_TASKS'):
Course-specific grade adjustment
@@ -713,13 +713,13 @@ function goto( mode)
%for hname in datatable['header']:
- | ${hname} |
+ ${hname | h} |
%endfor
%for row in datatable['data']:
%for value in row:
- | ${value} |
+ ${value | h} |
%endfor
%endfor
@@ -729,9 +729,9 @@ function goto( mode)
## Output tasks in progress
-%if course_tasks is not None and len(course_tasks) > 0:
+%if instructor_tasks is not None and len(instructor_tasks) > 0:
- Pending Course Tasks
+ Pending Instructor Tasks
@@ -744,16 +744,16 @@ function goto( mode)
| Duration (ms) |
Task Progress |
- %for tasknum, course_task in enumerate(course_tasks):
+ %for tasknum, instructor_task in enumerate(instructor_tasks):
- | ${course_task.task_type} |
- ${course_task.task_input} |
- ${course_task.task_id} |
- ${course_task.requester} |
- ${course_task.created} |
- ${course_task.task_state} |
+ ${instructor_task.task_type} |
+ ${instructor_task.task_input} |
+ ${instructor_task.task_id} |
+ ${instructor_task.requester} |
+ ${instructor_task.created} |
+ ${instructor_task.task_state} |
unknown |
unknown |
@@ -772,11 +772,11 @@ function goto( mode)
- ${course_stats['title']}
+ ${course_stats['title'] | h}
%for hname in course_stats['header']:
- | ${hname} |
+ ${hname | h} |
%endfor
%for row in course_stats['data']: