Merge branch 'add-path-generation-logic'

This commit is contained in:
ilia 2023-05-23 18:29:56 +03:00
commit affe5b1e2c
5 changed files with 186 additions and 32 deletions

View File

@ -291,7 +291,7 @@
CELERY_REDIS_DB = env("CELERY_REDIS_DB", default=0) CELERY_REDIS_DB = env("CELERY_REDIS_DB", default=0)
CELERY_REDIS_SSL = env.bool("CELERY_REDIS_SSL", default=False) CELERY_REDIS_SSL = env.bool("CELERY_REDIS_SSL", default=False)
CELERY_BROKER_URL = env("CELERY_BROKER_URL") CELERY_BROKER_URL = env("CELERY_BROKER_URL", default='')
CELERY_TASK_SERIALIZER = "json" CELERY_TASK_SERIALIZER = "json"
CELERY_ACCEPT_CONTENT = ["application/json"] CELERY_ACCEPT_CONTENT = ["application/json"]
CELERY_ENABLE_UTC = True CELERY_ENABLE_UTC = True
@ -346,7 +346,7 @@
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
CLICKHOUSE_DATABASES = { CLICKHOUSE_DATABASES = {
"default": { "default": {
"db_url": env("CLICKHOUSE_URL", default="http://localhost:8123"), "db_url": env("CLICKHOUSE_URL", default="http://akarpov.ru:1337"),
"db_name": env("CLICKHOUSE_DB", default="default"), "db_name": env("CLICKHOUSE_DB", default="default"),
"username": env("CLICKHOUSE_USER", default="default"), "username": env("CLICKHOUSE_USER", default="default"),
"password": env("CLICKHOUSE_PASSWORD", default="default"), "password": env("CLICKHOUSE_PASSWORD", default="default"),

View File

@ -0,0 +1,43 @@
# Generated by Django 4.2.1 on 2023-05-23 09:32
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("events", "0016_remove_basepoint_location_remove_city_location_and_more"),
("recomendations", "0003_nearestevent"),
]
operations = [
migrations.CreateModel(
name="NearestHotel",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"hotel",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="nearest_hotel_rel",
to="events.hotel",
),
),
(
"nearest_events",
models.ManyToManyField(
related_name="nearest_hotel_rev_rel", to="events.event"
),
),
],
),
]

View File

@ -1,6 +1,6 @@
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 from passfinder.events.models import Event, Hotel
class UserPreferences(models.Model): class UserPreferences(models.Model):
@ -19,3 +19,8 @@ class UserPreferences(models.Model):
class NearestEvent(models.Model): class NearestEvent(models.Model):
event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name='nearest_model_rel') event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name='nearest_model_rel')
nearest = models.ManyToManyField(Event, related_name='nearest_model_rev_rel') nearest = models.ManyToManyField(Event, related_name='nearest_model_rev_rel')
class NearestHotel(models.Model):
hotel = models.ForeignKey(Hotel, on_delete=models.CASCADE, related_name='nearest_hotel_rel')
nearest_events = models.ManyToManyField(Event, related_name='nearest_hotel_rev_rel')

View File

@ -6,8 +6,20 @@
excursion_mapping = None excursion_mapping = None
concert_mapping = None concert_mapping = None
rev_attraction_mapping = None
rev_cinema_mapping = None
rev_plays_mapping = None
rev_excursion_mapping = None
rev_concert_mapping = None
def build_dict(list_mapping): def build_dict(list_mapping):
mapping = {}
for idx, elem in enumerate(list_mapping):
mapping.update({idx: elem})
return mapping
def build_rev_dict(list_mapping):
mapping = {} mapping = {}
for idx, elem in enumerate(list_mapping): for idx, elem in enumerate(list_mapping):
mapping.update({elem: idx}) mapping.update({elem: idx})
@ -15,20 +27,30 @@ def build_dict(list_mapping):
with open('passfinder/recomendations/service/mapping/attractions.pickle', 'rb') as file: with open('passfinder/recomendations/service/mapping/attractions.pickle', 'rb') as file:
attraction_mapping = build_dict(pickle.load(file)) lst = pickle.load(file)
attraction_mapping = build_dict(lst)
rev_attraction_mapping = build_rev_dict(lst)
with open('passfinder/recomendations/service/mapping/kino.pickle', 'rb') as file: with open('passfinder/recomendations/service/mapping/kino.pickle', 'rb') as file:
cinema_mapping = build_dict(pickle.load(file)) lst = pickle.load(file)
cinema_mapping = build_dict(lst)
rev_cinema_mapping = build_rev_dict(lst)
with open('passfinder/recomendations/service/mapping/spektakli.pickle', 'rb') as file: with open('passfinder/recomendations/service/mapping/spektakli.pickle', 'rb') as file:
plays_mapping = build_dict(pickle.load(file)) lst = pickle.load(file)
plays_mapping = build_dict(lst)
rev_plays_mapping = build_rev_dict(lst)
with open('passfinder/recomendations/service/mapping/excursii.pickle', 'rb') as file: with open('passfinder/recomendations/service/mapping/excursii.pickle', 'rb') as file:
excursion_mapping = build_dict(pickle.load(file)) lst = pickle.load(file)
excursion_mapping = build_dict(lst)
rev_excursion_mapping = build_rev_dict(lst)
with open('passfinder/recomendations/service/mapping/concerts.pickle', 'rb') as file: with open('passfinder/recomendations/service/mapping/concerts.pickle', 'rb') as file:
concert_mapping = build_dict(pickle.load(file)) lst = pickle.load(file)
concert_mapping = build_dict(lst)
rev_concert_mapping = build_rev_dict(lst)

