mirror of
https://github.com/task-17-lct/backend.git
synced 2024-11-23 21:33:45 +03:00
Merge branch 'onboarding-reg'
This commit is contained in:
commit
5115deef66
|
@ -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 = [
|
||||||
|
|
|
@ -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])
|
|
@ -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):
|
||||||
|
|
|
@ -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()
|
|
@ -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)
|
||||||
|
|
|
@ -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"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
|
@ -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')
|
||||||
|
|
Binary file not shown.
|
@ -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)
|
|
@ -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')
|
||||||
|
|
|
@ -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)
|
Loading…
Reference in New Issue
Block a user