Пример #1
0
class StatusPhotoDocument(Document):
    '''状态图片

    :Variables:
      - `status`: 相关的状态
      - `name`: 照片名称
      - `body`: 相片内容
      - `thumbnail`: 相片略缩图
      - `content_type`: 照片格式
      - `upload_time`: 上传时间
    '''

    status = ReferenceField(StatusDocument, required=True)
    name = StringField(required=True, max_length=50)
    body = BinaryField(required=True)
    thumbnail = BinaryField(required=True)
    content_type = StringField(required=True)
    upload_time = DateTimeField(required=True)

    meta = {'collection': 'home_status_photo'}

    @gen.coroutine
    def generate_url(photo_id, thumbnail=False):
        '''生成照片的url'''

        url = '/status/photo/%s' % photo_id
        if thumbnail:
            url += '/thumbnail'

        raise gen.Return(url)
Пример #2
0
class ForbiddenLoginInfoEmbeddedDocument(EmbeddedDocument):
    '''禁止登陆的相关信息

    :Variables:
      - `time`: 禁止登陆的时间
      - `reason`: 禁止登陆原因
    '''

    time = DateTimeField(required=True)
    reason = StringField(required=True, max_length=100)
Пример #3
0
class UserActivityDocument(Document):
    '''用户的活动

    :Variables:
      - `user`: 用户
      - `activity_type`: 活动类型
      - `time`: 时间
      - `relevant`: 相关者
      - `data`: 有关的数据
    '''

    VISIT = 'visit'
    COMMENT = 'comment'
    REPLY = 'reply'
    LIKE = 'like'
    DOWNLOAD_SHARE = 'download_share'
    FETCH_LOGIN_REWARD = 'fetch_login_reward'

    STATUS_NEW = 'status_new'
    TOPIC_NEW = 'topic_new'
    SHARE_NEW = 'share_new'
    FRIEND_REQUEST_NEW = 'friend_request_new'
    LEAVE_MESSAGE_NEW = 'leave_message_new'

    user = ReferenceField(UserDocument, required=True)
    activity_type = StringField(required=True)
    time = DateTimeField(required=True)
    relevant = ReferenceField(UserDocument)
    data = ReferenceField()

    meta = {'collection': 'user_activity'}

    @gen.coroutine
    def login_reward_fetched(user_id, day=None):
        '''判断某一天是否领取了登陆奖励'''

        if day is None:
            day = datetime.now()

        today = datetime(year=day.year, month=day.month, day=day.day)
        tomorrow = today + timedelta(days=1)

        activity = yield UserActivityDocument.find_one({
            'user':
            DBRef(UserDocument.meta['collection'], ObjectId(user_id)),
            'activity_type':
            UserActivityDocument.FETCH_LOGIN_REWARD,
            'time': {
                '$gte': today,
                '$lt': tomorrow
            }
        })

        raise gen.Return(True if activity else False)
Пример #4
0
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'}
Пример #5
0
class AvatarDocument(Document):
    '''头像

    :Variables:
      - `owner`: 所有者
      - `name`: 图片名称
      - `content`: 完整头像内容
      - `content_type`: 图片类型
      - `upload_time`: 上传时间
      - `thumbnail50x50`: 略缩图50x50
      - `thumbnail180x180`: 略缩图180x180
      - `crop_area`: 头像裁剪区域
    '''
    owner = ReferenceField(UserDocument, required=True)
    name = StringField(required=True)
    content = BinaryField(required=True)
    content_type = StringField(required=True)
    upload_time = DateTimeField(required=True)
    thumbnail50x50 = BinaryField(required=True)
    thumbnail180x180 = BinaryField(required=True)
    crop_area = EmbeddedDocumentField(ImageCropAreaDocument)

    meta = {'collection': 'user_avatar'}
Пример #6
0
class CodeDocument(Document):
    '''激活码或者是重设密码码.

    :Variables:
      - `uid`: user id
      - `code`: 激活码, 生成规则base64.64encode(str(uuid1()))
      - `expired_time`: 到期时间
    '''

    uid = ObjectIdField(required=True)
    code = StringField(required=True, unique=True)
    expired_time = DateTimeField(required=True)

    meta = {'collection': 'user_code'}

    @classmethod
    def generate_code(cls):
        return base64.b64encode(str(uuid1()))
