def test_completed_projects(self):
        projects = Project.objects.all()
        initial_task = assign_task(self.workers[6].id,
                                   self.tasks['awaiting_processing'].id)
        with patch('orchestra.utils.task_lifecycle._is_review_needed',
                   return_value=False):
            initial_task = submit_task(initial_task.id, {},
                                       TaskAssignment.SnapshotType.SUBMIT,
                                       self.workers[6], 0)
        self.assertEquals(completed_projects(projects).count(), 0)

        next_task = assign_task(
            self.workers[6].id,
            initial_task.project.tasks.order_by('-start_datetime')[0].id)
        with patch('orchestra.utils.task_lifecycle._is_review_needed',
                   return_value=False):
            initial_task = submit_task(next_task.id, {},
                                       TaskAssignment.SnapshotType.SUBMIT,
                                       self.workers[6], 0)
        self.assertEquals(completed_projects(projects).count(), 0)

        next_task = assign_task(
            self.workers[6].id,
            initial_task.project.tasks.order_by('-start_datetime')[0].id)

        with patch('orchestra.utils.task_lifecycle._is_review_needed',
                   return_value=False):
            initial_task = submit_task(next_task.id, {},
                                       TaskAssignment.SnapshotType.SUBMIT,
                                       self.workers[6], 0)
        self.assertEquals(completed_projects(projects).count(), 1)
    def test_role_counter_required_for_new_task(self):
        task = TaskFactory(status=Task.Status.COMPLETE)
        with self.assertRaises(TaskAssignmentError):
            role_counter_required_for_new_task(task)

        project = self.projects['assignment_policy']

        # Create first task in test project
        create_subsequent_tasks(project)
        self.assertEquals(project.tasks.count(), 1)
        # Assign initial task to worker 0
        task = project.tasks.first()
        counter = role_counter_required_for_new_task(task)
        self.assertEquals(counter, 0)

        initial_task = assign_task(self.workers[0].id,
                                   task.id)
        # Submit task; next task should be created
        with patch('orchestra.utils.task_lifecycle._is_review_needed',
                   return_value=True):
            initial_task = submit_task(initial_task.id, {},
                                       Iteration.Status.REQUESTED_REVIEW,
                                       self.workers[0])

            counter = role_counter_required_for_new_task(initial_task)
            self.assertEquals(counter, 1)

            initial_task = assign_task(self.workers[1].id,
                                       task.id)
            initial_task = submit_task(initial_task.id, {},
                                       Iteration.Status.REQUESTED_REVIEW,
                                       self.workers[1])
            counter = role_counter_required_for_new_task(initial_task)
            self.assertEquals(counter, 2)
Exemple #3
0
    def test_completed_projects(self):
        projects = Project.objects.all()
        initial_task = assign_task(self.workers[6].id,
                                   self.tasks['awaiting_processing'].id)
        with patch('orchestra.utils.task_lifecycle._is_review_needed',
                   return_value=False):
            initial_task = submit_task(initial_task.id, {},
                                       Iteration.Status.REQUESTED_REVIEW,
                                       self.workers[6])
        self.assertEquals(completed_projects(projects).count(), 0)

        next_task = assign_task(
            self.workers[6].id,
            initial_task.project.tasks.order_by('-start_datetime')[0].id)
        with patch('orchestra.utils.task_lifecycle._is_review_needed',
                   return_value=False):
            initial_task = submit_task(next_task.id, {},
                                       Iteration.Status.REQUESTED_REVIEW,
                                       self.workers[6])
        self.assertEquals(completed_projects(projects).count(), 0)

        next_task = assign_task(
            self.workers[6].id,
            initial_task.project.tasks.order_by('-start_datetime')[0].id)

        with patch('orchestra.utils.task_lifecycle._is_review_needed',
                   return_value=False):
            initial_task = submit_task(next_task.id, {},
                                       Iteration.Status.REQUESTED_REVIEW,
                                       self.workers[6])
        self.assertEquals(completed_projects(projects).count(), 1)
Exemple #4
0
    def test_role_counter_required_for_new_task(self):
        task = TaskFactory(status=Task.Status.COMPLETE)
        with self.assertRaises(TaskAssignmentError):
            role_counter_required_for_new_task(task)

        project = self.projects['assignment_policy']

        # Create first task in test project
        create_subsequent_tasks(project)
        self.assertEquals(project.tasks.count(), 1)
        # Assign initial task to worker 0
        task = project.tasks.first()
        counter = role_counter_required_for_new_task(task)
        self.assertEquals(counter, 0)

        initial_task = assign_task(self.workers[0].id, task.id)
        # Submit task; next task should be created
        with patch('orchestra.utils.task_lifecycle._is_review_needed',
                   return_value=True):
            initial_task = submit_task(initial_task.id, {},
                                       Iteration.Status.REQUESTED_REVIEW,
                                       self.workers[0])

            counter = role_counter_required_for_new_task(initial_task)
            self.assertEquals(counter, 1)

            initial_task = assign_task(self.workers[1].id, task.id)
            initial_task = submit_task(initial_task.id, {},
                                       Iteration.Status.REQUESTED_REVIEW,
                                       self.workers[1])
            counter = role_counter_required_for_new_task(initial_task)
            self.assertEquals(counter, 2)
    def test_completed_projects(self):
        projects = Project.objects.all()
        initial_task = assign_task(self.workers[6].id,
                                   self.tasks['processing_task'].id)
        with patch('orchestra.utils.task_lifecycle._is_review_needed',
                   return_value=False):
            initial_task = submit_task(initial_task.id, {},
                                       TaskAssignment.SnapshotType.SUBMIT,
                                       self.workers[6], 0)
        self.assertEquals(completed_projects(projects).count(), 0)

        next_task = assign_task(
            self.workers[6].id,
            initial_task.project.tasks.order_by('-start_datetime')[0].id)
        with patch('orchestra.utils.task_lifecycle._is_review_needed',
                   return_value=False):
            initial_task = submit_task(next_task.id, {},
                                       TaskAssignment.SnapshotType.SUBMIT,
                                       self.workers[6], 0)
        self.assertEquals(completed_projects(projects).count(), 0)

        next_task = assign_task(
            self.workers[6].id,
            initial_task.project.tasks.order_by('-start_datetime')[0].id)

        with patch('orchestra.utils.task_lifecycle._is_review_needed',
                   return_value=False):
            initial_task = submit_task(next_task.id, {},
                                       TaskAssignment.SnapshotType.SUBMIT,
                                       self.workers[6], 0)
        self.assertEquals(completed_projects(projects).count(), 1)
Exemple #6
0
def handle_staffing_response(worker,
                             staffing_request_inquiry_id,
                             is_available=False):
    """
    Args:
        worker (orchestra.models.Worker):
            Worker instance that responsed to a staffing request inquiry
        staffing_request_inquiry_id (int):
            Id of a staffing_request_inquiry that is associated with a response
        is_available (boolean):
            Boolean that tells whether worker accepted an inquiry or not
    Returns:
        response (orchestra.models.StaffingResponse):
            StaffingResponse object that has been created for the worker
    """
    staffing_request_inquiry = get_object_or_None(
        StaffingRequestInquiry,
        communication_preference__worker=worker,
        id=staffing_request_inquiry_id)
    if staffing_request_inquiry is None:
        return None

    response = (StaffingResponse.objects.filter(
        request_inquiry=staffing_request_inquiry))
    if response.exists():
        response = response.first()
        if not is_available and response.is_winner:
            raise StaffingResponseException(
                'Cannot reject after accepting the task')

        response.is_available = is_available

    else:
        response = StaffingResponse.objects.create(
            request_inquiry=staffing_request_inquiry,
            is_available=is_available)

    if (is_available and not StaffingResponse.objects.filter(
            request_inquiry__request=staffing_request_inquiry.request,
            is_winner=True).exists()):
        request = staffing_request_inquiry.request
        task_assignment = get_object_or_None(
            TaskAssignment,
            task=request.task,
            assignment_counter=request.required_role_counter)

        # if task assignment exists then reassign
        if task_assignment is not None:
            reassign_assignment(worker.id, task_assignment.id,
                                staffing_request_inquiry)
        # otherwise assign task
        else:
            assign_task(worker.id, request.task.id, staffing_request_inquiry)

    response = (StaffingResponse.objects.filter(
        request_inquiry=staffing_request_inquiry).first())
    check_responses_complete(staffing_request_inquiry.request)
    return response
Exemple #7
0
def assign_task_api(request):
    worker_username = load_encoded_json(request.body)['worker_username']
    try:
        worker = Worker.objects.get(user__username=worker_username)
        task_id = load_encoded_json(request.body)['task_id']
        assign_task(worker.id, task_id)
    except (Worker.DoesNotExist, Task.DoesNotExist,
            WorkerCertificationError) as e:
        raise BadRequest(e)
Exemple #8
0
def assign_task_api(request):
    worker_username = load_encoded_json(request.body)['worker_username']
    try:
        worker = Worker.objects.get(user__username=worker_username)
        task_id = load_encoded_json(request.body)['task_id']
        assign_task(worker.id, task_id)
    except (Worker.DoesNotExist,
            Task.DoesNotExist,
            WorkerCertificationError) as e:
        raise BadRequest(e)
Exemple #9
0
    def test_task_form_init(self):
        """
        Test task form initialization for new, human and machine tasks
        """
        # Create new task form
        # (Test form init with no task instance)
        TaskForm()

        project = Project.objects.get(workflow_slug='test_workflow_2')
        self.assertEquals(Task.objects.filter(project=project).count(),
                          0)
        create_subsequent_tasks(project)

        # Human task was created but not assigned
        # (Test form init with empty assignment history)
        self.assertEquals(Task.objects.filter(project=project).count(),
                          1)
        human_task = Task.objects.filter(project=project).first()
        form = TaskForm(instance=human_task)
        self.assertEquals(form.fields['currently_assigned_to'].initial,
                          None)

        # Human task assigned to entry_level worker
        # (Test form init with a single entry-level worker)
        human_task = assign_task(self.workers[0].id, human_task.id)
        form = TaskForm(instance=human_task)
        with patch('orchestra.utils.task_lifecycle._is_review_needed',
                   return_value=True):
            human_task = submit_task(human_task.id, {},
                                     TaskAssignment.SnapshotType.SUBMIT,
                                     self.workers[0], 0)
        self.assertEquals(form.fields['currently_assigned_to'].initial,
                          self.workers[0].id)

        # Human task under review
        # (Test form init with both an entry-level worker and reviewer)
        human_task = assign_task(self.workers[1].id, human_task.id)
        form = TaskForm(instance=human_task)
        with patch('orchestra.utils.task_lifecycle._is_review_needed',
                   return_value=False):
            human_task = submit_task(human_task.id, {},
                                     TaskAssignment.SnapshotType.ACCEPT,
                                     self.workers[1], 0)
        self.assertEquals(form.fields['currently_assigned_to'].initial,
                          self.workers[1].id)

        # Machine task was created
        # (Test form init with a machine task)
        self.assertEquals(Task.objects.filter(project=project).count(),
                          2)
        machine_task = (Task.objects.filter(project=project)
                                    .exclude(id=human_task.id).first())
        form = TaskForm(instance=machine_task)
        self.assertEquals(form.fields['currently_assigned_to'].initial,
                          None)
Exemple #10
0
    def test_complete_all_tasks_slack_annoucement(self, mock_slack_archive):
        project = self.projects['single_human_step']
        create_subsequent_tasks(project)
        task = Task.objects.get(project=project)
        assign_task(self.workers[1].id, task.id)
        self.assertEqual(project.status, Project.Status.ACTIVE)
        self.assertFalse(mock_slack_archive.called)

        complete_and_skip_task(task.id)
        create_subsequent_tasks(project)
        project.refresh_from_db()
        self.assertEqual(project.status, Project.Status.COMPLETED)
        self.assertTrue(mock_slack_archive.called)
Exemple #11
0
def handle_staffing_response(worker, staffing_request_inquiry_id,
                             is_available=False):
    # TODO(kkamalov): add proper docstring
    staffing_request_inquiry = get_object_or_None(
        StaffingRequestInquiry,
        communication_preference__worker=worker,
        id=staffing_request_inquiry_id
    )
    if staffing_request_inquiry is None:
        return None

    response = (StaffingResponse.objects
                .filter(request=staffing_request_inquiry))
    if response.exists():
        response = response.first()
        if not is_available and response.is_winner:
            raise StaffingResponseException(
                'Cannot reject after accepting the task')

        response.is_available = is_available

    else:
        response = StaffingResponse.objects.create(
            request=staffing_request_inquiry,
            is_available=is_available)

    if (is_available and
            not StaffingResponse.objects.filter(
                request__request=staffing_request_inquiry.request,
                is_winner=True).exists()):
        response.is_winner = True
        request = staffing_request_inquiry.request

        task_assignment = get_object_or_None(
            TaskAssignment,
            task=request.task,
            assignment_counter=request.required_role_counter
        )

        # if task assignment exists then reassign
        if task_assignment is not None:
            reassign_assignment(worker.id, task_assignment.id)
        # otherwise assign task
        else:
            assign_task(worker.id, request.task.id)

    response.save()
    check_responses_complete(staffing_request_inquiry.request)
    return response
Exemple #12
0
def assign_worker_to_task(request):
    data = load_encoded_json(request.body)
    errors = {}
    try:
        assign_task(data.get('worker_id'), data.get('task_id'))
    except WorkerCertificationError as e:
        errors['worker_certification_error'] = str(e)
    except TaskAssignmentError as e:
        errors['task_assignment_error'] = str(e)
    except Exception as e:
        errors['error'] = str(e)
    success = len(errors) == 0
    return {
        'success': success,
        'errors': errors,
    }
Exemple #13
0
    def test_restaff_close_requests(self, mock_slack, mock_experts_slack,
                                    mock_mail):
        """
        Test that existing staffbot requests for a task is closed when
        a staff function is called.
        """
        CLOSED = StaffBotRequest.Status.CLOSED.value

        bot = StaffBot()
        task = (Task.objects.filter(
            status=Task.Status.AWAITING_PROCESSING).first())
        task = assign_task(self.worker.id, task.id)

        init_num_request = StaffBotRequest.objects.filter(task=task).count()
        self.assertEqual(init_num_request, 0)

        bot.restaff(task.id, self.worker.user.username)
        requests = StaffBotRequest.objects.filter(task=task)
        num_request = requests.count()
        self.assertEqual(num_request, init_num_request + 1)
        self.assertNotEqual(requests.last().status, CLOSED)

        # Calling restaff on the same task should close the previous request
        # and create a new one.
        bot.restaff(task.id, self.worker.user.username)
        requests = list(StaffBotRequest.objects.filter(task=task))
        num_request = len(requests)
        self.assertEqual(num_request, init_num_request + 2)
        self.assertEqual(requests[-2].status, CLOSED)
        self.assertNotEqual(requests[-1].status, CLOSED)
    def test_malformed_creation_policy(self):
        project = self.projects['creation_policy']
        workflow_version = project.workflow_version
        first_step = self.workflow_steps[
            workflow_version.slug]['creation_policy_step_0']

        # Create an invalid machine step with an assignment policy
        malformed_step = StepFactory(
            workflow_version=workflow_version,
            slug='machine_step',
            is_human=False,
            creation_policy={},
        )
        malformed_step.creation_depends_on.add(first_step)

        # Create first task in project
        create_subsequent_tasks(project)
        self.assertEquals(project.tasks.count(), 1)

        # Assign initial task to worker 0 and mark as complete
        initial_task = assign_task(self.workers[4].id,
                                   project.tasks.first().id)
        initial_task.status = Task.Status.COMPLETE
        initial_task.save()

        # Cannot have an invalid blob for the creation_policy
        with self.assertRaises(CreationPolicyError):
            create_subsequent_tasks(project)
Exemple #15
0
def assign_worker_to_task(request):
    data = load_encoded_json(request.body)
    errors = {}
    try:
        assign_task(data.get('worker_id'), data.get('task_id'))
    except WorkerCertificationError as e:
        errors['worker_certification_error'] = str(e)
    except TaskAssignmentError as e:
        errors['task_assignment_error'] = str(e)
    except Exception as e:
        errors['error'] = str(e)
    success = len(errors) == 0
    return {
        'success': success,
        'errors': errors,
    }
Exemple #16
0
    def test_staff_command(self, mock_slack, mock_experts_slack, mock_mail):
        """
        Test that the staffing logic is properly executed for the
        staff command.
        """
        task = (Task.objects
                .filter(status=Task.Status.AWAITING_PROCESSING)
                .first())

        # Get certified worker
        worker = self._get_worker_for_task(
            task, WorkerCertification.Role.ENTRY_LEVEL)

        self._test_staffing_requests(worker, task, 'staff {}'.format(task.id),
                                     can_slack=True, can_mail=True)

        self._test_staffing_requests(worker, task, 'staff {}'.format(task.id),
                                     can_slack=False, can_mail=False)

        # Change the task state to pending review
        task = assign_task(worker.id, task.id)
        task.status = Task.Status.PENDING_REVIEW
        task.save()

        StaffingRequestInquiry.objects.all().delete()

        worker = self._get_worker_for_task(task,
                                           WorkerCertification.Role.REVIEWER)
        self._test_staffing_requests(worker, task, 'staff {}'.format(task.id),
                                     can_slack=False, can_mail=False)
        self._test_staffing_requests(worker, task, 'staff {}'.format(task.id),
                                     can_slack=True, can_mail=True)
        self.assertTrue(mock_mail.called)
        self.assertTrue(mock_experts_slack.called)
        self.assertTrue(mock_slack.called)
Exemple #17
0
    def test_malformed_creation_policy(self):
        project = self.projects['creation_policy']
        workflow_version = project.workflow_version
        first_step = self.workflow_steps[
            workflow_version.slug]['creation_policy_step_0']

        # Create an invalid machine step with an assignment policy
        malformed_step = StepFactory(
            workflow_version=workflow_version,
            slug='machine_step',
            is_human=False,
            creation_policy={},
        )
        malformed_step.creation_depends_on.add(first_step)

        # Create first task in project
        create_subsequent_tasks(project)
        self.assertEqual(project.tasks.count(), 1)

        # Assign initial task to worker 0 and mark as complete
        initial_task = assign_task(self.workers[4].id,
                                   project.tasks.first().id)
        initial_task.status = Task.Status.COMPLETE
        initial_task.save()

        # Cannot have an invalid blob for the creation_policy
        with self.assertRaises(CreationPolicyError):
            create_subsequent_tasks(project)
Exemple #18
0
    def test_preassign_workers(self, mock_mail, mock_slack):
        request_cause = StaffBotRequest.RequestCause.TASK_POLICY.value
        staffing_request_count = StaffingRequestInquiry.objects.filter(
            request__request_cause=request_cause).count()
        project = self.projects['staffbot_assignment_policy']

        # Create first task in test project
        create_subsequent_tasks(project)
        address_staffing_requests()
        self.assertEqual(project.tasks.count(), 1)
        # Assign initial task to worker 0
        initial_task = assign_task(self.workers[0].id,
                                   project.tasks.first().id)
        # Submit task; next task should be created
        with patch('orchestra.utils.task_lifecycle._is_review_needed',
                   return_value=False):
            initial_task = submit_task(initial_task.id, {},
                                       Iteration.Status.REQUESTED_REVIEW,
                                       self.workers[0])

        # Mock mail should be called if we autostaff
        self.assertTrue(mock_mail.called)
        self.assertTrue(mock_slack.called)
        # Assert we created new StaffingRequestInquirys because of autostaff
        new_staffing_request_count = StaffingRequestInquiry.objects.filter(
            request__request_cause=request_cause).count()
        self.assertTrue(staffing_request_count < new_staffing_request_count)
        self.assertEqual(project.tasks.count(), 2)
        related_task = project.tasks.exclude(id=initial_task.id).first()
        # Worker 0 not certified for related tasks, so should not have been
        # auto-assigned
        self.assertEqual(related_task.assignments.count(), 0)
        self.assertEqual(related_task.status, Task.Status.AWAITING_PROCESSING)
    def test_staff_command(self, mock_slack, mock_experts_slack, mock_mail):
        """
        Test that the staffing logic is properly executed for the
        staff command.
        """
        task = (Task.objects
                .filter(status=Task.Status.AWAITING_PROCESSING)
                .first())

        # Get certified worker
        worker = self._get_worker_for_task(
            task, WorkerCertification.Role.ENTRY_LEVEL)

        self._test_staffing_requests(worker, task, 'staff {}'.format(task.id),
                                     can_slack=True, can_mail=True)

        self._test_staffing_requests(worker, task, 'staff {}'.format(task.id),
                                     can_slack=False, can_mail=False)

        # Change the task state to pending review
        task = assign_task(worker.id, task.id)
        task.status = Task.Status.PENDING_REVIEW
        task.save()

        StaffingRequestInquiry.objects.all().delete()

        worker = self._get_worker_for_task(task,
                                           WorkerCertification.Role.REVIEWER)
        self._test_staffing_requests(worker, task, 'staff {}'.format(task.id),
                                     can_slack=False, can_mail=False)
        self._test_staffing_requests(worker, task, 'staff {}'.format(task.id),
                                     can_slack=True, can_mail=True)
        self.assertTrue(mock_mail.called)
        self.assertTrue(mock_experts_slack.called)
        self.assertTrue(mock_slack.called)
    def test_preassign_workers(self):
        project = self.projects['assignment_policy']

        # Create first task in test project
        create_subsequent_tasks(project)
        self.assertEquals(project.tasks.count(), 1)
        # Assign initial task to worker 0
        initial_task = assign_task(self.workers[0].id,
                                   project.tasks.first().id)
        # Submit task; next task should be created
        with patch('orchestra.utils.task_lifecycle._is_review_needed',
                   return_value=False):
            initial_task = submit_task(initial_task.id, {},
                                       TaskAssignment.SnapshotType.SUBMIT,
                                       self.workers[0], 0)
        self.assertEquals(project.tasks.count(), 2)
        related_task = project.tasks.exclude(id=initial_task.id).first()
        # Worker 0 not certified for related tasks, so should not have been
        # auto-assigned
        self.assertEquals(related_task.assignments.count(), 0)
        self.assertEquals(related_task.status, Task.Status.AWAITING_PROCESSING)

        # Reset project
        project.tasks.all().delete()

        # Create first task in test project
        create_subsequent_tasks(project)
        self.assertEquals(project.tasks.count(), 1)
        # Assign initial task to worker 4
        initial_task = assign_task(self.workers[4].id,
                                   project.tasks.first().id)
        # Submit task; next task should be created
        with patch('orchestra.utils.task_lifecycle._is_review_needed',
                   return_value=False):
            initial_task = submit_task(initial_task.id, {},
                                       TaskAssignment.SnapshotType.SUBMIT,
                                       self.workers[4], 0)
        self.assertEquals(project.tasks.count(), 2)
        related_task = project.tasks.exclude(id=initial_task.id).first()
        # Worker 4 is certified for related task and should have been assigned
        self.assertEquals(related_task.assignments.count(), 1)
        self.assertEquals(related_task.status, Task.Status.PROCESSING)
        self.assertTrue(is_worker_assigned_to_task(self.workers[4],
                                                   related_task))

        # Reset project
        project.tasks.all().delete()
    def test_preassign_workers(self):
        project = self.projects['assignment_policy']

        # Create first task in test project
        create_subsequent_tasks(project)
        self.assertEquals(project.tasks.count(), 1)
        # Assign initial task to worker 0
        initial_task = assign_task(self.workers[0].id,
                                   project.tasks.first().id)
        # Submit task; next task should be created
        with patch('orchestra.utils.task_lifecycle._is_review_needed',
                   return_value=False):
            initial_task = submit_task(initial_task.id, {},
                                       TaskAssignment.SnapshotType.SUBMIT,
                                       self.workers[0], 0)
        self.assertEquals(project.tasks.count(), 2)
        related_task = project.tasks.exclude(id=initial_task.id).first()
        # Worker 0 not certified for related tasks, so should not have been
        # auto-assigned
        self.assertEquals(related_task.assignments.count(), 0)
        self.assertEquals(related_task.status, Task.Status.AWAITING_PROCESSING)

        # Reset project
        project.tasks.all().delete()

        # Create first task in test project
        create_subsequent_tasks(project)
        self.assertEquals(project.tasks.count(), 1)
        # Assign initial task to worker 4
        initial_task = assign_task(self.workers[4].id,
                                   project.tasks.first().id)
        # Submit task; next task should be created
        with patch('orchestra.utils.task_lifecycle._is_review_needed',
                   return_value=False):
            initial_task = submit_task(initial_task.id, {},
                                       TaskAssignment.SnapshotType.SUBMIT,
                                       self.workers[4], 0)
        self.assertEquals(project.tasks.count(), 2)
        related_task = project.tasks.exclude(id=initial_task.id).first()
        # Worker 4 is certified for related task and should have been assigned
        self.assertEquals(related_task.assignments.count(), 1)
        self.assertEquals(related_task.status, Task.Status.PROCESSING)
        self.assertTrue(
            is_worker_assigned_to_task(self.workers[4], related_task))

        # Reset project
        project.tasks.all().delete()
