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
)
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 Meta:

View File

@ -75,6 +75,40 @@ def post(self, request):
movement = data['movement']
except KeyError:
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
if city_id:
@ -95,7 +129,17 @@ def post(self, request):
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]))
return Response(data=tour[0])
@ -109,9 +153,7 @@ class ListRegionApiView(ListAPIView):
class ListCityApiView(ListAPIView):
serializer_class = CitySerializer
queryset = (
City.objects.annotate(points_num=Count("points"))
.filter(points_num__gte=100)
.order_by("title")
City.objects.annotate(points_count=Count('points')).filter(title__in=city_in_hotels).filter(points_count__gt=200).order_by('title')
)

View File

@ -1,5 +1,5 @@
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):
@ -49,3 +49,23 @@ class DailySelectionSerializer(serializers.Serializer):
)
)
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
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)
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()
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 passfinder.users.models import User
from passfinder.events.models import Event, Hotel, Restaurant
from django.contrib.postgres.fields import ArrayField
class UserPreferences(models.Model):
@ -21,6 +22,9 @@ class UserPreferences(models.Model):
prefferred_museums = models.ManyToManyField(Event, related_name='preffered_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):

View File

@ -266,8 +266,7 @@ def dist_func(event1: Event, event2: Event):
return dist
except:
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():
@ -349,6 +348,19 @@ def match_points():
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(
favorite_events: Iterable[Event],
target_event: Event,
@ -393,21 +405,50 @@ def calculate_favorite_metric(event: Event, user: User):
def get_exponential_koef(time: timedelta):
time = time.seconds
if time < 60 * 10:
return 1
return 2
if time < 60 * 20:
return 10
return 5
if time < 60 * 30:
return 1000
return 10
if time < 60 * 40:
return 100000
return 20
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(
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:
return sorted_events[0]
@ -424,14 +465,6 @@ def time_func(km_distance: float, velocity: float):
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):
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])
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)
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
paths, points, disallowed_rest = [], [], []
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)
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):
# region_events = Event.objects.filter(region=region)
#candidates = NearestHotel.objects.get(hotel=hotel).nearest_events.all()
allowed_types = ['museum', 'attraction']
start_point = NearestRestaurantToHotel.objects.filter(hotel=hotel).first().restaurants.filter(~Q(oid__in=disallowed_rests)).first()
disallowed_rests.append(start_point.oid)
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])))
points = [start_point]
path = [
generate_hotel(hotel),
generate_route(start_point, hotel, avg_velocity),
generate_restaurant(start_point)
def generate_path(
user: User,
disallowed_points: Iterable[BasePoint],
hotel: Hotel,
disallowed_rests: Iterable[Restaurant],
avg_velocity: float,
where_eat=['restaurant', 'bar', 'cafe'],
what_to_see=['attractions', 'museum', 'movie', 'concert', 'artwork', 'plays', 'shop', 'gallery', 'theme_park', 'viewpoint', 'zoo']
):
allowed_types = [
'museum',
'attraction',
'artwork',
'shop',
'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))
@ -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:
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]
disallowed_rests.append(point.oid)
points.append(point)
# Переделать - сделать еще один прекалк на рестораны с точками
candidates = NearestEventToRestaurant.objects.get(restaurant=point).events.all().filter(type__in=allowed_types)
if len(candidates) < 10:
candidates = NearestEventToRestaurant.objects.get(restaurant=point).events.all()
print(points, start_time)
try:
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)):
point_candidates = point_candidates.filter(type__in=where_eat)
point = point_candidates[0]
disallowed_rests.append(point.oid)
points.append(point)
candidates = NearestEventToRestaurant.objects.get(restaurant=point).events.all().filter(type__in=allowed_types)
if len(candidates) < 2:
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
path.append(generate_restaurant(points[-1]))
start_time += timedelta(seconds=path[-1]['time'])
how_many_eat += 1
continue
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:
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()
try:
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))
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
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(
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):
"""
Дописать
1) генерить маршруты от многих точек (не только по 2) (salesman problem)
2) Если в маршруте до 7 точек - добавлять похожие пока не станет 7 точек
"""
if len(points) < 7:
candidates = NearestEvent.objects.get(event=points[0]).nearest.all()
points.extend(list(get_nearest_favorite(candidates, user, points[0], [], velocity, 7-len(points))))
@ -663,3 +763,114 @@ def generate_points_path(user: User, points: Iterable[Event], velocity=3.0):
visited_points.append(pt)
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