Пример #7
0
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)
Пример #8
0
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'}
Пример #9
0
class UserSettingDocument(Document):
    '''用户的设置

    :Variables:
      - `user`: 用户
      - `profile_cover`: 个人封面
      - `require_verify_when_add_friend`: 加我为朋友时是否需要验证
      - `allow_stranger_visiting_profile`: 是否允许陌生人查看个人主页
      - `allow_stranger_chat_with_me`: 是否允许陌生人和我聊天
      - `enable_leaving_message`: 是否允许留言
      - `email_notify_when_offline`: 当离线有新的消息时发邮件通知
      - `theme`: 主题
    '''

    user = ReferenceField(UserDocument, required=True)
    profile_cover = ReferenceField(required=True)
    require_verify_when_add_friend = BooleanField(required=True, default=True)
    allow_stranger_visiting_profile = BooleanField(required=True,
                                                   default=False)
    allow_stranger_chat_with_me = BooleanField(required=True, default=False)
    enable_leaving_message = BooleanField(required=True, default=True)
    email_notify_when_offline = BooleanField(required=True, default=True)
    theme = StringField(required=True, default='default')

    meta = {'collection': 'user_setting'}

    def get_user_setting_sync(user_id):
        return UserSettingDocument.get_collection(True).find_one({
            'user':
            DBRef(UserDocument.meta['collection'], ObjectId(user_id))
        })

    @gen.coroutine
    def get_user_setting(user_id):
        user_setting = yield UserSettingDocument.find_one({
            'user':
            DBRef(UserDocument.meta['collection'], ObjectId(user_id))
        })

        raise gen.Return(user_setting)

    @gen.coroutine
    def get_profile_cover(user_id):
        '''得到用户profile_cover图片地址'''

        from app.base.document import ImageDocument

        user_setting = yield UserSettingDocument.get_user_setting(user_id)
        if not (user_setting and 'profile_cover' in user_setting
                and user_setting['profile_cover']):
            raise gen.Return("")

        cover = user_setting['profile_cover']
        if cover.collection == OfficialProfileCoverDocument.meta['collection']:
            cover = yield OfficialProfileCoverDocument.translate_dbref(cover)
            url = yield ImageDocument.generate_image_url(cover['image'].id)
        else:
            url = yield ImageDocument.generate_image_url(cover.id,
                                                         thumbnail=True)

        raise gen.Return(url)

    @gen.coroutine
    def set_profile_cover(user_id, cover):
        '''设置用户的个人封面'''

        from app.base.document import ImageDocument

        assert isinstance(cover, DBRef)

        uploader = DBRef(UserDocument.meta['collection'], ObjectId(user_id))
        user_setting = yield UserSettingDocument.get_user_setting(user_id)
        if (user_setting and 'profile_cover' in user_setting
                and user_setting['profile_cover']
                and user_setting['profile_cover'].collection
                == ImageDocument.meta['collection']):
            yield ImageDocument.remove(
                {'_id': ObjectId(user_setting['profile_cover'].id)})

        yield UserSettingDocument.update({'user': uploader},
                                         {'$set': {
                                             'profile_cover': cover
                                         }})

        raise gen.Return()
Пример #10
0
class ImageDocument(Document):
    '''图片

    :Variables:
      - `name`: 图片名称
      - `body`: 图片内容
      - `content_type`: 图片类型, 存储的内容是PNG, JPEG等
      - `thumbnail`: 略缩图
      - `uploader`: 上传者, 有可能是抓取的图片, 所以上传者不一定存在.
      - `upload_time`: 上传时间
      - `description`: 图片描述
      - `tag`: 标签
    '''

    name = StringField(required=True, max_length=200)
    body = BinaryField(required=True)
    content_type = StringField(required=True)
    thumbnail = BinaryField(required=True)
    uploader = ReferenceField(UserDocument)
    upload_time = DateTimeField()
    description = StringField(max_length=500)
    tags = ListField(StringField(max_length=50))

    meta = {'collection': 'image'}

    @gen.coroutine
    def generate_image_url(image_id, thumbnail=False):
        url = '/image/%s' % image_id
        if thumbnail:
            url += '/thumbnail'

        raise gen.Return(url)

    @gen.coroutine
    def insert_one(uploaded_file,
                   thumbnail_width=200,
                   target_width=None,
                   crop_area=None,
                   uploader=None,
                   upload_time=None,
                   description=None,
                   tags=None):
        '''
        插入一个图片

        :Parameters:
          - `uploaded_file`: 上传的文件
          - `thumbnail_width`: 略缩图宽度, 默认200px
          - `target_width`: Jcrop插件剪裁时, 图像所具有的宽度
          - `crop_area`: 要剪裁的区域, 其格式为(x, y, w, h), x/y/w/h是Jcrop插件传过来的数据,
                         x代表左上角x坐标, y代表左上角y坐标, w代表宽度, h代表高度
          - `uploader`: 上传者, 不是ObjectId, 是DBRef
          - `upload_time`: 上传时间
          - `description`: 描述
          - `tags`: 标签, 列表类型
        '''

        try:
            image = Image.open(StringIO(uploaded_file['body']))
        except:
            raise HTTPError(404)

        content_type = uploaded_file['content_type'].split('/')[1].upper()

        document = {
            'name': uploaded_file['filename'],
            'content_type': content_type,
        }
        if uploader:
            assert isinstance(uploader, DBRef)
            document["uploader"] = uploader

        if upload_time:
            document["upload_time"] = upload_time

        if description:
            document["description"] = description

        if tags:
            document["tags"] = tags

        if target_width is not None and crop_area is not None:
            scale = image.size[0] * 1.0 / target_width
            x, y, w, h = map(lambda x: int(x * scale), crop_area)

            box = (x, y, x + w, y + h)
            image = image.crop(box)

        _width = 1024
        if image.size[0] > _width:
            height = _width * 1.0 * image.size[1] / image.size[0]
            image = image.resize(map(int, (_width, height)), Image.ANTIALIAS)

        output = StringIO()
        image.save(output, content_type, quality=100)
        document["body"] = Binary(output.getvalue())
        output.close()

        thumbnail_height = thumbnail_width * 1.0 * image.size[1] / image.size[0]

        output = StringIO()
        image.resize(map(int, (thumbnail_width, thumbnail_height)),
                     Image.ANTIALIAS).save(output, content_type, quality=100)
        document["thumbnail"] = Binary(output.getvalue())
        output.close()

        image_id = yield ImageDocument.insert(document)

        raise gen.Return(image_id)