Exemple #22
0
    def test_malformed_assignment_policy(self):
        project = self.projects['assignment_policy']
        workflow_version = project.workflow_version
        first_step = self.workflow_steps[workflow_version.slug]['step_0']

        # Create an invalid machine step with an assignment policy
        malformed_step = StepFactory(
            workflow_version=workflow_version,
            slug='machine_step',
            is_human=False,
            assignment_policy={
                'policy_function': {
                    'entry_level': {
                        'path': ('orchestra.assignment_policies.'
                                 'previously_completed_steps'),
                        'kwargs': {
                            'related_steps': ['step_0']
                        },
                    }
                }
            },
            creation_policy=get_default_creation_policy(),
        )
        malformed_step.creation_depends_on.add(first_step)

        # Create first task in project
        create_subsequent_tasks(project)
        self.assertEqual(project.tasks.count(), 1)

        # Assign initial task to worker 0 and mark as complete
        initial_task = assign_task(self.workers[4].id,
                                   project.tasks.first().id)
        initial_task.status = Task.Status.COMPLETE
        initial_task.save()

        # Cannot preassign machine task
        with self.assertRaises(AssignmentPolicyError):
            create_subsequent_tasks(project)

        # Reset project
        project.tasks.all().delete()

        # Machine should not be member of assignment policy
        first_step.assignment_policy = {
            'policy_function': {
                'entry_level': {
                    'path': ('orchestra.assignment_policies.'
                             'previously_completed_steps'),
                    'kwargs': {
                        'related_steps': ['machine_step']
                    },
                },
            }
        }
        first_step.save()
        with self.assertRaises(AssignmentPolicyError):
            create_subsequent_tasks(project)
    def test_malformed_assignment_policy(self):
        project = self.projects['assignment_policy']
        workflow_version = project.workflow_version
        first_step = self.workflow_steps[workflow_version.slug]['step_0']

        # Create an invalid machine step with an assignment policy
        malformed_step = StepFactory(
            workflow_version=workflow_version,
            slug='machine_step',
            is_human=False,
            assignment_policy={
                'policy_function': {
                    'entry_level': {
                        'path': ('orchestra.assignment_policies.'
                                 'previously_completed_steps'),
                        'kwargs': {
                            'related_steps': ['step_0']
                        },
                    }
                }
            },
            creation_policy=get_default_creation_policy(),
        )
        malformed_step.creation_depends_on.add(first_step)

        # Create first task in project
        create_subsequent_tasks(project)
        self.assertEquals(project.tasks.count(), 1)

        # Assign initial task to worker 0 and mark as complete
        initial_task = assign_task(self.workers[4].id,
                                   project.tasks.first().id)
        initial_task.status = Task.Status.COMPLETE
        initial_task.save()

        # Cannot preassign machine task
        with self.assertRaises(AssignmentPolicyError):
            create_subsequent_tasks(project)

        # Reset project
        project.tasks.all().delete()

        # Machine should not be member of assignment policy
        first_step.assignment_policy = {
            'policy_function': {
                'entry_level': {
                    'path': ('orchestra.assignment_policies.'
                             'previously_completed_steps'),
                    'kwargs': {
                        'related_steps': ['machine_step']
                    },
                },
            }
        }
        first_step.save()
        with self.assertRaises(AssignmentPolicyError):
            create_subsequent_tasks(project)
Exemple #24
0
    def test_unarchive_slack_channel_api(self, mock_archive, mock_unarchive):
        project = self.projects['single_human_step']
        create_subsequent_tasks(project)
        task = Task.objects.get(project=project)
        assign_task(self.workers[1].id, task.id)
        complete_and_skip_task(task.id)
        create_subsequent_tasks(project)
        self.assertTrue(mock_archive.called)

        response = self.api_client.post(reverse(
            ('orchestra:orchestra:project_management'
             ':unarchive_slack_channel')),
                                        json.dumps({
                                            'project_id': project.id,
                                        }),
                                        content_type='application/json')
        self.assertEqual(response.status_code, 200)
        self.assertTrue(mock_unarchive.called)
    def test_completion_ends_project_false(self, mock_slack_archive):
        project = self.projects['test_human_and_machine']
        create_subsequent_tasks(project)

        task = project.tasks.first()
        assign_task(self.workers[1].id, task.id)
        self.assertEqual(project.status, Project.Status.ACTIVE)
        self.assertFalse(mock_slack_archive.called)
        task.status = Task.Status.COMPLETE
        task.save()

        create_subsequent_tasks(project)
        project.refresh_from_db()
        incomplete_tasks = (Task.objects.filter(project=project).exclude(
            Q(status=Task.Status.COMPLETE)
            | Q(status=Task.Status.ABORTED))).count()
        self.assertTrue(incomplete_tasks > 0)
        self.assertEqual(project.status, Project.Status.ACTIVE)
        self.assertFalse(mock_slack_archive.called)
    def test_assign_task(self):
        entry_task = TaskFactory(project=self.projects['base_test_project'],
                                 status=Task.Status.AWAITING_PROCESSING,
                                 step=self.test_step)

        # Assign entry-level task to entry-level worker
        entry_task = assign_task(self.workers[0].id, entry_task.id)
        self.assertTrue(is_worker_assigned_to_task(self.workers[0],
                                                   entry_task))
        self.assertEquals(entry_task.status, Task.Status.PROCESSING)
        self.assertEquals(entry_task.assignments.count(), 1)

        # Attempt to assign task which isn't awaiting a new assignment
        invalid = (Task.Status.PROCESSING, Task.Status.ABORTED,
                   Task.Status.REVIEWING, Task.Status.COMPLETE,
                   Task.Status.POST_REVIEW_PROCESSING)
        for status in invalid:
            invalid_status_task = Task.objects.create(
                project=self.projects['base_test_project'],
                status=status,
                step=self.test_step)

            with self.assertRaises(TaskAssignmentError):
                invalid_status_task = assign_task(self.workers[0].id,
                                                  invalid_status_task.id)

        # Attempt to assign review task to worker already in review hierarchy
        review_task = Task.objects.create(
            project=self.projects['base_test_project'],
            status=Task.Status.PENDING_REVIEW,
            step=self.test_step)
        test_data = {'test_assign': True}
        TaskAssignmentFactory(worker=self.workers[1],
                              task=review_task,
                              status=TaskAssignment.Status.SUBMITTED,
                              in_progress_task_data=test_data,
                              snapshots=empty_snapshots())

        with self.assertRaises(TaskAssignmentError):
            assign_task(self.workers[1].id, review_task.id)
        self.assertEqual(
            current_assignment(review_task).in_progress_task_data, test_data)

        # Attempt to assign review task to worker not certified for task
        with self.assertRaises(WorkerCertificationError):
            assign_task(self.workers[2].id, review_task.id)
        self.assertEqual(
            current_assignment(review_task).in_progress_task_data, test_data)

        # Assign review task to review worker
        self.assertEquals(review_task.assignments.count(), 1)
        review_task = assign_task(self.workers[3].id, review_task.id)
        self.assertEquals(review_task.assignments.count(), 2)
        self.assertEqual(
            current_assignment(review_task).worker, self.workers[3])
        self.assertEqual(
            current_assignment(review_task).in_progress_task_data, test_data)
        self.assertEquals(review_task.status, Task.Status.REVIEWING)
Exemple #27
0
def specified_worker(task, username, **kwargs):
    """
    Assign task to a specific person.

    Args:
        username (str):
            Username of the worker to assign.

    Returns:
        task (orchestra.models.Task): The modified task object.
    """
    worker = Worker.objects.get(user__username=username)
    return assign_task(worker.id, task.id)
Exemple #28
0
    def save(self, *args, **kwargs):
        # Create task before further modifications
        task = super(TaskForm, self).save(*args, **kwargs)

        new_worker_id = self.cleaned_data.get("currently_assigned_to", None)

        # TODO(jrbotros): write helper functions to move back and forth through
        # task statuses in the admin

        if new_worker_id and new_worker_id not in [worker.id for worker in all_workers(task)]:
            # If no pre-existing worker is present or the selected worker
            # has not previously been involved with the task, (re)assign it.
            task = assign_task(new_worker_id, task.id)
        return task
Exemple #29
0
    def test_restaff_command(self, mock_slack, mock_mail):
        data = get_mock_slack_data(user_id=self.worker.slack_user_id)

        task = (Task.objects.filter(
            status=Task.Status.AWAITING_PROCESSING).first())
        worker = self.workers[0]
        task = assign_task(worker.id, task.id)
        command = 'restaff {} {}'.format(task.id, worker.user.username)
        data = get_mock_slack_data(text=command,
                                   user_id=self.worker.slack_user_id)

        response = self.request_client.post(self.url, data)
        self.assertEqual(
            load_encoded_json(response.content)['attachments'][0]['text'],
            self.staffbot.restaffing_success.format(task.id))
Exemple #30
0
    def test_restaff_command(self, mock_slack, mock_mail):
        data = get_mock_slack_data(user_id=self.worker.slack_user_id)

        task = (
            Task.objects.filter(status=Task.Status.AWAITING_PROCESSING)
            .first())
        worker = self.workers[0]
        task = assign_task(worker.id, task.id)
        command = 'restaff {} {}'.format(task.id, worker.user.username)
        data = get_mock_slack_data(
            text=command,
            user_id=self.worker.slack_user_id)

        response = self.request_client.post(self.url, data)
        self.assertEqual(load_encoded_json(response.content).get('text'),
                         'Restaffed task {}!'.format(task.id))
Exemple #31
0
    def test_restaff_command(self, mock_slack, mock_mail):
        """
        Test that the restaffing logic is properly executed for the
        restaff command.
        """
        task = (Task.objects
                .filter(status=Task.Status.AWAITING_PROCESSING)
                .first())

        # Get certified worker
        task = assign_task(self.worker.id, task.id)
        command = 'restaff {} {}'.format(task.id, self.worker.user.username)

        worker = self.workers[3]
        self._test_staffing_requests(worker, task, command,
                                     can_slack=False, can_mail=True)
Exemple #32
0
def previously_completed_steps(task, related_steps, **kwargs):
    """
    Assign a new task to the entry-level worker of the specified tasks.
    If no worker can be assigned, return the unmodified task.

    Args:
        task (orchestra.models.Task):
            The newly created task to assign.
        related_steps ([str]):
            List of step slugs from which to attempt to assign a worker.

    Returns:
        task (orchestra.models.Task): The modified task object.

    Raises:
        orchestra.core.errors.AssignmentPolicyError:
            Machine steps cannot be included in an assignment policy.
    """
    if related_steps is None:
        raise AssignmentPolicyError('No related steps given')

    workflow_version = task.step.workflow_version
    for step_slug in related_steps:
        step = workflow_version.steps.get(slug=step_slug)
        if not step.is_human:
            raise AssignmentPolicyError('Machine step should not be '
                                        'member of assignment policy')
    related_tasks = (Task.objects.filter(
        step__slug__in=related_steps,
        project=task.project).select_related('step'))
    for related_task in related_tasks:
        entry_level_assignment = assignment_history(related_task).first()
        if entry_level_assignment and entry_level_assignment.worker:
            try:
                return assign_task(entry_level_assignment.worker.id, task.id)
            except WorkerCertificationError:
                # Task could not be assigned to related worker, try with
                # another related worker
                logger.warning(
                    'Tried to assign worker %s to step %s, for '
                    'which they are not certified',
                    entry_level_assignment.worker.id,
                    task.step.slug,
                    exc_info=True)
            except Exception:
                logger.warning('Unable to assign task.', exc_info=True)
    return task
Exemple #33
0
    def test_schedule_machine_tasks_failed(self, mock_schedule,
                                           mock_preassign):
        project = self.projects['test_human_and_machine']

        # Create first task in project
        create_subsequent_tasks(project)

        # Assign initial task to worker 0 and mark as complete
        initial_task = assign_task(self.workers[0].id,
                                   project.tasks.first().id)
        initial_task.status = Task.Status.COMPLETE
        initial_task.save()

        mock_preassign.side_effect = Exception
        with self.assertRaises(Exception):
            create_subsequent_tasks(project)
        mock_schedule.assert_not_called()
Exemple #34
0
    def test_schedule_machine_tasks(self, mock_schedule):
        project = self.projects['test_human_and_machine']

        # Create first task in project
        create_subsequent_tasks(project)

        # Assign initial task to worker 0 and mark as complete
        initial_task = assign_task(self.workers[0].id,
                                   project.tasks.first().id)
        initial_task.status = Task.Status.COMPLETE
        initial_task.save()

        create_subsequent_tasks(project)
        self.assertEqual(mock_schedule.call_count, 1)
        self.assertEqual(mock_schedule.call_args[0][0], project)
        steps = list(project.workflow_version.steps.filter(is_human=False))
        self.assertEqual(mock_schedule.call_args[0][1], steps)
