Beispiel #1
0
    def test_staff_command_errors(self):
        """
        Test that the staffing logic errors are raised during
        staff command.
        """
        bot = StaffBot()
        data = get_mock_slack_data(
            text='staff 999999999999',
            user_id=self.worker.slack_user_id)

        response = bot.dispatch(data)
        self.assertEqual(response['attachments'][0]['text'],
                         bot.task_does_not_exist_error.format('999999999999'))

        data['text'] = 'staff'
        response = bot.dispatch(data)
        self.assertTrue(bot.default_error_text in response.get('text'))

        task = TaskFactory(status=Task.Status.COMPLETE)
        data['text'] = 'staff {}'.format(task.id)
        response = bot.dispatch(data)
        self.assertEquals(response['attachments'][0]['text'],
                          bot.task_assignment_error
                          .format(task.id,
                                  'Status incompatible with new assignment'))
Beispiel #2
0
def get_available_requests(worker):
    # We want to show a worker only requests for which there is no
    # winner or for which they have not already replied.
    won_responses = StaffingResponse.objects.filter(is_winner=True)
    worker_provided_responses = StaffingResponse.objects.filter(
        request_inquiry__communication_preference__worker=worker)
    remaining_requests = (
        StaffBotRequest.objects
        .filter(inquiries__communication_preference__worker=worker)
        .exclude(inquiries__responses__in=won_responses)
        .exclude(inquiries__responses__in=worker_provided_responses)
        .distinct())
    inquiries = (
        StaffingRequestInquiry.objects
        .filter(request__in=remaining_requests)
        .filter(communication_preference__worker=worker)
        .order_by('request__task__start_datetime'))
    # Because we might send multiple request inquiries to the same
    # worker for the same request (e.g., email and slack), we
    # deduplicate the inquiries so that we will return at most one
    # inquiry's worth of content here.
    request_ids = set()
    contexts = []
    staffbot = StaffBot()
    for inquiry in inquiries:
        if inquiry.request.id in request_ids:
            continue
        request_ids.add(inquiry.request.id)
        metadata = staffbot.get_staffing_request_metadata(inquiry)
        metadata['detailed_description'] = markdown(
            metadata['detailed_description'])
        metadata['reject_url'] += '?next={}'.format(
            reverse('orchestra:communication:available_staffing_requests'))
        contexts.append(metadata)
    return contexts
Beispiel #3
0
    def test_commands(self):
        """
        Ensure that the bot can handle the following commands:
        /staffbot staff <task_id>
        /staffbot restaff <task_id> <username>

        This test only validates that the commands are processed, other
        tests verify the functionality of the command execution.
        """
        bot = StaffBot()

        # Test staff command
        mock_slack_data = get_mock_slack_data(
            text='staff 5',
            user_id=self.worker.slack_user_id)

        response = bot.dispatch(mock_slack_data)
        self.assertFalse(bot.default_error_text in response.get('text', ''))

        # Test the restaff command
        mock_slack_data['text'] = 'restaff 5 username'
        response = bot.dispatch(mock_slack_data)
        self.assertFalse(bot.default_error_text in response.get('text', ''))

        # Test we fail gracefully
        mock_slack_data['text'] = 'invalid command'
        response = bot.dispatch(mock_slack_data)
        self.assertTrue(bot.default_error_text in response.get('text', ''))
Beispiel #4
0
def get_available_requests(worker):
    # We want to show a worker only requests for which there is no
    # winner or for which they have not already replied.
    worker_provided_responses = StaffingResponse.objects.filter(
        request_inquiry__communication_preference__worker=worker)
    remaining_requests = (StaffBotRequest.objects.filter(
        inquiries__communication_preference__worker=worker
    ).exclude(status=StaffBotRequest.Status.CLOSED.value).exclude(
        task__status=Task.Status.COMPLETE).exclude(
            task__status=Task.Status.ABORTED).exclude(
                inquiries__responses__in=worker_provided_responses).distinct())
    inquiries = (StaffingRequestInquiry.objects.filter(
        request__in=remaining_requests).filter(
            communication_preference__worker=worker).order_by(
                'request__task__start_datetime'))
    # Because we might send multiple request inquiries to the same
    # worker for the same request (e.g., email and slack), we
    # deduplicate the inquiries so that we will return at most one
    # inquiry's worth of content here.
    request_ids = set()
    contexts = []
    staffbot = StaffBot()
    for inquiry in inquiries:
        if inquiry.request.id in request_ids:
            continue
        request_ids.add(inquiry.request.id)
        metadata = staffbot.get_staffing_request_metadata(inquiry)
        metadata['detailed_description'] = markdown(
            metadata['detailed_description'], extras=['target-blank-links'])
        metadata['reject_url'] += '?next={}'.format(
            reverse('orchestra:communication:available_staffing_requests'))
        contexts.append(metadata)
    return contexts
Beispiel #5
0
    def test_staff_command_errors(self):
        """
        Test that the staffing logic errors are raised during
        staff command.
        """
        bot = StaffBot()
        data = get_mock_slack_data(
            text='staff 999999999999',
            user_id=self.worker.slack_user_id)

        response = bot.dispatch(data)
        self.assertEqual(response['attachments'][0]['text'],
                         bot.task_does_not_exist_error.format('999999999999'))

        data['text'] = 'staff'
        response = bot.dispatch(data)
        self.assertTrue(bot.default_error_text in response.get('text'))

        task = TaskFactory(status=Task.Status.COMPLETE)
        data['text'] = 'staff {}'.format(task.id)
        response = bot.dispatch(data)
        self.assertEquals(response['attachments'][0]['text'],
                          bot.task_assignment_error
                          .format(task.id,
                                  'Status incompatible with new assignment'))
Beispiel #6
0
    def test_commands(self):
        """
        Ensure that the bot can handle the following commands:
        /staffbot staff <task_id>
        /staffbot restaff <task_id> <username>

        This test only validates that the commands are processed, other
        tests verify the functionality of the command execution.
        """
        bot = StaffBot()

        # Test staff command
        mock_slack_data = get_mock_slack_data(
            text='staff 5', user_id=self.worker.slack_user_id)

        response = bot.dispatch(mock_slack_data)
        self.assertFalse(bot.default_error_text in response.get('text', ''))

        # Test the restaff command
        mock_slack_data['text'] = 'restaff 5 username'
        response = bot.dispatch(mock_slack_data)
        self.assertFalse(bot.default_error_text in response.get('text', ''))

        # Test we fail gracefully
        mock_slack_data['text'] = 'invalid command'
        response = bot.dispatch(mock_slack_data)
        self.assertTrue(bot.default_error_text in response.get('text', ''))
Beispiel #7
0
    def test_restaff_command_errors(self):
        """
        Test that the staffing logic errors are raised during
        staff command.
        """
        bot = StaffBot()
        data = get_mock_slack_data(
            text='restaff 999999999999 unknown',
            user_id=self.worker.slack_user_id)

        response = bot.dispatch(data)
        self.assertEqual(response.get('text'),
                         bot.worker_does_not_exist.format('unknown'))

        worker = WorkerFactory(user__username='******')
        data['text'] = 'restaff 999999999999 slackusername'
        response = bot.dispatch(data)
        self.assertEqual(response.get('text'),
                         bot.task_does_not_exist_error.format('999999999999'))

        data['text'] = 'restaff'
        response = bot.dispatch(data)
        self.assertTrue(bot.default_error_text in response.get('text'))

        task = TaskFactory(status=Task.Status.COMPLETE)
        command = 'restaff {} {}'.format(task.id, worker.user.username)

        data['text'] = command
        response = bot.dispatch(data)
        self.assertEquals(response.get('text'),
                          (bot.task_assignment_does_not_exist_error
                           .format(worker.user.username, task.id)))
Beispiel #8
0
def remind_workers_about_available_tasks():
    staffbot = StaffBot()
    workers = Worker.objects.all()
    for worker in workers:
        requests = get_available_requests(worker)
        # TODO(kkamalov): send out reminder only if last request was sent
        # at least ORCHESTRA_STAFFBOT_MIN_FOLLOWUP_TIME ago
        if len(requests):
            staffbot.send_worker_tasks_available_reminder(worker)