Пример #11
0
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()
Пример #12
0
class TopicCommentDocument(Document):
    '''
    :Variables:
      - `topic`: 被评论的话题
      - `author`: 评论者
      - `comment_time`: 评论时间
      - `content`: 评论内容
      - `anonymous`: 是否匿名
      - `replyeder`: 被回复的人
    '''

    topic = ReferenceField(TopicDocument, required=True)
    author = ReferenceField(UserDocument, required=True)
    comment_time = DateTimeField(required=True)
    content = StringField(required=True, max_length=10**5)
    anonymous = BooleanField(required=True, default=False)
    replyeder = ReferenceField(UserDocument)

    meta = {'collection': 'community_topic_comment'}

    @gen.coroutine
    def get_comment_list(topic_id, skip=0, limit=None):
        cursor = TopicCommentDocument.find({
            'topic':
            DBRef(TopicDocument.meta['collection'], ObjectId(topic_id))
        }).sort([('comment_time', pymongo.ASCENDING)]).skip(skip)

        if limit is not None:
            cursor = cursor.limit(limit)

        comment_list = yield TopicCommentDocument.to_list(cursor)
        for i, comment in enumerate(comment_list):
            comment['floor'] = skip + 1 + i
            comment['author'] = yield UserDocument.translate_dbref(
                comment['author'])

            if 'replyeder' in comment:
                comment['replyeder'] = yield UserDocument.translate_dbref(
                    comment['replyeder'])

        raise gen.Return(comment_list)

    @gen.coroutine
    def get_comment_times(topic_id):
        comment_times = yield TopicCommentDocument.find({
            'topic':
            DBRef(TopicDocument.meta['collection'], ObjectId(topic_id))
        }).count()

        raise gen.Return(comment_times)

    @gen.coroutine
    def insert_one(document):
        comment_id = yield TopicCommentDocument.insert(document)
        topic_id = document['topic'].id

        comment_times = yield TopicCommentDocument.get_comment_times(topic_id)
        yield TopicDocument.update({'_id': ObjectId(topic_id)}, {
            '$set': {
                'comment_times': comment_times,
                'last_update_time': datetime.now()
            }
        })

        topic = yield TopicDocument.find_one({'_id': ObjectId(topic_id)})
        if topic:
            new_document = {
                'author':
                document['author'],
                'publish_time':
                document['comment_time'],
                'nodes':
                topic['nodes'],
                'anonymous':
                document['anonymous'],
                'data_type':
                'topic_comment',
                'data':
                DBRef(TopicCommentDocument.meta['collection'],
                      ObjectId(comment_id))
            }
            yield TopicStatisticsDocument.insert(new_document)

        raise gen.Return(comment_id)

    @gen.coroutine
    def get_last_comment(topic_id):
        '''得到某一话题的最后一个回复'''

        cursor = TopicCommentDocument.find({
            'topic':
            DBRef(TopicDocument.meta['collection'], ObjectId(topic_id))
        }).sort([('comment_time', pymongo.DESCENDING)]).limit(1)

        comment_list = yield TopicCommentDocument.to_list(cursor)

        last_comment = None
        if comment_list:
            last_comment = comment_list[0]
            last_comment['author'] = yield UserDocument.translate_dbref(
                last_comment['author'])

        raise gen.Return(last_comment)

    @gen.coroutine
    def delete_one(comment_id):
        comment = yield TopicCommentDocument.find_one(
            {'_id': ObjectId(comment_id)})
        if comment:
            yield TopicCommentDocument.remove({'_id': ObjectId(comment_id)})

            comment_times = yield TopicCommentDocument.get_comment_times(
                comment['topic'].id)

            yield TopicDocument.update(
                {'_id': ObjectId(comment['topic'].id)},
                {'$set': {
                    'comment_times': comment_times
                }})

            yield MessageDocument.remove({
                'data':
                DBRef(TopicCommentDocument.meta['collection'],
                      ObjectId(comment_id))
            })

        raise gen.Return()

    @gen.coroutine
    def delete(topic_id):
        '''将与某一个话题有关的评论都删除'''

        comment_list = yield TopicCommentDocument.get_comment_list(topic_id)
        for comment in comment_list:
            yield TopicCommentDocument.delete_one(comment['_id'])

        raise gen.Return()