def previously_completed_steps(task, related_steps, **kwargs):
    """
    Assign a new task to the entry-level worker of the specified tasks.
    If no worker can be assigned, return the unmodified task.

    Args:
        task (orchestra.models.Task):
            The newly created task to assign.
        related_steps ([str]):
            List of step slugs from which to attempt to assign a worker.

    Returns:
        task (orchestra.models.Task): The modified task object.

    Raises:
        orchestra.core.errors.AssignmentPolicyError:
            Machine steps cannot be included in an assignment policy.
    """
    if related_steps is None:
        raise AssignmentPolicyError('No related steps given')

    workflow_version = task.step.workflow_version
    for step_slug in related_steps:
        step = workflow_version.steps.get(slug=step_slug)
        if not step.is_human:
            raise AssignmentPolicyError('Machine step should not be '
                                        'member of assignment policy')
    related_tasks = (
        Task.objects
        .filter(step__slug__in=related_steps, project=task.project)
        .select_related('step'))
    for related_task in related_tasks:
        entry_level_assignment = assignment_history(related_task).first()
        if entry_level_assignment and entry_level_assignment.worker:
            try:
                return assign_task(entry_level_assignment.worker.id, task.id)
            except WorkerCertificationError:
                # Task could not be assigned to related worker, try with
                # another related worker
                logger.warning('Tried to assign worker %s to step %s, for '
                               'which they are not certified',
                               entry_level_assignment.worker.id,
                               task.step.slug, exc_info=True)
            except Exception:
                logger.warning('Unable to assign task.', exc_info=True)
    return task
    def test_schedule_machine_tasks(self, mock_schedule):
        project = self.projects['test_human_and_machine']

        # Create first task in project
        create_subsequent_tasks(project)

        # Assign initial task to worker 0 and mark as complete
        initial_task = assign_task(self.workers[0].id,
                                   project.tasks.first().id)
        initial_task.status = Task.Status.COMPLETE
        initial_task.save()

        create_subsequent_tasks(project)
        self.assertEqual(mock_schedule.call_count, 1)
        self.assertEqual(mock_schedule.call_args[0][0], project)
        steps = list(project.workflow_version.steps.filter(is_human=False))
        self.assertEqual(mock_schedule.call_args[0][1], steps)
    def test_schedule_machine_tasks_failed(self, mock_schedule,
                                           mock_preassign):
        project = self.projects['test_human_and_machine']

        # Create first task in project
        create_subsequent_tasks(project)

        # Assign initial task to worker 0 and mark as complete
        initial_task = assign_task(self.workers[0].id,
                                   project.tasks.first().id)
        initial_task.status = Task.Status.COMPLETE
        initial_task.save()

        mock_preassign.side_effect = Exception
        with self.assertRaises(Exception):
            create_subsequent_tasks(project)
        mock_schedule.assert_not_called()
Exemple #38
0
    def test_always_create_policy(self):
        project = self.projects['creation_policy']

        # Create first task in test project
        create_subsequent_tasks(project)
        self.assertEqual(project.tasks.count(), 1)

        # Assign initial task to worker 0
        initial_task = assign_task(self.workers[0].id,
                                   project.tasks.first().id)
        # Submit task; next task should not be created, it never is
        with patch('orchestra.utils.task_lifecycle._is_review_needed',
                   return_value=False):
            initial_task = submit_task(initial_task.id, {},
                                       Iteration.Status.REQUESTED_REVIEW,
                                       self.workers[0])

        self.assertEqual(project.tasks.count(), 1)
    def test_malformed_assignment_policy(self):
        project = self.projects['assignment_policy']

        # Machine should not have an assignment policy
        workflow = get_workflow_by_slug('assignment_policy_workflow')
        machine_step = Step(
            slug='machine_step',
            worker_type=Step.WorkerType.MACHINE,
            assignment_policy={'policy': 'previously_completed_steps',
                               'steps': ['step_0']},
            creation_depends_on=[workflow.get_step('step_0')],
            function=lambda *args: None,
        )
        workflow.add_step(machine_step)

        # Create first task in project
        create_subsequent_tasks(project)
        self.assertEquals(project.tasks.count(), 1)

        # Assign initial task to worker 0 and mark as complete
        initial_task = assign_task(self.workers[4].id,
                                   project.tasks.first().id)
        initial_task.status = Task.Status.COMPLETE
        initial_task.save()

        # Cannot preassign machine task
        with self.assertRaises(AssignmentPolicyError):
            create_subsequent_tasks(project)

        # Reset project
        project.tasks.all().delete()

        # Machine should not be member of assignment policy
        (workflow.get_step('step_0')
            .assignment_policy) = {'policy': 'previously_completed_steps',
                                   'steps': ['machine_step']}
        with self.assertRaises(AssignmentPolicyError):
            create_subsequent_tasks(project)

        # Reset workflow and project
        (workflow.get_step('step_0')
            .assignment_policy) = {'policy': 'anyone_certified'}
        del workflow.steps['machine_step']
        project.tasks.all().delete()
Exemple #40
0
    def test_restaff_command(self, mock_slack, mock_experts_slack, mock_mail):
        """
        Test that the restaffing logic is properly executed for the
        restaff command.
        """
        task = (Task.objects
                .filter(status=Task.Status.AWAITING_PROCESSING)
                .first())

        # Get certified worker
        task = assign_task(self.worker.id, task.id)
        command = 'restaff {} {}'.format(task.id, self.worker.user.username)

        worker = self.workers[3]
        self._test_staffing_requests(worker, task, command,
                                     can_slack=False, can_mail=True)
        self.assertTrue(mock_mail.called)
        self.assertTrue(mock_experts_slack.called)
        self.assertTrue(mock_slack.called)
    def test_preassign_workers(self, mock_mail, mock_slack):
        request_cause = StaffBotRequest.RequestCause.AUTOSTAFF.value
        staffing_request_count = StaffingRequestInquiry.objects.filter(
            request__request_cause=request_cause).count()
        project = self.projects['staffbot_assignment_policy']

        # Create first task in test project
        create_subsequent_tasks(project)
        send_staffing_requests()
        self.assertEqual(project.tasks.count(), 1)
        # Assign initial task to worker 0
        initial_task = assign_task(self.workers[0].id,
                                   project.tasks.first().id)
        # Submit task; next task should be created
        with patch('orchestra.utils.task_lifecycle._is_review_needed',
                   return_value=False):
            initial_task = submit_task(initial_task.id, {},
                                       Iteration.Status.REQUESTED_REVIEW,
                                       self.workers[0])

        # Mock mail should be called if we autostaff
        self.assertTrue(mock_mail.called)
        self.assertTrue(mock_slack.called)
        # Assert we created new StaffingRequestInquirys because of autostaff
        new_staffing_request_count = StaffingRequestInquiry.objects.filter(
            request__request_cause=request_cause).count()
        self.assertTrue(staffing_request_count < new_staffing_request_count)
        self.assertEqual(project.tasks.count(), 2)
        related_task = project.tasks.exclude(id=initial_task.id).first()
        # Worker 0 not certified for related tasks, so should not have been
        # auto-assigned
        self.assertEqual(related_task.assignments.count(), 0)
        self.assertEqual(related_task.status, Task.Status.AWAITING_PROCESSING)

        # Reset project
        project.tasks.all().delete()
Exemple #42
0
def setup_complete_task(test_case):
    # Microseconds are truncated when manually saving models
    test_start = timezone.now().replace(microsecond=0)
    times = {
        'awaiting_pickup': test_start,
        'entry_pickup': test_start + timedelta(hours=1),
        'entry_submit': test_start + timedelta(hours=2),
        'reviewer_pickup': test_start + timedelta(hours=3),
        'reviewer_reject': test_start + timedelta(hours=4),
        'entry_resubmit': test_start + timedelta(hours=5),
        'reviewer_accept': test_start + timedelta(hours=6),
    }

    task = TaskFactory(
        project=test_case.projects['empty_project'],
        status=Task.Status.AWAITING_PROCESSING,
        step=test_case.test_step,
        start_datetime=times['awaiting_pickup'])

    workers = {
        'entry': test_case.workers[0],
        'reviewer': test_case.workers[1]
    }

    assign_task(workers['entry'].id, task.id)

    task.refresh_from_db()
    test_case.assertEquals(task.status, Task.Status.PROCESSING)

    submit_task(
        task.id, {'test': 'entry_submit'},
        Iteration.Status.REQUESTED_REVIEW,
        workers['entry'])

    task.refresh_from_db()
    test_case.assertEquals(task.status, Task.Status.PENDING_REVIEW)

    assign_task(workers['reviewer'].id, task.id)
    reviewer_assignment = task.assignments.get(
        worker=workers['reviewer'])

    # Modify assignment with correct datetime
    reviewer_assignment.start_datetime = times['reviewer_pickup']
    reviewer_assignment.save()

    task.refresh_from_db()
    test_case.assertEquals(task.status, Task.Status.REVIEWING)

    submit_task(
        task.id, {'test': 'reviewer_reject'},
        Iteration.Status.PROVIDED_REVIEW,
        workers['reviewer'])

    task.refresh_from_db()
    test_case.assertEquals(task.status, Task.Status.POST_REVIEW_PROCESSING)

    submit_task(
        task.id, {'test': 'entry_resubmit'},
        Iteration.Status.REQUESTED_REVIEW,
        workers['entry'])

    task.refresh_from_db()
    test_case.assertEquals(task.status, Task.Status.REVIEWING)

    with patch('orchestra.utils.task_lifecycle._is_review_needed',
               return_value=False):
        submit_task(
            task.id, {'test': 'reviewer_accept'},
            Iteration.Status.REQUESTED_REVIEW,
            workers['reviewer'])

    task.refresh_from_db()
    test_case.assertEquals(task.status, Task.Status.COMPLETE)
    test_case.assertEquals(task.assignments.count(), 2)
    for assignment in task.assignments.all():
        test_case.assertEquals(
            assignment.status, TaskAssignment.Status.SUBMITTED)
        test_case.assertEquals(assignment.iterations.count(), 2)

    # Modify assignments with correct datetime
    new_datetime_labels = ('entry_pickup', 'reviewer_pickup')
    for i, assignment in enumerate(assignment_history(task).all()):
        assignment.start_datetime = times[new_datetime_labels[i]]
        assignment.save()

    # Modify iterations with correct datetime
    new_datetime_labels = (
        ('entry_pickup', 'entry_submit'),
        ('reviewer_pickup', 'reviewer_reject'),
        ('reviewer_reject', 'entry_resubmit'),
        ('entry_resubmit', 'reviewer_accept')
    )
    new_datetimes = [
        (times[start_label], times[end_label])
        for start_label, end_label in new_datetime_labels]

    for i, iteration in enumerate(get_iteration_history(task)):
        iteration.start_datetime, iteration.end_datetime = new_datetimes[i]
        iteration.save()

    verify_iterations(task.id)

    return task
Exemple #43
0
def setup_complete_task(test_case, times):
    task = TaskFactory(project=test_case.projects['empty_project'],
                       status=Task.Status.AWAITING_PROCESSING,
                       step=test_case.test_step,
                       start_datetime=times['awaiting_pickup'])

    workers = {'entry': test_case.workers[0], 'reviewer': test_case.workers[1]}

    assign_task(workers['entry'].id, task.id)
    entry_assignment = task.assignments.get(worker=workers['entry'])
    # Modify assignment with correct datetime
    entry_assignment.start_datetime = times['entry_pickup']
    entry_assignment.save()

    task.refresh_from_db()
    test_case.assertEquals(task.status, Task.Status.PROCESSING)

    submit_task(task.id, {'test': 'entry_submit'},
                TaskAssignment.SnapshotType.SUBMIT, workers['entry'], 0)

    task.refresh_from_db()
    test_case.assertEquals(task.status, Task.Status.PENDING_REVIEW)

    # Modify snapshot with correct datetime
    entry_assignment.refresh_from_db()
    (entry_assignment.snapshots['snapshots'][0]['datetime']
     ) = times['entry_submit']
    entry_assignment.save()

    assign_task(workers['reviewer'].id, task.id)
    reviewer_assignment = task.assignments.get(worker=workers['reviewer'])
    # Modify assignment with correct datetime
    reviewer_assignment.start_datetime = times['reviewer_pickup']
    reviewer_assignment.save()

    task.refresh_from_db()
    test_case.assertEquals(task.status, Task.Status.REVIEWING)

    submit_task(task.id, {'test': 'reviewer_reject'},
                TaskAssignment.SnapshotType.REJECT, workers['reviewer'], 0)
    # Modify snapshot with correct datetime
    reviewer_assignment.refresh_from_db()
    (reviewer_assignment.snapshots['snapshots'][0]['datetime']
     ) = times['reviewer_reject']
    reviewer_assignment.save()

    task.refresh_from_db()
    test_case.assertEquals(task.status, Task.Status.POST_REVIEW_PROCESSING)

    submit_task(task.id, {'test': 'entry_resubmit'},
                TaskAssignment.SnapshotType.SUBMIT, workers['entry'], 0)
    # Modify snapshot with correct datetime
    entry_assignment.refresh_from_db()
    (entry_assignment.snapshots['snapshots'][1]['datetime']
     ) = times['entry_resubmit']
    entry_assignment.save()

    task.refresh_from_db()
    test_case.assertEquals(task.status, Task.Status.REVIEWING)

    with patch('orchestra.utils.task_lifecycle._is_review_needed',
               return_value=False):
        submit_task(task.id, {'test': 'reviewer_accept'},
                    TaskAssignment.SnapshotType.ACCEPT, workers['reviewer'], 0)

    # Modify snapshot with correct datetime
    reviewer_assignment.refresh_from_db()
    (reviewer_assignment.snapshots['snapshots'][1]['datetime']
     ) = times['reviewer_accept']
    reviewer_assignment.save()

    task.refresh_from_db()
    test_case.assertEquals(task.status, Task.Status.COMPLETE)
    test_case.assertEquals(task.assignments.count(), 2)
    for assignment in task.assignments.all():
        test_case.assertEquals(assignment.status,
                               TaskAssignment.Status.SUBMITTED)
        test_case.assertEquals(len(assignment.snapshots['snapshots']), 2)
    return task