Beispiel #9
0
    def setUp(self):
        super().setUp()
        setup_models(self)
        self.worker = self.workers[0]
        self.worker.user.is_superuser = True
        self.worker.user.save()

        self.request_client = RequestClient(username=self.worker.user.username,
                                            password='******')
        self.url = reverse('orchestra:bots:staffbot')
        self.staffbot = StaffBot()
Beispiel #10
0
 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)
Beispiel #11
0
def send_staffing_requests(
        worker_batch_size=settings.ORCHESTRA_STAFFBOT_WORKER_BATCH_SIZE,
        frequency=settings.ORCHESTRA_STAFFBOT_BATCH_FREQUENCY):
    staffbot = StaffBot()
    cutoff_datetime = timezone.now() - frequency
    requests = (StaffBotRequest.objects.filter(
        status=StaffBotRequest.Status.SENDING_INQUIRIES.value).filter(
            Q(last_inquiry_sent__isnull=True)
            | Q(last_inquiry_sent__lte=cutoff_datetime)))

    for request in requests:
        send_request_inquiries(staffbot, request, worker_batch_size)
Beispiel #12
0
 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)
Beispiel #13
0
    def test_staff_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 = TaskFactory()
        init_num_request = StaffBotRequest.objects.filter(task=task).count()
        self.assertEqual(init_num_request, 0)

        bot.staff(task.id)
        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 staff on the same task should close the previous request
        # and create a new one.
        bot.staff(task.id)
        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)
Beispiel #14
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)
Beispiel #15
0
def address_staffing_requests(
        worker_batch_size=settings.ORCHESTRA_STAFFBOT_WORKER_BATCH_SIZE,
        frequency=settings.ORCHESTRA_STAFFBOT_BATCH_FREQUENCY):
    staffbot = StaffBot()
    cutoff_datetime = timezone.now() - frequency
    requests = (StaffBotRequest.objects.filter(status__in=[
        StaffBotRequest.Status.SENDING_INQUIRIES.value,
        StaffBotRequest.Status.DONE_SENDING_INQUIRIES.value
    ]).filter(
        Q(last_inquiry_sent__isnull=True)
        | Q(last_inquiry_sent__lte=cutoff_datetime)).order_by(
            '-task__project__priority', 'created_at'))

    for request in requests:
        staff_or_send_request_inquiries(staffbot, request, worker_batch_size)
Beispiel #16
0
    def test_restaff_command_errors(self):
        """
        Test that the staffing logic errors are raised during
        staff command.
        """
        bot = StaffBot()
        command = 'restaff 999999999999 unknown'
        data = get_mock_slack_data(
            text=command,
            user_id=self.worker.slack_user_id)

        response = bot.dispatch(data)
        self.assertEqual(response.get('text'),
                         command)

        self.assertEqual(response['attachments'][0]['text'],
                         bot.worker_does_not_exist.format('unknown'))

        worker = WorkerFactory(user__username='******')
        data['text'] = 'restaff 999999999999 username'
        response = bot.dispatch(data)
        self.assertEqual(response['attachments'][0]['text'],
                         bot.task_does_not_exist_error.format('999999999999'))

        # making sure it works with slack username as well.
        worker.slack_username = '******'
        worker.save()
        data['text'] = 'restaff 999999999999 slackusername'
        response = bot.dispatch(data)
        self.assertEqual(response['attachments'][0]['text'],
                         bot.task_does_not_exist_error.format('999999999999'))

        data['text'] = 'restaff'
        response = bot.dispatch(data)
        self.assertTrue(bot.default_error_text in response.get('text'))

        task = TaskFactory(status=Task.Status.COMPLETE)
        command = 'restaff {} {}'.format(task.id, worker.user.username)

        data['text'] = command
        response = bot.dispatch(data)
        self.assertEquals(response['attachments'][0]['text'],
                          (bot.task_assignment_does_not_exist_error
                           .format(worker.user.username, task.id)))
