Commit b224f4e8 by Brian Wilson

move test_tasks to test_integration.

parent c48dabc3
...@@ -10,8 +10,6 @@ from student.tests.factories import CourseEnrollmentAllowedFactory as StudentCou ...@@ -10,8 +10,6 @@ from student.tests.factories import CourseEnrollmentAllowedFactory as StudentCou
from student.tests.factories import RegistrationFactory as StudentRegistrationFactory from student.tests.factories import RegistrationFactory as StudentRegistrationFactory
from courseware.models import StudentModule, XModuleContentField, XModuleSettingsField from courseware.models import StudentModule, XModuleContentField, XModuleSettingsField
from courseware.models import XModuleStudentInfoField, XModuleStudentPrefsField from courseware.models import XModuleStudentInfoField, XModuleStudentPrefsField
from courseware.models import CourseTask
from celery.states import PENDING
from xmodule.modulestore import Location from xmodule.modulestore import Location
from pytz import UTC from pytz import UTC
...@@ -86,16 +84,3 @@ class StudentInfoFactory(DjangoModelFactory): ...@@ -86,16 +84,3 @@ class StudentInfoFactory(DjangoModelFactory):
field_name = 'existing_field' field_name = 'existing_field'
value = json.dumps('old_value') value = json.dumps('old_value')
student = SubFactory(UserFactory) 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)
...@@ -25,7 +25,6 @@ from xmodule.modulestore.django import modulestore ...@@ -25,7 +25,6 @@ from xmodule.modulestore.django import modulestore
from xmodule.modulestore.exceptions import ItemNotFoundError from xmodule.modulestore.exceptions import ItemNotFoundError
from courseware import grades from courseware import grades
from instructor_task import api as task_api
from courseware.access import (has_access, get_access_group_name, from courseware.access import (has_access, get_access_group_name,
course_beta_test_group_name) course_beta_test_group_name)
from courseware.courses import get_course_with_access from courseware.courses import get_course_with_access
...@@ -36,6 +35,13 @@ from django_comment_common.models import (Role, ...@@ -36,6 +35,13 @@ from django_comment_common.models import (Role,
FORUM_ROLE_COMMUNITY_TA) FORUM_ROLE_COMMUNITY_TA)
from django_comment_client.utils import has_forum_access from django_comment_client.utils import has_forum_access
from instructor.offline_gradecalc import student_grades, offline_grades_available 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 mitxmako.shortcuts import render_to_response
from psychometrics import psychoanalyze from psychometrics import psychoanalyze
from student.models import CourseEnrollment, CourseEnrollmentAllowed from student.models import CourseEnrollment, CourseEnrollmentAllowed
...@@ -69,7 +75,7 @@ def instructor_dashboard(request, course_id): ...@@ -69,7 +75,7 @@ def instructor_dashboard(request, course_id):
msg = '' msg = ''
problems = [] problems = []
plots = [] plots = []
datatable = None datatable = {}
# the instructor dashboard page is modal: grades, psychometrics, admin # the instructor dashboard page is modal: grades, psychometrics, admin
# keep that state in request.session (defaults to grades mode) # keep that state in request.session (defaults to grades mode)
...@@ -250,8 +256,8 @@ def instructor_dashboard(request, course_id): ...@@ -250,8 +256,8 @@ def instructor_dashboard(request, course_id):
problem_urlname = request.POST.get('problem_for_all_students', '') problem_urlname = request.POST.get('problem_for_all_students', '')
problem_url = get_module_url(problem_urlname) problem_url = get_module_url(problem_urlname)
try: try:
course_task = task_api.submit_rescore_problem_for_all_students(request, course_id, problem_url) instructor_task = submit_rescore_problem_for_all_students(request, course_id, problem_url)
if course_task is None: if instructor_task is None:
msg += '<font color="red">Failed to create a background task for rescoring "{0}".</font>'.format(problem_url) msg += '<font color="red">Failed to create a background task for rescoring "{0}".</font>'.format(problem_url)
else: else:
track_msg = 'rescore problem {problem} for all students in {course}'.format(problem=problem_url, course=course_id) 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): ...@@ -266,8 +272,8 @@ def instructor_dashboard(request, course_id):
problem_urlname = request.POST.get('problem_for_all_students', '') problem_urlname = request.POST.get('problem_for_all_students', '')
problem_url = get_module_url(problem_urlname) problem_url = get_module_url(problem_urlname)
try: try:
course_task = task_api.submit_reset_problem_attempts_for_all_students(request, course_id, problem_url) instructor_task = submit_reset_problem_attempts_for_all_students(request, course_id, problem_url)
if course_task is None: if instructor_task is None:
msg += '<font color="red">Failed to create a background task for resetting "{0}".</font>'.format(problem_url) msg += '<font color="red">Failed to create a background task for resetting "{0}".</font>'.format(problem_url)
else: else:
track_msg = 'reset problem {problem} for all students in {course}'.format(problem=problem_url, course=course_id) 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): ...@@ -288,18 +294,14 @@ def instructor_dashboard(request, course_id):
else: else:
problem_urlname = request.POST.get('problem_for_student', '') problem_urlname = request.POST.get('problem_for_student', '')
problem_url = get_module_url(problem_urlname) 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 msg += message
if task_datatable is not None:
datatable = task_datatable
elif "Show Background Task History" in action: elif "Show Background Task History" in action:
problem_urlname = request.POST.get('problem_for_all_students', '') problem_urlname = request.POST.get('problem_for_all_students', '')
problem_url = get_module_url(problem_urlname) 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 msg += message
if task_datatable is not None:
datatable = task_datatable
elif ("Reset student's attempts" in action or elif ("Reset student's attempts" in action or
"Delete student state for module" in action or "Delete student state for module" in action or
...@@ -357,8 +359,8 @@ def instructor_dashboard(request, course_id): ...@@ -357,8 +359,8 @@ def instructor_dashboard(request, course_id):
else: else:
# "Rescore student's problem submission" case # "Rescore student's problem submission" case
try: try:
course_task = task_api.submit_rescore_problem_for_student(request, course_id, module_state_key, student) instructor_task = submit_rescore_problem_for_student(request, course_id, module_state_key, student)
if course_task is None: if instructor_task is None:
msg += '<font color="red">Failed to create a background task for rescoring "{0}" for student {1}.</font>'.format(module_state_key, unique_student_identifier) msg += '<font color="red">Failed to create a background task for rescoring "{0}" for student {1}.</font>'.format(module_state_key, unique_student_identifier)
else: else:
track_msg = 'rescore problem {problem} for student {student} in {course}'.format(problem=module_state_key, student=unique_student_identifier, course=course_id) 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): ...@@ -721,13 +723,14 @@ def instructor_dashboard(request, course_id):
msg += "<br/><font color='orange'>Grades from %s</font>" % offline_grades_available(course_id) msg += "<br/><font color='orange'>Grades from %s</font>" % offline_grades_available(course_id)
# generate list of pending background tasks # generate list of pending background tasks
if settings.MITX_FEATURES.get('ENABLE_COURSE_BACKGROUND_TASKS'): if settings.MITX_FEATURES.get('ENABLE_INSTRUCTOR_BACKGROUND_TASKS'):
course_tasks = task_api.get_running_course_tasks(course_id) instructor_tasks = get_running_instructor_tasks(course_id)
else: else:
course_tasks = None instructor_tasks = None
# display course stats only if there is no other table to display:
course_stats = None course_stats = None
if datatable is None: if not datatable:
course_stats = get_course_stats_table() course_stats = get_course_stats_table()
#---------------------------------------- #----------------------------------------
# context for rendering # context for rendering
...@@ -744,7 +747,7 @@ def instructor_dashboard(request, course_id): ...@@ -744,7 +747,7 @@ def instructor_dashboard(request, course_id):
'problems': problems, # psychometrics 'problems': problems, # psychometrics
'plots': plots, # psychometrics 'plots': plots, # psychometrics
'course_errors': modulestore().get_item_errors(course.location), 'course_errors': modulestore().get_item_errors(course.location),
'course_tasks': course_tasks, 'instructor_tasks': instructor_tasks,
'djangopid': os.getpid(), 'djangopid': os.getpid(),
'mitx_version': getattr(settings, 'MITX_VERSION_STRING', ''), 'mitx_version': getattr(settings, 'MITX_VERSION_STRING', ''),
'offline_grade_log': offline_grades_available(course_id), 'offline_grade_log': offline_grades_available(course_id),
...@@ -1299,8 +1302,8 @@ def get_background_task_table(course_id, problem_url, student=None): ...@@ -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, 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. and the datatable is the datatable to be used for display.
""" """
history_entries = task_api.get_instructor_task_history(course_id, problem_url, student) history_entries = get_instructor_task_history(course_id, problem_url, student)
datatable = None datatable = {}
msg = "" msg = ""
# first check to see if there is any history at all # 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 # (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): ...@@ -1312,7 +1315,6 @@ def get_background_task_table(course_id, problem_url, student=None):
else: else:
msg += '<font color="red">Failed to find any background tasks for course "{course}" and module "{problem}".</font>'.format(course=course_id, problem=problem_url) msg += '<font color="red">Failed to find any background tasks for course "{course}" and module "{problem}".</font>'.format(course=course_id, problem=problem_url)
else: else:
datatable = {}
datatable['header'] = ["Task Type", datatable['header'] = ["Task Type",
"Task Id", "Task Id",
"Requester", "Requester",
...@@ -1323,23 +1325,23 @@ def get_background_task_table(course_id, problem_url, student=None): ...@@ -1323,23 +1325,23 @@ def get_background_task_table(course_id, problem_url, student=None):
"Task Output"] "Task Output"]
datatable['data'] = [] datatable['data'] = []
for course_task in history_entries: for instructor_task in history_entries:
# get duration info, if known: # get duration info, if known:
duration_ms = 'unknown' duration_ms = 'unknown'
if hasattr(course_task, 'task_output'): if hasattr(instructor_task, 'task_output'):
task_output = json.loads(course_task.task_output) task_output = json.loads(instructor_task.task_output)
if 'duration_ms' in task_output: if 'duration_ms' in task_output:
duration_ms = task_output['duration_ms'] duration_ms = task_output['duration_ms']
# get progress status message: # 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" status = "Complete" if success else "Incomplete"
# generate row for this task: # generate row for this task:
row = [str(course_task.task_type), row = [str(instructor_task.task_type),
str(course_task.task_id), str(instructor_task.task_id),
str(course_task.requester), str(instructor_task.requester),
course_task.created.isoformat(' '), instructor_task.created.isoformat(' '),
duration_ms, duration_ms,
str(course_task.task_state), str(instructor_task.task_state),
status, status,
task_message] task_message]
datatable['data'].append(row) datatable['data'].append(row)
......
...@@ -10,13 +10,13 @@ ...@@ -10,13 +10,13 @@
<script type="text/javascript" src="${static.url('js/vendor/jquery-jvectormap-1.1.1/jquery-jvectormap-world-mill-en.js')}"></script> <script type="text/javascript" src="${static.url('js/vendor/jquery-jvectormap-1.1.1/jquery-jvectormap-world-mill-en.js')}"></script>
<script type="text/javascript" src="${static.url('js/course_groups/cohorts.js')}"></script> <script type="text/javascript" src="${static.url('js/course_groups/cohorts.js')}"></script>
%if course_tasks is not None: %if instructor_tasks is not None:
<script type="text/javascript"> <script type="text/javascript">
// Define a CourseTaskProgress object for updating a table on the instructor // Define an InstructorTaskProgress object for updating a table on the instructor
// dashboard that shows the current background tasks that are currently running // dashboard that shows the current background tasks that are currently running
// for the instructor's course. Any tasks that were running when the page is // for the instructor's course. Any tasks that were running when the page is
// first displayed are passed in as course_tasks, and populate the "Pending Course // first displayed are passed in as instructor_tasks, and populate the "Pending Instructor
// Task" table. The CourseTaskProgress is bound to this table, and periodically // Task" table. The InstructorTaskProgress is bound to this table, and periodically
// polls the LMS to see if any of the tasks has completed. Once a task is complete, // polls the LMS to see if any of the tasks has completed. Once a task is complete,
// it is not included in any further polling. // it is not included in any further polling.
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
this.CourseTaskProgress = (function() { this.InstructorTaskProgress = (function() {
// Hardcode the refresh interval to be every five seconds. // Hardcode the refresh interval to be every five seconds.
// TODO: allow the refresh interval to be set. (And if it is disabled, // TODO: allow the refresh interval to be set. (And if it is disabled,
...@@ -34,7 +34,7 @@ ...@@ -34,7 +34,7 @@
// Hardcode the initial delay before the first refresh to two seconds: // Hardcode the initial delay before the first refresh to two seconds:
var initial_refresh_delay = 2000; var initial_refresh_delay = 2000;
function CourseTaskProgress(element) { function InstructorTaskProgress(element) {
this.update_progress = __bind(this.update_progress, this); this.update_progress = __bind(this.update_progress, this);
this.get_status = __bind(this.get_status, this); this.get_status = __bind(this.get_status, this);
this.element = element; this.element = element;
...@@ -42,14 +42,14 @@ ...@@ -42,14 +42,14 @@
if (window.queuePollerID) { if (window.queuePollerID) {
window.clearTimeout(window.queuePollerID); window.clearTimeout(window.queuePollerID);
} }
return window.queuePollerID = window.setTimeout(this.get_status, this.initial_refresh_delay); window.queuePollerID = window.setTimeout(this.get_status, this.initial_refresh_delay);
} }
CourseTaskProgress.prototype.$ = function(selector) { InstructorTaskProgress.prototype.$ = function(selector) {
return $(selector, this.element); return $(selector, this.element);
}; };
CourseTaskProgress.prototype.update_progress = function(response) { InstructorTaskProgress.prototype.update_progress = function(response) {
var _this = this; var _this = this;
// Response should be a dict with an entry for each requested task_id, // Response should be a dict with an entry for each requested task_id,
// with a "task-state" and "in_progress" key and optionally a "message" // with a "task-state" and "in_progress" key and optionally a "message"
...@@ -75,13 +75,13 @@ ...@@ -75,13 +75,13 @@
// if some entries are still incomplete, then repoll: // if some entries are still incomplete, then repoll:
if (something_in_progress) { if (something_in_progress) {
return window.queuePollerID = window.setTimeout(_this.get_status, _this.refresh_interval); window.queuePollerID = window.setTimeout(_this.get_status, _this.refresh_interval);
} else { } else {
delete window.queuePollerID; delete window.queuePollerID;
} }
} }
CourseTaskProgress.prototype.get_status = function() { InstructorTaskProgress.prototype.get_status = function() {
var _this = this; var _this = this;
var task_ids = []; var task_ids = [];
...@@ -99,20 +99,20 @@ ...@@ -99,20 +99,20 @@
// Note that the keyname here ends up with "[]" being appended // Note that the keyname here ends up with "[]" being appended
// in the POST parameter that shows up on the Django server. // in the POST parameter that shows up on the Django server.
// TODO: add error handler. // TODO: add error handler.
var ajax_url = '/course_task_log_status/'; var ajax_url = '/instructor_task_status/';
var data = {'task_ids': task_ids }; var data = {'task_ids': task_ids };
$.post(ajax_url, data).done(this.update_progress); $.post(ajax_url, data).done(this.update_progress);
}; };
return CourseTaskProgress; return InstructorTaskProgress;
})(); })();
}).call(this); }).call(this);
// once the page is rendered, create the progress object // once the page is rendered, create the progress object
var courseTaskProgress; var instructorTaskProgress;
$(document).ready(function() { $(document).ready(function() {
courseTaskProgress = new CourseTaskProgress($('#task-progress-wrapper')); instructorTaskProgress = new InstructorTaskProgress($('#task-progress-wrapper'));
}); });
</script> </script>
...@@ -302,7 +302,7 @@ function goto( mode) ...@@ -302,7 +302,7 @@ function goto( mode)
<hr width="40%" style="align:left"> <hr width="40%" style="align:left">
%endif %endif
%if settings.MITX_FEATURES.get('ENABLE_COURSE_BACKGROUND_TASKS'): %if settings.MITX_FEATURES.get('ENABLE_INSTRUCTOR_BACKGROUND_TASKS'):
<H2>Course-specific grade adjustment</h2> <H2>Course-specific grade adjustment</h2>
<p> <p>
...@@ -713,13 +713,13 @@ function goto( mode) ...@@ -713,13 +713,13 @@ function goto( mode)
<table class="stat_table"> <table class="stat_table">
<tr> <tr>
%for hname in datatable['header']: %for hname in datatable['header']:
<th>${hname}</th> <th>${hname | h}</th>
%endfor %endfor
</tr> </tr>
%for row in datatable['data']: %for row in datatable['data']:
<tr> <tr>
%for value in row: %for value in row:
<td>${value}</td> <td>${value | h}</td>
%endfor %endfor
</tr> </tr>
%endfor %endfor
...@@ -729,9 +729,9 @@ function goto( mode) ...@@ -729,9 +729,9 @@ function goto( mode)
## Output tasks in progress ## 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:
<hr width="100%"> <hr width="100%">
<h2>Pending Course Tasks</h2> <h2>Pending Instructor Tasks</h2>
<div id="task-progress-wrapper"> <div id="task-progress-wrapper">
<table class="stat_table"> <table class="stat_table">
<tr> <tr>
...@@ -744,16 +744,16 @@ function goto( mode) ...@@ -744,16 +744,16 @@ function goto( mode)
<th>Duration (ms)</th> <th>Duration (ms)</th>
<th>Task Progress</th> <th>Task Progress</th>
</tr> </tr>
%for tasknum, course_task in enumerate(course_tasks): %for tasknum, instructor_task in enumerate(instructor_tasks):
<tr id="task-progress-entry-${tasknum}" class="task-progress-entry" <tr id="task-progress-entry-${tasknum}" class="task-progress-entry"
data-task-id="${course_task.task_id}" data-task-id="${instructor_task.task_id}"
data-in-progress="true"> data-in-progress="true">
<td>${course_task.task_type}</td> <td>${instructor_task.task_type}</td>
<td>${course_task.task_input}</td> <td>${instructor_task.task_input}</td>
<td class="task-id">${course_task.task_id}</td> <td class="task-id">${instructor_task.task_id}</td>
<td>${course_task.requester}</td> <td>${instructor_task.requester}</td>
<td>${course_task.created}</td> <td>${instructor_task.created}</td>
<td class="task-state">${course_task.task_state}</td> <td class="task-state">${instructor_task.task_state}</td>
<td class="task-duration">unknown</td> <td class="task-duration">unknown</td>
<td class="task-progress">unknown</td> <td class="task-progress">unknown</td>
</tr> </tr>
...@@ -772,11 +772,11 @@ function goto( mode) ...@@ -772,11 +772,11 @@ function goto( mode)
<br/> <br/>
<p> <p>
<hr width="100%"> <hr width="100%">
<h2>${course_stats['title']}</h2> <h2>${course_stats['title'] | h}</h2>
<table class="stat_table"> <table class="stat_table">
<tr> <tr>
%for hname in course_stats['header']: %for hname in course_stats['header']:
<th>${hname}</th> <th>${hname | h}</th>
%endfor %endfor
</tr> </tr>
%for row in course_stats['data']: %for row in course_stats['data']:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment