diff --git a/main/extra_func.py b/main/extra_func.py
index e6a2a97..90ec414 100644
--- a/main/extra_func.py
+++ b/main/extra_func.py
@@ -6,8 +6,8 @@ from django.core.exceptions import ObjectDoesNotExist
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 81b2e8a..8ca6f13 100644
--- a/main/forms.py
+++ b/main/forms.py
@@ -140,3 +140,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 5f71955..59df965 100644
--- a/main/tests.py
+++ b/main/tests.py
@@ -1,5 +1,5 @@
import random
-from unittest.mock import patch
+from unittest.mock import patch, Mock
from django.contrib.auth.models import User
from django.core import mail
@@ -43,7 +43,7 @@ class RegistrationTestCase(TestCase):
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].to, [self.any_zendesk_user_email])
# context that the email template was rendered with
email_context = response.context[0].dicts[1]
@@ -262,3 +262,93 @@ class PasswordChangeTestCase(TestCase):
}
)
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)
diff --git a/main/views.py b/main/views.py
index 22978c2..4602bcf 100644
--- a/main/views.py
+++ b/main/views.py
@@ -1,19 +1,18 @@
from smtplib import SMTPException
-from typing import Dict, Any
from django.contrib import messages
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 User, 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
from django.core.handlers.wsgi import WSGIRequest
from django.http import HttpResponseRedirect, HttpResponse
from django.shortcuts import render, redirect
-from django.urls import reverse_lazy, reverse
+from django.urls import reverse_lazy
from django.views.generic import FormView
from django_registration.views import RegistrationView
# Django REST
@@ -23,13 +22,13 @@ from rest_framework.response import Response
from access_controller.settings import DEFAULT_FROM_EMAIL, ZENDESK_ROLES, ZENDESK_MAX_AGENTS, ZENDESK_GROUPS
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, \
- log, set_session_params_for_work_page, get_tickets_list_for_group
-from .statistic_data import StatisticData
-from main.zendesk_admin import zenpy
-from main.requester import TicketListRequester
-from main.forms import AdminPageUsers, CustomRegistrationForm, CustomAuthenticationForm, StatisticForm
+ set_session_params_for_work_page, get_tickets_list_for_group
+from main.forms import AdminPageUsers, CustomRegistrationForm, CustomAuthenticationForm, StatisticForm, \
+ WorkGetTicketsForm
from main.serializers import ProfileSerializer, ZendeskUserSerializer
+from main.zendesk_admin import zenpy
from .models import UserProfile
+from .statistic_data import StatisticData
class CustomRegistrationView(RegistrationView):
@@ -178,6 +177,7 @@ def work_page(request: WSGIRequest, 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")
@@ -211,21 +211,18 @@ def work_become_engineer(request: WSGIRequest) -> HttpResponseRedirect:
@login_required()
def work_get_tickets(request):
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 range(len(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 2a689ce..627d900 100644
--- a/main/zendesk_admin.py
+++ b/main/zendesk_admin.py
@@ -1,7 +1,9 @@
-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
@@ -28,6 +30,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.