diff --git a/.env.example b/.env.example index f86db45..58e7b51 100644 --- a/.env.example +++ b/.env.example @@ -20,6 +20,6 @@ LICENSE_NO=3 SHIFTH=12 ACTRL_ZENDESK_SUBDOMAIN="ngenix1612197338" -ACTRL_API_EMAIL="email@example.com" -ACTRL_API_TOKEN="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" -ACTRL_API_PASSWORD="" +ACTRL_API_EMAIL="stepanenko_olga@mail.ru" +ACTRL_API_TOKEN="X1x4QeNa4xRdul2rTIKhac98AsXMwd5bOGAyZOtU" + diff --git a/docs/source/conf.py b/docs/source/conf.py index cefcc10..ea14247 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -139,7 +139,8 @@ extensions = { 'sphinx.ext.graphviz', 'sphinx.ext.inheritance_diagram', 'sphinx_autodoc_typehints', - 'sphinxcontrib.spelling' + 'sphinxcontrib.spelling', + } @@ -173,6 +174,7 @@ html_static_path = ['_static'] # -- Extension configuration ------------------------------------------------- + # -- Options for intersphinx extension --------------------------------------- # Example configuration for intersphinx: refer to the Python standard library. @@ -211,3 +213,4 @@ always_document_param_types = True typehints_document_rtype = True napoleon_attr_annotations = True + diff --git a/docs/source/readme.rst b/docs/source/readme.rst index 0cee3b2..25dfdf6 100644 --- a/docs/source/readme.rst +++ b/docs/source/readme.rst @@ -1,4 +1,4 @@ READ.me ================== -.. include:: ../../README.md +.. include:: ../../README.rst diff --git a/main/extra_func.py b/main/extra_func.py index 86b6739..acaabb5 100644 --- a/main/extra_func.py +++ b/main/extra_func.py @@ -1,9 +1,11 @@ import logging from datetime import timedelta, datetime, date -from typing import Optional +from typing import Optional, Union from django.contrib.auth.models import User from django.core.exceptions import ObjectDoesNotExist +from django.core.handlers.wsgi import WSGIRequest +from django.http import HttpResponseRedirect, HttpResponsePermanentRedirect from django.shortcuts import redirect from django.utils import timezone from zenpy import Zenpy @@ -41,7 +43,7 @@ def make_engineer(user_profile: UserProfile, who_changes: User) -> None: update_role(user_profile, ROLES['engineer']) -def make_light_agent(user_profile: UserProfile, who_changes: User) -> None: +def make_light_agent(user_profile: UserProfile) -> None: """ Функция устанавливает пользователю роль легкого агента. @@ -77,7 +79,7 @@ def make_light_agent(user_profile: UserProfile, who_changes: User) -> None: def get_users_list() -> list: """ - Функция **get_users_list** возвращает список пользователей Zendesk, относящихся к организации SYSTEM. + Функция **get_users_list** возвращает список пользователей Zendesk, относящихся к организации SYSTEM. """ zendesk = zenpy @@ -87,14 +89,14 @@ def get_users_list() -> list: return users -def get_tickets_list(email): +def get_tickets_list(email) -> list: """ Функция возвращает список тикетов пользователя Zendesk """ return zenpy.admin.search(assignee=email, type='ticket') -def update_profile(user_profile: UserProfile): +def update_profile(user_profile: UserProfile) -> None: """ Функция обновляет профиль пользователя в соответствии с текущим в Zendesk. @@ -148,7 +150,7 @@ def check_user_auth(email: str, password: str) -> bool: return True -def update_user_in_model(profile: UserProfile, zendesk_user: ZenpyUser): +def update_user_in_model(profile: UserProfile, zendesk_user: ZenpyUser) -> None: """ Функция обновляет профиль пользователя при изменении данных пользователя на Zendesk. @@ -164,7 +166,7 @@ def update_user_in_model(profile: UserProfile, zendesk_user: ZenpyUser): profile.save() -def count_users(users) -> tuple: +def count_users(users: list) -> tuple: """ Функция подсчета количества сотрудников с ролями engineer и light_agent """ @@ -177,7 +179,7 @@ def count_users(users) -> tuple: return engineers, light_agents -def update_users_in_model(): +def update_users_in_model() -> list: """ Обновляет пользователей в модели UserProfile по списку пользователей в организации """ @@ -191,7 +193,7 @@ def update_users_in_model(): return users -def daterange(start_date, end_date) -> list: +def daterange(start_date: timedelta, end_date: timedelta) -> list: """ Функция возвращает список дней с start_date по end_date, исключая правую границу. @@ -205,7 +207,7 @@ def daterange(start_date, end_date) -> list: return dates -def get_timedelta(log, time=None) -> timedelta: +def get_timedelta(log: RoleChangeLogs, time: timedelta=None) -> timedelta: """ Функция возвращает объект класса timedelta, который хранит промежуток времени от начала суток до момента, который находится в log (объект класса RoleChangeLogs) или в time(datetime.time), если введён. @@ -272,7 +274,7 @@ class StatisticData: else: self.statistic = stat - def get_statistic(self) -> dict: + def get_statistic(self) -> Optional[dict]: """ Функция возвращает статистику работы пользователя. @@ -388,7 +390,7 @@ class StatisticData: return False return True - def _init_data(self): + def _init_data(self) -> None: """ Функция возвращает логи в диапазоне дат start_date - end_date для пользователя с указанным email. @@ -405,7 +407,7 @@ class StatisticData: except User.DoesNotExist: self.errors += ['Пользователь не найден'] - def _init_statistic(self) -> dict: + def _init_statistic(self) -> None: """ Функция заполняет словарь, в котором ключ - дата, значение - кол-во проработанных в этот день секунд. @@ -427,9 +429,11 @@ 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: Индекс текущего лога """ current_log, next_log = self.data[log_index], self.data[log_index + 1] if current_log.change_time.date() != next_log.change_time.date(): @@ -441,9 +445,11 @@ 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: Последний лог """ 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(): @@ -456,15 +462,17 @@ 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: """ - Функция обрабатывает случай, когда нам изветсно что инженер начал работу до диапазона + Функция обрабатывает случай, когда нам извеcтно, что инженер начал работу до диапазона. + + :param first_log: Первый лог """ self.fill_daterange(max(User.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 дне). @@ -475,7 +483,7 @@ class StatisticData: for day in daterange(first, last): self.statistic[day] = val - def clear_statistic(self) -> dict: + def clear_statistic(self) -> None: """ Функция осуществляет обновление всех дней. """ @@ -487,7 +495,12 @@ class DatabaseHandler(logging.Handler): def __init__(self): logging.Handler.__init__(self) - def emit(self, record): + def emit(self, record: logging.LogRecord) -> None: + """ + Функция осуществляет запись об изменении роли пользователя. + + :param record: Запись в сущность main.rolchangelogs + """ database = RoleChangeLogs() users = record.msg if users[1]: @@ -511,7 +524,12 @@ class CsvFormatter(logging.Formatter): def __init__(self): logging.Formatter.__init__(self) - def format(self, record): + def format(self, record: logging.LogRecord) -> str: + """ + Функция форматирует запись смены роли пользователя в строку + :param record: Запись смены роли пользователя. + :return: Строка с записью смены пользователя. + """ users = record.msg if users[1]: user = users[0] @@ -532,12 +550,12 @@ class CsvFormatter(logging.Formatter): return msg -def log(user, admin=0): +def log(user: User, admin: int = 0) -> None: """ - Осуществляет запись логов в базу данных и csv файл - :param admin: - :param user: - :return: + Функция осуществляет запись логов в базу данных и csv файл. + + :param admin: Админ, который меняет роль + :param user: Пользователь, которому изменена роль """ users = [user, admin] logger = logging.getLogger('MY_LOGGER') @@ -552,10 +570,15 @@ def log(user, admin=0): logger.info(users) -def set_session_params_for_work_page(request, count=None, is_confirm=True): +def set_session_params_for_work_page(request: WSGIRequest, count: int = None, is_confirm: bool = True) -> \ + Union[HttpResponsePermanentRedirect, HttpResponseRedirect]: """ - Функция для страницы получения прав - Устанавливает данные сессии о успешности запроса и количестве назначенных тикетов + Функция для страницы получения прав, устанавливает данные сессии о успешности запроса и количестве назначенных тикетов. + + :param request: Получение данных с рабочей страницы пользователя + :param count: Количество запрошенных тикетов + :param is_confirm: Назначение тикетов + :return: Перезагрузка страницы "Управление правами" соответствующего пользователя """ request.session['is_confirm'] = is_confirm request.session['count_tickets'] = count diff --git a/main/serializers.py b/main/serializers.py index 8436b54..f5100b4 100644 --- a/main/serializers.py +++ b/main/serializers.py @@ -29,7 +29,7 @@ class ZendeskUserSerializer(serializers.Serializer): email = serializers.EmailField() @staticmethod - def get_zendesk_role(obj): + def get_zendesk_role(obj) -> str: if obj.custom_role_id == ZENDESK_ROLES['engineer']: return 'engineer' elif obj.custom_role_id == ZENDESK_ROLES['light_agent']: diff --git a/main/views.py b/main/views.py index d21a6e9..bc05036 100644 --- a/main/views.py +++ b/main/views.py @@ -67,7 +67,7 @@ class CustomRegistrationView(RegistrationView): } redirect_url = 'done' - def register(self, form: CustomRegistrationForm) -> User: + def register(self, form: CustomRegistrationForm) -> None: """ Функция регистрации пользователя. 1. Ввод email пользователя, указанный на Zendesk @@ -124,7 +124,7 @@ class CustomRegistrationView(RegistrationView): ) user.user_permissions.add(permission) - def get_success_url(self, user: User = None): + def get_success_url(self, user: User = None) -> Dict: """ Функция возвращает url-адрес страницы, куда нужно перейти после успешной/не успешной регистрации. Используется самой django-registration. @@ -135,7 +135,13 @@ class CustomRegistrationView(RegistrationView): return self.urls[self.redirect_url] -def registration_error(request): +def registration_error(request: WSGIRequest) -> HttpResponse: + """ + Функция отображения страницы ошибки регистрации. + + :param request: регистрация + :return: адресация на страницу ошибки + """ return render(request, 'django_registration/registration_error.html') @@ -199,7 +205,7 @@ def work_page(request: WSGIRequest, id: int) -> HttpResponse: @login_required() -def work_hand_over(request: WSGIRequest): +def work_hand_over(request: WSGIRequest) -> HttpResponseRedirect: """ Функция позволяет текущему пользователю сдать права, а именно сменить в Zendesk роль с "engineer" на "light_agent" @@ -224,7 +230,12 @@ def work_become_engineer(request: WSGIRequest) -> HttpResponseRedirect: @login_required() -def work_get_tickets(request): +def work_get_tickets(request: WSGIRequest) -> HttpResponse: + """ + + :param request: + :return: + """ zenpy_user = zenpy.get_user(request.user.email) if zenpy_user.role == 'admin' or zenpy_user.custom_role_id == ZENDESK_ROLES['engineer']: tickets = [ticket for ticket in zenpy.admin.search(type="ticket") if @@ -280,7 +291,7 @@ class AdminPageView(LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageM self.make_light_agents(users) return super().form_valid(form) - def make_engineers(self, users): + def make_engineers(self, users: list) -> None: """ Функция проходит по списку пользователей, проставляя статус "engineer". @@ -291,7 +302,7 @@ class AdminPageView(LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageM make_engineer(user, self.request.user) log(user, self.request.user.userprofile) - def make_light_agents(self, users): + def make_light_agents(self, users: list) -> None: """ Функция проходит по списку пользователей, проставляя статус "light agent". @@ -333,7 +344,7 @@ class UsersViewSet(viewsets.ReadOnlyModelViewSet): return Response(res) @staticmethod - def choose_users(zendesk, model): + def choose_users(zendesk, model) -> list: users = [] for zendesk_user in zendesk: if zendesk_user.name not in [user.name for user in model]: @@ -341,7 +352,7 @@ class UsersViewSet(viewsets.ReadOnlyModelViewSet): return users @staticmethod - def get_zendesk_users(users): + def get_zendesk_users(users: list) -> ZendeskUserSerializer: zendesk_users = ZendeskUserSerializer( data=[user for user in users if user.role != 'admin'], many=True