Merge branch 'onboarding-reg'

This commit is contained in:
ilia 2023-05-24 18:00:42 +03:00
commit 5115deef66
13 changed files with 201 additions and 13 deletions

View File

@ -1,6 +1,6 @@
from django.urls import path, include
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 (
UserViewSet,
CreateUserPreferenceApiView,
@ -12,6 +12,7 @@
router.register("tinder", TinderView)
router.register("recommendations", PersonalRecommendation)
router.register("user", UserViewSet)
router.register('onboarding', OnboardingViewset)
app_name = "api"
urlpatterns = [

BIN
mus.ann

Binary file not shown.

View File

@ -1,3 +1,4 @@
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 Meta:
model = HotelPhone
exclude = "hotel"
exclude = ("hotel", )
class HotelSerializer(serializers.ModelSerializer):
@ -24,7 +24,7 @@ class MuseumSerializer(serializers.ModelSerializer):
class Meta:
model = Hotel
exclude = "oid"
exclude = ("oid", )
class EventSerializer(serializers.ModelSerializer):

View File

@ -1,7 +1,26 @@
from rest_framework import serializers
from passfinder.events.api.serializers import EventSerializer
from passfinder.events.api.serializers import EventSerializer, HotelSerializer
class TinderProceedSerializer(serializers.Serializer):
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.response import Response
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 rest_framework.decorators import action
from rest_framework.response import Response
from .serializers import TinderProceedSerializer
from .serializers import *
from passfinder.recomendations.models import UserPreferences
from ..service.service import update_preferences_state, get_next_tinder, get_personal_concerts_recommendation, \
get_personal_plays_recommendation, get_personal_movies_recommendation
from ..service.service import *
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={'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):
serializer_class = EventSerializer
@ -65,4 +72,45 @@ def movies(self, request, *args, **kwargs):
ans = []
for rec in recs:
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')
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):
event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name='nearest_model_rel')

View File

@ -5,12 +5,14 @@
plays_mapping = None
excursion_mapping = None
concert_mapping = None
mus_mapping = None
rev_attraction_mapping = None
rev_cinema_mapping = None
rev_plays_mapping = None
rev_excursion_mapping = None
rev_concert_mapping = None
mus_rev_mapping = None
def build_dict(list_mapping):
@ -53,4 +55,10 @@ def build_rev_dict(list_mapping):
with open('passfinder/recomendations/service/mapping/concerts.pickle', 'rb') as file:
lst = pickle.load(file)
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.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 passfinder.events.models import Event, Region, Hotel, BasePoint, City
from passfinder.recomendations.models import UserPreferences, NearestEvent, NearestHotel
from random import choice
from random import choice, sample
from collections import Counter
from passfinder.users.models import User
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):
return get_nearest_(
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)
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):
@ -312,6 +327,12 @@ def calculate_favorite_metric(event: Event, user: User):
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 1000000
@ -395,3 +416,40 @@ def generate_path(region: Region, user: User):
path.extend([transition_route, point_route])
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)