# coding=utf-8 from __future__ import print_function, absolute_import, unicode_literals import datetime from django.db import models from django.db.models import Count from django.utils import timezone from .topic import Problem from .topicreply import TopicReply from .topicvote import TopicVote class TopicRankingScore(models.Model): """ 帖子用于排序的各种分数 """ REPLY_COEF = 5.0 REPLY_BASE = 0.95 VOTE_COEF = 1.0 VOTE_BASE = 0.1 SPLIT_HOUR = 4 class Meta: db_table = 'api_topicrankingscore' app_label = 'talos' topic = models.OneToOneField(Problem, related_name='ranking_score') reply_score = models.FloatField(verbose_name=u'历史(今天之前)回复热度分', default=0.0) # 未加权 vote_score = models.FloatField(verbose_name=u'历史(今天之前)点赞分', default=0.0) # 未加权 last_modified = models.DateTimeField(auto_now=True, verbose_name=u'最后更新时间') @classmethod def get_daily_pop(cls, topic_ids, day_offset=0): """ 如果时间的小时数大等于SPLIT_HOUR,则只计算所在日的个数 否则将计算所在日和前一日的个数 :param topic: :param day_offset: 往前回溯几日,0为当天,1为昨天 :return: """ t = timezone.now()-datetime.timedelta(days=day_offset) end_time = (t+datetime.timedelta(days=1)).replace(hour=0, minute=0, second=0) if t.hour < cls.SPLIT_HOUR: start_time = (t-datetime.timedelta(days=1)).replace(hour=0, minute=0, second=0) else: start_time = t.replace(hour=0, minute=0, second=0) reply_count = TopicReply.objects.filter( problem_id__in=topic_ids, reply_date__gte=start_time, reply_date__lt=end_time ).values('problem_id').annotate(count=Count('*')) vote_count = TopicVote.objects.filter( topic_id__in=topic_ids, vote_time__gte=start_time, vote_time__lt=end_time ).values('topic_id').annotate(count=Count('*')) results = {topic_id:{'reply_count':0, 'vote_count':0} for topic_id in topic_ids} for rcnt in reply_count: results[rcnt['problem_id']]['reply_count'] = rcnt['count'] for vcnt in vote_count: results[vcnt['topic_id']]['vote_count'] = vcnt['count'] return results @classmethod def update_pop(cls, topic_ids): """ (定时任务)(批量)更新帖子历史分数 :param topic: :return: """ ranking_scores = [] for topic_id in topic_ids: ranking_score, created = cls.objects.get_or_create(topic_id=topic_id) ranking_scores.append(ranking_score) daily_pop = cls.get_daily_pop(topic_ids=topic_ids, day_offset=1) # 获取昨日的分数 for ranking_score in ranking_scores: topic_id = ranking_score.topic_id ranking_score.reply_score = (ranking_score.reply_score+daily_pop[topic_id]['reply_count'])*cls.REPLY_BASE ranking_score.vote_score = (ranking_score.vote_score+daily_pop[topic_id]['vote_count'])*cls.VOTE_BASE ranking_score.save() @classmethod def get_pop_score(cls, topic): """DEPRECATED 获取帖子当前的热度分 :param topic: :return: """ assert isinstance(topic, Problem) daily_pop = cls.get_daily_pop(topic_ids=[topic.id]) daily_pop = daily_pop[topic.id] try: ranking_score = cls.objects.get(topic=topic) old_reply_score = ranking_score.reply_score old_vote_score = ranking_score.vote_score except cls.DoesNotExist: old_reply_score = 0.0 old_vote_score = 0.0 return cls.REPLY_COEF*(old_reply_score+daily_pop['reply_count']) + \ cls.VOTE_COEF*(old_vote_score+daily_pop['vote_count']) @classmethod def get_pop_scores(cls, topic_ids): daily_pops = cls.get_daily_pop(topic_ids=topic_ids) ranking_scores = cls.objects.filter(topic_id__in=topic_ids) ranking_scores = {s.topic_id: s for s in ranking_scores} res = {} for topic_id in topic_ids: reply_count = daily_pops[topic_id]['reply_count'] vote_count = daily_pops[topic_id]['vote_count'] if topic_id in ranking_scores: old_reply_score = ranking_scores[topic_id].reply_score old_vote_score = ranking_scores[topic_id].vote_score else: old_reply_score = 0.0 old_vote_score = 0.0 res[topic_id] = cls.REPLY_COEF*(old_reply_score+reply_count) + \ cls.VOTE_COEF*(old_vote_score+vote_count) return res