remove bugs from path generation

This commit is contained in:
ilia 2023-05-27 22:32:52 +03:00
parent 8f80e65261
commit 1df35a5e4c
9 changed files with 742 additions and 305 deletions

270
passfinder/city.txt Normal file
View File

@ -0,0 +1,270 @@
Москва
Санкт-Петербург
Новосибирск
Екатеринбург
Нижний Новгород
Самара
Омск
Казань
Челябинск
Ростов-на-Дону
Уфа
Волгоград
Пермь
Красноярск
Воронеж
Саратов
Краснодар
Тольятти
Ижевск
Ульяновск
Барнаул
Владивосток
Ярославль
Иркутск
Тюмень
Махачкала
Хабаровск
Оренбург
Новокузнецк
Кемерово
Рязань
Томск
Астрахань
Пенза
Набережные Челны
Липецк
Тула
Киров
Чебоксары
Калининград
Брянск
Курск
Иваново
Магнитогорск
Улан-Удэ
Тверь
Ставрополь
Нижний Тагил
Белгород
Архангельск
Владимир
Сочи
Курган
Смоленск
Калуга
Чита
Ор
л
Волжский
Череповец
Владикавказ
Мурманск
Сургут
Вологда
Саранск
Тамбов
Стерлитамак
Грозный
Якутск
Кострома
Комсомольск-на-Амуре
Петрозаводск
Таганрог
Нижневартовск
Йошкар-Ола
Братск
Новороссийск
Дзержинск
Шахты
Нальчик
Орск
Сыктывкар
Нижнекамск
Ангарск
Старый Оскол
Великий Новгород
Балашиха
Благовещенск
Прокопьевск
Бийск
Химки
Псков
Энгельс
Рыбинск
Балаково
Северодвинск
Армавир
Подольск
Корол
в
Южно-Сахалинск
Петропавловск-Камчатский
Сызрань
Норильск
Златоуст
Каменск-Уральский
Мытищи
Люберцы
Волгодонск
Новочеркасск
Абакан
Находка
Уссурийск
Березники
Салават
Электросталь
Миасс
Первоуральск
Рубцовск
Альметьевск
Ковров
Коломна
Майкоп
Пятигорск
Одинцово
Колпино
Копейск
Хасавюрт
Новомосковск
Кисловодск
Серпухов
Новочебоксарск

View File