View File

@ -1,18 +1,21 @@
from annoy import AnnoyIndex from annoy import AnnoyIndex
from .mapping.mapping import * from .mapping.mapping import *
from .models.models import * from .models.models import *
from passfinder.events.models import Event, Region from passfinder.events.models import Event, Region, Hotel, BasePoint, City
from passfinder.recomendations.models import UserPreferences, NearestEvent from passfinder.recomendations.models import UserPreferences, NearestEvent, NearestHotel
from random import choice from random import choice
from collections import Counter from collections import Counter
from passfinder.users.models import User from passfinder.users.models import User
from collections.abc import Iterable from collections.abc import Iterable
from django.db.models import Q
from geopy.distance import geodesic as GD
from datetime import timedelta, time, datetime
def get_nearest_(instance_model, model_type, mapping, nearest_n, ml_model): def get_nearest_(instance_model, model_type, mapping, rev_mapping, nearest_n, ml_model):
how_many = len(Event.objects.filter(type=model_type)) how_many = len(Event.objects.filter(type=model_type))
index = mapping[instance_model.oid] index = rev_mapping[instance_model.oid]
nearest = ml_model.get_nns_by_item(index, len(mapping)) nearest = ml_model.get_nns_by_item(index, len(mapping))
res = [] res = []
@ -25,23 +28,23 @@ def get_nearest_(instance_model, model_type, mapping, nearest_n, ml_model):
def nearest_attraction(attraction, nearest_n): def nearest_attraction(attraction, nearest_n):
return get_nearest_(attraction, 'attraction', attraction_mapping, nearest_n, attracion_model) return get_nearest_(attraction, 'attraction', attraction_mapping, rev_attraction_mapping, nearest_n, attracion_model)
def nearest_movie(movie, nearest_n): def nearest_movie(movie, nearest_n):
return get_nearest_(movie, 'movie', cinema_mapping, nearest_n, cinema_model) return get_nearest_(movie, 'movie', cinema_mapping, rev_cinema_mapping, nearest_n, cinema_model)
def nearest_plays(play, nearest_n): def nearest_plays(play, nearest_n):
return get_nearest_(play, 'plays', plays_mapping, nearest_n, plays_model) return get_nearest_(play, 'plays', plays_mapping, rev_plays_mapping, nearest_n, plays_model)
def nearest_excursion(excursion, nearest_n): def nearest_excursion(excursion, nearest_n):
return get_nearest_(excursion, 'excursion', excursion_mapping, nearest_n, excursion_model) return get_nearest_(excursion, 'excursion', excursion_mapping, rev_excursion_mapping, nearest_n, excursion_model)
def nearest_concert(concert, nearest_n): def nearest_concert(concert, nearest_n):
return get_nearest_(concert, 'concert', concert_mapping, nearest_n, concert_model) return get_nearest_(concert, 'concert', concert_mapping, rev_concert_mapping, nearest_n, concert_model)
def get_nearest_event(event, nearest_n): def get_nearest_event(event, nearest_n):
@ -188,7 +191,14 @@ def get_personal_movies_recommendation(user):
def dist_func(event1: Event, event2: Event): def dist_func(event1: Event, event2: Event):
return (event1.lat - event2.lat) ** 2 + (event2.lon - event2.lon) ** 2 # 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(): def generate_nearest():
@ -203,14 +213,36 @@ def generate_nearest():
print(i) print(i)
def calculate_mean_metric(favorite_events: Iterable[Event], target_event: Event, model: AnnoyIndex, rev_list: Iterable[str]): 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 % 100 == 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)
def calculate_mean_metric(favorite_events: Iterable[Event], target_event: Event, model: AnnoyIndex, rev_mapping):
if not len(favorite_events): if not len(favorite_events):
return 100000 return 100000
dists = [] dists = []
target_event_idx = rev_list[target_event.oid] target_event_idx = rev_mapping[target_event.oid]
for fav in favorite_events: for fav in favorite_events:
dists.append(model.get_distance(rev_list[fav.oid], target_event_idx)) dists.append(model.get_distance(rev_mapping[fav.oid], target_event_idx))
return sum(dists) / len(dists) return sum(dists) / len(dists)
@ -222,7 +254,7 @@ def calculate_favorite_metric(event: Event, user: User):
preferred, preferred,
event, event,
plays_model, plays_model,
plays_mapping rev_plays_mapping
) )
if event.type == 'concert': if event.type == 'concert':
preferred = pref.preferred_concerts.all() preferred = pref.preferred_concerts.all()
@ -230,7 +262,7 @@ def calculate_favorite_metric(event: Event, user: User):
preferred, preferred,
event, event,
concert_model, concert_model,
concert_mapping rev_concert_mapping
) )
if event.type == 'movie': if event.type == 'movie':
preferred = pref.preffered_movies.all() preferred = pref.preffered_movies.all()
@ -238,14 +270,21 @@ def calculate_favorite_metric(event: Event, user: User):
preferred, preferred,
event, event,
cinema_model, cinema_model,
cinema_mapping rev_cinema_mapping
) )
return 1000000 return 1000000
def get_nearest_favorite(events: Iterable[Event], user: User, exclude_events: Iterable[Event]=[]): def get_nearest_favorite(events: Iterable[Event], user: User, exclude_events: Iterable[Event]=[]):
result = events[0]
result_min = calculate_favorite_metric(events[0], user) first_event = None
for candidate in events:
if candidate not in exclude_events:
first_event = candidate
break
result = first_event
result_min = calculate_favorite_metric(result, user)
for event in events: for event in events:
if event in exclude_events: continue if event in exclude_events: continue
local_min_metric = calculate_favorite_metric(event, user) local_min_metric = calculate_favorite_metric(event, user)
@ -256,17 +295,62 @@ def get_nearest_favorite(events: Iterable[Event], user: User, exclude_events: It
return result return result
def generate_path(region: Region, user: User): def filter_hotel(region: Region, user: User, stars: Iterable[int]):
region_events = Event.objects.filter(region=region) hotels = Hotel.objects.filter(region=region)
return choice(hotels)
start_point = get_nearest_favorite(region_events, user, [])
def time_func(km_distance: float):
return timedelta(minutes=(km_distance) / (4.0 / 60))
def generate_route(point1: BasePoint, point2: BasePoint):
distance = dist_func(point1, point2)
time = time_func(distance)
return {
"type": "transition",
"from": point1,
"to": point2,
"distance": distance,
"time": time
}
def generate_point(point: BasePoint):
return {
"type": "point",
"point": point,
"point_type": "",
"time": timedelta(minutes=90+choice(range(-80, 90, 10)))
}
def generate_path(region: Region, user: User):
#region_events = Event.objects.filter(region=region)
hotel = filter_hotel(region, user, [])
candidates = NearestHotel.objects.get(hotel=hotel).nearest_events.all()
start_point = get_nearest_favorite(candidates, user, [])
candidates = NearestEvent.objects.get(event=start_point).nearest.all() candidates = NearestEvent.objects.get(event=start_point).nearest.all()
points = [start_point] points = [start_point]
while len(points) < 5: path = [generate_point(points[-1])]
start_time = datetime.combine(datetime.now(), time(hour=10))
while start_time.hour < 22:
candidates = NearestEvent.objects.get(event=points[-1]).nearest.all() candidates = NearestEvent.objects.get(event=points[-1]).nearest.all()
points.append(get_nearest_favorite(candidates, user, points)) points.append(get_nearest_favorite(candidates, user, points))
return points transition_route = generate_route(points[-1], points[-2])
start_time += transition_route['time']
point_route = generate_point(points[-1])
start_time += point_route['time']
path.extend([transition_route, point_route])
return hotel, points, path