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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
from django.db.models import Count, Sum
from django.conf import settings
from collections import defaultdict
from gm_types.gaia import USER_TYPE
from gm_types.mimas.enum import MEDIA_IMAGE_URL_SOURCE
from social.models import SocialInfo
from qa.models.answer import Answer, AnswerVote, AnswerReply, AnswerImage, QuestionTag
from qa.portal import update_answer_reply_read_status
from qa.tasks import qa_image_add_base_info
from utils.common import get_data_from_rich_text
from utils.user import get_user_gm_url
from utils.protocol import gm_protocol
class AnswerTools(object):
@classmethod
def get_author(cls, obj):
return {
'gm_url': get_user_gm_url(obj.user.id),
'user_id': obj.user.id,
'user_type': USER_TYPE.NORMAL,
'user_name': obj.user.nickname,
'user_portrait': obj.user.portrait,
'membership_level': obj.user.membership_level,
'user_level': {
'membership_icon': obj.user.membership_icon,
'level_icon': obj.user.level_icon,
'constellation_icon': obj.user.constellation_icon,
},
"college_id": obj.user.get("college_id", 0),
"college_name": obj.user.get("college_name", ""),
}
@classmethod
def get_replies(cls, answer):
replies = AnswerReply.objects.filter(answer=answer, first_reply__isnull=True,
is_online=True).order_by("create_time")[:2]
return [reply.get_data_for_reply(new_order=True) for reply in replies]
@classmethod
def get_questions_data(cls, current_user, answers):
answer_ids = [answer.id for answer in answers]
uids = [answer.user_id for answer in answers]
is_following_users = SocialInfo(current_user.id).is_following_users(uids=uids) if current_user else {}
answers_dict = {}
for answer in answers:
answers_dict[answer.id] = {
"id": answer.id,
"content": answer.content,
"create_time": answer.create_time.strftime('%y-%m-%d %H:%M'),
"is_recommend": answer.is_recommend,
"platform": answer.platform,
"comment_count": answer.comment_num,
"user_type": USER_TYPE.NORMAL,
"like_num": answer.like_num,
"timestamp": int(answer.create_time.strftime("%s")),
"replies": cls.get_replies(answer),
"author": cls.get_author(answer),
}
images_info = cls.get_answers_images_info(answer_ids)
replies_count_info = cls.replies_count_info(answer_ids)
answers_vote_info = cls.get_answers_vote_info(answer_ids, current_user)
for answer_id, item in answers_dict.items():
item["author"]["is_following"] = is_following_users.get(item["author"]["user_id"], False)
item["images"] = images_info.get(answer_id, [])
item["first_reply_count"] = replies_count_info.get(answer_id, 0)
item["is_voted"] = answers_vote_info.get(answer_id, False)
# 还原排序
return sorted(list(answers_dict.values()), key=lambda i: answer_ids.index(i["id"]))
@classmethod
def get_answers_vote_info(cls, answer_ids, current_user):
# 获取点赞相关信息
if not current_user:
return {}
answers_vote_dict = {}
answers_vote = AnswerVote.objects.filter(answer_id__in=answer_ids, user=current_user.id). \
values_list('answer_id').annotate(vote_num=Count('id'))
for answer_id, vote_num in answers_vote:
answers_vote_dict[answer_id] = True if vote_num else False
return answers_vote_dict
@classmethod
def replies_count_info(cls, answer_ids):
# 获取并设置一级评论数images_dict{answer_id: first_reply_num]}
replies_count_dict = {}
replies_count = AnswerReply.objects.using(settings.SLAVE_DB_NAME).filter(is_online=True, answer_id__in=answer_ids, first_reply=None). \
values_list('answer_id').annotate(first_reply_num=Count('id'))
for answer_id, first_reply_num in replies_count:
replies_count_dict[answer_id] = first_reply_num
return replies_count_dict
@classmethod
def get_answers_images_info(cls, answer_ids):
# 获取回答的图片: images_dict{answer_id: [urls]}
images_dict = {}
answers_images = AnswerImage.objects.filter(
answer_id__in=answer_ids,
image_url_source=MEDIA_IMAGE_URL_SOURCE.CREATE
).values_list("answer_id", "image_url")
for answer_id, image_url in answers_images:
if answer_id not in images_dict:
images_dict[answer_id] = []
images_dict[answer_id].append(image_url)
return images_dict
@classmethod
def get_answer_replies(cls, answer_reply_ids):
# 我收到的评论
answer_replies = AnswerReply.objects.select_related("commented_reply", "answer").filter(
id__in=answer_reply_ids, is_online=True
)
answer_replies_dict = {reply.id: reply for reply in answer_replies}
update_answer_reply_read_status(answer_reply_ids) # 更新回答回复的已读状态
return answer_replies_dict
@classmethod
def get_user_publish_answer_nums(cls, user_ids):
"""
获取用户发布的总回答数
:param user_ids:
:return:
"""
_nums_dic = {}
answer_nums = Answer.objects.filter(
user__in=user_ids, is_online=True
).values_list("user").annotate(cnt=Count("id"))
for user_id, cnt in answer_nums:
_nums_dic[user_id] = cnt
return _nums_dic
@classmethod
def get_user_publish_answer_vote_nums(cls, user_ids):
"""
获取用户发布的所有回答的点赞数
:param user_ids:
:return:
"""
_nums_dic = {}
favor_nums = Answer.objects.filter(
user__in=user_ids, is_online=True
).values_list("user").annotate(snt=Sum("like_num"))
for user_id, snt in favor_nums:
_nums_dic[user_id] = snt
return _nums_dic
@classmethod
def get_question_all_answer_vote_nums(cls, question_ids):
"""
获取问题下所有回答的点赞数总和
:param question_ids:
:return:
"""
_nums_dic = {}
favor_nums = Answer.objects.filter(
question_id__in=question_ids, is_online=True
).values_list("question_id").annotate(snt=Sum("like_num"))
for question_id, snt in favor_nums:
_nums_dic[question_id] = snt
return _nums_dic
@classmethod
def get_header_images_by_answer_ids(cls, answer_ids, image_count=9):
"""
7.22.0 add 获取回答、问题头图
:param answer_ids:
:param image_count: 头图个数,创建时最多9张
:return:
"""
_images = defaultdict(list)
if not answer_ids:
return _images
images = AnswerImage.objects.filter(
image_url_source=MEDIA_IMAGE_URL_SOURCE.HEAD, answer_id__in=answer_ids
).order_by('id')
for image in images:
if len(_images[image.answer_id]) < image_count:
_images[image.answer_id].append(image.image_info_data)
return _images
class QaTool(object):
@staticmethod
def _sorted_qa_images(content_images, need_sorted_images):
"""
仅在获取新的图片数据中有用
:param content_images: 富文本内容中的图片列表
:param need_sorted_images:待排序的图片数据列表
:return:
"""
can_sorted_images, others = [], []
content_images = list(map(lambda item: item.split("-")[0], content_images))
for image_item in need_sorted_images:
if image_item.get("image", "") in content_images:
can_sorted_images.append(image_item)
else:
others.append(image_item)
_images_list = sorted(can_sorted_images, key=lambda item: content_images.index(item.get("image", "")))
_images_list.extend(others)
return _images_list
@classmethod
def get_intact_images(cls, obj, model_name):
"""
获取问答相关的新图片逻辑
:param obj:
:param model_name: 表名 question_image or answer_image
:return:
"""
_, content_images = get_data_from_rich_text(obj.content, u'//img/@src')
# 如果存在富文本里有图,但数据库里没图,则走创建逻辑
if not obj.images.filter(
image_url_source=MEDIA_IMAGE_URL_SOURCE.RICH_TEXT
).exists() and content_images:
qa_image_add_base_info.delay(
images_list=list(map(lambda item: item.split("-")[0], content_images)),
params_info={
"model": model_name,
"id": obj.id,
},
)
image_list = []
for image in obj.images.all():
image_list.append(image.image_info_data)
image_list = cls._sorted_qa_images(content_images, image_list)
return image_list
@classmethod
def get_following_info(cls, user_ids, current_user_id):
"""
获取当前用户的关注信息
:param user_ids:
:param current_user_id:
:return:
"""
following_dic = {}
if current_user_id:
following_dic = SocialInfo(current_user_id).is_following_users(uids=user_ids)
return following_dic
@classmethod
def get_tags_info_by_question_ids(cls, question_ids):
"""
通过问题id,获取对应的标签信息
:param question_ids:
:return:
"""
result = defaultdict(list)
if question_ids:
q_tag_list = QuestionTag.objects.filter(question_id__in=question_ids)
for q_tag in q_tag_list:
result[q_tag.question_id].append(q_tag.tag)
return dict(result)
@classmethod
def get_header_images(cls, obj, image_count=9):
"""
7.22.0 add 获取回答、问题头图
:param obj: answer question
:param image_count: 头图个数,创建时最多9张
:return:
"""
images = obj.images.filter(
image_url_source=MEDIA_IMAGE_URL_SOURCE.HEAD
).order_by('id')[:image_count]
_images = []
for image in images:
_images.append(image.image_info_data)
return _images