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)
def test_send_staffing_request_priorities(self, mock_slack): worker2 = WorkerFactory(staffing_priority=-1) CommunicationPreferenceFactory( worker=worker2, communication_type=( CommunicationPreference.CommunicationType .NEW_TASK_AVAILABLE.value)) worker3 = WorkerFactory(staffing_priority=1) CommunicationPreferenceFactory( worker=worker3, communication_type=( CommunicationPreference.CommunicationType .NEW_TASK_AVAILABLE.value)) request = StaffBotRequestFactory() self.assertEquals(request.status, StaffBotRequest.Status.PROCESSING.value) # Workers should be contacted in priority order. excluded = [] for worker in (worker3, self.worker, worker2): send_staffing_requests(worker_batch_size=1, frequency=timedelta(minutes=0)) inquiries = ( StaffingRequestInquiry.objects .filter(request=request) .exclude(communication_preference__worker__in=excluded)) for inquiry in inquiries: self.assertEquals(worker.id, inquiry.communication_preference.worker.id) excluded.append(worker)
def test_send_staffing_request_priorities(self, mock_slack): worker2 = WorkerFactory() WorkerCertificationFactory(worker=worker2, staffing_priority=-1) CommunicationPreferenceFactory( worker=worker2, communication_type=(CommunicationPreference.CommunicationType. NEW_TASK_AVAILABLE.value)) worker3 = WorkerFactory() WorkerCertificationFactory(worker=worker3, staffing_priority=1) CommunicationPreferenceFactory( worker=worker3, communication_type=(CommunicationPreference.CommunicationType. NEW_TASK_AVAILABLE.value)) request = StaffBotRequestFactory() self.assertEqual(request.status, StaffBotRequest.Status.PROCESSING.value) # Workers should be contacted in priority order. excluded = [] for worker in (worker3, self.worker, worker2): send_staffing_requests(worker_batch_size=1, frequency=timedelta(minutes=0)) inquiries = (StaffingRequestInquiry.objects.filter( request=request).exclude( communication_preference__worker__in=excluded)) for inquiry in inquiries: self.assertEqual(worker.id, inquiry.communication_preference.worker.id) excluded.append(worker)
def _test_staffing_requests(self, worker, task, command, can_slack=False, can_mail=False): StaffBotRequest.objects.all().delete() bot = StaffBot() communication_type = (CommunicationPreference.CommunicationType .NEW_TASK_AVAILABLE.value) communication_preference = CommunicationPreference.objects.get( worker=worker, communication_type=communication_type) communication_preference.methods.slack = can_slack communication_preference.methods.email = can_mail communication_preference.save() data = get_mock_slack_data( text=command, user_id=self.worker.slack_user_id) bot.dispatch(data) send_staffing_requests(worker_batch_size=20) self.assertEquals(StaffingRequestInquiry.objects.filter( communication_preference__worker_id=worker, request__task=task).count(), can_slack + can_mail)
def _test_staffing_requests(self, worker, task, command, can_slack=False, can_mail=False): StaffBotRequest.objects.all().delete() bot = StaffBot() communication_type = (CommunicationPreference.CommunicationType .NEW_TASK_AVAILABLE.value) communication_preference = CommunicationPreference.objects.get( worker=worker, communication_type=communication_type) communication_preference.methods.slack = can_slack communication_preference.methods.email = can_mail communication_preference.save() data = get_mock_slack_data( text=command, user_id=self.worker.slack_user_id) bot.dispatch(data) send_staffing_requests(worker_batch_size=20, frequency=timedelta(minutes=0)) self.assertEquals(StaffingRequestInquiry.objects.filter( communication_preference__worker_id=worker, request__task=task).count(), can_slack + can_mail)
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()
def test_send_staffing_requests(self, mock_slack): worker2 = WorkerFactory() CommunicationPreferenceFactory( worker=worker2, communication_type=( CommunicationPreference.CommunicationType .NEW_TASK_AVAILABLE.value)) WorkerCertificationFactory( worker=worker2, certification=self.certification ) request = StaffBotRequestFactory() request.task.step.required_certifications.add(self.certification) self.assertEqual(request.status, StaffBotRequest.Status.SENDING_INQUIRIES.value) send_staffing_requests(worker_batch_size=1, frequency=timedelta(minutes=0)) mock_slack.assert_not_called() request.refresh_from_db() self.assertEqual(request.status, StaffBotRequest.Status.SENDING_INQUIRIES.value) # Inquiries increase by two because we send a Slack and an # email notification. self.assertEqual( StaffingRequestInquiry.objects.filter(request=request).count(), 2) send_staffing_requests(worker_batch_size=1, frequency=timedelta(minutes=0)) self.assertEqual(mock_slack.call_count, 1) mock_slack.reset() request.refresh_from_db() self.assertEqual(request.status, StaffBotRequest.Status.SENDING_INQUIRIES.value) self.assertEqual( StaffingRequestInquiry.objects.filter(request=request).count(), 4) # marked as closed and no new request inquiries sent. send_staffing_requests(worker_batch_size=1, frequency=timedelta(minutes=0)) self.assertTrue(mock_slack.called) request.refresh_from_db() self.assertEqual(request.status, StaffBotRequest.Status.DONE_SENDING_INQUIRIES.value) self.assertEqual( StaffingRequestInquiry.objects.filter(request=request).count(), 4)
def test_send_staffing_requests(self, mock_slack): worker2 = WorkerFactory() CommunicationPreferenceFactory( worker=worker2, communication_type=( CommunicationPreference.CommunicationType .NEW_TASK_AVAILABLE.value)) request = StaffBotRequestFactory() self.assertEquals(request.status, StaffBotRequest.Status.PROCESSING.value) send_staffing_requests(worker_batch_size=1, frequency=timedelta(minutes=0)) mock_slack.assert_not_called() request.refresh_from_db() self.assertEquals(request.status, StaffBotRequest.Status.PROCESSING.value) # Inquiries increase by two because we send a Slack and an # email notification. self.assertEquals( StaffingRequestInquiry.objects.filter(request=request).count(), 2) send_staffing_requests(worker_batch_size=1, frequency=timedelta(minutes=0)) mock_slack.assert_not_called() request.refresh_from_db() self.assertEquals(request.status, StaffBotRequest.Status.PROCESSING.value) self.assertEquals( StaffingRequestInquiry.objects.filter(request=request).count(), 4) # marked as complete and no new request inquiries sent. send_staffing_requests(worker_batch_size=1, frequency=timedelta(minutes=0)) self.assertTrue(mock_slack.called) request.refresh_from_db() self.assertEquals(request.status, StaffBotRequest.Status.COMPLETE.value) self.assertEquals( StaffingRequestInquiry.objects.filter(request=request).count(), 4)
def test_send_staffing_requests(self, mock_slack): worker2 = WorkerFactory() StaffingRequestInquiryFactory( communication_preference__worker=worker2, communication_preference__communication_type=( CommunicationPreference.CommunicationType .NEW_TASK_AVAILABLE.value), request__task__step__is_human=True ) request = StaffBotRequestFactory() self.assertEquals(request.status, StaffBotRequest.Status.PROCESSING.value) send_staffing_requests(worker_batch_size=1) mock_slack.assert_not_called() request.refresh_from_db() self.assertEquals(request.status, StaffBotRequest.Status.PROCESSING.value) self.assertEquals( StaffingRequestInquiry.objects.filter(request=request).count(), 2) send_staffing_requests(worker_batch_size=1) mock_slack.assert_not_called() request.refresh_from_db() self.assertEquals(request.status, StaffBotRequest.Status.PROCESSING.value) self.assertEquals( StaffingRequestInquiry.objects.filter(request=request).count(), 4) # marked as complete and no new request inquiries sent. send_staffing_requests(worker_batch_size=1) self.assertTrue(mock_slack.called) request.refresh_from_db() self.assertEquals(request.status, StaffBotRequest.Status.COMPLETE.value) self.assertEquals( StaffingRequestInquiry.objects.filter(request=request).count(), 4)
def test_send_staffing_requests_parameters(self, mock_slack): for idx in range(5): worker = WorkerFactory() CommunicationPreferenceFactory( worker=worker, communication_type=( CommunicationPreference.CommunicationType .NEW_TASK_AVAILABLE.value)) WorkerCertificationFactory( worker=worker, certification=self.certification ) # Make a new request and turn off the global request, as the # global one has an inquiry on it already. request = StaffBotRequestFactory(task__step__is_human=True) request.task.step.required_certifications.add(self.certification) self.staffing_request_inquiry.request.status = ( StaffBotRequest.Status.DONE_SENDING_INQUIRIES) send_staffing_requests(worker_batch_size=1, frequency=timedelta(minutes=20)) # Inquiries increase by two because we send a Slack and an # email notification. `last_inquiry_sent` is None on new # tasks, so we send this batch regardless of frequency. self.assertEqual( StaffingRequestInquiry.objects.filter(request=request).count(), 2) send_staffing_requests(worker_batch_size=2, frequency=timedelta(minutes=20)) # Don't send more inquiries, since it hasn't been 20 minutes. self.assertEqual( StaffingRequestInquiry.objects.filter(request=request).count(), 2) request.last_inquiry_sent = timezone.now() - timedelta(minutes=21) request.save() send_staffing_requests(worker_batch_size=2, frequency=timedelta(minutes=20)) # Send two email and two slack inquiries since it's been 21 minutes. self.assertEqual( StaffingRequestInquiry.objects.filter(request=request).count(), 6) send_staffing_requests(worker_batch_size=10, frequency=timedelta(minutes=20)) # Don't send more inquiries, since it hasn't been 20 minutes. self.assertEqual( StaffingRequestInquiry.objects.filter(request=request).count(), 6) request.last_inquiry_sent = timezone.now() - timedelta(minutes=21) request.save() send_staffing_requests(worker_batch_size=10, frequency=timedelta(minutes=20)) # Send remaining inquiries, since enough time has passed. self.assertEqual( StaffingRequestInquiry.objects.filter(request=request).count(), 12) request.last_inquiry_sent = timezone.now() - timedelta(minutes=21) request.save() send_staffing_requests(worker_batch_size=10, frequency=timedelta(minutes=20)) # We're all out of workers to whom we'd like to send inquiries. self.assertEqual( StaffingRequestInquiry.objects.filter(request=request).count(), 12)
def test_get_available_request(self, mock_slack): # Close all open requests so new worker doesn't receive them. send_staffing_requests(worker_batch_size=2, frequency=timedelta(minutes=0)) self.assertEqual(len(get_available_requests(self.worker)), 1) worker2 = WorkerFactory() CommunicationPreferenceFactory( worker=worker2, communication_type=( CommunicationPreference.CommunicationType .NEW_TASK_AVAILABLE.value)) WorkerCertificationFactory( worker=worker2, certification=self.certification ) request1 = StaffBotRequestFactory(task__step__is_human=True) request1.task.step.required_certifications.add(self.certification) request2 = StaffBotRequestFactory(task__step__is_human=True) request2.task.step.required_certifications.add(self.certification) send_staffing_requests(worker_batch_size=2, frequency=timedelta(minutes=0)) inquiry1 = ( StaffingRequestInquiry.objects .filter(communication_preference__worker=self.worker) .filter(request=request1).first()) inquiry2 = ( StaffingRequestInquiry.objects .filter(communication_preference__worker=worker2) .filter(request=request2).first()) # `self.worker` now has three available tasks, whereas `worker2` # just has access to the two new tasks. available_requests = get_available_requests(self.worker) self.assertEqual(len(available_requests), 3) self.assertEqual(len(get_available_requests(worker2)), 2) # Tasks should be sorted by start_datetime in ascending order. first_task, second_task, third_task = ( available_requests[0]['task'], available_requests[1]['task'], available_requests[2]['task']) self.assertLess( first_task.start_datetime, second_task.start_datetime) self.assertLess( second_task.start_datetime, third_task.start_datetime) # `self.worker` will lose an available task (they accept it), # whereas `worker2` is unchanged. handle_staffing_response( self.worker, self.staffing_request_inquiry.id, is_available=True) self.assertEqual(len(get_available_requests(self.worker)), 2) self.assertEqual(len(get_available_requests(worker2)), 2) # `self.worker` will lose an available task (they ignore it), # whereas `worker2` is unchanged. handle_staffing_response( self.worker, inquiry1.id, is_available=False) self.assertEqual(len(get_available_requests(self.worker)), 1) self.assertEqual(len(get_available_requests(worker2)), 2) # `worker2` takes a task. handle_staffing_response( worker2, inquiry2.id, is_available=True) self.assertEqual(len(get_available_requests(self.worker)), 0) self.assertEqual(len(get_available_requests(worker2)), 1)
def send_staffing_requests_periodically(): send_staffing_requests()
def test_send_staffing_requests_parameters(self, mock_slack): for idx in range(5): CommunicationPreferenceFactory( worker=WorkerFactory(), communication_type=( CommunicationPreference.CommunicationType .NEW_TASK_AVAILABLE.value)) # Make a new request and turn off the global request, as the # global one has an inquiry on it already. request = StaffBotRequestFactory(task__step__is_human=True) self.staffing_request_inquiry.request.status = ( StaffBotRequest.Status.COMPLETE) send_staffing_requests(worker_batch_size=1, frequency=timedelta(minutes=20)) # Inquiries increase by two because we send a Slack and an # email notification. `last_inquiry_sent` is None on new # tasks, so we send this batch regardless of frequency. self.assertEquals( StaffingRequestInquiry.objects.filter(request=request).count(), 2) send_staffing_requests(worker_batch_size=2, frequency=timedelta(minutes=20)) # Don't send more inquiries, since it hasn't been 20 minutes. self.assertEquals( StaffingRequestInquiry.objects.filter(request=request).count(), 2) request.last_inquiry_sent = timezone.now() - timedelta(minutes=21) request.save() send_staffing_requests(worker_batch_size=2, frequency=timedelta(minutes=20)) # Send two email and two slack inquiries since it's been 21 minutes. self.assertEquals( StaffingRequestInquiry.objects.filter(request=request).count(), 6) send_staffing_requests(worker_batch_size=10, frequency=timedelta(minutes=20)) # Don't send more inquiries, since it hasn't been 20 minutes. self.assertEquals( StaffingRequestInquiry.objects.filter(request=request).count(), 6) request.last_inquiry_sent = timezone.now() - timedelta(minutes=21) request.save() send_staffing_requests(worker_batch_size=10, frequency=timedelta(minutes=20)) # Send remaining inquiries, since enough time has passed. self.assertEquals( StaffingRequestInquiry.objects.filter(request=request).count(), 12) request.last_inquiry_sent = timezone.now() - timedelta(minutes=21) request.save() send_staffing_requests(worker_batch_size=10, frequency=timedelta(minutes=20)) # We're all out of workers to whom we'd like to send inquiries. self.assertEquals( StaffingRequestInquiry.objects.filter(request=request).count(), 12)
def test_get_available_request(self, mock_slack): # Complete all open requests so new worker doesn't receive them. send_staffing_requests(worker_batch_size=2, frequency=timedelta(minutes=0)) self.assertEquals(len(get_available_requests(self.worker)), 1) worker2 = WorkerFactory() CommunicationPreferenceFactory( worker=worker2, communication_type=( CommunicationPreference.CommunicationType .NEW_TASK_AVAILABLE.value)) request1 = StaffBotRequestFactory(task__step__is_human=True) request2 = StaffBotRequestFactory(task__step__is_human=True) send_staffing_requests(worker_batch_size=2, frequency=timedelta(minutes=0)) inquiry1 = ( StaffingRequestInquiry.objects .filter(communication_preference__worker=self.worker) .filter(request=request1).first()) inquiry2 = ( StaffingRequestInquiry.objects .filter(communication_preference__worker=worker2) .filter(request=request2).first()) # `self.worker` now has three available tasks, whereas `worker2` # just has access to the two new tasks. available_requests = get_available_requests(self.worker) self.assertEquals(len(available_requests), 3) self.assertEquals(len(get_available_requests(worker2)), 2) # Tasks should be sorted by start_datetime in ascending order. first_task, second_task, third_task = ( available_requests[0]['task'], available_requests[1]['task'], available_requests[2]['task']) self.assertLess( first_task.start_datetime, second_task.start_datetime) self.assertLess( second_task.start_datetime, third_task.start_datetime) # `self.worker` will lose an available task (they accept it), # whereas `worker2` is unchanged. handle_staffing_response( self.worker, self.staffing_request_inquiry.id, is_available=True) self.assertEquals(len(get_available_requests(self.worker)), 2) self.assertEquals(len(get_available_requests(worker2)), 2) # `self.worker` will lose an available task (they ignore it), # whereas `worker2` is unchanged. handle_staffing_response( self.worker, inquiry1.id, is_available=False) self.assertEquals(len(get_available_requests(self.worker)), 1) self.assertEquals(len(get_available_requests(worker2)), 2) # `worker2` takes a task. handle_staffing_response( worker2, inquiry2.id, is_available=True) self.assertEquals(len(get_available_requests(self.worker)), 0) self.assertEquals(len(get_available_requests(worker2)), 1)