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 import cyrtranslit from urllib.parse import unquote def get_city_variations(city_name: str) -> list[str]: """ Получает варианты написания города, включая транслитерацию """ variations = set() # добавляем оригинальное название и его варианты с разным регистром variations.add(city_name) variations.add(city_name.lower()) variations.add(city_name.title()) # пробуем добавить транслитерации try: # пробуем транслитерировать в обе стороны lat = cyrtranslit.to_latin(city_name, 'ru') cyr = cyrtranslit.to_cyrillic(city_name, 'ru') # добавляем варианты транслитерации с разным регистром for variant in [lat, cyr]: variations.add(variant) variations.add(variant.lower()) variations.add(variant.title()) except: pass return list(variations) 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', '') print(f"Raw route from URL: {route}") if route: # декодируем URL дважды, так как он может быть дважды закодирован route = unquote(unquote(route)) print(f"Decoded route: {route}") try: from_city, to_city = route.split('-', 1) print(f"Split cities - from: {from_city}, to: {to_city}") 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}") print(f"Query params - from: {from_city}, to: {to_city}") 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: print(f"Searching for from_city: {from_city}") # Получаем варианты написания для поиска from_city_variations = get_city_variations(from_city) print(f"From city variations: {from_city_variations}") # Используем 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: print(f"Searching for to_city: {to_city}") # получаем варианты написания для поиска to_city_variations = get_city_variations(to_city) print(f"To city variations: {to_city_variations}") # используем 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) return queryset.order_by('-arrival_DT')