mirror of
https://github.com/task-17-lct/backend.git
synced 2024-11-27 18:13:45 +03:00
Merge pull request #2 from Alexander-D-Karpov/personal-recommendation
add personal recommendations
This commit is contained in:
commit
9c2beb8cd7
|
@ -1,10 +1,11 @@
|
||||||
from rest_framework.routers import DefaultRouter
|
from rest_framework.routers import DefaultRouter
|
||||||
from passfinder.recomendations.api.views import TinderView
|
from passfinder.recomendations.api.views import TinderView, PersonalRecommendation
|
||||||
|
|
||||||
|
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
|
|
||||||
router.register('tinder', TinderView)
|
router.register('tinder', TinderView)
|
||||||
|
router.register("recommendations", PersonalRecommendation)
|
||||||
|
|
||||||
app_name = "api"
|
app_name = "api"
|
||||||
urlpatterns = router.urls
|
urlpatterns = router.urls
|
|
@ -1,3 +1,4 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
from .models import UserPreferences
|
||||||
|
|
||||||
# Register your models here.
|
admin.site.register([UserPreferences])
|
|
@ -8,6 +8,11 @@
|
||||||
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 TinderProceedSerializer
|
||||||
|
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 django.views.decorators.csrf import csrf_exempt
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class TinderView(viewsets.GenericViewSet):
|
class TinderView(viewsets.GenericViewSet):
|
||||||
|
@ -17,10 +22,47 @@ class TinderView(viewsets.GenericViewSet):
|
||||||
|
|
||||||
@action(methods=['GET'], detail=False, serializer_class=EventSerializer)
|
@action(methods=['GET'], detail=False, serializer_class=EventSerializer)
|
||||||
def start(self, request: Request, *args: Any, **kwargs: Any):
|
def start(self, request: Request, *args: Any, **kwargs: Any):
|
||||||
|
UserPreferences.objects.get_or_create(user=request.user)
|
||||||
event = EventSerializer(choice(Event.objects.all()))
|
event = EventSerializer(choice(Event.objects.all()))
|
||||||
return Response(data=event.data, status=200)
|
return Response(data=event.data, status=200)
|
||||||
|
|
||||||
|
@csrf_exempt
|
||||||
@action(methods=['POST'], detail=True, serializer_class=TinderProceedSerializer)
|
@action(methods=['POST'], detail=True, serializer_class=TinderProceedSerializer)
|
||||||
def proceed(self, request: Request, *args: Any, **kwargs: Any):
|
def proceed(self, request: Request, pk):
|
||||||
event = EventSerializer(choice(Event.objects.all()))
|
update_preferences_state(request.user, Event.objects.get(oid=pk), request.data['action'])
|
||||||
return Response(data={'event': event.data}, status=200)
|
event = get_next_tinder(request.user, Event.objects.get(oid=pk), request.data['action'])
|
||||||
|
if event is None:
|
||||||
|
return Response(data={}, status=404)
|
||||||
|
return Response(data={'event': EventSerializer(event).data}, status=200)
|
||||||
|
|
||||||
|
|
||||||
|
class PersonalRecommendation(viewsets.GenericViewSet):
|
||||||
|
serializer_class = EventSerializer
|
||||||
|
model = Event
|
||||||
|
queryset = Event.objects.all()
|
||||||
|
|
||||||
|
@action(methods=['GET'], detail=False)
|
||||||
|
def plays(self, request, *args, **kwargs):
|
||||||
|
recs = get_personal_plays_recommendation(request.user)
|
||||||
|
ans = []
|
||||||
|
for rec in recs:
|
||||||
|
ans.append(EventSerializer(rec[1]).data)
|
||||||
|
return Response(ans, 200)
|
||||||
|
|
||||||
|
|
||||||
|
@action(methods=['GET'], detail=False)
|
||||||
|
def concerts(self, request, *args, **kwargs):
|
||||||
|
recs = get_personal_concerts_recommendation(request.user)
|
||||||
|
ans = []
|
||||||
|
for rec in recs:
|
||||||
|
ans.append(EventSerializer(rec[1]).data)
|
||||||
|
return Response(ans, 200)
|
||||||
|
|
||||||
|
|
||||||
|
@action(methods=['GET'], detail=False)
|
||||||
|
def movies(self, request, *args, **kwargs):
|
||||||
|
recs = get_personal_movies_recommendation(request.user)
|
||||||
|
ans = []
|
||||||
|
for rec in recs:
|
||||||
|
ans.append(EventSerializer(rec[1]).data)
|
||||||
|
return Response(ans, 200)
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 4.2.1 on 2023-05-21 10:58
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("recomendations", "0001_initial"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name="userpreferences",
|
||||||
|
old_name="unpreffered_lays",
|
||||||
|
new_name="unpreffered_plays",
|
||||||
|
),
|
||||||
|
]
|
|
@ -7,7 +7,7 @@ class UserPreferences(models.Model):
|
||||||
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
|
|
||||||
preffered_plays = models.ManyToManyField(Event, related_name='preffered_user_play')
|
preffered_plays = models.ManyToManyField(Event, related_name='preffered_user_play')
|
||||||
unpreffered_lays = models.ManyToManyField(Event, related_name='unpreffered_users_play')
|
unpreffered_plays = models.ManyToManyField(Event, related_name='unpreffered_users_play')
|
||||||
|
|
||||||
preffered_movies = models.ManyToManyField(Event, related_name='preffered_user_movie')
|
preffered_movies = models.ManyToManyField(Event, related_name='preffered_user_movie')
|
||||||
unpreffered_movies = models.ManyToManyField(Event, related_name='unpreffered_user_movie')
|
unpreffered_movies = models.ManyToManyField(Event, related_name='unpreffered_user_movie')
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
from .mapping.mapping import *
|
from .mapping.mapping import *
|
||||||
from .models.models import *
|
from .models.models import *
|
||||||
from passfinder.events.models import Event
|
from passfinder.events.models import Event
|
||||||
|
from passfinder.recomendations.models import UserPreferences
|
||||||
|
from random import choice
|
||||||
|
from collections import Counter
|
||||||
|
|
||||||
|
|
||||||
def get_nearest_(instance_model, model_type, mapping, nearest_n, ml_model):
|
def get_nearest_(instance_model, model_type, mapping, nearest_n, ml_model):
|
||||||
|
@ -28,7 +31,7 @@ def nearest_movie(movie, nearest_n):
|
||||||
|
|
||||||
|
|
||||||
def nearest_plays(play, nearest_n):
|
def nearest_plays(play, nearest_n):
|
||||||
return get_nearest_(play, 'play', plays_mapping, nearest_n, plays_model)
|
return get_nearest_(play, 'plays', plays_mapping, nearest_n, plays_model)
|
||||||
|
|
||||||
|
|
||||||
def nearest_excursion(excursion, nearest_n):
|
def nearest_excursion(excursion, nearest_n):
|
||||||
|
@ -37,3 +40,145 @@ def nearest_excursion(excursion, nearest_n):
|
||||||
|
|
||||||
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, nearest_n, concert_model)
|
||||||
|
|
||||||
|
|
||||||
|
def get_nearest_event(event, nearest_n):
|
||||||
|
if event.type == 'plays':
|
||||||
|
return nearest_plays(event, nearest_n)
|
||||||
|
if event.type == 'concert':
|
||||||
|
return nearest_concert(event, nearest_n)
|
||||||
|
if event.type == 'movie':
|
||||||
|
return nearest_movie(event, nearest_n)
|
||||||
|
|
||||||
|
|
||||||
|
def update_preferences_state(user, event, direction):
|
||||||
|
pref = UserPreferences.objects.get(user=user)
|
||||||
|
|
||||||
|
if direction == 'left':
|
||||||
|
if event.type == 'plays':
|
||||||
|
pref.unpreffered_plays.add(event)
|
||||||
|
if event.type == 'movie':
|
||||||
|
pref.unpreffered_movies.add(event)
|
||||||
|
if event.type == 'concert':
|
||||||
|
pref.unpreferred_concerts.add(event)
|
||||||
|
else:
|
||||||
|
if event.type == 'plays':
|
||||||
|
pref.preffered_plays.add(event)
|
||||||
|
if event.type == 'movie':
|
||||||
|
pref.preffered_movies.add(event)
|
||||||
|
if event.type == 'concert':
|
||||||
|
pref.preferred_concerts.add(event)
|
||||||
|
pref.save()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def get_next_tinder(user, prev_event, prev_direction):
|
||||||
|
pref = UserPreferences.objects.get(user=user)
|
||||||
|
print(prev_event.type, len(pref.preferred_concerts.all()))
|
||||||
|
if prev_direction == 'left':
|
||||||
|
if prev_event.type == 'plays' and len(pref.unpreffered_plays.all()) <= 2:
|
||||||
|
candidates = nearest_plays(prev_event, 100)
|
||||||
|
# print(candidates, type(candidates), len(Event.objects.filter(type='plays')))
|
||||||
|
return candidates[-1]
|
||||||
|
if prev_event.type == 'movie' and len(pref.unpreffered_movies.all()) <= 2:
|
||||||
|
candidates = nearest_movie(prev_event, 100)
|
||||||
|
return candidates[-1]
|
||||||
|
if prev_event.type == 'concert' and len(pref.unpreferred_concerts.all()) <= 2:
|
||||||
|
candidates = nearest_concert(prev_event, 100)
|
||||||
|
return candidates[-1]
|
||||||
|
|
||||||
|
if prev_direction == 'right':
|
||||||
|
if prev_event.type == 'plays' and len(pref.preffered_plays.all()) < 2:
|
||||||
|
candidates = nearest_plays(prev_event, 2)
|
||||||
|
return candidates[1]
|
||||||
|
if prev_event.type == 'movie' and len(pref.preffered_movies.all()) < 2:
|
||||||
|
candidates = nearest_movie(prev_event, 2)
|
||||||
|
return candidates[1]
|
||||||
|
if prev_event.type == 'concert' and len(pref.preferred_concerts.all()) < 2:
|
||||||
|
candidates = nearest_concert(prev_event, 2)
|
||||||
|
return candidates[1]
|
||||||
|
|
||||||
|
if prev_event.type == 'plays':
|
||||||
|
if not len(pref.preffered_movies.all()) and not len(pref.unpreffered_movies.all()):
|
||||||
|
return choice(Event.objects.filter(type='movie'))
|
||||||
|
if not len(pref.preferred_concerts.all()) and not len(pref.unpreferred_concerts.all()):
|
||||||
|
return choice(Event.objects.filter(type='concert'))
|
||||||
|
|
||||||
|
if prev_event.type == 'movie':
|
||||||
|
if not len(pref.preffered_plays.all()) and not len(pref.unpreffered_plays.all()):
|
||||||
|
return choice(Event.objects.filter(type='plays'))
|
||||||
|
if not len(pref.preferred_concerts.all()) and not len(pref.unpreferred_concerts.all()):
|
||||||
|
return choice(Event.objects.filter(type='concert'))
|
||||||
|
|
||||||
|
if prev_event.type == 'concert':
|
||||||
|
if not len(pref.preffered_plays.all()) and not len(pref.unpreffered_plays.all()):
|
||||||
|
return choice(Event.objects.filter(type='plays'))
|
||||||
|
if not len(pref.preffered_movies.all()) and not len(pref.unpreffered_movies.all()):
|
||||||
|
return choice(Event.objects.filter(type='movie'))
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def rank_candidates(candidates_list, negative_candidates_list):
|
||||||
|
flatten_c_list = []
|
||||||
|
ranks = {}
|
||||||
|
|
||||||
|
flatten_negatives = []
|
||||||
|
|
||||||
|
for negative in negative_candidates_list:
|
||||||
|
flatten_negatives.extend(negative)
|
||||||
|
|
||||||
|
for lst in candidates_list:
|
||||||
|
flatten_c_list.extend(lst)
|
||||||
|
for cand in lst:
|
||||||
|
ranks.update({cand: {'rank': 0, 'lst': lst}})
|
||||||
|
|
||||||
|
cnt = Counter(flatten_c_list)
|
||||||
|
|
||||||
|
for candidate, how_many in cnt.most_common(len(flatten_c_list)):
|
||||||
|
ranks[candidate]['rank'] = how_many * (len(ranks[candidate]['lst']) - ranks[candidate]['lst'].index(candidate))
|
||||||
|
|
||||||
|
res = []
|
||||||
|
for cand in ranks.keys():
|
||||||
|
res.append((ranks[cand]['rank'], cand))
|
||||||
|
return list(filter(lambda x: x[1] not in flatten_negatives, sorted(res, key=lambda x: -x[0])))
|
||||||
|
|
||||||
|
|
||||||
|
def get_personal_recommendation(prefer, unprefer):
|
||||||
|
candidates = []
|
||||||
|
negative_candidates = []
|
||||||
|
|
||||||
|
for rec in prefer:
|
||||||
|
candidates.append(list(map(lambda x: x.oid, get_nearest_event(rec, 10)[1:])))
|
||||||
|
|
||||||
|
for neg in unprefer:
|
||||||
|
negative_candidates.append(list(map(lambda x: x.oid, get_nearest_event(neg, 10)[1:])))
|
||||||
|
|
||||||
|
ranked = rank_candidates(candidates, negative_candidates)
|
||||||
|
|
||||||
|
return list(map(lambda x: (x[0], Event.objects.get(oid=x[1])), ranked[0:5]))
|
||||||
|
|
||||||
|
|
||||||
|
def get_personal_plays_recommendation(user):
|
||||||
|
pref = UserPreferences.objects.get(user=user)
|
||||||
|
|
||||||
|
prefer = pref.preffered_plays.all()
|
||||||
|
unprefer = pref.unpreffered_plays.all()
|
||||||
|
return get_personal_recommendation(prefer, unprefer)
|
||||||
|
|
||||||
|
|
||||||
|
def get_personal_concerts_recommendation(user):
|
||||||
|
pref = UserPreferences.objects.get(user=user)
|
||||||
|
|
||||||
|
prefer = pref.preferred_concerts.all()
|
||||||
|
unprefer = pref.unpreferred_concerts.all()
|
||||||
|
return get_personal_recommendation(prefer, unprefer)
|
||||||
|
|
||||||
|
|
||||||
|
def get_personal_movies_recommendation(user):
|
||||||
|
pref = UserPreferences.objects.get(user=user)
|
||||||
|
|
||||||
|
prefer = pref.preffered_movies.all()
|
||||||
|
unprefer = pref.unpreffered_movies.all()
|
||||||
|
return get_personal_recommendation(prefer, unprefer)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user