コード例 #1
0
ファイル: models.py プロジェクト: endsh/chiki
class Group(db.Document):
    """ 管理组 """

    name = db.StringField(verbose_name='组名')
    power = db.ListField(db.ReferenceField('View'), verbose_name='使用权限')
    can_create = db.ListField(db.ReferenceField('View'), verbose_name='创建权限')
    can_edit = db.ListField(db.ReferenceField('View'), verbose_name='编辑权限')
    can_delete = db.ListField(db.ReferenceField('View'), verbose_name='删除权限')
    modified = db.DateTimeField(default=datetime.now, verbose_name='修改时间')
    created = db.DateTimeField(default=datetime.now, verbose_name='创建时间')

    def __unicode__(self):
        return self.name

    @cached_property
    def power_list(self):
        return [x.name for x in self.power]

    @cached_property
    def can_create_list(self):
        return [x.name for x in self.can_create]

    @cached_property
    def can_edit_list(self):
        return [x.name for x in self.can_edit]

    @cached_property
    def can_delete_list(self):
        return [x.name for x in self.can_delete]
コード例 #2
0
ファイル: models.py プロジェクト: next-backend/chiki
class User(db.Document, UserMixin):
    """ 用户模型 """

    MENU_ICON = 'user'

    SEX_UNKNOWN = 'unknown'
    SEX_MALE = 'male'
    SEX_FEMALE = 'female'
    SEX_CHOICES = (
        (SEX_UNKNOWN, '保密'),
        (SEX_MALE, '男'),
        (SEX_FEMALE, '女'),
    )
    SEX_VALUES = [x[0] for x in SEX_CHOICES]
    SEX_DICT = dict(SEX_CHOICES)
    SEX_FROM_WECHAT = {0: SEX_UNKNOWN, 1: SEX_MALE, 2: SEX_FEMALE}

    id = db.IntField(primary_key=True, verbose_name='ID')
    phone = db.StringField(max_length=20, verbose_name='手机')
    email = db.StringField(max_length=40, verbose_name='邮箱')
    password = db.StringField(max_length=40, verbose_name='密码')
    nickname = db.StringField(max_length=40, verbose_name='昵称')
    avatar = db.XImageField(verbose_name='头像')
    birthday = db.DateTimeField(verbose_name='生日')
    sex = db.StringField(default=SEX_UNKNOWN,
                         choices=SEX_CHOICES,
                         verbose_name='性别')
    location = db.AreaField(verbose_name='所在地')
    address = db.StringField(max_length=100, verbose_name='通讯地址')
    resume = db.StringField(max_length=100, verbose_name='简介')
    debug = db.BooleanField(default=False, verbose_name='允许调试')
    active = db.BooleanField(default=True, verbose_name='激活')
    inviter = db.ReferenceField('User', verbose_name='邀请者')
    inviter2 = db.ReferenceField('User', verbose_name='邀请者2')
    inviter3 = db.ReferenceField('User', verbose_name='邀请者3')
    channel = db.IntField(verbose_name='注册渠道ID')
    spm = db.StringField(max_length=100, verbose_name='登录SPM')
    ip = db.StringField(max_length=20, verbose_name='登录IP')
    generate = db.BooleanField(default=False, verbose_name='生成')
    error = db.IntField(default=0, verbose_name='登录错误次数')
    locked = db.DateTimeField(default=lambda: datetime(1970, 1, 1),
                              verbose_name='锁定时间')
    logined = db.DateTimeField(default=lambda: datetime.now(),
                               verbose_name='登录时间')
    registered = db.DateTimeField(default=lambda: datetime.now(),
                                  verbose_name='注册时间')

    meta = {
        'indexes': [
            'phone',
            'nickname',
            'ip',
            '-logined',
            '-registered',
        ],
    }

    @property
    def sex_text(self):
        return self.SEX_DICT[self.sex]