@ -1,226 +1,47 @@
city_in_hotels = ['Абзаково', city_in_hotels = {'Астрахань',
'Абрамовка',
'Абрау-Дюрсо',
'Адлер',
'Азов',
'Аксай',
'Альметьевск',
'Анапа',
'Андрианово',
'Арамиль',
'Арзамас',
'Арнеево',
'Архипо-Осиповка',
'Бабкино',
'Базы отдыха ВТО',
'Балашиха', 'Балашиха',
'Батайск', 'Березники',
'Беличье', 'Благовещенск',
'Белореченск', 'Владикавказ',
'Бердск', 'Волгоград',
'Бердяш', 'Волгодонск',
'Березовка', 'Волжский',
'Бжид', 'Воронеж',
'Битца', 'Грозный',
'Благовещенская',
'Болтино',
'Большой Сочи',
'Бор, Нижегородская область',
'Борисово',
'Борносово',
'Будённовск',
'Вардане',
'Васильево, Ленинградская область',
'Васкелово',
'Вербилки',
'Веселовка, Краснодарский край',
'Видное',
'Витязево',
'Владимировка',
'Внуково',
'Воскресенск',
'Вотря',
'Всеволожск',
'Всходы',
'Выборг',
'Вырубово',
'Гатчина',
'Гвардейское',
'Геленджик',
'Голиково',
'Головинка Краснодарский край',
'Голубицкая',
'Горки',
'Городец',
'Горячий ключ',
'Григорчиково',
'Гуамка',
'Д/О Авангард',
'Дагомыс',
'Джемете',
'Джубга',
'Дзержинск', 'Дзержинск',
'Дивеево',
'Дивногорье, Краснодарский край',
'Дивноморское',
'Дмитров',
'Домодедово',
'Дракино',
'Дранишники',
'Дубечино',
'Егорьевск',
'Ейск',
'Екатеринбург', 'Екатеринбург',
'Ершово', 'Златоуст',
'Ессентуки', 'Ижевск',
'Железноводск', 'Каменск-Уральский',
'Жуковский', 'Киров',
'За Родину',
'Звенигород',
'Зеленая поляна',
'Ивантеевка',
'Ильичево',
'Иннолово ',
'Иноземцево',
'Исаково',
'Истра',
'Кабардинка',
'Казань',
'Каменск-Шахтинский',
'Каневская',
'Кингисепп',
'Кисловодск', 'Кисловодск',
'Клин', 'Колпино',
'Коломна', 'Комсомольск-на-Амуре',
'Коробицыно', 'Копейск',
'Королев', 'Кострома',
'Косулино', 'Красноярск',
'Котельники', 'Курган',
'Красная Горка',
'Красная Поляна',
'Красногорск',
'Краснодар',
'Красный Колос',
'Кудряшовский',
'Курово',
'Кусимовского Рудника',
'Кучугуры',
'Лабинск',
'Лазаревское',
'Лермонтово',
'Лесной городок',
'Лодейное Поле',
'Лоо',
'Лосево',
'Люберцы', 'Люберцы',
'Магнитогорск', 'Магнитогорск',
'Малые Решники', 'Махачкала',
'Марьино, Ленинградская область',
'Маяковского',
'Мезмай',
'Мещерино',
'Миасс',
'Минеральные Воды',
'Мистолово',
'Мишуткино',
'Можайск',
'Москва', 'Москва',
'Мостовской',
'Мытищи', 'Мытищи',
'Набережные Челны', 'Нальчик',
'Наро-Фоминск', 'Нижнекамск',
'Нарынка',
'Небуг',
'Нестерово',
'Нижний Новгород',
'Нижний Тагил', 'Нижний Тагил',
'Новая', 'Новочебоксарск',
'Новоабзаково',
'Нововолково',
'Новомихайловский',
'Новороссийск',
'Новосибирск',
'Новочеркасск',
'Новый путь',
'Ногинск',
'Нурлат',
'Овсяники',
'Одинцово', 'Одинцово',
'Озеры', 'Омск',
'Оксино', 'Оренбург',
'Октябрьский, Московская область', 'Орск',
'Ольгинка', 'Пермь',
'Остров, Московская область',
'Павловск',
'Падиково',
'Пересвет',
'Платформа 69-й километр, Сосновское сельское поселение',
'Подольск', 'Подольск',
'Подпорожье',
'Полтавская',
'Приморско-Ахтарск',
'Приозерск',
'Прохорово',
'Пушкино',
'Пятигорск', 'Пятигорск',
'Раменское', 'Салават',
'Реутов',
'Рождествено',
'Роза Хутор',
'Ростов-на-Дону',
'Рощино',
'Руза',
'Санкт-Петербург', 'Санкт-Петербург',
'Светлое', 'Томск',
'Светлый',
'Свирица',
'Сергиев Посад',
'Серпухов',
'Симагино',
'Сириус',
'Скоково',
'Снегири',
'Солнечногорск',
'Солохаул',
'Сосново',
'Сосновый Бор, Ленинградская область',
'Сосновый Бор, Московская область',
'Софрино',
'Сочи',
'Ставрополь',
'Станица Динская',
'Станица Должанская',
'Старая Руза',
'Степаньково',
'Суйда',
'Сукко',
'Супсех',
'Таганрог',
'Тарасово',
'Тимашевск',
'Тихвин',
'Тихорецк',
'Тобольск',
'Туапсе',
'Тучково',
'Тюмень', 'Тюмень',
'Увильды ',
'Углегорский',
'Удельная',
'Усть-Койсуг',
'Усть-Лабинск',
'Уфа', 'Уфа',
'Ушаки', 'Хасавюрт',
'Фрязино',
'Хадыженск',
'Химки', 'Химки',
'Хоста', 'Энгельс'}
'Чебаркуль',
'Челябинск',
'Чехов, Сахалинская область',
'Чудская',
'Шахты',
'Широкая балка',
'Щёлково',
'Эсто-Садок',
'Якорная щель']

