Merge pull request #2 from Alexander-D-Karpov/personal-recommendation

add personal recommendations
This commit is contained in:
Ilia vasilenko 2023-05-21 17:14:49 +03:00 committed by GitHub
commit 9c2beb8cd7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 214 additions and 7 deletions

View File

@ -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

View File

@ -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])

View File

@ -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)

View File

@ -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",
),
]

View File

@ -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')

View File

@ -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)