from rest_framework import generics from rest_framework.request import Request as DRFRequest from typing import cast from routes.models import Route from .serializers import SearchRouteSerializer from api.utils.pagination import StandardResultsSetPagination from rest_framework.exceptions import ValidationError from routes.constants.routeChoices import owner_type_choices from django.utils import timezone from django.db.models import Q from urllib.parse import unquote from api.utils.cityVariator import get_city_variations from django.db.models.functions import Coalesce class SearchRouteListView(generics.ListAPIView): serializer_class = SearchRouteSerializer pagination_class = StandardResultsSetPagination def get_queryset(self): request = cast(DRFRequest, self.request) owner_type = self.kwargs.get('owner_type') # получаем маршрут из URL и разбиваем его на города route = self.kwargs.get('route', '') if route: # декодируем URL дважды, так как он может быть дважды закодирован route = unquote(unquote(route)) try: from_city, to_city = route.split('-', 1) except ValueError: from_city = to_city = None else: # если маршрут не указан в URL, берем из query params и декодируем from_city = request.query_params.get('from', '') to_city = request.query_params.get('to', '') # декодируем значения, если они закодированы if from_city: try: # пробуем декодировать дважды для случая двойного кодирования from_city = unquote(from_city) if '%' in from_city: # если после первого декодирования остались %-коды from_city = unquote(from_city) except Exception as e: print(f"Error decoding from_city: {e}") if to_city: try: # пробуем декодировать дважды для случая двойного кодирования to_city = unquote(to_city) if '%' in to_city: # если после первого декодирования остались %-коды to_city = unquote(to_city) except Exception as e: print(f"Error decoding to_city: {e}") valid_types = [choice[0] for choice in owner_type_choices] current_time = timezone.now() if not owner_type or owner_type not in valid_types: raise ValidationError("Invalid or missing owner_type. Must be either 'customer' or 'mover'") # базовый фильтр по типу владельца и актуальности queryset = Route.objects.all() # Применяем базовые фильтры queryset = queryset.filter(owner_type=owner_type, status='actual') # фильтруем по городам если они указаны if from_city: # Получаем варианты написания для поиска from_city_variations = get_city_variations(from_city) # Используем Q objects для поиска по обоим полям from_city_filter = ( Q(from_city__name__in=from_city_variations) | Q(from_city__russian_name__in=from_city_variations) ) queryset = queryset.filter(from_city_filter) if to_city: # получаем варианты написания для поиска to_city_variations = get_city_variations(to_city) # используем Q objects для поиска по обоим полям to_city_filter = ( Q(to_city__name__in=to_city_variations) | Q(to_city__russian_name__in=to_city_variations) ) queryset = queryset.filter(to_city_filter) # фильтруем по времени в зависимости от типа if owner_type == 'mover': queryset = queryset.filter(departure_DT__gt=current_time) else: # customer queryset = queryset.filter(arrival_DT__gt=current_time) # премиум мембершип -- добавляем аннотацию и сортируем по последнему поднятию или дате создания queryset = queryset.annotate( sort_time=Coalesce('rising_DT', 'created_at') ).order_by('-sort_time') return queryset