Пример #13
0
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)
Пример #14
0
class TopicStatisticsDocument(Document):
    '''
    话题统计, 添加该集合的目的是为了更好的统计活跃用户

    :Variables:
      - `author`: 用户
      - `publish_time`: 发表时间, 可以根据发表时间来确定一段时间内的活跃用户,而不是所有时间段
                        的活跃用户
      - `nodes`: 话题或者话题评论所属的节点
      - `anonymous`: 是否匿名, 如果匿名的话, 则不算
      - `data_type`: 所保存的数据类型
      - `data`: 数据
    '''

    author = ReferenceField(UserDocument, required=True)
    publish_time = DateTimeField(required=True)
    nodes = ListField(ReferenceField(NodeDocument), required=True)
    anonymous = BooleanField(required=True)
    data_type = StringField(required=True,
                            candidate=['topic', 'topic_comment'])
    data = ReferenceField(required=True)

    meta = {'collection': 'community_topic_statistics'}

    @gen.coroutine
    def get_active_author_list(node_id=None, period=None, skip=0, limit=None):
        '''得到某一节点下的活跃用户排名

        :Parameters:
          - `node_id`: 如果不为None, 则表示某一节点下的活跃用户
          - `period`: 如果不为None, 则为timedelta类型, 表示从现在往前算多长时间内的活跃用户
          - `skip`: 默认0
          - `limit`: 默认None
        '''

        query = {
            'anonymous': False,
        }
        if node_id is not None:
            query.update({
                'nodes':
                DBRef(NodeDocument.meta['collection'], ObjectId(node_id))
            })

        if period is not None:
            begin = datetime.now() - period
            query.update({'publish_time': {'$gt': begin}})

        aggregate_pipeline = [{
            '$match': query
        }, {
            '$group': {
                '_id': {
                    'author': '$author'
                },
                'times': {
                    '$sum': 1
                }
            }
        }, {
            '$sort': {
                'times': -1
            }
        }, {
            '$skip': skip
        }, {
            '$limit': limit
        }]

        cursor = TopicStatisticsDocument.aggregate(aggregate_pipeline)

        author_list = []
        while (yield cursor.fetch_next):
            item = cursor.next_object()
            author_list.append({
                'author': item['_id']['author'],
                'times': item['times']
            })

        author_list = yield UserDocument.translate_dbref_in_document_list(
            author_list)

        raise gen.Return(author_list)