Beispiel #17
0
def staff_task(request):
    data = load_encoded_json(request.body)
    errors = {}
    try:
        task = Task.objects.get(id=data.get('task_id'))
        request_cause = StaffBotRequest.RequestCause.USER.value
        bot = StaffBot()
        assignment = current_assignment(task)
        is_restaff = assignment is not None
        if is_restaff:
            username = assignment.worker.user.username
            bot.restaff(task.id, username, request_cause=request_cause)
        else:
            bot.staff(task.id, request_cause=request_cause)
    except Exception as e:
        raise BadRequest(e)
    success = len(errors) == 0
    return {'success': success, 'is_restaff': is_restaff}
Beispiel #18
0
def staffbot_autoassign(task, **kwargs):
    request_cause = StaffBotRequest.RequestCause.AUTOSTAFF.value
    bot = StaffBot()
    bot.staff(task.id, request_cause=request_cause)
    return task
Beispiel #19
0
 def test_assert_validate_error(self):
     bot = StaffBot()
     with self.assertRaises(SlackUserUnauthorized):
         mock_slack_data = get_mock_slack_data(text='staff 5')
         bot.dispatch(mock_slack_data)
Beispiel #20
0
    def test_get_staffing_request_messsage(self, mock_mail):
        def _task_factory(status, path):
            description_no_kwargs = {'path': path}
            return TaskFactory(
                status=status,
                step=StepFactory(
                    slug='stepslug',
                    description='the step',
                    detailed_description_function=description_no_kwargs),
                project__workflow_version__workflow__description=(
                    'the workflow'),
                project__short_description='the coolest project')

        # Test slack without review and with a detailed_description_function
        task = _task_factory(
            Task.Status.AWAITING_PROCESSING,
            'orchestra.tests.helpers.fixtures.get_detailed_description')
        staffing_request_inquiry = StaffingRequestInquiryFactory(
            communication_preference__worker__user__first_name='test-name',
            request__task=task)
        message = StaffBot()._get_staffing_request_message(
            staffing_request_inquiry,
            'communication/new_task_available_slack.txt')
        self.assertEqual(message, '''Hello test-name!

A new task is available for you to work on, if you'd like!  Here are the details:

Project: the workflow
Project description: the coolest project
Task: the step
Details: No text given stepslug

<http://127.0.0.1:8000/orchestra/communication/accept_staffing_request_inquiry/{}/|Accept the Task>
<http://127.0.0.1:8000/orchestra/communication/reject_staffing_request_inquiry/{}/|Ignore the Task>
<http://127.0.0.1:8000/orchestra/communication/available_staffing_requests/|View All Available Tasks>

'''.format(staffing_request_inquiry.id, staffing_request_inquiry.id))  # noqa

        # Test email with review and no detailed_description_function
        task = _task_factory(
            Task.Status.PENDING_REVIEW,
            'orchestra.bots.tests.test_staffbot._noop_details')
        staffing_request_inquiry = StaffingRequestInquiryFactory(
            communication_preference__worker__user__first_name='test-name2',
            request=StaffBotRequestFactory(task=task, required_role_counter=1))
        message = StaffBot()._get_staffing_request_message(
            staffing_request_inquiry,
            'communication/new_task_available_email.txt')
        self.assertEqual(message, '''Hello test-name2!

A new task is available for you to work on, if you'd like!  Here are the details:

Project: the workflow
Project description: the coolest project
Task: the step [Review]


<a href="http://127.0.0.1:8000/orchestra/communication/accept_staffing_request_inquiry/{}/">Accept the Task</a>
<a href="http://127.0.0.1:8000/orchestra/communication/reject_staffing_request_inquiry/{}/">Ignore the Task</a>
<a href="http://127.0.0.1:8000/orchestra/communication/available_staffing_requests/">View All Available Tasks</a>

'''.format(staffing_request_inquiry.id, staffing_request_inquiry.id))  # noqa

        # Test that we markdown things
        StaffBot()._send_staffing_request_by_mail('*****@*****.**', message)
        mock_mail.assert_called_once_with(
            'A new task is available for you',
            message,
            settings.ORCHESTRA_NOTIFICATIONS_FROM_EMAIL, ['*****@*****.**'],
            html_message=html_from_plaintext(message))
Beispiel #21
0
 def test_assert_validate_error(self):
     bot = StaffBot()
     with self.assertRaises(SlackUserUnauthorized):
         mock_slack_data = get_mock_slack_data(text='staff 5')
         bot.dispatch(mock_slack_data)