Pylint 7.5

This commit is contained in:
Степаненко Ольга 2021-04-27 19:41:47 +03:00
parent 3f6cf23f4e
commit 8e0610840d
7 changed files with 113 additions and 50 deletions

View File

@ -148,7 +148,10 @@ docker run -d -p 8000:8000 \
Пример полной конфигурации можно найти в [.env.example](.env.example). Почту и токен админа ZenDesk взять у руководителя (если вы не админ). Пример полной конфигурации можно найти в [.env.example](.env.example). Почту и токен админа ZenDesk взять у руководителя (если вы не админ).
## Для проверки pylint используем: ## Для проверки pylint используем:
pylint --load-plugins pylint_django ../access_controller pylint --load-plugins pylint_django --disable=E5110,C0415 ../access_controller
## Для приведения файлов к стандарту PEP8 используем:
autopep8 --in-place <filename>
## Read more ## Read more
- Zenpy: [http://docs.facetoe.com.au](http://docs.facetoe.com.au) - Zenpy: [http://docs.facetoe.com.au](http://docs.facetoe.com.au)

View File

@ -1,5 +1,11 @@
"""
Стандартный файл Django конфигурации приложения.
"""
from django.apps import AppConfig from django.apps import AppConfig
class MainConfig(AppConfig): class MainConfig(AppConfig):
"""
Старт приложения
"""
name = 'main' name = 'main'

View File

@ -38,7 +38,8 @@ def make_engineer(user_profile: UserProfile, who_changes: User) -> None:
Функция устанавливает пользователю роль инженера. Функция устанавливает пользователю роль инженера.
:param user_profile: Профиль пользователя :param user_profile: Профиль пользователя
:return: Вызов функции **update_role** с параметрами: профиль пользователя, роль "engineer" :return: Вызов функции **update_role** с параметрами:
профиль пользователя, роль "engineer"
""" """
update_role(user_profile, ROLES['engineer']) update_role(user_profile, ROLES['engineer'])
@ -48,7 +49,8 @@ def make_light_agent(user_profile: UserProfile) -> None:
Функция устанавливает пользователю роль легкого агента. Функция устанавливает пользователю роль легкого агента.
:param user_profile: Профиль пользователя :param user_profile: Профиль пользователя
:return: Вызов функции **update_role** с параметрами: профиль пользователя, роль "light_agent" :return: Вызов функции **update_role** с параметрами:
профиль пользователя, роль "light_agent"
""" """
tickets = get_tickets_list(user_profile.user.email) tickets = get_tickets_list(user_profile.user.email)
ticket: ZenpyTicket ticket: ZenpyTicket
@ -56,7 +58,8 @@ def make_light_agent(user_profile: UserProfile) -> None:
UnassignedTicket.objects.create( UnassignedTicket.objects.create(
assignee=user_profile.user, assignee=user_profile.user,
ticket_id=ticket.id, ticket_id=ticket.id,
status=UnassignedTicketStatus.SOLVED if ticket.status == 'solved' else UnassignedTicketStatus.UNASSIGNED status=UnassignedTicketStatus.SOLVED if ticket.status == 'solved'
else UnassignedTicketStatus.UNASSIGNED
) )
if ticket.status == 'solved': if ticket.status == 'solved':
ticket.assignee_id = zenpy.solved_tickets_user_id ticket.assignee_id = zenpy.solved_tickets_user_id
@ -79,7 +82,8 @@ def make_light_agent(user_profile: UserProfile) -> None:
def get_users_list() -> list: def get_users_list() -> list:
""" """
Функция **get_users_list** возвращает список пользователей Zendesk, относящихся к организации SYSTEM. Функция **get_users_list** возвращает список
пользователей Zendesk, относящихся к организации SYSTEM.
""" """
zendesk = zenpy zendesk = zenpy
@ -101,7 +105,8 @@ def update_profile(user_profile: UserProfile) -> None:
Функция обновляет профиль пользователя в соответствии с текущим в Zendesk. Функция обновляет профиль пользователя в соответствии с текущим в Zendesk.
:param user_profile: Профиль пользователя :param user_profile: Профиль пользователя
:return: Обновленный, в соответствие с текущими данными в Zendesk, профиль пользователя :return: Обновленный, в соответствие с текущими данными
в Zendesk, профиль пользователя
""" """
user = zenpy.get_user(user_profile.user.email) user = zenpy.get_user(user_profile.user.email)
user_profile.name = user.name user_profile.name = user.name
@ -135,7 +140,8 @@ def check_user_auth(email: str, password: str) -> bool:
""" """
Функция проверяет, верны ли входные данные. Функция проверяет, верны ли входные данные.
:raise: :class:`APIException`: исключение, вызываемое если пользователь не аутентифицирован :raise: :class:`APIException`: исключение,
вызываемое если пользователь не аутентифицирован
""" """
creds = { creds = {
'email': email, 'email': email,
@ -207,10 +213,12 @@ def daterange(start_date: timedelta, end_date: timedelta) -> list:
return dates return dates
def get_timedelta(log: RoleChangeLogs, time: timedelta=None) -> timedelta: def get_timedelta(log: RoleChangeLogs, time: timedelta = None) -> timedelta:
""" """
Функция возвращает объект класса timedelta, который хранит промежуток времени от начала суток до момента, Функция возвращает объект класса timedelta,
который находится в log (объект класса RoleChangeLogs) или в time(datetime.time), если введён. который хранит промежуток времени от начала суток до момента,
который находится в log (объект класса RoleChangeLogs)
или в time(datetime.time), если введён.
:param log: Лог :param log: Лог
:param time: Время :param time: Время
@ -278,7 +286,9 @@ class StatisticData:
""" """
Функция возвращает статистику работы пользователя. Функция возвращает статистику работы пользователя.
:return: Словарь statistic с применением формата отображения и интервала работы(если они есть). None, если были ошибки при создании. :return: Словарь statistic с применением формата отображения
и интервала работы(если они есть).
None, если были ошибки при создании.
""" """
if self.is_valid_statistic(): if self.is_valid_statistic():
stat = self.statistic stat = self.statistic
@ -369,9 +379,12 @@ class StatisticData:
if self.interval == 'months': if self.interval == 'months':
# Переделываем ключи под формат('началоесяца - конец_месяца') # Переделываем ключи под формат('началоесяца - конец_месяца')
for key, value in stat.items(): for key, value in stat.items():
current_month_start = max(self.start_date, date(year=key.year, month=key.month, day=1)) current_month_start = max(self.start_date, date(
current_month_end = min(self.end_date, last_day_of_month(date(year=key.year, month=key.month, day=1))) year=key.year, month=key.month, day=1))
index = ' - '.join([str(current_month_start), str(current_month_end)]) current_month_end = min(self.end_date, last_day_of_month(
date(year=key.year, month=key.month, day=1)))
index = ' - '.join([str(current_month_start),
str(current_month_end)])
if new_stat.get(index): if new_stat.get(index):
new_stat[index] += value new_stat[index] += value
else: else:
@ -392,16 +405,20 @@ class StatisticData:
def _init_data(self) -> None: def _init_data(self) -> None:
""" """
Функция возвращает логи в диапазоне дат start_date - end_date для пользователя с указанным email. Функция возвращает логи в диапазоне дат
start_date - end_date для пользователя с указанным email.
:return: Данные о смене статусов пользователя. Если пользователь не найден или интервал времени некорректен - ошибку. :return: Данные о смене статусов пользователя.
Если пользователь не найден или интервал времени некорректен - ошибку.
""" """
if not self.check_time(): if not self.check_time():
self.errors += ['Конец диапазона должен быть позже начала диапазона и раньше текущего времени'] self.errors += [
'Конец диапазона должен быть позже начала диапазона и раньше текущего времени']
return return
try: try:
self.data = RoleChangeLogs.objects.filter( self.data = RoleChangeLogs.objects.filter(
change_time__range=[self.start_date, self.end_date + timedelta(days=1)], change_time__range=[self.start_date,
self.end_date + timedelta(days=1)],
user=User.objects.get(email=self.email), user=User.objects.get(email=self.email),
).order_by('change_time') ).order_by('change_time')
except User.DoesNotExist: except User.DoesNotExist:
@ -409,7 +426,8 @@ class StatisticData:
def _init_statistic(self) -> None: def _init_statistic(self) -> None:
""" """
Функция заполняет словарь, в котором ключ - дата, значение - кол-во проработанных в этот день секунд. Функция заполняет словарь, в котором ключ - дата,
значение - кол-во проработанных в этот день секунд.
:return: Статистика работы пользователя (statistic) :return: Статистика работы пользователя (statistic)
""" """
@ -439,11 +457,14 @@ class StatisticData:
if current_log.change_time.date() != next_log.change_time.date(): if current_log.change_time.date() != next_log.change_time.date():
self.statistic[current_log.change_time.date()] += ( self.statistic[current_log.change_time.date()] += (
timedelta(days=1) - get_timedelta(current_log)).total_seconds() timedelta(days=1) - get_timedelta(current_log)).total_seconds()
self.statistic[next_log.change_time.date()] += get_timedelta(next_log).total_seconds() self.statistic[next_log.change_time.date(
self.fill_daterange(current_log.change_time.date() + timedelta(days=1), next_log.change_time.date()) )] += get_timedelta(next_log).total_seconds()
self.fill_daterange(current_log.change_time.date(
) + timedelta(days=1), next_log.change_time.date())
else: else:
elapsed_time = next_log.change_time - current_log.change_time elapsed_time = next_log.change_time - current_log.change_time
self.statistic[current_log.change_time.date()] += elapsed_time.total_seconds() self.statistic[current_log.change_time.date(
)] += elapsed_time.total_seconds()
def post_engineer_logic(self, last_log: RoleChangeLogs) -> None: def post_engineer_logic(self, last_log: RoleChangeLogs) -> None:
""" """
@ -451,16 +472,19 @@ class StatisticData:
:param last_log: Последний лог :param last_log: Последний лог
""" """
self.fill_daterange(last_log.change_time.date() + timedelta(days=1), self.end_date + timedelta(days=1)) 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(): if last_log.change_time.date() == timezone.now().date():
self.statistic[last_log.change_time.date()] += ( self.statistic[last_log.change_time.date()] += (
get_timedelta(None, timezone.now().time()) - get_timedelta(last_log) get_timedelta(None, timezone.now().time()) -
get_timedelta(last_log)
).total_seconds() ).total_seconds()
else: else:
self.statistic[last_log.change_time.date()] += ( self.statistic[last_log.change_time.date()] += (
timedelta(days=1) - get_timedelta(last_log)).total_seconds() timedelta(days=1) - get_timedelta(last_log)).total_seconds()
if self.end_date == timezone.now().date(): if self.end_date == timezone.now().date():
self.statistic[self.end_date] = get_timedelta(None, timezone.now().time()).total_seconds() self.statistic[self.end_date] = get_timedelta(
None, timezone.now().time()).total_seconds()
def prev_engineer_logic(self, first_log: RoleChangeLogs) -> None: def prev_engineer_logic(self, first_log: RoleChangeLogs) -> None:
""" """
@ -470,7 +494,8 @@ class StatisticData:
""" """
self.fill_daterange(max(User.objects.get(email=self.email).date_joined.date(), self.start_date), self.fill_daterange(max(User.objects.get(email=self.email).date_joined.date(), self.start_date),
first_log.change_time.date()) first_log.change_time.date())
self.statistic[first_log.change_time.date()] += get_timedelta(first_log).total_seconds() 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) -> None: def fill_daterange(self, first: date, last: date, val: int = 24 * 3600) -> None:
""" """
@ -488,7 +513,8 @@ class StatisticData:
Функция осуществляет обновление всех дней. Функция осуществляет обновление всех дней.
""" """
self.statistic.clear() self.statistic.clear()
self.fill_daterange(self.start_date, self.end_date + timedelta(days=1), 0) self.fill_daterange(
self.start_date, self.end_date + timedelta(days=1), 0)
class DatabaseHandler(logging.Handler): class DatabaseHandler(logging.Handler):
@ -571,9 +597,10 @@ def log(user: User, admin: int = 0) -> None:
def set_session_params_for_work_page(request: WSGIRequest, count: int = None, is_confirm: bool = True) -> \ def set_session_params_for_work_page(request: WSGIRequest, count: int = None, is_confirm: bool = True) -> \
Union[HttpResponsePermanentRedirect, HttpResponseRedirect]: Union[HttpResponsePermanentRedirect, HttpResponseRedirect]:
""" """
Функция для страницы получения прав, устанавливает данные сессии о успешности запроса и количестве назначенных тикетов. Функция для страницы получения прав, устанавливает данные сессии
о успешности запроса и количестве назначенных тикетов.
:param request: Получение данных с рабочей страницы пользователя :param request: Получение данных с рабочей страницы пользователя
:param count: Количество запрошенных тикетов :param count: Количество запрошенных тикетов

View File

@ -66,8 +66,7 @@ class CustomAuthenticationForm(AuthenticationForm):
error_messages = { error_messages = {
'invalid_login': 'invalid_login':
"Пожалуйста, введите правильные электронную почту и пароль. Оба поля " "Пожалуйста, введите правильные электронную почту и пароль. Оба поля "
"могут быть чувствительны к регистру." "могут быть чувствительны к регистру.",
,
'inactive': "Аккаунт не активен.", 'inactive': "Аккаунт не активен.",
} }

View File

@ -19,11 +19,15 @@ class UserProfile(models.Model):
('has_control_access', 'Can view admin page'), ('has_control_access', 'Can view admin page'),
) )
user = models.OneToOneField(to=User, on_delete=models.CASCADE, help_text='Пользователь') user = models.OneToOneField(
role = models.CharField(default='None', max_length=100, help_text='Глобальное имя роли пользователя') to=User, on_delete=models.CASCADE, help_text='Пользователь')
custom_role_id = models.IntegerField(default=0, help_text='Код роли пользователя') role = models.CharField(default='None', max_length=100,
help_text='Глобальное имя роли пользователя')
custom_role_id = models.IntegerField(
default=0, help_text='Код роли пользователя')
image = models.URLField(null=True, blank=True, help_text='Аватарка') image = models.URLField(null=True, blank=True, help_text='Аватарка')
name = models.CharField(default='None', max_length=100, help_text='Имя пользователя на нашем сайте') name = models.CharField(default='None', max_length=100,
help_text='Имя пользователя на нашем сайте')
@property @property
def zendesk_role(self): def zendesk_role(self):
@ -49,12 +53,16 @@ class RoleChangeLogs(models.Model):
""" """
Модель для логирования изменений ролей пользователя. Модель для логирования изменений ролей пользователя.
""" """
user = models.ForeignKey(to=User, on_delete=models.CASCADE, user = models.ForeignKey(to=User,
on_delete=models.CASCADE,
help_text='Пользователь, которому присвоили другую роль') help_text='Пользователь, которому присвоили другую роль')
old_role = models.IntegerField(default=0, help_text='Старая роль') old_role = models.IntegerField(default=0, help_text='Старая роль')
new_role = models.IntegerField(default=0, help_text='Присвоенная роль') new_role = models.IntegerField(default=0, help_text='Присвоенная роль')
change_time = models.DateTimeField(default=timezone.now, help_text='Дата и время изменения роли') change_time = models.DateTimeField(
changed_by = models.ForeignKey(to=User, on_delete=models.CASCADE, related_name='changed_by', default=timezone.now, help_text='Дата и время изменения роли')
changed_by = models.ForeignKey(to=User,
on_delete=models.CASCADE,
related_name='changed_by',
help_text='Кем была изменена роль') help_text='Кем была изменена роль')
@ -64,13 +72,15 @@ class UnassignedTicketStatus(models.IntegerChoices):
:param UNASSIGNED: Снят с пользователя, перенесён в буферную группу :param UNASSIGNED: Снят с пользователя, перенесён в буферную группу
:param RESTORED: Авторство восстановлено :param RESTORED: Авторство восстановлено
:param NOT_FOUND: Пока нас не было, тикет испарился из буферной группы. Дополнительные действия не требуются :param NOT_FOUND: Пока нас не было, тикет испарился из буферной группы.
Дополнительные действия не требуются
:param CLOSED: Тикет уже был закрыт. Дополнительные действия не требуются :param CLOSED: Тикет уже был закрыт. Дополнительные действия не требуются
:param SOLVED: Тикет решён. Записан на пользователя с почтой SOLVED_TICKETS_EMAIL :param SOLVED: Тикет решён. Записан на пользователя с почтой SOLVED_TICKETS_EMAIL
""" """
UNASSIGNED = 0, 'Снят с пользователя, перенесён в буферную группу' UNASSIGNED = 0, 'Снят с пользователя, перенесён в буферную группу'
RESTORED = 1, 'Авторство восстановлено' RESTORED = 1, 'Авторство восстановлено'
NOT_FOUND = 2, 'Пока нас не было, тикет испарился из буферной группы. Дополнительные действия не требуются' NOT_FOUND = 2, 'Пока нас не было, тикет испарился из ' \
'буферной группы. Дополнительные действия не требуются'
CLOSED = 3, 'Тикет уже был закрыт. Дополнительные действия не требуются' CLOSED = 3, 'Тикет уже был закрыт. Дополнительные действия не требуются'
SOLVED = 4, 'Тикет решён. Записан на пользователя с почтой SOLVED_TICKETS_EMAIL' SOLVED = 4, 'Тикет решён. Записан на пользователя с почтой SOLVED_TICKETS_EMAIL'
@ -79,6 +89,11 @@ class UnassignedTicket(models.Model):
""" """
Модель не распределенного тикета. Модель не распределенного тикета.
""" """
assignee = models.ForeignKey(to=User, on_delete=models.CASCADE, related_name='tickets', help_text='Пользователь, с которого снят тикет') assignee = models.ForeignKey(to=User, on_delete=models.CASCADE,
ticket_id = models.IntegerField(help_text='Номер тикера, для которого сняли ответственного') related_name='tickets',
status = models.IntegerField(choices=UnassignedTicketStatus.choices, default=UnassignedTicketStatus.UNASSIGNED, help_text='Статус тикета') help_text='Пользователь, с которого снят тикет')
ticket_id = models.IntegerField(
help_text='Номер тикера, для которого сняли ответственного')
status = models.IntegerField(choices=UnassignedTicketStatus.choices,
default=UnassignedTicketStatus.UNASSIGNED,
help_text='Статус тикета')

