def _check_worker_allowed_new_assignment(worker, task_status): """ Check if the worker can be assigned to a new task. Args: worker (orchestra.models.Worker): The worker submitting the task. task_status (orchestra.models.Task.Status): The status of the desired new task assignment. Returns: allowed_new_assignment (bool): True if the worker can be assigned to a new task. Raises: orchestra.core.errors.TaskAssignmentError: Worker has pending reviewer feedback or is assigned to the maximum number of tasks. orchestra.core.errors.TaskStatusError: New task assignment is not permitted for the given status. """ valid_statuses = [ Task.Status.AWAITING_PROCESSING, Task.Status.PENDING_REVIEW ] if task_status not in valid_statuses: raise TaskStatusError('Invalid status for new task assignment.') elif worker_assigned_to_rejected_task(worker): raise TaskAssignmentError('Worker has pending reviewer feedback that ' 'must be addressed.') elif worker_assigned_to_max_tasks(worker): raise TaskAssignmentError('Worker assigned to max number of tasks.')
def submit_task(task_id, task_data, iteration_status, worker): """ Returns a dict mapping task prerequisites onto their latest task assignment information. The dict is of the form: {'previous-slug': {task_assignment_data}, ...} Args: task_id (int): The ID of the task to submit. task_data (str): A JSON blob of task data to submit. iteration_status (orchestra.models.Iteration.Status): The action taken upon task submission (i.e., REQUESTED_REVIEW or PROVIDED_REVIEW). worker (orchestra.models.Worker): The worker submitting the task. Returns: task (orchestra.models.Task): The modified task object. Raises: orchestra.core.errors.IllegalTaskSubmission: Submission prerequisites for the task are incomplete or the assignment is in a non-processing state. orchestra.core.errors.TaskAssignmentError: Worker belongs to more than one assignment for the given task. orchestra.core.errors.TaskStatusError: Task has already been completed. """ submit_datetime = timezone.now() task = Task.objects.select_related('step', 'project').get(id=task_id) step = task.step if not _are_desired_steps_completed_on_project(step.submission_depends_on, project=task.project): raise IllegalTaskSubmission('Submission prerequisites are not ' 'complete.') if task.status == Task.Status.COMPLETE: raise TaskStatusError('Task already completed') # Use select_for_update to prevent concurrency issues with save_task. # See https://github.com/b12io/orchestra/issues/2. assignments = (TaskAssignment.objects.select_for_update().filter( worker=worker, task=task)) # Worker can belong to only one assignment for a given task. if not assignments.count() == 1: raise TaskAssignmentError( 'Task assignment with worker is in broken state.') assignment = assignments[0] if assignment.status != TaskAssignment.Status.PROCESSING: raise IllegalTaskSubmission('Worker is not allowed to submit') next_status = get_next_task_status(task, iteration_status) assignment.in_progress_task_data = task_data # Submit latest iteration latest_iteration = get_latest_iteration(assignment) latest_iteration.status = iteration_status latest_iteration.submitted_data = assignment.in_progress_task_data latest_iteration.end_datetime = submit_datetime latest_iteration.save() assignment.status = TaskAssignment.Status.SUBMITTED assignment.save() previous_status = task.status task.status = next_status task.save() if task.status == Task.Status.PENDING_REVIEW: # Check the assignment policy to try to assign a reviewer automatically task = _preassign_workers(task, AssignmentPolicyType.REVIEWER) elif task.status == Task.Status.REVIEWING: update_related_assignment_status(task, assignment.assignment_counter + 1, assignment.in_progress_task_data, submit_datetime) elif task.status == Task.Status.POST_REVIEW_PROCESSING: update_related_assignment_status(task, assignment.assignment_counter - 1, assignment.in_progress_task_data, submit_datetime) elif task.status == Task.Status.COMPLETE: create_subsequent_tasks(task.project) notify_status_change(task, previous_status) return task
def assert_new_task_status_valid(task_status): valid_statuses = [ Task.Status.AWAITING_PROCESSING, Task.Status.PENDING_REVIEW ] if task_status not in valid_statuses: raise TaskStatusError('Invalid status for new task assignment.')
def submit_task(task_id, task_data, snapshot_type, worker, work_time_seconds): """ Returns a dict mapping task prerequisites onto their latest task assignment information. The dict is of the form: {'previous-slug': {task_assignment_data}, ...} Args: task_id (int): The ID of the task to submit. task_data (str): A JSON blob of task data to submit. snapshot_type (orchestra.models.TaskAssignment.SnapshotType): The action to take upon task submission (e.g., SUBMIT, ACCEPT, REJECT). worker (orchestra.models.Worker): The worker submitting the task. work_time_seconds (int): The time taken by the worker on the latest iteration of their task assignment. Returns: task (orchestra.models.Task): The modified task object. Raises: orchestra.core.errors.IllegalTaskSubmission: Submission prerequisites for the task are incomplete or the assignment is in a non-processing state. orchestra.core.errors.TaskAssignmentError: Worker belongs to more than one assignment for the given task. orchestra.core.errors.TaskStatusError: Task has already been completed. """ task = Task.objects.select_related('step', 'project').get(id=task_id) step = task.step if not _are_desired_steps_completed_on_project(step.submission_depends_on, project=task.project): raise IllegalTaskSubmission('Submission prerequisites are not ' 'complete.') if task.status == Task.Status.COMPLETE: raise TaskStatusError('Task already completed') # Use select_for_update to prevent concurrency issues with save_task. # See https://github.com/unlimitedlabs/orchestra/issues/2. assignments = (TaskAssignment.objects.select_for_update().filter( worker=worker, task=task)) # Worker can belong to only one assignment for a given task. if not assignments.count() == 1: raise TaskAssignmentError( 'Task assignment with worker is in broken state.') assignment = assignments[0] if assignment.status != TaskAssignment.Status.PROCESSING: raise IllegalTaskSubmission('Worker is not allowed to submit') next_status = get_next_task_status(task, snapshot_type) assignment.in_progress_task_data = task_data assignment.snapshots['snapshots'].append({ 'data': assignment.in_progress_task_data, 'datetime': timezone.now().isoformat(), 'type': snapshot_type, 'work_time_seconds': work_time_seconds }) assignment.status = TaskAssignment.Status.SUBMITTED assignment.save() previous_status = task.status task.status = next_status task.save() if task.status == Task.Status.REVIEWING: update_related_assignment_status(task, assignment.assignment_counter + 1, assignment.in_progress_task_data) elif task.status == Task.Status.POST_REVIEW_PROCESSING: update_related_assignment_status(task, assignment.assignment_counter - 1, assignment.in_progress_task_data) elif task.status == Task.Status.COMPLETE: create_subsequent_tasks(task.project) notify_status_change(task, previous_status) return task