mirror of
https://github.com/task-17-lct/backend.git
synced 2024-11-30 19:13:43 +03:00
1005 lines
30 KiB
Python
1005 lines
30 KiB
Python
from annoy import AnnoyIndex
|
||
from .mapping.mapping import *
|
||
from .models.models import *
|
||
from passfinder.events.models import Event, Region, Hotel, BasePoint, City, Restaurant
|
||
from passfinder.events.api.serializers import (
|
||
HotelSerializer,
|
||
EventSerializer,
|
||
RestaurantSerializer,
|
||
ObjectRouteSerializer,
|
||
)
|
||
from passfinder.recomendations.models import *
|
||
from random import choice, sample
|
||
from collections import Counter
|
||
from passfinder.users.models import User
|
||
from collections.abc import Iterable
|
||
from django.db.models import Q
|
||
from geopy.distance import geodesic as GD
|
||
from datetime import timedelta, time, datetime
|
||
from gevent.pool import Pool
|
||
from python_tsp.exact import solve_tsp_dynamic_programming
|
||
import numpy as np
|
||
|
||
|
||
def get_nearest_(instance_model, model_type, mapping, rev_mapping, nearest_n, ml_model):
|
||
how_many = len(Event.objects.filter(type=model_type))
|
||
|
||
index = rev_mapping[instance_model.oid]
|
||
nearest = ml_model.get_nns_by_item(index, len(mapping))
|
||
|
||
res = []
|
||
for i in range(how_many):
|
||
try:
|
||
res.append(Event.objects.get(oid=mapping[nearest[i]]))
|
||
except Event.DoesNotExist:
|
||
...
|
||
if len(res) == nearest_n:
|
||
break
|
||
return res
|
||
|
||
|
||
def nearest_attraction(attraction, nearest_n):
|
||
return get_nearest_(
|
||
attraction,
|
||
"attraction",
|
||
attraction_mapping,
|
||
rev_attraction_mapping,
|
||
nearest_n,
|
||
attracion_model,
|
||
)
|
||
|
||
|
||
def nearest_mus(museum, nearest_n):
|
||
return get_nearest_(
|
||
museum, "museum", mus_mapping, rev_mus_mapping, nearest_n, mus_model
|
||
)
|
||
|
||
|
||
def nearest_movie(movie, nearest_n):
|
||
return get_nearest_(
|
||
movie, "movie", cinema_mapping, rev_cinema_mapping, nearest_n, cinema_model
|
||
)
|
||
|
||
|
||
def nearest_plays(play, nearest_n):
|
||
return get_nearest_(
|
||
play, "plays", plays_mapping, rev_plays_mapping, nearest_n, plays_model
|
||
)
|
||
|
||
|
||
def nearest_excursion(excursion, nearest_n):
|
||
return get_nearest_(
|
||
excursion,
|
||
"excursion",
|
||
excursion_mapping,
|
||
rev_excursion_mapping,
|
||
nearest_n,
|
||
excursion_model,
|
||
)
|
||
|
||
|
||
def nearest_concert(concert, nearest_n):
|
||
return get_nearest_(
|
||
concert,
|
||
"concert",
|
||
concert_mapping,
|
||
rev_concert_mapping,
|
||
nearest_n,
|
||
concert_model,
|
||
)
|
||
|
||
|
||
def get_nearest_event(event, nearest_n):
|
||
if event.type == "plays":
|
||
return nearest_plays(event, nearest_n)
|
||
if event.type == "concert":
|
||
return nearest_concert(event, nearest_n)
|
||
if event.type == "movie":
|
||
return nearest_movie(event, nearest_n)
|
||
if event.type == "museum":
|
||
return nearest_mus(event, nearest_n)
|
||
if event.type == "attraction":
|
||
return nearest_attraction(event, nearest_n)
|
||
|
||
|
||
def update_preferences_state(user, event, direction):
|
||
pref = UserPreferences.objects.get(user=user)
|
||
|
||
if direction == "left":
|
||
if event.type == "plays":
|
||
pref.unpreffered_plays.add(event)
|
||
if event.type == "movie":
|
||
pref.unpreffered_movies.add(event)
|
||
if event.type == "concert":
|
||
pref.unpreferred_concerts.add(event)
|
||
else:
|
||
if event.type == "plays":
|
||
pref.preffered_plays.add(event)
|
||
if event.type == "movie":
|
||
pref.preffered_movies.add(event)
|
||
if event.type == "concert":
|
||
pref.preferred_concerts.add(event)
|
||
pref.save()
|
||
|
||
|
||
def get_next_tinder(user, prev_event, prev_direction):
|
||
pref = UserPreferences.objects.get(user=user)
|
||
print(prev_event.type, len(pref.preferred_concerts.all()))
|
||
if prev_direction == "left":
|
||
if prev_event.type == "plays" and len(pref.unpreffered_plays.all()) <= 2:
|
||
candidates = nearest_plays(prev_event, 100)
|
||
# print(candidates, type(candidates), len(Event.objects.filter(type='plays')))
|
||
return candidates[-1]
|
||
if prev_event.type == "movie" and len(pref.unpreffered_movies.all()) <= 2:
|
||
candidates = nearest_movie(prev_event, 100)
|
||
return candidates[-1]
|
||
if prev_event.type == "concert" and len(pref.unpreferred_concerts.all()) <= 2:
|
||
candidates = nearest_concert(prev_event, 100)
|
||
return candidates[-1]
|
||
|
||
if prev_direction == "right":
|
||
if prev_event.type == "plays" and len(pref.preffered_plays.all()) < 2:
|
||
candidates = nearest_plays(prev_event, 2)
|
||
return candidates[1]
|
||
if prev_event.type == "movie" and len(pref.preffered_movies.all()) < 2:
|
||
candidates = nearest_movie(prev_event, 2)
|
||
return candidates[1]
|
||
if prev_event.type == "concert" and len(pref.preferred_concerts.all()) < 2:
|
||
candidates = nearest_concert(prev_event, 2)
|
||
return candidates[1]
|
||
|
||
if prev_event.type == "plays":
|
||
if not len(pref.preffered_movies.all()) and not len(
|
||
pref.unpreffered_movies.all()
|
||
):
|
||
return choice(Event.objects.filter(type="movie"))
|
||
if not len(pref.preferred_concerts.all()) and not len(
|
||
pref.unpreferred_concerts.all()
|
||
):
|
||
return choice(Event.objects.filter(type="concert"))
|
||
|
||
if prev_event.type == "movie":
|
||
if not len(pref.preffered_plays.all()) and not len(
|
||
pref.unpreffered_plays.all()
|
||
):
|
||
return choice(Event.objects.filter(type="plays"))
|
||
if not len(pref.preferred_concerts.all()) and not len(
|
||
pref.unpreferred_concerts.all()
|
||
):
|
||
return choice(Event.objects.filter(type="concert"))
|
||
|
||
if prev_event.type == "concert":
|
||
if not len(pref.preffered_plays.all()) and not len(
|
||
pref.unpreffered_plays.all()
|
||
):
|
||
return choice(Event.objects.filter(type="plays"))
|
||
if not len(pref.preffered_movies.all()) and not len(
|
||
pref.unpreffered_movies.all()
|
||
):
|
||
return choice(Event.objects.filter(type="movie"))
|
||
|
||
return None
|
||
|
||
|
||
def rank_candidates(candidates_list, negative_candidates_list):
|
||
flatten_c_list = []
|
||
ranks = {}
|
||
|
||
flatten_negatives = []
|
||
|
||
for negative in negative_candidates_list:
|
||
flatten_negatives.extend(negative)
|
||
|
||
for lst in candidates_list:
|
||
flatten_c_list.extend(lst)
|
||
for cand in lst:
|
||
ranks.update({cand: {"rank": 0, "lst": lst}})
|
||
|
||
cnt = Counter(flatten_c_list)
|
||
|
||
for candidate, how_many in cnt.most_common(len(flatten_c_list)):
|
||
ranks[candidate]["rank"] = how_many * (
|
||
len(ranks[candidate]["lst"]) - ranks[candidate]["lst"].index(candidate)
|
||
)
|
||
|
||
res = []
|
||
for cand in ranks.keys():
|
||
res.append((ranks[cand]["rank"], cand))
|
||
return list(
|
||
filter(
|
||
lambda x: x[1] not in flatten_negatives, sorted(res, key=lambda x: -x[0])
|
||
)
|
||
)
|
||
|
||
|
||
def get_personal_recommendation(prefer, unprefer):
|
||
candidates = []
|
||
negative_candidates = []
|
||
|
||
for rec in prefer:
|
||
candidates.append(list(map(lambda x: x.oid, get_nearest_event(rec, 10)[1:])))
|
||
|
||
for neg in unprefer:
|
||
negative_candidates.append(
|
||
list(map(lambda x: x.oid, get_nearest_event(neg, 10)[1:]))
|
||
)
|
||
|
||
ranked = rank_candidates(candidates, negative_candidates)
|
||
|
||
return list(map(lambda x: (x[0], Event.objects.get(oid=x[1])), ranked[0:5]))
|
||
|
||
|
||
def get_personal_plays_recommendation(user):
|
||
pref = UserPreferences.objects.get(user=user)
|
||
|
||
prefer = pref.preffered_plays.all()
|
||
unprefer = pref.unpreffered_plays.all()
|
||
return get_personal_recommendation(prefer, unprefer)
|
||
|
||
|
||
def get_personal_concerts_recommendation(user):
|
||
pref = UserPreferences.objects.get(user=user)
|
||
|
||
prefer = pref.preferred_concerts.all()
|
||
unprefer = pref.unpreferred_concerts.all()
|
||
return get_personal_recommendation(prefer, unprefer)
|
||
|
||
|
||
def get_personal_movies_recommendation(user):
|
||
pref = UserPreferences.objects.get(user=user)
|
||
|
||
prefer = pref.preffered_movies.all()
|
||
unprefer = pref.unpreffered_movies.all()
|
||
return get_personal_recommendation(prefer, unprefer)
|
||
|
||
|
||
def dist_func(event1: Event, event2: Event):
|
||
cords1 = [event1.lat, event1.lon]
|
||
cords2 = [event2.lat, event2.lon]
|
||
try:
|
||
dist = GD(cords1, cords2).km
|
||
return dist
|
||
except:
|
||
return 1000000
|
||
return (event1.lon - event2.lon) ** 2 + (event1.lat - event2.lat) ** 2
|
||
|
||
|
||
def generate_nearest():
|
||
NearestEvent.objects.all().delete()
|
||
all_events = list(Event.objects.all())
|
||
for i, event in enumerate(Event.objects.all()):
|
||
event_all_events = list(
|
||
sorted(all_events.copy(), key=lambda x: dist_func(event, x))
|
||
)
|
||
nearest = NearestEvent.objects.create(event=event)
|
||
nearest.nearest.set(event_all_events[0:100])
|
||
nearest.save()
|
||
if i % 10 == 0:
|
||
print(i)
|
||
|
||
|
||
def generate_nearest_rest():
|
||
NearestEventToRestaurant.objects.all().delete()
|
||
all_events = list(Event.objects.all())
|
||
for i, rest in enumerate(Restaurant.objects.all()):
|
||
sorted_events = list(
|
||
sorted(all_events.copy(), key=lambda event: dist_func(rest, event))
|
||
)
|
||
nearest = NearestEventToRestaurant.objects.create(restaurant=rest)
|
||
nearest.events.set(sorted_events[0:100])
|
||
if i % 10 == 0:
|
||
print(i)
|
||
|
||
|
||
def generate_hotel_nearest():
|
||
NearestHotel.objects.all().delete()
|
||
all_events = list(Event.objects.all())
|
||
hotels = list(Hotel.objects.all())
|
||
for i, hotel in enumerate(hotels):
|
||
event_all_events = list(
|
||
sorted(all_events.copy(), key=lambda x: dist_func(hotel, x))
|
||
)
|
||
nearest = NearestHotel.objects.create(hotel=hotel)
|
||
nearest.nearest_events.set(event_all_events[0:100])
|
||
if i % 10 == 0:
|
||
print(i)
|
||
|
||
|
||
def generate_nearest_restaurants():
|
||
rests = list(Restaurant.objects.all())
|
||
for i, event in enumerate(Event.objects.all()):
|
||
sorted_rests = list(sorted(rests.copy(), key=lambda x: dist_func(x, event)))
|
||
nr = NearestRestaurantToEvent.objects.create(event=event)
|
||
nr.restaurants.set(sorted_rests[0:20])
|
||
nr.save()
|
||
if i % 10 == 0:
|
||
print(i)
|
||
|
||
for i, hotel in enumerate(Hotel.objects.all()):
|
||
sorted_rests = list(sorted(rests.copy(), key=lambda x: dist_func(x, hotel)))
|
||
nr = NearestRestaurantToHotel.objects.create(hotel=hotel)
|
||
nr.restaurants.set(sorted_rests[0:20])
|
||
nr.save()
|
||
if i % 10 == 0:
|
||
print(i)
|
||
|
||
|
||
def match_points():
|
||
regions = list(City.objects.all())
|
||
for i, point in enumerate(Event.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)
|
||
for i, point in enumerate(Hotel.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 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,
|
||
model: AnnoyIndex,
|
||
rev_mapping,
|
||
):
|
||
if not len(favorite_events):
|
||
return 100000
|
||
|
||
dists = []
|
||
try:
|
||
target_event_idx = rev_mapping[target_event.oid]
|
||
except:
|
||
return 10
|
||
for fav in favorite_events:
|
||
try:
|
||
dists.append(model.get_distance(rev_mapping[fav.oid], target_event_idx))
|
||
except: pass
|
||
try:
|
||
return sum(dists) / len(dists)
|
||
except ZeroDivisionError:
|
||
return 10
|
||
|
||
|
||
def calculate_favorite_metric(event: Event, user: User):
|
||
pref = UserPreferences.objects.get(user=user)
|
||
if event.type == "plays":
|
||
preferred = pref.preffered_plays.all()
|
||
return calculate_mean_metric(preferred, event, plays_model, rev_plays_mapping)
|
||
if event.type == "concert":
|
||
preferred = pref.preferred_concerts.all()
|
||
return calculate_mean_metric(
|
||
preferred, event, concert_model, rev_concert_mapping
|
||
)
|
||
if event.type == "movie":
|
||
preferred = pref.preffered_movies.all()
|
||
return calculate_mean_metric(preferred, event, cinema_model, rev_cinema_mapping)
|
||
if event.type == "attraction":
|
||
preferred = pref.prefferred_attractions.all()
|
||
return calculate_mean_metric(
|
||
preferred, event, attracion_model, rev_attraction_mapping
|
||
)
|
||
if event.type == "museum":
|
||
preferred = pref.prefferred_museums.all()
|
||
return calculate_mean_metric(preferred, event, mus_model, rev_mus_mapping)
|
||
return 10
|
||
|
||
|
||
def get_exponential_koef(time: timedelta):
|
||
time = time.seconds
|
||
if time < 60 * 10:
|
||
return 2
|
||
if time < 60 * 20:
|
||
return 5
|
||
if time < 60 * 30:
|
||
return 10
|
||
if time < 60 * 40:
|
||
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,
|
||
):
|
||
|
||
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:
|
||
try:
|
||
return sorted_events[0]
|
||
except: return None
|
||
|
||
return sorted_events[0:top_k]
|
||
|
||
|
||
def filter_hotel(region: Region, user: User, stars: Iterable[int]):
|
||
hotels = Hotel.objects.filter(city=region)
|
||
return choice(hotels)
|
||
|
||
|
||
def time_func(km_distance: float, velocity: float):
|
||
return timedelta(minutes=(km_distance) / (velocity / 60))
|
||
|
||
|
||
def generate_route(point1: BasePoint, point2: BasePoint, velocity):
|
||
distance = dist_func(point1, point2)
|
||
time = time_func(distance, velocity)
|
||
return {
|
||
"type": "transition",
|
||
"distance": distance,
|
||
"time": time.seconds,
|
||
}
|
||
|
||
|
||
def generate_point(point: BasePoint):
|
||
event_data = ObjectRouteSerializer(point).data
|
||
return {
|
||
"type": "point",
|
||
"point": event_data,
|
||
"point_type": "point",
|
||
"time": timedelta(minutes=90 + choice(range(-10, 90, 10))).seconds,
|
||
"distance": 0,
|
||
}
|
||
|
||
|
||
def generate_restaurant(point: BasePoint):
|
||
rest_data = ObjectRouteSerializer(point).data
|
||
|
||
return {
|
||
"type": "point",
|
||
"point": rest_data,
|
||
"point_type": "restaurant",
|
||
"time": timedelta(minutes=90 + choice(range(-10, 90, 10))).seconds,
|
||
}
|
||
|
||
|
||
def generate_multiple_tours(
|
||
user: User, city: City, start_date: datetime.date, end_date: datetime.date
|
||
):
|
||
hotels = sample(list(Hotel.objects.filter(city=city)), 5)
|
||
pool = Pool(5)
|
||
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,
|
||
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)
|
||
|
||
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,
|
||
where_eat=where_eat,
|
||
what_to_see=what_to_see,
|
||
)
|
||
points.extend(local_points)
|
||
paths.append({"date": current_date, "paths": local_paths})
|
||
disallowed_rest = local_disallowed_rest
|
||
current_date += timedelta(days=1)
|
||
return paths, points
|
||
|
||
|
||
def generate_hotel(hotel: Hotel):
|
||
hotel_data = ObjectRouteSerializer(hotel).data
|
||
return {
|
||
"type": "point",
|
||
"point": hotel_data,
|
||
"point_type": "hotel",
|
||
"time": 0,
|
||
"distance": 0,
|
||
}
|
||
|
||
|
||
def nearest_distance_points(point: BasePoint, user: User, velocity: float = 3.0):
|
||
nearest = []
|
||
print(isinstance(point, Event), point)
|
||
if isinstance(point, Event):
|
||
nearest = NearestEvent.objects.get(event=point).nearest.all()
|
||
if isinstance(point, Hotel):
|
||
nearest = NearestHotel.objects.get(hotel=point).nearest_events.all()
|
||
if isinstance(point, Restaurant):
|
||
nearest = NearestEventToRestaurant.objects.get(restaurant=point).events.all()
|
||
|
||
top_nearest = get_nearest_favorite(nearest, user, point, [], velocity, top_k=10)
|
||
return top_nearest
|
||
|
||
|
||
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 not len(what_to_see):
|
||
what_to_see=[
|
||
"attractions",
|
||
"museum",
|
||
"movie",
|
||
"concert",
|
||
"artwork",
|
||
"plays",
|
||
"shop",
|
||
"gallery",
|
||
"theme_park",
|
||
"viewpoint",
|
||
"zoo",
|
||
]
|
||
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)
|
||
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))
|
||
|
||
how_many_eat = 1
|
||
|
||
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
|
||
):
|
||
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
|
||
|
||
if start_time.hour > 17:
|
||
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) < 2:
|
||
candidates = NearestEvent.objects.get(event=points[-1]).nearest.all()
|
||
|
||
try:
|
||
fav = get_nearest_favorite(
|
||
candidates, user, points[-1], points + disallowed_points
|
||
)
|
||
if fav is None:
|
||
raise ValueError()
|
||
points.append(
|
||
fav
|
||
)
|
||
|
||
except:
|
||
fav = get_nearest_favorite(candidates, user, points[-1], points)
|
||
if fav is None:
|
||
return points, path, disallowed_rests
|
||
|
||
points.append(fav)
|
||
print(points, "points")
|
||
transition_route = generate_route(points[-1], points[-2], avg_velocity)
|
||
|
||
start_time += timedelta(seconds=transition_route["time"])
|
||
|
||
point_route = generate_point(points[-1])
|
||
start_time += timedelta(seconds=point_route["time"])
|
||
path.extend([transition_route, point_route])
|
||
candidates = None
|
||
|
||
# = "Сгенерируй описание туристического маршрута, проходящего через следующие точки:\n"
|
||
|
||
# prompt += 'Отель: {hotel.name}\n'
|
||
# for i in points:
|
||
# prompt += f'Название: {i.title}\nОписание: {i.description}\nТип: {i.type}\n\n'
|
||
# print(promptprompt)
|
||
|
||
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 get_onboarding_attractions():
|
||
sample_attractions = sample(list(Event.objects.filter(type="attraction")), 200)
|
||
first_attraction = choice(sample_attractions)
|
||
|
||
attractions = [first_attraction]
|
||
|
||
while len(attractions) < 10:
|
||
mx_dist = 0
|
||
mx_attraction = None
|
||
for att in sample_attractions:
|
||
if att in attractions:
|
||
continue
|
||
local_dist = calculate_distance(
|
||
att, attractions, attracion_model, rev_attraction_mapping
|
||
)
|
||
if local_dist > mx_dist:
|
||
mx_dist = local_dist
|
||
mx_attraction = att
|
||
attractions.append(mx_attraction)
|
||
return attractions
|
||
|
||
|
||
def get_onboarding_hotels(stars=Iterable[int]):
|
||
return sample(list(Hotel.objects.filter(stars__in=stars)), 10)
|
||
|
||
|
||
def generate_points_path(user: User, points: Iterable[Event], velocity=3.0):
|
||
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)
|
||
)
|
||
)
|
||
)
|
||
|
||
dist_matrix = [[0 for j in range(len(points))] for i in range(len(points))]
|
||
for i in range(len(dist_matrix)):
|
||
for j in range(len(dist_matrix)):
|
||
dist_matrix[i][j] = time_func(
|
||
dist_func(points[i], points[j]), velocity
|
||
).seconds
|
||
for i in range(len(dist_matrix)):
|
||
dist_matrix[i][0] = 0
|
||
dist_matrix = np.array(dist_matrix)
|
||
dist_matrix[:, 0] = 0
|
||
perm, dist = solve_tsp_dynamic_programming(dist_matrix)
|
||
|
||
perm_pts = [points[i] for i in perm]
|
||
|
||
res = [generate_point(perm_pts[0])]
|
||
visited_points = [perm_pts[0]]
|
||
|
||
for pt in perm_pts[1:]:
|
||
res.extend(
|
||
[generate_route(visited_points[-1], pt, velocity), generate_point(pt)]
|
||
)
|
||
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],
|
||
}
|
||
try:
|
||
if candidates[0].type in ["attraction", "museum", "movie", "concert", "plays"]:
|
||
candidates = sorted(
|
||
map(
|
||
lambda x: [
|
||
calculate_mean_metric(
|
||
favorite_events, x, *model_mappings[x.type]
|
||
), x
|
||
],
|
||
candidates
|
||
),
|
||
key=lambda x: x[0],
|
||
)
|
||
return candidates[0:10]
|
||
return sample(candidates, 10)
|
||
except: return []
|
||
|
||
|
||
|
||
candidates_generate_strategy = {
|
||
"plays": [
|
||
lambda pref: flat_list(
|
||
list(
|
||
map(
|
||
lambda cand: nearest_plays(cand, 10), pref.preffered_plays.all()[0:5]
|
||
)
|
||
),
|
||
),
|
||
lambda pref: pref.preffered_plays.all()[0:10],
|
||
],
|
||
"movie": [
|
||
lambda pref: flat_list(
|
||
list(
|
||
map(
|
||
lambda cand: nearest_movie(cand, 30),
|
||
pref.preffered_movies.all()[0:4],
|
||
)
|
||
),
|
||
),
|
||
lambda pref: pref.preffered_movies.all()[0:10],
|
||
],
|
||
"concert": [
|
||
lambda pref: flat_list(
|
||
list(
|
||
map(
|
||
lambda cand: nearest_concert(cand, 30),
|
||
pref.preferred_concerts.all()[0:4],
|
||
)
|
||
),
|
||
),
|
||
lambda pref: pref.preferred_concerts.all()[0:4],
|
||
],
|
||
"attractions": [
|
||
lambda pref: flat_list(
|
||
list(
|
||
map(
|
||
lambda cand: nearest_attraction(cand, 10),
|
||
pref.prefferred_attractions.all()[0:4],
|
||
)
|
||
),
|
||
),
|
||
lambda pref: pref.prefferred_attractions.all()[0:4],
|
||
],
|
||
"museum": [
|
||
lambda pref: flat_list(
|
||
list(
|
||
map(
|
||
lambda cand: nearest_mus(cand, 10),
|
||
pref.prefferred_museums.all()[0:4],
|
||
)
|
||
),
|
||
),
|
||
lambda pref: pref.prefferred_museums.all()[0:10],
|
||
],
|
||
"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: [],
|
||
],
|
||
"artwork": [
|
||
lambda pref: sample(list(Event.objects.filter(type="zoo")), 10),
|
||
lambda x: [],
|
||
],
|
||
}
|
||
|
||
|
||
|
||
def get_personal_recomendations(user):
|
||
up, _ = UserPreferences.objects.get_or_create(user=user)
|
||
|
||
res = []
|
||
for category_candidate in up.preferred_categories:
|
||
print(category_candidate)
|
||
candidates = candidates_generate_strategy[category_candidate][0](up)
|
||
print(len(candidates))
|
||
ranged = range_candidates(
|
||
candidates, user, candidates_generate_strategy[category_candidate][1](up)
|
||
)
|
||
res.append(
|
||
{
|
||
"category": category_candidate,
|
||
"events": list(map(lambda x: ObjectRouteSerializer(x[1]).data, ranged)),
|
||
}
|
||
)
|
||
return res
|
||
|
||
|
||
def get_events(
|
||
user: User,
|
||
allowed_regions: Iterable[City],
|
||
what_to_see: Iterable[str]
|
||
):
|
||
up, _ = UserPreferences.objects.get_or_create(user=user)
|
||
events = Event.objects.filter(type__in=what_to_see, city__in=allowed_regions)
|
||
ranged = []
|
||
for category in what_to_see:
|
||
candidates = events.filter(type=category)
|
||
ranged.extend(
|
||
range_candidates(
|
||
candidates,
|
||
user,
|
||
candidates_generate_strategy[category][1](up)
|
||
)
|
||
)
|
||
ranged.sort(key=lambda x: x[0])
|
||
return list(
|
||
map(
|
||
lambda x: ObjectRouteSerializer(x[1]).data,
|
||
ranged[0:10]
|
||
)
|
||
)
|
||
|
||
|
||
def remap_points(date: datetime.date, region: City, point: Event):
|
||
allowed_types = [
|
||
|
||
]
|