Exemple #44
0
    def test_preassign_workers(self):
        project = self.projects['assignment_policy']

        # Create first task in test project
        create_subsequent_tasks(project)
        self.assertEquals(project.tasks.count(), 1)
        # Assign initial task to worker 0
        initial_task = assign_task(self.workers[0].id,
                                   project.tasks.first().id)
        # Submit task; next task should be created
        with patch('orchestra.utils.task_lifecycle._is_review_needed',
                   return_value=False):
            initial_task = submit_task(initial_task.id, {},
                                       Iteration.Status.REQUESTED_REVIEW,
                                       self.workers[0])
        self.assertEquals(project.tasks.count(), 2)
        related_task = project.tasks.exclude(id=initial_task.id).first()
        # Worker 0 not certified for related tasks, so should not have been
        # auto-assigned
        self.assertEquals(related_task.assignments.count(), 0)
        self.assertEquals(related_task.status, Task.Status.AWAITING_PROCESSING)

        # Reset project
        project.tasks.all().delete()

        # Create first task in test project
        create_subsequent_tasks(project)
        self.assertEquals(project.tasks.count(), 1)
        # Assign initial task to worker 0
        initial_task = assign_task(self.workers[0].id,
                                   project.tasks.first().id)
        # Submit task; verify we use the reviewer assignment policy
        mock_preassign_workers = MagicMock(return_value=initial_task)
        patch_path = 'orchestra.utils.task_lifecycle._preassign_workers'
        with patch(patch_path, new=mock_preassign_workers):
            initial_task = submit_task(initial_task.id, {},
                                       Iteration.Status.REQUESTED_REVIEW,
                                       self.workers[0])
            mock_preassign_workers.assert_called_once_with(
                initial_task, AssignmentPolicyType.REVIEWER)

        # Reset project
        project.tasks.all().delete()

        # Create first task in test project
        create_subsequent_tasks(project)
        self.assertEquals(project.tasks.count(), 1)
        # Assign initial task to worker 4
        initial_task = assign_task(self.workers[4].id,
                                   project.tasks.first().id)
        # Submit task; next task should be created
        with patch('orchestra.utils.task_lifecycle._is_review_needed',
                   return_value=False):
            initial_task = submit_task(initial_task.id, {},
                                       Iteration.Status.REQUESTED_REVIEW,
                                       self.workers[4])
        self.assertEquals(project.tasks.count(), 2)
        related_task = project.tasks.exclude(id=initial_task.id).first()
        # Worker 4 is certified for related task and should have been assigned
        self.assertEquals(related_task.assignments.count(), 1)
        self.assertEquals(related_task.status, Task.Status.PROCESSING)
        self.assertTrue(related_task.is_worker_assigned(self.workers[4]))

        # Reset project
        project.tasks.all().delete()
Exemple #45
0
    def test_assign_task(self):
        entry_task = TaskFactory(project=self.projects['base_test_project'],
                                 status=Task.Status.AWAITING_PROCESSING,
                                 step=self.test_step)

        # No iterations should be present for task
        self.assertEqual(
            Iteration.objects.filter(assignment__task=entry_task).count(), 0)

        # Assign entry-level task to entry-level worker
        entry_task = assign_task(self.workers[0].id, entry_task.id)
        self.assertTrue(entry_task.is_worker_assigned(self.workers[0]))
        self.assertEqual(entry_task.status, Task.Status.PROCESSING)

        self.assertEqual(entry_task.assignments.count(), 1)
        entry_assignment = entry_task.assignments.first()

        # A single iteration was created for the assignment
        self.assertEqual(entry_assignment.iterations.count(), 1)
        self.assertEqual(
            Iteration.objects.filter(assignment__task=entry_task).count(), 1)
        self.assertEqual(entry_assignment.iterations.first().start_datetime,
                         entry_assignment.start_datetime)

        # Attempt to assign task which isn't awaiting a new assignment
        invalid = (Task.Status.PROCESSING, Task.Status.ABORTED,
                   Task.Status.REVIEWING, Task.Status.COMPLETE,
                   Task.Status.POST_REVIEW_PROCESSING)
        for status in invalid:
            invalid_status_task = Task.objects.create(
                project=self.projects['base_test_project'],
                status=status,
                step=self.test_step)

            with self.assertRaises(TaskAssignmentError):
                invalid_status_task = assign_task(self.workers[0].id,
                                                  invalid_status_task.id)

        # Attempt to assign review task to worker already in review hierarchy
        review_task = Task.objects.create(
            project=self.projects['base_test_project'],
            status=Task.Status.PENDING_REVIEW,
            step=self.test_step)
        test_data = {'test_assign': True}
        TaskAssignmentFactory(worker=self.workers[1],
                              task=review_task,
                              status=TaskAssignment.Status.SUBMITTED,
                              in_progress_task_data=test_data)

        with self.assertRaises(TaskAssignmentError):
            assign_task(self.workers[1].id, review_task.id)
        self.assertEqual(
            current_assignment(review_task).in_progress_task_data, test_data)

        # Attempt to assign review task to worker not certified for task
        with self.assertRaises(WorkerCertificationError):
            assign_task(self.workers[2].id, review_task.id)
        self.assertEqual(
            current_assignment(review_task).in_progress_task_data, test_data)

        # Assign review task to review worker
        self.assertEquals(review_task.assignments.count(), 1)
        review_task = assign_task(self.workers[3].id, review_task.id)
        self.assertEquals(review_task.assignments.count(), 2)

        reviewer_assignment = current_assignment(review_task)
        self.assertEqual(reviewer_assignment.worker, self.workers[3])
        self.assertEqual(reviewer_assignment.in_progress_task_data, test_data)
        self.assertEquals(reviewer_assignment.iterations.count(), 1)
        self.assertEqual(reviewer_assignment.iterations.first().start_datetime,
                         reviewer_assignment.start_datetime)

        self.assertEquals(review_task.status, Task.Status.REVIEWING)
Exemple #46
0
    def test_task_form_save(self):
        """
        Test task form save for new, human and machine tasks
        """
        workflow_version = self.workflow_versions['test_workflow']
        human_step = self.workflow_steps[workflow_version.slug]['step1']
        project = ProjectFactory(workflow_version=workflow_version)

        # Add new task to project
        form = TaskForm({'project': project.id,
                         'status': Task.Status.AWAITING_PROCESSING,
                         'step': human_step.id,
                         'start_datetime': timezone.now()})
        form.is_valid()
        self.assertTrue(form.is_valid())
        task = form.save()
        self.assertFalse(task.assignments.exists())

        # Add new task to project and assign to entry_level worker (0)
        form = TaskForm({'project': project.id,
                         'status': Task.Status.AWAITING_PROCESSING,
                         'step': human_step.id,
                         'start_datetime': timezone.now()})
        self.assertTrue(form.is_valid())
        form.cleaned_data['currently_assigned_to'] = self.workers[0].id
        task = form.save()
        self.assertTrue(is_worker_assigned_to_task(self.workers[0],
                                                   task))
        self.assertEquals(assignment_history(task).count(), 1)
        self.assertTrue(task.assignments.exists())
        self.assertEquals(task.status, Task.Status.PROCESSING)

        # Render task with preexisting entry_level assignment (0) and reassign
        # to another entry_level worker (4)
        form = TaskForm(model_to_dict(task), instance=task)
        self.assertEquals(form.fields['currently_assigned_to'].initial,
                          self.workers[0].id)
        form.is_valid()
        self.assertTrue(form.is_valid())
        form.cleaned_data['currently_assigned_to'] = self.workers[4].id
        task = form.save()
        self.assertTrue(is_worker_assigned_to_task(self.workers[4],
                                                   task))
        self.assertEquals(assignment_history(task).count(), 1)
        self.assertEquals(task.status, Task.Status.PROCESSING)

        # Submit task
        with patch('orchestra.utils.task_lifecycle._is_review_needed',
                   return_value=True):
            task = submit_task(task.id, {},
                               TaskAssignment.SnapshotType.SUBMIT,
                               self.workers[4], 0)

        # Assign to reviewer (1) and reassign to another reviewer (3)
        task = assign_task(self.workers[1].id, task.id)
        self.assertTrue(task.status, Task.Status.REVIEWING)
        self.assertTrue(is_worker_assigned_to_task(self.workers[1],
                                                   task))
        task = Task.objects.get(id=task.id)
        form = TaskForm(model_to_dict(task), instance=task)
        self.assertEquals(form.fields['currently_assigned_to'].initial,
                          self.workers[1].id)
        self.assertTrue(form.is_valid())
        form.cleaned_data['currently_assigned_to'] = self.workers[3].id
        task = form.save()
        self.assertTrue(is_worker_assigned_to_task(self.workers[3],
                                                   task))
        self.assertEquals(assignment_history(task).count(), 2)
        self.assertEquals(task.status, Task.Status.REVIEWING)

        # Attempt to reassign to non-certified worker (2)
        form = TaskForm(model_to_dict(task), instance=task)
        self.assertTrue(form.is_valid())
        form.cleaned_data['currently_assigned_to'] = self.workers[2].id
        with self.assertRaises(WorkerCertificationError):
            form.save()
