class ImageCropAreaDocument(EmbeddedDocument): '''图片被剪切的图片区域. :Variables: - `x`: 左上角x坐标 - `y`: 左上角y坐标 - `w`: 宽度 - `h`: 高度 - `target_width`: 显示图片的宽度,以便计算放缩比例 ''' x = IntegerField(required=True) y = IntegerField(required=True) w = IntegerField(required=True) h = IntegerField(required=True) target_width = IntegerField(required=True)
class ShareCategoryDocument(Document): '''分享类别 :Variables: - `name`: 名称 - `sort`: 按从小到大排序 ''' name = StringField(required=True, max_length=10) sort = IntegerField(required=True) meta = {'collection': 'share_category'} @gen.coroutine def get_share_category_list(): cursor = ShareCategoryDocument.find().sort([('sort', pymongo.ASCENDING) ]) category_list = yield ShareCategoryDocument.to_list(cursor) raise gen.Return(category_list)
class TemporaryFileDocument(Document): '''临时文件 :Variables: - `body`: 文件内容,不超过16M - `chunk_index`: 片索引 - `filename`: 文件名称 - `uploader`: 上传者 - `upload_time`: 上传时间 - `upload_token`: 上传id ''' body = BinaryField(required=True) chunk_index = IntegerField(required=True) filename = StringField(required=True, max_length=500) uploader = ReferenceField(UserDocument, required=True) upload_time = DateTimeField(required=True) upload_token = StringField(required=True, max_length=100) meta = {'collection': 'tmporary_file'}
class WealthRecordDocument(Document): '''用户财富记录 :Variables: - `user`: 相关的用户 - `in_out_type`: 支出还是收入 - `activity`: 相关的活动 - `quantity`: 支出或者收入数量 - `time`: 时间 ''' IN = 'in' OUT = 'out' user = ReferenceField(UserDocument, required=True) in_out_type = StringField(required=True, candidate=[IN, OUT]) activity = ReferenceField(UserActivityDocument, required=True) quantity = IntegerField(required=True) time = DateTimeField(required=True) meta = {'collection': 'user_wealth_record'}
class ShareDocument(Document): '''分享 :Variables: - `title`: 分享标题 - `category`: 类别 - `filename`: 文件标题 - `content_type`: 文件类型, 根据文件头判断出文件类型, 而不是文件名称, 存储的是MIME, 在后续处理里边更新该属性 - `passed`: 审核是否通过 - `description`: 描述 - `uploader`: 上传者 - `upload_time`: 上传时间 - `cost`: 下载需要多少金币 - `like_times`: 点赞次数 - `comment_times`: 评论次数 - `download_times`: 下载次数 - `score`: 评分 - `origin_file`: 在后续处理里边更新该属性 - `mime`: 文件真正的格式,保存的是content_type,如果不存在那就说明没 判断出来 ''' title = StringField(required=True, max_length=1000) category = StringField(required=True) filename = StringField(required=True, max_length=500) content_type = StringField(required=True, max_length=100) passed = BooleanField(required=True, default=True) description = StringField(required=True, max_length=10**5) uploader = ReferenceField(UserDocument, required=True) upload_time = DateTimeField(required=True) cost = IntegerField(required=True, min_value=0) like_times = IntegerField(required=True, default=0) comment_times = IntegerField(required=True, default=0) download_times = IntegerField(required=True, default=0) score = FloatField(required=True, default=-1) origin_file = GridFileField() mime = StringField() meta = {'collection': 'share'} @gen.coroutine def get_share(share_id, user_id=None): share = yield ShareDocument.find_one({'_id': ObjectId(share_id)}) if share: share = yield ShareDocument.translate_dbref_in_document(share) share['like_list'] = yield ShareLikeDocument.get_like_list( share['_id'], limit=10) fs = ShareDocument.get_gridfs() gridout = yield fs.get(ObjectId(share['origin_file'])) share['size'] = gridout.length if user_id is not None: share['liked'] = yield ShareLikeDocument.is_liked( share_id, user_id) raise gen.Return(share) @gen.coroutine def get_share_list(category=None, sort='time', skip=0, limit=None): def score(share): ''' 公式为: score = like_times + comment_times/2 + read_times/5 即: 1 * download_times = 2 * like_times = 5 * comment_times ''' return (share['download_times'] + share['like_times'] / 2.0 + share['comment_times'] / 5.0) assert sort in ['time', 'popularity', 'score'] query = {"passed": True} if category is not None: query.update({'category': category}) cursor = ShareDocument.find(query) if sort != 'popularity': _sort = 'upload_time' if sort == 'time' else 'score' cursor = cursor.sort([(_sort, pymongo.DESCENDING)]).skip(skip) if limit is not None: cursor = cursor.limit(limit) share_list = yield ShareDocument.to_list(cursor) else: share_list = yield ShareDocument.to_list(cursor) share_list.sort(cmp=lambda x, y: -1 if score(x) < score(y) else 1, reverse=True) if limit is not None: share_list = share_list[skip:skip + limit] else: share_list = share_list[skip:] share_list = yield ShareDocument.translate_dbref_in_document_list( share_list) for share in share_list: share['like_list'] = yield ShareLikeDocument.get_like_list( share['_id'], limit=10) raise gen.Return(share_list) @gen.coroutine def get_recommend_share_list(share_id, size=10): '''根据某一话题推荐话题''' share_list = [] share = yield ShareDocument.find_one({'_id': ObjectId(share_id)}) if share: query = { '_id': { '$ne': ObjectId(share_id) }, 'category': share['category'], "passed": True } count = yield ShareDocument.find(query).count() if count > size: skip = random.randint(0, count - size) cursor = ShareDocument.find(query).skip(skip).limit(size) else: cursor = ShareDocument.find(query) share_list = yield ShareDocument.to_list(cursor) raise gen.Return(share_list) @gen.coroutine def get_share_number(category=None): '''得到某一类下分享总数''' query = {'passed': True} if category is not None: query.update({'category': category}) count = yield ShareDocument.find(query).count() raise gen.Return(count) @gen.coroutine def get_uploader_number(category=None): '''得到某一类下上传者总数''' query = {'passed': True} if category is not None: query.update({'category': category}) cursor = ShareDocument.aggregate([{ '$match': query }, { '$group': { '_id': '$uploader' } }]) num = 0 while (yield cursor.fetch_next): cursor.next_object() num += 1 raise gen.Return(num) @gen.coroutine def get_uploader_list(skip=0, limit=None): '''按照上传者上传数量大->小得到上传者''' piplines = [{ '$match': { 'passed': True } }, { '$group': { '_id': '$uploader', 'upload_times': { '$sum': 1 } } }, { '$sort': { 'upload_times': -1 } }, { '$skip': skip }] if limit is not None: piplines.append({'$limit': limit}) cursor = ShareDocument.aggregate(piplines) r = [] while (yield cursor.fetch_next): r.append(cursor.next_object()) translate = ShareDocument.translate_dbref_in_document_list uploader_list = yield translate(r) for uploader in uploader_list: uploader['uploader'] = uploader['_id'] raise gen.Return(uploader_list)
class UserDocument(Document): '''用户 :Variables: - `email`: 邮箱 - `password`: 密码 - `name`: 名称 - `register_date`: 注册日期 - `user_type`: 用户类型 - `activated`: 账号是否已经激活 - `sex`: 性别 - `avatar_updated`: 头像是否更新 - `wealth`: 金币的个数 - `continuous_login_days`: 现在连续登录的天数 - `forbidden_login`: 是否禁止登陆 - `forbidden_login_info`: 禁止登陆的相关信息 - `relationship_status`: 感情状况 - `birthday`: 生日 - `home`: 家乡, 格式: 省份-城市 - `qq`: QQ - `wechat`: 微信 - `phone`: 手机号码 - `signature`: 签名 - `league_bulletin`: 如果是社团, 社团公告 ''' email = EmailField(required=True, unique=True) password = StringField(required=True, min_length=6) name = StringField(required=True) register_date = DateTimeField(required=True) user_type = StringField(required=True, candidate=['person', 'league']) activated = BooleanField(required=True, default=False) sex = StringField(required=True, candidate=['male', 'female'], default='male') avatar_updated = BooleanField(required=True, default=False) wealth = IntegerField(required=True, default=100) continuous_login_days = IntegerField(required=True, default=0) forbidden_login = BooleanField(required=True, default=False) forbidden_login_info = ListField( EmbeddedDocumentField(ForbiddenLoginInfoEmbeddedDocument)) relationship_status = StringField(candidate=['', 'single', 'in_love']) birthday = DateField() home = StringField(max_length=100) qq = StringField(max_length=30) wechat = StringField(max_length=30) phone = StringField(max_length=30) signature = StringField(max_length=100) league_bulletin = StringField(max_length=300) meta = {'collection': 'user'} def get_user_sync(user_id): return UserDocument.get_collection(pymongo=True).find_one( {'_id': ObjectId(user_id)}) @gen.coroutine def get_user_list(skip=0, limit=51): cursor = UserDocument.find().sort([('register_date', pymongo.DESCENDING)]).skip(skip) if limit is not None: cursor = cursor.limit(limit) user_list = yield UserDocument.to_list(cursor) raise gen.Return(user_list) @gen.coroutine def get_avatared_user_list(skip=0, limit=48): '''得到头像改变的人的列表''' cursor = UserDocument.find({ 'avatar_updated': True, 'user_type': { '$ne': 'league' } }).sort([('register_date', pymongo.DESCENDING)]).skip(skip) if limit is not None: cursor = cursor.limit(limit) user_list = yield UserDocument.to_list(cursor) raise gen.Return(user_list) @gen.coroutine def get_random_user_list(user_id, size=4): '''得到随机推荐的size个用户. :Parameters: - `user_id`: 为谁推荐? - `size`: 推荐数量 ''' user = yield UserDocument.find_one({'_id': ObjectId(user_id)}) if not user: raise gen.Return([]) friend_list = yield FriendDocument.get_friend_list(user['_id']) friend_objectid_list = [user['_id'] ] + [friend['_id'] for friend in friend_list] query = {'_id': {'$nin': friend_objectid_list}, 'activated': True} cursor = UserDocument.find(query) count = yield UserDocument.find(query).count() if count > size: cursor = cursor.skip(random.randint(0, count - size)) cursor = cursor.limit(size) user_list = yield UserDocument.to_list(cursor) raise gen.Return(user_list) @gen.coroutine def get_recommend_friends(user_id, size=5): '''得到推荐的朋友 :Parameters: - `user_id`: 为谁推荐? - `size`: 推荐数量 ''' from app.message.document import MessageDocument user = yield UserDocument.find_one({'_id': ObjectId(user_id)}) if not user: raise gen.Return([]) friend_list = yield FriendDocument.get_friend_list(user['_id']) friend_objectid_list = [user['_id'] ] + [friend['_id'] for friend in friend_list] cursor = MessageDocument.find({ 'sender': DBRef(UserDocument.meta['collection'], ObjectId(user_id)), 'message_type': MessageTopic.FRIEND_REQUEST_NEW }) message_list = yield MessageDocument.to_list(cursor) for message in message_list: friend_objectid_list.append(ObjectId(message['recipient'].id)) query = { '_id': { '$nin': friend_objectid_list }, 'activated': True, 'avatar_updated': True } cursor = UserDocument.find(query) count = yield UserDocument.find(query).count() if count > size: cursor = cursor.skip(random.randint(0, count - size)) cursor = cursor.limit(size) user_list = yield UserDocument.to_list(cursor) raise gen.Return(user_list) @gen.coroutine def encrypt_password(password): '''加密密码''' raise gen.Return(hashlib.new('md5', password).hexdigest()) @gen.coroutine def can_seen(user_id, visitor_id): '''visitor能否访问user的个人主页''' is_friend = yield FriendDocument.is_friend(user_id, visitor_id) user_setting = yield UserSettingDocument.get_user_setting(user_id) can_seen = (is_friend or str(user_id) == str(visitor_id) or (user_setting and user_setting["allow_stranger_visiting_profile"])) raise gen.Return(can_seen) @gen.coroutine def update_wealth(user_id, quantity): yield UserDocument.update({'_id': ObjectId(user_id)}, {'$inc': { 'wealth': quantity }}) raise gen.Return() @gen.coroutine def get_continuous_login_days(user_id): user = yield UserDocument.find_one({'_id': ObjectId(user_id)}) if not user: raise gen.Return(0) yesterday = datetime.now() - timedelta(days=1) login_reward_fetched = UserActivityDocument.login_reward_fetched( user_id, day=yesterday) if login_reward_fetched: raise gen.Return(user['continuous_login_days']) yield UserDocument.update({'_id': ObjectId(user_id)}, {'$set': { 'continuous_login_days': 0 }}) raise gen.Return(0) @gen.coroutine def can_afford(user_id, quantity): '''某人是否能够支出quantity金币''' user = yield UserDocument.find_one({'_id': ObjectId(user_id)}) if user: raise gen.Return(user['wealth'] >= quantity) raise gen.Return(False)
class StatusDocument(Document): ''' :Variables: - `author`: 发布者 - `publish_time`: 发布时间 - `content`: 内容 - `like_times`: 点赞次数 - `comment_times`: 评论次数 ''' author = ReferenceField(UserDocument, required=True) publish_time = DateTimeField(required=True) content = StringField(required=True, max_length=1000) like_times = IntegerField(required=True, default=0) comment_times = IntegerField(required=True, default=0) meta = {'collection': 'home_status'} REGEX_AT = re.compile('@([^@\d\(\)]+)\(([0-9a-f]{24})\)') @gen.coroutine def translate_at(content): '''将status content中的@转换成链接''' result = StatusDocument.REGEX_AT.findall(content) for item in result: origin = '@%s(%s)' % (item[0], item[1]) link = '<a href="/profile/%s" data-userid="%s">@%s</a>' % ( item[1], item[1], item[0]) content = content.replace(origin, link) raise gen.Return(content) @gen.coroutine def get_status(status_id, user_id=None): '''得到一个status, 是status_list的item''' status = yield StatusDocument.find_one({'_id': ObjectId(status_id)}) if status: status = yield StatusDocument.translate_dbref_in_document(status) photo = yield StatusPhotoDocument.find_one({ 'status': DBRef(StatusDocument.meta['collection'], ObjectId(status['_id'])) }) if photo: url = yield StatusPhotoDocument.generate_url(photo['_id']) thumbnail = yield StatusPhotoDocument.generate_url( photo['_id'], thumbnail=True) status['photo'] = {'url': url, 'thumbnail': thumbnail} if user_id is not None: status['liked'] = yield StatusLikeDocument.is_liked( status['_id'], user_id) status['like_list'] = yield StatusLikeDocument.get_like_list( status['_id'], user_id) raise gen.Return(status) @gen.coroutine def _extend_status_list(status_list, user_id): for status in status_list: like_times_f = StatusLikeDocument.get_like_times_can_seen status['like_times'] = yield like_times_f(status['_id'], user_id) comment_times_f = StatusCommentDocument.get_comment_times_can_seen status['comment_times'] = yield comment_times_f( status['_id'], user_id) photo = yield StatusPhotoDocument.find_one({ 'status': DBRef(StatusDocument.meta['collection'], ObjectId(status['_id'])) }) if photo: url = yield StatusPhotoDocument.generate_url(photo['_id']) thumbnail = yield StatusPhotoDocument.generate_url( photo['_id'], thumbnail=True) status['photo'] = {'url': url, 'thumbnail': thumbnail} status['liked'] = yield StatusLikeDocument.is_liked( status['_id'], user_id) status['like_list'] = yield StatusLikeDocument.get_like_list( status['_id'], user_id) raise gen.Return(status_list) @gen.coroutine def get_status_list(user_id, visitor_id, skip=0, limit=None): '''按时间从近致远的顺序获取user_id发布的状态 :Parameters: - `user_id`: 用户id - `visitor_id`: 当前访问者id - `skip`: 默认0 - `limit`: 默认None ''' cursor = StatusDocument.find({ 'author': DBRef(UserDocument.meta['collection'], ObjectId(user_id)) }).sort([('publish_time', pymongo.DESCENDING)]).skip(skip) if limit is not None: cursor = cursor.limit(limit) status_list = yield StatusDocument.to_list(cursor) status_list = yield StatusDocument.translate_dbref_in_document_list( status_list) status_list = yield StatusDocument._extend_status_list( status_list, visitor_id) raise gen.Return(status_list) @gen.coroutine def get_friends_status_list(user_id, skip=0, limit=None): '''得到user_id的朋友的状态, 包括自己. :Parameters: - `user_id`: 用户id - `skip`: 默认0 - `limit`: 默认None ''' friend_list = yield FriendDocument.get_friend_list(user_id) shielded_friend_list = yield FriendDocument.get_shielded_friends( user_id) blocked_friend_list = yield FriendDocument.get_blocked_friends(user_id) all_friend_dbref_list = [ DBRef(UserDocument.meta['collection'], ObjectId(friend['_id'])) for friend in friend_list ] shielded_friend_dbref_list = [ DBRef(UserDocument.meta['collection'], ObjectId(friend['_id'])) for friend in shielded_friend_list ] blocked_friend_dbref_list = [ DBRef(UserDocument.meta['collection'], ObjectId(friend['_id'])) for friend in blocked_friend_list ] friend_dbref_list = [ DBRef(UserDocument.meta['collection'], ObjectId(user_id)) ] for friend in all_friend_dbref_list: if (friend not in shielded_friend_dbref_list and friend not in blocked_friend_dbref_list): friend_dbref_list.append(friend) cursor = StatusDocument.find({ 'author': { '$in': friend_dbref_list } }).sort([('publish_time', pymongo.DESCENDING)]).skip(skip) if limit is not None: cursor = cursor.limit(limit) status_list = yield StatusDocument.to_list(cursor) status_list = yield StatusDocument.translate_dbref_in_document_list( status_list) status_list = yield StatusDocument._extend_status_list( status_list, user_id) raise gen.Return(status_list) @gen.coroutine def get_status_number(user_id): '''得到某一个人的已发表的微博的数量''' status_number = yield StatusDocument.find({ 'author': DBRef(UserDocument.meta['collection'], ObjectId(user_id)) }).count() raise gen.Return(status_number) @gen.coroutine def can_see(status, user_id): '''判断某人能否看到某状态. :Parameters: - `status`: 状态, 是id或者document - `user_id`: 相关用户 ''' can = False if isinstance(status, (str, ObjectId)): status = yield StatusDocument.find_one({'_id': ObjectId(status)}) if status: if isinstance(status['author'], DBRef): author = status['author'].id elif isinstance(status['author'], dict): author = status['author']['_id'] else: raise gen.Return(can) is_friend = yield FriendDocument.is_friend(user_id, author) user_setting = yield UserSettingDocument.get_user_setting(author) if (str(author) == str(user_id) or is_friend or user_setting['allow_stranger_visiting_profile']): can = True raise gen.Return(can)
class NodeDocument(Document): ''' :Variables: - `name`: 节点名称 - `topic_number`: 节点下有多少话题, 添加该属性是为了高效的根据话题多少对节点进行排名 - `father`: 父节点 - `category`: 节点类型, 比如内建 - `sort`: 对于某些特殊节点的排序方式 - `description`: 节点描述 ''' BUILTIN = 'builtin' name = StringField(required=True, max_length=20, unique=True) topic_number = IntegerField(required=True, default=0) father = ReferenceField('NodeDocument') category = StringField(max_length=30) sort = IntegerField() description = StringField(max_length=300) last_modified_by = ReferenceField(UserDocument) last_modified_time = DateTimeField() meta = {'collection': 'community_node'} @gen.coroutine def get_node(node_id): node = yield NodeDocument.find_one({'_id': ObjectId(node_id)}) if node: node = yield NodeDocument.translate_dbref_in_document(node) url = yield NodeAvatarDocument.get_node_avatar_url(node['_id']) if url: node['avatar'] = url raise gen.Return(node) @gen.coroutine def get_node_list_by_category(category, skip=0, limit=None): cursor = NodeDocument.find({ 'category': category }).sort([('sort', pymongo.ASCENDING), ('topic_number', pymongo.DESCENDING)]).skip(skip) if isinstance(limit, int) and limit > 0: cursor = cursor.limit(limit) node_list = yield NodeDocument.to_list(cursor) raise gen.Return(node_list) @gen.coroutine def get_hot_node_list(size=None): cursor = NodeDocument.find({ 'category': { '$exists': False } }).sort([('topic_number', pymongo.DESCENDING)]) if size is not None: cursor = cursor.limit(size) node_list = yield NodeDocument.to_list(cursor) raise gen.Return(node_list) @gen.coroutine def get_top_header_node_list(): '''得到社区模块上边部分的节点''' node_list = yield NodeDocument.get_node_list_by_category( NodeDocument.BUILTIN) raise gen.Return(node_list)
class TopicDocument(Document): ''' :Variables: - `author`: 话题者 - `publish_time`: 话题时间 - `last_update_time`: 最后更新时间, 添加此项是为了更好的排名, 当有新的评论时更新此项 - `title`: 标题 - `anonymous`: 是否匿名 - `nodes`: 所属节点 - `read_times`: 阅读次数 - `like_times`: 点赞次数 - `comment_times`: 评论次数 - `top`: 是否被置顶 - `perfect`: 是否被加精 - `content`: 话题的内容 - `images`: 话题内容里边的图片url, 每当话题内容改变时, 应该改变images ''' author = ReferenceField(UserDocument, required=True) publish_time = DateTimeField(required=True) last_update_time = DateTimeField(required=True) title = StringField(required=True, max_length=100) anonymous = BooleanField(required=True, default=False) nodes = ListField(ReferenceField(NodeDocument), required=True) read_times = IntegerField(required=True, default=0) like_times = IntegerField(required=True, default=0) comment_times = IntegerField(required=True, default=0) top = BooleanField(required=True, default=False) perfect = BooleanField(required=True, default=False) content = StringField(max_length=10**5) images = ListField(StringField()) meta = {'collection': 'community_topic'} @gen.coroutine def _extend_images(topic): regex = re.compile('^/image/([a-f0-9]{24})/?$') images = [] for image in topic['images']: url = thumbnail = image if regex.match(image): thumbnail = os.path.join(image, 'thumbnail') images.append({'url': url, 'thumbnail': thumbnail}) return images @gen.coroutine def get_topic(topic_id, user_id): ''' :Parameters: - `topic_id`: 话题id - `user_id`: 判断该user是否赞了该话题 ''' topic = yield TopicDocument.find_one({'_id': ObjectId(topic_id)}) if topic: topic['author'] = yield UserDocument.translate_dbref( topic['author']) liked = yield TopicLikeDocument.is_liked(topic_id, user_id) last_comment = yield TopicCommentDocument.get_last_comment( topic['_id']) topic.update({'liked': liked, 'last_comment': last_comment}) if 'images' in topic and topic['images']: topic['images'] = yield TopicDocument._extend_images(topic) for i, node in enumerate(topic['nodes']): topic['nodes'][i] = yield NodeDocument.translate_dbref(node) raise gen.Return(topic) @gen.coroutine def get_top_topic_list(user_id=None, skip=0, limit=None): '''得到置顶的帖子''' query = {'top': True} cursor = TopicDocument.find(query).sort([ ('publish_time', pymongo.DESCENDING) ]).skip(skip) if limit is not None: cursor = cursor.limit(limit) topic_list = yield TopicDocument.to_list(cursor) for topic in topic_list: topic['author'] = yield UserDocument.translate_dbref( topic['author']) topic[ 'last_comment'] = yield TopicCommentDocument.get_last_comment( topic['_id']) if 'images' in topic and topic['images']: topic['images'] = yield TopicDocument._extend_images(topic) if user_id is not None: topic['liked'] = yield TopicLikeDocument.is_liked( topic['_id'], user_id) for i, node in enumerate(topic['nodes']): topic['nodes'][i] = yield NodeDocument.translate_dbref(node) raise gen.Return(topic_list) @gen.coroutine def get_topic_list(node_id=None, user_id=None, sort=None, skip=0, limit=None): ''' :Parameters: - `node_id`: 如果node_id不为None, 那么得到该节点下的话题 - `sort`: 排序方式, 只可能为time或者popularity - `skip`: 默认0 - `limit`: 默认None NOTE: NEED CACHE !! ''' def score(topic): ''' 公式为: score = like_times + comment_times/2 + read_times/5 即: 1 * like_times = 2 * comment_times = 5 * read_times ''' return (topic['like_times'] + topic['comment_times'] / 2.0 + topic['read_times'] / 5.0) top_topic_list = yield TopicDocument.get_top_topic_list(user_id) if node_id is not None: query = { 'nodes': DBRef(NodeDocument.meta['collection'], ObjectId(node_id)) } else: query = { '_id': { '$nin': [ObjectId(t['_id']) for t in top_topic_list] } } cursor = TopicDocument.find(query) if sort == 'time' or sort is None: cursor = cursor.sort([('last_update_time', pymongo.DESCENDING) ]).skip(skip) if limit is not None: cursor = cursor.limit(limit) topic_list = yield TopicDocument.to_list(cursor) else: topic_list = yield TopicDocument.to_list(cursor) topic_list.sort(cmp=lambda x, y: -1 if score(x) < score(y) else 1, reverse=True) if limit is not None: topic_list = topic_list[skip:skip + limit] else: topic_list = topic_list[skip:] for topic in topic_list: topic['author'] = yield UserDocument.translate_dbref( topic['author']) topic[ 'last_comment'] = yield TopicCommentDocument.get_last_comment( topic['_id']) if 'images' in topic and topic['images']: topic['images'] = yield TopicDocument._extend_images(topic) if user_id is not None: topic['liked'] = yield TopicLikeDocument.is_liked( topic['_id'], user_id) for i, node in enumerate(topic['nodes']): topic['nodes'][i] = yield NodeDocument.translate_dbref(node) if not node_id and skip == 0 and top_topic_list: topic_list = top_topic_list + topic_list raise gen.Return(topic_list) @gen.coroutine def get_topic_number(node_id=None): '''统计某节点下共有多少话题''' if node_id: count = yield TopicDocument.find({ 'nodes': DBRef(NodeDocument.meta['collection'], ObjectId(node_id)) }).count() else: count = yield TopicDocument.count() raise gen.Return(count) @gen.coroutine def get_topic_number_by_someone(user_id, visitor_id=None): '''得到某人发布的状态总的个数''' query = { 'author': DBRef(UserDocument.meta['collection'], ObjectId(user_id)) } # if visitor_id is not None: # if ObjectId(user_id) != ObjectId(visitor_id): # query.update({'anonymous': False}) query.update({'anonymous': False}) topic_number = yield TopicDocument.find(query).count() raise gen.Return(topic_number) @gen.coroutine def get_hot_topic_list(period=None, skip=0, limit=None): '''在一段时间内的热门话题, 跟赞/评论/阅读的次数有关. :Parameters: - `period`: 从现在往前算起, 多长时间之内的新闻 - `skip`: 默认0 - `limit`: 最终得到的热门话题的个数 NOTE: NEED CACHE! ''' def score(topic): ''' 公式为: score = like_times + comment_times/2 + read_times/5 即: 1 * like_times = 2 * comment_times = 5 * read_times ''' return (topic['like_times'] + topic['comment_times'] / 2.0 + topic['read_times'] / 5.0) query = {} if period is not None: query.update( {'last_update_time': { '$gt': datetime.now() - period }}) cursor = TopicDocument.find(query) topic_list = yield TopicDocument.to_list(cursor) topic_list.sort(cmp=lambda x, y: -1 if score(x) < score(y) else 1, reverse=True) if limit is not None: topic_list = topic_list[skip:skip + limit] else: topic_list = topic_list[skip:] raise gen.Return(topic_list) @gen.coroutine def get_recommend_topic_list(topic_id, size=10): '''根据某一话题推荐话题''' topic_list = [] topic = yield TopicDocument.find_one({'_id': ObjectId(topic_id)}) if topic: query = { '$and': [{ '_id': { '$ne': ObjectId(topic_id) } }, { '$or': [{ 'nodes': node } for node in topic['nodes']] }] } count = yield TopicDocument.find(query).count() if count > size: skip = random.randint(0, count - size) cursor = TopicDocument.find(query).skip(skip).limit(size) else: cursor = TopicDocument.find(query) topic_list = yield TopicDocument.to_list(cursor) if not topic_list or len(topic_list) < size: query = {'$and': [{'_id': {'$ne': ObjectId(topic_id)}}]} count = yield TopicDocument.find(query).count() if count > size: skip = random.randint(0, count - size) cursor = TopicDocument.find(query).skip(skip).limit(size) else: cursor = TopicDocument.find(query) topic_list = yield TopicDocument.to_list(cursor) for topic in topic_list: topic['author'] = yield UserDocument.translate_dbref( topic['author']) raise gen.Return(topic_list) @gen.coroutine def get_topic_list_by_someone(author_id, skip=0, limit=None): '''得到某人的话题''' cursor = TopicDocument.find({ 'author': DBRef(UserDocument.meta['collection'], ObjectId(author_id)) }).sort([('publish_time', pymongo.DESCENDING)]).skip(skip) if limit is not None: cursor = cursor.limit(limit) topic_list = yield TopicDocument.to_list(cursor) for topic in topic_list: topic['author'] = yield UserDocument.translate_dbref( topic['author']) topic[ 'last_comment'] = yield TopicCommentDocument.get_last_comment( topic['_id']) for i, node in enumerate(topic['nodes']): topic['nodes'][i] = yield NodeDocument.translate_dbref(node) raise gen.Return(topic_list) @gen.coroutine def insert_one(document): topic_id = yield TopicDocument.insert(document) for node in document['nodes']: topic_number = yield TopicDocument.get_topic_number(node.id) yield NodeDocument.update({'_id': ObjectId(node.id)}, {'$set': { 'topic_number': topic_number }}) new_document = { 'author': document['author'], 'publish_time': document['publish_time'], 'nodes': document['nodes'], 'anonymous': document['anonymous'], 'data_type': 'topic', 'data': DBRef(TopicDocument.meta['collection'], ObjectId(topic_id)) } yield TopicStatisticsDocument.insert(new_document) raise gen.Return(topic_id) @gen.coroutine def delete_one(topic_id): topic = DBRef(TopicDocument.meta['collection'], ObjectId(topic_id)) yield TopicDocument.remove({'_id': ObjectId(topic_id)}) yield TopicStatisticsDocument.remove({'data': topic}) yield TopicLikeDocument.delete(topic_id) yield TopicCommentDocument.delete(topic_id) yield MessageDocument.remove({'data': topic}) raise gen.Return()