コード例 #3
0
ファイル: models.py プロジェクト: txfyteen/chiki
class Field(db.EmbeddedDocument):
    """ 选项 """

    TYPE_INT = 'IntField'
    TYPE_STRING = 'StringField'
    TYPE_REF = 'ReferenceField'
    TYPE_IMAGE = 'XImageField'
    TYPE_FILE = 'XFileField'
    TYPE_DATETIME = 'DateTimeField'
    TYPE_BOOL = 'BooleanField'
    TYPE_CHOICES = [
        (TYPE_INT, TYPE_INT),
        (TYPE_STRING, TYPE_STRING),
        (TYPE_REF, TYPE_REF),
        (TYPE_IMAGE, TYPE_IMAGE),
        (TYPE_FILE, TYPE_FILE),
        (TYPE_DATETIME, TYPE_DATETIME),
        (TYPE_BOOL, TYPE_BOOL),
    ]

    key = db.StringField(max_length=100, verbose_name='键名')
    type = db.StringField(default=TYPE_INT,
                          choices=TYPE_CHOICES,
                          verbose_name='类型')
    name = db.StringField(max_length=100, verbose_name='名称')
    default = db.StringField(max_length=100, verbose_name='默认值')
    model = db.ReferenceField('Model', verbose_name='引用')
    choices = db.ReferenceField('Choices', verbose_name='选项')

    def __unicode__(self):
        return '%s - %s' % (self.key, self.name)
コード例 #4
0
class Action(db.Document):
    """ 功能模型 """

    MENU_ICON = 'bars'

    key = db.StringField(verbose_name='ID')
    name = db.StringField(verbose_name='名称')
    desc = db.StringField(verbose_name='描述')
    icon = db.Base64ImageField(verbose_name='图标')
    active_icon = db.Base64ImageField(verbose_name='激活图标')
    module = choice(db.StringField(verbose_name='模块'), 'action_module', '功能模块')
    data = db.StringField(verbose_name='数据')
    target = db.StringField(verbose_name='目标')
    share = db.EmbeddedDocumentField(Share, verbose_name='分享')
    sort = db.IntField(verbose_name='排序')
    android_start = db.ReferenceField(AndroidVersion, verbose_name='安卓版本')
    android_end = db.ReferenceField(AndroidVersion, verbose_name='安卓最大版本')
    ios_start = db.ReferenceField(IOSVersion, verbose_name='IOS版本')
    ios_end = db.ReferenceField(IOSVersion, verbose_name='IOS最大版本')
    login = db.BooleanField(default=False, verbose_name='登陆')
    login_show = db.BooleanField(default=False, verbose_name='显示')
    debug = db.BooleanField(default=False, verbose_name='调试')
    enable = db.StringField(default=Enable.ENABLED,
                            verbose_name='状态',
                            choices=Enable.CHOICES)
    modified = db.DateTimeField(default=datetime.now, verbose_name='修改时间')
    created = db.DateTimeField(default=datetime.now, verbose_name='创建时间')

    meta = {
        'indexes': [
            'key',
            'sort',
            '-created',
        ]
    }

    @property
    def detail(self):
        return dict(
            id=self.key,
            name=self.name,
            desc=self.desc,
            icon=self.icon.base64,
            active_icon=self.active_icon.base64,
            data=self.data,
            target=self.target,
            share=unicode(self.share),
            login=self.login,
            login_show=self.login_show,
            debug=self.debug,
        )
コード例 #5
0
ファイル: models.py プロジェクト: txfyteen/chiki
class ActionItem(db.Document):
    """ 功能模型 """

    MENU_ICON = 'bars'

    name = db.StringField(verbose_name='名称')
    key = db.StringField(verbose_name='键名')
    desc = db.StringField(verbose_name='描述')
    icon = db.XImageField(verbose_name='图标')
    module = db.ReferenceField('ActionModule', verbose_name='模块')
    action = db.StringField(default=Action.DEFAULT,
                            verbose_name='动作',
                            choices=Action.CHOICES)
    url = db.StringField(verbose_name='链接')
    share = db.EmbeddedDocumentField(ShareItem, verbose_name='分享')
    sort = db.IntField(verbose_name='排序')
    android_version = db.ReferenceField(AndroidVersion, verbose_name='安卓版本')
    android_version_end = db.ReferenceField(AndroidVersion,
                                            verbose_name='安卓最大版本')
    ios_version = db.ReferenceField(IOSVersion, verbose_name='IOS版本')
    ios_version_end = db.ReferenceField(IOSVersion, verbose_name='IOS最大版本')
    login = db.BooleanField(default=False, verbose_name='登陆')
    login_show = db.BooleanField(default=False, verbose_name='登录显示')
    enable = db.StringField(default=Enable.ENABLED,
                            verbose_name='状态',
                            choices=Enable.CHOICES)
    modified = db.DateTimeField(default=datetime.now, verbose_name='修改时间')
    created = db.DateTimeField(default=datetime.now, verbose_name='创建时间')

    meta = {
        'indexes': [
            'key',
            'sort',
            '-created',
        ]
    }

    @property
    def detail(self):
        return dict(
            name=self.name,
            key=self.key,
            desc=self.desc,
            icon=self.icon.link,
            action=self.action,
            login=self.login,
            url=self.url,
            share=unicode(self.share),
            extras='',
        )
