add route generation route

This commit is contained in:
ilia 2023-05-25 17:12:39 +03:00
parent 5115deef66
commit b229e017a0
6 changed files with 514 additions and 65 deletions

View File

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

View File

@ -1,7 +1,7 @@
from rest_framework import serializers
from rest_framework.generics import get_object_or_404
from passfinder.events.models import Hotel, HotelPhone, City, Event, BasePoint, Region
from passfinder.events.models import Hotel, HotelPhone, City, Event, BasePoint, Region, Restaurant
class HotelPhoneSerializer(serializers.ModelSerializer):
@ -49,7 +49,7 @@ class RouteSerializer(serializers.Serializer):
class RouteInputSerializer(serializers.Serializer):
date_from = serializers.DateField(required=False, allow_null=True)
date_to = serializers.DateField(required=False, allow_null=True)
region = serializers.CharField(
city = serializers.CharField(
min_length=24, max_length=24, required=False, allow_blank=True, allow_null=True
)
@ -106,3 +106,16 @@ def validate(self, data):
class InputRouteSerializer(serializers.Serializer):
points = serializers.ListSerializer(child=InputRoutePointSerializer())
class ResaurantSerializer(serializers.ModelSerializer):
class Meta:
model = Restaurant
exclude = ('phones', )
class ObjectRouteSerializer(serializers.Serializer):
lat = serializers.FloatField()
lon = serializers.FloatField()
title = serializers.CharField()
description = serializers.CharField()

View File

@ -1,6 +1,12 @@
from rest_framework.generics import GenericAPIView, ListAPIView, get_object_or_404
from rest_framework.response import Response
from drf_spectacular.utils import extend_schema
from django.db.models import Count
from random import choice
from passfinder.recomendations.service.service import generate_tour
from datetime import timedelta, datetime
from .consts import *
from passfinder.events.api.serializers import (
PointSerializer,
@ -46,34 +52,34 @@ def post(self, request):
serializer = RouteInputSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
data = serializer.data
region = data["region"]
routes = []
if region:
region = get_object_or_404(Region, oid=region)
for _ in range(2):
routes.append(
{
"name": "bebra",
"date": data["date_from"],
"description": "bebra bebra bebra",
"points": PointSerializer(many=True).to_representation(
region.points.all().order_by("?")[:10]
),
}
)
city_id = data["city"]
start_date = datetime.strptime(data['date_from'], '%Y-%m-%d')
end_date = datetime.strptime(data['date_to'], '%Y-%m-%d')
region = None
if city_id:
region = get_object_or_404(City, oid=city_id)
else:
for _ in range(10):
routes.append(
{
"name": "bebra",
"date": data["date_from"],
"description": "bebra bebra bebra",
"points": PointSerializer(many=True).to_representation(
BasePoint.objects.order_by("?")[:10]
),
}
)
return Response(data=routes)
region = choice(City.objects.annotate(points_count=Count('points')).filter(title__in=city_in_hotels))
if not start_date and end_date:
tour_length = choice([timedelta(days=i) for i in range(1, 4)])
start_date = end_date - tour_length
if not end_date and start_date:
tour_length = choice([timedelta(days=i) for i in range(1, 4)])
end_date = end_date + tour_length
if not end_date and not start_date:
max_date = datetime.now() + timedelta(days=15)
start_date = choice([max_date - timedelta(days=i) for i in range(1, 5)])
tour_length = choice([timedelta(days=i) for i in range(1, 4)])
end_date = start_date + tour_length
print(request.user, region, start_date, end_date)
tour = generate_tour(request.user, region, start_date, end_date)
print(len(tour[1]))
return Response(data=tour[0])
class ListRegionApiView(ListAPIView):

View File

@ -0,0 +1,57 @@
# Generated by Django 4.2.1 on 2023-05-24 15:57
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("events", "0019_category_pointcategory_delete_tag"),
("recomendations", "0005_userpreferences_prefferred_attractions_and_more"),
]
operations = [
migrations.CreateModel(
name="NearestRestaurantToHotel",
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, to="events.hotel"
),
),
("restaurants", models.ManyToManyField(to="events.restaurant")),
],
),
migrations.CreateModel(
name="NearestRestaurantToEvent",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"event",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to="events.event"
),
),
("restaurants", models.ManyToManyField(to="events.restaurant")),
],
),
]

View File

@ -1,6 +1,6 @@
from django.db import models
from passfinder.users.models import User
from passfinder.events.models import Event, Hotel
from passfinder.events.models import Event, Hotel, Restaurant
class UserPreferences(models.Model):
@ -27,7 +27,36 @@ class NearestEvent(models.Model):
event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name='nearest_model_rel')
nearest = models.ManyToManyField(Event, related_name='nearest_model_rev_rel')
"""
from passfinder.recomendations.service.service import generate_tour
from passfinder.users.models import User
from passfinder.events.models import City
from datetime import datetime
start_date = datetime(year=2023, month=6, day=10)
end_date = datetime(year=2023, month=6, day=13)
c = City.objects.get(title='Таганрог')
u = User.objects.all()[0]
generate_tour(u, c, start_date, end_date)
"""
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')
class NearestRestaurantToEvent(models.Model):
event = models.ForeignKey(Event, on_delete=models.CASCADE)
restaurants = models.ManyToManyField(Restaurant)
class NearestRestaurantToHotel(models.Model):
hotel = models.ForeignKey(Hotel, on_delete=models.CASCADE)
restaurants = models.ManyToManyField(Restaurant)

