mirror of
https://github.com/task-17-lct/backend.git
synced 2024-12-03 22:13:43 +03:00
Merge pull request #2 from Alexander-D-Karpov/personal-recommendation
add personal recommendations
This commit is contained in:
commit
e08ff38976
|
@ -1,10 +1,11 @@
|
|||
from rest_framework.routers import DefaultRouter
|
||||
from passfinder.recomendations.api.views import TinderView
|
||||
from passfinder.recomendations.api.views import TinderView, PersonalRecommendation
|
||||
|
||||
|
||||
router = DefaultRouter()
|
||||
|
||||
router.register('tinder', TinderView)
|
||||
router.register("recommendations", PersonalRecommendation)
|
||||
|
||||
app_name = "api"
|
||||
urlpatterns = router.urls
|
|
@ -1,3 +1,4 @@
|
|||
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.response import Response
|
||||
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):
|
||||
|
@ -17,10 +22,47 @@ class TinderView(viewsets.GenericViewSet):
|
|||
|
||||
@action(methods=['GET'], detail=False, serializer_class=EventSerializer)
|
||||
def start(self, request: Request, *args: Any, **kwargs: Any):
|
||||
UserPreferences.objects.get_or_create(user=request.user)
|
||||
event = EventSerializer(choice(Event.objects.all()))
|
||||
return Response(data=event.data, status=200)
|
||||
|
||||
@csrf_exempt
|
||||
@action(methods=['POST'], detail=True, serializer_class=TinderProceedSerializer)
|
||||
def proceed(self, request: Request, *args: Any, **kwargs: Any):
|
||||
event = EventSerializer(choice(Event.objects.all()))
|
||||
return Response(data={'event': event.data}, status=200)
|
||||
def proceed(self, request: Request, pk):
|
||||
update_preferences_state(request.user, Event.objects.get(oid=pk), request.data['action'])
|
||||
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)
|
||||
|
||||
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')
|
||||
unpreffered_movies = models.ManyToManyField(Event, related_name='unpreffered_user_movie')
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
from .mapping.mapping import *
|
||||
from .models.models import *
|
||||
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):
|
||||
|
@ -28,7 +31,7 @@ def nearest_movie(movie, 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):
|
||||
|
@ -37,3 +40,145 @@ def nearest_excursion(excursion, nearest_n):
|
|||
|
||||
def nearest_concert(concert, nearest_n):
|
||||
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