View File

@ -1,3 +1,7 @@
"""
Основной функционал приложения.
"""
from smtplib import SMTPException from smtplib import SMTPException
from typing import Dict, Any from typing import Dict, Any
@ -117,7 +121,8 @@ class CustomRegistrationView(RegistrationView):
@staticmethod @staticmethod
def set_permission(user: User) -> None: def set_permission(user: User) -> None:
""" """
Функция дает разрешение на просмотр страница администратора, если пользователь имеет роль admin. Функция дает разрешение на просмотр страница администратора,
если пользователь имеет роль admin.
:param user: авторизованный пользователь (получает разрешение, имея роль "admin") :param user: авторизованный пользователь (получает разрешение, имея роль "admin")
""" """
@ -131,7 +136,8 @@ class CustomRegistrationView(RegistrationView):
def get_success_url(self, user: User = None) -> Dict: def get_success_url(self, user: User = None) -> Dict:
""" """
Функция возвращает url-адрес страницы, куда нужно перейти после успешной/не успешной регистрации. Функция возвращает url-адрес страницы, куда нужно перейти после
успешной/не успешной регистрации.
Используется самой django-registration. Используется самой django-registration.
:param user: пользователь, пытающийся зарегистрироваться :param user: пользователь, пытающийся зарегистрироваться
@ -172,11 +178,13 @@ def profile_page(request: WSGIRequest) -> HttpResponse:
@login_required() @login_required()
def work_page(request: WSGIRequest, id: int) -> HttpResponse: def work_page(request: WSGIRequest, id: int) -> HttpResponse:
""" """
Функция отображения страницы "Управления правами" для текущего пользователя (login_required). Функция отображения страницы "Управления правами"
для текущего пользователя (login_required).
:param request: объект пользователя :param request: объект пользователя
:param id: id пользователя, используется для динамической адресации :param id: id пользователя, используется для динамической адресации
:return: адресация на страницу "Управления правами" (либо на страницу "Авторизации", если id и user.id не совпадают :return: адресация на страницу "Управления правами"
(либо на страницу "Авторизации", если id и user.id не совпадают)
""" """
users = get_users_list() users = get_users_list()
if request.user.id == id: if request.user.id == id:
@ -212,7 +220,8 @@ def work_page(request: WSGIRequest, id: int) -> HttpResponse:
@login_required() @login_required()
def work_hand_over(request: WSGIRequest) -> HttpResponseRedirect: def work_hand_over(request: WSGIRequest) -> HttpResponseRedirect:
""" """
Функция позволяет текущему пользователю сдать права, а именно сменить в Zendesk роль с "engineer" на "light_agent" Функция позволяет текущему пользователю сдать права,
а именно сменить в Zendesk роль с "engineer" на "light_agent"
:param request: данные текущего пользователя (login_required) :param request: данные текущего пользователя (login_required)
:return: перезагрузка текущей страницы после выполнения смены роли :return: перезагрузка текущей страницы после выполнения смены роли
@ -224,7 +233,9 @@ def work_hand_over(request: WSGIRequest) -> HttpResponseRedirect:
@login_required() @login_required()
def work_become_engineer(request: WSGIRequest) -> HttpResponseRedirect: def work_become_engineer(request: WSGIRequest) -> HttpResponseRedirect:
""" """
Функция позволяет текущему пользователю получить права, а именно сменить в Zendesk роль с "light_agent" на "engineer" Функция позволяет текущему пользователю получить права,
а именно сменить в Zendesk роль с "light_agent" на
"engineer"
:param request: данные текущего пользователя (login_required) :param request: данные текущего пользователя (login_required)
:return: перезагрузка текущей страницы после выполнения смены роли :return: перезагрузка текущей страницы после выполнения смены роли
@ -371,7 +382,8 @@ def statistic_page(request: WSGIRequest) -> HttpResponse:
""" """
Функция отображения страницы статистики (для "superuser"). Функция отображения страницы статистики (для "superuser").
:param request: данные о пользователе: email, время и интервал работы. Данные получаем через forms.StatisticForm :param request: данные о пользователе: email, время и интервал работы.
Данные получаем через forms.StatisticForm
:return: адресация на страницу статистики :return: адресация на страницу статистики
""" """

View File

@ -17,3 +17,4 @@ sphinxcontrib-spelling==7.1.0
m2r == 0.2.1 m2r == 0.2.1
pylint == 2.8.2 pylint == 2.8.2
pylint-django == 2.4.4 pylint-django == 2.4.4
autopep8 = 1.5.6