View File

@ -65,6 +65,49 @@ class RouteInputSerializer(serializers.Serializer):
min_length=24, max_length=24, required=False, allow_blank=True, allow_null=True min_length=24, max_length=24, required=False, allow_blank=True, allow_null=True
) )
movement = serializers.ChoiceField(['walk', 'bike', 'scooter', 'auto'], required=False, allow_blank=True) movement = serializers.ChoiceField(['walk', 'bike', 'scooter', 'auto'], required=False, allow_blank=True)
stars = serializers.ListField(
child=serializers.ChoiceField([1, 2, 3, 4, 5]),
required=False,
allow_empty=True,
allow_null=True
)
what_to_see = serializers.ListField(
child=serializers.ChoiceField(
[
'attractions',
'museum',
'movie',
'concert',
'artwork',
'plays',
'shop',
'gallery',
'theme_park',
'viewpoint',
'zoo'
]
),
required=False,
allow_empty=True,
allow_null=True
)
where_stay = serializers.ListField(
child=serializers.ChoiceField([
'hotel', 'apartment', 'hostel'
]),
required=False,
allow_empty=True,
allow_null=True
)
where_eat = serializers.ListField(
child=serializers.ChoiceField(['restaurant', 'bar', 'cafe']),
required=False,
allow_empty=True,
allow_null=True
)
with_kids = serializers.BooleanField(required=False, allow_null=True)
with_animals = serializers.BooleanField(required=False, allow_null=True)
class CitySerializer(serializers.ModelSerializer): class CitySerializer(serializers.ModelSerializer):
class Meta: class Meta:

View File

@ -75,6 +75,40 @@ def post(self, request):
movement = data['movement'] movement = data['movement']
except KeyError: except KeyError:
movement = 'walk' movement = 'walk'
hotel_stars = data['stars']
if hotel_stars is None:
hotel_stars = []
hotel_type = data['where_stay']
if hotel_type is None:
hotel_type = ['hotel']
where_eat = data['where_eat']
if where_eat is None:
where_eat = ['restaurant', 'bar', 'cafe']
what_to_see = data['what_to_see']
if what_to_see is None:
what_to_see = [
'attractions',
'museum',
'movie',
'concert',
'artwork',
'plays',
'shop',
'gallery',
'theme_park',
'viewpoint',
'zoo'
]
if 'hotel' not in hotel_type:
hotel_stars = []
region = None region = None
if city_id: if city_id:
@ -95,7 +129,17 @@ def post(self, request):
print(request.user, region, start_date, end_date) print(request.user, region, start_date, end_date)
tour = generate_tour(request.user, region, start_date, end_date, movement_mapping[movement]) tour = generate_tour(
request.user,
region,
start_date,
end_date,
avg_velocity=movement_mapping[movement],
stars=hotel_stars,
hotel_type=hotel_type,
where_eat=where_eat,
what_to_see=what_to_see
)
print(len(tour[1])) print(len(tour[1]))
return Response(data=tour[0]) return Response(data=tour[0])
@ -109,9 +153,7 @@ class ListRegionApiView(ListAPIView):
class ListCityApiView(ListAPIView): class ListCityApiView(ListAPIView):
serializer_class = CitySerializer serializer_class = CitySerializer
queryset = ( queryset = (
City.objects.annotate(points_num=Count("points")) City.objects.annotate(points_count=Count('points')).filter(title__in=city_in_hotels).filter(points_count__gt=200).order_by('title')
.filter(points_num__gte=100)
.order_by("title")
) )

View File

