diff --git a/access_controller/auth.py b/access_controller/auth.py index be707e1..00b43a2 100644 --- a/access_controller/auth.py +++ b/access_controller/auth.py @@ -1,19 +1,19 @@ 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): def authenticate(self, request, username=None, password=None, **kwargs): try: - user = User.objects.get(email=username) + user = get_user_model().objects.get(email=username) if user.check_password(password): return user return None - except User.DoesNotExist: + except get_user_model().DoesNotExist: return None def get_user(self, user_id): try: - return User.objects.get(pk=user_id) - except User.DoesNotExist: + return get_user_model().objects.get(pk=user_id) + except get_user_model().DoesNotExist: return None diff --git a/access_controller/settings.py b/access_controller/settings.py index 60367a3..37a7e63 100644 --- a/access_controller/settings.py +++ b/access_controller/settings.py @@ -24,7 +24,7 @@ BASE_DIR = Path(__file__).resolve().parent.parent SECRET_KEY = os.getenv('ACTRL_SECRET_KEY', 'empty') # 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 = [ '127.0.0.1', @@ -60,8 +60,8 @@ ROOT_URLCONF = 'access_controller.urls' EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' EMAIL_HOST = os.getenv('ACTRL_EMAIL_HOST', 'smtp.gmail.com') -EMAIL_PORT = int(os.getenv('ACTRL_EMAIL_PORT', 587)) -EMAIL_USE_TLS = bool(int(os.getenv('ACTRL_EMAIL_TLS', 1))) +EMAIL_PORT = int(os.getenv('ACTRL_EMAIL_PORT', '587')) +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_PASSWORD = os.getenv('ACTRL_EMAIL_HOST_PASSWORD', 'djangogroup02') DEFAULT_FROM_EMAIL = os.getenv('ACTRL_FROM_EMAIL', EMAIL_HOST_USER) @@ -154,8 +154,8 @@ AUTHENTICATION_BACKENDS = [ ZENDESK_ROLES = { - 'engineer': int(os.getenv('ENG_CROLE_ID', 0)), - 'light_agent': int(os.getenv('LA_CROLE_ID', 0)), + 'engineer': int(os.getenv('ENG_CROLE_ID', '0')), + 'light_agent': int(os.getenv('LA_CROLE_ID', '0')), } ZENDESK_GROUPS = { @@ -165,7 +165,7 @@ ZENDESK_GROUPS = { 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 = { # 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_API_EMAIL = os.getenv('ACTRL_API_EMAIL') or os.getenv('ACCESS_CONTROLLER_API_EMAIL') diff --git a/main/extra_func.py b/main/extra_func.py index 61c337b..10ed367 100644 --- a/main/extra_func.py +++ b/main/extra_func.py @@ -5,7 +5,7 @@ import logging from datetime import timedelta, datetime, date 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.handlers.wsgi import WSGIRequest from django.http import HttpResponseRedirect, HttpResponsePermanentRedirect @@ -21,12 +21,13 @@ from main.models import UserProfile, RoleChangeLogs, UnassignedTicket, Unassigne 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 role: Новая роль + :param who_changes: Пользователь, меняющий роль :return: Пользователь с обновленной ролью """ zendesk = zenpy @@ -34,21 +35,21 @@ def update_role(user_profile: UserProfile, role: int) -> None: user.custom_role_id = role user_profile.custom_role_id = role user_profile.save() - log(user_profile) + log(user_profile, who_changes.userprofile) 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: Профиль пользователя :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: ticket.assignee = None ticket.group_id = zenpy.buffer_group_id + if tickets.count: zenpy.admin.tickets.update(tickets.values) attempts, success = 5, False while not success and attempts != 0: try: - update_role(user_profile, ROLES['light_agent']) + update_role(user_profile, ROLES['light_agent'], who_changes) success = True except APIException as e: attempts -= 1 @@ -101,7 +103,7 @@ def get_tickets_list(email) -> list: return zenpy.admin.search(assignee=email, type='ticket') -def update_profile(user_profile: UserProfile): +def update_profile(user_profile: UserProfile) -> None: """ Функция обновляет профиль пользователя в соответствии с текущим в Zendesk. @@ -173,7 +175,7 @@ def update_user_in_model(profile: UserProfile, zendesk_user: ZenpyUser) -> None: def count_users(users: list) -> tuple: """ - Функция подсчета количества сотрудников с ролями engineer и light_agent + Функция подсчета количества сотрудников с ролями engineer и light_agent. """ engineers, light_agents = 0, 0 for user in users: @@ -184,14 +186,14 @@ def count_users(users: list) -> tuple: return engineers, light_agents -def update_users_in_model() -> list: +def update_users_in_model(): """ Обновляет пользователей в модели UserProfile по списку пользователей в организации """ users = get_users_list() for user in users: 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) except ObjectDoesNotExist: pass @@ -290,8 +292,7 @@ class StatisticData: stat = self._use_display(stat) stat = self._use_interval(stat) return stat - else: - return None + return None def is_valid_statistic(self) -> bool: """ @@ -333,8 +334,7 @@ class StatisticData: """ if self.is_valid_data(): return self.data - else: - return None + return None def is_valid_data(self) -> bool: """ @@ -407,9 +407,9 @@ class StatisticData: try: self.data = RoleChangeLogs.objects.filter( 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') - except User.DoesNotExist: + except get_user_model().DoesNotExist: self.errors += ['Пользователь не найден'] def _init_statistic(self) -> None: @@ -434,9 +434,11 @@ class StatisticData: if self.data[log_index].new_role == ROLES['engineer']: 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] 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: """ - Функция обрабатывает случай, когда нам изветсно что инженер работал и после диапазона + Функция обрабатывает случай, когда нам известно что инженер работал и после диапазона. + + :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(): @@ -463,11 +467,13 @@ 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), + 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() @@ -556,12 +562,12 @@ class CsvFormatter(logging.Formatter): return msg -def log(user, admin=None): +def log(user: get_user_model(), admin: int = 0) -> None: """ - Осуществляет запись логов в базу данных и csv файл - :param admin: - :param user: - :return: + Функция осуществляет запись логов в базу данных и csv файл. + + :param admin: Админ, который меняет роль + :param user: Пользователь, которому изменена роль """ users = [user, admin] logger = logging.getLogger('MY_LOGGER') diff --git a/main/models.py b/main/models.py index ea3388a..24db2f2 100644 --- a/main/models.py +++ b/main/models.py @@ -4,7 +4,7 @@ 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.dispatch import receiver from django.utils import timezone @@ -25,7 +25,7 @@ class UserProfile(models.Model): ('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='Глобальное имя роли пользователя') custom_role_id = models.IntegerField(default=0, help_text='Код роли пользователя') image = models.URLField(null=True, blank=True, help_text='Аватарка') @@ -47,7 +47,7 @@ class UserProfile(models.Model): return 'UNDEFINED' -@receiver(post_save, sender=User) +@receiver(post_save, sender=get_user_model()) def create_user_profile(instance, created, **kwargs) -> None: """ Функция создания профиля пользователя (Userprofile) при регистрации пользователя. @@ -61,7 +61,7 @@ def create_user_profile(instance, created, **kwargs) -> None: UserProfile.objects.create(user=instance) -@receiver(post_save, sender=User) +@receiver(post_save, sender=get_user_model()) 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='Пользователь, которому присвоили другую роль') old_role = models.IntegerField(default=0, help_text='Старая роль') new_role = models.IntegerField(default=0, 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='Кем была изменена роль') @@ -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='Пользователь, с которого снят тикет') ticket_id = models.IntegerField(help_text='Номер тикета, для которого сняли ответственного') status = models.IntegerField(choices=UnassignedTicketStatus.choices, default=UnassignedTicketStatus.UNASSIGNED, diff --git a/main/serializers.py b/main/serializers.py index e72dc7a..70c4352 100644 --- a/main/serializers.py +++ b/main/serializers.py @@ -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 main.models import UserProfile from access_controller.settings import ZENDESK_ROLES @@ -17,7 +17,7 @@ class UserSerializer(serializers.HyperlinkedModelSerializer): :type email: :class:`list` """ class Meta: - model = User + model = get_user_model() fields = ['email'] diff --git a/main/tests.py b/main/tests.py index c06bc21..42c8542 100644 --- a/main/tests.py +++ b/main/tests.py @@ -1,6 +1,6 @@ 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.test import TestCase, Client from django.urls import reverse @@ -71,13 +71,13 @@ class RegistrationTestCase(TestCase): def test_registration_user_creating(self): 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): 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')) diff --git a/main/views.py b/main/views.py index 8f58b70..bdff369 100644 --- a/main/views.py +++ b/main/views.py @@ -9,7 +9,8 @@ from typing import Dict, Any, Optional from django.contrib import messages from django.contrib.auth.decorators import login_required 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.forms import PasswordResetForm from django.contrib.auth.views import LoginView @@ -83,7 +84,7 @@ class CustomRegistrationView(RegistrationView): } redirect_url = 'done' - def register(self, form: CustomRegistrationForm) -> Optional[User]: + def register(self, form: CustomRegistrationForm) -> Optional[get_user_model()]: """ Функция регистрации пользователя. 1. Ввод email пользователя, указанный на Zendesk @@ -108,10 +109,10 @@ class CustomRegistrationView(RegistrationView): 'html_email_template_name': None, 'extra_email_context': None, } - user = User.objects.create_user( + user = get_user_model().objects.create_user( username=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: update_profile(user.userprofile) @@ -128,7 +129,7 @@ class CustomRegistrationView(RegistrationView): return None @staticmethod - def set_permission(user: User) -> None: + def set_permission(user: get_user_model()) -> None: """ Функция дает разрешение на просмотр страница администратора, если пользователь имеет роль admin. @@ -142,7 +143,7 @@ class CustomRegistrationView(RegistrationView): ) 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-адрес страницы, куда нужно перейти после успешной/не успешной регистрации. Используется самой django-registration.