|
|
|
@ -105,4 +105,109 @@ class SearchView(ListView):
|
|
|
|
|
'current_page': current_page,
|
|
|
|
|
'left_has_more': left_has_more,
|
|
|
|
|
'right_has_more': right_has_more
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 推荐电影视图
|
|
|
|
|
class RecommendMovieView(ListView):
|
|
|
|
|
model = Movie
|
|
|
|
|
template_name = 'movie/recommend.html'
|
|
|
|
|
paginate_by = 15
|
|
|
|
|
context_object_name = 'movies'
|
|
|
|
|
ordering = 'movie_rating__score'
|
|
|
|
|
page_kwarg = 'p'
|
|
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
|
super().__init__()
|
|
|
|
|
# 最相似的20个用户
|
|
|
|
|
self.K = 20
|
|
|
|
|
# 推荐出10本书
|
|
|
|
|
self.N = 10
|
|
|
|
|
# 存放当前用户评分过的电影querySet
|
|
|
|
|
self.cur_user_movie_qs = None
|
|
|
|
|
|
|
|
|
|
# 获取用户相似度
|
|
|
|
|
def get_user_sim(self):
|
|
|
|
|
# 用户相似度字典,格式为{ user_id1:val , user_id2:val , ... }
|
|
|
|
|
user_sim_dct = dict()
|
|
|
|
|
'''获取用户之间的相似度,存放在user_sim_dct中'''
|
|
|
|
|
# 获取当前用户
|
|
|
|
|
cur_user_id = self.request.session['user_id']
|
|
|
|
|
cur_user = User.objects.get(pk=cur_user_id)
|
|
|
|
|
# 获取其它用户
|
|
|
|
|
other_users = User.objects.exclude(pk=cur_user_id) # 除了当前用户外的所有用户
|
|
|
|
|
|
|
|
|
|
# 当前用户评分过的电影
|
|
|
|
|
self.cur_user_movie_qs = Movie.objects.filter(user=cur_user)
|
|
|
|
|
|
|
|
|
|
# 计算当前用户与其他用户共同评分过的电影交集数
|
|
|
|
|
for user in other_users:
|
|
|
|
|
# 记录感兴趣的数量
|
|
|
|
|
user_sim_dct[user.id] = len(Movie.objects.filter(user=user) & self.cur_user_movie_qs)
|
|
|
|
|
|
|
|
|
|
# 按照key排序value,返回K个最相近的用户(共同评分过的电影交集数更多)
|
|
|
|
|
print("user similarity calculated!")
|
|
|
|
|
# 格式 [ (user, value), (user, value), ... ]
|
|
|
|
|
return sorted(user_sim_dct.items(), key=lambda x: -x[1])[:self.K]
|
|
|
|
|
|
|
|
|
|
# 获取推荐电影(按照相似用户总得分排序)
|
|
|
|
|
def get_recommend_movie(self, user_lst):
|
|
|
|
|
# 电影兴趣值字典,{ movie:value, movie:value , ...}
|
|
|
|
|
movie_val_dct = dict()
|
|
|
|
|
# 用户,相似度
|
|
|
|
|
for user, _ in user_lst:
|
|
|
|
|
# 获取相似用户评分过的电影,并且不在前用户的评分列表中的,再加上score字段,方便计算兴趣值
|
|
|
|
|
movie_set = Movie.objects.filter(user=user).exclude(id__in=self.cur_user_movie_qs).annotate(
|
|
|
|
|
score=Max('movie_rating__score'))
|
|
|
|
|
for movie in movie_set:
|
|
|
|
|
movie_val_dct.setdefault(movie, 0)
|
|
|
|
|
# 累计用户的评分
|
|
|
|
|
movie_val_dct[movie] += movie.score
|
|
|
|
|
return sorted(movie_val_dct.items(), key=lambda x: -x[1])[:self.N]
|
|
|
|
|
|
|
|
|
|
# 获取数据
|
|
|
|
|
def get_queryset(self):
|
|
|
|
|
s = time.time()
|
|
|
|
|
# 获得最相似的K个用户列表
|
|
|
|
|
user_lst = self.get_user_sim()
|
|
|
|
|
# 获得推荐电影的id
|
|
|
|
|
movie_lst = self.get_recommend_movie(user_lst)
|
|
|
|
|
# print(movie_lst)
|
|
|
|
|
result_lst = []
|
|
|
|
|
for movie, _ in movie_lst:
|
|
|
|
|
result_lst.append(movie)
|
|
|
|
|
e = time.time()
|
|
|
|
|
print(f"算法推荐用时:{e - s}秒!")
|
|
|
|
|
return result_lst
|
|
|
|
|
|
|
|
|
|
def get_context_data(self, *, object_list=None, **kwargs):
|
|
|
|
|
context = super(RecommendMovieView, self).get_context_data(*kwargs)
|
|
|
|
|
print(context)
|
|
|
|
|
paginator = context.get('paginator')
|
|
|
|
|
page_obj = context.get('page_obj')
|
|
|
|
|
pagination_data = self.get_pagination_data(paginator, page_obj)
|
|
|
|
|
context.update(pagination_data)
|
|
|
|
|
return context
|
|
|
|
|
|
|
|
|
|
def get_pagination_data(self, paginator, page_obj, around_count=2):
|
|
|
|
|
current_page = page_obj.number
|
|
|
|
|
|
|
|
|
|
if current_page <= around_count + 2:
|
|
|
|
|
left_pages = range(1, current_page)
|
|
|
|
|
left_has_more = False
|
|
|
|
|
else:
|
|
|
|
|
left_pages = range(current_page - around_count, current_page)
|
|
|
|
|
left_has_more = True
|
|
|
|
|
|
|
|
|
|
if current_page >= paginator.num_pages - around_count - 1:
|
|
|
|
|
right_pages = range(current_page + 1, paginator.num_pages + 1)
|
|
|
|
|
right_has_more = False
|
|
|
|
|
else:
|
|
|
|
|
right_pages = range(current_page + 1, current_page + 1 + around_count)
|
|
|
|
|
right_has_more = True
|
|
|
|
|
return {
|
|
|
|
|
'left_pages': left_pages,
|
|
|
|
|
'right_pages': right_pages,
|
|
|
|
|
'current_page': current_page,
|
|
|
|
|
'left_has_more': left_has_more,
|
|
|
|
|
'right_has_more': right_has_more
|
|
|
|
|
}
|
|
|
|
|