コード例 #6
0
ファイル: models.py プロジェクト: endsh/chiki
class AdminUser(db.Document):
    """ 管理员 """

    xid = db.IntField(verbose_name='XID')
    username = db.StringField(verbose_name='用户')
    password = db.StringField(verbose_name='密码')
    group = db.ReferenceField('Group', verbose_name='组')
    root = db.BooleanField(default=False, verbose_name='超级管理员')
    active = db.BooleanField(default=True, verbose_name='激活')
    freezed = db.DateTimeField(verbose_name='冻结时间')
    logined = db.DateTimeField(default=datetime.now, verbose_name='登录时间')
    modified = db.DateTimeField(default=datetime.now, verbose_name='修改时间')
    created = db.DateTimeField(default=datetime.now, verbose_name='创建时间')

    def __unicode__(self):
        return self.username

    def is_user(self):
        return True

    def is_authenticated(self):
        """ 是否登录 """
        return True

    def is_active(self):
        """ 是否激活 """
        return self.active

    def is_anonymous(self):
        """ 是否游客 """
        return False

    def get_id(self):
        s = sign(current_app.config.get('SECRET_KEY'), password=self.password)
        return '{0}|{1}'.format(self.id, s)
コード例 #7
0
ファイル: models.py プロジェクト: endsh/chiki
class AdminUserLoginLog(db.Document):
    """ 管理登录日志 """

    TYPE = db.choices(login='******', logout='退出', error='密码错误')

    user = db.ReferenceField('AdminUser', verbose_name='用户')
    type = db.StringField(choices=TYPE.CHOICES, verbose_name='类型')
    spm = db.StringField(max_length=100, verbose_name='SPM')
    ip = db.StringField(max_length=20, verbose_name='IP')
    created = db.DateTimeField(default=datetime.now, verbose_name='创建时间')

    def __unicode__(self):
        return '%s' % self.user.username

    @staticmethod
    def log(user, type, spm=None, ip=None):
        spm = spm if spm else get_spm()
        ip = ip if ip else get_ip()
        AdminUserLoginLog(user=user, type=type, spm=spm, ip=ip).save()

    @staticmethod
    def login(user):
        AdminUserLoginLog.log(user, AdminUserLoginLog.TYPE.LOGIN)

    @staticmethod
    def logout(user):
        AdminUserLoginLog.log(user, AdminUserLoginLog.TYPE.LOGOUT)

    @staticmethod
    def error(user):
        AdminUserLoginLog.log(user, AdminUserLoginLog.TYPE.ERROR)
