Example #1
0
class User(BaseModel, UserMixin):
    __tablename__ = 'users'

    email = db.Column(db.String(length=100))
    name = db.Column(db.String(length=100), unique=True)
    password = db.Column(db.Text)
    active = db.Column(db.Boolean, default=True)
Example #2
0
class PostTag(BaseModel):
    __tablename__ = 'post_tags'

    post_id = db.Column(db.Integer)
    tag_id = db.Column(db.Integer)

    @classmethod
    def update_multi(cls, post_id, tags):
        tags = set(tags)
        origin_tags = set([t.name for t in Post.query.get(post_id).tags])

        need_add = tags - origin_tags
        need_del = origin_tags - tags
        need_add_tag_ids = set()
        need_del_tag_ids = set()
        for tag_name in need_add:
            tag, _ = Tag.get_or_create(name=tag_name)
            need_add_tag_ids.add(tag.id)
        for tag_name in need_del:
            tag, _ = Tag.get_or_create(name=tag_name)
            need_del_tag_ids.add(tag.id)

        if need_del_tag_ids:
            cls.query.filter(cls.post_id == post_id,
                             cls.tag_id.in_(need_del_tag_ids)).delete(
                synchronize_session=False)
            db.session.commit()
        for tag_id in need_add_tag_ids:
            PostTag.get_or_create(post_id=post_id, tag_id=tag_id)

        clear_mc(MC_KEY_TAGS_BY_POST_ID % post_id)
Example #3
0
class AdminLog(db.Model):
    __table__ = 'adminlog'
    id = db.Column(db.Integer, primary_key=True)
    message = db.Column(db.Text)
    addTime = db.Column(db.DateTime, default=datetime.now())
    adminId = db.Column(db.ForeignKey('admin.id'))

    def __repr__(self):
        return "<AdminLog %r>" % self.id
Example #4
0
class Admin(db.Model):
    __tablename__ = 'admin'
    id = db.Column(db.Integer, primary_key=True)
    adminName = db.Column(db.String(100), unique=True)
    password = db.Column(db.String(100))
    logs = db.relationship('AdminLog', backref='admin')

    def __repr__(self):
        return "<Admin %r>" % self.adminName
Example #5
0
class UserLog(db.Model):
    __tablename__ = 'userlog'
    id = db.Column(db.Integer, primary_key=True)
    message = db.Column(db.Text)
    userId = db.Column(db.ForeignKey('user.id'))
    addTime = db.Column(db.DateTime, default=datetime.now())\

    def __repr__(self):
        return "<UserLog> %r" % self.id
Example #6
0
class Comment(db.Model):
    __tablename__ = 'comment'
    id = db.Column(db.Integer, primary_key=True)
    text = db.Column(db.Text)
    sendTime = db.Column(db.DateTime, default=datetime.now())
    sendUser = db.Column(db.ForeignKey('user.id'))
    commentBlog = db.Column(db.ForeignKey('blog.id'))

    def __repr__(self):
        return "<Comment %r>" % self.id
