diff --git a/README.md b/README.md
index c3b9666..b3376cf 100644
--- a/README.md
+++ b/README.md
@@ -46,9 +46,9 @@ cp .env.example .env
sudo apt install make
pip install --upgrade pip
pip install -r requirements/dev.txt
-(set -a && source .env && ./manage.py migrate)
-(set -a && source .env && ./manage.py loaddata data.json)
-(set -a && source .env && ./manage.py runserver)
+./manage.py migrate
+./manage.py loaddata data.json
+./manage.py runserver
```
## Перед запуском для тестирования:
@@ -63,7 +63,7 @@ pip install -r requirements/dev.txt
- Перейти в папку приложения
- Активировать виртуальное окружение
- Выполнить команду `pip install -r requirements/dev.txt`
-- В виртуальное окружение добавить следующие переменные:
+- В файл `.env` добавить следующие переменные:
```
diff --git a/access_controller/settings.py b/access_controller/settings.py
index b1b98a9..55af7a5 100644
--- a/access_controller/settings.py
+++ b/access_controller/settings.py
@@ -11,6 +11,7 @@ https://docs.djangoproject.com/en/3.1/ref/settings/
"""
import os
from pathlib import Path
+from dotenv import load_dotenv
# Build paths inside the project like this: BASE_DIR / 'subdir'.
@@ -20,6 +21,9 @@ BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/
+# Load environment variables from .env
+load_dotenv()
+
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.getenv('ACTRL_SECRET_KEY', 'empty')
diff --git a/fixtures/profile.json b/fixtures/profile.json
new file mode 100644
index 0000000..8ce02db
--- /dev/null
+++ b/fixtures/profile.json
@@ -0,0 +1,59 @@
+[
+ {
+ "model": "auth.user",
+ "pk": 1,
+ "fields": {
+ "password": "pbkdf2_sha256$216000$gHBBCr1jBELf$ZkEDW3IEd8Wij7u8vkv+0Eze32CS01bcaYWhcD9OIC4=",
+ "last_login": null,
+ "is_superuser": true,
+ "username": "idar.sokurov.05@mail.ru",
+ "first_name": "",
+ "last_name": "",
+ "email": "idar.sokurov.05@mail.ru",
+ "is_staff": true,
+ "is_active": true,
+ "date_joined": "2021-03-10T16:38:56.303Z",
+ "groups": [],
+ "user_permissions": [
+ 33
+ ]
+ }
+ },
+ {
+ "model": "main.userprofile",
+ "pk": 1,
+ "fields": {
+ "name": "ZendeskAdmin",
+ "user": 1,
+ "role": "admin"
+ }
+ },
+ {
+ "model": "auth.user",
+ "pk": 2,
+ "fields": {
+ "password": "pbkdf2_sha256$216000$5qLJgrm2Quq9$KDBNNymVZXkUx0HKBPFst2m83kLe0egPBnkW7KnkORU=",
+ "last_login": null,
+ "is_superuser": false,
+ "username": "krav-88@mail.ru",
+ "first_name": "",
+ "last_name": "",
+ "email": "krav-88@mail.ru",
+ "is_staff": false,
+ "is_active": true,
+ "date_joined": "2021-03-10T16:38:56.303Z",
+ "groups": [],
+ "user_permissions": []
+ }
+ },
+ {
+ "model": "main.userprofile",
+ "pk": 2,
+ "fields": {
+ "name": "UserForAccessTest",
+ "user": 2,
+ "role": "agent",
+ "custom_role_id": "360005209000"
+ }
+ }
+]
diff --git a/main/extra_func.py b/main/extra_func.py
index 4760bb4..6d68944 100644
--- a/main/extra_func.py
+++ b/main/extra_func.py
@@ -12,8 +12,8 @@ from django.http import HttpResponseRedirect, HttpResponsePermanentRedirect
from django.shortcuts import redirect
from django.utils import timezone
from zenpy import Zenpy
-from zenpy.lib.exception import APIException
from zenpy.lib.api_objects import User as ZenpyUser, Ticket as ZenpyTicket
+from zenpy.lib.exception import APIException
from zenpy.lib.generator import SearchResultGenerator
from access_controller.settings import ZENDESK_ROLES as ROLES, ACTRL_ZENDESK_SUBDOMAIN
diff --git a/main/forms.py b/main/forms.py
index 5666255..929f3d9 100644
--- a/main/forms.py
+++ b/main/forms.py
@@ -144,3 +144,23 @@ class StatisticForm(forms.Form):
}
),
)
+
+
+class WorkGetTicketsForm(forms.Form):
+ """
+ Форма получения количества тикетов для страницы work и work_get_tickets.
+
+ :param count_tickets: Поле для ввода количества тикетов
+ :type count_tickets: :class:`django.forms.fields.IntegerField`
+ """
+ count_tickets = forms.IntegerField(
+ min_value=0,
+ max_value=100,
+ required=True,
+ widget=forms.NumberInput(
+ attrs={
+ 'class': 'form-control mb-3',
+ 'value': 1
+ }
+ ),
+ )
diff --git a/main/templates/pages/work.html b/main/templates/pages/work.html
index cb07b6d..bd46341 100644
--- a/main/templates/pages/work.html
+++ b/main/templates/pages/work.html
@@ -66,10 +66,11 @@
Сдать права инженера
-
+
{% for message in messages %}
diff --git a/main/tests.py b/main/tests.py
index f9fed9a..6d72f2e 100644
--- a/main/tests.py
+++ b/main/tests.py
@@ -3,11 +3,13 @@
"""
-from unittest.mock import patch
-from urllib.parse import urlparse
+import random
+from unittest.mock import patch, Mock
from django.contrib.auth import get_user_model
from django.core import mail
+from django.http import HttpResponseRedirect
+from django.template.loader import render_to_string
from django.test import TestCase, Client
from django.urls import reverse, reverse_lazy
from django.utils import translation
@@ -55,42 +57,23 @@ class RegistrationTestCase(TestCase):
resp = self.client.post(reverse('registration'), data={'email': '123@test.ru'})
self.assertContains(resp, 'Этот адрес электронной почты уже используется', count=1, status_code=200)
- def test_registration_email_sending(self):
+ def test_registration_send_email(self):
"""
Функция тестирования отправки email.
- # TODO: Найти способ лучше проверять сообщения
"""
- email_template = [
- '',
- 'Вы получили это письмо, потому что вы (или кто-то другой) запросили восстановление пароля '
- 'от учётной записи на сайте testserver, которая связана с этим адресом электронной почты.',
- '',
- 'Пожалуйста, перейдите на эту страницу и введите новый пароль:',
- '',
- 'url',
- '',
- f'Ваше имя пользователя (на случай, если вы его забыли): {self.any_zendesk_user_email}',
- '',
- 'Спасибо, что используете наш сайт!',
- '',
- 'Команда сайта testserver',
- '',
- '',
- '',
- ]
- with self.settings(EMAIL_BACKEND=self.email_backend) and translation.override('ru'):
- self.client.post(reverse('registration'), data={'email': self.any_zendesk_user_email})
-
+ with self.settings(EMAIL_BACKEND=self.email_backend):
+ response: HttpResponseRedirect = \
+ self.client.post(reverse('registration'), data={'email': self.any_zendesk_user_email})
+ self.assertEqual(response.status_code, 302)
self.assertEqual(len(mail.outbox), 1)
- self.assertEqual(mail.outbox[0].to, [self.zendesk_admin_email])
- self.assertEqual(mail.outbox[0].from_email, sets.DEFAULT_FROM_EMAIL)
+ self.assertEqual(mail.outbox[0].to, [self.any_zendesk_user_email])
- message = mail.outbox[0].body.split('\n')
- for i in enumerate(message):
- if email_template[i] != 'url':
- self.assertEqual(message[i], email_template[i])
- else:
- self.assertTrue(urlparse(message[i]).scheme)
+ # context that the email template was rendered with
+ email_context = response.context[0].dicts[1]
+ correct_subject = render_to_string('registration/password_reset_subject.txt', email_context, response.request)
+ self.assertEqual(mail.outbox[0].subject, correct_subject.strip())
+ correct_body = render_to_string('registration/password_reset_email.html', email_context, response.request)
+ self.assertEqual(mail.outbox[0].body, correct_body)
def test_registration_user_creating(self):
"""
@@ -202,3 +185,284 @@ class MakeEngineerTestCase(TestCase):
self.assertEqual(len(call_list), 2)
for obj in mock_objects:
self.assertEqual(obj[0][0].custom_role_id, sets.ZENDESK_ROLES['engineer'])
+
+
+class PasswordResetTestCase(TestCase):
+ fixtures = ['fixtures/test_make_engineer.json']
+
+ def setUp(self):
+ self.user = '123@test.ru'
+ self.email_backend = 'django.core.mail.backends.locmem.EmailBackend'
+ self.client = Client()
+ self.client.force_login(User.objects.get(email=self.user))
+
+ def test_redirect(self):
+ with self.settings(EMAIL_BACKEND=self.email_backend):
+ resp = self.client.post(reverse_lazy('password_reset'), data={'email': self.user})
+ self.assertRedirects(resp, reverse('password_reset_done'))
+ self.assertEqual(resp.status_code, 302)
+
+ def test_send_email(self):
+ with self.settings(EMAIL_BACKEND=self.email_backend):
+ response: HttpResponseRedirect = \
+ self.client.post(reverse_lazy('password_reset'), data={'email': self.user})
+ self.assertEqual(response.status_code, 302)
+ self.assertEqual(len(mail.outbox), 1)
+ self.assertEqual(mail.outbox[0].to, [self.user])
+
+ # context that the email template was rendered with
+ email_context = response.context[0].dicts[1]
+ correct_subject = render_to_string('registration/password_reset_subject.txt', email_context, response.request)
+ self.assertEqual(mail.outbox[0].subject, correct_subject.strip())
+ correct_body = render_to_string('registration/password_reset_email.html', email_context, response.request)
+ self.assertEqual(mail.outbox[0].body, correct_body)
+
+ def test_email_invalid(self):
+ with self.settings(EMAIL_BACKEND=self.email_backend) and translation.override('ru'):
+ resp = self.client.post(reverse_lazy('password_reset'), data={'email': 1})
+ self.assertContains(resp, 'Введите правильный адрес электронной почты.', count=1, status_code=200)
+
+ def test_user_does_not_exist(self):
+ with self.settings(EMAIL_BACKEND=self.email_backend):
+ resp = self.client.post(reverse_lazy('password_reset'), data={'email': self.user + str(random.random())})
+ self.assertRedirects(resp, reverse('password_reset_done'))
+ self.assertEqual(resp.status_code, 302)
+ self.assertEqual(len(mail.outbox), 0)
+
+
+class PasswordChangeTestCase(TestCase):
+ fixtures = ['fixtures/test_make_engineer.json']
+
+ def setUp(self):
+ self.user = '123@test.ru'
+ self.client = Client()
+ self.client.force_login(User.objects.get(email=self.user))
+ self.set_password()
+
+ def set_password(self):
+ user: User = User.objects.get(email=self.user)
+ user.set_password('ImpossiblyHardPassword')
+ user.save()
+ self.client.force_login(User.objects.get(email=self.user))
+
+ def test_change_successful(self):
+ self.client.post(
+ reverse_lazy('password_change'),
+ data={
+ 'old_password': 'ImpossiblyHardPassword',
+ 'new_password1': 'EasyPassword',
+ 'new_password2': 'EasyPassword',
+ }
+ )
+ user = User.objects.get(email=self.user)
+ self.assertTrue(user.check_password('EasyPassword'))
+
+ def test_invalid_old_password(self):
+ with translation.override('ru'):
+ resp = self.client.post(
+ reverse_lazy('password_change'),
+ data={
+ 'old_password': 'EasyPassword',
+ 'new_password1': 'EasyPassword',
+ 'new_password2': 'EasyPassword',
+ }
+ )
+ self.assertContains(resp, 'Ваш старый пароль введен неправильно', count=1, status_code=200)
+
+ def test_different_new_passwords(self):
+ with translation.override('ru'):
+ resp = self.client.post(
+ reverse_lazy('password_change'),
+ data={
+ 'old_password': 'ImpossiblyHardPassword',
+ 'new_password1': 'EasyPassword',
+ 'new_password2': 'EasyPassword1',
+ }
+ )
+ self.assertContains(resp, 'Введенные пароли не совпадают', count=1, status_code=200)
+
+ def test_invalid_new_password1(self):
+ with translation.override('ru'):
+ resp = self.client.post(
+ reverse_lazy('password_change'),
+ data={
+ 'old_password': 'ImpossiblyHardPassword',
+ 'new_password1': 'short',
+ 'new_password2': 'short',
+ }
+ )
+ self.assertContains(resp, 'Введённый пароль слишком короткий', count=1, status_code=200)
+
+ def test_invalid_new_password2(self):
+ with translation.override('ru'):
+ resp = self.client.post(
+ reverse_lazy('password_change'),
+ data={
+ 'old_password': 'ImpossiblyHardPassword',
+ 'new_password1': '123123123123123123132123123',
+ 'new_password2': '123123123123123123132123123',
+ }
+ )
+ self.assertContains(resp, 'Введённый пароль состоит только из цифр', count=1, status_code=200)
+
+ def test_invalid_new_password3(self):
+ with translation.override('ru'):
+ resp = self.client.post(
+ reverse_lazy('password_change'),
+ data={
+ 'old_password': 'ImpossiblyHardPassword',
+ 'new_password1': self.user,
+ 'new_password2': self.user,
+ }
+ )
+ self.assertContains(resp, 'Введённый пароль слишком похож на имя пользователя', count=1, status_code=200)
+
+
+class GetTicketsTestCase(TestCase):
+ """
+ Класс тестов для проверки функции получения тикетов.
+ """
+ fixtures = ['fixtures/test_make_engineer.json']
+
+ def setUp(self):
+ """
+ Предустановленные значения для проведения тестов.
+ """
+ self.light_agent = '123@test.ru'
+ self.engineer = 'customer@example.com'
+ self.client = Client()
+ self.client.force_login(User.objects.get(email=self.engineer))
+ self.light_agent_client = Client()
+ self.light_agent_client.force_login(User.objects.get(email=self.light_agent))
+
+ @patch('main.views.zenpy.get_user')
+ @patch('main.extra_func.zenpy')
+ def test_redirect(self, ZenpyMock, GetUserMock):
+ """
+ Функция проверки переадресации пользователя на рабочую страницу.
+ """
+ GetUserMock.return_value = Mock()
+ user = User.objects.get(email=self.engineer)
+ resp = self.client.post(reverse('work_get_tickets'))
+ self.assertRedirects(resp, reverse('work', args=[user.id]))
+ self.assertEqual(resp.status_code, 302)
+
+ @patch('main.views.zenpy')
+ @patch('main.views.get_tickets_list_for_group')
+ def test_take_one_ticket(self, TicketsMock, ZenpyViewsMock):
+ """
+ Функция проверки назначения одного тикета на engineer.
+ """
+ TicketsMock.return_value = [Mock()]
+ ZenpyViewsMock.get_user.return_value = Mock(role='agent', custom_role_id=sets.ZENDESK_ROLES['engineer'])
+ self.client.post(reverse('work_get_tickets'), data={'count_tickets': 1})
+ tickets = ZenpyViewsMock.update_tickets.call_args
+ self.assertEqual(tickets[0][0][0].assignee, ZenpyViewsMock.get_user.return_value)
+
+ @patch('main.views.get_tickets_list_for_group')
+ @patch('main.views.zenpy')
+ def test_take_many_tickets(self, ZenpyMock, TicketsMock):
+ """
+ Функция проверки назначения нескольких тикетов на engineer.
+ """
+ TicketsMock.return_value = [Mock()] * 3
+ ZenpyMock.get_user.return_value = Mock(role='agent', custom_role_id=sets.ZENDESK_ROLES['engineer'])
+ self.client.post(reverse('work_get_tickets'), data={'count_tickets': 3})
+ tickets = ZenpyMock.update_tickets.call_args
+ for ticket in tickets[0][0]:
+ self.assertEqual(ticket.assignee, ZenpyMock.get_user.return_value)
+
+ @patch('main.views.zenpy.get_user')
+ @patch('main.views.zenpy')
+ def test_light_agent_take_ticket(self, ZenpyMock, GetUserMock):
+ """
+ Функция проверки попытки назначения тикета на light_agent.
+ """
+ GetUserMock.return_value = Mock(role='agent', custom_role_id=sets.ZENDESK_ROLES['light_agent'])
+ self.light_agent_client.post(reverse('work_get_tickets'), data={'count_tickets': 3})
+ tickets = ZenpyMock.update_tickets.call_args
+ self.assertIsNone(tickets)
+
+ @patch('main.views.zenpy')
+ @patch('main.views.get_tickets_list_for_group')
+ def test_take_zero_tickets(self, TicketsMock, ZenpyMock):
+ """
+ Функция проверки попытки назначения нуля тикета на engineer.
+ """
+ TicketsMock.return_value = [Mock()] * 3
+ ZenpyMock.get_user.return_value = Mock(role='agent', custom_role_id=sets.ZENDESK_ROLES['engineer'])
+ self.client.post(reverse('work_get_tickets'), data={'count_tickets': 0})
+ tickets = ZenpyMock.update_tickets.call_args[0][0]
+ self.assertListEqual(tickets, [])
+
+ @patch('main.views.get_tickets_list_for_group')
+ @patch('main.views.zenpy')
+ def test_take_invalid_count_tickets(self, ZenpyMock, TicketsMock, ):
+ """
+ Функция проверки попытки назначения нуля тикетов на engineer.
+ """
+ TicketsMock.return_value = [Mock()] * 3
+ ZenpyMock.get_user.return_value = Mock(role='agent', custom_role_id=sets.ZENDESK_ROLES['engineer'])
+ self.client.post(reverse('work_get_tickets'), data={'count_tickets': 'asd'})
+ tickets = ZenpyMock.update_tickets.call_args
+ self.assertIsNone(tickets)
+
+
+class ProfileTestCase(TestCase):
+ """
+ Класс тестов для проверки синхронизации профиля пользователя.
+ """
+ fixtures = ['fixtures/profile.json']
+
+ def setUp(self):
+ """
+ Предустановленные значения для проведения тестов.
+ """
+ self.zendesk_agent_email = 'krav-88@mail.ru'
+ self.zendesk_admin_email = 'idar.sokurov.05@mail.ru'
+ self.client = Client()
+ self.client.force_login(User.objects.get(email=self.zendesk_agent_email))
+ self.admin_client = Client()
+ self.admin_client.force_login(User.objects.get(email=self.zendesk_admin_email))
+
+ def test_correct_username(self):
+ """
+ Функция проверки синхронизации имени пользователя.
+ """
+ resp = self.client.get(reverse('profile'))
+ self.assertEqual(resp.context['profile'].name, zenpy.get_user(self.zendesk_agent_email).name)
+
+ def test_correct_email(self):
+ """
+ Функция проверки синхронизации почты пользователя.
+ """
+ resp = self.client.get(reverse('profile'))
+ self.assertEqual(resp.context['profile'].user.email, zenpy.get_user(self.zendesk_agent_email).email)
+
+ def test_correct_role(self):
+ """
+ Функция проверки синхронизации роли пользователя.
+ """
+ resp = self.client.get(reverse('profile'))
+ self.assertEqual(resp.context['profile'].role, zenpy.get_user(self.zendesk_agent_email).role)
+ resp = self.admin_client.get(reverse('profile'))
+ self.assertEqual(resp.context['profile'].role, zenpy.get_user(self.zendesk_admin_email).role)
+
+ def test_correct_custom_role_id(self):
+ """
+ Функция проверки синхронизации рабочей роли пользователя.
+ """
+ resp = self.client.get(reverse('profile'))
+ user = zenpy.get_user(self.zendesk_agent_email)
+ self.assertEqual(resp.context['profile'].custom_role_id, user.custom_role_id if user.custom_role_id else 0)
+ resp = self.admin_client.get(reverse('profile'))
+ user = zenpy.get_user(self.zendesk_admin_email)
+ self.assertEqual(resp.context['profile'].custom_role_id, user.custom_role_id if user.custom_role_id else 0)
+
+ def test_correct_image(self):
+ """
+ Функция проверки синхронизации изображения пользователя.
+ """
+ resp = self.client.get(reverse('profile'))
+ user = zenpy.get_user(self.zendesk_agent_email)
+ self.assertEqual(resp.context['profile'].image, user.photo['content_url'] if user.photo else None)
diff --git a/main/views.py b/main/views.py
index e8c1317..1369d94 100644
--- a/main/views.py
+++ b/main/views.py
@@ -7,12 +7,12 @@ from smtplib import SMTPException
from typing import Dict, Any, Optional
from django.contrib import messages
-from django.contrib.auth.decorators import login_required
-from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
from django.contrib.auth import get_user_model
+from django.contrib.auth.decorators import login_required
+from django.contrib.auth.forms import PasswordResetForm
+from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
from django.contrib.auth.models import Permission
from django.contrib.auth.tokens import default_token_generator
-from django.contrib.auth.forms import PasswordResetForm
from django.contrib.auth.views import LoginView
from django.contrib.contenttypes.models import ContentType
from django.contrib.messages.views import SuccessMessageMixin
@@ -22,7 +22,7 @@ from django.shortcuts import render, redirect
from django.urls import reverse_lazy
from django.views.generic import FormView
from django_registration.views import RegistrationView
-# Django REST
+
from rest_framework import viewsets
from rest_framework.response import Response
@@ -30,12 +30,12 @@ from access_controller.settings import DEFAULT_FROM_EMAIL, ZENDESK_ROLES, ZENDES
from main.extra_func import check_user_exist, update_profile, get_user_organization, \
make_engineer, make_light_agent, get_users_list, update_users_in_model, count_users, \
set_session_params_for_work_page, get_tickets_list_for_group
-from main.zendesk_admin import zenpy
-from main.forms import AdminPageUsers, CustomRegistrationForm, CustomAuthenticationForm, StatisticForm
+from main.forms import AdminPageUsers, CustomRegistrationForm, CustomAuthenticationForm, \
+ StatisticForm, WorkGetTicketsForm
from main.serializers import ProfileSerializer, ZendeskUserSerializer
-from .statistic_data import StatisticData
-
+from main.zendesk_admin import zenpy
from .models import UserProfile
+from .statistic_data import StatisticData
def setup_context(**kwargs) -> Dict[str, Any]:
@@ -216,6 +216,7 @@ def work_page(request: WSGIRequest, required_id: int) -> HttpResponse:
'messages': messages.get_messages(request),
'licences_remaining': max(0, ZENDESK_MAX_AGENTS - len(engineers)),
'pagename': 'Управление правами',
+ 'get_tickets_form': WorkGetTicketsForm()
}
return render(request, 'pages/work.html', context)
return redirect("login")
@@ -254,21 +255,18 @@ def work_get_tickets(request: WSGIRequest) -> HttpResponse:
:return:
"""
zenpy_user = zenpy.get_user(request.user.email)
- if zenpy_user.role == 'admin' or zenpy_user.custom_role_id == ZENDESK_ROLES['engineer']:
- tickets = get_tickets_list_for_group(ZENDESK_GROUPS['buffer'])
- assigned_tickets = []
- count = 0
- for i in enumerate(tickets):
- if i == int(request.GET.get('count_tickets')):
- if assigned_tickets:
- zenpy.admin.tickets.update(assigned_tickets)
- return set_session_params_for_work_page(request, count)
- tickets[i].assignee = zenpy_user
- assigned_tickets.append(tickets[i])
- count += 1
- if assigned_tickets:
- zenpy.admin.tickets.update(assigned_tickets)
- return set_session_params_for_work_page(request, count)
+
+ if request.method == 'POST':
+ if zenpy_user.role == 'admin' or zenpy_user.custom_role_id == ZENDESK_ROLES['engineer']:
+ form = WorkGetTicketsForm(request.POST)
+ if form.is_valid():
+ tickets = get_tickets_list_for_group(ZENDESK_GROUPS['buffer'])
+ assigned_tickets = []
+ for i in range(min(form.cleaned_data['count_tickets'], len(tickets))):
+ tickets[i].assignee = zenpy_user
+ assigned_tickets.append(tickets[i])
+ zenpy.update_tickets(assigned_tickets)
+ return set_session_params_for_work_page(request, len(assigned_tickets))
return set_session_params_for_work_page(request, is_confirm=False)
diff --git a/main/zendesk_admin.py b/main/zendesk_admin.py
index f6372a5..c6a383f 100644
--- a/main/zendesk_admin.py
+++ b/main/zendesk_admin.py
@@ -2,10 +2,12 @@
Функционал работы администратора Zendesk.
"""
-from typing import Optional, Dict
+from typing import Optional, Dict, List
+
from zenpy import Zenpy
-from zenpy.lib.api_objects import User as ZenpyUser, Group as ZenpyGroup
+from zenpy.lib.api_objects import User as ZenpyUser, Group as ZenpyGroup, Ticket as ZenpyTicket
from zenpy.lib.exception import APIException
+
from access_controller.settings import ACTRL_ZENDESK_SUBDOMAIN, ACTRL_API_EMAIL, ACTRL_API_TOKEN, ACTRL_API_PASSWORD, \
ZENDESK_GROUPS, SOLVED_TICKETS_EMAIL
@@ -38,6 +40,15 @@ class ZendeskAdmin:
"""
self.admin.users.update(user)
+ def update_tickets(self, tickets: List[ZenpyTicket]):
+ """
+ Функция сохраняет изменение тикетов в Zendesk.
+
+ :param tickets: Тикеты с изменёнными данными
+ """
+ if tickets:
+ self.admin.tickets.update(tickets)
+
def check_user(self, email: str) -> bool:
"""
Функция осуществляет проверку существования пользователя в Zendesk по email.
diff --git a/requirements/common.txt b/requirements/common.txt
index 69294d4..0ba9415 100644
--- a/requirements/common.txt
+++ b/requirements/common.txt
@@ -7,7 +7,6 @@ zenpy~=2.0.24
django_registration==3.1.1
djangorestframework==3.12.2
-
# Documentation
Sphinx==3.4.3
sphinx-rtd-theme==0.5.1
@@ -15,6 +14,11 @@ sphinx-autodoc-typehints==1.11.1
pyenchant==3.2.0
sphinxcontrib-spelling==7.1.0
m2r == 0.2.1
+
+# Code style
pylint == 2.8.2
pylint-django == 2.4.4
autopep8 == 1.5.6
+
+# Misc
+python-dotenv==0.17.1