コード例 #8
0
ファイル: models.py プロジェクト: Star2One/chiki
class Slide(db.Document):
    """ 广告模型 """

    MENU_ICON = 'paw'

    key = db.StringField(verbose_name='ID')
    name = db.StringField(verbose_name='名称')
    icon = db.XImageField(verbose_name='图标')
    module = choice(db.StringField(verbose_name='模块'), 'slide_module', '广告模块')
    target = db.StringField(verbose_name='目标')
    share = db.EmbeddedDocumentField(Share, verbose_name='分享')
    sort = db.IntField(verbose_name='排序')
    android_start = db.ReferenceField(AndroidVersion, verbose_name='安卓版本')
    android_end = db.ReferenceField(AndroidVersion, verbose_name='安卓最大版本')
    ios_start = db.ReferenceField(IOSVersion, verbose_name='IOS版本')
    ios_end = db.ReferenceField(IOSVersion, verbose_name='IOS最大版本')
    login = db.BooleanField(default=False, verbose_name='登陆')
    enable = db.StringField(default=Enable.ENABLED, verbose_name='状态', choices=Enable.CHOICES)
    modified = db.DateTimeField(default=datetime.now, verbose_name='修改时间')
    created = db.DateTimeField(default=datetime.now, verbose_name='创建时间')

    meta = {
        'indexes': [
            'key',
            'sort',
            '-created',
        ]
    }

    @property
    def detail(self):
        return dict(
            id=self.key,
            name=self.name,
            icon=self.icon.link,
            action=self.action,
            login=self.login,
            url=self.url,
            share=unicode(self.share),
            extras='',
        )
コード例 #9
0
ファイル: models.py プロジェクト: txfyteen/chiki
class SlideItem(db.Document):
    """ 广告模型 """

    MENU_ICON = 'paw'

    name = db.StringField(verbose_name='名称')
    key = db.StringField(verbose_name='键名')
    icon = db.XImageField(verbose_name='图标')
    module = db.ReferenceField('SlideModule', verbose_name='模块')
    action = db.StringField(default=Action.DEFAULT,
                            verbose_name='动作',
                            choices=Action.CHOICES)
    url = db.StringField(verbose_name='链接')
    share = db.EmbeddedDocumentField(ShareItem, verbose_name='分享')
    sort = db.IntField(verbose_name='排序')
    login = db.BooleanField(default=False, verbose_name='登陆')
    enable = db.StringField(default=Enable.ENABLED,
                            verbose_name='状态',
                            choices=Enable.CHOICES)
    modified = db.DateTimeField(default=datetime.now, verbose_name='修改时间')
    created = db.DateTimeField(default=datetime.now, verbose_name='创建时间')

    meta = {
        'indexes': [
            'key',
            'sort',
            '-created',
        ]
    }

    @property
    def detail(self):
        return dict(
            name=self.name,
            key=self.key,
            icon=self.icon.link,
            action=self.action,
            login=self.login,
            url=self.url,
            share=unicode(self.share),
            extras='',
        )
コード例 #10
0
class TraceLog(db.Document):
    """ 监控统计 """

    MENU_ICON = 'bug'

    user = db.ReferenceField('User', verbose_name='用户')
    key = db.StringField(verbose_name='KEY')
    tid = db.StringField(verbose_name='TID')
    label = db.StringField(verbose_name='标识')
    value = db.StringField(verbose_name='结果')
    created = db.DateTimeField(default=datetime.now, verbose_name='创建时间')

    meta = {
        'indexes': [
            'key',
            'tid',
            'user',
            'label',
            '-created',
        ]
    }
コード例 #11
0
class ShareLog(db.Document):
    """ 分享日志 """

    STATUS = db.choices(success='成功', cancel='取消', error='错误')
    MEDIA = db.choices(timeline='朋友圈', message='消息', qq='QQ', qzone='qzone')

    user = db.ReferenceField('User', verbose_name='用户')
    media = db.StringField(verbose_name='平台', choices=MEDIA.CHOICES)
    title = db.StringField(verbose_name='标题')
    desc = db.StringField(verbose_name='描述')
    link = db.StringField(verbose_name='链接')
    image = db.StringField(verbose_name='图片链接')
    status = db.StringField(verbose_name='状态', choices=STATUS.CHOICES)
    created = db.DateTimeField(default=datetime.now, verbose_name='创建时间')

    meta = {
        'indexes': [
            'user',
            'link',
            '-created',
        ]
    }