Example #7
0
class ReactItem(BaseModel):
    __tablename__ = 'react_items'

    target_id = db.Column(db.Integer)
    target_kind = db.Column(db.Integer)
    user_id = db.Column(db.Integer)
    reaction_type = db.Column(db.Integer)

    REACTION_KINDS = (K_UPVOTE, K_FUNNY, K_LOVE, K_SURPRISED, K_SAD) = range(5)

    REACTION_MAP = {
        'upvote': K_UPVOTE,
        'funny': K_FUNNY,
        'love': K_LOVE,
        'surprised': K_SURPRISED,
        'sad': K_SAD
    }

    @classmethod
    def create(cls, **kwargs):
        obj = super().create(**kwargs)
        react_name = next((name for name, type in cls.REACTION_MAP.items()
                           if type == obj.reaction_type), None)
        stat = ReactStats.get_by_target(obj.target_id, obj.target_kind)
        field = f'{react_name}_count'
        setattr(stat, field, getattr(stat, field) + 1)
        stat.save()
        return obj

    @classmethod
    @cache(MC_KEY_REACTION_ITEM_BY_USER_TARGET %
           ('{user_id}', '{target_id}', '{target_kind}'))
    def get_reaction_item(cls, user_id, target_id, target_kind):
        rv = cls.query.filter_by(user_id=user_id,
                                 target_id=target_id,
                                 target_kind=target_kind).first()
        return rv

    def delete(self):
        super().delete()
        stat = ReactStats.get_by_target(self.target_id, self.target_kind)
        react_name = next((name for name, type in self.REACTION_MAP.items()
                           if type == self.reaction_type), None)
        field = f'{react_name}_count'
        setattr(stat, field, getattr(stat, field) - 1)
        stat.save()

    def clear_mc(self):
        clear_mc(MC_KEY_REACTION_ITEM_BY_USER_TARGET %
                 (self.user_id, self.target_id, self.target_kind))

        if self.target_kind == K_COMMENT:
            comment_reacted.send(user_id=self.user_id,
                                 comment_id=self.target_id)
Example #8
0
class User(db.Model):
    __tablename__ = 'user'
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(100), unique=True)
    password = db.Column(db.String(100))
    userAvatar = db.Column(db.String(100))
    selfIntroduction = db.Column(db.String(250))
    userLogs = db.relationship('UserLog', backref='user')
    blogs = db.relationship('Blog', backref='user')

    def __repr__(self):
        return "<User %r>" % self.username
Example #9
0
class GithubUser(BaseModel):
    __tablename__ = 'github_users'

    gid = db.Column(db.Integer, unique=True)
    email = db.Column(db.String(100), unique=True)
    username = db.Column(db.String(100), unique=True)
    picture = db.Column(db.String(100), default='')
    link = db.Column(db.String(100), default='')

    def to_dict(self):
        return {
            key: value
            for key, value in self.__dict__.items() if not key.startswith('_')
        }
Example #10
0
class Comment(ReactMixin, BaseModel):
    __tablename__ = 'comments'

    github_id = db.Column(db.Integer)
    post_id = db.Column(db.Integer)
    reaction_type = db.Column(db.Integer)
    ref_id = db.Column(db.Integer)
    kind = K_COMMENT

    @property
    def content(self):
        rv = self.get_props_by_key('content')
        if rv:
            return rv.decode('utf-8')

    @property
    def html_content(self):
        content = self.content
        if not content:
            return ''
        return markdown(content)

    @property
    def user(self):
        return GithubUser.query.filter_by(gid=self.github_id).first()

    @property
    def n_likes(self):
        return self.stats.love_count

    def set_content(self, content):
        return self.set_props_by_key('content', content)

    def save(self, *args, **kwargs):
        content = kwargs.pop('content', None)
        if content is not None:
            self.set_content(content)
        return super().save()

    def clear_mc(self):
        for key in (MC_KEY_N_COMMENTS, MC_KEY_COMMENT_LIST):
            clear_mc(key % self.post_id)
Example #11
0
class Tag(BaseModel):
    __tablename__ = 'tags'

    name = db.Column(db.String(length=100), unique=True)

    @classmethod
    def get_by_name(cls, name):
        return cls.query.filter_by(name=name).first()

    @classmethod
    def create(cls, **kwargs):
        name = kwargs.pop('name')
        kwargs['name'] = name.lower()
        return super().create(**kwargs)
Example #12
0
class Blog(db.Model):
    __tablename__ = 'blog'
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(100))
    text = db.Column(db.Text)
    sendTime = db.Column(db.DateTime, default=datetime.now())
    modifyTime = db.Column(db.DateTime)
    userId = db.Column(db.ForeignKey('user.id'))

    def __repr__(self):
        return "<Blog %r>" % self.title
