From 8024ed979446488b7fbbb91a8127ad73486bceeb Mon Sep 17 00:00:00 2001 From: Kainar Kamalov Date: Fri, 29 Apr 2016 18:44:36 +0000 Subject: [PATCH] Added worker slack_ids. Datamigration to port ids for existing users --- orchestra/accounts/forms.py | 20 ++++++++- orchestra/accounts/tests/test_views.py | 12 ++++- .../migrations/0053_worker_slack_user_id.py | 20 +++++++++ orchestra/models/core/models.py | 3 ++ orchestra/scripts/add_slack_user_ids.py | 45 +++++++++++++++++++ orchestra/utils/slack.py | 13 ++++++ 6 files changed, 110 insertions(+), 3 deletions(-) create mode 100644 orchestra/migrations/0053_worker_slack_user_id.py create mode 100644 orchestra/scripts/add_slack_user_ids.py create mode 100644 orchestra/utils/slack.py diff --git a/orchestra/accounts/forms.py b/orchestra/accounts/forms.py index 1c42847bf..516f2288b 100644 --- a/orchestra/accounts/forms.py +++ b/orchestra/accounts/forms.py @@ -6,6 +6,7 @@ from orchestra.accounts.bitformfield import BitFormField from orchestra.models import CommunicationPreference from orchestra.models import Worker +from orchestra.utils.slack import get_slack_user_id UserModel = get_user_model() @@ -28,7 +29,24 @@ class WorkerForm(forms.ModelForm): class Meta: model = Worker fields = ('slack_username', 'phone') - exclude = ('user', 'start_datetime') + exclude = ('user', 'start_datetime', 'slack_user_id') + + def clean_slack_username(self): + slack_username = self.cleaned_data.get('slack_username') + if slack_username: + slack_user_id = get_slack_user_id(slack_username) + if slack_user_id is None: + raise forms.ValidationError( + 'Incorrect slack username provided') + self.cleaned_data['slack_user_id'] = slack_user_id + return self.cleaned_data['slack_username'] + + def save(self, commit=True): + instance = super().save(commit=False) + instance.slack_user_id = self.cleaned_data.get('slack_user_id') + if commit: + instance.save() + return instance class CommunicationPreferenceForm(forms.ModelForm): diff --git a/orchestra/accounts/tests/test_views.py b/orchestra/accounts/tests/test_views.py index 61bf1f50c..8f817ba03 100644 --- a/orchestra/accounts/tests/test_views.py +++ b/orchestra/accounts/tests/test_views.py @@ -1,4 +1,5 @@ from django.core.urlresolvers import reverse +from unittest.mock import patch from orchestra.tests.helpers import OrchestraAuthenticatedTestCase from orchestra.models import CommunicationPreference @@ -28,7 +29,10 @@ def test_get_form(self): self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, 'accounts/settings.html') - def test_change_all_fields(self): + @patch('orchestra.accounts.forms.get_slack_user_id') + def test_change_all_fields(self, mock_get_slack_user_id): + mock_get_slack_user_id.return_value = 'test_id' + data = self._get_account_settings_mock_data() response = self.request_client.post(self.url, data) self.assertTrue(response.context['success']) @@ -40,8 +44,12 @@ def test_change_all_fields(self): self.worker.refresh_from_db() self.assertEqual(self.worker.slack_username, data['slack_username']) self.assertEqual(self.worker.phone, data['phone_0'] + data['phone_1']) + self.assertEqual(self.worker.slack_user_id, 'test_id') + + @patch('orchestra.accounts.forms.get_slack_user_id') + def test_missing_fields(self, mock_get_slack_user_id): + mock_get_slack_user_id.return_value = 'test_id' - def test_missing_fields(self): required_fields = self._get_account_settings_mock_data().keys() for field in required_fields: data = self._get_account_settings_mock_data() diff --git a/orchestra/migrations/0053_worker_slack_user_id.py b/orchestra/migrations/0053_worker_slack_user_id.py new file mode 100644 index 000000000..8bf68528c --- /dev/null +++ b/orchestra/migrations/0053_worker_slack_user_id.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.5 on 2016-04-29 16:42 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('orchestra', '0052_auto_20160428_2142'), + ] + + operations = [ + migrations.AddField( + model_name='worker', + name='slack_user_id', + field=models.CharField(blank=True, max_length=200, null=True), + ), + ] diff --git a/orchestra/models/core/models.py b/orchestra/models/core/models.py index 436fec8bd..71bd65aea 100644 --- a/orchestra/models/core/models.py +++ b/orchestra/models/core/models.py @@ -185,12 +185,15 @@ class Worker(WorkerMixin, models.Model): The time the worker was created. slack_username (str): The worker's Slack username if Slack integration is enabled. + slack_user_id (str): + The worker's Slack id if Slack integration is enabled. phone (str): The worker's phone number """ user = models.OneToOneField(settings.AUTH_USER_MODEL) start_datetime = models.DateTimeField(default=timezone.now) slack_username = models.CharField(max_length=200, blank=True, null=True) + slack_user_id = models.CharField(max_length=200, blank=True, null=True) phone = PhoneNumberField(null=True) class Meta: diff --git a/orchestra/scripts/add_slack_user_ids.py b/orchestra/scripts/add_slack_user_ids.py new file mode 100644 index 000000000..29b90e2a7 --- /dev/null +++ b/orchestra/scripts/add_slack_user_ids.py @@ -0,0 +1,45 @@ +from annoying.functions import get_object_or_None +from django.conf import settings +from slacker import Slacker + +from orchestra.models import Worker + +import logging +logger = logging.getLogger(__name__) + + +def add_worker_slack_ids(): + slack = Slacker(getattr(settings, 'SLACK_EXPERTS_API_KEY', None)) + slack_response = slack.users.list() + if not slack_response.successful: + logger.warning('Slack could not be reached.') + return + + members = slack_response.body.get('members') + for member in members: + if member.get('is_bot'): + continue + worker = get_object_or_None(Worker, + slack_username=member['name']) + if worker is None: + + # Try to match user by first name and last name. + if (member['profile'].get('first_name') is None or + member['profile'].get('last_name') is None): + continue + + workers = Worker.objects.filter( + user__first_name=member['profile']['first_name'], + user__last_name=member['profile']['last_name']) + if workers.count() == 1: + worker = workers.first() + + if worker: + logger.info('Username {} has slack id {}'.format( + member['name'], member['id'])) + worker.slack_id = member['id'] + worker.slack_username = member['name'] + worker.save() + else: + logger.info('Could not find worker for username {}'.format( + member['name'])) diff --git a/orchestra/utils/slack.py b/orchestra/utils/slack.py new file mode 100644 index 000000000..a106961f3 --- /dev/null +++ b/orchestra/utils/slack.py @@ -0,0 +1,13 @@ +from django.conf import settings +from slacker import Slacker as _Slacker + + +class Slacker(_Slacker): + def __init__(self): + super().__init__(settings.SLACK_EXPERTS_API_KEY) + + +def get_slack_user_id(slack_username): + slack = Slacker() + slack_user_id = slack.users.get_user_id(slack_username) + return slack_user_id