1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# 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