コード例 #12
0
ファイル: models.py プロジェクト: endsh/chiki
class AdminChangeLog(db.Document):
    """ 管理员操作日志 """

    TYPE = db.choices(edit='修改', created='创建', delete='删除')

    user = db.ReferenceField('AdminUser', verbose_name='用户')
    model = db.StringField(verbose_name='模块')
    before_data = db.StringField(verbose_name='操作前')
    after_data = db.StringField(verbose_name='操作后')
    type = db.StringField(verbose_name='类型', choices=TYPE.CHOICES)
    spm = db.StringField(verbose_name='spm')
    ip = db.StringField(verbose_name='IP')
    headers = db.StringField(verbose_name='头信息')
    created = db.DateTimeField(default=datetime.now, verbose_name='创建时间')

    meta = dict(indexes=['-created'], )

    @staticmethod
    def log(user, model, before_data, after_data, type, **kwargs):
        ip = kwargs.get('ip', get_ip())
        spm = kwargs.get('spm', get_spm())
        headers = kwargs.get('headers', request.headers)
        AdminChangeLog(user=user,
                       model=model,
                       before_data=before_data,
                       after_data=after_data,
                       type=type,
                       ip=ip,
                       spm=spm,
                       headers=str(headers)).save()

    @staticmethod
    def modify_data(user, model, **kwargs):
        # 使用 before = after = dict(id=model.id) 到有内存引用的问题
        before = dict(id=model.id)
        after = dict(id=model.id)
        if kwargs.get('form'):
            try:
                for k, v in kwargs.get('form').data.iteritems():
                    if v != model[k]:
                        before[k] = model[k]
                        after[k] = v
            except:
                pass
        else:
            before = model.to_mongo()
        if kwargs.get('type') == 'delete':
            after = ''
        AdminChangeLog.log(
            user=user,
            model=model.__class__.__name__,
            before_data=str(before),
            after_data=str(after),
            type=kwargs.get('type'),
        )

    @staticmethod
    def dropdown_modify(user, model, **kwargs):
        before_data = dict(id=kwargs.get('id'))
        after_data = dict(id=kwargs.get('id'))
        key = kwargs.get('key')
        before_data[key] = kwargs.get('before_data')
        after_data[key] = kwargs.get('after_data')

        AdminChangeLog.log(user=user,
                           model=model.__name__,
                           before_data=str(before_data),
                           after_data=str(after_data),
                           type='edit',
                           what='waht')
コード例 #13
0
ファイル: models.py プロジェクト: txfyteen/chiki
class View(db.Document):
    """ 管理 """

    MENU_ICON = 'futbol-o'

    TYPE_VIEW = 'view'
    TYPE_MODEL = 'model'
    TYPE_CATE = 'cate'
    TYPE_CHOICES = [
        (TYPE_VIEW, '默认'),
        (TYPE_MODEL, '模型'),
        (TYPE_CATE, '分类'),
    ]

    name = db.StringField(max_length=100, verbose_name='名称')
    label = db.StringField(max_length=100, verbose_name='标识')
    type = db.StringField(default=TYPE_VIEW,
                          choices=TYPE_CHOICES,
                          verbose_name='类型')
    model = db.ReferenceField('Model', verbose_name='模型')
    icon = db.StringField(max_length=100, verbose_name='图标')
    page_size = db.IntField(default=50, verbose_name='分页数')
    can_create = db.BooleanField(default=True, verbose_name='能创建')
    can_edit = db.BooleanField(default=True, verbose_name='能修改')
    can_delete = db.BooleanField(default=True, verbose_name='能删除')
    column_default_sort = db.StringField(max_length=100, verbose_name='默认排序')
    column_list = db.ListField(db.StringField(), verbose_name='显示列表')
    column_center_list = db.ListField(db.StringField(), verbose_name='居中列表')
    column_hidden_list = db.ListField(db.StringField(), verbose_name='隐藏列表')
    column_filters = db.ListField(db.StringField(), verbose_name='过滤器列表')
    column_sortable_list = db.ListField(db.StringField(), verbose_name='排序列表')
    column_searchable_list = db.ListField(db.StringField(),
                                          verbose_name='查找列表')
    form_excluded_columns = db.ListField(db.StringField(),
                                         verbose_name='表单隐藏列表')
    modified = db.DateTimeField(default=datetime.now, verbose_name='修改时间')
    created = db.DateTimeField(default=datetime.now, verbose_name='创建时间')

    def __unicode__(self):
        return self.name

    def setup(self, admin, view):
        view.name = self.label or view.name
        view.menu_icon_value = self.icon or view.menu_icon_value
        if hasattr(view, 'model'):
            if not view.menu_icon_value:
                if view.model and hasattr(view.model, 'MENU_ICON'):
                    view.menu_icon_value = view.model.MENU_ICON
                else:
                    view.menu_icon_value = 'file-o'
            view.page_size = self.page_size or view.page_size
            view.can_create = self.can_create
            view.can_edit = self.can_edit
            view.can_delete = self.can_delete
            if self.column_default_sort:
                try:
                    view.column_default_sort = json.loads(
                        self.column_default_sort)
                except:
                    pass
            view.column_list = self.column_list or view.column_list
            view.column_center_list = self.column_center_list or getattr(
                view, 'column_center_list', None)
            view.column_hidden_list = self.column_hidden_list or getattr(
                view, 'column_hidden_list', None)
            view.column_filters = self.column_filters or view.column_filters
            view.column_sortable_list = self.column_sortable_list or view.column_sortable_list
            view.column_searchable_list = self.column_searchable_list or view.column_searchable_list
            view.form_excluded_columns = self.form_excluded_columns or view.form_excluded_columns
            view._refresh_cache()
        elif not view.menu_icon_value:
            if hasattr(view, 'MENU_ICON'):
                view.menu_icon_value = view.MENU_ICON
        admin._refresh()

    def save(self):
        super(View, self).save()

        if current_app.cool_manager and not current_app.cool_manager.loading:
            for admin in current_app.extensions.get('admin', []):
                for view in admin._views:
                    if view.__class__.__name__ == self.name:
                        self.setup(admin, view)
                        break

    def add_text(self, key):
        attr = getattr(self, key)
        if attr:
            return '    %s = %s' % (key, json.dumps(attr))

    @property
    def code_text(self):
        texts = []
        keys = [
            'column_list', 'column_center_list', 'column_hidden_list',
            'column_filters', 'column_sortable_list', 'column_searchable_list',
            'form_excluded_columns'
        ]
        for key in keys:
            text = self.add_text(key)
            if text:
                texts.append(text)
        return '\n'.join(texts)
