Merge branch 'feature/pylint' into feature/documentation

# Conflicts:
#	main/extra_func.py
This commit is contained in:
Степаненко Ольга 2021-05-06 12:44:12 +03:00
commit 9c54fcca11
7 changed files with 65 additions and 58 deletions

View File

@ -1,19 +1,19 @@
from django.contrib.auth.backends import ModelBackend from django.contrib.auth.backends import ModelBackend
from django.contrib.auth.models import User from django.contrib.auth import get_user_model
class EmailAuthBackend(ModelBackend): class EmailAuthBackend(ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs): def authenticate(self, request, username=None, password=None, **kwargs):
try: try:
user = User.objects.get(email=username) user = get_user_model().objects.get(email=username)
if user.check_password(password): if user.check_password(password):
return user return user
return None return None
except User.DoesNotExist: except get_user_model().DoesNotExist:
return None return None
def get_user(self, user_id): def get_user(self, user_id):
try: try:
return User.objects.get(pk=user_id) return get_user_model().objects.get(pk=user_id)
except User.DoesNotExist: except get_user_model().DoesNotExist:
return None return None

View File

@ -24,7 +24,7 @@ BASE_DIR = Path(__file__).resolve().parent.parent
SECRET_KEY = os.getenv('ACTRL_SECRET_KEY', 'empty') SECRET_KEY = os.getenv('ACTRL_SECRET_KEY', 'empty')
# SECURITY WARNING: don't run with debug turned on in production! # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = bool(int(os.getenv('ACTRL_DEBUG', 1))) DEBUG = bool(int(os.getenv('ACTRL_DEBUG', '1')))
ALLOWED_HOSTS = [ ALLOWED_HOSTS = [
'127.0.0.1', '127.0.0.1',
@ -60,8 +60,8 @@ ROOT_URLCONF = 'access_controller.urls'
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = os.getenv('ACTRL_EMAIL_HOST', 'smtp.gmail.com') EMAIL_HOST = os.getenv('ACTRL_EMAIL_HOST', 'smtp.gmail.com')
EMAIL_PORT = int(os.getenv('ACTRL_EMAIL_PORT', 587)) EMAIL_PORT = int(os.getenv('ACTRL_EMAIL_PORT', '587'))
EMAIL_USE_TLS = bool(int(os.getenv('ACTRL_EMAIL_TLS', 1))) EMAIL_USE_TLS = bool(int(os.getenv('ACTRL_EMAIL_TLS', '1')))
EMAIL_HOST_USER = os.getenv('ACTRL_EMAIL_HOST_USER', 'group02django@gmail.com') EMAIL_HOST_USER = os.getenv('ACTRL_EMAIL_HOST_USER', 'group02django@gmail.com')
EMAIL_HOST_PASSWORD = os.getenv('ACTRL_EMAIL_HOST_PASSWORD', 'djangogroup02') EMAIL_HOST_PASSWORD = os.getenv('ACTRL_EMAIL_HOST_PASSWORD', 'djangogroup02')
DEFAULT_FROM_EMAIL = os.getenv('ACTRL_FROM_EMAIL', EMAIL_HOST_USER) DEFAULT_FROM_EMAIL = os.getenv('ACTRL_FROM_EMAIL', EMAIL_HOST_USER)
@ -154,8 +154,8 @@ AUTHENTICATION_BACKENDS = [
ZENDESK_ROLES = { ZENDESK_ROLES = {
'engineer': int(os.getenv('ENG_CROLE_ID', 0)), 'engineer': int(os.getenv('ENG_CROLE_ID', '0')),
'light_agent': int(os.getenv('LA_CROLE_ID', 0)), 'light_agent': int(os.getenv('LA_CROLE_ID', '0')),
} }
ZENDESK_GROUPS = { ZENDESK_GROUPS = {
@ -165,7 +165,7 @@ ZENDESK_GROUPS = {
SOLVED_TICKETS_EMAIL = os.getenv('ST_EMAIL') SOLVED_TICKETS_EMAIL = os.getenv('ST_EMAIL')
ZENDESK_MAX_AGENTS = int(os.getenv('LICENSE_NO', 0)) ZENDESK_MAX_AGENTS = int(os.getenv('LICENSE_NO', '0'))
REST_FRAMEWORK = { REST_FRAMEWORK = {
# Use Django's standard `django.contrib.auth` permissions, # Use Django's standard `django.contrib.auth` permissions,
@ -175,7 +175,7 @@ REST_FRAMEWORK = {
] ]
} }
ONE_DAY = int(os.getenv('SHIFTH', 0)) # Количество часов в 1 рабочем дне ONE_DAY = int(os.getenv('SHIFTH', '0')) # Количество часов в 1 рабочем дне
ACTRL_ZENDESK_SUBDOMAIN = os.getenv('ACTRL_ZENDESK_SUBDOMAIN') or os.getenv('ZD_DOMAIN') ACTRL_ZENDESK_SUBDOMAIN = os.getenv('ACTRL_ZENDESK_SUBDOMAIN') or os.getenv('ZD_DOMAIN')
ACTRL_API_EMAIL = os.getenv('ACTRL_API_EMAIL') or os.getenv('ACCESS_CONTROLLER_API_EMAIL') ACTRL_API_EMAIL = os.getenv('ACTRL_API_EMAIL') or os.getenv('ACCESS_CONTROLLER_API_EMAIL')

View File

@ -5,7 +5,7 @@ import logging
from datetime import timedelta, datetime, date from datetime import timedelta, datetime, date
from typing import Optional, Union from typing import Optional, Union
from django.contrib.auth.models import User from django.contrib.auth import get_user_model
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.core.handlers.wsgi import WSGIRequest from django.core.handlers.wsgi import WSGIRequest
from django.http import HttpResponseRedirect, HttpResponsePermanentRedirect from django.http import HttpResponseRedirect, HttpResponsePermanentRedirect
@ -21,12 +21,13 @@ from main.models import UserProfile, RoleChangeLogs, UnassignedTicket, Unassigne
from main.zendesk_admin import zenpy from main.zendesk_admin import zenpy
def update_role(user_profile: UserProfile, role: int) -> None: def update_role(user_profile: UserProfile, role: int, who_changes: get_user_model()) -> None:
""" """
Функция меняет роль пользователя. Функция меняет роль пользователя.
:param user_profile: Профиль пользователя :param user_profile: Профиль пользователя
:param role: Новая роль :param role: Новая роль
:param who_changes: Пользователь, меняющий роль
:return: Пользователь с обновленной ролью :return: Пользователь с обновленной ролью
""" """
zendesk = zenpy zendesk = zenpy
@ -34,21 +35,21 @@ def update_role(user_profile: UserProfile, role: int) -> None:
user.custom_role_id = role user.custom_role_id = role
user_profile.custom_role_id = role user_profile.custom_role_id = role
user_profile.save() user_profile.save()
log(user_profile) log(user_profile, who_changes.userprofile)
zendesk.admin.users.update(user) zendesk.admin.users.update(user)
def make_engineer(user_profile: UserProfile) -> None: def make_engineer(user_profile: UserProfile, who_changes: get_user_model()) -> 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'], who_changes)
def make_light_agent(user_profile: UserProfile) -> None: def make_light_agent(user_profile: UserProfile, who_changes: get_user_model()) -> None:
""" """
Функция устанавливает пользователю роль легкого агента. Функция устанавливает пользователю роль легкого агента.
@ -68,13 +69,14 @@ def make_light_agent(user_profile: UserProfile) -> None:
else: else:
ticket.assignee = None ticket.assignee = None
ticket.group_id = zenpy.buffer_group_id ticket.group_id = zenpy.buffer_group_id
if tickets.count: if tickets.count:
zenpy.admin.tickets.update(tickets.values) zenpy.admin.tickets.update(tickets.values)
attempts, success = 5, False attempts, success = 5, False
while not success and attempts != 0: while not success and attempts != 0:
try: try:
update_role(user_profile, ROLES['light_agent']) update_role(user_profile, ROLES['light_agent'], who_changes)
success = True success = True
except APIException as e: except APIException as e:
attempts -= 1 attempts -= 1
@ -101,7 +103,7 @@ def get_tickets_list(email) -> list:
return zenpy.admin.search(assignee=email, type='ticket') return zenpy.admin.search(assignee=email, type='ticket')
def update_profile(user_profile: UserProfile): def update_profile(user_profile: UserProfile) -> None:
""" """
Функция обновляет профиль пользователя в соответствии с текущим в Zendesk. Функция обновляет профиль пользователя в соответствии с текущим в Zendesk.
@ -173,7 +175,7 @@ def update_user_in_model(profile: UserProfile, zendesk_user: ZenpyUser) -> None:
def count_users(users: list) -> tuple: def count_users(users: list) -> tuple:
""" """
Функция подсчета количества сотрудников с ролями engineer и light_agent Функция подсчета количества сотрудников с ролями engineer и light_agent.
""" """
engineers, light_agents = 0, 0 engineers, light_agents = 0, 0
for user in users: for user in users:
@ -184,14 +186,14 @@ def count_users(users: list) -> tuple:
return engineers, light_agents return engineers, light_agents
def update_users_in_model() -> list: def update_users_in_model():
""" """
Обновляет пользователей в модели UserProfile по списку пользователей в организации Обновляет пользователей в модели UserProfile по списку пользователей в организации
""" """
users = get_users_list() users = get_users_list()
for user in users: for user in users:
try: try:
profile = User.objects.get(email=user.email).userprofile profile = get_user_model().objects.get(email=user.email).userprofile
update_user_in_model(profile, user) update_user_in_model(profile, user)
except ObjectDoesNotExist: except ObjectDoesNotExist:
pass pass
@ -290,7 +292,6 @@ class StatisticData:
stat = self._use_display(stat) stat = self._use_display(stat)
stat = self._use_interval(stat) stat = self._use_interval(stat)
return stat return stat
else:
return None return None
def is_valid_statistic(self) -> bool: def is_valid_statistic(self) -> bool:
@ -333,7 +334,6 @@ class StatisticData:
""" """
if self.is_valid_data(): if self.is_valid_data():
return self.data return self.data
else:
return None return None
def is_valid_data(self) -> bool: def is_valid_data(self) -> bool:
@ -407,9 +407,9 @@ class StatisticData:
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=get_user_model().objects.get(email=self.email),
).order_by('change_time') ).order_by('change_time')
except User.DoesNotExist: except get_user_model().DoesNotExist:
self.errors += ['Пользователь не найден'] self.errors += ['Пользователь не найден']
def _init_statistic(self) -> None: def _init_statistic(self) -> None:
@ -434,9 +434,11 @@ class StatisticData:
if self.data[log_index].new_role == ROLES['engineer']: if self.data[log_index].new_role == ROLES['engineer']:
self.engineer_logic(log_index) self.engineer_logic(log_index)
def engineer_logic(self, log_index: int) -> None: def engineer_logic(self, log_index):
""" """
Функция обрабатывает основную часть работы инженера Функция обрабатывает основную часть работы инженера.
:param log_index: Индекс текущего лога
""" """
current_log, next_log = self.data[log_index], self.data[log_index + 1] current_log, next_log = self.data[log_index], self.data[log_index + 1]
if current_log.change_time.date() != next_log.change_time.date(): if current_log.change_time.date() != next_log.change_time.date():
@ -450,7 +452,9 @@ class StatisticData:
def post_engineer_logic(self, last_log: RoleChangeLogs) -> None: 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)) 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():
@ -463,11 +467,13 @@ class StatisticData:
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): 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), self.fill_daterange(max(get_user_model().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()
@ -556,12 +562,12 @@ class CsvFormatter(logging.Formatter):
return msg return msg
def log(user, admin=None): def log(user: get_user_model(), admin: int = 0) -> None:
""" """
Осуществляет запись логов в базу данных и csv файл Функция осуществляет запись логов в базу данных и csv файл.
:param admin:
:param user: :param admin: Админ, который меняет роль
:return: :param user: Пользователь, которому изменена роль
""" """
users = [user, admin] users = [user, admin]
logger = logging.getLogger('MY_LOGGER') logger = logging.getLogger('MY_LOGGER')

View File

@ -4,7 +4,7 @@
from django.db import models from django.db import models
from django.contrib.auth.models import User from django.contrib.auth import get_user_model
from django.db.models.signals import post_save from django.db.models.signals import post_save
from django.dispatch import receiver from django.dispatch import receiver
from django.utils import timezone from django.utils import timezone
@ -25,7 +25,7 @@ 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(to=get_user_model(), on_delete=models.CASCADE, help_text='Пользователь')
role = models.CharField(default='None', max_length=100, 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.IntegerField(default=0, help_text='Код роли пользователя')
image = models.URLField(null=True, blank=True, help_text='Аватарка') image = models.URLField(null=True, blank=True, help_text='Аватарка')
@ -47,7 +47,7 @@ class UserProfile(models.Model):
return 'UNDEFINED' return 'UNDEFINED'
@receiver(post_save, sender=User) @receiver(post_save, sender=get_user_model())
def create_user_profile(instance, created, **kwargs) -> None: def create_user_profile(instance, created, **kwargs) -> None:
""" """
Функция создания профиля пользователя (Userprofile) при регистрации пользователя. Функция создания профиля пользователя (Userprofile) при регистрации пользователя.
@ -61,7 +61,7 @@ def create_user_profile(instance, created, **kwargs) -> None:
UserProfile.objects.create(user=instance) UserProfile.objects.create(user=instance)
@receiver(post_save, sender=User) @receiver(post_save, sender=get_user_model())
def save_user_profile(instance, **kwargs) -> None: def save_user_profile(instance, **kwargs) -> None:
""" """
Функция записи БД профиля пользователя. Функция записи БД профиля пользователя.
@ -77,12 +77,12 @@ class RoleChangeLogs(models.Model):
""" """
Модель для логгирования изменений ролей пользователя Модель для логгирования изменений ролей пользователя
""" """
user = models.ForeignKey(to=User, on_delete=models.CASCADE, user = models.ForeignKey(to=get_user_model(), 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(default=timezone.now, help_text='Дата и время изменения роли')
changed_by = models.ForeignKey(to=User, on_delete=models.CASCADE, related_name='changed_by', changed_by = models.ForeignKey(to=get_user_model(), on_delete=models.CASCADE, related_name='changed_by',
help_text='Кем была изменена роль') help_text='Кем была изменена роль')
@ -110,7 +110,7 @@ class UnassignedTicket(models.Model):
Модель не распределенного тикета. Модель не распределенного тикета.
""" """
assignee = models.ForeignKey(to=User, on_delete=models.CASCADE, related_name='tickets', assignee = models.ForeignKey(to=get_user_model(), on_delete=models.CASCADE, related_name='tickets',
help_text='Пользователь, с которого снят тикет') help_text='Пользователь, с которого снят тикет')
ticket_id = models.IntegerField(help_text='Номер тикета, для которого сняли ответственного') ticket_id = models.IntegerField(help_text='Номер тикета, для которого сняли ответственного')
status = models.IntegerField(choices=UnassignedTicketStatus.choices, default=UnassignedTicketStatus.UNASSIGNED, status = models.IntegerField(choices=UnassignedTicketStatus.choices, default=UnassignedTicketStatus.UNASSIGNED,

View File

@ -1,7 +1,7 @@
""" """
Сериализаторы. Сериализаторы.
""" """
from django.contrib.auth.models import User from django.contrib.auth import get_user_model
from rest_framework import serializers from rest_framework import serializers
from main.models import UserProfile from main.models import UserProfile
from access_controller.settings import ZENDESK_ROLES from access_controller.settings import ZENDESK_ROLES
@ -17,7 +17,7 @@ class UserSerializer(serializers.HyperlinkedModelSerializer):
:type email: :class:`list` :type email: :class:`list`
""" """
class Meta: class Meta:
model = User model = get_user_model()
fields = ['email'] fields = ['email']

View File

@ -1,6 +1,6 @@
from urllib.parse import urlparse from urllib.parse import urlparse
from django.contrib.auth.models import User from django.contrib.auth import get_user_model
from django.core import mail from django.core import mail
from django.test import TestCase, Client from django.test import TestCase, Client
from django.urls import reverse from django.urls import reverse
@ -71,13 +71,13 @@ class RegistrationTestCase(TestCase):
def test_registration_user_creating(self): def test_registration_user_creating(self):
with self.settings(EMAIL_BACKEND=self.email_backend): with self.settings(EMAIL_BACKEND=self.email_backend):
self.client.post(reverse('registration'), data={'email': self.any_zendesk_user_email}) 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) zendesk_user = zenpy.get_user(self.any_zendesk_user_email)
self.assertEqual(user.userprofile.name, zendesk_user.name) self.assertEqual(user.userprofile.name, zendesk_user.name)
def test_permissions_applying(self): def test_permissions_applying(self):
with self.settings(EMAIL_BACKEND=self.email_backend): with self.settings(EMAIL_BACKEND=self.email_backend):
self.client.post(reverse('registration'), data={'email': self.zendesk_admin_email}) 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.assertEqual(user.userprofile.role, 'admin')
self.assertTrue(user.has_perm('main.has_control_access')) self.assertTrue(user.has_perm('main.has_control_access'))

View File

@ -9,7 +9,8 @@ from typing import Dict, Any, Optional
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
from django.contrib.auth.models import User, Permission from django.contrib.auth import get_user_model
from django.contrib.auth.models import Permission
from django.contrib.auth.tokens import default_token_generator from django.contrib.auth.tokens import default_token_generator
from django.contrib.auth.forms import PasswordResetForm from django.contrib.auth.forms import PasswordResetForm
from django.contrib.auth.views import LoginView from django.contrib.auth.views import LoginView
@ -83,7 +84,7 @@ class CustomRegistrationView(RegistrationView):
} }
redirect_url = 'done' redirect_url = 'done'
def register(self, form: CustomRegistrationForm) -> Optional[User]: def register(self, form: CustomRegistrationForm) -> Optional[get_user_model()]:
""" """
Функция регистрации пользователя. Функция регистрации пользователя.
1. Ввод email пользователя, указанный на Zendesk 1. Ввод email пользователя, указанный на Zendesk
@ -108,10 +109,10 @@ class CustomRegistrationView(RegistrationView):
'html_email_template_name': None, 'html_email_template_name': None,
'extra_email_context': None, 'extra_email_context': None,
} }
user = User.objects.create_user( user = get_user_model().objects.create_user(
username=form.data['email'], username=form.data['email'],
email=form.data['email'], email=form.data['email'],
password=User.objects.make_random_password(length=50) password=get_user_model().objects.make_random_password(length=50)
) )
try: try:
update_profile(user.userprofile) update_profile(user.userprofile)
@ -128,7 +129,7 @@ class CustomRegistrationView(RegistrationView):
return None return None
@staticmethod @staticmethod
def set_permission(user: User) -> None: def set_permission(user: get_user_model()) -> None:
""" """
Функция дает разрешение на просмотр страница администратора, если пользователь имеет роль admin. Функция дает разрешение на просмотр страница администратора, если пользователь имеет роль admin.
@ -142,7 +143,7 @@ class CustomRegistrationView(RegistrationView):
) )
user.user_permissions.add(permission) user.user_permissions.add(permission)
def get_success_url(self, user: User = None) -> Dict: def get_success_url(self, user: get_user_model() = None) -> Dict:
""" """
Функция возвращает url-адрес страницы, куда нужно перейти после успешной/не успешной регистрации. Функция возвращает url-адрес страницы, куда нужно перейти после успешной/не успешной регистрации.
Используется самой django-registration. Используется самой django-registration.