def message_project_team(request): """ Endpoint for sending arbitrary message to a project team. Payload example: {'message': 'Chat message', 'project_id': 'some-id-123'} """ data = load_encoded_json(request.body) try: message = data['message'] project_id = data['project_id'] project = Project.objects.get(id=project_id) except KeyError: text = ('An object with `message` and `project_id` attributes' ' should be supplied') raise BadRequest(text) except Project.DoesNotExist: raise BadRequest('No project for given id') if project.slack_group_id: message_experts_slack_group(project.slack_group_id, message) else: error_message = ( "The following project doesn't have slack_group_id: {}" ).format(project) raise BadRequest(error_message) return {'success': True}
def _send_request_inquiries(staffbot, request, worker_batch_size, worker_certifications): inquiries_sent = 0 required_role = get_role_from_counter(request.required_role_counter) contacted_workers = set() for certification in worker_certifications: worker = certification.worker if worker.id in contacted_workers: continue contacted_workers.add(worker.id) if _is_worker_assignable(worker, request.task, required_role): staffbot.send_task_to_worker(worker, request) inquiries_sent += 1 if inquiries_sent >= worker_batch_size: break # check whether all inquiries have been sent out. if inquiries_sent < worker_batch_size: message_experts_slack_group( request.task.project.slack_group_id, ('All staffing requests for task {} have been sent!'.format( request.task))) request.status = StaffBotRequest.Status.DONE_SENDING_INQUIRIES.value request.last_inquiry_sent = timezone.now() request.save()
def perform_update(self, serializer): todo = serializer.save() sender = Worker.objects.get( user=self.request.user).formatted_slack_username() message = '{} has marked `{}` as `{}`.'.format( sender, todo.description, 'complete' if todo.completed else 'incomplete') message_experts_slack_group(todo.task.project.slack_group_id, message)
def notify_single_todo_update(user, old_todo, todo): # To avoid Slack noise, only send updates for changed TODOs with # depth 0 (no parent) or 1 (no grandparent). sender = _get_sender(user) message = get_update_message(old_todo, todo, sender) if message and \ (not (todo.parent_todo and todo.parent_todo.parent_todo)): message_experts_slack_group(todo.project.slack_group_id, message)
def perform_create(self, serializer): todo = serializer.save() sender = Worker.objects.get( user=self.request.user).formatted_slack_username() recipients = ' & '.join(assignment.worker.formatted_slack_username() for assignment in todo.task.assignments.all() if assignment and assignment.worker) message = '{} has created a new todo `{}` for {}.'.format( sender, todo.description, recipients if recipients else '`{}`'.format(todo.task.step.slug)) message_experts_slack_group(todo.task.project.slack_group_id, message)
def check_responses_complete(request): # check all responses have been complete responses = StaffingResponse.objects.filter( request_inquiry__request=request) request_inquiries = StaffingRequestInquiry.objects.filter(request=request) if (responses.count() == request_inquiries.count() and not responses.filter(is_winner=True).exists()): # notify that all workers have rejected a task message_experts_slack_group( request.task.project.slack_group_id, ('No worker has accepted to work on task {}'.format(request.task)))
def check_responses_complete(request): # check all responses have been complete responses = StaffingResponse.objects.filter(request__request=request) request_inquiries = StaffingRequestInquiry.objects.filter( request=request) if (responses.count() == request_inquiries.count() and not responses.filter(is_winner=True).exists()): # notify that all workers have rejected a task message_experts_slack_group( request.task.project.slack_group_id, ('No worker has accepted to work on task {}' .format(request.task)))
def notify_todo_created(todo, user): sender = _get_sender(user) tasks = (task for task in todo.project.tasks.all() if task.step.slug == todo.step.slug) recipients = ' & '.join(assignment.worker.formatted_slack_username() for task in tasks for assignment in task.assignments.all() if assignment and assignment.worker) if sender: message = '{} has created a new todo `{}` for {}.'.format( sender, todo.title, recipients if recipients else '`{}`'.format(todo.step.slug)) else: message = 'A new todo `{}` was created for {}.'.format( todo.title, recipients if recipients else '`{}`'.format(todo.step.slug)) message_experts_slack_group(todo.project.slack_group_id, message)
def _handle(project, sanity_check, handler): handler_type = handler.get('type') handler_message = handler.get('message') handler_steps = handler.get('steps') if (handler_type != 'slack_project_channel' or not handler_message or not handler_steps): raise SanityBotError('Invalid handler: {}'.format(handler)) tasks = ( task for task in project.tasks.all() if task.step.slug in handler_steps) usernames = { assignment.worker.formatted_slack_username() for task in tasks for assignment in task.assignments.all() if assignment and assignment.worker } message = '{}: {}'.format(' '.join(usernames), handler_message) message_experts_slack_group( project.slack_group_id, message)
def staff(self, task_id, request_cause=StaffBotRequest.RequestCause.USER.value): """ This function handles staffing a request for the given task_id. """ command = 'staff {}'.format(task_id) try: task = Task.objects.get(id=task_id) required_role_counter = role_counter_required_for_new_task(task) error_msg = None assert_new_task_status_valid(task.status) except TaskStatusError: error_msg = self.staffing_is_not_allowed.format(task_id) except Task.DoesNotExist: error_msg = self.task_does_not_exist_error.format(task_id) except TaskAssignmentError as error: error_msg = self.task_assignment_error.format(task_id, error) if error_msg is not None: logger.exception(error_msg) return format_slack_message( command, attachments=[{ 'color': 'danger', 'title': 'Error', 'text': error_msg }]) StaffBotRequest.objects.create( task=task, required_role_counter=required_role_counter, request_cause=request_cause) slack_message = self.staffing_success.format(task_id) message_experts_slack_group(task.project.slack_group_id, slack_message) return format_slack_message( command, attachments=[{ 'color': 'good', 'title': 'Success', 'text': slack_message }])
def staff(self, task_id, request_cause=StaffBotRequest.RequestCause.USER.value): """ This function handles staffing a request for the given task_id. """ command = 'staff {}'.format(task_id) try: task = Task.objects.get(id=task_id) required_role_counter = role_counter_required_for_new_task(task) error_msg = None assert_new_task_status_valid(task.status) except TaskStatusError: error_msg = self.staffing_is_not_allowed.format(task_id) except Task.DoesNotExist: error_msg = self.task_does_not_exist_error.format(task_id) except TaskAssignmentError as error: error_msg = self.task_assignment_error.format(task_id, error) if error_msg is not None: logger.exception(error_msg) return format_slack_message( command, attachments=[{ 'color': 'danger', 'title': 'Error', 'text': error_msg }]) StaffBotRequest.objects.create( task=task, required_role_counter=required_role_counter, request_cause=request_cause) slack_message = self.staffing_success.format(task_id) message_experts_slack_group(task.project.slack_group_id, slack_message) return format_slack_message( command, attachments=[{ 'color': 'good', 'title': 'Success', 'text': slack_message }])
def send_request_inquiries(staffbot, request, worker_batch_size): # Get Workers that haven't already received an inquiry. workers_with_inquiries = (StaffingRequestInquiry.objects.filter( request=request).distinct().values_list( 'communication_preference__worker__id', flat=True)) # Sort Workers by their staffing priority first, and then randomly # within competing staffing priorities. workers = (Worker.objects.exclude(id__in=workers_with_inquiries).order_by( '-staffing_priority', '?')) required_role = get_role_from_counter(request.required_role_counter) inquiries_sent = 0 for worker in workers: try: check_worker_allowed_new_assignment(worker) if (is_worker_certified_for_task(worker, request.task, required_role, require_staffbot_enabled=True) and not request.task.is_worker_assigned(worker)): staffbot.send_task_to_worker(worker, request) inquiries_sent += 1 if inquiries_sent >= worker_batch_size: break except TaskStatusError: pass except TaskAssignmentError: pass # check whether all inquiries have been sent out. if inquiries_sent < worker_batch_size: message_experts_slack_group( request.task.project.slack_group_id, ('All staffing requests for task {} have been sent!'.format( request.task))) request.status = StaffBotRequest.Status.COMPLETE.value request.last_inquiry_sent = timezone.now() request.save()
def send_request_inquiries(staffbot, request, worker_batch_size): # Get Workers that haven't already received an inquiry. workers_with_inquiries = (StaffingRequestInquiry.objects.filter( request=request).distinct().values_list( 'communication_preference__worker__id', flat=True)) # Sort Workers by their staffing priority first, and then randomly # within competing staffing priorities. workers = (Worker.objects .exclude(id__in=workers_with_inquiries) .order_by('-staffing_priority', '?')) required_role = get_role_from_counter(request.required_role_counter) inquiries_sent = 0 for worker in workers: try: check_worker_allowed_new_assignment(worker) if (is_worker_certified_for_task(worker, request.task, required_role, require_staffbot_enabled=True) and not request.task.is_worker_assigned(worker)): staffbot.send_task_to_worker(worker, request) inquiries_sent += 1 if inquiries_sent >= worker_batch_size: break except TaskStatusError: pass except TaskAssignmentError: pass # check whether all inquiries have been sent out. if inquiries_sent < worker_batch_size: message_experts_slack_group( request.task.project.slack_group_id, ('All staffing requests for task {} have been sent!' .format(request.task))) request.status = StaffBotRequest.Status.COMPLETE.value request.last_inquiry_sent = timezone.now() request.save()
def _send_request_inquiries(staffbot, request, worker_batch_size, worker_certifications): inquiries_sent = 0 required_role = get_role_from_counter(request.required_role_counter) contacted_workers = set() for certification in worker_certifications: try: worker = certification.worker if worker.id in contacted_workers: continue contacted_workers.add(worker.id) check_worker_allowed_new_assignment(worker) if (is_worker_certified_for_task(worker, request.task, required_role, require_staffbot_enabled=True) and not request.task.is_worker_assigned(worker)): staffbot.send_task_to_worker(worker, request) inquiries_sent += 1 if inquiries_sent >= worker_batch_size: break except TaskStatusError: pass except TaskAssignmentError: pass # check whether all inquiries have been sent out. if inquiries_sent < worker_batch_size: message_experts_slack_group( request.task.project.slack_group_id, ('All staffing requests for task {} have been sent!'.format( request.task))) request.status = StaffBotRequest.Status.DONE_SENDING_INQUIRIES.value request.last_inquiry_sent = timezone.now() request.save()
def check_responses_complete(request): inquiries = (StaffingRequestInquiry.objects.filter( request=request)).distinct() num_inquired_workers = len( set( inquiries.values_list('communication_preference__worker__id', flat=True))) responded_inquiries = inquiries.filter(responses__isnull=False).distinct() num_responded_workers = len( set( responded_inquiries.values_list( 'communication_preference__worker__id', flat=True))) responses = StaffingResponse.objects.filter( request_inquiry__request=request) if (num_responded_workers >= num_inquired_workers and not responses.filter(is_winner=True).exists()): request.status = StaffBotRequest.Status.CLOSED.value request.save() # notify that all workers have rejected a task message_experts_slack_group( request.task.project.slack_group_id, ('No worker has accepted to work on task {}'.format(request.task)))
def send_request_inquiries(staffbot, request, worker_batch_size): # get names of workers that that already received inquiry worker_usernames = (StaffingRequestInquiry.objects.filter( request=request).values_list( 'communication_preference__worker__user__username', flat=True)) workers = (Worker.objects .exclude(user__username__in=worker_usernames) .order_by('?')) required_role = get_role_from_counter(request.required_role_counter) inquiries_sent = 0 for worker in workers: try: check_worker_allowed_new_assignment(worker) if (is_worker_certified_for_task(worker, request.task, required_role) and not request.task.is_worker_assigned(worker)): staffbot.send_task_to_worker(worker, request) inquiries_sent += 1 if inquiries_sent >= worker_batch_size: break except TaskStatusError: pass except TaskAssignmentError: pass # check whether all inquiries have been sent out. if inquiries_sent < worker_batch_size: message_experts_slack_group( request.task.project.slack_group_id, ('All staffing requests for task {} have been sent!' .format(request.task))) request.status = StaffBotRequest.Status.COMPLETE.value request.save()
def perform_update(self, serializer): old_todo = self.get_object() todo = serializer.save() sender = Worker.objects.get( user=self.request.user).formatted_slack_username() if old_todo.completed != todo.completed: todo_change = 'complete' if todo.completed else 'incomplete' elif old_todo.skipped_datetime != todo.skipped_datetime: todo_change = 'not relevant' \ if todo.skipped_datetime else 'relevant' else: # When activity_log is updated, `todo_change = None` # to avoid triggering any slack messages todo_change = None # To avoid Slack noise, only send updates for changed TODOs with # depth 0 (no parent) or 1 (no grantparent). if todo_change and \ (not (todo.parent_todo and todo.parent_todo.parent_todo)): message = '{} has marked `{}` as `{}`.'.format( sender, todo.description, todo_change) message_experts_slack_group(todo.task.project.slack_group_id, message)
def restaff(self, task_id, username, request_cause=StaffBotRequest.RequestCause.USER.value): """ This function handles restaffing a request for the given task_id. The current user for the given username is removed, and a new user is found. """ command = 'restaff {} {}'.format(task_id, username) try: error_msg = None worker = Worker.objects.filter( Q(user__username=username) | Q(slack_username=username)) if worker.exists(): worker = worker.first() else: error_msg = self.worker_does_not_exist.format(username) return format_slack_message( command, attachments=[{ 'color': 'danger', 'title': 'Error', 'text': error_msg }]) task = Task.objects.get(id=task_id) task_assignment = TaskAssignment.objects.get(worker=worker, task=task) required_role_counter = task_assignment.assignment_counter except Task.DoesNotExist: error_msg = self.task_does_not_exist_error.format(task_id) except TaskAssignment.DoesNotExist: error_msg = (self.task_assignment_does_not_exist_error .format(username, task_id)) except TaskAssignmentError as error: error_msg = self.task_assignment_error.format(task_id, error) if error_msg is not None: logger.exception(error_msg) return format_slack_message( command, attachments=[{ 'color': 'danger', 'title': 'Error', 'text': error_msg }]) StaffBotRequest.objects.create( task=task, required_role_counter=required_role_counter, request_cause=request_cause) slack_message = self.restaffing_success.format(task_id) message_experts_slack_group(task.project.slack_group_id, slack_message) return format_slack_message( command, attachments=[{ 'color': 'good', 'title': 'Success', 'text': slack_message }])
def restaff(self, task_id, username, request_cause=StaffBotRequest.RequestCause.USER.value): """ This function handles restaffing a request for the given task_id. The current user for the given username is removed, and a new user is found. """ command = 'restaff {} {}'.format(task_id, username) try: error_msg = None worker = Worker.objects.filter( Q(user__username=username) | Q(slack_username=username)) if worker.exists(): worker = worker.first() else: error_msg = self.worker_does_not_exist.format(username) return format_slack_message( command, attachments=[{ 'color': 'danger', 'title': 'Error', 'text': error_msg }]) task = Task.objects.get(id=task_id) task_assignment = TaskAssignment.objects.get(worker=worker, task=task) required_role_counter = task_assignment.assignment_counter except Task.DoesNotExist: error_msg = self.task_does_not_exist_error.format(task_id) except TaskAssignment.DoesNotExist: error_msg = (self.task_assignment_does_not_exist_error .format(username, task_id)) except TaskAssignmentError as error: error_msg = self.task_assignment_error.format(task_id, error) if error_msg is not None: logger.exception(error_msg) return format_slack_message( command, attachments=[{ 'color': 'danger', 'title': 'Error', 'text': error_msg }]) StaffBotRequest.objects.create( task=task, required_role_counter=required_role_counter, request_cause=request_cause) slack_message = self.restaffing_success.format(task_id) message_experts_slack_group(task.project.slack_group_id, slack_message) return format_slack_message( command, attachments=[{ 'color': 'good', 'title': 'Success', 'text': slack_message }])