From 9f0ccf23e77f4fbdf55f95a7d4b5d0f7f0e61440 Mon Sep 17 00:00:00 2001 From: Dmitriy Andreev Date: Wed, 5 May 2021 20:43:25 +0300 Subject: [PATCH 01/18] Changes data.json in local --- data.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/data.json b/data.json index 97678f3..2afb1d9 100644 --- a/data.json +++ b/data.json @@ -23,7 +23,8 @@ "fields": { "name": "ZendeskAdmin", "user": 3, - "role": "admin" + "role": "admin", + "user_id": 1 } }, { From 3443c60a5f05542a4716a6a7e087f30f95646ac3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D1=82=D0=B5=D0=BF=D0=B0=D0=BD=D0=B5=D0=BD=D0=BA?= =?UTF-8?q?=D0=BE=20=D0=9E=D0=BB=D1=8C=D0=B3=D0=B0?= Date: Wed, 19 May 2021 21:12:28 +0300 Subject: [PATCH 02/18] Check spelling --- README.rst | 13 +++++-------- docs/source/spelling_wordlist.txt | 28 ++++++++++++++++++++++++++++ main/extra_func.py | 2 +- 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/README.rst b/README.rst index f1b08b5..fb32f4b 100644 --- a/README.rst +++ b/README.rst @@ -57,9 +57,9 @@ Quickstart 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 Перед запуском для тестирования: -------------------------------- @@ -76,7 +76,7 @@ Quickstart * Перейти в папку приложения * Активировать виртуальное окружение * Выполнить команду ``pip install -r requirements/dev.txt`` -* В виртуальное окружение добавить следующие переменные: +* В файл ``.env`` добавить следующие переменные: .. code-block:: @@ -170,10 +170,7 @@ Quickstart Для проверки pylint используем: ------------------------------- -pylint ../access_controller_new - -Вместо "access_controller_new" необходимо указать папку проекта. - +pylint --django-settings-module=access_controller.access_controller.settings ../access_controller (каталог, где лежит проект) Для приведения файлов к стандарту PEP8 используем: -------------------------------------------------- diff --git a/docs/source/spelling_wordlist.txt b/docs/source/spelling_wordlist.txt index 1e9713d..fb1f974 100644 --- a/docs/source/spelling_wordlist.txt +++ b/docs/source/spelling_wordlist.txt @@ -191,4 +191,32 @@ docs a Аватарка filename +work +form +work_get_tickets +get +tickets +Do +takes +whatever +it +to +actually +log +the +specified +logging +record +This +version +is +intended +be +implemented +by +subclasses +so +new + + diff --git a/main/extra_func.py b/main/extra_func.py index 6d68944..880daba 100644 --- a/main/extra_func.py +++ b/main/extra_func.py @@ -104,7 +104,7 @@ def get_tickets_list(email) -> list: def get_tickets_list_for_group(group_name): """ - Функция возвращает список неназначенных, нерешённых тикетов группы Zendesk + Функция возвращает список не назначенных, нерешённых тикетов группы Zendesk """ return TicketListRequester().get_tickets_list_for_group(zenpy.get_group(group_name)) From 1114c916e9c3d3112d5270f94c05d34b74479569 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D1=82=D0=B5=D0=BF=D0=B0=D0=BD=D0=B5=D0=BD=D0=BA?= =?UTF-8?q?=D0=BE=20=D0=9E=D0=BB=D1=8C=D0=B3=D0=B0?= Date: Thu, 20 May 2021 16:19:42 +0300 Subject: [PATCH 03/18] Update docs (views, extra_func, forms, models) --- access_controller/settings.py | 2 + docs/source/code.rst | 30 +++ main/apiauth.py | 3 +- main/extra_func.py | 51 +++-- main/forms.py | 2 +- main/models.py | 17 +- main/requester.py | 26 ++- main/serializers.py | 2 +- main/views.py | 60 +++--- .../site-packages/enchant/tokenize/ru.py | 185 ------------------ 10 files changed, 136 insertions(+), 242 deletions(-) delete mode 100644 venv/lib/python3.6/site-packages/enchant/tokenize/ru.py diff --git a/access_controller/settings.py b/access_controller/settings.py index 55af7a5..6293efc 100644 --- a/access_controller/settings.py +++ b/access_controller/settings.py @@ -11,6 +11,8 @@ 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'. diff --git a/docs/source/code.rst b/docs/source/code.rst index 1f0bd15..b07c93c 100644 --- a/docs/source/code.rst +++ b/docs/source/code.rst @@ -49,3 +49,33 @@ Views :members: +***************** +Обработка тикетов +***************** + +.. automodule:: main.requester + :members: + + +********************* +Обработка статистики +********************* + +.. automodule:: main.statistic_data + :members: + + +********************************* +Функционал администратора Zendesk +********************************* + +.. automodule:: main.zendesk_admin + :members: + + +******** +Тесты +******** + +.. automodule:: main.tests + :members: diff --git a/main/apiauth.py b/main/apiauth.py index b6488ba..c82d797 100644 --- a/main/apiauth.py +++ b/main/apiauth.py @@ -12,10 +12,11 @@ def api_auth() -> dict: Функция создания пользователя с использованием Zendesk API. Получает из env Zendesk - email, token, password пользователя. + Если данные валидны и пользователь Zendesk с указанным email и токеном или паролем существует, создается словарь данных пользователя, полученных через API c Zendesk. - :return: данные пользователя + :return: данные пользователя в виде словаря: id, имя, email, роль, аватар """ credentials = { 'subdomain': ACTRL_ZENDESK_SUBDOMAIN diff --git a/main/extra_func.py b/main/extra_func.py index 880daba..1c87f65 100644 --- a/main/extra_func.py +++ b/main/extra_func.py @@ -1,9 +1,9 @@ """ -Вспомогательные функции со списками пользователей, статистикой и т.д. +Вспомогательные функции. """ import logging from datetime import timedelta -from typing import Union +from typing import Union, Optional from django.contrib.auth import get_user_model from django.core.exceptions import ObjectDoesNotExist @@ -55,7 +55,8 @@ def make_light_agent(user_profile: UserProfile, who_changes: get_user_model()) - Функция устанавливает пользователю роль легкого агента. :param user_profile: Профиль пользователя - :return: Вызов функции **update_role** с параметрами: профиль пользователя, роль "light_agent" + :return: Вызов функции **update_role** с параметрами: профиль пользователя, роль "light_agent". + Предварительно снимаем тикеты, находящие в работы у пользователя. """ tickets: SearchResultGenerator = get_tickets_list(user_profile.user.email) ticket: ZenpyTicket @@ -85,7 +86,7 @@ def make_light_agent(user_profile: UserProfile, who_changes: get_user_model()) - def get_users_list() -> list: """ - Функция **get_users_list** возвращает список пользователей Zendesk, относящихся к организации SYSTEM. + Функция возвращает список пользователей Zendesk, относящихся к организации SYSTEM. """ zendesk = zenpy @@ -95,23 +96,29 @@ def get_users_list() -> list: return users -def get_tickets_list(email) -> list: +def get_tickets_list(email: str) -> Optional[list]: """ - Функция возвращает список тикетов пользователя Zendesk + Функция возвращает список тикетов пользователя Zendesk. + + :param email: Email пользователя + :return: Список тикетов пользователя """ return TicketListRequester().get_tickets_list_for_user(zenpy.get_user(email)) -def get_tickets_list_for_group(group_name): +def get_tickets_list_for_group(group_name: str) -> Optional[list]: """ - Функция возвращает список не назначенных, нерешённых тикетов группы Zendesk + Функция возвращает список не назначенных, не решённых тикетов группы Zendesk. + + :param group_name: Название группы пользователя + :return: Список тикетов группы """ return TicketListRequester().get_tickets_list_for_group(zenpy.get_group(group_name)) def update_profile(user_profile: UserProfile) -> None: """ - Функция обновляет профиль пользователя в соответствии с текущим в Zendesk. + Функция обновляет профиль пользователя в БД в соответствии с текущим в Zendesk. :param user_profile: Профиль пользователя :return: Обновленный, в соответствие с текущими данными в Zendesk, профиль пользователя @@ -148,6 +155,9 @@ def check_user_auth(email: str, password: str) -> bool: """ Функция проверяет, верны ли входные данные. + :param email: Email пользователя + :param password: Пароль пользователя + :return: Существует ли пользователь :raise: :class:`APIException`: исключение, вызываемое если пользователь не аутентифицирован """ creds = { @@ -165,7 +175,7 @@ def check_user_auth(email: str, password: str) -> bool: def update_user_in_model(profile: UserProfile, zendesk_user: ZenpyUser) -> None: """ - Функция обновляет профиль пользователя при изменении данных пользователя на Zendesk. + Функция обновляет профиль пользователя в модели при изменении данных пользователя на Zendesk. :param profile: Профиль пользователя :param zendesk_user: Данные пользователя в Zendesk @@ -181,7 +191,10 @@ def update_user_in_model(profile: UserProfile, zendesk_user: ZenpyUser) -> None: def count_users(users: list) -> tuple: """ - Функция подсчета количества сотрудников с ролями engineer и light_agent + Функция подсчета количества сотрудников с ролями engineer и light_agent. + + :param users: Список пользователей + :return: Количество инженеров, количество light_agents """ engineers, light_agents = 0, 0 for user in users: @@ -194,7 +207,7 @@ def count_users(users: list) -> tuple: def update_users_in_model() -> list: """ - Обновляет пользователей в модели UserProfile по списку пользователей в организации + Обновляет пользователей в модели UserProfile по списку пользователей в организации. """ users = get_users_list() for user in users: @@ -253,7 +266,13 @@ class DatabaseHandler(logging.Handler): def __init__(self): logging.Handler.__init__(self) - def emit(self, record): + def emit(self, record: logging.LogRecord) -> None: + """ + Функция записи в базу данных лога с изменением роли пользователя. + + :param record: Лог смены роли пользователя + :return: Запись в БД лога по смене роли пользователя с указанием новой и старой роли, а также автора изменения + """ database = RoleChangeLogs() users = record.msg if users[1]: @@ -284,7 +303,7 @@ class CsvFormatter(logging.Formatter): """ Функция форматирует запись смены роли пользователя в строку. - :param record: Запись смены роли пользователя. + :param record: Лог смены роли пользователя. :return: Строка с записью смены пользователя. """ users = record.msg @@ -307,7 +326,7 @@ class CsvFormatter(logging.Formatter): return msg -def log(user, admin=None): +def log(user: get_user_model(), admin: get_user_model() = None) -> None: """ Функция осуществляет запись логов в базу данных и csv файл. @@ -335,7 +354,7 @@ def set_session_params_for_work_page(request: WSGIRequest, count: int = None, is :param request: Получение данных с рабочей страницы пользователя :param count: Количество запрошенных тикетов - :param is_confirm: Назначение тикетов + :param is_confirm: Назначены ли тикеты :return: Перезагрузка страницы "Управление правами" соответствующего пользователя """ request.session['is_confirm'] = is_confirm diff --git a/main/forms.py b/main/forms.py index 929f3d9..e4db54a 100644 --- a/main/forms.py +++ b/main/forms.py @@ -1,5 +1,5 @@ """ -Формы. +Формы, использующиеся в приложении. """ from django import forms from django.contrib.auth.forms import AuthenticationForm diff --git a/main/models.py b/main/models.py index c934ab1..790a322 100644 --- a/main/models.py +++ b/main/models.py @@ -13,7 +13,6 @@ from access_controller.settings import ZENDESK_ROLES class UserProfile(models.Model): """ Модель профиля пользователя. - Профиль создается и изменяется при создании и изменении модель User. """ @@ -31,11 +30,7 @@ class UserProfile(models.Model): @property def zendesk_role(self) -> str: """ - Функция возвращает роль пользователя в Zendesk. - - В формате str, либо UNDEFINED, если пользователь не найден - - :return: Роль пользователя в Zendesk + Роль пользователя в Zendesk, либо UNDEFINED, если пользователь не найден. """ for role, r_id in ZENDESK_ROLES.items(): if r_id == self.custom_role_id: @@ -44,12 +39,12 @@ class UserProfile(models.Model): @receiver(post_save, sender=get_user_model()) -def create_user_profile(instance, created, **kwargs) -> None: +def create_user_profile(instance: get_user_model(), created: bool, **kwargs) -> None: """ Функция создания профиля пользователя (Userprofile) при регистрации пользователя. :param instance: Экземпляр класса User - :param created: Создание профиля пользователя + :param created: Существует ли пользователь :param kwargs: Параметры :return: Обновленный список объектов профилей пользователей """ @@ -58,7 +53,7 @@ def create_user_profile(instance, created, **kwargs) -> None: @receiver(post_save, sender=get_user_model()) -def save_user_profile(instance, **kwargs) -> None: +def save_user_profile(instance: get_user_model(), **kwargs) -> None: """ Функция записи БД профиля пользователя. @@ -84,7 +79,7 @@ class RoleChangeLogs(models.Model): class UnassignedTicketStatus(models.IntegerChoices): """ - Класс статусов не распределенных тикетов. + Модель статусов нераспределенных тикетов. :param UNASSIGNED: Снят с пользователя, перенесён в буферную группу :param RESTORED: Авторство восстановлено @@ -95,7 +90,7 @@ class UnassignedTicketStatus(models.IntegerChoices): """ UNASSIGNED = 0, 'Снят с пользователя, перенесён в буферную группу' RESTORED = 1, 'Авторство восстановлено' - NOT_FOUND = 2, 'Пока нас не было, тикет испарился из ' \ + NOT_FOUND = 2, 'Пока нас не было, тикет был перенесен из ' \ 'буферной группы. Дополнительные действия не требуются' CLOSED = 3, 'Тикет уже был закрыт. Дополнительные действия не требуются' SOLVED = 4, 'Тикет решён. Записан на пользователя с почтой SOLVED_TICKETS_EMAIL' diff --git a/main/requester.py b/main/requester.py index d0c57ed..f5ba9e4 100644 --- a/main/requester.py +++ b/main/requester.py @@ -1,6 +1,8 @@ """ -Обработка тикетов. +Обработка тикетов, составление списков тикетов для пользователя и группы пользователей. """ +from typing import Optional + import requests from zenpy import TicketApi from zenpy.lib.api_objects import Ticket @@ -11,6 +13,13 @@ from main.zendesk_admin import zenpy class TicketListRequester: """ Класс обработки тикетов. + + :param email: Email пользователя + :type display: :class:`str` + :param token_or_password: Токен или пароль + :type display: :class:`str` + :param prefix: Формат строка url страницы Zendesk + :type display: :class:`str` """ def __init__(self): self.email = zenpy.credentials['email'] @@ -21,16 +30,22 @@ class TicketListRequester: self.token_or_password = zenpy.credentials.get('password') self.prefix = f'https://{zenpy.credentials.get("subdomain")}.zendesk.com/api/v2/' - def get_tickets_list_for_user(self, zendesk_user: zenpy) -> str: + def get_tickets_list_for_user(self, zendesk_user: zenpy) -> Optional[list]: """ Функция получения списка тикетов пользователя Zendesk. + + :param zendesk_user: Пользователь Zendesk + :return: Список тикетов, назначенных на данного пользователя в Zendesk """ url = self.prefix + f'users/{zendesk_user.id}/tickets/assigned' return self._get_tickets(url) - def get_tickets_list_for_group(self, group: zenpy) -> list(): + def get_tickets_list_for_group(self, group: zenpy) -> Optional[list]: """ Функция получения списка тикетов группы пользователей Zendesk. + + :param group: Название группы + :return: Список тикетов """ url = self.prefix + '/tickets' all_tickets = self._get_tickets(url) @@ -40,9 +55,12 @@ class TicketListRequester: tickets.append(ticket) return tickets - def _get_tickets(self, url: str) -> list(): + def _get_tickets(self, url: str) -> Optional[list]: """ Функция получения полного списка тикетов по url. + + :param url: Url Zendesk c указанием тикетов, назначенных на пользователя + :return: Список тикетов """ response = requests.get(url, auth=(self.email, self.token_or_password)) tickets = [] diff --git a/main/serializers.py b/main/serializers.py index 70c4352..00b85a6 100644 --- a/main/serializers.py +++ b/main/serializers.py @@ -1,5 +1,5 @@ """ -Сериализаторы. +Сериализаторы, используемые в приложении. """ from django.contrib.auth import get_user_model from rest_framework import serializers diff --git a/main/views.py b/main/views.py index 1369d94..e58533c 100644 --- a/main/views.py +++ b/main/views.py @@ -61,7 +61,7 @@ def setup_context(**kwargs) -> Dict[str, Any]: class CustomRegistrationView(RegistrationView): """ - Отображение и логика работы страницы регистрации пользователя. + Класс отображения и логики работы страницы регистрации пользователя. :param form_class: Форма, которую необходимо заполнить для регистрации :type form_class: :class:`forms.CustomRegistrationForm` @@ -86,9 +86,12 @@ class CustomRegistrationView(RegistrationView): def register(self, form: CustomRegistrationForm) -> Optional[get_user_model()]: """ Функция регистрации пользователя. - 1. Ввод email пользователя, указанный на Zendesk + + 1. Ввод email пользователя, указанный на Zendesk. + 2. В случае если пользователь с данным паролем зарегистрирован на Zendesk и относится к организации SYSTEM, - происходит сброс ссылки с установлением пароля на указанный email + происходит сброс ссылки с установлением пароля на указанный email. + 3. Создается пользователь class User, а также его профиль. :param form: Email пользователя на Zendesk @@ -133,7 +136,7 @@ class CustomRegistrationView(RegistrationView): """ Функция дает разрешение на просмотр страница администратора, если пользователь имеет роль admin. - :param user: авторизованный пользователь (получает разрешение, имея роль "admin") + :param user: Авторизованный пользователь (получает разрешение, имея роль "admin") """ if user.userprofile.role == 'admin': content_type = ContentType.objects.get_for_model(UserProfile) @@ -148,8 +151,8 @@ class CustomRegistrationView(RegistrationView): Функция возвращает url-адрес страницы, куда нужно перейти после успешной/не успешной регистрации. Используется самой django-registration. - :param user: пользователь, пытающийся зарегистрироваться - :return: адресация на страницу успешной регистрации + :param user: Пользователь, пытающийся зарегистрироваться + :return: Адресация на страницу успешной регистрации """ return self.urls[self.redirect_url] @@ -158,8 +161,8 @@ def registration_error(request: WSGIRequest) -> HttpResponse: """ Функция отображения страницы ошибки регистрации. - :param request: регистрация - :return: адресация на страницу ошибки + :param request: Регистрация + :return: Адресация на страницу ошибки """ return render(request, 'django_registration/registration_error.html') @@ -169,8 +172,8 @@ def profile_page(request: WSGIRequest) -> HttpResponse: """ Функция отображения страницы профиля. - :param request: данные пользователя из БД - :return: адресация на страницу пользователя + :param request: Данные пользователя из БД + :return: Адресация на страницу пользователя """ user_profile: UserProfile = request.user.userprofile update_profile(user_profile) @@ -187,9 +190,9 @@ def work_page(request: WSGIRequest, required_id: int) -> HttpResponse: """ Функция отображения страницы "Управления правами" для текущего пользователя (login_required). - :param request: объект пользователя + :param request: Объект пользователя :param id: id пользователя, используется для динамической адресации - :return: адресация на страницу "Управления правами" (либо на страницу "Авторизации", если id и user.id не совпадают + :return: Адресация на страницу "Управления правами" (либо на страницу "Авторизации", если id и user.id не совпадают """ users = get_users_list() if request.user.id == required_id: @@ -227,8 +230,8 @@ def work_hand_over(request: WSGIRequest) -> HttpResponseRedirect: """ Функция позволяет текущему пользователю сдать права, а именно сменить в Zendesk роль с "engineer" на "light_agent" - :param request: данные текущего пользователя (login_required) - :return: перезагрузка текущей страницы после выполнения смены роли + :param request: Данные текущего пользователя (login_required) + :return: Перезагрузка текущей страницы после выполнения смены роли """ make_light_agent(request.user.userprofile, request.user) return set_session_params_for_work_page(request) @@ -240,8 +243,8 @@ def work_become_engineer(request: WSGIRequest) -> HttpResponseRedirect: Функция позволяет текущему пользователю получить права, а именно сменить в Zendesk роль с "light_agent" на "engineer". - :param request: данные текущего пользователя (login_required) - :return: перезагрузка текущей страницы после выполнения смены роли + :param request: Данные текущего пользователя (login_required) + :return: Перезагрузка текущей страницы после выполнения смены роли """ make_engineer(request.user.userprofile, request.user) return set_session_params_for_work_page(request) @@ -250,9 +253,10 @@ def work_become_engineer(request: WSGIRequest) -> HttpResponseRedirect: @login_required() def work_get_tickets(request: WSGIRequest) -> HttpResponse: """ + Функция получения тикетов в работу. - :param request: - :return: + :param request: Запрос на принятие тикетов в работу + :return: Перезагрузка рабочей страницы """ zenpy_user = zenpy.get_user(request.user.email) @@ -289,6 +293,8 @@ class AdminPageView(LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageM :type form_class: :class:`forms.AdminPageUsersForm` :param success_url: Адрес страницы администратора :type success_url: :class:`HttpResponseRedirect` + :param success_message: Уведомление об изменении прав + :type success_url: :class:`str` """ permission_required = 'main.has_control_access' template_name = 'pages/adm_ruleset.html' @@ -333,7 +339,12 @@ class AdminPageView(LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageM class CustomLoginView(LoginView): """ - Отображение страницы авторизации пользователя + Класс отображения страницы авторизации пользователя. + + :param extra_context: Добавление в контекст статус пользователя "залогинен" + :type extra_context: :class:`dict` + :param form_class: Форма страницы авторизации + :type form_class: :class: forms.CustomAuthenticationForm """ extra_context = setup_context(login_lit=True) form_class = CustomAuthenticationForm @@ -353,7 +364,8 @@ class UsersViewSet(viewsets.ReadOnlyModelViewSet): def list(self, request: WSGIRequest, *args, **kwargs) -> Response: """ - Функция возвращает список пользователей, список пользователей Zendesk, количество engineers и light-agents. + Функция возвращает список пользователей Zendesk, количество engineers и light-agents. + :param request: Запрос :param args: Аргументы :param kwargs: Параметры @@ -376,6 +388,7 @@ class UsersViewSet(viewsets.ReadOnlyModelViewSet): def choose_users(zendesk: list, model: list) -> list: """ Функция формирует список пользователей, которые не зарегистрированы у нас. + :param zendesk: Список пользователей Zendesk :param model: Список пользователей (модель Userprofile) :return: Список @@ -389,7 +402,8 @@ class UsersViewSet(viewsets.ReadOnlyModelViewSet): @staticmethod def get_zendesk_users(users: list) -> list: """ - Получение списка пользователей Zendesk, не являющихся админами. + Функция получения списка пользователей Zendesk, не являющихся админами. + :param users: Список пользователей :return: Список пользователей, не являющимися администраторами. """ @@ -406,8 +420,8 @@ def statistic_page(request: WSGIRequest) -> HttpResponse: """ Функция отображения страницы статистики (для "superuser"). - :param request: данные о пользователе: email, время и интервал работы. Данные получаем через forms.StatisticForm - :return: адресация на страницу статистики + :param request: Данные о пользователе: email, время и интервал работы. Данные получаем через forms.StFatisticForm + :return: Адресация на страницу статистики """ # if not request.user.has_perm('main.has_control_access'): diff --git a/venv/lib/python3.6/site-packages/enchant/tokenize/ru.py b/venv/lib/python3.6/site-packages/enchant/tokenize/ru.py deleted file mode 100644 index 7e15379..0000000 --- a/venv/lib/python3.6/site-packages/enchant/tokenize/ru.py +++ /dev/null @@ -1,185 +0,0 @@ -# pyenchant -# -# Copyright (C) 2004-2008, Ryan Kelly -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., 59 Temple Place - Suite 330, -# Boston, MA 02111-1307, USA. -# -# In addition, as a special exception, you are -# given permission to link the code of this program with -# non-LGPL Spelling Provider libraries (eg: a MSFT Office -# spell checker backend) and distribute linked combinations including -# the two. You must obey the GNU Lesser General Public License in all -# respects for all of the code used other than said providers. If you modify -# this file, you may extend this exception to your version of the -# file, but you are not obligated to do so. If you do not wish to -# do so, delete this exception statement from your version. -# -""" - - enchant.tokenize.en: Tokenizer for the English language - - This module implements a PyEnchant text tokenizer for the English - language, based on very simple rules. - -""" - -import unicodedata - -import enchant.tokenize - - -class tokenize(enchant.tokenize.tokenize): # noqa: N801 - """Iterator splitting text into words, reporting position. - - This iterator takes a text string as input, and yields tuples - representing each distinct word found in the text. The tuples - take the form: - - (,) - - Where is the word string found and is the position - of the start of the word within the text. - - The optional argument may be used to specify a - list of additional characters that can form part of a word. - By default, this list contains only the apostrophe ('). Note that - these characters cannot appear at the start or end of a word. - """ - - _DOC_ERRORS = ["pos", "pos"] - - def __init__(self, text, valid_chars=None): - self._valid_chars = valid_chars - self._text = text - self._offset = 0 - # Select proper implementation of self._consume_alpha. - # 'text' isn't necessarily a string (it could be e.g. a mutable array) - # so we can't use isinstance(text, str) to detect unicode. - # Instead we typetest the first character of the text. - # If there's no characters then it doesn't matter what implementation - # we use since it won't be called anyway. - try: - char1 = text[0] - except IndexError: - self._initialize_for_binary() - else: - if isinstance(char1, str): - self._initialize_for_unicode() - else: - self._initialize_for_binary() - - def _initialize_for_binary(self): - self._consume_alpha = self._consume_alpha_b - if self._valid_chars is None: - self._valid_chars = ("'",) - - def _initialize_for_unicode(self): - self._consume_alpha = self._consume_alpha_u - if self._valid_chars is None: - # XXX TODO: this doesn't seem to work correctly with the - # MySpell provider, disabling for now. - # Allow unicode typographic apostrophe - # self._valid_chars = (u"'",u"\u2019") - self._valid_chars = ("'",) - - def _consume_alpha_b(self, text, offset): - """Consume an alphabetic character from the given bytestring. - - Given a bytestring and the current offset, this method returns - the number of characters occupied by the next alphabetic character - in the string. Non-ASCII bytes are interpreted as utf-8 and can - result in multiple characters being consumed. - """ - assert offset < len(text) - if text[offset].isalpha(): - return 1 - elif text[offset] >= "\x80": - return self._consume_alpha_utf8(text, offset) - return 0 - - def _consume_alpha_utf8(self, text, offset): - """Consume a sequence of utf8 bytes forming an alphabetic character.""" - incr = 2 - u = "" - while not u and incr <= 4: - try: - try: - # In the common case this will be a string - u = text[offset : offset + incr].decode("utf8") - except AttributeError: - # Looks like it was e.g. a mutable char array. - try: - s = text[offset : offset + incr].tostring() - except AttributeError: - s = "".join([c for c in text[offset : offset + incr]]) - u = s.decode("utf8") - except UnicodeDecodeError: - incr += 1 - if not u: - return 0 - if u.isalpha(): - return incr - if unicodedata.category(u)[0] == "M": - return incr - return 0 - - def _consume_alpha_u(self, text, offset): - """Consume an alphabetic character from the given unicode string. - - Given a unicode string and the current offset, this method returns - the number of characters occupied by the next alphabetic character - in the string. Trailing combining characters are consumed as a - single letter. - """ - assert offset < len(text) - incr = 0 - if text[offset].isalpha(): - incr = 1 - while offset + incr < len(text): - if unicodedata.category(text[offset + incr])[0] != "M": - break - incr += 1 - return incr - - def next(self): - text = self._text - offset = self._offset - while offset < len(text): - # Find start of next word (must be alpha) - while offset < len(text): - incr = self._consume_alpha(text, offset) - if incr: - break - offset += 1 - cur_pos = offset - # Find end of word using, allowing valid_chars - while offset < len(text): - incr = self._consume_alpha(text, offset) - if not incr: - if text[offset] in self._valid_chars: - incr = 1 - else: - break - offset += incr - # Return if word isn't empty - if cur_pos != offset: - # Make sure word doesn't end with a valid_char - while text[offset - 1] in self._valid_chars: - offset = offset - 1 - self._offset = offset - return (text[cur_pos:offset], cur_pos) - self._offset = offset - raise StopIteration() From 029b602f96c26e2962a5a02b1c2c429a7cc80701 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D1=82=D0=B5=D0=BF=D0=B0=D0=BD=D0=B5=D0=BD=D0=BA?= =?UTF-8?q?=D0=BE=20=D0=9E=D0=BB=D1=8C=D0=B3=D0=B0?= Date: Thu, 20 May 2021 17:45:25 +0300 Subject: [PATCH 04/18] Update docs (statistic, zendesk_admin) --- main/statistic_data.py | 30 +++++++++++++++++++++--------- main/zendesk_admin.py | 7 +++---- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/main/statistic_data.py b/main/statistic_data.py index f569c68..e5f4fdc 100644 --- a/main/statistic_data.py +++ b/main/statistic_data.py @@ -172,8 +172,8 @@ class StatisticData: """ Функция возвращает логи в диапазоне дат start_date - end_date для пользователя с указанным email. - :return: Данные о смене статусов пользователя. Если пользователь не найден или - интервал времени некорректен - ошибку. + :return: Данные о смене статусов пользователя. + Если пользователь не найден или интервал времени некорректен - ошибку. """ if not self.check_time(): self.errors += ['Конец диапазона должен быть позже начала диапазона и раньше текущего времени'] @@ -208,9 +208,12 @@ class StatisticData: if self.data[log_index].new_role == ROLES['engineer']: self.engineer_logic(log_index) - def engineer_logic(self, log_index): + def engineer_logic(self, log_index: int) -> None: """ - Функция обрабатывает основную часть работы инженера + Функция обрабатывает подсчета времени работы инженера. + + :param log_index: Индекс текущего лога + :return: Дополняет статистику работы инженера временем между текущим и последующим логом """ current_log, next_log = self.data[log_index], self.data[log_index + 1] if current_log.change_time.date() != next_log.change_time.date(): @@ -222,9 +225,12 @@ class StatisticData: elapsed_time = next_log.change_time - current_log.change_time self.statistic[current_log.change_time.date()] += elapsed_time.total_seconds() - def post_engineer_logic(self, last_log): + def post_engineer_logic(self, last_log: RoleChangeLogs) -> None: """ - Функция обрабатывает случай, когда нам изветсно что инженер работал и после диапазона + Функция обрабатывает случай, когда пользователя назначили инженером в последнем логе. + + :param last_log: Последний лог изменения роли, в результате которого пользователь назначен инженером. + :return: Дополняет статистику работы """ self.fill_daterange(last_log.change_time.date() + timedelta(days=1), self.end_date + timedelta(days=1)) if last_log.change_time.date() == timezone.now().date(): @@ -237,9 +243,12 @@ class StatisticData: if self.end_date == timezone.now().date(): self.statistic[self.end_date] = get_timedelta(None, timezone.now().time()).total_seconds() - def prev_engineer_logic(self, first_log): + def prev_engineer_logic(self, first_log: RoleChangeLogs) -> None: """ - Функция обрабатывает случай, когда нам изветсно что инженер начал работу до диапазона + Функция обрабатывает случай, когда пользователь в первом логе диапазона был назначен легким агентом. + + :param first_log_log: Первый лог в диапазоне, в результате которого пользователь назначен легким агентом. + :return: Дополняет статистику работы """ self.fill_daterange(max(get_user_model().objects.get(email=self.email).date_joined.date(), self.start_date), first_log.change_time.date()) @@ -258,7 +267,10 @@ class StatisticData: def clear_statistic(self) -> dict: """ - Функция осуществляет обновление всех дней. + Функция осуществляет очищает статистику и устанавливает время в диапазоне start_date - end_date в количестве + 24*3600 секунд. + + :return: Обновленная статистика """ self.statistic.clear() self.fill_daterange(self.start_date, self.end_date + timedelta(days=1), 0) diff --git a/main/zendesk_admin.py b/main/zendesk_admin.py index c6a383f..a979ca4 100644 --- a/main/zendesk_admin.py +++ b/main/zendesk_admin.py @@ -32,7 +32,7 @@ class ZendeskAdmin: self.buffer_group_id= self.get_group(ZENDESK_GROUPS['buffer']).id self.solved_tickets_user_id = self.get_user(SOLVED_TICKETS_EMAIL).id - def update_user(self, user: ZenpyUser) -> bool: + def update_user(self, user: ZenpyUser) -> None: """ Функция сохраняет изменение пользователя в Zendesk. @@ -40,7 +40,7 @@ class ZendeskAdmin: """ self.admin.users.update(user) - def update_tickets(self, tickets: List[ZenpyTicket]): + def update_tickets(self, tickets: List[ZenpyTicket]) -> None: """ Функция сохраняет изменение тикетов в Zendesk. @@ -79,7 +79,7 @@ class ZendeskAdmin: return group return None - def get_user_org(self, email: str) -> str: + def get_user_org(self, email: str) -> Optional[str]: """ Функция возвращает организацию, к которой относится пользователь по его email. @@ -96,7 +96,6 @@ class ZendeskAdmin: :raise: :class:`ValueError`: исключение, вызываемое если email не введен в env :raise: :class:`APIException`: исключение, вызываемое если пользователя с таким email не существует в Zendesk """ - if self.credentials.get('email') is None: raise ValueError('access_controller email not in env') From 26f32327b3914a4221130a12f47a7540af4b4989 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D1=82=D0=B5=D0=BF=D0=B0=D0=BD=D0=B5=D0=BD=D0=BA?= =?UTF-8?q?=D0=BE=20=D0=9E=D0=BB=D1=8C=D0=B3=D0=B0?= Date: Thu, 20 May 2021 18:49:04 +0300 Subject: [PATCH 05/18] Update REDME --- README.md | 4 ++-- README.rst | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b3376cf..f989953 100644 --- a/README.md +++ b/README.md @@ -157,13 +157,13 @@ autopep8 --in-place filename ##Для проверки орфографии: cd docs -(set -a && source ../.env && make spelling) +make spelling ##Для обновления документации: m2r README.md cd docs -(set -a && source ../.env && make html) +make html ## Read more diff --git a/README.rst b/README.rst index fb32f4b..8c9907d 100644 --- a/README.rst +++ b/README.rst @@ -182,7 +182,7 @@ autopep8 --in-place filename cd docs -(set -a && source ../.env && make spelling) +make spelling Для обновления документации: ---------------------------- @@ -191,7 +191,7 @@ m2r README.md cd docs -(set -a && source ../.env && make html) +make html Read more --------- From 45ac48448044b3c716595fdf028d313f0bd6005f Mon Sep 17 00:00:00 2001 From: Andrew Smirnov Date: Thu, 20 May 2021 20:41:55 +0300 Subject: [PATCH 06/18] Update statistic docs --- main/extra_func.py | 4 ++-- main/statistic_data.py | 44 ++++++++++++++++++++++++++++-------------- 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/main/extra_func.py b/main/extra_func.py index 1c87f65..53bf919 100644 --- a/main/extra_func.py +++ b/main/extra_func.py @@ -2,7 +2,7 @@ Вспомогательные функции. """ import logging -from datetime import timedelta +from datetime import timedelta, date from typing import Union, Optional from django.contrib.auth import get_user_model @@ -219,7 +219,7 @@ def update_users_in_model() -> list: return users -def daterange(start_date: timedelta, end_date: timedelta) -> list: +def daterange(start_date: date, end_date: date) -> list: """ Функция возвращает список дней с start_date по end_date, исключая правую границу. diff --git a/main/statistic_data.py b/main/statistic_data.py index e5f4fdc..cfb506e 100644 --- a/main/statistic_data.py +++ b/main/statistic_data.py @@ -1,6 +1,9 @@ """ Обработка статистики. + +Обнаруживает факт изменения роли пользователя и вычисляет отработанное на смене время. """ + from datetime import date, datetime, timedelta from typing import Optional @@ -14,7 +17,7 @@ from main.models import RoleChangeLogs class StatisticData: """ - Класс для учета статистики интервалов работы пользователей. + Класс для учета статистики времени работы пользователей. Передаваемые параметры: start_date, end_date, email, stat. :param display: Формат отображения времени (часы, минуты) @@ -37,7 +40,7 @@ class StatisticData: :type statistic: :class:`dict` """ - def __init__(self, start_date, end_date, user_email, stat=None): + def __init__(self, start_date, end_date, user_email: str, stat=None): self.display = None self.interval = None self.start_date = start_date @@ -57,7 +60,8 @@ class StatisticData: """ Функция возвращает статистику работы пользователя. - :return: Словарь statistic с применением формата отображения и интервала работы(если они есть). + :return: Словарь statistic с применением формата отображения + и интервала работы (если они есть). None, если были ошибки при создании. """ if self.is_valid_statistic(): @@ -117,7 +121,7 @@ class StatisticData: """ return not self.errors - def _use_display(self, stat: list) -> list: + def _use_display(self, stat: dict) -> dict: """ Функция приводит данные к формату отображения. @@ -136,7 +140,9 @@ class StatisticData: def _use_interval(self, stat: dict) -> dict: """ - Функция объединяет ключи и значения в соответствии с интервалом работы. + Переупаковка результата в соответствии с указанным временным диапазоном + + Сжимает набор дней в месяцы, если указан режим работы "по месяцам" :param stat: Статистика работы пользователя :return: Обновленная статистика @@ -210,7 +216,7 @@ class StatisticData: def engineer_logic(self, log_index: int) -> None: """ - Функция обрабатывает подсчета времени работы инженера. + Функция вычисляет время работы инженера. :param log_index: Индекс текущего лога :return: Дополняет статистику работы инженера временем между текущим и последующим логом @@ -227,7 +233,9 @@ class StatisticData: def post_engineer_logic(self, last_log: RoleChangeLogs) -> None: """ - Функция обрабатывает случай, когда пользователя назначили инженером в последнем логе. + Обработка случая, в котором инженер не закрыл смену. + + В таком случае считается всё время от момента открытия смены до текущего момента. :param last_log: Последний лог изменения роли, в результате которого пользователь назначен инженером. :return: Дополняет статистику работы @@ -245,16 +253,23 @@ class StatisticData: def prev_engineer_logic(self, first_log: RoleChangeLogs) -> None: """ - Функция обрабатывает случай, когда пользователь в первом логе диапазона был назначен легким агентом. + Обработка случая, в котором инженер закрыл смену в отражаемом периоде, а открыл её до этого периода. + + В таком случае должен быть учтён только период от начала отображаемого диапазона до закрытия смены. :param first_log_log: Первый лог в диапазоне, в результате которого пользователь назначен легким агентом. :return: Дополняет статистику работы """ - self.fill_daterange(max(get_user_model().objects.get(email=self.email).date_joined.date(), self.start_date), - first_log.change_time.date()) + self.fill_daterange( + max( + get_user_model().objects.get(email=self.email).date_joined.date(), + self.start_date + ), + first_log.change_time.date() + ) self.statistic[first_log.change_time.date()] += get_timedelta(first_log).total_seconds() - def fill_daterange(self, first: date, last: date, val: int = 24 * 3600) -> dict: + def fill_daterange(self, first: date, last: date, val: int = 24 * 3600) -> None: """ Функция заполняет диапазон дат значением val (по умолчанию val = кол-во секунд в 1 дне). @@ -265,12 +280,11 @@ class StatisticData: for day in daterange(first, last): self.statistic[day] = val - def clear_statistic(self) -> dict: + def clear_statistic(self) -> None: """ - Функция осуществляет очищает статистику и устанавливает время в диапазоне start_date - end_date в количестве - 24*3600 секунд. + Чистка статистики и установка времени по умолчанию. - :return: Обновленная статистика + Устанавливает время смены в 0 """ self.statistic.clear() self.fill_daterange(self.start_date, self.end_date + timedelta(days=1), 0) From 975b6085770f055eff16f6bd053b28cd45575a12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D1=82=D0=B5=D0=BF=D0=B0=D0=BD=D0=B5=D0=BD=D0=BA?= =?UTF-8?q?=D0=BE=20=D0=9E=D0=BB=D1=8C=D0=B3=D0=B0?= Date: Sun, 23 May 2021 17:09:52 +0300 Subject: [PATCH 07/18] Add tests docs --- access_controller/settings.py | 1 + main/extra_func.py | 1 - main/tests.py | 345 ++++++++++++++++++++++++++++------ 3 files changed, 288 insertions(+), 59 deletions(-) diff --git a/access_controller/settings.py b/access_controller/settings.py index 6293efc..30875f6 100644 --- a/access_controller/settings.py +++ b/access_controller/settings.py @@ -10,6 +10,7 @@ For the full list of settings and their values, see https://docs.djangoproject.com/en/3.1/ref/settings/ """ import os + from pathlib import Path diff --git a/main/extra_func.py b/main/extra_func.py index 53bf919..e652a7e 100644 --- a/main/extra_func.py +++ b/main/extra_func.py @@ -29,7 +29,6 @@ def update_role(user_profile: UserProfile, role: int, who_changes: get_user_mode :param user_profile: Профиль пользователя :param role: Новая роль :param who_changes: Пользователь, меняющий роль - :return: Пользователь с обновленной ролью """ zendesk = zenpy user = zendesk.get_user(user_profile.user.email) diff --git a/main/tests.py b/main/tests.py index 60f31ed..4353ddf 100644 --- a/main/tests.py +++ b/main/tests.py @@ -1,7 +1,8 @@ import random from unittest.mock import patch, Mock -from django.contrib.auth.models import User +from django.contrib.auth import get_user_model +# from django.contrib.auth.models import User from django.core import mail from django.http import HttpResponseRedirect from django.template.loader import render_to_string @@ -16,47 +17,112 @@ from main.extra_func import log class UsersBaseTestCase(TestCase): - """Базовый класс загружения данных для тестов с пользователями""" + """ + Базовый класс загрузки данных для тестов с пользователями. + + Для тестов используются фикстуры тестовых пользователей (test_users.json). + """ fixtures = ['fixtures/test_users.json'] - def setUp(self): - """Добавление в переменные почт и клиентов для пользователей""" + def setUp(self) -> None: + """ + Функция предустановки значений переменных. + + Добавляем email тестовых пользователей и создаем клиентов для тестов. + + :param light_agent: email тестового пользователя с правами light_agent + :type light_agent: :class:`str` + :param engineer: email тестового пользователя с правами engineer + :type engineer: :class:`str` + :param admin: email тестового пользователя с правами admin + :type admin: :class:`str` + :param agent_client: клиент, залогиненный как пользователь с email light_agent + :type agent_client: :class:`django.test.client.Client` + :param engineer_client: клиент, залогиненный как пользователь с email engineer + :type engineer_client: :class:`django.test.client.Client` + :param admin_client: клиент, залогиненный как пользователь с email admin + :type admin_client: :class:`django.test.client.Client` + """ self.light_agent = '123@test.ru' self.admin = 'admin@gmail.com' - self.engineer = 'customer@example.com' + self.engineer = 'customer@example.com' self.agent_client = Client() - self.agent_client.force_login(User.objects.get(email=self.light_agent)) + self.agent_client.force_login(get_user_model().objects.get(email=self.light_agent)) self.admin_client = Client() - self.admin_client.force_login(User.objects.get(email=self.admin)) + self.admin_client.force_login(get_user_model().objects.get(email=self.admin)) self.engineer_client = Client() - self.engineer_client.force_login(User.objects.get(email=self.engineer)) + self.engineer_client.force_login(get_user_model().objects.get(email=self.engineer)) class RegistrationTestCase(TestCase): + """ + Класс тестирования регистрации. + + Для тестов используются фикстуры с данными пользователей engeneer и light_agent (data.json). + """ fixtures = ['fixtures/data.json'] - def setUp(self): + def setUp(self) -> None: + """ + Функция предустановки значений переменных. + + Добавляем email тестовых пользователей и создаем клиентов для тестов. + + :param email_backend: locmem бэкенд со списком отправленных писем + :type email_backend: :class:`str` + :param any_zendesk_user_email: email пользователя, зарегистрированного на Zendesk + :type any_zendesk_user_email: :class:`str` + :param zendesk_admin_email: email администратора + :type zendesk_admin_email: :class:`str` + :param client: новый клиент + :type client: :class:`django.test.client.Client` + + """ self.email_backend = 'django.core.mail.backends.locmem.EmailBackend' self.any_zendesk_user_email = 'idar.sokurov.05@mail.ru' self.zendesk_admin_email = 'idar.sokurov.05@mail.ru' self.client = Client() - def test_registration_complete_redirect(self): + def test_registration_complete_redirect(self) -> None: + """ + Функция тестирования успешной регистрации пользователя. + + Проверяет, что в случае если email пользователя зарегистрирован на Zendesk, была заполнена форма регистрации + и направлено письмо со ссылкой для завершения регистрации, происходит редирект на страницу завершения + регистрации. + """ with self.settings(EMAIL_BACKEND=self.email_backend): resp = self.client.post(reverse('registration'), data={'email': self.any_zendesk_user_email}) self.assertRedirects(resp, reverse('password_reset_done')) - def test_registration_fail_redirect(self): + def test_registration_fail_redirect(self) -> None: + """ + Функция тестирования неуспешной регистрации пользователя (введен email, не зарегистированный на Zendesk). + + Проверяет, что происходит редирект на страницу "registration disallowed" + """ with self.settings(EMAIL_BACKEND=self.email_backend): resp = self.client.post(reverse('registration'), data={'email': self.any_zendesk_user_email + 'asd'}) self.assertRedirects(resp, reverse('django_registration_disallowed')) - def test_registration_user_already_exist(self): + def test_registration_user_already_exist(self) -> None: + """ + Функция тестирования попытки зарегистрироваться, используя email уже зарегистрированного в приложении + пользователя ("123@test.ru"). + + Проверяет, что пользователь получает сообщение "Этот адрес электронной почты уже используется" + """ with self.settings(EMAIL_BACKEND=self.email_backend) and translation.override('ru'): resp = self.client.post(reverse('registration'), data={'email': '123@test.ru'}) self.assertContains(resp, 'Этот адрес электронной почты уже используется', count=1, status_code=200) - def test_registration_send_email(self): + def test_registration_send_email(self) -> None: + """ + Функция тестирования отправки email пользователю при регистрации. + + Проверяет отправку уведомления на указанный пользователем адрес, а также содержание письма (заголовка и тела) + через email locmem backend. + """ with self.settings(EMAIL_BACKEND=self.email_backend): response: HttpResponseRedirect = \ self.client.post(reverse('registration'), data={'email': self.any_zendesk_user_email}) @@ -71,50 +137,88 @@ class RegistrationTestCase(TestCase): 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): + def test_registration_user_creating(self) -> None: + """ + Функция тестирования создания пользователя приложения при регистрации. + + Проверяет соответствие имени созданного пользователя с именем пользователя в Zendesk + """ with self.settings(EMAIL_BACKEND=self.email_backend): self.client.post(reverse('registration'), data={'email': self.any_zendesk_user_email}) - user = User.objects.get(email=self.any_zendesk_user_email) + user = get_user_model().objects.get(email=self.any_zendesk_user_email) zendesk_user = zenpy.get_user(self.any_zendesk_user_email) self.assertEqual(user.userprofile.name, zendesk_user.name) - def test_permissions_applying(self): + def test_permissions_applying(self) -> None: + """ + Функция тестирования создания администратора и присвоения ему соответствующих прав. + + Проверяет, что у созданного пользователя роль "admin" и права "has_control_access". + """ with self.settings(EMAIL_BACKEND=self.email_backend): self.client.post(reverse('registration'), data={'email': self.zendesk_admin_email}) - user = User.objects.get(email=self.zendesk_admin_email) + user = get_user_model().objects.get(email=self.zendesk_admin_email) self.assertEqual(user.userprofile.role, 'admin') self.assertTrue(user.has_perm('main.has_control_access')) class MakeEngineerTestCase(UsersBaseTestCase): + """ + Класс тестирования присвоения пользователю роли engineer. + + В тестах используется @patch('main.extra_func.zenpy') замещающий API Zendesk. + """ @patch('main.extra_func.zenpy') - def test_become_engineer_redirect(self, _zenpy_mock): - user = User.objects.get(email=self.light_agent) + def test_become_engineer_redirect(self, _zenpy_mock: zenpy) -> None: + """ + Функция тестирования редиректа на рабочую страницу тестового пользователя при назначении его инженером. + """ + user = get_user_model().objects.get(email=self.light_agent) resp = self.agent_client.post(reverse_lazy('work_become_engineer')) self.assertRedirects(resp, reverse('work', args=[user.id])) self.assertEqual(resp.status_code, 302) @patch('main.extra_func.zenpy') - def test_light_agent_make_engineer(self, zenpy_mock): + def test_light_agent_make_engineer(self, zenpy_mock: zenpy) -> None: + """ + Функция тестирования назначения легкого агента на роль инженера. + + Проверяет установку роли "engineer" в Zendesk. + """ self.agent_client.post(reverse_lazy('work_become_engineer')) self.assertEqual(zenpy_mock.update_user.call_args[0][0].custom_role_id, sets.ZENDESK_ROLES['engineer']) @patch('main.extra_func.zenpy') - def test_admin_make_engineer(self, zenpy_mock): + def test_admin_make_engineer(self, zenpy_mock: zenpy) -> None: + """ + Функция тестирования назначения администратора на роль инженера. + + Проверяет установку роли "engineer" в Zendesk. + """ self.admin_client.post(reverse_lazy('work_become_engineer')) self.assertEqual(zenpy_mock.update_user.call_args[0][0].custom_role_id, sets.ZENDESK_ROLES['engineer']) @patch('main.extra_func.zenpy') - def test_engineer_make_engineer(self, zenpy_mock): + def test_engineer_make_engineer(self, zenpy_mock: zenpy) -> None: + """ + Функция тестирования назначения инженера на роль инженера. + + Проверяет установку роли "engineer" в Zendesk. + """ self.engineer_client.post(reverse_lazy('work_become_engineer')) self.assertEqual(zenpy_mock.update_user.call_args[0][0].custom_role_id, sets.ZENDESK_ROLES['engineer']) @patch('main.extra_func.zenpy') - def test_control_page_make_engineer_one(self, zenpy_mock): + def test_control_page_make_engineer_one(self, zenpy_mock: zenpy) -> None: + """ + Функция тестирования назначения администратором одного инженера на странице "Управление". + + Проверяет обновление администратором роли пользователя с light_agent на engineer. + """ self.admin_client.post( reverse_lazy('control'), - data={'users': [User.objects.get(email=self.light_agent).userprofile.id], 'engineer': 'engineer'} + data={'users': [get_user_model().objects.get(email=self.light_agent).userprofile.id], 'engineer': 'engineer'} ) call_list = zenpy_mock.update_user.call_args_list mock_object = call_list[0][0][0] @@ -122,13 +226,18 @@ class MakeEngineerTestCase(UsersBaseTestCase): self.assertEqual(mock_object.custom_role_id, sets.ZENDESK_ROLES['engineer']) @patch('main.extra_func.zenpy') - def test_control_page_make_engineer_many(self, zenpy_mock): + def test_control_page_make_engineer_many(self, zenpy_mock: zenpy) -> None: + """ + Функция тестирования назначения администратором нескольких инженеров на странице "Управление". + + Проверяет обновление администратором ролей двух пользователей с light_agent на engineer. + """ self.admin_client.post( reverse_lazy('control'), data={ 'users': [ - User.objects.get(email=self.light_agent).userprofile.id, - User.objects.get(email=self.engineer).userprofile.id, + get_user_model().objects.get(email=self.light_agent).userprofile.id, + get_user_model().objects.get(email=self.engineer).userprofile.id, ], 'engineer': 'engineer' } @@ -141,18 +250,33 @@ class MakeEngineerTestCase(UsersBaseTestCase): class MakeLightAgentTestCase(UsersBaseTestCase): + """ + Класс тестирования присвоения пользователю роли light_agent. + + В тестах используется @patch('main.extra_func.zenpy') замещающий API Zendesk, а также + @patch('main.requester.TicketListRequester.get_tickets_list_for_user', side_effect=[[]]), предоставляющий список + тикетов. + """ @patch('main.requester.TicketListRequester.get_tickets_list_for_user', side_effect=[[]]) @patch('main.extra_func.zenpy') - def test_hand_over_redirect(self, _zenpy_mock, _user_tickets_mock): - user = User.objects.get(email=self.engineer) + def test_hand_over_redirect(self, _zenpy_mock: zenpy, _user_tickets_mock: list) -> None: + """ + Функция тестирования переадресации инженера на рабочую страницу, после сдачи прав. + """ + user = get_user_model().objects.get(email=self.engineer) resp = self.engineer_client.post(reverse_lazy('work_hand_over')) self.assertRedirects(resp, reverse('work', args=[user.id])) self.assertEqual(resp.status_code, 302) @patch('main.requester.TicketListRequester.get_tickets_list_for_user', side_effect=[[]]) @patch('main.extra_func.zenpy') - def test_engineer_make_light_agent_no_tickets(self, zenpy_mock, _user_tickets_mock): + def test_engineer_make_light_agent_no_tickets(self, zenpy_mock: zenpy, _user_tickets_mock: list) -> None: + """ + Функция тестирования назначения инженера легким агентом, в случае, когда у него в работе нет тикетов. + + Проверяет назначение роли light_agent в Zendesk. + """ self.engineer_client.post(reverse_lazy('work_hand_over')) self.assertEqual(zenpy_mock.update_user.call_args[0][0].custom_role_id, sets.ZENDESK_ROLES['light_agent']) @@ -160,7 +284,15 @@ class MakeLightAgentTestCase(UsersBaseTestCase): [Mock(id=1, status='solved'), Mock(id=2, status='open'), Mock(id=3, status='open')] ]) @patch('main.extra_func.zenpy') - def test_engineer_make_light_agent_with_tickets(self, zenpy_mock, _user_tickets_mock): + def test_engineer_make_light_agent_with_tickets(self, zenpy_mock: zenpy, _user_tickets_mock: list): + """ + Функция тестирования назначения инженера легким агентом, в случае, когда у него в работе есть тикеты. + + Для тестирования принимается, что в работе у инженера находится 3 тикета, один в состоянии: решен, + два в состоянии: открыт. + Проверяет распределение тикетов (поместить в решенные или назначить нового ответственного), + а также назначение роли light_agent в Zendesk. + """ zenpy_mock.solved_tickets_user_id = Mock() self.engineer_client.post(reverse_lazy('work_hand_over')) @@ -172,7 +304,12 @@ class MakeLightAgentTestCase(UsersBaseTestCase): @patch('main.requester.TicketListRequester.get_tickets_list_for_user', side_effect=[[]]) @patch('main.extra_func.zenpy') - def test_admin_make_light_agent_no_tickets(self, zenpy_mock, _user_tickets_mock): + def test_admin_make_light_agent_no_tickets(self, zenpy_mock: zenpy, _user_tickets_mock: list) -> None: + """ + Функция тестирования назначения администратора на роль легкого агента. + + Проверяет назначение роли light_agent в Zendesk. + """ self.admin_client.post(reverse_lazy('work_hand_over')) self.assertEqual(zenpy_mock.update_user.call_args[0][0].custom_role_id, sets.ZENDESK_ROLES['light_agent']) @@ -180,7 +317,15 @@ class MakeLightAgentTestCase(UsersBaseTestCase): [Mock(id=1, status='solved'), Mock(id=2, status='open'), Mock(id=3, status='open')] ]) @patch('main.extra_func.zenpy') - def test_admin_make_light_agent_with_tickets(self, zenpy_mock, _user_tickets_mock): + def test_admin_make_light_agent_with_tickets(self, zenpy_mock: zenpy, _user_tickets_mock: list) -> None: + """ + Функция тестирования назначения администратора легким агентом, в случае, когда у него в работе есть тикеты. + + Для тестирования принимается, что в работе находится 3 тикета, один в состоянии: решен, + два в состоянии: открыт. + Проверяет распределение тикетов (поместить в решенные или назначить нового ответственного), + а также назначение роли light_agent в Zendesk. + """ zenpy_mock.solved_tickets_user_id = Mock() self.admin_client.post(reverse_lazy('work_hand_over')) @@ -192,16 +337,26 @@ class MakeLightAgentTestCase(UsersBaseTestCase): @patch('main.requester.TicketListRequester.get_tickets_list_for_user', side_effect=[[]]) @patch('main.extra_func.zenpy') - def test_light_agent_make_light_agent(self, zenpy_mock, _user_tickets_mock): + def test_light_agent_make_light_agent(self, zenpy_mock: zenpy, _user_tickets_mock: list): + """ + Функция тестирования назначения легкого агента на роль легкого агента. + + Проверяет назначение роли light_agent в Zendesk. + """ self.agent_client.post(reverse_lazy('work_hand_over')) self.assertEqual(zenpy_mock.update_user.call_args[0][0].custom_role_id, sets.ZENDESK_ROLES['light_agent']) @patch('main.requester.TicketListRequester.get_tickets_list_for_user', side_effect=[[]]) @patch('main.extra_func.zenpy') - def test_control_page_make_light_agent_one(self, zenpy_mock, _user_tickets_mock): + def test_control_page_make_light_agent_one(self, zenpy_mock: zenpy, _user_tickets_mock: list) -> None: + """ + Функция тестирования назначения администратором одного легкого агента на странице "Управление". + + Проверяет обновление администратором роли пользователя с engineer на light_agent. + """ self.admin_client.post( reverse_lazy('control'), - data={'users': [User.objects.get(email=self.engineer).userprofile.id], 'light_agent': 'light_agent'} + data={'users': [get_user_model().objects.get(email=self.engineer).userprofile.id], 'light_agent': 'light_agent'} ) call_list = zenpy_mock.update_user.call_args_list mock_object = call_list[0][0][0] @@ -210,13 +365,19 @@ class MakeLightAgentTestCase(UsersBaseTestCase): @patch('main.requester.TicketListRequester.get_tickets_list_for_user', side_effect=[[], []]) @patch('main.extra_func.zenpy') - def test_control_page_make_light_agent_many(self, zenpy_mock, _user_tickets_mock): + def test_control_page_make_light_agent_many(self, zenpy_mock: zenpy, _user_tickets_mock: list) -> None: + """ + Функция тестирования назначения администратором нескольких легких агентов на странице "Управление". + + Проверяет обновление администратором ролей двух пользователей с engineer на light_agent. + """ + self.admin_client.post( reverse_lazy('control'), data={ 'users': [ - User.objects.get(email=self.light_agent).userprofile.id, - User.objects.get(email=self.engineer).userprofile.id, + get_user_model().objects.get(email=self.light_agent).userprofile.id, + get_user_model().objects.get(email=self.engineer).userprofile.id, ], 'light_agent': 'light_agent' } @@ -229,18 +390,32 @@ class MakeLightAgentTestCase(UsersBaseTestCase): class PasswordResetTestCase(UsersBaseTestCase): + """ + Класс тестирования сброса пароля. + """ def setUp(self): super().setUp() self.email_backend = 'django.core.mail.backends.locmem.EmailBackend' - def test_redirect(self): + def test_redirect(self) -> None: + """ + Функция тестирования успешной смены пароля. + + Проверяется переадресация на страницу завершения смены пароля, в случае, когда пользователь существует и на его + email было направлено письмо для сброса пароля. + """ with self.settings(EMAIL_BACKEND=self.email_backend): resp = self.agent_client.post(reverse_lazy('password_reset'), data={'email': self.light_agent}) self.assertRedirects(resp, reverse('password_reset_done')) self.assertEqual(resp.status_code, 302) - def test_send_email(self): + def test_send_email(self) -> None: + """ + Функция тестирования отправки email для сброса пароля. + + Проверяет наличие отправленного письма, и его содержание, сверяет email адресата с email пользователя. + """ with self.settings(EMAIL_BACKEND=self.email_backend): response: HttpResponseRedirect = \ self.agent_client.post(reverse_lazy('password_reset'), data={'email': self.light_agent}) @@ -255,12 +430,22 @@ class PasswordResetTestCase(UsersBaseTestCase): 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): + def test_email_invalid(self) -> None: + """ + Функция тестирования попытки смены пароля с некорректным email. + + Проверяет уведомление пользователя о неверном адресе электронной почты. + """ with self.settings(EMAIL_BACKEND=self.email_backend) and translation.override('ru'): resp = self.agent_client.post(reverse_lazy('password_reset'), data={'email': 1}) self.assertContains(resp, 'Введите правильный адрес электронной почты.', count=1, status_code=200) - def test_user_does_not_exist(self): + def test_user_does_not_exist(self) -> None: + """ + Функция тестирования попытки смены пароля с email, который не зарегистрирован. + + Проверяет отсутствие отправки письма о смене пароля. + """ with self.settings(EMAIL_BACKEND=self.email_backend): resp = self.agent_client.post(reverse_lazy('password_reset'), data={'email': self.light_agent + str(random.random())}) self.assertRedirects(resp, reverse('password_reset_done')) @@ -269,18 +454,31 @@ class PasswordResetTestCase(UsersBaseTestCase): class PasswordChangeTestCase(UsersBaseTestCase): + """ + Класс тестирования смены пароля. + """ - def setUp(self): + def setUp(self) -> None: super().setUp() self.set_password() - def set_password(self): - user: User = User.objects.get(email=self.light_agent) + def set_password(self) -> None: + """ + Функция предустанавливает тестовому пользователю с ролью light_agent пароль 'ImpossiblyHardPassword' и создает + клиента с соответствующими данным для тестирования. + """ + user = get_user_model().objects.get(email=self.light_agent) user.set_password('ImpossiblyHardPassword') user.save() - self.agent_client.force_login(User.objects.get(email=self.light_agent)) + self.agent_client.force_login(get_user_model().objects.get(email=self.light_agent)) - def test_change_successful(self): + def test_change_successful(self) -> None: + """ + Функция тестирования успешной смены пароля. + + Проверяет установку нового пароля пользователю при вводе корректных данных: старый пароль, новый пароль + (2 раза). + """ self.agent_client.post( reverse_lazy('password_change'), data={ @@ -289,10 +487,15 @@ class PasswordChangeTestCase(UsersBaseTestCase): 'new_password2': 'EasyPassword', } ) - user = User.objects.get(email=self.light_agent) + user = get_user_model().objects.get(email=self.light_agent) self.assertTrue(user.check_password('EasyPassword')) - def test_invalid_old_password(self): + def test_invalid_old_password(self) -> None: + """ + Функция тестирования смены пароля, при неверном вводе старого пароля. + + Проверяет текст уведомления пользователя 'Ваш старый пароль введен неправильно'. + """ with translation.override('ru'): resp = self.agent_client.post( reverse_lazy('password_change'), @@ -304,7 +507,12 @@ class PasswordChangeTestCase(UsersBaseTestCase): ) self.assertContains(resp, 'Ваш старый пароль введен неправильно', count=1, status_code=200) - def test_different_new_passwords(self): + def test_different_new_passwords(self) -> None: + """ + Функция тестирования смены пароля, при вводе несовпадающих новых паролей. + + Проверяет текст уведомления пользователя 'Введенные пароли не совпадают'. + """ with translation.override('ru'): resp = self.agent_client.post( reverse_lazy('password_change'), @@ -317,6 +525,11 @@ class PasswordChangeTestCase(UsersBaseTestCase): self.assertContains(resp, 'Введенные пароли не совпадают', count=1, status_code=200) def test_invalid_new_password1(self): + """ + Функция тестирования попытки смены пароля, когда новый пароль не соответствует требованиям: слишком короткий. + + Проверяет текст уведомления пользователя 'Введённый пароль слишком короткий'. + """ with translation.override('ru'): resp = self.agent_client.post( reverse_lazy('password_change'), @@ -328,7 +541,13 @@ class PasswordChangeTestCase(UsersBaseTestCase): ) self.assertContains(resp, 'Введённый пароль слишком короткий', count=1, status_code=200) - def test_invalid_new_password2(self): + def test_invalid_new_password2(self) -> None: + """ + Функция тестирования попытки смены пароля, когда новый пароль не соответствует требованиям: состоит + только из цифр. + + Проверяет текст уведомления пользователя 'Введённый пароль состоит только из цифр'. + """ with translation.override('ru'): resp = self.agent_client.post( reverse_lazy('password_change'), @@ -341,6 +560,12 @@ class PasswordChangeTestCase(UsersBaseTestCase): self.assertContains(resp, 'Введённый пароль состоит только из цифр', count=1, status_code=200) def test_invalid_new_password3(self): + """ + Функция тестирования попытки смены пароля, когда новый пароль не соответствует требованиям: аналогчен имени + пользователя. + + Проверяет текст уведомления пользователя 'Введённый пароль слишком похож на имя пользователя'. + """ with translation.override('ru'): resp = self.agent_client.post( reverse_lazy('password_change'), @@ -356,6 +581,8 @@ class PasswordChangeTestCase(UsersBaseTestCase): class GetTicketsTestCase(UsersBaseTestCase): """ Класс тестов для проверки функции получения тикетов. + + """ @patch('main.views.zenpy.get_user') @@ -365,7 +592,7 @@ class GetTicketsTestCase(UsersBaseTestCase): Функция проверки переадресации пользователя на рабочую страницу. """ get_user_mock.return_value = Mock() - user = User.objects.get(email=self.engineer) + user = get_user_model().objects.get(email=self.engineer) resp = self.engineer_client.post(reverse('work_get_tickets')) self.assertRedirects(resp, reverse('work', args=[user.id])) self.assertEqual(resp.status_code, 302) @@ -434,6 +661,8 @@ class GetTicketsTestCase(UsersBaseTestCase): class ProfileTestCase(TestCase): """ Класс тестов для проверки синхронизации профиля пользователя. + + """ fixtures = ['fixtures/profile.json'] @@ -444,9 +673,9 @@ class ProfileTestCase(TestCase): 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.client.force_login(get_user_model().objects.get(email=self.zendesk_agent_email)) self.admin_client = Client() - self.admin_client.force_login(User.objects.get(email=self.zendesk_admin_email)) + self.admin_client.force_login(get_user_model().objects.get(email=self.zendesk_admin_email)) def test_correct_username(self): """ @@ -495,9 +724,9 @@ class LoggingTestCase(UsersBaseTestCase): def setUp(self): super().setUp() - self.admin_profile = User.objects.get(email=self.admin).userprofile - self.agent_profile = User.objects.get(email=self.light_agent).userprofile - self.engineer_profile = User.objects.get(email=self.engineer).userprofile + self.admin_profile = get_user_model().objects.get(email=self.admin).userprofile + self.agent_profile = get_user_model().objects.get(email=self.light_agent).userprofile + self.engineer_profile = get_user_model().objects.get(email=self.engineer).userprofile @staticmethod def get_file_output(): From dc47c12efc2c793fc24b73f81a589c251c48a089 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D1=82=D0=B5=D0=BF=D0=B0=D0=BD=D0=B5=D0=BD=D0=BA?= =?UTF-8?q?=D0=BE=20=D0=9E=D0=BB=D1=8C=D0=B3=D0=B0?= Date: Sun, 23 May 2021 21:05:06 +0300 Subject: [PATCH 08/18] Fix problem in test for pylint --- README.rst | 2 +- access_controller/settings.py | 2 -- main/tests.py | 23 +++++++++++++++-------- main/views.py | 5 ++++- 4 files changed, 20 insertions(+), 12 deletions(-) diff --git a/README.rst b/README.rst index 8c9907d..721f60a 100644 --- a/README.rst +++ b/README.rst @@ -170,7 +170,7 @@ Quickstart Для проверки pylint используем: ------------------------------- -pylint --django-settings-module=access_controller.access_controller.settings ../access_controller (каталог, где лежит проект) +pylint ../access_controller_new (каталог, где лежит проект) Для приведения файлов к стандарту PEP8 используем: -------------------------------------------------- diff --git a/access_controller/settings.py b/access_controller/settings.py index 30875f6..3f15f93 100644 --- a/access_controller/settings.py +++ b/access_controller/settings.py @@ -12,8 +12,6 @@ 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'. diff --git a/main/tests.py b/main/tests.py index 4353ddf..6ebf157 100644 --- a/main/tests.py +++ b/main/tests.py @@ -1,3 +1,8 @@ +""" +Тестирование работы программы. +""" + + import random from unittest.mock import patch, Mock @@ -218,7 +223,8 @@ class MakeEngineerTestCase(UsersBaseTestCase): """ self.admin_client.post( reverse_lazy('control'), - data={'users': [get_user_model().objects.get(email=self.light_agent).userprofile.id], 'engineer': 'engineer'} + data={'users': [get_user_model().objects.get(email=self.light_agent).userprofile.id], + 'engineer': 'engineer'} ) call_list = zenpy_mock.update_user.call_args_list mock_object = call_list[0][0][0] @@ -356,7 +362,8 @@ class MakeLightAgentTestCase(UsersBaseTestCase): """ self.admin_client.post( reverse_lazy('control'), - data={'users': [get_user_model().objects.get(email=self.engineer).userprofile.id], 'light_agent': 'light_agent'} + data={'users': [get_user_model().objects.get(email=self.engineer).userprofile.id], + 'light_agent': 'light_agent'} ) call_list = zenpy_mock.update_user.call_args_list mock_object = call_list[0][0][0] @@ -447,7 +454,8 @@ class PasswordResetTestCase(UsersBaseTestCase): Проверяет отсутствие отправки письма о смене пароля. """ with self.settings(EMAIL_BACKEND=self.email_backend): - resp = self.agent_client.post(reverse_lazy('password_reset'), data={'email': self.light_agent + str(random.random())}) + resp = self.agent_client.post(reverse_lazy('password_reset'), + data={'email': self.light_agent + str(random.random())}) self.assertRedirects(resp, reverse('password_reset_done')) self.assertEqual(resp.status_code, 302) self.assertEqual(len(mail.outbox), 0) @@ -635,11 +643,11 @@ class GetTicketsTestCase(UsersBaseTestCase): @patch('main.views.zenpy') @patch('main.views.get_tickets_list_for_group') - def test_take_zero_tickets(self, TicketsMock, zenpy_mock): + def test_take_zero_tickets(self, tickets_mock, zenpy_mock): """ Функция проверки попытки назначения нуля тикета на engineer. """ - TicketsMock.return_value = [Mock()] * 3 + tickets_mock.return_value = [Mock()] * 3 zenpy_mock.get_user.return_value = Mock(role='agent', custom_role_id=sets.ZENDESK_ROLES['engineer']) self.engineer_client.post(reverse('work_get_tickets'), data={'count_tickets': 0}) tickets = zenpy_mock.update_tickets.call_args[0][0] @@ -730,9 +738,8 @@ class LoggingTestCase(UsersBaseTestCase): @staticmethod def get_file_output(): - file = open('logs/logs.csv', 'r') - file_output = file.readlines()[-1] - file.close() + with open('logs/logs.csv', 'r') as file: + file_output = file.readlines()[-1] return file_output def test_engineer_with_admin(self): diff --git a/main/views.py b/main/views.py index 44ce560..07f4cc5 100644 --- a/main/views.py +++ b/main/views.py @@ -453,5 +453,8 @@ def statistic_page(request: WSGIRequest) -> HttpResponse: context['form'] = form return render(request, 'pages/statistic.html', context) -def registration_failed(request): +def registration_failed(request: WSGIRequest) -> HttpResponse: + """ + Функция отображения страницы "Регистрация закрыта". + """ return render(request, 'pages/registration_failed.html') From fdc1f3b448053d5f337123f178a1d933a448585f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D1=82=D0=B5=D0=BF=D0=B0=D0=BD=D0=B5=D0=BD=D0=BA?= =?UTF-8?q?=D0=BE=20=D0=9E=D0=BB=D1=8C=D0=B3=D0=B0?= Date: Mon, 24 May 2021 20:22:53 +0300 Subject: [PATCH 09/18] Add tests docs, draft 2 --- main/tests.py | 123 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 100 insertions(+), 23 deletions(-) diff --git a/main/tests.py b/main/tests.py index 6ebf157..8a6ad6c 100644 --- a/main/tests.py +++ b/main/tests.py @@ -590,14 +590,17 @@ class GetTicketsTestCase(UsersBaseTestCase): """ Класс тестов для проверки функции получения тикетов. - + В тестах используются @patch, замещающие работу с API Zendesk. """ @patch('main.views.zenpy.get_user') @patch('main.extra_func.zenpy') - def test_redirect(self, _zenpy_mock, get_user_mock): + def test_redirect(self, _zenpy_mock: zenpy, get_user_mock: get_user_model()) -> None: """ Функция проверки переадресации пользователя на рабочую страницу. + + Проверяет редирект на рабочую страницу, в случае, когда пользователь с правами инженера заполняет форму + принятия тикетов в работу. """ get_user_mock.return_value = Mock() user = get_user_model().objects.get(email=self.engineer) @@ -607,9 +610,12 @@ class GetTicketsTestCase(UsersBaseTestCase): @patch('main.views.zenpy') @patch('main.views.get_tickets_list_for_group') - def test_take_one_ticket(self, group_tickets_mock, zenpy_mock): + def test_take_one_ticket(self, group_tickets_mock: list, zenpy_mock: zenpy) -> None: """ Функция проверки назначения одного тикета на engineer. + + Проверяет соответствие ответственного за тикет объекта tickets и тестового клиента правами инженера, + направившего запрос на назначение одного тикета. """ group_tickets_mock.return_value = [Mock()] zenpy_mock.get_user.return_value = Mock(role='agent', custom_role_id=sets.ZENDESK_ROLES['engineer']) @@ -619,9 +625,12 @@ class GetTicketsTestCase(UsersBaseTestCase): @patch('main.views.get_tickets_list_for_group') @patch('main.views.zenpy') - def test_take_many_tickets(self, zenpy_mock, group_tickets_mock): + def test_take_many_tickets(self, zenpy_mock: zenpy, group_tickets_mock: list) -> None: """ Функция проверки назначения нескольких тикетов на engineer. + + Проверяет соответствие ответственного за тикеты объекта tickets и тестового клиента правами инженера, + направившего запрос на назначение трех тикетов. """ group_tickets_mock.return_value = [Mock()] * 3 zenpy_mock.get_user.return_value = Mock(role='agent', custom_role_id=sets.ZENDESK_ROLES['engineer']) @@ -632,7 +641,7 @@ class GetTicketsTestCase(UsersBaseTestCase): @patch('main.views.zenpy.get_user') @patch('main.views.zenpy') - def test_light_agent_take_ticket(self, zenpy_mock, get_user_mock): + def test_light_agent_take_ticket(self, zenpy_mock: zenpy, get_user_mock: get_user_model()): """ Функция проверки попытки назначения тикета на light_agent. """ @@ -643,9 +652,11 @@ class GetTicketsTestCase(UsersBaseTestCase): @patch('main.views.zenpy') @patch('main.views.get_tickets_list_for_group') - def test_take_zero_tickets(self, tickets_mock, zenpy_mock): + def test_take_zero_tickets(self, tickets_mock: list, zenpy_mock: zenpy) -> None: """ - Функция проверки попытки назначения нуля тикета на engineer. + Функция проверки попытки назначения нулевого количества тикетов. + + Проверяет, что список тикетов остался пустым. """ tickets_mock.return_value = [Mock()] * 3 zenpy_mock.get_user.return_value = Mock(role='agent', custom_role_id=sets.ZENDESK_ROLES['engineer']) @@ -655,9 +666,12 @@ class GetTicketsTestCase(UsersBaseTestCase): @patch('main.views.get_tickets_list_for_group') @patch('main.views.zenpy') - def test_take_invalid_count_tickets(self, zenpy_mock, group_tickets_mock): + def test_take_invalid_count_tickets(self, zenpy_mock: zenpy, group_tickets_mock: list) -> None: """ - Функция проверки попытки назначения нуля тикетов на engineer. + Функция проверки попытки назначения некорректного количества тикетов (введении в форму назначения тикетов + не числового значения, а строки). + + Проверяет, отсутствие списка тикетов. """ group_tickets_mock.return_value = [Mock()] * 3 zenpy_mock.get_user.return_value = Mock(role='agent', custom_role_id=sets.ZENDESK_ROLES['engineer']) @@ -670,13 +684,24 @@ class ProfileTestCase(TestCase): """ Класс тестов для проверки синхронизации профиля пользователя. - + Для тестов используются фикстуры тестовых пользователей (profile.json). """ fixtures = ['fixtures/profile.json'] - def setUp(self): + def setUp(self) -> None: """ - Предустановленные значения для проведения тестов. + Функция предустановки значений переменных. + + Добавляем email тестовых пользователей Zendesk и создаем клиентов для тестов. + + :param zendesk_agent_email: email тестового пользователя с правами light_agent + :type zendesk_agent_email: :class:`str` + :param zendesk_admin_email: email тестового пользователя с правами admin + :type zendesk_admin_email: :class:`str` + :param client: клиент, залогиненный как пользователь с email zendesk_agent_email + :type client: :class:`django.test.client.Client` + :param admin_client: клиент, залогиненный как пользователь с zendesk_admin_email + :type admin_client: :class:`django.test.client.Client` """ self.zendesk_agent_email = 'krav-88@mail.ru' self.zendesk_admin_email = 'idar.sokurov.05@mail.ru' @@ -685,32 +710,42 @@ class ProfileTestCase(TestCase): self.admin_client = Client() self.admin_client.force_login(get_user_model().objects.get(email=self.zendesk_admin_email)) - def test_correct_username(self): + def test_correct_username(self) -> None: """ Функция проверки синхронизации имени пользователя. + + Проверяет соответствие имени пользователя из контекста страницы профиля имени пользователя в Zendesk. """ 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): + def test_correct_email(self) -> None: """ Функция проверки синхронизации почты пользователя. + + Проверяет соответствие email пользователя из контекста страницы профиля email пользователя в Zendesk. """ 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): + def test_correct_role(self) -> None: """ Функция проверки синхронизации роли пользователя. + + Проверяет соответствие роли пользователя из контекста страницы профиля роли пользователя в Zendesk. Проверка + осуществляется на примере администратора и агента. """ 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): + def test_correct_custom_role_id(self) -> None: """ Функция проверки синхронизации рабочей роли пользователя. + + Проверяет соответствие id рабочей роли пользователя из контекста страницы профиля id + роли пользователя в Zendesk. Проверка осуществляется на примере администратора и агента. """ resp = self.client.get(reverse('profile')) user = zenpy.get_user(self.zendesk_agent_email) @@ -719,9 +754,11 @@ class ProfileTestCase(TestCase): 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): + def test_correct_image(self) -> None: """ Функция проверки синхронизации изображения пользователя. + + Проверяет соответствие аватарки пользователя из контекста страницы профиля аватарке пользователя в Zendesk. """ resp = self.client.get(reverse('profile')) user = zenpy.get_user(self.zendesk_agent_email) @@ -729,38 +766,78 @@ class ProfileTestCase(TestCase): class LoggingTestCase(UsersBaseTestCase): + """ + Класс тестирования процесса логгирования. + """ - def setUp(self): + def setUp(self) -> None: + """ + Функция предустановки значений переменных. + + Определяем профили пользователей с разными ролями. + + :param admin_profile: профиль тестового пользователя с правами admin + :type admin_profile: :class:`Userprofile` + :param agent_profile: профиль тестового пользователя с правами light_agent + :type agent_profile: :class:`Userprofile` + :param engineer_profile: профиль тестового пользователя с правами engineer + :type engineer_profile: :class:`Userprofile` + """ super().setUp() self.admin_profile = get_user_model().objects.get(email=self.admin).userprofile self.agent_profile = get_user_model().objects.get(email=self.light_agent).userprofile self.engineer_profile = get_user_model().objects.get(email=self.engineer).userprofile @staticmethod - def get_file_output(): + def get_file_output() -> str: + """ + Получение данных из файла логов. + """ with open('logs/logs.csv', 'r') as file: file_output = file.readlines()[-1] return file_output - def test_engineer_with_admin(self): + def test_engineer_with_admin(self) -> None: + """ + Функция проверки корректной записи лога по смене роли инженера в файл. + + Сравнивает запись в файле и созданный лог с переданными значениями профилей инженера и администратора + для смены прав. + """ log(self.engineer_profile, self.admin_profile) file_output = self.get_file_output() self.assertEqual(file_output, f'UserForAccessTest,engineer,' f'{str(timezone.now().today())[:16]},ZendeskAdmin\n') - def test_engineer_without_admin(self): + def test_engineer_without_admin(self) -> None: + """ + Функция проверки корректной записи лога по смене роли инженера в файл без указания администратора. + + Сравнивает запись в файле и созданный лог с переданным значением профиля инженера для смены прав. + """ log(self.engineer_profile) file_output = self.get_file_output() self.assertEqual(file_output, f'UserForAccessTest,engineer,' f'{str(timezone.now().today())[:16]},UserForAccessTest\n') - def test_light_agent_with_admin(self): + def test_light_agent_with_admin(self) -> None: + """ + Функция проверки корректной записи лога по смене роли агента в файл. + + Сравнивает запись в файле и созданный лог с переданными значениями профилей агента и администратора + для смены прав. + """ log(self.agent_profile, self.admin_profile) file_output = self.get_file_output() self.assertEqual(file_output, f'UserForAccessTest,light_agent,' f'{str(timezone.now().today())[:16]},ZendeskAdmin\n') - def test_light_agent_without_admin(self): + def test_light_agent_without_admin(self) -> None: + """ + Функция проверки корректной записи лога по смене роли агента в файл без указания администратора. + + Сравнивает запись в файле и созданный лог с переданным значением профиля агента для смены прав. + """ log(self.agent_profile) file_output = self.get_file_output() self.assertEqual(file_output, f'UserForAccessTest,light_agent,' From 33421ca11229c95a42ee86b7c15f516bf8d1aeaa Mon Sep 17 00:00:00 2001 From: Sokurov Idar Date: Tue, 25 May 2021 21:45:28 +0300 Subject: [PATCH 10/18] del inint.py --- __init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 __init__.py diff --git a/__init__.py b/__init__.py deleted file mode 100644 index e69de29..0000000 From 614b82c24d897e4ea25f5fa0f82f609ddf5e93a5 Mon Sep 17 00:00:00 2001 From: Iurii Tatishchev Date: Tue, 25 May 2021 12:22:43 -0700 Subject: [PATCH 11/18] Update README.md pylint section --- README.md | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index b3376cf..1438361 100644 --- a/README.md +++ b/README.md @@ -149,22 +149,25 @@ docker run -d -p 8000:8000 \ Пример полной конфигурации можно найти в [.env.example](.env.example). Почту и токен админа ZenDesk взять у руководителя (если вы не админ). ## Для проверки pylint используем: -pylint --django-settings-module=access_controller.access_controller.settings ../access_controller (каталог, где лежит проект) +```bash +pylint --django-settings-module=access_controller.settings main +``` ## Для приведения файлов к стандарту PEP8 используем: +```bash autopep8 --in-place filename +``` ##Для проверки орфографии: -cd docs +```bash +(cd docs && make spelling) +``` -(set -a && source ../.env && make spelling) ##Для обновления документации: +```bash m2r README.md - -cd docs - -(set -a && source ../.env && make html) - +(cd docs && make html) +``` ## Read more - Zenpy: [http://docs.facetoe.com.au](http://docs.facetoe.com.au) From bdd5a68b268328ea013a77225f733eb85990ed4b Mon Sep 17 00:00:00 2001 From: Dmitriy Andreev Date: Wed, 26 May 2021 15:34:28 +0300 Subject: [PATCH 12/18] profile look fix --- main/templates/pages/profile.html | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/main/templates/pages/profile.html b/main/templates/pages/profile.html index 4b7016a..2f628f8 100644 --- a/main/templates/pages/profile.html +++ b/main/templates/pages/profile.html @@ -23,7 +23,7 @@ {% block content %}
-
+

Имя пользователя

{{ profile.name }}
@@ -44,7 +46,7 @@ {% elif profile.custom_role_id == ZENDESK_ROLES.light_agent %}
light_agent
{% else %} -
None
+
Без роли
{% endif %}
@@ -52,7 +54,7 @@
{% endblock %} From 72030040b842b47f77ef13329038f0cafb1b4f75 Mon Sep 17 00:00:00 2001 From: Dmitriy Andreev Date: Wed, 26 May 2021 17:05:48 +0300 Subject: [PATCH 13/18] Fix looking of profile, work and control pages --- main/templates/pages/adm_ruleset.html | 12 +-- main/templates/pages/profile.html | 4 +- main/templates/pages/work.html | 123 ++++++++++++++------------ static/main/js/control.js | 4 +- 4 files changed, 74 insertions(+), 69 deletions(-) diff --git a/main/templates/pages/adm_ruleset.html b/main/templates/pages/adm_ruleset.html index cbbfc1b..39caf41 100644 --- a/main/templates/pages/adm_ruleset.html +++ b/main/templates/pages/adm_ruleset.html @@ -39,10 +39,10 @@
-
Список сотрудников
+

Список сотрудников

{% block table %} - +
+ From 8b3f8b8b8748504006f38b1abc3f7619ee33d8e1 Mon Sep 17 00:00:00 2001 From: Dmitriy Andreev Date: Wed, 26 May 2021 17:47:32 +0300 Subject: [PATCH 14/18] Page design became better for user. Changes in profile, work, control and statistic pages. --- main/templates/base/base.html | 2 ++ main/templates/pages/statistic.html | 26 +++++++++++++------------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/main/templates/base/base.html b/main/templates/base/base.html index 166195d..e82af52 100644 --- a/main/templates/base/base.html +++ b/main/templates/base/base.html @@ -19,6 +19,8 @@ user-select: none; } + + @media (min-width: 768px) { .bd-placeholder-img-lg { font-size: 3.5rem; diff --git a/main/templates/pages/statistic.html b/main/templates/pages/statistic.html index b467250..82b714a 100644 --- a/main/templates/pages/statistic.html +++ b/main/templates/pages/statistic.html @@ -7,21 +7,21 @@ {% block heading %} Страницы просмотра статистики{% endblock %} {% block content%} -
+
{% csrf_token %}
-
- {{ form.email.label }} +
+

{{ form.email.label }}

{{ form.email }}
-
- {{ form.interval.label }} +
+

{{ form.interval.label }}

{% for radio in form.interval%} @@ -33,8 +33,8 @@
-
- {{ form.display_format.label }} +
+

{{ form.display_format.label }}

{% for radio in form.display_format%} @@ -46,8 +46,8 @@
-
- {{ form.range_start.label}} +
+

{{ form.range_start.label}}

@@ -56,8 +56,8 @@
-
- {{ form.range_end.label}} +
+

{{ form.range_end.label}}

@@ -65,9 +65,9 @@
-
+
- +
From e595156f56753ee0e3f3c09d85ba1f59d431b240 Mon Sep 17 00:00:00 2001 From: Iurii Tatishchev Date: Wed, 26 May 2021 09:10:26 -0700 Subject: [PATCH 15/18] Change custom role ids to BigInt for postgres compatibility. --- access_controller/settings.py | 4 +++- main/models.py | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/access_controller/settings.py b/access_controller/settings.py index 55af7a5..056de17 100644 --- a/access_controller/settings.py +++ b/access_controller/settings.py @@ -147,7 +147,6 @@ LOGIN_REDIRECT_URL = '/' LOGOUT_REDIRECT_URL = '/' - # Название_приложения.Название_файла.Название_класса_обработчика AUTHENTICATION_BACKENDS = [ 'access_controller.auth.EmailAuthBackend', @@ -185,3 +184,6 @@ ACTRL_ZENDESK_SUBDOMAIN = os.getenv('ACTRL_ZENDESK_SUBDOMAIN') or os.getenv('ZD_ ACTRL_API_EMAIL = os.getenv('ACTRL_API_EMAIL') or os.getenv('ACCESS_CONTROLLER_API_EMAIL') ACTRL_API_TOKEN = os.getenv('ACTRL_API_TOKEN') or os.getenv('ACCESS_CONTROLLER_API_TOKEN') ACTRL_API_PASSWORD = os.getenv('ACTRL_API_PASSWORD') or os.getenv('ACCESS_CONTROLLER_API_PASSWORD') + + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' diff --git a/main/models.py b/main/models.py index c934ab1..828fce2 100644 --- a/main/models.py +++ b/main/models.py @@ -24,7 +24,7 @@ class UserProfile(models.Model): user = models.OneToOneField(to=get_user_model(), on_delete=models.CASCADE, help_text='Пользователь') role = models.CharField(default='None', max_length=100, help_text='Глобальное имя роли пользователя') - custom_role_id = models.IntegerField(default=0, help_text='Код роли пользователя') + custom_role_id = models.BigIntegerField(default=0, help_text='Код роли пользователя') image = models.URLField(null=True, blank=True, help_text='Аватарка') name = models.CharField(default='None', max_length=100, help_text='Имя пользователя на нашем сайте') @@ -75,8 +75,8 @@ class RoleChangeLogs(models.Model): """ user = models.ForeignKey(to=get_user_model(), on_delete=models.CASCADE, help_text='Пользователь, которому присвоили другую роль') - old_role = models.IntegerField(default=0, help_text='Старая роль') - new_role = models.IntegerField(default=0, help_text='Присвоенная роль') + old_role = models.BigIntegerField(default=0, help_text='Старая роль') + new_role = models.BigIntegerField(default=0, help_text='Присвоенная роль') change_time = models.DateTimeField(default=timezone.now, help_text='Дата и время изменения роли') changed_by = models.ForeignKey(to=get_user_model(), on_delete=models.CASCADE, related_name='changed_by', help_text='Кем была изменена роль') From 22154ca7fc3580e57a2787f02aa31f992a512d70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D1=82=D0=B5=D0=BF=D0=B0=D0=BD=D0=B5=D0=BD=D0=BA?= =?UTF-8?q?=D0=BE=20=D0=9E=D0=BB=D1=8C=D0=B3=D0=B0?= Date: Wed, 26 May 2021 21:13:35 +0300 Subject: [PATCH 16/18] Add tests docs: params for Mock --- main/tests.py | 104 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 79 insertions(+), 25 deletions(-) diff --git a/main/tests.py b/main/tests.py index 8a6ad6c..79afcfc 100644 --- a/main/tests.py +++ b/main/tests.py @@ -7,7 +7,6 @@ import random from unittest.mock import patch, Mock from django.contrib.auth import get_user_model -# from django.contrib.auth.models import User from django.core import mail from django.http import HttpResponseRedirect from django.template.loader import render_to_string @@ -171,13 +170,15 @@ class MakeEngineerTestCase(UsersBaseTestCase): """ Класс тестирования присвоения пользователю роли engineer. - В тестах используется @patch('main.extra_func.zenpy') замещающий API Zendesk. + В тестах используется @patch('main.extra_func.zenpy') Mock для работы с API Zendesk. """ @patch('main.extra_func.zenpy') - def test_become_engineer_redirect(self, _zenpy_mock: zenpy) -> None: + def test_become_engineer_redirect(self, _zenpy_mock: Mock) -> None: """ Функция тестирования редиректа на рабочую страницу тестового пользователя при назначении его инженером. + + :param _zenpy_mock: Mock объекта zenpy для функций, работающих с API Zendesk. """ user = get_user_model().objects.get(email=self.light_agent) resp = self.agent_client.post(reverse_lazy('work_become_engineer')) @@ -185,41 +186,49 @@ class MakeEngineerTestCase(UsersBaseTestCase): self.assertEqual(resp.status_code, 302) @patch('main.extra_func.zenpy') - def test_light_agent_make_engineer(self, zenpy_mock: zenpy) -> None: + def test_light_agent_make_engineer(self, zenpy_mock: Mock) -> None: """ Функция тестирования назначения легкого агента на роль инженера. Проверяет установку роли "engineer" в Zendesk. + + :param zenpy_mock: Mock объекта zenpy для функций, работающих с API Zendesk. """ self.agent_client.post(reverse_lazy('work_become_engineer')) self.assertEqual(zenpy_mock.update_user.call_args[0][0].custom_role_id, sets.ZENDESK_ROLES['engineer']) @patch('main.extra_func.zenpy') - def test_admin_make_engineer(self, zenpy_mock: zenpy) -> None: + def test_admin_make_engineer(self, zenpy_mock: Mock) -> None: """ Функция тестирования назначения администратора на роль инженера. Проверяет установку роли "engineer" в Zendesk. + + :param zenpy_mock: Mock объекта zenpy для функций, работающих с API Zendesk. """ self.admin_client.post(reverse_lazy('work_become_engineer')) self.assertEqual(zenpy_mock.update_user.call_args[0][0].custom_role_id, sets.ZENDESK_ROLES['engineer']) @patch('main.extra_func.zenpy') - def test_engineer_make_engineer(self, zenpy_mock: zenpy) -> None: + def test_engineer_make_engineer(self, zenpy_mock: Mock) -> None: """ Функция тестирования назначения инженера на роль инженера. Проверяет установку роли "engineer" в Zendesk. + + :param zenpy_mock: Mock объекта zenpy для функций, работающих с API Zendesk. """ self.engineer_client.post(reverse_lazy('work_become_engineer')) self.assertEqual(zenpy_mock.update_user.call_args[0][0].custom_role_id, sets.ZENDESK_ROLES['engineer']) @patch('main.extra_func.zenpy') - def test_control_page_make_engineer_one(self, zenpy_mock: zenpy) -> None: + def test_control_page_make_engineer_one(self, zenpy_mock: Mock) -> None: """ Функция тестирования назначения администратором одного инженера на странице "Управление". Проверяет обновление администратором роли пользователя с light_agent на engineer. + + :param zenpy_mock: Mock объекта zenpy для функций, работающих с API Zendesk. """ self.admin_client.post( reverse_lazy('control'), @@ -232,11 +241,13 @@ class MakeEngineerTestCase(UsersBaseTestCase): self.assertEqual(mock_object.custom_role_id, sets.ZENDESK_ROLES['engineer']) @patch('main.extra_func.zenpy') - def test_control_page_make_engineer_many(self, zenpy_mock: zenpy) -> None: + def test_control_page_make_engineer_many(self, zenpy_mock: Mock) -> None: """ Функция тестирования назначения администратором нескольких инженеров на странице "Управление". Проверяет обновление администратором ролей двух пользователей с light_agent на engineer. + + :param zenpy_mock: Mock объекта zenpy для функций, работающих с API Zendesk. """ self.admin_client.post( reverse_lazy('control'), @@ -259,16 +270,19 @@ class MakeLightAgentTestCase(UsersBaseTestCase): """ Класс тестирования присвоения пользователю роли light_agent. - В тестах используется @patch('main.extra_func.zenpy') замещающий API Zendesk, а также - @patch('main.requester.TicketListRequester.get_tickets_list_for_user', side_effect=[[]]), предоставляющий список - тикетов. + В тестах используется @patch('main.extra_func.zenpy') Mock для работы API Zendesk, а также + @patch('main.requester.TicketListRequester.get_tickets_list_for_user', side_effect=[[]]), предоставляющий пустой + список в качестве списка тикетов пользователя. """ @patch('main.requester.TicketListRequester.get_tickets_list_for_user', side_effect=[[]]) @patch('main.extra_func.zenpy') - def test_hand_over_redirect(self, _zenpy_mock: zenpy, _user_tickets_mock: list) -> None: + def test_hand_over_redirect(self, _zenpy_mock: Mock, _user_tickets_Mock: Mock) -> None: """ Функция тестирования переадресации инженера на рабочую страницу, после сдачи прав. + + :param _zenpy_mock: Mock объекта zenpy для функций, работающих с API Zendesk. + :param _user_tickets_Mock: Mock, заменяющий список тикетов пользователя на пустой список. """ user = get_user_model().objects.get(email=self.engineer) resp = self.engineer_client.post(reverse_lazy('work_hand_over')) @@ -277,11 +291,14 @@ class MakeLightAgentTestCase(UsersBaseTestCase): @patch('main.requester.TicketListRequester.get_tickets_list_for_user', side_effect=[[]]) @patch('main.extra_func.zenpy') - def test_engineer_make_light_agent_no_tickets(self, zenpy_mock: zenpy, _user_tickets_mock: list) -> None: + def test_engineer_make_light_agent_no_tickets(self, zenpy_mock: Mock, _user_tickets_mock: Mock) -> None: """ Функция тестирования назначения инженера легким агентом, в случае, когда у него в работе нет тикетов. Проверяет назначение роли light_agent в Zendesk. + + :param zenpy_mock: Mock объекта zenpy для функций, работающих с API Zendesk. + :param _user_tickets_mock: Mock, заменяющий список тикетов пользователя на пустой список. """ self.engineer_client.post(reverse_lazy('work_hand_over')) self.assertEqual(zenpy_mock.update_user.call_args[0][0].custom_role_id, sets.ZENDESK_ROLES['light_agent']) @@ -290,7 +307,7 @@ class MakeLightAgentTestCase(UsersBaseTestCase): [Mock(id=1, status='solved'), Mock(id=2, status='open'), Mock(id=3, status='open')] ]) @patch('main.extra_func.zenpy') - def test_engineer_make_light_agent_with_tickets(self, zenpy_mock: zenpy, _user_tickets_mock: list): + def test_engineer_make_light_agent_with_tickets(self, zenpy_mock: Mock, _user_tickets_mock: Mock) -> None: """ Функция тестирования назначения инженера легким агентом, в случае, когда у него в работе есть тикеты. @@ -298,6 +315,9 @@ class MakeLightAgentTestCase(UsersBaseTestCase): два в состоянии: открыт. Проверяет распределение тикетов (поместить в решенные или назначить нового ответственного), а также назначение роли light_agent в Zendesk. + + :param zenpy_mock: Mock объекта zenpy для функций, работающих с API Zendesk. + :param _user_tickets_mock: Mock, заменяющий список тикетов пользователя на пустой список. """ zenpy_mock.solved_tickets_user_id = Mock() self.engineer_client.post(reverse_lazy('work_hand_over')) @@ -310,11 +330,14 @@ class MakeLightAgentTestCase(UsersBaseTestCase): @patch('main.requester.TicketListRequester.get_tickets_list_for_user', side_effect=[[]]) @patch('main.extra_func.zenpy') - def test_admin_make_light_agent_no_tickets(self, zenpy_mock: zenpy, _user_tickets_mock: list) -> None: + def test_admin_make_light_agent_no_tickets(self, zenpy_mock: Mock, _user_tickets_mock: Mock) -> None: """ Функция тестирования назначения администратора на роль легкого агента. Проверяет назначение роли light_agent в Zendesk. + + :param zenpy_mock: Mock объекта zenpy для функций, работающих с API Zendesk. + :param _user_tickets_mock: Mock, заменяющий список тикетов пользователя на пустой список. """ self.admin_client.post(reverse_lazy('work_hand_over')) self.assertEqual(zenpy_mock.update_user.call_args[0][0].custom_role_id, sets.ZENDESK_ROLES['light_agent']) @@ -331,6 +354,9 @@ class MakeLightAgentTestCase(UsersBaseTestCase): два в состоянии: открыт. Проверяет распределение тикетов (поместить в решенные или назначить нового ответственного), а также назначение роли light_agent в Zendesk. + + :param zenpy_mock: Mock объекта zenpy для функций, работающих с API Zendesk. + :param _user_tickets_mock: Mock, заменяющий список тикетов пользователя на пустой список. """ zenpy_mock.solved_tickets_user_id = Mock() self.admin_client.post(reverse_lazy('work_hand_over')) @@ -343,22 +369,28 @@ class MakeLightAgentTestCase(UsersBaseTestCase): @patch('main.requester.TicketListRequester.get_tickets_list_for_user', side_effect=[[]]) @patch('main.extra_func.zenpy') - def test_light_agent_make_light_agent(self, zenpy_mock: zenpy, _user_tickets_mock: list): + def test_light_agent_make_light_agent(self, zenpy_mock: Mock, _user_tickets_mock: Mock) -> None: """ Функция тестирования назначения легкого агента на роль легкого агента. Проверяет назначение роли light_agent в Zendesk. + + :param zenpy_mock: Mock объекта zenpy для функций, работающих с API Zendesk. + :param _user_tickets_mock: Mock, заменяющий список тикетов пользователя на пустой список. """ self.agent_client.post(reverse_lazy('work_hand_over')) self.assertEqual(zenpy_mock.update_user.call_args[0][0].custom_role_id, sets.ZENDESK_ROLES['light_agent']) @patch('main.requester.TicketListRequester.get_tickets_list_for_user', side_effect=[[]]) @patch('main.extra_func.zenpy') - def test_control_page_make_light_agent_one(self, zenpy_mock: zenpy, _user_tickets_mock: list) -> None: + def test_control_page_make_light_agent_one(self, zenpy_mock: Mock, _user_tickets_mock: Mock) -> None: """ Функция тестирования назначения администратором одного легкого агента на странице "Управление". Проверяет обновление администратором роли пользователя с engineer на light_agent. + + :param zenpy_mock: Mock объекта zenpy для функций, работающих с API Zendesk. + :param _user_tickets_mock: Mock, заменяющий список тикетов пользователя на пустой список. """ self.admin_client.post( reverse_lazy('control'), @@ -372,11 +404,14 @@ class MakeLightAgentTestCase(UsersBaseTestCase): @patch('main.requester.TicketListRequester.get_tickets_list_for_user', side_effect=[[], []]) @patch('main.extra_func.zenpy') - def test_control_page_make_light_agent_many(self, zenpy_mock: zenpy, _user_tickets_mock: list) -> None: + def test_control_page_make_light_agent_many(self, zenpy_mock: Mock, _user_tickets_mock: Mock) -> None: """ Функция тестирования назначения администратором нескольких легких агентов на странице "Управление". Проверяет обновление администратором ролей двух пользователей с engineer на light_agent. + + :param zenpy_mock: Mock объекта zenpy для функций, работающих с API Zendesk. + :param _user_tickets_mock: Mock, заменяющий список тикетов пользователя на пустой список. """ self.admin_client.post( @@ -590,17 +625,21 @@ class GetTicketsTestCase(UsersBaseTestCase): """ Класс тестов для проверки функции получения тикетов. - В тестах используются @patch, замещающие работу с API Zendesk. + В тестах используются @patch('main.views.zenpy.get_user') и @patch('main.views.zenpy.get_user') + для работы с API Zendesk. """ @patch('main.views.zenpy.get_user') @patch('main.extra_func.zenpy') - def test_redirect(self, _zenpy_mock: zenpy, get_user_mock: get_user_model()) -> None: + def test_redirect(self, _zenpy_mock: Mock, get_user_mock: Mock) -> None: """ Функция проверки переадресации пользователя на рабочую страницу. Проверяет редирект на рабочую страницу, в случае, когда пользователь с правами инженера заполняет форму принятия тикетов в работу. + + :param _zenpy_mock: Mock объекта zenpy для функций, работающих с API Zendesk. + :param get_user_mock: Mock объекта zenpy_user. """ get_user_mock.return_value = Mock() user = get_user_model().objects.get(email=self.engineer) @@ -610,12 +649,15 @@ class GetTicketsTestCase(UsersBaseTestCase): @patch('main.views.zenpy') @patch('main.views.get_tickets_list_for_group') - def test_take_one_ticket(self, group_tickets_mock: list, zenpy_mock: zenpy) -> None: + def test_take_one_ticket(self, group_tickets_mock: Mock, zenpy_mock: Mock) -> None: """ Функция проверки назначения одного тикета на engineer. Проверяет соответствие ответственного за тикет объекта tickets и тестового клиента правами инженера, направившего запрос на назначение одного тикета. + + :param zenpy_mock: Mock объекта zenpy для функций, работающих с API Zendesk. + :param group_tickets_mock: Mock списка не назначенных и нерешенных тикетов группы. """ group_tickets_mock.return_value = [Mock()] zenpy_mock.get_user.return_value = Mock(role='agent', custom_role_id=sets.ZENDESK_ROLES['engineer']) @@ -625,12 +667,15 @@ class GetTicketsTestCase(UsersBaseTestCase): @patch('main.views.get_tickets_list_for_group') @patch('main.views.zenpy') - def test_take_many_tickets(self, zenpy_mock: zenpy, group_tickets_mock: list) -> None: + def test_take_many_tickets(self, zenpy_mock: Mock, group_tickets_mock: Mock) -> None: """ Функция проверки назначения нескольких тикетов на engineer. Проверяет соответствие ответственного за тикеты объекта tickets и тестового клиента правами инженера, направившего запрос на назначение трех тикетов. + + :param zenpy_mock: Mock объекта zenpy для функций, работающих с API Zendesk. + :param group_tickets_mock: Mock списка не назначенных и нерешенных тикетов группы. """ group_tickets_mock.return_value = [Mock()] * 3 zenpy_mock.get_user.return_value = Mock(role='agent', custom_role_id=sets.ZENDESK_ROLES['engineer']) @@ -641,9 +686,12 @@ class GetTicketsTestCase(UsersBaseTestCase): @patch('main.views.zenpy.get_user') @patch('main.views.zenpy') - def test_light_agent_take_ticket(self, zenpy_mock: zenpy, get_user_mock: get_user_model()): + def test_light_agent_take_ticket(self, zenpy_mock: Mock, get_user_mock: Mock) -> None: """ Функция проверки попытки назначения тикета на light_agent. + + :param zenpy_mock: Mock объекта zenpy для функций, работающих с API Zendesk. + :param get_user_mock: Mock объекта zenpy_user. """ get_user_mock.return_value = Mock(role='agent', custom_role_id=sets.ZENDESK_ROLES['light_agent']) self.agent_client.post(reverse('work_get_tickets'), data={'count_tickets': 3}) @@ -652,11 +700,14 @@ class GetTicketsTestCase(UsersBaseTestCase): @patch('main.views.zenpy') @patch('main.views.get_tickets_list_for_group') - def test_take_zero_tickets(self, tickets_mock: list, zenpy_mock: zenpy) -> None: + def test_take_zero_tickets(self, tickets_mock: Mock, zenpy_mock: Mock) -> None: """ Функция проверки попытки назначения нулевого количества тикетов. Проверяет, что список тикетов остался пустым. + + :param zenpy_mock: Mock объекта zenpy для функций, работающих с API Zendesk. + :param tickets_mock: Mock списка тикетов - возвращает пустой список. """ tickets_mock.return_value = [Mock()] * 3 zenpy_mock.get_user.return_value = Mock(role='agent', custom_role_id=sets.ZENDESK_ROLES['engineer']) @@ -666,12 +717,15 @@ class GetTicketsTestCase(UsersBaseTestCase): @patch('main.views.get_tickets_list_for_group') @patch('main.views.zenpy') - def test_take_invalid_count_tickets(self, zenpy_mock: zenpy, group_tickets_mock: list) -> None: + def test_take_invalid_count_tickets(self, zenpy_mock: Mock, group_tickets_mock: Mock) -> None: """ Функция проверки попытки назначения некорректного количества тикетов (введении в форму назначения тикетов не числового значения, а строки). Проверяет, отсутствие списка тикетов. + + :param zenpy_mock: Mock объекта zenpy для функций, работающих с API Zendesk. + :param group_tickets_mock: Mock списка не назначенных и нерешенных тикетов группы. """ group_tickets_mock.return_value = [Mock()] * 3 zenpy_mock.get_user.return_value = Mock(role='agent', custom_role_id=sets.ZENDESK_ROLES['engineer']) From a908fc4388fa3d872d930bdbcc64491bbbe22267 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D1=82=D0=B5=D0=BF=D0=B0=D0=BD=D0=B5=D0=BD=D0=BA?= =?UTF-8?q?=D0=BE=20=D0=9E=D0=BB=D1=8C=D0=B3=D0=B0?= Date: Thu, 27 May 2021 15:24:56 +0300 Subject: [PATCH 17/18] Add user docs --- README.rst | 2 +- docs/source/overview.rst | 17 +++++++++++++++- docs/source/spelling_wordlist.txt | 32 ++++++++++++++++++++++++++++--- main/models.py | 2 +- main/tests.py | 12 ++++++------ main/views.py | 2 +- 6 files changed, 54 insertions(+), 13 deletions(-) diff --git a/README.rst b/README.rst index 721f60a..219cdd7 100644 --- a/README.rst +++ b/README.rst @@ -170,7 +170,7 @@ Quickstart Для проверки pylint используем: ------------------------------- -pylint ../access_controller_new (каталог, где лежит проект) +pylint ../access_controller (каталог, где лежит проект) Для приведения файлов к стандарту PEP8 используем: -------------------------------------------------- diff --git a/docs/source/overview.rst b/docs/source/overview.rst index 0e3bebb..42ef622 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -81,6 +81,12 @@ .. image:: _static/role_change.png +Являясь инженером, Вы можете запросить в работу необходимое количество тикетов. + +.. image:: _static/take_tickets.png + +Назначенные тикеты будут доступны в Zendesk. + ****************************************** Управление правами доступа администратором ****************************************** @@ -97,4 +103,13 @@ .. image:: _static/admin_manage_done.png -.. |copy| unicode:: 0xA9 .. Школа программистов S101, группа 2. 2021гю +Вы можете смотреть статистику работы пользователя. +Для этого на странице статистика необходимо указать: + +* email пользователя +* период, за который необходима статистика +* формат отображения данных + +.. image:: _static/statistic.png + +.. |copy| unicode:: 0xA9 .. Школа программистов S101, группа 2. 2021г. diff --git a/docs/source/spelling_wordlist.txt b/docs/source/spelling_wordlist.txt index fb1f974..706d045 100644 --- a/docs/source/spelling_wordlist.txt +++ b/docs/source/spelling_wordlist.txt @@ -217,6 +217,32 @@ by subclasses so new - - - +тикеты +StatisticForm +patch +zenpy +Mock +редирект +редиректа +предустановки +TicketListRequester +get_tickets_list_for_user +side +effect +for +залогиненный +предустанавливает +переадресация +фикстуры +profile +json +аватарки +аватарке +locmem +бэкенд +has +control +disallowed +test +users +Contents diff --git a/main/models.py b/main/models.py index 790a322..2a664b0 100644 --- a/main/models.py +++ b/main/models.py @@ -79,7 +79,7 @@ class RoleChangeLogs(models.Model): class UnassignedTicketStatus(models.IntegerChoices): """ - Модель статусов нераспределенных тикетов. + Модель статусов не распределенных тикетов. :param UNASSIGNED: Снят с пользователя, перенесён в буферную группу :param RESTORED: Авторство восстановлено diff --git a/main/tests.py b/main/tests.py index 79afcfc..3f2721d 100644 --- a/main/tests.py +++ b/main/tests.py @@ -62,7 +62,7 @@ class RegistrationTestCase(TestCase): """ Класс тестирования регистрации. - Для тестов используются фикстуры с данными пользователей engeneer и light_agent (data.json). + Для тестов используются фикстуры с данными пользователей engineer и light_agent (data.json). """ fixtures = ['fixtures/data.json'] @@ -101,7 +101,7 @@ class RegistrationTestCase(TestCase): def test_registration_fail_redirect(self) -> None: """ - Функция тестирования неуспешной регистрации пользователя (введен email, не зарегистированный на Zendesk). + Функция тестирования не успешной регистрации пользователя (введен email, не зарегистрированный на Zendesk). Проверяет, что происходит редирект на страницу "registration disallowed" """ @@ -277,12 +277,12 @@ class MakeLightAgentTestCase(UsersBaseTestCase): @patch('main.requester.TicketListRequester.get_tickets_list_for_user', side_effect=[[]]) @patch('main.extra_func.zenpy') - def test_hand_over_redirect(self, _zenpy_mock: Mock, _user_tickets_Mock: Mock) -> None: + def test_hand_over_redirect(self, _zenpy_mock: Mock, _user_tickets_mock: Mock) -> None: """ Функция тестирования переадресации инженера на рабочую страницу, после сдачи прав. :param _zenpy_mock: Mock объекта zenpy для функций, работающих с API Zendesk. - :param _user_tickets_Mock: Mock, заменяющий список тикетов пользователя на пустой список. + :param _user_tickets_mock: Mock, заменяющий список тикетов пользователя на пустой список. """ user = get_user_model().objects.get(email=self.engineer) resp = self.engineer_client.post(reverse_lazy('work_hand_over')) @@ -552,7 +552,7 @@ class PasswordChangeTestCase(UsersBaseTestCase): def test_different_new_passwords(self) -> None: """ - Функция тестирования смены пароля, при вводе несовпадающих новых паролей. + Функция тестирования смены пароля, при вводе не совпадающих новых паролей. Проверяет текст уведомления пользователя 'Введенные пароли не совпадают'. """ @@ -604,7 +604,7 @@ class PasswordChangeTestCase(UsersBaseTestCase): def test_invalid_new_password3(self): """ - Функция тестирования попытки смены пароля, когда новый пароль не соответствует требованиям: аналогчен имени + Функция тестирования попытки смены пароля, когда новый пароль не соответствует требованиям: аналогичен имени пользователя. Проверяет текст уведомления пользователя 'Введённый пароль слишком похож на имя пользователя'. diff --git a/main/views.py b/main/views.py index 07f4cc5..d79eec5 100644 --- a/main/views.py +++ b/main/views.py @@ -420,7 +420,7 @@ def statistic_page(request: WSGIRequest) -> HttpResponse: """ Функция отображения страницы статистики (для "superuser"). - :param request: Данные о пользователе: email, время и интервал работы. Данные получаем через forms.StFatisticForm + :param request: Данные о пользователе: email, время и интервал работы. Данные получаем через forms.StatisticForm :return: Адресация на страницу статистики """ From bcae073fffb2fd7acce4a6d8cab7eff1bae64e28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D1=82=D0=B5=D0=BF=D0=B0=D0=BD=D0=B5=D0=BD=D0=BA?= =?UTF-8?q?=D0=BE=20=D0=9E=D0=BB=D1=8C=D0=B3=D0=B0?= Date: Thu, 27 May 2021 15:54:12 +0300 Subject: [PATCH 18/18] Merge with dev --- docs/source/_static/statistic.png | Bin 0 -> 93533 bytes docs/source/_static/take_tickets.png | Bin 0 -> 73713 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/source/_static/statistic.png create mode 100644 docs/source/_static/take_tickets.png diff --git a/docs/source/_static/statistic.png b/docs/source/_static/statistic.png new file mode 100644 index 0000000000000000000000000000000000000000..279df9bacedc65ea490b4f341009b0ac7e9fe6b6 GIT binary patch literal 93533 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�fjx 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 z zz~p*zeLw#39={DV!kg6@qhCoGS{8J2* zPj&15!t`+s3+eQ?J7bjevg%ZsVNV*Y4DAY{;?m!Vx~0=uab-SoNZPRDGBT`qiV>OP zfeDJg7OQ`+qN8V4QF1nX5&8M})PRYBX|^OHCYR+afJ=QT^eHckK3i6@`Z0NmuT65e zaWhEvLnRB1n&7i9?fI8KI!Y=T_Hal@a2@0FTPjj3%5}`;!c}A=i8j#5YW&$K#>g(x zXrZBH8CJH5dV~6o50cHj(+zrd-uo+HhSmMyw(SJjz|f+^R(Yn7fsDql$fLn6!UF-0 zsGC)K;N|X~(FewNici@s_k;iTM+1PZ~E3#t|xl49NJTN4Ev!^>@6&@;4L()vR+CPt3Sm= zvRww!j0)5p2=f`Ewig9}xwsTV62$?OM5243-hydHYMJhHKxm>kfU_tjPtx&r?rw5ofv=UZ z;jA(C!hBc0I&jWDsib?S@dTX>hYG^sSqEEdO1-|3k+GM-Y0f*N>eXk(wV2b6nsLsf%qgT38nAtzg(E_Z9~Hb!!%S~z z*A3H-DQNQx?_kn}683iEJuC0Yak7r9FBg0OJbk=SuHqQz|BTN1gjKe?8M@!pMI{RG#_T}|eaXux5ux@R?lGt~A) z63l&2>p5kSulKUI{MpcgGN_0pT;TcToYDP_3fLLezu)yW%!jOoVb{6jy)VVrUAiTV zT&l9b!;cTXLj|;{kt^IOWW}^7rqHLq2MEfyAfn986$}T|1Fx^^pAfEDiq>(c{bvar&2#>SNB?@V!>li%F9QP zn*B7*2z;aX<~I2&F=HJFI&a1cK0Y%dG+ZNm)ZnRB{|VzW(DVIAyP=Wj3lb)F!j9Sr zvQE`sb^1mJ?BkoRiHknjRiHP&!y#`c2${`5$f4FF`em+^paaDVbChgXwObBx3*IWiH;=ZxG12|sKn<+o@TTv z=JrV1_DqfTFudAxY|fo#F{wsY7Q*=_zg+e9F*)p0ztFg*C;%`u7F)8zWgf=ws(lP> z>~tLX1!QtFhX-^oXT_&i-d0wvpR!&{E;x!l3f6}P2TLfcRDmALyiXf(UFxcd0puu; z>zF_fqjSM4x;~G=Ya^`sMo_CRAn)xOeY!+50=cqvX~=L)u5+HrJ%c~$m8Re~A71={ z-gl(X+O?6w)3W+wbw3k9dV4@z7L0&ugajqz<(Of zBj#|R<@|^pTBMKn(pGpG!Sps=$(j!8t8ZI$Z2F-%9$)gJt=Kmr0s`$ZL~--X4wUDX zETr@e5A;|sBn@lK;+;AR2LbW^$W?1;Af?fMteTe(Kn4(s_lEa6em7Ul2@v}H>PLyE ztEj!BrpK>1ik0q5l=PNKP$r}YW&>8UZBlM&4lOgJWCPZ1hScqnK?XfT!gG|Xma?<< z3`i#$$HITk^M%FJ#sDQ=JFVp$e0sSvm;9#rc5ie9(JWdkvDHg&fk`h=>B*2L#gSuA zo%Lz)#7L?075nEM8F!VV&7ve zsY`>}3%0w}ZYhxzv5Z%PQl?4$g0hiI?xKX4VYOOO8PXnf;KKDkS;{~TJ-~$hcyDdV zUG=-W^;N@+W0b?fI6-x^gPQPcEQ{xF)o8-$^JS?a<@V=&BxuR9`V&pw1txEB{ z5-SF2j^%fG;^KBYl;NxbRuAHRo{6{|lNFeV^JS0M<~m4+s9B*fM-N4(O4hwmKw7Ri z?Q4B+^|$#(RY_eWQ#O$9`Fqhw)2RjCB^b&_SLH~$%*a2rVZbx~p{ICB6eIu55v|l- zz}-FvEh1xK0F9&XQ`Z)pWEjlA?5D_R8KrpTA zI{%2xvyZW({S3l-H_fniJBj5>x|iXGW5BPnRjw&)NdMNXA{`eyynrML6TA4z!Xg)? zOHa>)YdD=bU*sX-n9)7mTTu8)D>-oIdgtw`t6NjI@lZx2-&WLjvl=%voqBXpT~&$T zR@}8bKDo(KLG@l3LqTd}KWAt#zuX7)7T0=K_k=pLN|H#KCAvCTRjg!=-7Nty!mUQ$ zhK(Nj2($qqt|fctN94b>Nr+oki+R7l8g=%Tq1U%bwTrKpnmi^WHjqi%5LlXsmaO@W z6br|00b+o>@GW+M&<(-gb(TxTNeliZr+=KZd0f1n__N=PQc(YX9op)Mty{&ocNF&C zBI?1O8r3a`*nSXCX`{LqZR05ol!+FX3<5%a8$l=OOzhfM0BwxiC#F*-EBcciQdK+K z@Ro!uMA~nTUzTIFt5~p&K`ug&6`KGF_ zAS1w895c&muW0=LQvCkvzKT}`TQ*J8_BV?ivHu$AKx=ZiA99AF-kl$84<4ZPv{^g zG(NSc#uP0F&RPO*$0p`rh!vpY5+@`B9>J$27W>L7n0=#O@tD1XTA(EW@m#J&)}3N$ zaThqRV672cVhzJDfQ9TiasUnOOBLIlG^3gd%7l@eAA%PvR~TEK=h?USHf=p;Ro>q_ z-3Z>8aVI)uhw2C_)81aIOpDuEvo|a%aSRLi#lo3ENMX}~#;-~WGp-CgWwk%NIX?lP z<43w46&MMO^H3w=y^#Xnjk<)ndeZK!E?i|9pJx4t3Vj(<=keW!KD_Kz?{GE{g!}d& zU58~Y0dV&5H#9JHXwWi7YFQLWf8RSkki4iVSXrXu7+$N&Hf~l_%R(EF6;h8GLf`c` zas;NR_E6Da(i+Bii(^K{Xr>Vw{Wb==L6kTvIZmu2uNRh_|DsDYISF;KH7vrqagoz3 z$&NnS944fKkhrW<#RgsEaIc9^551zJ417mUJ^W?}^dD-R9S6So!NqP2MxKEdzoM_& zA0A3t(GUfcv`>o%!ny0~N_x$$k_+zXOjp79oEYCg(Gpr!Y|vVq=%w)n z^!j{RxQ(2aKEm8CooC*7FU=>H{=LbKS_Edc5&`QgdzMt*+%c zL_Lr${!YpV;$HcrSCQ(a!MzQTt6wC7Ms#7zP2OHcJ3lpQw&g2~JWyfl!=t9G6%AsW zVNS%OlLKY(Yn9`8iCGF{jlr65BZNmSe4{lXD<%il@^-(HPQ2y|@= zKoL~(DA(5Hpq|}~O(e@cJzfi+$khyV1l3je*V42j$}GSv3Fq$=u*tT7cM1SC^3!HK zNKkTFdsCzxu57#I7R_-{b;B`I=4NNcpc+-xL-6nBV)I~d)FG>`D$~8|>SeA{JxpQo zD3I2y>>J^nR%&9}bmuWCblK6VeM0#09vXoRw%slO7roU-6++}DAwhLtPi-Ebb7MMr zv5=!(;aM?$KpAxTSWsu6a-olVvDbbL!xhX%t~N)T!Xm@)Q~mB7PS`m_%T)Yy(&@S? z6F`zl+n5!@z2-HiUnz8c**DdObFB|Z!WYkaX(Zn6#((ricBU7AN8-#?3j9+YIA@-0 zqp?9YR=aQe#*+BO35GG2=<>=~$|LW`s>RLC%`?S=Inr!xW9Mv^K^0`Yw}AdTqV zR~iB>NGI$+_pZCQ8L{w}DC5MMV*i~GpE$J%k-g7)YYgoG+ioWpRN@)a3?TzKgms zdG9Q?)_pEXOMJxMvR^($|K^ThfoDyLGFd#0yK|R+Dd&>z&j4%w^4r8uD;ZMP0HiE2j}kaLrQhEx*Z1mhKvFf&WN5PBuL@ z&#Pt;6J?ZUG|9EEW1E<6leIRiDRVzTe%#;eG6K4r zwn{A`uv802%oOy^%tGbG36+!km<6A_%2psTXmi9*ID-)9?l=fbF9-olzwvn#QQI-F zxy5{A0@)S^)r*w@`f@Ad0+5ComXT-M);7HKWC1Nd%0A|+iOcxPo=$d^#zb9atV*rK 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 literal 0 HcmV?d00001

@@ -71,7 +71,7 @@
-
Инженеров:
+
Инженеров:
{{ engineers }} @@ -79,7 +79,7 @@
-
Легких агентов:
+
Легких агентов:
{{ light_agents }} @@ -93,11 +93,11 @@ {% block buttons %}
- -
diff --git a/main/templates/pages/profile.html b/main/templates/pages/profile.html index 2f628f8..d5f95b3 100644 --- a/main/templates/pages/profile.html +++ b/main/templates/pages/profile.html @@ -32,7 +32,7 @@ >
@@ -54,7 +54,7 @@
{% endblock %} diff --git a/main/templates/pages/work.html b/main/templates/pages/work.html index bd46341..8144b1b 100644 --- a/main/templates/pages/work.html +++ b/main/templates/pages/work.html @@ -16,66 +16,73 @@ {% endblock %} {% block content %} -
-
-

Свободных Мест: {{ licences_remaining }}

-
+

Свободных Мест: {{ licences_remaining }}

-
-
-
Список сотрудников с правами инженера
- - - - - - - {% for engineer in engineers %} - - - - - {% endfor %} - -
EmailName
{{ engineer.email }}{{ engineer.name }}
-
-
-
-
-
-
-
инженеров:
-
-
- {{ engineers|length }} -
-
-
-
легких агентов:
-
-
- {{ agents|length }} -
- -
-
-
- -
-
- {% csrf_token %} - {{ get_tickets_form.count_tickets }} - -
-
- {% for message in messages %} - - {% endfor %} -
+
+
+

Список сотрудников с правами инженера

+ + + + + + + {% for engineer in engineers %} + + + + + {% endfor %} + +
EmailName
{{ engineer.email }}{{ engineer.name }}
+
+
+
+
+
+
инженеров:
+
+
+ {{ engineers|length }} +
+
+
+
легких агентов:
+
+
+ {{ agents|length }} +
+ +
+
+
+
+ + + +
+
+ +
+
+ +
+
+
+
+ +{% for message in messages %} + +{% endfor %} {% endblock %} diff --git a/static/main/js/control.js b/static/main/js/control.js index 6dd9172..c7a9d06 100644 --- a/static/main/js/control.js +++ b/static/main/js/control.js @@ -21,9 +21,7 @@ class ModelUserTableRow extends React.Component { name="users" /> -
- {this.props.user.name} - {this.props.user.name} {this.props.user.user.email} {this.props.user.zendesk_role}