Пример #15
0
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)
Пример #16
0
class ChatMessageDocument(Document):
    '''两个人之间的私聊, 把两个人之间的聊天历史保存起来.

    :Variables:
      - `body`: 消息内容
      - `betwwen`: 谁跟谁之间的私聊, 添加此属性是为了方便查询两人之间的私聊信息.
      - `sender`: 发送者
      - `send_time`: 发送时间
      - `recipient`: 接收者
      - `received`: 是否被接收, 如果对方离线(未和Ejabberd服务器建立连接), 那么将会存储为离线消息
      - `read`: 是否被阅读, 接收后不一定被阅读
    '''

    body = StringField(required=True, max_length=1000)
    between = ListField(ReferenceField(UserDocument), required=True)
    sender = ReferenceField(UserDocument, required=True)
    send_time = DateTimeField(required=True)
    recipient = ReferenceField(UserDocument, required=True)
    received = BooleanField(required=True, default=False)
    read = BooleanField(required=True, default=False)

    meta = {
        'collection': 'chat_message'
    }

    @gen.coroutine
    def get_chat_message_list(user_id, skip=0, limit=None):
        '''得到与某人有关的私聊信息'''

        user_dbref = DBRef(UserDocument.meta['collection'], ObjectId(user_id))
        query = {
            '$or': [{'sender': user_dbref}, {'recipient': user_dbref}]
        }

        cursor = ChatMessageDocument.find(query).sort(
            [('send_time', pymongo.DESCENDING)]
        ).skip(skip)

        if limit is not None:
            cursor = cursor.limit(limit)

        chat_message_list = yield ChatMessageDocument.to_list(cursor)
        chat_message_list = yield ChatMessageDocument.translate_dbref_in_document_list(
            chat_message_list)

        raise gen.Return(chat_message_list)

    @gen.coroutine
    def get_history_messages(id_a, id_b, since):
        '''得到两人之间的历史消息'''

        limit = setting.history_messages_number_per_time

        user_a = DBRef(UserDocument.meta['collection'], ObjectId(id_a))
        user_b = DBRef(UserDocument.meta['collection'], ObjectId(id_b))

        cursor = ChatMessageDocument.find({
            'between': user_a, 'between': user_b, 'send_time': {'$lt': since}
        })

        cursor = ChatMessageDocument.find(
            {'$or': [{'between': [user_a, user_b]},
                     {'between': [user_b, user_a]}], 'send_time': {'$lt': since}}
        ).sort([('send_time', pymongo.DESCENDING)]).limit(limit)

        result = yield ChatMessageDocument.to_list(cursor)
        raise gen.Return(result[::-1])

    def set_read(recipient_id):
        '''将某人所有的未读信息设置为已读'''

        ChatMessageDocument.get_collection(True).update(
            {'recipient': DBRef(UserDocument.meta['collection'], ObjectId(recipient_id))},
            {'$set': {'read': True}},
            multi=True
        )

    def has_unread_chat_message(recipient_id):
        '''判断是否有未读的信息'''

        message = ChatMessageDocument.get_collection(pymongo=True).find_one({
            'recipient': DBRef(UserDocument.meta['collection'], ObjectId(recipient_id)),
            'read': False
        })

        return True if message else False

    def get_message_number(recipient_id, read=None):
        '''得到消息数量'''

        query = {
            'recipient': DBRef(UserDocument.meta['collection'], ObjectId(recipient_id))
        }

        if read is not None:
            assert isinstance(read, bool)
            query.update({'read': read})

        count = ChatMessageDocument.get_collection(True).find(query).count()
        return count

    def get_unread_messages(user_id):
        '''得到某人未读信息'''

        recipient = DBRef(UserDocument.meta['collection'], ObjectId(user_id))
        result = ChatMessageDocument.get_collection(True).aggregate([
            {'$match': {'recipient': recipient, 'received': True, 'read': False}},
            {'$sort': {'send_time': 1}},
            {'$group': {'_id': '$sender',
                        'messages': {'$push': {'send_time': '$send_time',
                                               'body': '$body',
                                               'sender': '$sender'}}}}
        ])
        return result['result']
Пример #17
0
class ShareCommentDocument(Document):
    '''
    :Variables:
      - `share`: 被评论的分享
      - `author`: 评论者
      - `comment_time`: 评论时间
      - `content`: 评论内容
      - `anonymous`: 是否匿名
      - `replyeder`: 被回复的人
    '''

    share = ReferenceField(ShareDocument, required=True)
    author = ReferenceField(UserDocument, required=True)
    comment_time = DateTimeField(required=True)
    content = StringField(required=True, max_length=10**5)
    anonymous = BooleanField(required=True, default=False)
    replyeder = ReferenceField(UserDocument)

    meta = {'collection': 'share_comment'}

    @gen.coroutine
    def get_comment_list(share_id, skip=0, limit=None):
        cursor = ShareCommentDocument.find({
            'share':
            DBRef(ShareDocument.meta['collection'], ObjectId(share_id))
        }).sort([('comment_time', pymongo.ASCENDING)]).skip(skip)

        if limit is not None:
            cursor = cursor.limit(limit)

        comment_list = yield ShareCommentDocument.to_list(cursor)

        for i, comment in enumerate(comment_list):
            comment['floor'] = skip + 1 + i
            comment['author'] = yield UserDocument.translate_dbref(
                comment['author'])
            if 'replyeder' in comment:
                comment['replyeder'] = yield UserDocument.translate_dbref(
                    comment['replyeder'])

        raise gen.Return(comment_list)

    @gen.coroutine
    def get_comment_times(share_id):
        comment_times = yield ShareCommentDocument.find({
            'share':
            DBRef(ShareDocument.meta['collection'], ObjectId(share_id))
        }).count()

        raise gen.Return(comment_times)

    @gen.coroutine
    def insert_one(document):
        comment_id = yield ShareCommentDocument.insert(document)

        comment_times = yield ShareCommentDocument.get_comment_times(
            document['share'].id)
        yield ShareDocument.update({'_id': ObjectId(document['share'].id)},
                                   {'$set': {
                                       'comment_times': comment_times
                                   }})

        raise gen.Return(comment_id)

    @gen.coroutine
    def delete_one(comment_id):
        comment = yield ShareCommentDocument.find_one(
            {'_id': ObjectId(comment_id)})
        if comment:
            yield ShareCommentDocument.remove({'_id': ObjectId(comment_id)})

            comment_times = yield ShareCommentDocument.get_comment_times(
                comment['share'].id)
            yield ShareDocument.update(
                {'_id': ObjectId(comment['share'].id)},
                {'$set': {
                    'comment_times': comment_times
                }})

            yield MessageDocument.remove({
                'data':
                DBRef(ShareCommentDocument.meta['collection'],
                      ObjectId(comment_id))
            })

        raise gen.Return()
