""" Переиспользуемые миксины для представлений mainapp. Этот модуль содержит миксины для стандартизации общей логики в представлениях, включая проверку прав доступа, обработку координат и сообщений. """ # Standard library imports from datetime import datetime from typing import Optional, Tuple # Django imports from django.contrib import messages from django.contrib.auth.mixins import UserPassesTestMixin from django.contrib.gis.geos import Point class RoleRequiredMixin(UserPassesTestMixin): """ Mixin для проверки роли пользователя. Проверяет, что пользователь имеет одну из требуемых ролей для доступа к представлению. Attributes: required_roles (list): Список допустимых ролей для доступа. По умолчанию ['admin', 'moderator']. Example: class MyView(RoleRequiredMixin, View): required_roles = ['admin', 'moderator'] def get(self, request): # Только пользователи с ролью admin или moderator могут получить доступ return render(request, 'template.html') """ required_roles = ["admin", "moderator"] def test_func(self) -> bool: """ Проверяет, имеет ли пользователь требуемую роль. Returns: bool: True если пользователь имеет одну из требуемых ролей, иначе False. """ if not self.request.user.is_authenticated: return False if not hasattr(self.request.user, "customuser"): return False return self.request.user.customuser.role in self.required_roles class CoordinateProcessingMixin: """ Mixin для обработки координат из POST данных форм. Предоставляет методы для извлечения и обработки координат различных типов (геолокация, кубсат, оперативники) из POST запроса и применения их к объекту Geo. Example: class MyFormView(CoordinateProcessingMixin, FormView): def form_valid(self, form): geo_instance = Geo() self.process_coordinates(geo_instance) geo_instance.save() return super().form_valid(form) """ def process_coordinates(self, geo_instance, prefix: str = "geo") -> None: """ Обрабатывает координаты из POST данных и применяет их к объекту Geo. Извлекает координаты геолокации, кубсата и оперативников из POST запроса и устанавливает соответствующие поля объекта Geo. Args: geo_instance: Экземпляр модели Geo для обновления координат. prefix (str): Префикс для полей формы (по умолчанию 'geo'). Note: Метод ожидает следующие поля в request.POST: - geo_longitude, geo_latitude: координаты геолокации - kupsat_longitude, kupsat_latitude: координаты кубсата - valid_longitude, valid_latitude: координаты оперативников """ # Обрабатываем координаты геолокации geo_coords = self._extract_coordinates("geo") if geo_coords: geo_instance.coords = Point(geo_coords[0], geo_coords[1], srid=4326) # Обрабатываем координаты Кубсата kupsat_coords = self._extract_coordinates("kupsat") if kupsat_coords: geo_instance.coords_kupsat = Point( kupsat_coords[0], kupsat_coords[1], srid=4326 ) # Обрабатываем координаты оперативников valid_coords = self._extract_coordinates("valid") if valid_coords: geo_instance.coords_valid = Point( valid_coords[0], valid_coords[1], srid=4326 ) def _extract_coordinates(self, coord_type: str) -> Optional[Tuple[float, float]]: """ Извлекает координаты указанного типа из POST данных. Args: coord_type (str): Тип координат ('geo', 'kupsat', 'valid'). Returns: Optional[Tuple[float, float]]: Кортеж (longitude, latitude) или None, если координаты не найдены или невалидны. """ longitude_key = f"{coord_type}_longitude" latitude_key = f"{coord_type}_latitude" longitude = self.request.POST.get(longitude_key) latitude = self.request.POST.get(latitude_key) if longitude and latitude: try: return (float(longitude), float(latitude)) except (ValueError, TypeError): return None return None def process_timestamp(self, geo_instance) -> None: """ Обрабатывает дату и время из POST данных и применяет к объекту Geo. Args: geo_instance: Экземпляр модели Geo для обновления timestamp. Note: Метод ожидает следующие поля в request.POST: - timestamp_date: дата в формате YYYY-MM-DD - timestamp_time: время в формате HH:MM """ timestamp_date = self.request.POST.get("timestamp_date") timestamp_time = self.request.POST.get("timestamp_time") if timestamp_date and timestamp_time: try: naive_datetime = datetime.strptime( f"{timestamp_date} {timestamp_time}", "%Y-%m-%d %H:%M" ) geo_instance.timestamp = naive_datetime except ValueError: # Если формат даты/времени неверный, пропускаем pass class FormMessageMixin: """ Mixin для стандартизации сообщений об успехе и ошибках в формах. Автоматически добавляет сообщения пользователю при успешной или неуспешной обработке формы. Attributes: success_message (str): Сообщение при успешной обработке формы. error_message (str): Сообщение при ошибке обработки формы. Example: class MyFormView(FormMessageMixin, FormView): success_message = "Данные успешно сохранены!" error_message = "Ошибка при сохранении данных" def form_valid(self, form): # Автоматически добавит success_message return super().form_valid(form) """ success_message = "Операция выполнена успешно" error_message = "Произошла ошибка при обработке формы" def form_valid(self, form): """ Обрабатывает валидную форму и добавляет сообщение об успехе. Args: form: Валидная форма Django. Returns: HttpResponse: Результат обработки родительского метода form_valid. """ if self.success_message: messages.success(self.request, self.success_message) return super().form_valid(form) def form_invalid(self, form): """ Обрабатывает невалидную форму и добавляет сообщение об ошибке. Args: form: Невалидная форма Django. Returns: HttpResponse: Результат обработки родительского метода form_invalid. """ if self.error_message: messages.error(self.request, self.error_message) return super().form_invalid(form) def get_success_message(self) -> str: """ Возвращает сообщение об успехе. Может быть переопределен в подклассах для динамического формирования сообщения. Returns: str: Сообщение об успехе. """ return self.success_message def get_error_message(self) -> str: """ Возвращает сообщение об ошибке. Может быть переопределен в подклассах для динамического формирования сообщения. Returns: str: Сообщение об ошибке. """ return self.error_message