Example #13
0
class ReactStats(BaseModel):
    target_id = db.Column(db.Integer)
    target_kind = db.Column(db.Integer)
    upvote_count = db.Column(db.Integer, default=0)
    funny_count = db.Column(db.Integer, default=0)
    love_count = db.Column(db.Integer, default=0)
    surprised_count = db.Column(db.Integer, default=0)
    sad_count = db.Column(db.Integer, default=0)

    @classmethod
    @cache(MC_KEY_USER_REACT_STAT % ('{target_id}', '{target_kind}'))
    def get_by_target(cls, target_id, target_kind):
        rv = cls.query.filter_by(target_id=target_id,
                                 target_kind=target_kind).first()
        if not rv:
            rv = cls.create(target_id=target_id, target_kind=target_kind)
        return rv

    def clear_mc(self):
        clear_mc(MC_KEY_USER_REACT_STAT % (self.target_id, self.target_kind))
Example #14
0
class Post(CommentMixin, ReactMixin, BaseModel):
    __tablename__ = 'posts'

    STATUSES = (
        STATUS_UNPUBLISHED,
        STATUS_ONLINE
    ) = range(2)

    TYPES = (TYPE_ARTICLE, TYPE_PAGE) = range(2)

    title = db.Column(db.String(length=100), unique=True)
    author_id = db.Column(db.Integer)
    slug = db.Column(db.String(length=100))
    summary = db.Column(db.String(length=255))
    can_comment = db.Column(db.Boolean, default=True)
    status = db.Column(db.Integer, default=STATUS_UNPUBLISHED)
    type = db.Column(db.Integer, default=TYPE_ARTICLE)
    kind = K_POST

    @classmethod
    def create(cls, **kwargs):
        tags = kwargs.pop('tags', [])
        content = kwargs.pop('content')
        obj = super().create(**kwargs)
        if tags:
            PostTag.update_multi(obj.id, tags)
        obj.set_content(content)
        return obj

    def update_tags(self, tagnames):
        if tagnames:
            PostTag.update_multi(self.id, tagnames)
        return True

    @property
    @cache(MC_KEY_TAGS_BY_POST_ID % ('{self.id}'))
    def tags(self):
        pts = PostTag.query.filter_by(post_id=self.id).all()
        if not pts:
            return []
        ids = [pt.tag_id for pt in pts]
        return Tag.query.filter(Tag.id.in_(ids)).all()

    @property
    def author(self):
        from app.models import User
        return User.query.get(self.author_id)

    def preview_url(self):
        return f'/{self.__class__.__name__.lower()}/{self.id}/preview'

    def set_content(self, content):
        return self.set_props_by_key('content', content)

    def save(self, **kwargs):
        content = kwargs.pop('content', None)
        if content is not None:
            self.set_content(content)
        return super().save()

    @property
    def content(self):
        rv = self.get_props_by_key('content')
        if rv:
            return rv.decode('utf-8')

    @property
    def html_content(self):
        content = self.content
        if not content:
            return ''
        return markdown(content)

    @property
    def excerpt(self):
        if self.summary:
            return self.summary
        s = MLStripper()
        s.feed(self.html_content)
        return trunc_utf8(BQ_REGEX.sub('', s.get_data()).replace('\n', ''), 100)

    @property
    def toc(self):
        content = self.content
        if not content:
            return ''
        toc.reset_toc()
        toc_md.parse(content)
        return toc.render_toc(level=4)

    @cache(MC_KEY_RELATED % ('{self.id}'), ONE_HOUR)
    def get_related(self, limit=4):
        tag_ids = [tag.id for tag in self.tags]
        if not tag_ids:
            return []
        post_ids = set(PostTag.query.filter(
            PostTag.post_id != self.id, PostTag.tag_id.in_(tag_ids))
                       .with_entities(PostTag.post_id).all())

        excluded_ids = (self.query.filter(Post.status != self.STATUS_ONLINE)
                        .with_entities(Post.id).all())

        post_ids -= set(excluded_ids)

        try:
            post_ids = random.sample(post_ids, limit)
        except ValueError:
            pass

        return self.get_multi(post_ids)

    def clear_mc(self):
        clear_mc(MC_KEY_RELATED % self.id)
        clear_mc(MC_KEY_POST_BY_SLUG % self.slug)
        for key in [MC_KEY_FEED, MC_KEY_SITEMAP, MC_KEY_SEARCH,
                    MC_KEY_ARCHIVES, MC_KEY_TAGS]:
            clear_mc(key)

        for i in [True, False]:
            clear_mc(MC_KEY_ALL_POSTS % i)
        clear_mc(MC_KEY_ARCHIVE % self.created_at.year)

        for tag in self.tags:
            clear_mc(MC_KEY_TAG % tag.id)

    @classmethod
    @cache(MC_KEY_POST_BY_SLUG % '{slug}')
    def get_by_slug(cls, slug):
        return cls.query.filter_by(slug=slug).first()

    @classmethod
    @cache(MC_KEY_ALL_POSTS % '{with_page}')
    def get_all(cls, with_page=True):
        if with_page:
            return cls.query.filter_by(status=cls.STATUS_ONLINE).order_by(
                cls.id.desc()).all()
        return cls.query.filter_by(status=cls.STATUS_ONLINE,
                                   type=cls.TYPE_ARTICLE).order_by(
            cls.id.desc()).all()

    @classmethod
    def cache(cls, ident):
        if str(ident).isdigit() or hasattr(ident, 'post_id'):
            return super().cache(ident)
        return cls.get_by_slug(ident)

    @property
    def is_page(self):
        return self.type == self.TYPE_PAGE

    @property
    def url(self):
        return f'/page/{self.slug}' if self.is_page else super().url