Пример #18
0
class LeaveMessageDocument(Document):
    '''留言

    :Variables:
      - `user`: 被留言者
      - `author`: 留言者
      - `leave_time`: 留言时间
      - `private`: 是不是私密的
      - `content`: 留言内容
      - `replyeder`: 被回复者
    '''
    user = ReferenceField(UserDocument, required=True)
    author = ReferenceField(UserDocument, required=True)
    leave_time = DateTimeField(required=True)
    private = BooleanField(required=True, default=False)
    content = StringField(required=True, max_length=5000)
    replyeder = ReferenceField(UserDocument)

    meta = {'collection': 'profile_leave_message'}

    @gen.coroutine
    def get_leave_message_number(user_id, visitor_id):
        '''得到留给某人的留言的总个数'''

        query = {
            'user':
            DBRef(UserDocument.meta['collection'], ObjectId(user_id)),
            '$or': [{
                'private': False
            }, {
                'private':
                True,
                'author':
                DBRef(UserDocument.meta['collection'], ObjectId(visitor_id))
            }, {
                'private':
                True,
                'user':
                DBRef(UserDocument.meta['collection'], ObjectId(visitor_id))
            }]
        }

        count = yield LeaveMessageDocument.find(query).count()
        raise gen.Return(count)

    @gen.coroutine
    def get_leave_message_list(user_id, visitor_id, skip=0, limit=None):
        '''得到某人得到的留言'''

        query = {
            'user':
            DBRef(UserDocument.meta['collection'], ObjectId(user_id)),
            '$or': [{
                'private': False
            }, {
                'private':
                True,
                'author':
                DBRef(UserDocument.meta['collection'], ObjectId(visitor_id))
            }, {
                'private':
                True,
                'user':
                DBRef(UserDocument.meta['collection'], ObjectId(visitor_id))
            }]
        }

        cursor = LeaveMessageDocument.find(query).sort([
            ('leave_time', pymongo.ASCENDING)
        ]).skip(skip)

        if limit is not None:
            cursor = cursor.limit(limit)

        translate = LeaveMessageDocument.translate_dbref_in_document_list
        leave_message_list = yield LeaveMessageDocument.to_list(cursor)
        leave_message_list = yield translate(leave_message_list)

        # message_sum = yield LeaveMessageDocument.get_leave_message_number(
        #     user_id, visitor_id)
        # for i, leave_message in enumerate(leave_message_list):
        #     leave_message['floor'] = message_sum - i - skip

        for i, leave_message in enumerate(leave_message_list):
            leave_message['floor'] = skip + 1 + i

        raise gen.Return(leave_message_list)