コード例 #14
0
class QRCode(db.Document):
    """ 二维码 """

    MENU_ICON = 'qrcode'

    user = db.ReferenceField('User', verbose_name='用户')
    url = db.StringField(verbose_name='链接')
    image = db.XImageField(verbose_name='二维码')
    modified = db.DateTimeField(default=datetime.now, verbose_name='修改时间')
    created = db.DateTimeField(default=datetime.now, verbose_name='创建时间')

    meta = dict(indexes=['user', '-created'])

    @staticmethod
    def get(user, url=None):
        qr = QRCode.objects(user=user.id).first()
        if not qr:
            qr = QRCode(user=user.id, url=url)
            qr.save()

        if not qr.url and url:
            qr.url = url

        config = current_app.config.get('QRCODE', {})
        if config.get('wxclient', True) and (not qr.url or datetime.now() >
                                             qr.modified + timedelta(days=25)):
            data = dict(
                expire_seconds=2592000,
                action_name='QR_SCENE',
                action_info=dict(scene=dict(scene_id=user.id)),
            )
            qr.url = current_app.wxclient.create_qrcode(**data).get('url')
            qr.modified = datetime.now()

        if qr.url and not qr.image:

            @retry(3)
            def simple():
                qr.create_image(user)

        qr.save()
        return qr

    def create_qrcode(self, config):
        logo = config.get('logo', current_app.get_data_path('imgs/logo.jpg'))
        A, B, C = 250, 66, 58
        qr = qrcode.QRCode(version=2,
                           box_size=10,
                           border=1,
                           error_correction=qrcode.constants.ERROR_CORRECT_M)
        qr.add_data(self.url)
        qr.make(fit=True)
        im = qr.make_image()
        im = im.convert("RGBA")
        im = im.resize((A, A), Image.BILINEAR)

        if config.get('qr_logo', True):
            em = Image.new("RGBA", (B, B), "white")
            im.paste(em, ((A - B) / 2, (A - B) / 2), em)

            with open(logo) as fd:
                icon = Image.open(StringIO(fd.read()))
            icon = icon.resize((C, C), Image.ANTIALIAS)
            icon = icon.convert("RGBA")
            im.paste(icon, ((A - C) / 2, (A - C) / 2), icon)

        qr_width = config.get('qr_width', im.size[0])
        im = im.resize((qr_width, qr_width), Image.BILINEAR)
        return im

    def create_bg(self, config, user, qr):
        logo = config.get('logo', current_app.get_data_path('imgs/logo.jpg'))
        bgpath = config.get('bg')
        if bgpath:
            with open(bgpath) as fd:
                bg = Image.open(StringIO(fd.read()))
            qr_x = config.get('qr_x', (bg.size[0] - qr.size[0]) / 2)
            qr_y = config.get('qr_y', bg.size[1] / 2)

            bg.convert("RGBA")
            bg.paste(qr, (qr_x, qr_y), qr)

            if user.avatar:
                ic = Image.open(StringIO(user.avatar.content))
            else:
                with open(logo) as fd:
                    ic = Image.open(StringIO(fd.read()))

            avatar_width = config.get('avatar_width', ic.size[0])
            avatar_x = config.get('avatar_x', (bg.size[0] - avatar_width) / 2)
            avatar_y = config.get('avatar_y', bg.size[1] / 2)
            ic = ic.resize((avatar_width, avatar_width), Image.ANTIALIAS)
            ic = ic.convert("RGBA")

            if config.get('avatar_circle', False):
                bigsize = (ic.size[0] * 3, ic.size[1] * 3)
                mask = Image.new('L', bigsize, 0)
                draw = ImageDraw.Draw(mask)
                draw.ellipse((0, 0) + bigsize, fill=255)
                del draw
                mask = mask.resize(ic.size, Image.ANTIALIAS)
                ic.putalpha(mask)
            bg.paste(ic, (avatar_x, avatar_y), ic)
            return bg
        return qr

    def textsize(self, user, draw, font, width, texts):
        w, has_nick = 0, False
        for text in texts:
            if type(text) in [list, tuple]:
                text = text[0]

            if '<nickname>' in text:
                has_nick = True

            text = text.replace('<id>', str(user.id))
            text = text.replace('<nickname>', user.nickname or '佚名')
            text = text.replace('<expire>',
                                (self.modified +
                                 timedelta(days=30)).strftime('%Y-%m-%d'))
            w += draw.textsize(text, font=font)[0]

        limit = len(user.nickname)
        if has_nick:
            nick_width = draw.textsize(user.nickname or '佚名', font=font)[0]
            while limit > 4 and width - w + draw.textsize(
                    user.nickname[:limit], font=font)[0] < nick_width:
                limit -= 1
            w += draw.textsize(user.nickname[:limit],
                               font=font)[0] - nick_width
        return (width - w) / 2, limit

    def draw_texts(self, config, user, bg):
        draw = ImageDraw.Draw(bg)
        default = config.get('font', current_app.get_data_path('fonts/yh.ttf'))
        for line in config.get('lines', []):
            size = line.get('size', 18)
            font = ImageFont.truetype(line.get('font', default), size)
            x = line.get('x', 0)
            y = line.get('y', 0)

            texts = line.get('texts', [])
            if x == 'center':
                x, limit = self.textsize(user, draw, font, bg.size[0], texts)

            for text in texts:
                color = line.get('color', '#333333')
                if type(text) in [list, tuple]:
                    text, color = text[0], text[1]
                text = text.replace('<id>', str(user.id))
                text = text.replace('<nickname>', user.nickname[:limit]
                                    or '佚名')
                text = text.replace('<expire>',
                                    (self.modified +
                                     timedelta(days=30)).strftime('%Y-%m-%d'))
                width, _ = draw.textsize(text, font=font)
                draw.text((x, y), text, font=font, fill=color)
                x += width
        del draw
        return bg

    def create_image(self, user):
        config = current_app.config.get('QRCODE', {})
        qr = self.create_qrcode(config)
        bg = self.create_bg(config, user, qr)
        bg = self.draw_texts(config, user, bg)
        stream = StringIO()
        bg.save(stream, format='png')
        self.image = dict(stream=stream, format='png')
        self.save()