Commit a67674fe by Brian Wilson

set task_id on LMS side.

parent b224f4e8
...@@ -263,50 +263,3 @@ class OfflineComputedGradeLog(models.Model): ...@@ -263,50 +263,3 @@ class OfflineComputedGradeLog(models.Model):
def __unicode__(self): def __unicode__(self):
return "[OCGLog] %s: %s" % (self.course_id, self.created) return "[OCGLog] %s: %s" % (self.course_id, self.created)
class CourseTask(models.Model):
"""
Stores information about background tasks that have been submitted to
perform course-specific work.
Examples include grading and rescoring.
`task_type` identifies the kind of task being performed, e.g. rescoring.
`course_id` uses the course run's unique id to identify the course.
`task_input` stores input arguments as JSON-serialized dict, for reporting purposes.
Examples include url of problem being rescored, id of student if only one student being rescored.
`task_key` stores relevant input arguments encoded into key value for testing to see
if the task is already running (together with task_type and course_id).
`task_id` stores the id used by celery for the background task.
`task_state` stores the last known state of the celery task
`task_output` stores the output of the celery task.
Format is a JSON-serialized dict. Content varies by task_type and task_state.
`requester` stores id of user who submitted the task
`created` stores date that entry was first created
`updated` stores date that entry was last modified
"""
task_type = models.CharField(max_length=50, db_index=True)
course_id = models.CharField(max_length=255, db_index=True)
task_key = models.CharField(max_length=255, db_index=True)
task_input = models.CharField(max_length=255)
task_id = models.CharField(max_length=255, db_index=True) # max_length from celery_taskmeta
task_state = models.CharField(max_length=50, null=True, db_index=True) # max_length from celery_taskmeta
task_output = models.CharField(max_length=1024, null=True)
requester = models.ForeignKey(User, db_index=True)
created = models.DateTimeField(auto_now_add=True, null=True)
updated = models.DateTimeField(auto_now=True)
def __repr__(self):
return 'CourseTask<%r>' % ({
'task_type': self.task_type,
'course_id': self.course_id,
'task_input': self.task_input,
'task_id': self.task_id,
'task_state': self.task_state,
'task_output': self.task_output,
},)
def __unicode__(self):
return unicode(repr(self))
...@@ -54,6 +54,14 @@ def submit_rescore_problem_for_student(request, course_id, problem_url, student) ...@@ -54,6 +54,14 @@ def submit_rescore_problem_for_student(request, course_id, problem_url, student)
ItemNotFoundException is raised if the problem doesn't exist, or AlreadyRunningError ItemNotFoundException is raised if the problem doesn't exist, or AlreadyRunningError
if the problem is already being rescored for this student, or NotImplementedError if if the problem is already being rescored for this student, or NotImplementedError if
the problem doesn't support rescoring. the problem doesn't support rescoring.
This method makes sure the InstructorTask entry is committed.
When called from any view that is wrapped by TransactionMiddleware,
and thus in a "commit-on-success" transaction, an autocommit buried within here
will cause any pending transaction to be committed by a successful
save here. Any future database operations will take place in a
separate transaction.
""" """
# check arguments: let exceptions return up to the caller. # check arguments: let exceptions return up to the caller.
check_arguments_for_rescoring(course_id, problem_url) check_arguments_for_rescoring(course_id, problem_url)
...@@ -76,6 +84,13 @@ def submit_rescore_problem_for_all_students(request, course_id, problem_url): ...@@ -76,6 +84,13 @@ def submit_rescore_problem_for_all_students(request, course_id, problem_url):
ItemNotFoundException is raised if the problem doesn't exist, or AlreadyRunningError ItemNotFoundException is raised if the problem doesn't exist, or AlreadyRunningError
if the problem is already being rescored, or NotImplementedError if the problem doesn't if the problem is already being rescored, or NotImplementedError if the problem doesn't
support rescoring. support rescoring.
This method makes sure the InstructorTask entry is committed.
When called from any view that is wrapped by TransactionMiddleware,
and thus in a "commit-on-success" transaction, an autocommit buried within here
will cause any pending transaction to be committed by a successful
save here. Any future database operations will take place in a
separate transaction.
""" """
# check arguments: let exceptions return up to the caller. # check arguments: let exceptions return up to the caller.
check_arguments_for_rescoring(course_id, problem_url) check_arguments_for_rescoring(course_id, problem_url)
...@@ -98,6 +113,13 @@ def submit_reset_problem_attempts_for_all_students(request, course_id, problem_u ...@@ -98,6 +113,13 @@ def submit_reset_problem_attempts_for_all_students(request, course_id, problem_u
ItemNotFoundException is raised if the problem doesn't exist, or AlreadyRunningError ItemNotFoundException is raised if the problem doesn't exist, or AlreadyRunningError
if the problem is already being reset. if the problem is already being reset.
This method makes sure the InstructorTask entry is committed.
When called from any view that is wrapped by TransactionMiddleware,
and thus in a "commit-on-success" transaction, an autocommit buried within here
will cause any pending transaction to be committed by a successful
save here. Any future database operations will take place in a
separate transaction.
""" """
# check arguments: make sure that the problem_url is defined # check arguments: make sure that the problem_url is defined
# (since that's currently typed in). If the corresponding module descriptor doesn't exist, # (since that's currently typed in). If the corresponding module descriptor doesn't exist,
...@@ -121,6 +143,13 @@ def submit_delete_problem_state_for_all_students(request, course_id, problem_url ...@@ -121,6 +143,13 @@ def submit_delete_problem_state_for_all_students(request, course_id, problem_url
ItemNotFoundException is raised if the problem doesn't exist, or AlreadyRunningError ItemNotFoundException is raised if the problem doesn't exist, or AlreadyRunningError
if the particular problem is already being deleted. if the particular problem is already being deleted.
This method makes sure the InstructorTask entry is committed.
When called from any view that is wrapped by TransactionMiddleware,
and thus in a "commit-on-success" transaction, an autocommit buried within here
will cause any pending transaction to be committed by a successful
save here. Any future database operations will take place in a
separate transaction.
""" """
# check arguments: make sure that the problem_url is defined # check arguments: make sure that the problem_url is defined
# (since that's currently typed in). If the corresponding module descriptor doesn't exist, # (since that's currently typed in). If the corresponding module descriptor doesn't exist,
......
...@@ -60,14 +60,14 @@ class TaskSubmitTestCase(TestCase): ...@@ -60,14 +60,14 @@ class TaskSubmitTestCase(TestCase):
progress_json = json.dumps(task_output) progress_json = json.dumps(task_output)
task_input, task_key = encode_problem_and_student_input(self.problem_url, student) task_input, task_key = encode_problem_and_student_input(self.problem_url, student)
course_task = InstructorTaskFactory.create(course_id=TEST_COURSE_ID, instructor_task = InstructorTaskFactory.create(course_id=TEST_COURSE_ID,
requester=self.instructor, requester=self.instructor,
task_input=json.dumps(task_input), task_input=json.dumps(task_input),
task_key=task_key, task_key=task_key,
task_id=task_id, task_id=task_id,
task_state=task_state, task_state=task_state,
task_output=progress_json) task_output=progress_json)
return course_task return instructor_task
def _create_failure_entry(self): def _create_failure_entry(self):
"""Creates a InstructorTask entry representing a failed task.""" """Creates a InstructorTask entry representing a failed task."""
...@@ -97,24 +97,24 @@ class TaskSubmitTestCase(TestCase): ...@@ -97,24 +97,24 @@ class TaskSubmitTestCase(TestCase):
self._create_failure_entry() self._create_failure_entry()
self._create_success_entry() self._create_success_entry()
progress_task_ids = [self._create_progress_entry().task_id for _ in range(1, 5)] progress_task_ids = [self._create_progress_entry().task_id for _ in range(1, 5)]
task_ids = [course_task.task_id for course_task in get_running_instructor_tasks(TEST_COURSE_ID)] task_ids = [instructor_task.task_id for instructor_task in get_running_instructor_tasks(TEST_COURSE_ID)]
self.assertEquals(set(task_ids), set(progress_task_ids)) self.assertEquals(set(task_ids), set(progress_task_ids))
def _get_course_task_status(self, task_id): def _get_instructor_task_status(self, task_id):
request = Mock() request = Mock()
request.REQUEST = {'task_id': task_id} request.REQUEST = {'task_id': task_id}
return instructor_task_status(request) return instructor_task_status(request)
def test_course_task_status(self): def test_instructor_task_status(self):
course_task = self._create_failure_entry() instructor_task = self._create_failure_entry()
task_id = course_task.task_id task_id = instructor_task.task_id
request = Mock() request = Mock()
request.REQUEST = {'task_id': task_id} request.REQUEST = {'task_id': task_id}
response = instructor_task_status(request) response = instructor_task_status(request)
output = json.loads(response.content) output = json.loads(response.content)
self.assertEquals(output['task_id'], task_id) self.assertEquals(output['task_id'], task_id)
def test_course_task_status_list(self): def test_instructor_task_status_list(self):
# Fetch status for existing tasks by arg list, as if called from ajax. # Fetch status for existing tasks by arg list, as if called from ajax.
# Note that ajax does something funny with the marshalling of # Note that ajax does something funny with the marshalling of
# list data, so the key value has "[]" appended to it. # list data, so the key value has "[]" appended to it.
...@@ -128,9 +128,9 @@ class TaskSubmitTestCase(TestCase): ...@@ -128,9 +128,9 @@ class TaskSubmitTestCase(TestCase):
self.assertEquals(output[task_id]['task_id'], task_id) self.assertEquals(output[task_id]['task_id'], task_id)
def test_get_status_from_failure(self): def test_get_status_from_failure(self):
course_task = self._create_failure_entry() instructor_task = self._create_failure_entry()
task_id = course_task.task_id task_id = instructor_task.task_id
response = self._get_course_task_status(task_id) response = self._get_instructor_task_status(task_id)
output = json.loads(response.content) output = json.loads(response.content)
self.assertEquals(output['task_id'], task_id) self.assertEquals(output['task_id'], task_id)
self.assertEquals(output['task_state'], FAILURE) self.assertEquals(output['task_state'], FAILURE)
...@@ -138,9 +138,9 @@ class TaskSubmitTestCase(TestCase): ...@@ -138,9 +138,9 @@ class TaskSubmitTestCase(TestCase):
self.assertEquals(output['message'], TEST_FAILURE_MESSAGE) self.assertEquals(output['message'], TEST_FAILURE_MESSAGE)
def test_get_status_from_success(self): def test_get_status_from_success(self):
course_task = self._create_success_entry() instructor_task = self._create_success_entry()
task_id = course_task.task_id task_id = instructor_task.task_id
response = self._get_course_task_status(task_id) response = self._get_instructor_task_status(task_id)
output = json.loads(response.content) output = json.loads(response.content)
self.assertEquals(output['task_id'], task_id) self.assertEquals(output['task_id'], task_id)
self.assertEquals(output['task_state'], SUCCESS) self.assertEquals(output['task_state'], SUCCESS)
...@@ -148,8 +148,8 @@ class TaskSubmitTestCase(TestCase): ...@@ -148,8 +148,8 @@ class TaskSubmitTestCase(TestCase):
def test_update_progress_to_progress(self): def test_update_progress_to_progress(self):
# view task entry for task in progress # view task entry for task in progress
course_task = self._create_progress_entry() instructor_task = self._create_progress_entry()
task_id = course_task.task_id task_id = instructor_task.task_id
mock_result = Mock() mock_result = Mock()
mock_result.task_id = task_id mock_result.task_id = task_id
mock_result.state = PROGRESS mock_result.state = PROGRESS
...@@ -159,7 +159,7 @@ class TaskSubmitTestCase(TestCase): ...@@ -159,7 +159,7 @@ class TaskSubmitTestCase(TestCase):
'action_name': 'rescored'} 'action_name': 'rescored'}
with patch('celery.result.AsyncResult.__new__') as mock_result_ctor: with patch('celery.result.AsyncResult.__new__') as mock_result_ctor:
mock_result_ctor.return_value = mock_result mock_result_ctor.return_value = mock_result
response = self._get_course_task_status(task_id) response = self._get_instructor_task_status(task_id)
output = json.loads(response.content) output = json.loads(response.content)
self.assertEquals(output['task_id'], task_id) self.assertEquals(output['task_id'], task_id)
self.assertEquals(output['task_state'], PROGRESS) self.assertEquals(output['task_state'], PROGRESS)
...@@ -168,8 +168,8 @@ class TaskSubmitTestCase(TestCase): ...@@ -168,8 +168,8 @@ class TaskSubmitTestCase(TestCase):
def test_update_progress_to_failure(self): def test_update_progress_to_failure(self):
# view task entry for task in progress that later fails # view task entry for task in progress that later fails
course_task = self._create_progress_entry() instructor_task = self._create_progress_entry()
task_id = course_task.task_id task_id = instructor_task.task_id
mock_result = Mock() mock_result = Mock()
mock_result.task_id = task_id mock_result.task_id = task_id
mock_result.state = FAILURE mock_result.state = FAILURE
...@@ -177,7 +177,7 @@ class TaskSubmitTestCase(TestCase): ...@@ -177,7 +177,7 @@ class TaskSubmitTestCase(TestCase):
mock_result.traceback = "random traceback" mock_result.traceback = "random traceback"
with patch('celery.result.AsyncResult.__new__') as mock_result_ctor: with patch('celery.result.AsyncResult.__new__') as mock_result_ctor:
mock_result_ctor.return_value = mock_result mock_result_ctor.return_value = mock_result
response = self._get_course_task_status(task_id) response = self._get_instructor_task_status(task_id)
output = json.loads(response.content) output = json.loads(response.content)
self.assertEquals(output['task_id'], task_id) self.assertEquals(output['task_id'], task_id)
self.assertEquals(output['task_state'], FAILURE) self.assertEquals(output['task_state'], FAILURE)
...@@ -186,14 +186,14 @@ class TaskSubmitTestCase(TestCase): ...@@ -186,14 +186,14 @@ class TaskSubmitTestCase(TestCase):
def test_update_progress_to_revoked(self): def test_update_progress_to_revoked(self):
# view task entry for task in progress that later fails # view task entry for task in progress that later fails
course_task = self._create_progress_entry() instructor_task = self._create_progress_entry()
task_id = course_task.task_id task_id = instructor_task.task_id
mock_result = Mock() mock_result = Mock()
mock_result.task_id = task_id mock_result.task_id = task_id
mock_result.state = REVOKED mock_result.state = REVOKED
with patch('celery.result.AsyncResult.__new__') as mock_result_ctor: with patch('celery.result.AsyncResult.__new__') as mock_result_ctor:
mock_result_ctor.return_value = mock_result mock_result_ctor.return_value = mock_result
response = self._get_course_task_status(task_id) response = self._get_instructor_task_status(task_id)
output = json.loads(response.content) output = json.loads(response.content)
self.assertEquals(output['task_id'], task_id) self.assertEquals(output['task_id'], task_id)
self.assertEquals(output['task_state'], REVOKED) self.assertEquals(output['task_state'], REVOKED)
...@@ -201,10 +201,10 @@ class TaskSubmitTestCase(TestCase): ...@@ -201,10 +201,10 @@ class TaskSubmitTestCase(TestCase):
self.assertEquals(output['message'], "Task revoked before running") self.assertEquals(output['message'], "Task revoked before running")
def _get_output_for_task_success(self, attempted, updated, total, student=None): def _get_output_for_task_success(self, attempted, updated, total, student=None):
"""returns the task_id and the result returned by course_task_status().""" """returns the task_id and the result returned by instructor_task_status()."""
# view task entry for task in progress # view task entry for task in progress
course_task = self._create_progress_entry(student) instructor_task = self._create_progress_entry(student)
task_id = course_task.task_id task_id = instructor_task.task_id
mock_result = Mock() mock_result = Mock()
mock_result.task_id = task_id mock_result.task_id = task_id
mock_result.state = SUCCESS mock_result.state = SUCCESS
...@@ -214,7 +214,7 @@ class TaskSubmitTestCase(TestCase): ...@@ -214,7 +214,7 @@ class TaskSubmitTestCase(TestCase):
'action_name': 'rescored'} 'action_name': 'rescored'}
with patch('celery.result.AsyncResult.__new__') as mock_result_ctor: with patch('celery.result.AsyncResult.__new__') as mock_result_ctor:
mock_result_ctor.return_value = mock_result mock_result_ctor.return_value = mock_result
response = self._get_course_task_status(task_id) response = self._get_instructor_task_status(task_id)
output = json.loads(response.content) output = json.loads(response.content)
return task_id, output return task_id, output
...@@ -271,9 +271,9 @@ class TaskSubmitTestCase(TestCase): ...@@ -271,9 +271,9 @@ class TaskSubmitTestCase(TestCase):
# def test_submit_when_running(self): # def test_submit_when_running(self):
# # get exception when trying to submit a task that is already running # # get exception when trying to submit a task that is already running
# course_task = self._create_progress_entry() # instructor_task = self._create_progress_entry()
# problem_url = json.loads(course_task.task_input).get('problem_url') # problem_url = json.loads(instructor_task.task_input).get('problem_url')
# course_id = course_task.course_id # course_id = instructor_task.course_id
# # requester doesn't have to be the same when determining if a task is already running # # requester doesn't have to be the same when determining if a task is already running
# request = Mock() # request = Mock()
# request.user = self.instructor # request.user = self.instructor
......
...@@ -9,7 +9,6 @@ import logging ...@@ -9,7 +9,6 @@ import logging
import json import json
from mock import Mock, patch from mock import Mock, patch
import textwrap import textwrap
from uuid import uuid4
from celery.states import SUCCESS, FAILURE from celery.states import SUCCESS, FAILURE
from django.contrib.auth.models import User from django.contrib.auth.models import User
...@@ -23,17 +22,18 @@ from xmodule.modulestore.django import modulestore ...@@ -23,17 +22,18 @@ from xmodule.modulestore.django import modulestore
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.exceptions import ItemNotFoundError from xmodule.modulestore.exceptions import ItemNotFoundError
from student.tests.factories import CourseEnrollmentFactory, UserFactory, AdminFactory
from student.tests.factories import CourseEnrollmentFactory, UserFactory, AdminFactory
from courseware.model_data import StudentModule from courseware.model_data import StudentModule
from courseware.tests.tests import LoginEnrollmentTestCase, TEST_DATA_MONGO_MODULESTORE
from instructor_task.api import (submit_rescore_problem_for_all_students, from instructor_task.api import (submit_rescore_problem_for_all_students,
submit_rescore_problem_for_student, submit_rescore_problem_for_student,
submit_reset_problem_attempts_for_all_students, submit_reset_problem_attempts_for_all_students,
submit_delete_problem_state_for_all_students) submit_delete_problem_state_for_all_students)
from instructor_task.views import instructor_task_status from instructor_task.models import InstructorTask
from courseware.tests.tests import LoginEnrollmentTestCase, TEST_DATA_MONGO_MODULESTORE
from instructor_task.tests.factories import InstructorTaskFactory from instructor_task.tests.factories import InstructorTaskFactory
from instructor_task.views import instructor_task_status
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
...@@ -306,6 +306,7 @@ class TestRescoring(TestRescoringBase): ...@@ -306,6 +306,7 @@ class TestRescoring(TestRescoringBase):
instructor_task = self.submit_rescore_all_student_answers('instructor', problem_url_name) instructor_task = self.submit_rescore_all_student_answers('instructor', problem_url_name)
# check instructor_task returned # check instructor_task returned
instructor_task = InstructorTask.objects.get(id=instructor_task.id)
self.assertEqual(instructor_task.task_state, 'FAILURE') self.assertEqual(instructor_task.task_state, 'FAILURE')
self.assertEqual(instructor_task.requester.username, 'instructor') self.assertEqual(instructor_task.requester.username, 'instructor')
self.assertEqual(instructor_task.task_type, 'rescore_problem') self.assertEqual(instructor_task.task_type, 'rescore_problem')
...@@ -359,6 +360,8 @@ class TestRescoring(TestRescoringBase): ...@@ -359,6 +360,8 @@ class TestRescoring(TestRescoringBase):
self.submit_student_answer('u1', problem_url_name, ["answer1", "answer2"]) self.submit_student_answer('u1', problem_url_name, ["answer1", "answer2"])
instructor_task = self.submit_rescore_all_student_answers('instructor', problem_url_name) instructor_task = self.submit_rescore_all_student_answers('instructor', problem_url_name)
instructor_task = InstructorTask.objects.get(id=instructor_task.id)
self.assertEqual(instructor_task.task_state, FAILURE) self.assertEqual(instructor_task.task_state, FAILURE)
status = json.loads(instructor_task.task_output) status = json.loads(instructor_task.task_output)
self.assertEqual(status['exception'], 'NotImplementedError') self.assertEqual(status['exception'], 'NotImplementedError')
...@@ -510,7 +513,8 @@ class TestResetAttempts(TestRescoringBase): ...@@ -510,7 +513,8 @@ class TestResetAttempts(TestRescoringBase):
mock_save.side_effect = ZeroDivisionError(expected_message) mock_save.side_effect = ZeroDivisionError(expected_message)
instructor_task = self.reset_problem_attempts('instructor', problem_url_name) instructor_task = self.reset_problem_attempts('instructor', problem_url_name)
# check instructor_task returned # check instructor_task
instructor_task = InstructorTask.objects.get(id=instructor_task.id)
self.assertEqual(instructor_task.task_state, FAILURE) self.assertEqual(instructor_task.task_state, FAILURE)
self.assertEqual(instructor_task.requester.username, 'instructor') self.assertEqual(instructor_task.requester.username, 'instructor')
self.assertEqual(instructor_task.task_type, 'reset_problem_attempts') self.assertEqual(instructor_task.task_type, 'reset_problem_attempts')
...@@ -529,6 +533,7 @@ class TestResetAttempts(TestRescoringBase): ...@@ -529,6 +533,7 @@ class TestResetAttempts(TestRescoringBase):
"""confirm that a non-problem can still be successfully reset""" """confirm that a non-problem can still be successfully reset"""
problem_url_name = self.problem_section.location.url() problem_url_name = self.problem_section.location.url()
instructor_task = self.reset_problem_attempts('instructor', problem_url_name) instructor_task = self.reset_problem_attempts('instructor', problem_url_name)
instructor_task = InstructorTask.objects.get(id=instructor_task.id)
self.assertEqual(instructor_task.task_state, SUCCESS) self.assertEqual(instructor_task.task_state, SUCCESS)
def test_reset_nonexistent_problem(self): def test_reset_nonexistent_problem(self):
...@@ -586,6 +591,7 @@ class TestDeleteProblem(TestRescoringBase): ...@@ -586,6 +591,7 @@ class TestDeleteProblem(TestRescoringBase):
instructor_task = self.delete_problem_state('instructor', problem_url_name) instructor_task = self.delete_problem_state('instructor', problem_url_name)
# check instructor_task returned # check instructor_task returned
instructor_task = InstructorTask.objects.get(id=instructor_task.id)
self.assertEqual(instructor_task.task_state, FAILURE) self.assertEqual(instructor_task.task_state, FAILURE)
self.assertEqual(instructor_task.requester.username, 'instructor') self.assertEqual(instructor_task.requester.username, 'instructor')
self.assertEqual(instructor_task.task_type, 'delete_problem_state') self.assertEqual(instructor_task.task_type, 'delete_problem_state')
...@@ -604,6 +610,7 @@ class TestDeleteProblem(TestRescoringBase): ...@@ -604,6 +610,7 @@ class TestDeleteProblem(TestRescoringBase):
"""confirm that a non-problem can still be successfully deleted""" """confirm that a non-problem can still be successfully deleted"""
problem_url_name = self.problem_section.location.url() problem_url_name = self.problem_section.location.url()
instructor_task = self.delete_problem_state('instructor', problem_url_name) instructor_task = self.delete_problem_state('instructor', problem_url_name)
instructor_task = InstructorTask.objects.get(id=instructor_task.id)
self.assertEqual(instructor_task.task_state, SUCCESS) self.assertEqual(instructor_task.task_state, SUCCESS)
def test_delete_nonexistent_module(self): def test_delete_nonexistent_module(self):
......
...@@ -6,8 +6,8 @@ from django.http import HttpResponse ...@@ -6,8 +6,8 @@ from django.http import HttpResponse
from celery.states import FAILURE, REVOKED, READY_STATES from celery.states import FAILURE, REVOKED, READY_STATES
from instructor_task.api_helper import (_get_instructor_task_status, from instructor_task.api_helper import (get_status_from_instructor_task,
_get_updated_instructor_task) get_updated_instructor_task)
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
...@@ -31,10 +31,36 @@ def instructor_task_status(request): ...@@ -31,10 +31,36 @@ def instructor_task_status(request):
Task_id values that are unrecognized are skipped. Task_id values that are unrecognized are skipped.
The dict with status information for a task contains the following keys:
'message': status message reporting on progress, or providing exception message if failed.
'succeeded': on complete tasks, indicates if the task outcome was successful:
did it achieve what it set out to do.
This is in contrast with a successful task_state, which indicates that the
task merely completed.
'task_id': id assigned by LMS and used by celery.
'task_state': state of task as stored in celery's result store.
'in_progress': boolean indicating if task is still running.
'task_progress': dict containing progress information. This includes:
'attempted': number of attempts made
'updated': number of attempts that "succeeded"
'total': number of possible subtasks to attempt
'action_name': user-visible verb to use in status messages. Should be past-tense.
'duration_ms': how long the task has (or had) been running.
'exception': name of exception class raised in failed tasks.
'message': returned for failed and revoked tasks.
'traceback': optional, returned if task failed and produced a traceback.
""" """
def get_instructor_task_status(task_id): def get_instructor_task_status(task_id):
instructor_task = _get_updated_instructor_task(task_id) """
status = _get_instructor_task_status(instructor_task) Returns status for a specific task.
Written as an internal method here (rather than as a helper)
so that get_task_completion_info() can be called without
causing a circular dependency (since it's also called directly).
"""
instructor_task = get_updated_instructor_task(task_id)
status = get_status_from_instructor_task(instructor_task)
if instructor_task.task_state in READY_STATES: if instructor_task.task_state in READY_STATES:
succeeded, message = get_task_completion_info(instructor_task) succeeded, message = get_task_completion_info(instructor_task)
status['message'] = message status['message'] = message
......
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