View File

@ -1,8 +1,9 @@
from annoy import AnnoyIndex
from .mapping.mapping import *
from .models.models import *
from passfinder.events.models import Event, Region, Hotel, BasePoint, City
from passfinder.recomendations.models import UserPreferences, NearestEvent, NearestHotel
from passfinder.events.models import Event, Region, Hotel, BasePoint, City, Restaurant
from passfinder.events.api.serializers import HotelSerializer, EventSerializer, ResaurantSerializer, ObjectRouteSerializer
from passfinder.recomendations.models import *
from random import choice, sample
from collections import Counter
from passfinder.users.models import User
@ -10,6 +11,7 @@
from django.db.models import Q
from geopy.distance import geodesic as GD
from datetime import timedelta, time, datetime
from gevent.pool import Pool
def get_nearest_(instance_model, model_type, mapping, rev_mapping, nearest_n, ml_model):
@ -250,14 +252,14 @@ def get_personal_movies_recommendation(user):
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
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():
@ -288,6 +290,26 @@ def generate_hotel_nearest():
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 % 100 == 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 % 100 == 0:
print(i)
def match_points():
regions = list(City.objects.all())
for i, point in enumerate(Event.objects.all()):
@ -339,14 +361,18 @@ def calculate_favorite_metric(event: Event, user: User):
def get_nearest_favorite(
events: Iterable[Event], user: User, exclude_events: Iterable[Event] = []
):
print(events)
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)
if first_event is None:
result = events[0]
else:
result = first_event
result_min = 1000000
for event in events:
if event in exclude_events:
continue
@ -372,50 +398,142 @@ def generate_route(point1: BasePoint, point2: BasePoint):
time = time_func(distance)
return {
"type": "transition",
"from": point1,
"to": point2,
"distance": distance,
"time": time,
"time": time.seconds,
}
def generate_point(point: BasePoint):
event_data = ObjectRouteSerializer(point).data
return {
"type": "point",
"point": point,
"point_type": "",
"time": timedelta(minutes=90+choice(range(-10, 90, 10)))
"point": event_data,
"point_type": "point",
"time": timedelta(minutes=90+choice(range(-10, 90, 10))).seconds
}
def generate_path(region: Region, user: User):
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):
print("start hotel")
hotel = choice(list(Hotel.objects.filter(city=city)))
print("end hotel")
current_date = start_date
paths, points = [], []
while current_date < end_date:
print("start day gen")
local_points, local_paths = generate_path(user, points, hotel)
points.extend(local_points)
paths.append(
{
'date': current_date,
'paths': local_paths
}
)
print("end day gen")
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": timedelta(minutes=90+choice(range(-10, 90, 10))).seconds
}
def generate_path(user: User, disallowed_points: Iterable[BasePoint], hotel: Hotel):
# region_events = Event.objects.filter(region=region)
hotel = filter_hotel(region, user, [])
#candidates = NearestHotel.objects.get(hotel=hotel).nearest_events.all()
allowed_types = ['museum', 'attraction']
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()
print("start start point gen")
start_point = NearestRestaurantToHotel.objects.get(hotel=hotel).restaurants.first()
print("end start point gen")
print("start first cand gen")
candidates = list(filter(lambda x: x.type in allowed_types, map(lambda x: x.event, start_point.nearestrestauranttoevent_set.all()[0:100])))
print("end first cand gen")
points = [start_point]
path = [generate_point(points[-1])]
path = [
generate_hotel(hotel),
generate_route(start_point, hotel),
generate_restaurant(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()
points.append(get_nearest_favorite(candidates, user, points))
how_many_eat = 1
while start_time.hour < 22:
print(start_time)
if (start_time.hour > 13 and how_many_eat == 1) or (start_time.hour > 20 and how_many_eat == 2):
print("start rest event gen")
point = NearestRestaurantToEvent.objects.get(event=points[-1]).restaurants.all()[0]
points.append(point)
candidates = list(filter(lambda x: x.type in allowed_types, map(lambda x: x.event, point.nearestrestauranttoevent_set.all()[0:100])))
if not len(candidates):
candidates = list(map(lambda x: x.event, point.nearestrestauranttoevent_set.all()[0:100]))
path.append(generate_restaurant(points[-1]))
start_time += timedelta(seconds=path[-1]['time'])
how_many_eat += 1
print("start rest event gen")
continue
if start_time.hour > 17:
allowed_types = ['play', 'concert', 'movie']
print("start events gen")
if candidates is None:
candidates = NearestEvent.objects.get(event=points[-1]).nearest.filter(type__in=allowed_types)
if not len(candidates):
candidates = NearestEvent.objects.get(event=points[-1]).nearest.all()
print("end events gen")
print("start events select")
try:
print(points)
points.append(get_nearest_favorite(candidates, user, points + disallowed_points))
except AttributeError:
points.append(get_nearest_favorite(candidates, user, points))
print("end events select")
print("start route gen")
transition_route = generate_route(points[-1], points[-2])
start_time += transition_route["time"]
start_time += timedelta(seconds=transition_route["time"])
point_route = generate_point(points[-1])
start_time += point_route["time"]
start_time += timedelta(seconds=point_route["time"])
path.extend([transition_route, point_route])
return hotel, points, path
candidates = None
print("end route gen")
return points, path
def calculate_distance(sample1: Event, samples: Iterable[Event], model: AnnoyIndex, rev_mapping):