Exemple #47
0
    def test_task_form_save(self):
        """
        Test task form save for new, human and machine tasks
        """
        # Workflow steps are hard-coded on `choices` for `Project` models
        # regardless of `settings.py`.  Once we move workflows back into the
        # database, we should use the test workflows rather than the production
        # ones in `settings.py.`  Until then, the hack below suffices.
        workflows = get_workflows()
        test_workflow_slug = 'website_design'
        workflow = workflows[test_workflow_slug]
        human_steps = {step_slug: step
                       for step_slug, step in workflow.steps.items()
                       if step.worker_type == Step.WorkerType.HUMAN}
        step_slug, step = human_steps.popitem()
        project = ProjectFactory(workflow_slug=test_workflow_slug)
        for certification_slug in step.required_certifications:
            certification = CertificationFactory(slug=certification_slug)
            for uname in (0, 1, 3, 6):
                WorkerCertificationFactory(
                    certification=certification,
                    worker=self.workers[uname],
                    role=WorkerCertification.Role.ENTRY_LEVEL)
            for uname in (3, 6):
                WorkerCertificationFactory(
                    certification=certification,
                    worker=self.workers[uname],
                    role=WorkerCertification.Role.REVIEWER)

        # Add new task to project
        form = TaskForm({'project': project.id,
                         'status': Task.Status.AWAITING_PROCESSING,
                         'step_slug': step_slug})
        form.is_valid()
        self.assertTrue(form.is_valid())
        task = form.save()
        self.assertFalse(task.assignments.exists())

        # Add new task to project and assign to entry_level worker (0)
        form = TaskForm({'project': project.id,
                         'status': Task.Status.AWAITING_PROCESSING,
                         'step_slug': step_slug})
        self.assertTrue(form.is_valid())
        form.cleaned_data['currently_assigned_to'] = self.workers[0].id
        task = form.save()
        self.assertTrue(is_worker_assigned_to_task(self.workers[0],
                                                   task))
        self.assertEquals(assignment_history(task).count(), 1)
        self.assertTrue(task.assignments.exists())
        self.assertEquals(task.status, Task.Status.PROCESSING)

        # Render task with preexisting entry_level assignment (0) and reassign
        # to another entry_level worker (1)
        form = TaskForm(model_to_dict(task), instance=task)
        self.assertEquals(form.fields['currently_assigned_to'].initial,
                          self.workers[0].id)
        form.is_valid()
        self.assertTrue(form.is_valid())
        form.cleaned_data['currently_assigned_to'] = self.workers[1].id
        task = form.save()
        self.assertTrue(is_worker_assigned_to_task(self.workers[1],
                                                   task))
        self.assertEquals(assignment_history(task).count(), 1)
        self.assertEquals(task.status, Task.Status.PROCESSING)

        # Submit task
        with patch('orchestra.utils.task_lifecycle._is_review_needed',
                   return_value=True):
            task = submit_task(task.id, {},
                               TaskAssignment.SnapshotType.SUBMIT,
                               self.workers[1], 0)

        # Assign to reviewer (3) and reassign to another reviewer (6)
        task = assign_task(self.workers[3].id, task.id)
        self.assertTrue(task.status, Task.Status.REVIEWING)
        self.assertTrue(is_worker_assigned_to_task(self.workers[3],
                                                   task))
        task = Task.objects.get(id=task.id)
        form = TaskForm(model_to_dict(task), instance=task)
        self.assertEquals(form.fields['currently_assigned_to'].initial,
                          self.workers[3].id)
        self.assertTrue(form.is_valid())
        form.cleaned_data['currently_assigned_to'] = self.workers[6].id
        task = form.save()
        self.assertTrue(is_worker_assigned_to_task(self.workers[6],
                                                   task))
        self.assertEquals(assignment_history(task).count(), 2)
        self.assertEquals(task.status, Task.Status.REVIEWING)

        # Attempt to reassign to non-certified worker (2)
        form = TaskForm(model_to_dict(task), instance=task)
        self.assertTrue(form.is_valid())
        form.cleaned_data['currently_assigned_to'] = self.workers[2].id
        with self.assertRaises(WorkerCertificationError):
            form.save()
    def test_assign_task(self):
        # Assign entry-level task to entry-level worker
        entry_tasks = Task.objects.filter(
            status=Task.Status.AWAITING_PROCESSING)
        self.assertEquals(entry_tasks.count(), 1)
        entry_task = entry_tasks.first()
        entry_task = assign_task(self.workers[0].id, entry_task.id)
        self.assertTrue(is_worker_assigned_to_task(self.workers[0],
                                                   entry_task))
        self.assertEquals(entry_task.status, Task.Status.PROCESSING)
        self.assertEquals(entry_task.assignments.count(), 1)

        # Attempt to reassign task to same worker
        with self.assertRaises(TaskAssignmentError):
            entry_task = assign_task(self.workers[0].id, entry_task.id)
        self.assertTrue(is_worker_assigned_to_task(self.workers[0],
                                                   entry_task))
        self.assertEquals(entry_task.status, Task.Status.PROCESSING)
        self.assertEquals(entry_task.assignments.count(), 1)

        # Reassign entry-level task to another entry-level worker
        entry_task = assign_task(self.workers[1].id, entry_task.id)
        self.assertFalse(is_worker_assigned_to_task(self.workers[0],
                                                    entry_task))
        self.assertTrue(is_worker_assigned_to_task(self.workers[1],
                                                   entry_task))
        self.assertEquals(entry_task.assignments.count(), 1)
        self.assertEquals(entry_task.status, Task.Status.PROCESSING)

        # Assign review task to review worker
        review_tasks = Task.objects.filter(status=Task.Status.PENDING_REVIEW)
        self.assertEquals(review_tasks.count(), 1)
        review_task = review_tasks.first()
        self.assertEquals(review_task.assignments.count(), 1)
        review_task = assign_task(self.workers[1].id, review_task.id)
        self.assertEquals(review_task.assignments.count(), 2)
        self.assertEqual(current_assignment(review_task).worker,
                         self.workers[1])
        self.assertEquals(review_task.status, Task.Status.REVIEWING)

        # Attempt to reassign review task to entry-level worker
        with self.assertRaises(WorkerCertificationError):
            review_task = assign_task(self.workers[0].id, review_task.id)
        self.assertEquals(review_task.assignments.count(), 2)
        self.assertEqual(current_assignment(review_task).worker,
                         self.workers[1])
        self.assertEquals(review_task.status, Task.Status.REVIEWING)

        # Reassign review task to another reviewer
        review_task = assign_task(self.workers[3].id, review_task.id)
        self.assertEquals(review_task.assignments.count(), 2)
        self.assertEqual(current_assignment(review_task).worker,
                         self.workers[3])
        self.assertEquals(review_task.status, Task.Status.REVIEWING)

        # Reassign rejected entry-level task to another entry-level worker
        reject_entry_tasks = Task.objects.filter(
            status=Task.Status.POST_REVIEW_PROCESSING,
            project=self.projects['reject_entry_proj'])
        self.assertEquals(reject_entry_tasks.count(), 1)
        reject_entry_task = reject_entry_tasks.first()
        reject_entry_task = assign_task(self.workers[5].id,
                                        reject_entry_task.id)
        self.assertFalse(is_worker_assigned_to_task(self.workers[4],
                                                    reject_entry_task))
        self.assertTrue(is_worker_assigned_to_task(self.workers[5],
                                                   reject_entry_task))
        self.assertEquals(reject_entry_task.status,
                          Task.Status.POST_REVIEW_PROCESSING)
        self.assertEquals(reject_entry_task.assignments.count(), 2)
        # In-progress data preserved after successful reassign
        self.assertEquals((current_assignment(reject_entry_task)
                           .in_progress_task_data),
                          {'test_key': 'test_value'})

        # Attempt to reassign rejected review task to entry-level worker
        reject_tasks = Task.objects.filter(
            status=Task.Status.POST_REVIEW_PROCESSING,
            project=self.projects['reject_rev_proj'])
        self.assertEquals(reject_tasks.count(), 1)
        reject_review_task = reject_tasks.first()
        with self.assertRaises(WorkerCertificationError):
            reject_review_task = assign_task(self.workers[4].id,
                                             reject_review_task.id)
        self.assertFalse(is_worker_assigned_to_task(self.workers[4],
                                                    reject_review_task))
        self.assertTrue(is_worker_assigned_to_task(self.workers[6],
                                                   reject_review_task))
        self.assertEquals(reject_review_task.status,
                          Task.Status.POST_REVIEW_PROCESSING)
        self.assertEquals(reject_review_task.assignments.count(), 3)

        # Reassign reviewer post-review task to another reviewer
        reject_review_task = assign_task(self.workers[8].id,
                                         reject_review_task.id)
        self.assertFalse(is_worker_assigned_to_task(self.workers[6],
                                                    reject_review_task))
        self.assertTrue(is_worker_assigned_to_task(self.workers[8],
                                                   reject_review_task))
        self.assertEquals(reject_review_task.status,
                          Task.Status.POST_REVIEW_PROCESSING)
        self.assertEquals(reject_review_task.assignments.count(), 3)

        # Attempt to reassign aborted task
        aborted_tasks = Task.objects.filter(status=Task.Status.ABORTED)
        self.assertEquals(aborted_tasks.count(), 1)
        aborted_task = aborted_tasks.first()
        with self.assertRaises(TaskStatusError):
            aborted_task = assign_task(self.workers[5].id, aborted_task.id)
        self.assertEquals(aborted_task.assignments.count(), 1)
        self.assertEqual(current_assignment(aborted_task).worker,
                         self.workers[4])
    def test_preassign_workers(self):
        project = self.projects['assignment_policy']

        # Create first task in test project
        create_subsequent_tasks(project)
        self.assertEquals(project.tasks.count(), 1)
        # Assign initial task to worker 0
        initial_task = assign_task(self.workers[0].id,
                                   project.tasks.first().id)
        # Submit task; next task should be created
        with patch('orchestra.utils.task_lifecycle._is_review_needed',
                   return_value=False):
            initial_task = submit_task(initial_task.id, {},
                                       Iteration.Status.REQUESTED_REVIEW,
                                       self.workers[0])
        self.assertEquals(project.tasks.count(), 2)
        related_task = project.tasks.exclude(id=initial_task.id).first()
        # Worker 0 not certified for related tasks, so should not have been
        # auto-assigned
        self.assertEquals(related_task.assignments.count(), 0)
        self.assertEquals(related_task.status, Task.Status.AWAITING_PROCESSING)

        # Reset project
        project.tasks.all().delete()

        # Create first task in test project
        create_subsequent_tasks(project)
        self.assertEquals(project.tasks.count(), 1)
        # Assign initial task to worker 0
        initial_task = assign_task(self.workers[0].id,
                                   project.tasks.first().id)
        # Submit task; verify we use the reviewer assignment policy
        mock_preassign_workers = MagicMock(return_value=initial_task)
        patch_path = 'orchestra.utils.task_lifecycle._preassign_workers'
        with patch(patch_path, new=mock_preassign_workers):
            initial_task = submit_task(initial_task.id, {},
                                       Iteration.Status.REQUESTED_REVIEW,
                                       self.workers[0])
            mock_preassign_workers.assert_called_once_with(
                initial_task, AssignmentPolicyType.REVIEWER)

        # Reset project
        project.tasks.all().delete()

        # Create first task in test project
        create_subsequent_tasks(project)
        self.assertEquals(project.tasks.count(), 1)
        # Assign initial task to worker 4
        initial_task = assign_task(self.workers[4].id,
                                   project.tasks.first().id)
        # Submit task; next task should be created
        with patch('orchestra.utils.task_lifecycle._is_review_needed',
                   return_value=False):
            initial_task = submit_task(initial_task.id, {},
                                       Iteration.Status.REQUESTED_REVIEW,
                                       self.workers[4])
        self.assertEquals(project.tasks.count(), 2)
        related_task = project.tasks.exclude(id=initial_task.id).first()
        # Worker 4 is certified for related task and should have been assigned
        self.assertEquals(related_task.assignments.count(), 1)
        self.assertEquals(related_task.status, Task.Status.PROCESSING)
        self.assertTrue(
            related_task.is_worker_assigned(self.workers[4]))
    def test_notify_status_change(self):
        project = self.projects['empty_project']
        internal_name = settings.SLACK_INTERNAL_NOTIFICATION_CHANNEL.strip('#')
        internal_groups = [
            group for group in self.slack.groups.list().body['groups']
            if group['name'] == internal_name]
        internal_group_id = internal_groups[0]['id']
        internal_slack_messages = self.slack.get_messages(internal_group_id)
        experts_slack_messages = self.slack.get_messages(
            project.slack_group_id)

        def _validate_slack_messages(message_stub):
            """
            Check that correct slack message was sent if API key present.
            """
            self.assertIn(message_stub, internal_slack_messages.pop())
            self.assertIn(message_stub, experts_slack_messages.pop())

        task = TaskFactory(project=project,
                           step_slug=self.test_step_slug,
                           status=Task.Status.AWAITING_PROCESSING)

        # Entry-level worker picks up task
        self.assertEquals(task.status, Task.Status.AWAITING_PROCESSING)
        task = assign_task(self.workers[0].id, task.id)
        self.assertTrue(is_worker_assigned_to_task(self.workers[0], task))

        # Notification should be sent to entry-level worker
        self.assertEquals(len(self.mail.inbox), 1)
        notification = self.mail.inbox.pop()
        self.assertEquals(notification['recipient'],
                          self.workers[0].user.email)
        self.assertEquals(notification['subject'],
                          "You've been assigned to a new task!")

        _validate_slack_messages('Task has been picked up by a worker.')
        self.assertEquals(len(internal_slack_messages), 0)
        self.assertEquals(len(experts_slack_messages), 0)

        with patch('orchestra.utils.task_lifecycle._is_review_needed',
                   return_value=True):
            # Entry-level worker submits task
            task = submit_task(task.id, {}, TaskAssignment.SnapshotType.SUBMIT,
                               self.workers[0], 0)

        self.assertEquals(task.status, Task.Status.PENDING_REVIEW)
        # Notification should be sent to entry-level worker
        self.assertEquals(len(self.mail.inbox), 1)
        notification = self.mail.inbox.pop()
        self.assertEquals(notification['recipient'],
                          self.workers[0].user.email)
        self.assertEquals(notification['subject'],
                          'Your task is under review!')

        _validate_slack_messages('Task is awaiting review.')
        self.assertEquals(len(internal_slack_messages), 0)
        self.assertEquals(len(experts_slack_messages), 0)

        # Reviewer picks up task
        task = assign_task(self.workers[1].id, task.id)
        self.assertEquals(task.status, Task.Status.REVIEWING)
        # No notification should be sent
        self.assertEquals(len(self.mail.inbox), 0)

        _validate_slack_messages('Task is under review.')
        self.assertEquals(len(internal_slack_messages), 0)
        self.assertEquals(len(experts_slack_messages), 0)

        # Reviewer rejects task
        task = submit_task(task.id, {}, TaskAssignment.SnapshotType.REJECT,
                           self.workers[1], 0)
        self.assertEquals(task.status, Task.Status.POST_REVIEW_PROCESSING)
        # Notification should be sent to original worker
        self.assertEquals(len(self.mail.inbox), 1)
        notification = self.mail.inbox.pop()
        self.assertEquals(notification['recipient'],
                          self.workers[0].user.email)
        self.assertEquals(notification['subject'],
                          'Your task has been returned')

        _validate_slack_messages('Task was returned by reviewer.')
        self.assertEquals(len(internal_slack_messages), 0)
        self.assertEquals(len(experts_slack_messages), 0)

        # Entry-level worker resubmits task
        task = submit_task(task.id, {}, TaskAssignment.SnapshotType.SUBMIT,
                           self.workers[0], 0)
        self.assertEquals(task.status, Task.Status.REVIEWING)
        # Notification should be sent to reviewer
        self.assertEquals(len(self.mail.inbox), 1)
        notification = self.mail.inbox.pop()
        self.assertEquals(notification['recipient'],
                          self.workers[1].user.email)
        self.assertEquals(notification['subject'],
                          'A task is ready for re-review!')

        _validate_slack_messages('Task is under review.')
        self.assertEquals(len(internal_slack_messages), 0)
        self.assertEquals(len(experts_slack_messages), 0)

        # First reviewer accepts task
        with patch('orchestra.utils.task_lifecycle._is_review_needed',
                   return_value=True):
            task = submit_task(task.id, {}, TaskAssignment.SnapshotType.ACCEPT,
                               self.workers[1], 0)
        self.assertEquals(task.status, Task.Status.PENDING_REVIEW)
        # Notification should be sent to first reviewer
        self.assertEquals(len(self.mail.inbox), 1)
        notification = self.mail.inbox.pop()
        self.assertEquals(notification['recipient'],
                          self.workers[1].user.email)
        self.assertEquals(notification['subject'],
                          'Your task is under review!')

        _validate_slack_messages('Task is awaiting review.')
        self.assertEquals(len(internal_slack_messages), 0)
        self.assertEquals(len(experts_slack_messages), 0)

        # Second reviewer picks up task
        task = assign_task(self.workers[3].id, task.id)
        self.assertEquals(task.status, Task.Status.REVIEWING)
        # No notification should be sent
        self.assertEquals(len(self.mail.inbox), 0)

        _validate_slack_messages('Task is under review.')
        self.assertEquals(len(internal_slack_messages), 0)
        self.assertEquals(len(experts_slack_messages), 0)

        # Second reviewer rejects task
        task = submit_task(task.id, {}, TaskAssignment.SnapshotType.REJECT,
                           self.workers[3], 0)
        self.assertEquals(task.status, Task.Status.POST_REVIEW_PROCESSING)
        # Notification should be sent to first reviewer
        self.assertEquals(len(self.mail.inbox), 1)
        notification = self.mail.inbox.pop()
        self.assertEquals(notification['recipient'],
                          self.workers[1].user.email)
        self.assertEquals(notification['subject'],
                          'Your task has been returned')

        _validate_slack_messages('Task was returned by reviewer.')
        self.assertEquals(len(internal_slack_messages), 0)
        self.assertEquals(len(experts_slack_messages), 0)

        # First reviewer resubmits task
        task = submit_task(task.id, {}, TaskAssignment.SnapshotType.SUBMIT,
                           self.workers[1], 0)
        self.assertEquals(task.status, Task.Status.REVIEWING)
        # Notification should be sent to second reviewer
        self.assertEquals(len(self.mail.inbox), 1)
        notification = self.mail.inbox.pop()
        self.assertEquals(notification['recipient'],
                          self.workers[3].user.email)
        self.assertEquals(notification['subject'],
                          'A task is ready for re-review!')

        _validate_slack_messages('Task is under review.')
        self.assertEquals(len(internal_slack_messages), 0)
        self.assertEquals(len(experts_slack_messages), 0)

        # Second reviewer accepts task; task is complete
        with patch('orchestra.utils.task_lifecycle._is_review_needed',
                   return_value=False):
            task = submit_task(task.id, {}, TaskAssignment.SnapshotType.ACCEPT,
                               self.workers[3], 0)
        self.assertEquals(task.status, Task.Status.COMPLETE)

        # Notification should be sent to all workers on task
        self.assertEquals(len(self.mail.inbox), 3)
        recipients = {mail['recipient'] for mail in self.mail.inbox}
        subjects = {mail['subject'] for mail in self.mail.inbox}
        self.assertEquals(recipients,
                          {self.workers[uid].user.email for uid in (0, 1, 3)})
        self.assertEquals(subjects, {'Task complete!'})
        self.mail.clear()

        _validate_slack_messages('Task has been completed.')
        self.assertEquals(len(internal_slack_messages), 0)
        self.assertEquals(len(experts_slack_messages), 0)

        # End project
        end_project(task.project.id)
        task = Task.objects.get(id=task.id)
        self.assertEquals(task.status, Task.Status.ABORTED)

        # Notification should be sent to all workers on task
        self.assertEquals(len(self.mail.inbox), 3)
        recipients = {mail['recipient'] for mail in self.mail.inbox}
        subjects = {mail['subject'] for mail in self.mail.inbox}
        self.assertEquals(recipients,
                          {self.workers[uid].user.email for uid in (0, 1, 3)})
        self.assertEquals(subjects,
                          {'A task you were working on has been ended'})
        self.mail.clear()

        for task in project.tasks.all():
            _validate_slack_messages('Task has been aborted.')
        self.assertEquals(len(internal_slack_messages), 0)
        self.assertEquals(len(experts_slack_messages), 0)
    def test_assign_task(self):
        entry_task = TaskFactory(
            project=self.projects['base_test_project'],
            status=Task.Status.AWAITING_PROCESSING,
            step=self.test_step)

        # Assign entry-level task to entry-level worker
        entry_task = assign_task(self.workers[0].id, entry_task.id)
        self.assertTrue(is_worker_assigned_to_task(self.workers[0],
                                                   entry_task))
        self.assertEquals(entry_task.status, Task.Status.PROCESSING)
        self.assertEquals(entry_task.assignments.count(), 1)

        # Attempt to assign task which isn't awaiting a new assignment
        invalid = (Task.Status.PROCESSING, Task.Status.ABORTED,
                   Task.Status.REVIEWING, Task.Status.COMPLETE,
                   Task.Status.POST_REVIEW_PROCESSING)
        for status in invalid:
            invalid_status_task = Task.objects.create(
                project=self.projects['base_test_project'],
                status=status,
                step=self.test_step)

            with self.assertRaises(TaskAssignmentError):
                invalid_status_task = assign_task(
                    self.workers[0].id, invalid_status_task.id)

        # Attempt to assign review task to worker already in review hierarchy
        review_task = Task.objects.create(
            project=self.projects['base_test_project'],
            status=Task.Status.PENDING_REVIEW,
            step=self.test_step)
        test_data = {'test_assign': True}
        TaskAssignmentFactory(
            worker=self.workers[1],
            task=review_task,
            status=TaskAssignment.Status.SUBMITTED,
            in_progress_task_data=test_data,
            snapshots=empty_snapshots())

        with self.assertRaises(TaskAssignmentError):
            assign_task(self.workers[1].id, review_task.id)
        self.assertEqual(
            current_assignment(review_task).in_progress_task_data, test_data)

        # Attempt to assign review task to worker not certified for task
        with self.assertRaises(WorkerCertificationError):
            assign_task(self.workers[2].id, review_task.id)
        self.assertEqual(
            current_assignment(review_task).in_progress_task_data, test_data)

        # Assign review task to review worker
        self.assertEquals(review_task.assignments.count(), 1)
        review_task = assign_task(self.workers[3].id, review_task.id)
        self.assertEquals(review_task.assignments.count(), 2)
        self.assertEqual(
            current_assignment(review_task).worker, self.workers[3])
        self.assertEqual(
            current_assignment(review_task).in_progress_task_data, test_data)
        self.assertEquals(
            review_task.status, Task.Status.REVIEWING)
