add museums and attractions

This commit is contained in:
ilia 2023-05-24 17:58:51 +03:00
parent 08bc90142b
commit 6b6348ffe1
14 changed files with 201 additions and 13 deletions

View File

@ -1,6 +1,6 @@
from django.urls import path, include from django.urls import path, include
from rest_framework.routers import DefaultRouter from rest_framework.routers import DefaultRouter
from passfinder.recomendations.api.views import TinderView, PersonalRecommendation from passfinder.recomendations.api.views import TinderView, PersonalRecommendation, OnboardingViewset
from passfinder.users.api.views import ( from passfinder.users.api.views import (
UserViewSet, UserViewSet,
CreateUserPreferenceApiView, CreateUserPreferenceApiView,
@ -12,6 +12,7 @@
router.register("tinder", TinderView) router.register("tinder", TinderView)
router.register("recommendations", PersonalRecommendation) router.register("recommendations", PersonalRecommendation)
router.register("user", UserViewSet) router.register("user", UserViewSet)
router.register('onboarding', OnboardingViewset)
app_name = "api" app_name = "api"
urlpatterns = [ urlpatterns = [

View File

@ -1,3 +1,4 @@
from django.contrib import admin from django.contrib import admin
from .models import Hotel
# Register your models here. admin.site.register([Hotel])

View File

@ -7,7 +7,7 @@
class HotelPhoneSerializer(serializers.ModelSerializer): class HotelPhoneSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = HotelPhone model = HotelPhone
exclude = "hotel" exclude = ("hotel", )
class HotelSerializer(serializers.ModelSerializer): class HotelSerializer(serializers.ModelSerializer):
@ -24,7 +24,7 @@ class MuseumSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Hotel model = Hotel
exclude = "oid" exclude = ("oid", )
class EventSerializer(serializers.ModelSerializer): class EventSerializer(serializers.ModelSerializer):

View File

@ -1,7 +1,26 @@
from rest_framework import serializers from rest_framework import serializers
from passfinder.events.api.serializers import EventSerializer from passfinder.events.api.serializers import EventSerializer, HotelSerializer
class TinderProceedSerializer(serializers.Serializer): class TinderProceedSerializer(serializers.Serializer):
action = serializers.ChoiceField(['left', 'right'], write_only=True) action = serializers.ChoiceField(['left', 'right'], write_only=True)
event = EventSerializer(read_only=True) event = EventSerializer(read_only=True)
class AddToPreferenceSerializer(serializers.Serializer):
oid = serializers.CharField(write_only=True)
class EventOnboardingRetrieve(serializers.Serializer):
events = serializers.ListField(child=EventSerializer(), read_only=True)
types = serializers.ListField(child=serializers.ChoiceField(['park', 'monument', 'museum', 'unseco']), write_only=True)
class HotelOnboardingRetrieve(serializers.Serializer):
stars = serializers.ListField(child=serializers.ChoiceField([1, 2, 3, 4, 5]), write_only=True)
hotels = serializers.ListField(child=HotelSerializer(), read_only=True)
class TinderGetEventFilterSerializer(serializers.Serializer):
type = serializers.ListField(child=serializers.ChoiceField(['attraction', 'museum', 'movie', 'play', 'concert']))
event = EventSerializer()

View File

@ -3,14 +3,13 @@
from rest_framework.request import Request from rest_framework.request import Request
from rest_framework.response import Response from rest_framework.response import Response
from passfinder.events.models import Event from passfinder.events.models import Event
from passfinder.events.api.serializers import EventSerializer from passfinder.events.api.serializers import EventSerializer, HotelSerializer
from random import choice from random import choice
from rest_framework.decorators import action from rest_framework.decorators import action
from rest_framework.response import Response from rest_framework.response import Response
from .serializers import TinderProceedSerializer from .serializers import *
from passfinder.recomendations.models import UserPreferences from passfinder.recomendations.models import UserPreferences
from ..service.service import update_preferences_state, get_next_tinder, get_personal_concerts_recommendation, \ from ..service.service import *
get_personal_plays_recommendation, get_personal_movies_recommendation
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
@ -35,6 +34,14 @@ def proceed(self, request: Request, pk):
return Response(data={}, status=404) return Response(data={}, status=404)
return Response(data={'event': EventSerializer(event).data}, status=200) return Response(data={'event': EventSerializer(event).data}, status=200)
@action(methods=['POST'], detail=False, serializer_class=TinderGetEventFilterSerializer)
def get_event(self, request: Request):
# отдавать под пользователя
events = Event.objects.filter(type__in=request.data['type'])
return Response(data={
'event': EventSerializer(choice(events)).data
}, status=200)
class PersonalRecommendation(viewsets.GenericViewSet): class PersonalRecommendation(viewsets.GenericViewSet):
serializer_class = EventSerializer serializer_class = EventSerializer
@ -66,3 +73,44 @@ def movies(self, request, *args, **kwargs):
for rec in recs: for rec in recs:
ans.append(EventSerializer(rec[1]).data) ans.append(EventSerializer(rec[1]).data)
return Response(ans, 200) return Response(ans, 200)
class OnboardingViewset(viewsets.GenericViewSet):
serializer_class = EventSerializer
model = Event
queryset = Event.objects.all()
@action(methods=['POST'], detail=False, serializer_class=HotelOnboardingRetrieve)
def hotels(self, reqeust, *args, **kwargs):
hotels = get_onboarding_hotels(reqeust.data['stars'])
res = HotelOnboardingRetrieve({'hotels': hotels}).data
return Response(res, 200)
@action(methods=['POST'], detail=False, serializer_class=EventOnboardingRetrieve)
def event(self, request, *args, **kwargs):
events = get_onboarding_attractions()
res = EventOnboardingRetrieve({'events': events}).data
return Response(res, 200)
@action(methods=['GET'], detail=True)
def add_to_favorites(self, request, pk, *args, **kwargs):
pref, _ = UserPreferences.objects.get_or_create(user=request.user)
event = Event.objects.get(oid=pk)
if event.type == 'attraction':
pref.prefferred_attractions.add(event)
elif event.type == 'museum':
pref.prefferred_museums.add(event)
elif event.type == 'movie':
pref.preffered_movies.add(event)
elif event.type == 'play':
pref.preffered_plays.add(event)
elif event.type == 'concert':
pref.preferred_concerts.add(event)
pref.save()
return Response(status=200)

View File

@ -0,0 +1,42 @@
# Generated by Django 4.2.1 on 2023-05-24 10:09
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("events", "0018_userroute_baseuserroutepoint_userroutetransaction_and_more"),
("recomendations", "0004_nearesthotel"),
]
operations = [
migrations.AddField(
model_name="userpreferences",
name="prefferred_attractions",
field=models.ManyToManyField(
related_name="preffered_users_attractions", to="events.event"
),
),
migrations.AddField(
model_name="userpreferences",
name="prefferred_museums",
field=models.ManyToManyField(
related_name="preffered_users_museums", to="events.event"
),
),
migrations.AddField(
model_name="userpreferences",
name="unprefferred_attractions",
field=models.ManyToManyField(
related_name="unpreffered_users_attractions", to="events.event"
),
),
migrations.AddField(
model_name="userpreferences",
name="unprefferred_museums",
field=models.ManyToManyField(
related_name="unpreffered_users_museums", to="events.event"
),
),
]

View File

@ -15,6 +15,13 @@ class UserPreferences(models.Model):
preferred_concerts = models.ManyToManyField(Event, related_name='preffered_users_concert') preferred_concerts = models.ManyToManyField(Event, related_name='preffered_users_concert')
unpreferred_concerts = models.ManyToManyField(Event, related_name='unpreffered_users_concert') unpreferred_concerts = models.ManyToManyField(Event, related_name='unpreffered_users_concert')
prefferred_attractions = models.ManyToManyField(Event, related_name='preffered_users_attractions')
unprefferred_attractions = models.ManyToManyField(Event, related_name='unpreffered_users_attractions')
prefferred_museums = models.ManyToManyField(Event, related_name='preffered_users_museums')
unprefferred_museums = models.ManyToManyField(Event, related_name='unpreffered_users_museums')
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')

View File

@ -5,12 +5,14 @@
plays_mapping = None plays_mapping = None
excursion_mapping = None excursion_mapping = None
concert_mapping = None concert_mapping = None
mus_mapping = None
rev_attraction_mapping = None rev_attraction_mapping = None
rev_cinema_mapping = None rev_cinema_mapping = None
rev_plays_mapping = None rev_plays_mapping = None
rev_excursion_mapping = None rev_excursion_mapping = None
rev_concert_mapping = None rev_concert_mapping = None
mus_rev_mapping = None
def build_dict(list_mapping): def build_dict(list_mapping):
@ -54,3 +56,9 @@ def build_rev_dict(list_mapping):
lst = pickle.load(file) lst = pickle.load(file)
concert_mapping = build_dict(lst) concert_mapping = build_dict(lst)
rev_concert_mapping = build_rev_dict(lst) rev_concert_mapping = build_rev_dict(lst)
with open('passfinder/recomendations/service/mapping/mus.pickle', 'rb') as file:
lst = pickle.load(file)
mus_mapping = build_dict(lst)
rev_mus_mapping = build_rev_dict(lst)

View File

@ -20,3 +20,7 @@
concert_model = AnnoyIndex(N_DIMENSIONAL, 'angular') concert_model = AnnoyIndex(N_DIMENSIONAL, 'angular')
concert_model.load('passfinder/recomendations/service/models/concerts.ann') concert_model.load('passfinder/recomendations/service/models/concerts.ann')
mus_model = AnnoyIndex(N_DIMENSIONAL, 'angular')
mus_model.load('passfinder/recomendations/service/models/mus.ann')

View File

@ -3,7 +3,7 @@
from .models.models import * from .models.models import *
from passfinder.events.models import Event, Region, Hotel, BasePoint, City from passfinder.events.models import Event, Region, Hotel, BasePoint, City
from passfinder.recomendations.models import UserPreferences, NearestEvent, NearestHotel from passfinder.recomendations.models import UserPreferences, NearestEvent, NearestHotel
from random import choice from random import choice, sample
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
@ -40,6 +40,17 @@ def nearest_attraction(attraction, nearest_n):
) )
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): def nearest_movie(movie, nearest_n):
return get_nearest_( return get_nearest_(
movie, "movie", cinema_mapping, rev_cinema_mapping, nearest_n, cinema_model movie, "movie", cinema_mapping, rev_cinema_mapping, nearest_n, cinema_model
@ -81,6 +92,10 @@ def get_nearest_event(event, nearest_n):
return nearest_concert(event, nearest_n) return nearest_concert(event, nearest_n)
if event.type == "movie": if event.type == "movie":
return nearest_movie(event, nearest_n) 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): def update_preferences_state(user, event, direction):
@ -312,6 +327,12 @@ def calculate_favorite_metric(event: Event, user: User):
if event.type == "movie": if event.type == "movie":
preferred = pref.preffered_movies.all() preferred = pref.preffered_movies.all()
return calculate_mean_metric(preferred, event, cinema_model, rev_cinema_mapping) 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 1000000 return 1000000
@ -395,3 +416,40 @@ def generate_path(region: Region, user: User):
path.extend([transition_route, point_route]) path.extend([transition_route, point_route])
return hotel, points, path return hotel, points, path
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)