display from_city and to_city on ui

This commit is contained in:
2025-05-27 13:07:23 +03:00
parent e0b705efa6
commit 7915221fc4
5 changed files with 72 additions and 63 deletions

View File

@@ -1,5 +1,5 @@
from rest_framework import serializers
from routes.models import Route, Country
from routes.models import Route, Country, City
from api.main.serializers import RouteSerializer
class SearchRouteSerializer(RouteSerializer):
@@ -7,8 +7,10 @@ class SearchRouteSerializer(RouteSerializer):
username = serializers.SerializerMethodField()
owner_type = serializers.CharField()
from_city_name = serializers.SerializerMethodField('get_start_point')
from_city_russian_name = serializers.SerializerMethodField()
from_country_name = serializers.SerializerMethodField('get_country_from')
to_city_name = serializers.SerializerMethodField('get_end_point')
to_city_russian_name = serializers.SerializerMethodField()
to_country_name = serializers.SerializerMethodField('get_country_to')
formatted_cargo_type = serializers.SerializerMethodField('get_cargo_type')
formatted_transport = serializers.SerializerMethodField('get_moving_type')
@@ -23,11 +25,12 @@ class SearchRouteSerializer(RouteSerializer):
class Meta:
model = Route
fields = (
'id', 'username', 'owner_type', 'from_city_name', 'from_country_name',
'to_city_name', 'to_country_name', 'formatted_cargo_type',
'formatted_transport', 'type_transport', 'userImg', 'comment',
'formatted_departure', 'formatted_arrival', 'country_from_icon',
'country_to_icon'
'id', 'username', 'owner_type',
'from_city_name', 'from_city_russian_name', 'from_country_name',
'to_city_name', 'to_city_russian_name', 'to_country_name',
'formatted_cargo_type', 'formatted_transport', 'type_transport',
'userImg', 'comment', 'formatted_departure', 'formatted_arrival',
'country_from_icon', 'country_to_icon'
)
def get_username(self, obj):
@@ -79,3 +82,17 @@ class SearchRouteSerializer(RouteSerializer):
def get_moving_type(self, obj):
return self.get_formatted_transport(obj)
def get_from_city_russian_name(self, obj):
try:
city = City.objects.get(id=obj.from_city_id)
return city.russian_name or city.name
except City.DoesNotExist:
return None
def get_to_city_russian_name(self, obj):
try:
city = City.objects.get(id=obj.to_city_id)
return city.russian_name or city.name
except City.DoesNotExist:
return None

View File

@@ -8,35 +8,8 @@ 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)
from api.utils.cityVariator import get_city_variations
class SearchRouteListView(generics.ListAPIView):
serializer_class = SearchRouteSerializer
@@ -48,15 +21,12 @@ class SearchRouteListView(generics.ListAPIView):
# получаем маршрут из 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:
@@ -82,8 +52,7 @@ class SearchRouteListView(generics.ListAPIView):
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()
@@ -99,10 +68,8 @@ class SearchRouteListView(generics.ListAPIView):
# фильтруем по городам если они указаны
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) |
@@ -111,10 +78,8 @@ class SearchRouteListView(generics.ListAPIView):
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) |
@@ -128,4 +93,5 @@ class SearchRouteListView(generics.ListAPIView):
else: # customer
queryset = queryset.filter(arrival_DT__gt=current_time)
# print(queryset)
return queryset.order_by('-arrival_DT')

View File

@@ -0,0 +1,28 @@
import cyrtranslit
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)

View File

@@ -68,9 +68,11 @@ export default async function SearchPage(props: RouteSearchPageProps) {
return (
<div className="container mx-auto p-4">
<h1 className="mb-4 text-2xl font-bold">
{category === 'mover'
? `Поиск перевозчика по маршруту : ${fromCity}${toCity}`
: `Поиск посылки по маршруту : ${fromCity} ${toCity}`}
{results.length > 0
? category === 'mover'
? `Поиск перевозчика по маршруту ${results[0].from_city_russian_name} - ${results[0].to_city_russian_name}`
: `Поиск посылки по маршруту ${results[0].from_city_russian_name} - ${results[0].to_city_russian_name}`
: 'Результаты не найдены'}
</h1>
<Suspense fallback={<div>Загрузка результатов...</div>}>

View File

@@ -40,20 +40,10 @@ export default function AddressSelector() {
.replace(/\s+/g, '-')
}
const validateInputs = () => {
if (!fromAddress.trim()) {
throw new Error('Укажите город отправления')
}
if (!toAddress.trim()) {
throw new Error('Укажите город назначения')
}
if (fromAddress.trim() === toAddress.trim()) {
throw new Error('Города отправления и назначения должны различаться')
}
}
const getSearchUrl = async (category: 'mover' | 'customer') => {
validateInputs()
if (!fromAddress.trim() || !toAddress.trim()) {
return `/search/${category}`
}
const [fromCity, toCity] = await Promise.all([getCityName(fromAddress), getCityName(toAddress)])
@@ -65,6 +55,12 @@ export default function AddressSelector() {
const handleSearch = async (category: 'mover' | 'customer') => {
setError(null)
if (!fromAddress.trim() || !toAddress.trim()) {
router.push(`/search/${category}`)
return
}
setIsLoading(true)
try {
@@ -82,8 +78,8 @@ export default function AddressSelector() {
<div className="flex flex-col gap-4 sm:flex-row sm:items-end sm:gap-3">
<div className="w-full min-w-0 sm:flex-[3] sm:px-1">
<TextInput
placeholder="Минск, Беларусь"
tooltip="Укажите пункт (Город/Страна), откуда необходимо забрать посылку."
placeholder="Минск"
tooltip="Укажите пункт (Город), откуда необходимо забрать посылку."
label="Забрать посылку из"
value={fromAddress}
handleChange={e => {
@@ -97,9 +93,9 @@ export default function AddressSelector() {
</div>
<div className="w-full min-w-0 sm:flex-[3] sm:px-1">
<TextInput
placeholder="Москва, Россия"
placeholder="Москва"
label="Доставить посылку в"
tooltip="Укажите пункт (Город/Страна), куда необходимо доставить посылку."
tooltip="Укажите пункт (Город), куда необходимо доставить посылку."
value={toAddress}
handleChange={e => {
setError(null)