@ -1,5 +1,5 @@
from rest_framework import serializers from rest_framework import serializers
from passfinder.events.api.serializers import EventSerializer, HotelSerializer from passfinder.events.api.serializers import EventSerializer, HotelSerializer, ObjectRouteSerializer
class TinderProceedSerializer(serializers.Serializer): class TinderProceedSerializer(serializers.Serializer):
@ -49,3 +49,23 @@ class DailySelectionSerializer(serializers.Serializer):
) )
) )
event = EventSerializer() event = EventSerializer()
class StarSelectionSerializer(serializers.Serializer):
stars = serializers.ListField(child=serializers.IntegerField(), write_only=True)
class CategorySelectionSerializer(serializers.Serializer):
categories = serializers.ListField(child=serializers.ChoiceField(
['attractions', 'museum', 'movie', 'concert', 'artwork', 'plays', 'shop', 'gallery', 'theme_park', 'viewpoint', 'zoo']
))
class RecomendationNode(serializers.Serializer):
category = serializers.CharField()
events = serializers.ListField(child=ObjectRouteSerializer())
class SelfRecomendationSerializer(serializers.Serializer):
recomendations = serializers.ListField(child=RecomendationNode(), write_only=True)

View File

@ -49,32 +49,13 @@ class PersonalRecommendation(viewsets.GenericViewSet):
model = Event model = Event
queryset = Event.objects.all() queryset = Event.objects.all()
@action(methods=['GET'], detail=False)
def plays(self, request, *args, **kwargs):
recs = get_personal_plays_recommendation(request.user)
ans = []
for rec in recs:
ans.append(EventSerializer(rec[1]).data)
return Response(ans, 200)
@action(methods=['GET'], detail=False)
def concerts(self, request, *args, **kwargs):
recs = get_personal_concerts_recommendation(request.user)
ans = []
for rec in recs:
ans.append(EventSerializer(rec[1]).data)
return Response(ans, 200)
@action(methods=['GET'], detail=False)
def movies(self, request, *args, **kwargs):
recs = get_personal_movies_recommendation(request.user)
ans = []
for rec in recs:
ans.append(EventSerializer(rec[1]).data)
return Response(ans, 200)
@action(methods=['GET'], detail=False, serializer_class=SelfRecomendationSerializer)
def recommendations(self, request, *args, **kwargs):
return Response(
data=get_personal_recomendations(request.user),
status=200
)
@action(methods=['GET'], detail=True) @action(methods=['GET'], detail=True)
def get_nearest_user_distance(self, request, pk, *args, **kwargs): def get_nearest_user_distance(self, request, pk, *args, **kwargs):
@ -166,3 +147,17 @@ def add_to_favorites(self, request, pk, *args, **kwargs):
pref.save() pref.save()
return Response(status=200) return Response(status=200)
@action(methods=['POST'], detail=False, serializer_class=StarSelectionSerializer)
def set_hotel_stars(self, request, *args, **kwargs):
up, _ = UserPreferences.objects.get_or_create(user=request.user)
up.preferred_stars = request.data['stars']
up.save()
return Response(status=200)
@action(methods=['POST'], detail=False, serializer_class=CategorySelectionSerializer)
def set_categories(self, request, *args, **kwargs):
up, _ = UserPreferences.objects.get_or_create(user=request.user)
up.preferred_categories = request.data['categories']
up.save()
return Response(status=200)

View File

@ -0,0 +1,31 @@
# Generated by Django 4.2.1 on 2023-05-27 10:21
import django.contrib.postgres.fields
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("recomendations", "0007_nearesteventtorestaurant"),
]
operations = [
migrations.AddField(
model_name="userpreferences",
name="preferred_categories",
field=django.contrib.postgres.fields.ArrayField(
base_field=models.CharField(max_length=100),
blank=True,
null=True,
size=None,
),
),
migrations.AddField(
model_name="userpreferences",
name="preferred_stars",
field=django.contrib.postgres.fields.ArrayField(
base_field=models.IntegerField(), blank=True, null=True, size=None
),
),
]

View File

@ -1,6 +1,7 @@
from django.db import models from django.db import models
from passfinder.users.models import User from passfinder.users.models import User
from passfinder.events.models import Event, Hotel, Restaurant from passfinder.events.models import Event, Hotel, Restaurant
from django.contrib.postgres.fields import ArrayField
class UserPreferences(models.Model): class UserPreferences(models.Model):
@ -21,6 +22,9 @@ class UserPreferences(models.Model):
prefferred_museums = models.ManyToManyField(Event, related_name='preffered_users_museums') prefferred_museums = models.ManyToManyField(Event, related_name='preffered_users_museums')
unprefferred_museums = models.ManyToManyField(Event, related_name='unpreffered_users_museums') unprefferred_museums = models.ManyToManyField(Event, related_name='unpreffered_users_museums')
preferred_categories = ArrayField(base_field=models.CharField(max_length=100), null=True, blank=True)
preferred_stars = ArrayField(base_field=models.IntegerField(), null=True, blank=True)
class NearestEvent(models.Model): class NearestEvent(models.Model):