Пример #19
0
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)
Пример #20
0
class StatusCommentDocument(Document):
    '''状态的评论.

    :Variables:
      - `status`: 相关的状态
      - `commenter`: 评论者
      - `comment_time`: 评论时间
      - `content`: 评论内容
      - `replyeder`: 被回复者, 如果有此属性, 说明该评论是回复某人的
    '''

    status = ReferenceField(StatusDocument, required=True)
    author = ReferenceField(UserDocument, required=True)
    comment_time = DateTimeField(required=True)
    content = StringField(required=True, max_length=200)
    replyeder = ReferenceField(UserDocument)

    meta = {'collection': 'home_status_comment'}

    @gen.coroutine
    def get_comment(comment_id):
        '''得到某一条评论, 是comment_list的一个item'''

        comment = yield StatusCommentDocument.find_one(
            {'_id': ObjectId(comment_id)})
        if comment:
            comment = yield StatusCommentDocument.translate_dbref_in_document(
                comment)

        raise gen.Return(comment)

    @gen.coroutine
    def get_comment_list(status_id, user_id, skip=0, limit=None):
        '''得到某一状态的评论, 只能看到共同好友和自己的评论.

        :Parameters:
          - `status_id`: 相关的状态
          - `user_id`: 查看评论者
          - `skip`: 默认0
          - `limit`: 默认None
        '''

        comment_list = []

        status = yield StatusDocument.find_one({'_id': ObjectId(status_id)})
        if status:
            status = yield StatusDocument.translate_dbref_in_document(status)
            if status['author']['_id'] == ObjectId(user_id):
                cursor = StatusCommentDocument.find({
                    'status':
                    DBRef(StatusDocument.meta['collection'],
                          ObjectId(status['_id']))
                })
            else:
                can_see = yield StatusDocument.can_see(status, user_id)
                if not can_see:
                    raise gen.Return(comment_list)

                friend_ids = yield FriendDocument.get_same_friend_ids(
                    status['author']['_id'], user_id)

                friend_ids += [
                    DBRef(UserDocument.meta['collection'], ObjectId(user_id)),
                    DBRef(UserDocument.meta['collection'],
                          ObjectId(status['author']['_id']))
                ]

                cursor = StatusCommentDocument.find({
                    'status':
                    DBRef(StatusDocument.meta['collection'],
                          ObjectId(status['_id'])),
                    'author': {
                        '$in': friend_ids
                    },
                    '$or': [{
                        'replyeder': {
                            '$exists': False
                        }
                    }, {
                        'replyeder': {
                            '$exists': True,
                            '$in': friend_ids
                        }
                    }]
                })

            cursor = cursor.sort([('comment_time', pymongo.ASCENDING)
                                  ]).skip(skip)

            if limit is not None:
                cursor = cursor.limit(limit)

            translate = StatusCommentDocument.translate_dbref_in_document_list
            comment_list = yield StatusCommentDocument.to_list(cursor)
            comment_list = yield translate(comment_list)

        raise gen.Return(comment_list)

    @gen.coroutine
    def get_comment_times(status_id):
        '''得到某一个评论的评论次数, 所有人的评论'''

        comment_times = yield StatusCommentDocument.find({
            'status':
            DBRef(StatusDocument.meta['collection'], ObjectId(status_id))
        }).count()

        raise gen.Return(comment_times)

    @gen.coroutine
    def get_comment_times_can_seen(status_id, user_id):
        '''得到某一个人能够看到的评论次数'''

        status = yield StatusDocument.get_status(status_id)
        if not status:
            raise gen.Return(0)

        if ObjectId(status['author']['_id']) == ObjectId(user_id):
            cnt = yield StatusCommentDocument.get_comment_times(status_id)
            raise gen.Return(cnt)

        can_see = yield StatusDocument.can_see(status, user_id)
        if not can_see:
            raise gen.Return(0)

        friend_ids = yield FriendDocument.get_same_friend_ids(
            status['author']['_id'], user_id)

        friend_ids += [
            DBRef(UserDocument.meta['collection'], ObjectId(user_id)),
            DBRef(UserDocument.meta['collection'],
                  ObjectId(status['author']['_id']))
        ]

        cnt = yield StatusCommentDocument.find({
            'status':
            DBRef(StatusDocument.meta['collection'], ObjectId(status['_id'])),
            'author': {
                '$in': friend_ids
            },
            '$or': [{
                'replyeder': {
                    '$exists': False
                }
            }, {
                'replyeder': {
                    '$exists': True,
                    '$in': friend_ids
                }
            }]
        }).count()

        raise gen.Return(cnt)

    @gen.coroutine
    def insert_one(document):
        comment_id = yield StatusCommentDocument.insert(document)
        comment_times = yield StatusCommentDocument.get_comment_times(
            document['status'].id)

        yield StatusDocument.update({'_id': ObjectId(document['status'].id)},
                                    {'$set': {
                                        'comment_times': comment_times
                                    }})

        raise gen.Return(comment_id)
