class FollowRecommendationsServicer(follows_pb2_grpc.FollowsServicer): RECOMMENDERS = { 'surprise': SurpriseRecommender, 'cn': CNRecommender, 'graphdist': GraphDistanceRecommender, } DEFAULT_RECOMMENDER = 'graphdist' ENV_VAR = 'FOLLOW_RECOMMENDER_METHOD' DEFAULT_IMAGE = "https://upload.wikimedia.org/wikipedia/commons/8/89/Portrait_Placeholder.png" def __init__(self, logger, users_util, db_stub): self._logger = logger self._users_util = users_util self._db_stub = db_stub self._recommender_util = RecommendersUtil( logger, db_stub, self.DEFAULT_RECOMMENDER, self.ENV_VAR, self.RECOMMENDERS) # self.active_recommenders contains one or more recommender system # objects (out of the constructors in self.RECOMMENDERS). self.active_recommenders = self._recommender_util._get_active_recommenders() def _get_recommendations(self, user_id): '''Get recommendations for users for the given user_id to follow, using the one or more systems in self.active_recommenders. Could return empty list if there are no good recommendations.''' # TODO(iandioch): Allow for combining the results of multiple systems # in a smarter way than just concatenation. for r in self.active_recommenders: yield from r.get_recommendations(user_id) def GetFollowRecommendations(self, request, context): self._logger.debug('GetFollowRecommendations, user_id = %s', request.user_id) resp = recommend_follows_pb2.FollowRecommendationResponse() user = self._users_util.get_user_from_db(global_id=request.user_id) if user is None: resp.result_type = \ general_pb2.ResultType.ERROR resp.error = "Could not find the given user_id." return resp if not (user.host is None or user.host == ""): resp.result_type = \ general_pb2.ResultType.ERROR resp.error = "Can only give recommendations for local users." return resp resp.result_type = general_pb2.ResultType.OK # Get the recommendations and package them into proto. for p in self._get_recommendations(user.global_id): a = self._users_util.get_or_create_user_from_db(global_id=p[0]) user_obj = resp.results.add() user_obj.handle = a.handle user_obj.host = a.host user_obj.display_name = a.display_name user_obj.bio = a.bio user_obj.image = self.DEFAULT_IMAGE user_obj.global_id = a.global_id return resp def UpdateFollowRecommendations(self, request, context): self._logger.debug('UpdateFollowRecommendations, %d following %d: %s', request.follower, request.followed, request.following) resp = recommend_follows_pb2.UpdateFollowRecommendationsResponse() for r in self.active_recommenders: r.update_recommendations(request.follower, request.followed, request.following) return resp
class PostRecommendationsServicer(recommend_posts_pb2_grpc.PostRecommendationsServicer): RECOMMENDERS = { 'random': RandomRecommender, 'cosine': CosineRecommender, } DEFAULT_RECOMMENDER = 'random' ENV_VAR = 'POSTS_RECOMMENDER_METHOD' MAX_RECOMMENDATIONS = 50 DEFAULT_IMAGE = "https://upload.wikimedia.org/wikipedia/commons/8/89/Portrait_Placeholder.png" def __init__(self, users_util, logger, db_stub): self._logger = logger self._db_stub = db_stub self._users_util = users_util self._activ_util = ActivitiesUtil(logger, db_stub) self._recommender_util = RecommendersUtil( logger, db_stub, self.DEFAULT_RECOMMENDER, self.ENV_VAR, self.RECOMMENDERS) # self.active_recommenders contains one or more recommender system # objects (out of the constructors in self.RECOMMENDERS). self.active_recommenders = self._recommender_util._get_active_recommenders() def Get(self, request, context): self._logger.debug('Get PostRecommendations, user_id = %s', request.user_id) resp = recommend_posts_pb2.PostRecommendationsResponse() recommended_posts = [] post_ids = set() max_posts_per_r = self.MAX_RECOMMENDATIONS // len( self.active_recommenders) for r in self.active_recommenders: r_posts, error = r.get_recommendations( request.user_id, max_posts_per_r) if error: resp.result_type = \ recommend_posts_pb2.PostRecommendationsResponse.ERROR resp.message = error return resp recommended_posts.append(r_posts) # Join recommendations together, with the highest recommended first posts = [] for i in range(max_posts_per_r + 1): for r_p in recommended_posts: # See proto/Feed.Post and proto/database.PostsEntry if i < len(r_p): author = self._users_util.get_user_from_db( global_id=r_p[i].author_id) if author == None: resp.result_type = \ recommend_posts_pb2.PostRecommendationsResponse.ERROR resp.message = "Post Author could not be found" return resp post_obj = resp.results.add() post_obj.global_id = r_p[i].global_id post_obj.author = author.handle post_obj.author_host = author.host post_obj.author_id = r_p[i].author_id post_obj.title = r_p[i].title post_obj.body = r_p[i].body post_obj.published = self._activ_util.timestamp_to_rfc( r_p[i].creation_datetime) post_obj.likes_count = r_p[i].likes_count post_obj.bio = author.bio post_obj.image = self.DEFAULT_IMAGE post_obj.is_liked = r_p[i].is_liked post_obj.is_followed = r_p[i].is_followed post_obj.shares_count = r_p[i].shares_count post_obj.summary = r_p[i].summary tags = self._recommender_util.split_tags(r_p[i].tags) post_obj.tags.extend(tags) resp.result_type = \ recommend_posts_pb2.PostRecommendationsResponse.OK return resp def UpdateModel(self, request, context): self._logger.debug('UpdateModel PostRecommendations, user_id = %s', request.user_id) resp = recommend_posts_pb2.PostRecommendationsResponse() for r in self.active_recommenders: error = r.update_model(request.user_id, request.article_id) if error: resp.result_type = \ recommend_posts_pb2.PostRecommendationsResponse.ERROR resp.message = error return resp resp.result_type = \ recommend_posts_pb2.PostRecommendationsResponse.OK return resp def AddPost(self, request, context): self._logger.debug('UpdateModel PostRecommendations, user_id = %s', request.author_id) resp = recommend_posts_pb2.PostRecommendationsResponse() for r in self.active_recommenders: error = r.add_post(request) if error: resp.result_type = \ recommend_posts_pb2.PostRecommendationsResponse.ERROR resp.message = error return resp resp.result_type = \ recommend_posts_pb2.PostRecommendationsResponse.OK return resp