View File

@ -266,8 +266,7 @@ def dist_func(event1: Event, event2: Event):
return dist return dist
except: except:
return 1000000 return 1000000
#return (event1.lon - event2.lon) ** 2 + (event1.lat - event2.lat) ** 2 return (event1.lon - event2.lon) ** 2 + (event1.lat - event2.lat) ** 2
# return (event1.lon - event2.lon) ** 2 + (event1.lat - event2.lat) ** 2
def generate_nearest(): def generate_nearest():
@ -349,6 +348,19 @@ def match_points():
print(i) print(i)
def match_restaurants():
regions = list(City.objects.all())
for i, point in enumerate(Restaurant.objects.all()):
s_regions = list(sorted(regions.copy(), key=lambda x: dist_func(point, x)))
point.city = s_regions[0]
point.save()
if i % 10 == 0:
print(i)
def calculate_mean_metric( def calculate_mean_metric(
favorite_events: Iterable[Event], favorite_events: Iterable[Event],
target_event: Event, target_event: Event,
@ -393,21 +405,50 @@ def calculate_favorite_metric(event: Event, user: User):
def get_exponential_koef(time: timedelta): def get_exponential_koef(time: timedelta):
time = time.seconds time = time.seconds
if time < 60 * 10: if time < 60 * 10:
return 1 return 2
if time < 60 * 20: if time < 60 * 20:
return 10 return 5
if time < 60 * 30: if time < 60 * 30:
return 1000 return 10
if time < 60 * 40: if time < 60 * 40:
return 100000 return 20
return int(1e10) return int(1e10)
def get_category_similarity_coef(event, user):
up, _ = UserPreferences.objects.get_or_create(user=user)
cat = up.preferred_categories
if event.type in cat:
return 0.7
else:
return 1.2
def get_nearest_favorite( def get_nearest_favorite(
events: Iterable[Event], user: User, base_event: Event, exclude_events: Iterable[Event] = [], velocity=3.0, top_k=1 events: Iterable[Event],
user: User,
base_event: Event,
exclude_events: Iterable[Event] = [],
velocity=3.0,
top_k=1
): ):
sorted_events = list(sorted(events, key=lambda event: calculate_favorite_metric(event, user) * get_exponential_koef(time_func(dist_func(event, base_event), velocity)))) sorted_events = list(
sorted(
filter(lambda event: event not in exclude_events, events),
key=lambda event:
calculate_favorite_metric(event, user) *
get_exponential_koef(
time_func(
dist_func(
event, base_event
),
velocity
)
) *
get_category_similarity_coef(event, user)
)
)
if top_k == 1: if top_k == 1:
return sorted_events[0] return sorted_events[0]
@ -424,14 +465,6 @@ def time_func(km_distance: float, velocity: float):
return timedelta(minutes=(km_distance) / (velocity / 60)) return timedelta(minutes=(km_distance) / (velocity / 60))
def generate_route(point1: BasePoint, point2: BasePoint, velocity: float):
distance = dist_func(point1, point2)
time = time_func(distance, velocity)
def time_func(km_distance: float, velocity):
return timedelta(minutes=(km_distance) / (velocity / 60))
def generate_route(point1: BasePoint, point2: BasePoint, velocity): def generate_route(point1: BasePoint, point2: BasePoint, velocity):
distance = dist_func(point1, point2) distance = dist_func(point1, point2)
@ -470,14 +503,40 @@ def generate_multiple_tours(user: User, city: City, start_date: datetime.date, e
return pool.map(generate_tour, [(user, start_date, end_date, hotel) for hotel in hotels]) return pool.map(generate_tour, [(user, start_date, end_date, hotel) for hotel in hotels])
def generate_tour(user: User, city: City, start_date: datetime.date, end_date: datetime.date, avg_velocity=3.0): def generate_tour(
user: User,
city: City,
start_date: datetime.date,
end_date: datetime.date,
avg_velocity=3.0,
stars=[],
hotel_type=['hotel', 'hostel', 'apartment'],
where_eat=['restaurant', 'bar', 'cafe'],
what_to_see=['attractions', 'museum', 'movie', 'concert', 'artwork', 'plays', 'shop', 'gallery', 'theme_park', 'viewpoint', 'zoo']
):
UserPreferences.objects.get_or_create(user=user) UserPreferences.objects.get_or_create(user=user)
hotel = choice(list(Hotel.objects.filter(city=city)))
hotels_candidates = Hotel.objects.filter(city=city)
if len(hotels_candidates.filter(stars__in=stars)):
hotels_candidates = hotels_candidates.filter(stars__in=stars)
try:
hotel = choice(list(hotels_candidates))
except:
hotel = city
current_date = start_date current_date = start_date
paths, points, disallowed_rest = [], [], [] paths, points, disallowed_rest = [], [], []
while current_date < end_date: while current_date < end_date:
local_points, local_paths, local_disallowed_rest = generate_path(user, points, hotel, disallowed_rest, avg_velocity) local_points, local_paths, local_disallowed_rest = generate_path(
user,
points,
hotel,
disallowed_rest,
avg_velocity,
where_eat=where_eat,
what_to_see=what_to_see
)
points.extend(local_points) points.extend(local_points)
paths.append( paths.append(
{ {
@ -514,22 +573,56 @@ def nearest_distance_points(point: BasePoint, user: User, velocity: float=3.0):
def generate_path(user: User, disallowed_points: Iterable[BasePoint], hotel: Hotel, disallowed_rests: Iterable[Restaurant], avg_velocity: float): def generate_path(
# region_events = Event.objects.filter(region=region) user: User,
disallowed_points: Iterable[BasePoint],
#candidates = NearestHotel.objects.get(hotel=hotel).nearest_events.all() hotel: Hotel,
allowed_types = ['museum', 'attraction'] disallowed_rests: Iterable[Restaurant],
avg_velocity: float,
start_point = NearestRestaurantToHotel.objects.filter(hotel=hotel).first().restaurants.filter(~Q(oid__in=disallowed_rests)).first() where_eat=['restaurant', 'bar', 'cafe'],
disallowed_rests.append(start_point.oid) what_to_see=['attractions', 'museum', 'movie', 'concert', 'artwork', 'plays', 'shop', 'gallery', 'theme_park', 'viewpoint', 'zoo']
candidates = list(filter(lambda x: x not in disallowed_points, hotel.nearest_hotel_rel.all().first().nearest_events.filter(type__in=allowed_types))) ):
#candidates = list(filter(lambda x: x.type in allowed_types, map(lambda x: x.event, start_point.nearestrestauranttoevent_set.all()[0:100]))) allowed_types = [
points = [start_point] 'museum',
path = [ 'attraction',
generate_hotel(hotel), 'artwork',
generate_route(start_point, hotel, avg_velocity), 'shop',
generate_restaurant(start_point) 'gallery',
'theme_park',
'zoo',
'other',
'viewpoint'
] ]
if len(set(allowed_types) & set(what_to_see)) == 0:
allowed_types = what_to_see
else:
allowed_types = list(set(allowed_types) & set(what_to_see))
print(allowed_types, hotel)
if isinstance(hotel, City):
start_points_candidate = Restaurant.objects.filter(city=hotel).filter(~Q(oid__in=disallowed_rests))
else:
start_points_candidate = NearestRestaurantToHotel.objects.filter(hotel=hotel).first().restaurants.filter(~Q(oid__in=disallowed_rests))
if len(start_points_candidate.filter(type__in=where_eat)):
start_points_candidate = start_points_candidate.filter(type__in=where_eat)
start_point = start_points_candidate[0]
disallowed_rests.append(start_point.oid)
candidates = NearestEventToRestaurant.objects.get(restaurant=start_point).events.all().filter(type__in=allowed_types)
points = [start_point]
if isinstance(hotel, Hotel):
path = [
generate_hotel(hotel),
generate_route(start_point, hotel, avg_velocity),
generate_restaurant(start_point)
]
else:
path = [
generate_restaurant(start_point)
]
start_time = datetime.combine(datetime.now(), time(hour=10)) start_time = datetime.combine(datetime.now(), time(hour=10))
@ -537,32 +630,53 @@ def generate_path(user: User, disallowed_points: Iterable[BasePoint], hotel: Hot
while start_time.hour < 22 and start_time.day == datetime.now().day: while start_time.hour < 22 and start_time.day == datetime.now().day:
if (start_time.hour > 14 and how_many_eat == 1) or (start_time.hour > 20 and how_many_eat == 2): if (start_time.hour > 14 and how_many_eat == 1) or (start_time.hour > 20 and how_many_eat == 2):
point = NearestRestaurantToEvent.objects.filter(event=points[-1]).first().restaurants.filter(~Q(oid__in=disallowed_rests))[0] print(points, start_time)
disallowed_rests.append(point.oid) try:
points.append(point) point_candidates = NearestRestaurantToEvent.objects.filter(event=points[-1]).first().restaurants.filter(~Q(oid__in=disallowed_rests))
# Переделать - сделать еще один прекалк на рестораны с точками if len(point_candidates.filter(type__in=where_eat)):
candidates = NearestEventToRestaurant.objects.get(restaurant=point).events.all().filter(type__in=allowed_types) point_candidates = point_candidates.filter(type__in=where_eat)
if len(candidates) < 10: point = point_candidates[0]
candidates = NearestEventToRestaurant.objects.get(restaurant=point).events.all()
disallowed_rests.append(point.oid)
points.append(point)
path.append(generate_restaurant(points[-1]))
start_time += timedelta(seconds=path[-1]['time']) candidates = NearestEventToRestaurant.objects.get(restaurant=point).events.all().filter(type__in=allowed_types)
how_many_eat += 1 if len(candidates) < 2:
continue candidates = NearestEventToRestaurant.objects.get(restaurant=point).events.all()
path.append(generate_restaurant(points[-1]))
start_time += timedelta(seconds=path[-1]['time'])
how_many_eat += 1
continue
except:
return points, path, disallowed_rests
if start_time.hour > 17: if start_time.hour > 17:
allowed_types = ['play', 'concert', 'movie'] allowed_types = [
'play',
'concert',
'movie',
'shop',
'gallery',
'theme_park',
'viewpoint'
]
if len(set(allowed_types) & set(what_to_see)) == 0:
allowed_types = what_to_see
else:
allowed_types = list(set(allowed_types) & set(what_to_see))
if candidates is None: if candidates is None:
candidates = NearestEvent.objects.get(event=points[-1]).nearest.filter(type__in=allowed_types) candidates = NearestEvent.objects.get(event=points[-1]).nearest.filter(type__in=allowed_types)
if len(candidates) < 10: if len(candidates) < 2:
candidates = NearestEvent.objects.get(event=points[-1]).nearest.all() candidates = NearestEvent.objects.get(event=points[-1]).nearest.all()
try: try:
points.append(get_nearest_favorite(candidates, user, points[-1], points + disallowed_points)) points.append(get_nearest_favorite(candidates, user, points[-1], points + disallowed_points))
except AttributeError: except:
points.append(get_nearest_favorite(candidates, user, points[-1], points)) points.append(get_nearest_favorite(candidates, user, points[-1], points))
transition_route = generate_route(points[-1], points[-2], avg_velocity) transition_route = generate_route(points[-1], points[-2], avg_velocity)
@ -576,15 +690,6 @@ def generate_path(user: User, disallowed_points: Iterable[BasePoint], hotel: Hot
return points, path, disallowed_rests return points, path, disallowed_rests
def calculate_distance(sample1: Event, samples: Iterable[Event], model: AnnoyIndex, rev_mapping):
metrics = []
for sample in samples:
metrics.append(model.get_distance(rev_mapping[sample1.oid], rev_mapping[sample.oid]))
return sum(metrics) / len(metrics)
def calculate_distance( def calculate_distance(
sample1: Event, samples: Iterable[Event], model: AnnoyIndex, rev_mapping sample1: Event, samples: Iterable[Event], model: AnnoyIndex, rev_mapping
): ):
@ -627,11 +732,6 @@ def get_onboarding_hotels(stars=Iterable[int]):
def generate_points_path(user: User, points: Iterable[Event], velocity=3.0): def generate_points_path(user: User, points: Iterable[Event], velocity=3.0):
"""
Дописать
1) генерить маршруты от многих точек (не только по 2) (salesman problem)
2) Если в маршруте до 7 точек - добавлять похожие пока не станет 7 точек
"""
if len(points) < 7: if len(points) < 7:
candidates = NearestEvent.objects.get(event=points[0]).nearest.all() candidates = NearestEvent.objects.get(event=points[0]).nearest.all()
points.extend(list(get_nearest_favorite(candidates, user, points[0], [], velocity, 7-len(points)))) points.extend(list(get_nearest_favorite(candidates, user, points[0], [], velocity, 7-len(points))))
@ -662,4 +762,115 @@ def generate_points_path(user: User, points: Iterable[Event], velocity=3.0):
]) ])
visited_points.append(pt) visited_points.append(pt)
return res return res
def flat_list(lst):
res = []
for i in lst:
res.extend(i)
return res
def range_candidates(candidates, user, favorite_events):
model_mappings = {
'attraction': [attracion_model, rev_attraction_mapping],
'museum': [mus_model, rev_mus_mapping],
'movie': [cinema_model, rev_cinema_mapping],
'concert': [concert_model, rev_concert_mapping],
'plays': [plays_model, rev_plays_mapping]
}
if candidates[0].type in ['attraction', 'museum', 'movie', 'concert', 'plays']:
candidates = sorted(
candidates,
key=lambda cand: calculate_mean_metric(
favorite_events,
cand,
*model_mappings[cand.type]
)
)
return candidates[0:10]
return sample(candidates, 10)
def get_personal_recomendations(user):
up, _ = UserPreferences.objects.get_or_create(user=user)
candidates_generate_strategy = {
'plays': [lambda pref: flat_list(
list(
map(
lambda cand: nearest_plays(
cand, 30
),
pref.preffered_plays.all()
)
),
), lambda pref: pref.preffered_plays.all()],
'movie': [lambda pref: flat_list(
list(
map(
lambda cand: nearest_movie(
cand, 30
),
pref.preffered_movies.all()
)
),
), lambda pref: pref.preffered_movies.all()],
'concert': [lambda pref: flat_list(
list(
map(
lambda cand: nearest_concert(
cand, 30
),
pref.preferred_concerts.all()
)
),
), lambda pref: pref.preferred_concerts.all()],
'attractions': [lambda pref: flat_list(
list(
map(
lambda cand: nearest_attraction(
cand, 30
),
pref.prefferred_attractions.all()
)
),
), lambda pref: pref.prefferred_attractions.all()],
'museum': [lambda pref: flat_list(
list(
map(
lambda cand: nearest_mus(
cand, 30
),
pref.prefferred_museums.all()
)
),
), lambda pref: pref.prefferred_museums.all()],
'shop': [lambda pref: sample(list(Event.objects.filter(type='shop')), 10), lambda x: []],
'gallery': [lambda pref: sample(list(Event.objects.filter(type='gallery')), 10), lambda x: []],
'theme_park': [lambda pref: sample(list(Event.objects.filter(type='theme_park')), 10), lambda x: []],
'viewpoint': [lambda pref: sample(list(Event.objects.filter(type='viewpoint')), 10), lambda x: []],
'zoo': [lambda pref: sample(list(Event.objects.filter(type='zoo')), 10), lambda x: []],
}
res = []
for category_candidate in up.preferred_categories:
candidates = candidates_generate_strategy[category_candidate][0](up)
ranged = range_candidates(
candidates,
user,
candidates_generate_strategy[category_candidate][1](up)
)
res.append(
{
'category': category_candidate,
'events': list(
map(
lambda x: ObjectRouteSerializer(x).data,
ranged
)
)
}
)
return res