Exemple #52
0
def handle_staffing_response(worker, staffing_request_inquiry_id,
                             is_available=False):
    """
    Args:
        worker (orchestra.models.Worker):
            Worker instance that responsed to a staffing request inquiry
        staffing_request_inquiry_id (int):
            Id of a staffing_request_inquiry that is associated with a response
        is_available (boolean):
            Boolean that tells whether worker accepted an inquiry or not
    Returns:
        response (orchestra.models.StaffingResponse):
            StaffingResponse object that has been created for the worker
    """

    # TODO(kkamalov): add proper docstring
    staffing_request_inquiry = get_object_or_None(
        StaffingRequestInquiry,
        communication_preference__worker=worker,
        id=staffing_request_inquiry_id
    )
    if staffing_request_inquiry is None:
        return None

    response = (StaffingResponse.objects
                .filter(request_inquiry=staffing_request_inquiry))
    if response.exists():
        response = response.first()
        if not is_available and response.is_winner:
            raise StaffingResponseException(
                'Cannot reject after accepting the task')

        response.is_available = is_available

    else:
        response = StaffingResponse.objects.create(
            request_inquiry=staffing_request_inquiry,
            is_available=is_available)

    if (is_available and
            not StaffingResponse.objects.filter(
                request_inquiry__request=staffing_request_inquiry.request,
                is_winner=True).exists()):
        response.is_winner = True
        request = staffing_request_inquiry.request

        task_assignment = get_object_or_None(
            TaskAssignment,
            task=request.task,
            assignment_counter=request.required_role_counter
        )

        # if task assignment exists then reassign
        if task_assignment is not None:
            reassign_assignment(worker.id, task_assignment.id)
        # otherwise assign task
        else:
            assign_task(worker.id, request.task.id)

    response.save()
    check_responses_complete(staffing_request_inquiry.request)
    return response