Example #15
0
class BaseModel(db.Model):
    __abstract__ = True

    _redis = None

    id = db.Column(db.Integer, primary_key=True)
    created_at = db.Column(db.DateTime, default=datetime.utcnow)

    @property
    def url(self):
        return f'/{self.__class__.__name__.lower()}/{self.id}/'

    @property
    def redis(self):
        global _redis
        if _redis is None:
            _redis = context.get('redis')
        return _redis

    @classmethod
    def get_or_create(cls, **kwargs):
        instance = cls.query.filter_by(**kwargs).first()
        if instance:
            return instance, False
        return cls.create(**kwargs), True

    def get_db_key(self, key):
        return f'{self.__class__.__name__}/{self.id}/props/{key}'

    def set_props_by_key(self, key, value):
        key = self.get_db_key(key)
        return self.redis.set(key, value)

    def get_props_by_key(self, key):
        key = self.get_db_key(key)
        return self.redis.get(key) or b''

    @classmethod
    def get_or_404(cls, id):
        obj = cls.cache(id)
        if not obj:
            abort(404)
        return obj

    @classmethod
    @cache(MC_KEY_ITEM_BY_ID % ('{cls.__name__}', '{id}'))
    def cache(cls, id):
        return cls.query.get(id)

    @classmethod
    def get_multi(cls, ids):
        return [cls.cache(id) for id in ids]

    @classmethod
    def create(cls, **kwargs):
        instance = cls(**kwargs)
        db.session.add(instance)
        db.session.commit()
        cls.__flush__(instance)
        return instance

    def save(self):
        db.session.add(self)
        db.session.commit()
        self.__flush__(self)

    def delete(self):
        db.session.delete(self)
        db.session.commit()
        self.__flush__(self)

    @classmethod
    def __flush__(cls, target):
        clear_mc(MC_KEY_ITEM_BY_ID % (target.__class__.__name__, target.id))
        target.clear_mc()

    def clear_mc(self):
        ...