class ImageItem(db.Document): """ 图片模型 """ MENU_ICON = 'picture-o' image = db.XImageField(verbose_name='图片') created = db.DateTimeField(default=datetime.now, verbose_name='创建时间')
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]
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='', )
class UserImage(db.Document): """ 用户图片 """ user = db.IntField(verbose_name='用户') source = db.StringField(verbose_name='来源') image = db.XImageField(config='USER_IMAGES', verbose_name='图片') modified = db.DateTimeField(default=datetime.now, verbose_name='修改时间') created = db.DateTimeField(default=datetime.now, verbose_name='创建时间') meta = { 'indexes': [ 'source', '-created', ] }
class Channel(db.Document): """ 渠道模型 """ MENU_ICON = 'road' id = db.IntField(primary_key=True, verbose_name='ID') name = db.StringField(max_length=40, verbose_name='名称') password = db.StringField(max_length=40, verbose_name='密码') desc = db.StringField(verbose_name='描述') url = db.StringField(verbose_name='链接') image = db.XImageField(verbose_name='二维码') active = db.BooleanField(default=True, verbose_name='激活') modified = db.DateTimeField(default=datetime.now, verbose_name='修改时间') created = db.DateTimeField(default=datetime.now, verbose_name='创建时间') meta = { 'indexes': [ '-created', ] } def is_user(self): return True def is_authenticated(self): """ 是否登录 """ return True def is_active(self): """ 是否激活 """ return True def is_anonymous(self): """ 是否游客 """ return False def get_id(self): """ 获取用户ID """ return '%s:%s' % ('channel', str(self.id)) def create(self): """ 创建渠道 """ if not self.id: self.id = Item.inc('channel_index', 1000) self.save() return self.id
class ActionItem(db.Document): """ 功能模型 """ DEFAULT = 'default' MODULE_CHOICES = ((DEFAULT, '默认')) MODULE_VALUES = [x[0] for x in MODULE_CHOICES] name = db.StringField(verbose_name='名称') key = db.StringField(verbose_name='键名') icon = db.XImageField(verbose_name='图标') module = db.StringField(default=DEFAULT, verbose_name='模块', choices=MODULE_CHOICES) 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), )
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='登陆') 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, icon=self.icon.link, target=self.target, share=unicode(self.share), login=self.login, login_show=self.login_show, debug=self.debug, )
class Share(db.EmbeddedDocument): """ 分享模型 """ title = db.StringField(verbose_name='标题') content = db.StringField(verbose_name='描述') url = db.StringField(verbose_name='链接') image = db.XImageField(verbose_name='图片链接') def __unicode__(self): url = self.url if current_user.is_authenticated() and url.strip(): if url and '?' in url: url = '%s&uid=%d' % (url, current_user.id) else: url = '%s?uid=%d' % (url, current_user.id) return json.dumps( dict( title=self.title, content=self.content, url=url, image=self.image.link, )) @staticmethod def get(share, title='', content='', url='', image=''): if share: title = share.title or title content = share.content or content url = share.url or url image = share.image.link or image if current_user.is_authenticated() and url.strip(): if url and '?' in url: url = '%s&uid=%d' % (url, current_user.id) else: url = '%s?uid=%d' % (url, current_user.id) return json.dumps( dict( title=title, content=content, url=url, image=image, ))
class SlideItem(db.Document): """ 广告模型 """ MODULE_HEAD = 'home_head' MODULE_FOOT = 'home_foot' MODULE_CHOICES = ( (MODULE_HEAD, '头部'), (MODULE_FOOT, '底部'), ) name = db.StringField(verbose_name='名称') image = db.XImageField(verbose_name='图片') module = db.StringField(default=MODULE_HEAD, verbose_name='模块', choices=MODULE_CHOICES) 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='排序') 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': [ 'sort', '-created', ] } @property def detail(self): return dict( name=self.name, icon=self.image.link, action=self.action, url=self.url, share=unicode(self.share), )
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='', )
class Menu(db.Document): """ 菜单模型 """ MENU_ICON = 'sitemap' key = db.StringField(verbose_name='键名') name = db.StringField(verbose_name='名称') link = db.StringField(verbose_name='链接') icon = db.XImageField(verbose_name='图标') module = choice(db.StringField(verbose_name='模块'), 'menu_module', '菜单模块') sort = db.IntField(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 = dict(indexes=[('enable', 'module', 'sort')]) @staticmethod def get(module): return Menu.objects(module=module, enable=Enable.get()).order_by('sort')
class ImageItem(db.Document): image = db.XImageField(verbose_name='图片') created = db.DateTimeField(default=datetime.now, verbose_name='创建时间')
class ArticleItem(db.EmbeddedDocument): title = db.StringField(verbose_name='标题') description = db.StringField(verbose_name='描述') img = db.XImageField(verbose_name='图片链接') url = db.StringField(verbose_name='跳转链接')
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()