Exemple #53
0
    def test_notify_status_change(self):
        project = self.projects['empty_project']
        internal_name = settings.SLACK_INTERNAL_NOTIFICATION_CHANNEL.strip('#')
        internal_groups = [
            group for group in self.slack.groups.list().body['groups']
            if group['name'] == internal_name
        ]
        internal_group_id = internal_groups[0]['id']
        internal_slack_messages = self.slack.get_messages(internal_group_id)
        experts_slack_messages = self.slack.get_messages(
            project.slack_group_id)

        def _validate_slack_messages(message_stub):
            """
            Check that correct slack message was sent if API key present.
            """
            self.assertIn(message_stub, internal_slack_messages.pop())
            self.assertIn(message_stub, experts_slack_messages.pop())

        task = TaskFactory(project=project,
                           step=self.test_step,
                           status=Task.Status.AWAITING_PROCESSING)

        # Entry-level worker picks up task
        self.assertEqual(task.status, Task.Status.AWAITING_PROCESSING)
        task = assign_task(self.workers[0].id, task.id)
        self.assertTrue(task.is_worker_assigned(self.workers[0]))

        # Notification should be sent to entry-level worker
        self.assertEqual(len(self.mail.inbox), 1)
        notification = self.mail.inbox.pop()
        self.assertEqual(notification['recipient'], self.workers[0].user.email)
        self.assertEqual(notification['subject'],
                         "You've been assigned to a new task!")

        _validate_slack_messages('Task has been picked up by a worker.')
        self.assertEqual(len(internal_slack_messages), 0)
        self.assertEqual(len(experts_slack_messages), 0)

        with patch('orchestra.utils.task_lifecycle._is_review_needed',
                   return_value=True):
            # Entry-level worker submits task
            task = submit_task(task.id, {}, Iteration.Status.REQUESTED_REVIEW,
                               self.workers[0])

        self.assertEqual(task.status, Task.Status.PENDING_REVIEW)
        # Notification should be sent to entry-level worker
        self.assertEqual(len(self.mail.inbox), 1)
        notification = self.mail.inbox.pop()
        self.assertEqual(notification['recipient'], self.workers[0].user.email)
        self.assertEqual(notification['subject'], 'Your task is under review!')

        _validate_slack_messages('Task is awaiting review.')
        self.assertEqual(len(internal_slack_messages), 0)
        self.assertEqual(len(experts_slack_messages), 0)

        # Reviewer picks up task
        task = assign_task(self.workers[1].id, task.id)
        self.assertEqual(task.status, Task.Status.REVIEWING)
        # No notification should be sent
        self.assertEqual(len(self.mail.inbox), 0)

        _validate_slack_messages('Task is under review.')
        self.assertEqual(len(internal_slack_messages), 0)
        self.assertEqual(len(experts_slack_messages), 0)

        # Reviewer rejects task
        task = submit_task(task.id, {}, Iteration.Status.PROVIDED_REVIEW,
                           self.workers[1])
        self.assertEqual(task.status, Task.Status.POST_REVIEW_PROCESSING)
        # Notification should be sent to original worker
        self.assertEqual(len(self.mail.inbox), 1)
        notification = self.mail.inbox.pop()
        self.assertEqual(notification['recipient'], self.workers[0].user.email)
        self.assertEqual(notification['subject'],
                         'Your task has been returned')

        _validate_slack_messages('Task was returned by reviewer.')
        self.assertEqual(len(internal_slack_messages), 0)
        self.assertEqual(len(experts_slack_messages), 0)

        # Entry-level worker resubmits task
        task = submit_task(task.id, {}, Iteration.Status.REQUESTED_REVIEW,
                           self.workers[0])
        self.assertEqual(task.status, Task.Status.REVIEWING)
        # Notification should be sent to reviewer
        self.assertEqual(len(self.mail.inbox), 1)
        notification = self.mail.inbox.pop()
        self.assertEqual(notification['recipient'], self.workers[1].user.email)
        self.assertEqual(notification['subject'],
                         'A task is ready for re-review!')

        _validate_slack_messages('Task is under review.')
        self.assertEqual(len(internal_slack_messages), 0)
        self.assertEqual(len(experts_slack_messages), 0)

        # First reviewer accepts task
        with patch('orchestra.utils.task_lifecycle._is_review_needed',
                   return_value=True):
            task = submit_task(task.id, {}, Iteration.Status.REQUESTED_REVIEW,
                               self.workers[1])
        self.assertEqual(task.status, Task.Status.PENDING_REVIEW)
        # Notification should be sent to first reviewer
        self.assertEqual(len(self.mail.inbox), 1)
        notification = self.mail.inbox.pop()
        self.assertEqual(notification['recipient'], self.workers[1].user.email)
        self.assertEqual(notification['subject'], 'Your task is under review!')

        _validate_slack_messages('Task is awaiting review.')
        self.assertEqual(len(internal_slack_messages), 0)
        self.assertEqual(len(experts_slack_messages), 0)

        # Second reviewer picks up task
        task = assign_task(self.workers[3].id, task.id)
        self.assertEqual(task.status, Task.Status.REVIEWING)
        # No notification should be sent
        self.assertEqual(len(self.mail.inbox), 0)

        _validate_slack_messages('Task is under review.')
        self.assertEqual(len(internal_slack_messages), 0)
        self.assertEqual(len(experts_slack_messages), 0)

        # Second reviewer rejects task
        task = submit_task(task.id, {}, Iteration.Status.PROVIDED_REVIEW,
                           self.workers[3])
        self.assertEqual(task.status, Task.Status.POST_REVIEW_PROCESSING)
        # Notification should be sent to first reviewer
        self.assertEqual(len(self.mail.inbox), 1)
        notification = self.mail.inbox.pop()
        self.assertEqual(notification['recipient'], self.workers[1].user.email)
        self.assertEqual(notification['subject'],
                         'Your task has been returned')

        _validate_slack_messages('Task was returned by reviewer.')
        self.assertEqual(len(internal_slack_messages), 0)
        self.assertEqual(len(experts_slack_messages), 0)

        # First reviewer resubmits task
        task = submit_task(task.id, {}, Iteration.Status.REQUESTED_REVIEW,
                           self.workers[1])
        self.assertEqual(task.status, Task.Status.REVIEWING)
        # Notification should be sent to second reviewer
        self.assertEqual(len(self.mail.inbox), 1)
        notification = self.mail.inbox.pop()
        self.assertEqual(notification['recipient'], self.workers[3].user.email)
        self.assertEqual(notification['subject'],
                         'A task is ready for re-review!')

        _validate_slack_messages('Task is under review.')
        self.assertEqual(len(internal_slack_messages), 0)
        self.assertEqual(len(experts_slack_messages), 0)

        # Second reviewer accepts task; task is complete
        with patch('orchestra.utils.task_lifecycle._is_review_needed',
                   return_value=False):
            task = submit_task(task.id, {}, Iteration.Status.REQUESTED_REVIEW,
                               self.workers[3])
        self.assertEqual(task.status, Task.Status.COMPLETE)

        # Notification should be sent to all workers on task
        self.assertEqual(len(self.mail.inbox), 3)
        recipients = {mail['recipient'] for mail in self.mail.inbox}
        subjects = {mail['subject'] for mail in self.mail.inbox}
        self.assertEqual(recipients,
                         {self.workers[uid].user.email
                          for uid in (0, 1, 3)})
        self.assertEqual(subjects, {'Task complete!'})
        self.mail.clear()

        _validate_slack_messages('Task has been completed.')
        self.assertEqual(len(internal_slack_messages), 0)
        self.assertEqual(len(experts_slack_messages), 0)

        # End project
        end_project(task.project.id)
        task = Task.objects.get(id=task.id)
        self.assertEqual(task.status, Task.Status.ABORTED)

        # Notification should be sent to all workers on task
        self.assertEqual(len(self.mail.inbox), 3)
        recipients = {mail['recipient'] for mail in self.mail.inbox}
        subjects = {mail['subject'] for mail in self.mail.inbox}
        self.assertEqual(recipients,
                         {self.workers[uid].user.email
                          for uid in (0, 1, 3)})
        self.assertEqual(subjects,
                         {'A task you were working on has been ended'})
        self.mail.clear()

        for task in project.tasks.all():
            _validate_slack_messages('Task has been aborted.')
        self.assertEqual(len(internal_slack_messages), 0)
        self.assertEqual(len(experts_slack_messages), 0)
    def test_assign_task(self):
        entry_task = TaskFactory(
            project=self.projects['base_test_project'],
            status=Task.Status.AWAITING_PROCESSING,
            step=self.test_step)

        # No iterations should be present for task
        self.assertEqual(
            Iteration.objects.filter(assignment__task=entry_task).count(), 0)

        # Assign entry-level task to entry-level worker
        entry_task = assign_task(self.workers[0].id, entry_task.id)
        self.assertTrue(entry_task.is_worker_assigned(self.workers[0]))
        self.assertEqual(entry_task.status, Task.Status.PROCESSING)

        self.assertEqual(entry_task.assignments.count(), 1)
        entry_assignment = entry_task.assignments.first()

        # A single iteration was created for the assignment
        self.assertEqual(entry_assignment.iterations.count(), 1)
        self.assertEqual(
            Iteration.objects.filter(assignment__task=entry_task).count(), 1)
        self.assertEqual(
            entry_assignment.iterations.first().start_datetime,
            entry_assignment.start_datetime)

        # Attempt to assign task which isn't awaiting a new assignment
        invalid = (Task.Status.PROCESSING, Task.Status.ABORTED,
                   Task.Status.REVIEWING, Task.Status.COMPLETE,
                   Task.Status.POST_REVIEW_PROCESSING)
        for status in invalid:
            invalid_status_task = Task.objects.create(
                project=self.projects['base_test_project'],
                status=status,
                step=self.test_step)

            with self.assertRaises(TaskAssignmentError):
                invalid_status_task = assign_task(
                    self.workers[0].id, invalid_status_task.id)

        # Attempt to assign review task to worker already in review hierarchy
        review_task = Task.objects.create(
            project=self.projects['base_test_project'],
            status=Task.Status.PENDING_REVIEW,
            step=self.test_step)
        test_data = {'test_assign': True}
        TaskAssignmentFactory(
            worker=self.workers[1],
            task=review_task,
            status=TaskAssignment.Status.SUBMITTED,
            in_progress_task_data=test_data)

        with self.assertRaises(TaskAssignmentError):
            assign_task(self.workers[1].id, review_task.id)
        self.assertEqual(
            current_assignment(review_task).in_progress_task_data, test_data)

        # Attempt to assign review task to worker not certified for task
        with self.assertRaises(WorkerCertificationError):
            assign_task(self.workers[2].id, review_task.id)
        self.assertEqual(
            current_assignment(review_task).in_progress_task_data, test_data)

        # Assign review task to review worker
        self.assertEquals(review_task.assignments.count(), 1)
        review_task = assign_task(self.workers[3].id, review_task.id)
        self.assertEquals(review_task.assignments.count(), 2)

        reviewer_assignment = current_assignment(review_task)
        self.assertEqual(
            reviewer_assignment.worker, self.workers[3])
        self.assertEqual(
            reviewer_assignment.in_progress_task_data, test_data)
        self.assertEquals(
            reviewer_assignment.iterations.count(), 1)
        self.assertEqual(
            reviewer_assignment.iterations.first().start_datetime,
            reviewer_assignment.start_datetime)

        self.assertEquals(
            review_task.status, Task.Status.REVIEWING)
Exemple #55
0
def setup_complete_task(test_case):
    # Microseconds are truncated when manually saving models
    test_start = timezone.now().replace(microsecond=0)
    times = {
        'awaiting_pickup': test_start,
        'entry_pickup': test_start + timedelta(hours=1),
        'entry_submit': test_start + timedelta(hours=2),
        'reviewer_pickup': test_start + timedelta(hours=3),
        'reviewer_reject': test_start + timedelta(hours=4),
        'entry_resubmit': test_start + timedelta(hours=5),
        'reviewer_accept': test_start + timedelta(hours=6),
    }

    task = TaskFactory(
        project=test_case.projects['empty_project'],
        status=Task.Status.AWAITING_PROCESSING,
        step=test_case.test_step,
        start_datetime=times['awaiting_pickup'])

    workers = {
        'entry': test_case.workers[0],
        'reviewer': test_case.workers[1]
    }

    assign_task(workers['entry'].id, task.id)

    task.refresh_from_db()
    test_case.assertEqual(task.status, Task.Status.PROCESSING)

    submit_task(
        task.id, {'test': 'entry_submit'},
        Iteration.Status.REQUESTED_REVIEW,
        workers['entry'])

    task.refresh_from_db()
    test_case.assertEqual(task.status, Task.Status.PENDING_REVIEW)

    assign_task(workers['reviewer'].id, task.id)
    reviewer_assignment = task.assignments.get(
        worker=workers['reviewer'])

    # Modify assignment with correct datetime
    reviewer_assignment.start_datetime = times['reviewer_pickup']
    reviewer_assignment.save()

    task.refresh_from_db()
    test_case.assertEqual(task.status, Task.Status.REVIEWING)

    submit_task(
        task.id, {'test': 'reviewer_reject'},
        Iteration.Status.PROVIDED_REVIEW,
        workers['reviewer'])

    task.refresh_from_db()
    test_case.assertEqual(task.status, Task.Status.POST_REVIEW_PROCESSING)

    submit_task(
        task.id, {'test': 'entry_resubmit'},
        Iteration.Status.REQUESTED_REVIEW,
        workers['entry'])

    task.refresh_from_db()
    test_case.assertEqual(task.status, Task.Status.REVIEWING)

    with patch('orchestra.utils.task_lifecycle._is_review_needed',
               return_value=False):
        submit_task(
            task.id, {'test': 'reviewer_accept'},
            Iteration.Status.REQUESTED_REVIEW,
            workers['reviewer'])

    task.refresh_from_db()
    test_case.assertEqual(task.status, Task.Status.COMPLETE)
    test_case.assertEqual(task.assignments.count(), 2)
    for assignment in task.assignments.all():
        test_case.assertEqual(
            assignment.status, TaskAssignment.Status.SUBMITTED)
        test_case.assertEqual(assignment.iterations.count(), 2)

    # Modify assignments with correct datetime
    new_datetime_labels = ('entry_pickup', 'reviewer_pickup')
    for i, assignment in enumerate(assignment_history(task).all()):
        assignment.start_datetime = times[new_datetime_labels[i]]
        assignment.save()

    # Modify iterations with correct datetime
    new_datetime_labels = (
        ('entry_pickup', 'entry_submit'),
        ('reviewer_pickup', 'reviewer_reject'),
        ('reviewer_reject', 'entry_resubmit'),
        ('entry_resubmit', 'reviewer_accept')
    )
    new_datetimes = [
        (times[start_label], times[end_label])
        for start_label, end_label in new_datetime_labels]

    for i, iteration in enumerate(get_iteration_history(task)):
        iteration.start_datetime, iteration.end_datetime = new_datetimes[i]
        iteration.save()

    verify_iterations(task.id)

    return task
Exemple #56
0
def setup_complete_task(test_case, times):
    task = TaskFactory(
        project=test_case.projects['empty_project'],
        status=Task.Status.AWAITING_PROCESSING,
        step=test_case.test_step,
        start_datetime=times['awaiting_pickup'])

    workers = {
        'entry': test_case.workers[0],
        'reviewer': test_case.workers[1]
    }

    assign_task(workers['entry'].id, task.id)
    entry_assignment = task.assignments.get(worker=workers['entry'])
    # Modify assignment with correct datetime
    entry_assignment.start_datetime = times['entry_pickup']
    entry_assignment.save()

    task.refresh_from_db()
    test_case.assertEquals(task.status, Task.Status.PROCESSING)

    submit_task(
        task.id, {'test': 'entry_submit'},
        TaskAssignment.SnapshotType.SUBMIT,
        workers['entry'], 0)

    task.refresh_from_db()
    test_case.assertEquals(task.status, Task.Status.PENDING_REVIEW)

    # Modify snapshot with correct datetime
    entry_assignment.refresh_from_db()
    (entry_assignment.snapshots
        ['snapshots'][0]['datetime']) = times['entry_submit']
    entry_assignment.save()

    assign_task(workers['reviewer'].id, task.id)
    reviewer_assignment = task.assignments.get(
        worker=workers['reviewer'])
    # Modify assignment with correct datetime
    reviewer_assignment.start_datetime = times['reviewer_pickup']
    reviewer_assignment.save()

    task.refresh_from_db()
    test_case.assertEquals(task.status, Task.Status.REVIEWING)

    submit_task(
        task.id, {'test': 'reviewer_reject'},
        TaskAssignment.SnapshotType.REJECT,
        workers['reviewer'], 0)
    # Modify snapshot with correct datetime
    reviewer_assignment.refresh_from_db()
    (reviewer_assignment.snapshots
        ['snapshots'][0]['datetime']) = times['reviewer_reject']
    reviewer_assignment.save()

    task.refresh_from_db()
    test_case.assertEquals(task.status, Task.Status.POST_REVIEW_PROCESSING)

    submit_task(
        task.id, {'test': 'entry_resubmit'},
        TaskAssignment.SnapshotType.SUBMIT,
        workers['entry'], 0)
    # Modify snapshot with correct datetime
    entry_assignment.refresh_from_db()
    (entry_assignment.snapshots
        ['snapshots'][1]['datetime']) = times['entry_resubmit']
    entry_assignment.save()

    task.refresh_from_db()
    test_case.assertEquals(task.status, Task.Status.REVIEWING)

    with patch('orchestra.utils.task_lifecycle._is_review_needed',
               return_value=False):
        submit_task(
            task.id, {'test': 'reviewer_accept'},
            TaskAssignment.SnapshotType.ACCEPT,
            workers['reviewer'], 0)

    # Modify snapshot with correct datetime
    reviewer_assignment.refresh_from_db()
    (reviewer_assignment.snapshots
        ['snapshots'][1]['datetime']) = times['reviewer_accept']
    reviewer_assignment.save()

    task.refresh_from_db()
    test_case.assertEquals(task.status, Task.Status.COMPLETE)
    test_case.assertEquals(task.assignments.count(), 2)
    for assignment in task.assignments.all():
        test_case.assertEquals(
            assignment.status, TaskAssignment.Status.SUBMITTED)
        test_case.assertEquals(len(assignment.snapshots['snapshots']), 2)
    return task