Пример #21
0
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)
Пример #22
0
class MessageDocument(Document):
    '''消息系统中的消息

    :Variables:
      - `sender`: 发送者, 系统消息发送者不存在, 用户之间的消息必须存在, 哪些是系统消息,
                  见MessageTopic
      - `recipient`: 接收者
      - `message_type`: 消息类型
      - `time`: 消息产生的时间
      - `received`: 是否被接收, 前端是否已接收, 接收后未必已读
      - `read`: 接收者是否已经阅读
      - `data`: 相关的数据
    '''

    sender = ReferenceField(UserDocument)
    recipient = ReferenceField(UserDocument, required=True)
    message_type = StringField(
        required=True,
        candidate=[
            'comment:status', 'comment:topic', 'comment:market_goods',
            'comment:market_need', 'comment:activity', 'reply:status',
            'reply:topic', 'reply:surround_shop', 'reply:surround_goods',
            'reply:market_goods', 'reply:market_need', 'reply:activity',
            'reply:news', 'reply:leavemessage', 'like:status', 'like:topic',
            'like:market_goods', 'like:market_need', 'like:activity'
        ].extend(MessageTopic.all_topic))
    time = DateTimeField(required=True)
    received = BooleanField(required=True, default=False)
    read = BooleanField(required=True, default=False)
    data = ReferenceField()

    meta = {'collection': 'message'}

    @gen.coroutine
    def get_message_list(recipient_id,
                         message_topic=None,
                         read=None,
                         skip=0,
                         limit=None):
        '''得到消息列表'''

        query = MessageDocument.get_query(recipient_id, message_topic)

        if read is not None:
            assert isinstance(read, bool)
            query.update({'read': read})

        cursor = MessageDocument.find(query).sort([('time', pymongo.DESCENDING)
                                                   ]).skip(skip)

        if limit is not None:
            cursor = cursor.limit(limit)

        message_list = yield MessageDocument.to_list(cursor)
        for message in message_list:
            if 'sender' in message:
                message['sender'] = yield UserDocument.translate_dbref(
                    message['sender'])

            message['recipient'] = yield UserDocument.translate_dbref(
                message['recipient'])

            if 'data' not in message:
                continue

            if str(message['data'].collection) == str(
                    StatusDocument.meta['collection']):
                message['data'] = yield StatusDocument.get_status(
                    message['data'].id)
                continue

            message['data'] = yield Document.translate_dbref(message['data'])

            if message['data']:
                message['data'] = yield Document.translate_dbref_in_document(
                    message['data'], depth=2)

                if 'status' in message['data']:
                    message['data'][
                        'status'] = yield StatusDocument.get_status(
                            message['data']['status']['_id'])

        raise gen.Return(message_list)

    @gen.coroutine
    def set_read(recipient_id, message_topic=None):
        '''将消息设置为已读'''

        query = MessageDocument.get_query(recipient_id, message_topic)
        yield MessageDocument.update(
            query, {'$set': {
                'received': True,
                'read': True
            }}, multi=True)

        raise gen.Return()

    def set_read_sync(recipient_id, message_topic=None):
        '''将消息设置为已读'''

        query = MessageDocument.get_query(recipient_id, message_topic)
        MessageDocument.get_collection(True).update(
            query, {'$set': {
                'received': True,
                'read': True
            }}, multi=True)

    def set_received(recipient_id, message_topic=None):
        '''将消息设置为已接受'''

        query = MessageDocument.get_query(recipient_id, message_topic)
        MessageDocument.get_collection(True).update(
            query, {'$set': {
                'received': True
            }}, multi=True)

    def get_query(recipient_id, message_topic=None):
        '''得到查询'''

        query = {
            'recipient':
            DBRef(UserDocument.meta['collection'], ObjectId(recipient_id))
        }

        if message_topic is not None:
            if message_topic == MessageTopic._COMMENT_AND_REPLY:
                query.update(
                    {'message_type': {
                        '$regex': '(comment|reply):.*'
                    }})
            elif message_topic == MessageTopic.COMMENT:
                query.update({'message_type': {'$regex': 'comment:.*'}})
            elif message_topic == MessageTopic.REPLY:
                query.update({'message_type': {'$regex': 'reply:.*'}})
            elif message_topic == MessageTopic.LIKE:
                query.update({'message_type': {'$regex': 'like:.*'}})
            elif message_topic == MessageTopic._FRIENDS_DYNAMIC:
                query.update({
                    'message_type': {
                        '$in': [
                            MessageTopic.STATUS_NEW, MessageTopic.TOPIC_NEW,
                            MessageTopic.SHARE_NEW
                        ]
                    }
                })
            else:
                query.update({'message_type': message_topic})

        return query

    def get_message_number(recipient_id, message_topic=None, read=None):
        '''得到相关消息的数量'''

        query = MessageDocument.get_query(recipient_id, message_topic)
        if read is not None:
            assert isinstance(read, bool)
            query.update({'read': read})

        count = MessageDocument.get_collection(
            pymongo=True).find(query).count()

        return count

    def has_unreceived(recipient_id):
        '''是否有未读的消息'''

        message = MessageDocument.get_collection(pymongo=True).find_one({
            'recipient':
            DBRef(UserDocument.meta['collection'], ObjectId(recipient_id)),
            'received':
            False,
            'read':
            False
        })

        return True if message else False

    def get_unread_message_numbers(recipient_id):
        '''得到未读的消息个数'''

        unread_message_numbers = {
            MessageTopic._FRIENDS_DYNAMIC:
            MessageDocument.get_message_number(recipient_id,
                                               MessageTopic._FRIENDS_DYNAMIC,
                                               read=False),
            MessageTopic._COMMENT_AND_REPLY:
            MessageDocument.get_message_number(recipient_id,
                                               MessageTopic._COMMENT_AND_REPLY,
                                               read=False),
            MessageTopic.LIKE:
            MessageDocument.get_message_number(recipient_id,
                                               MessageTopic.LIKE,
                                               read=False),
            MessageTopic.CHAT_MESSAGE_NEW:
            ChatMessageDocument.get_message_number(recipient_id, read=False),
            MessageTopic.LEAVE_MESSAGE_NEW:
            MessageDocument.get_message_number(recipient_id,
                                               MessageTopic.LEAVE_MESSAGE_NEW,
                                               read=False),
            MessageTopic.FRIEND_REQUEST_NEW:
            MessageDocument.get_message_number(recipient_id,
                                               MessageTopic.FRIEND_REQUEST_NEW,
                                               read=False)
        }

        return unread_message_numbers