Exemplo n.º 1
0
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.')
Exemplo n.º 2
0
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
Exemplo n.º 3
0
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.')
Exemplo n.º 4
0
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