class AdminLoginLog(db.Document): """ 管理员登录日志 """ TYPE = db.choices(LOGIN='******', LOGOUT='退出登录', ERROR='登录认证失败') user = db.ReferenceField('AdminUser', verbose_name='用户') log_type = db.StringField(choices=TYPE.CHOICES, verbose_name='类型') useragent = db.StringField(verbose_name='用户代理(UA)') ip = db.StringField(max_length=20, verbose_name='IP') created_at = db.DateTimeField(default=datetime.now, verbose_name='创建时间') @staticmethod def log(user, log_type, **kwargs): ua = kwargs.get('ua', get_useragent()) ip = kwargs.get('ip', get_ip()) AdminLoginLog(user=user, log_type=log_type, useragent=ua, ip=ip).save() @staticmethod def login(user): AdminLoginLog.log(user, AdminLoginLog.TYPE.LOGIN) @staticmethod def logout(user): AdminLoginLog.log(user, AdminLoginLog.TYPE.LOGOUT) @staticmethod def error(user): AdminLoginLog.log(user, AdminLoginLog.TYPE.ERROR)
class View(db.Document): """ 视图 """ TYPE = db.choices(DEFAULT='默认', MODEL='模型', CATEGORY='分类') name = db.StringField(max_length=128, verbose_name='名称') label = db.StringField(max_length=128, verbose_name='标签') view_type = db.StringField(default=TYPE.DEFAULT, choices=TYPE.CHOICES, verbose_name='类型') # model = db.ReferenceField('Model', verbose_name='模型') menu_icon = db.StringField(max_length=128, 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='允许删除') can_view_details = db.BooleanField(default=False, verbose_name='允许查看详情') can_export = db.BooleanField(default=False, verbose_name='允许导出') # column_list = db.ListField(db.StringField(), verbose_name='显示列表字段') # column_exclude_list = db.ListField(db.StringField(), verbose_name='隐藏列表字段') column_center_list = db.ListField(db.StringField(), verbose_name='居中列表') created_at = db.DateTimeField(default=datetime.now, verbose_name='创建时间') updated_at = db.DateTimeField(default=datetime.now, verbose_name='更新时间') def __repr__(self): return '<View {name!r}>'.format(name=self.name) def __unicode__(self): return self.name def process(self, admin, view): pass
class Image(db.Document): """ 图片 """ key = db.StringField(max_length=128, verbose_name='键名') name = db.StringField(max_length=128, verbose_name='图片名称') image = db.XImageField(verbose_name='图片') # image = db.StringField(verbose_name='图片名称') created_at = db.DateTimeField(default=datetime.now, verbose_name='创建时间') updated_at = db.DateTimeField(default=datetime.now, verbose_name='更新时间') def __unicode__(self): return '%s-%s' % (self.key, self.name)
class File(db.Document): """ 文件 """ key = db.StringField(max_length=128, verbose_name='键名') name = db.StringField(max_length=128, verbose_name='文件名称') file = db.XFileField(max_size=20 * 1024 * 1024, verbose_name='文件') created_at = db.DateTimeField(default=datetime.now, verbose_name='创建时间') updated_at = db.DateTimeField(default=datetime.now, verbose_name='更新时间') meta = dict(allow_inheritance=True, ) def __unicode__(self): return '%s-%s' % (self.key, self.name)
class AdminUser(db.Document): """ 管理员 """ uid = db.StringField(max_length=50, verbose_name='UID') username = db.StringField(max_length=50, verbose_name='用户名') password = db.StringField(max_length=128, verbose_name='密码') group = db.ReferenceField('AdminGroup', verbose_name='管理组') is_root = db.BooleanField(default=False, verbose_name='是否超级管理员') active = db.BooleanField(default=True, verbose_name='是否激活') freezed_at = db.DateTimeField(verbose_name='冻结时间') logined_at = db.DateTimeField(default=datetime.now, verbose_name='登录时间') updated_at = db.DateTimeField(default=datetime.now, verbose_name='更新时间') created_at = db.DateTimeField(default=datetime.now, verbose_name='创建时间') meta = dict( ordering=['-created_at'], ) def __unicode__(self): return self.username def __repr__(self): return '<AdminUser {username!r}>'.format(username=self.username) @property def is_authenticated(self): """ 是否登录 """ return True @property def is_active(self): """ 是否激活 """ return self.active @property def is_anonymous(self): """ 是否游客 """ return False def get_id(self): return str(self.username) def hash_password(self, password): """ hash算法加密密码 """ # 在python3中,你需要使用在generate_password_hash()上使用decode('utf-8')方法 self.password = bcrypt.generate_password_hash(password).decode('utf-8') def verify_password(self, password): """ 验证密码 """ return bcrypt.check_password_hash(self.password, password)
class AdminGroup(db.Document): """ 管理组 """ name = db.StringField(max_length=50, 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='删除权限') updated_at = db.DateTimeField(default=datetime.now, verbose_name='更新时间') created_at = db.DateTimeField(default=datetime.now, verbose_name='创建时间') def __unicode__(self): return self.name def __repr__(self): return '<AdminGroup {name!r}>'.format(name=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]
class AdminChangeLog(db.Document): """ 管理员操作日志 """ TYPE = db.choices(CREATE='创建', EDIT='编辑', DELETE='删除') user = db.ReferenceField('AdminUser', verbose_name='用户') log_type = db.StringField(choices=TYPE.CHOICES, verbose_name='类型') model = db.StringField(verbose_name='模块') before_data = db.StringField(verbose_name='操作前数据') after_data = db.StringField(verbose_name='操作后数据') useragent = db.StringField(verbose_name='用户代理(UA)') ip = db.StringField(max_length=20, verbose_name='IP') created_at = db.DateTimeField(default=datetime.now, verbose_name='创建时间') @staticmethod def log(user, log_type, model, before_data, after_data, **kwargs): ua = kwargs.get('ua', get_useragent()) ip = kwargs.get('ip', get_ip()) AdminChangeLog(user=user, log_type=log_type, model=model, before_data=before_data, after_data=after_data, useragent=ua, ip=ip).save() @staticmethod def change_data(user, model, **kwargs): """ 变更数据 """ before = dict(id=model.id) after = dict(id=model.id) if kwargs.get('form'): try: for k, v in kwargs.get('form').data.items(): if v != model[k]: before[k] = model[k] after[k] = v except: pass else: before = model.to_mongo() if kwargs.get('log_type') == AdminChangeLog.TYPE.DELETE: after = '' AdminChangeLog.log( user=user, log_type=kwargs.get('log_type'), model=model.__class__.__name__, before_data=str(before), after_data=str(after), ) @staticmethod def ajax_change(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), log_type=AdminChangeLog.TYPE.EDIT, )
class Item(db.Document): """ 选项 """ MENU_ICON = 'gear' TYPE = db.choices(INT='整数', FLOAT='浮点数', STRING='字符串', BOOLEAN='布尔值') name = db.StringField(max_length=40, verbose_name='名称') key = db.StringField(max_length=40, verbose_name='键名') data_type = db.StringField(default=TYPE.INT, choices=TYPE.CHOICES, verbose_name='类型') value = db.DynamicField(verbose_name='值') created_at = db.DateTimeField(default=datetime.now, verbose_name='创建时间') updated_at = db.DateTimeField(default=datetime.now, verbose_name='更新时间') meta = dict( indexes=['key', '-created_at'], ordering=['-created_at'], ) @staticmethod # @cache.memoize(timeout=5) def get(key, value=0, name=None): """ 取值(整型/浮点型) """ item = Item.objects(key=key).first() if item: try: if item.data_type == Item.TYPE.INT: return int(item.value) if item.data_type == Item.TYPE.FLOAT: return float(item.value) except ValueError as e: return 0 data_type = Item.TYPE.FLOAT if type(value) is float else Item.TYPE.INT Item(key=key, data_type=data_type, value=value, name=name).save() return value @staticmethod def set(key, value=0, name=None): """ 设置值(整型/浮点型) """ item = Item.objects(key=key).first() if not item: item = Item(key=key) if name is not None: item.name = name item.data_type = Item.TYPE.FLOAT if type( value) is float else Item.TYPE.INT item.value = value item.updated_at = datetime.now() item.save() @staticmethod def inc(key, start=0, value=1, name=None): """ 递增,步长为num, 默认递增1; 不存在则创建 """ params = dict(inc__value=value, set__updated_at=datetime.now()) if name is not None: params['set__name'] = name item = Item.objects(key=key).modify(**params) if not item: params = dict(key=key, data_type=Item.TYPE.INT, value=start + value) if name is not None: params['name'] = name Item(**params).save() return start + value else: return item.value + value @staticmethod # @cache.memoize(timeout=5) def text(key, value='', name=None): """ 取值(字符串) """ item = Item.objects(key=key).first() if item: return str(item.value) Item(key=key, data_type=Item.TYPE.STRING, value=value, name=name).save() return value @staticmethod def set_text(key, value='', name=None): """ 设置值(字符串) """ item = Item.objects(key=key).first() if not item: item = Item(key=key) if name is not None: item.name = name item.data_type = Item.TYPE.STRING item.value = value item.updated_at = datetime.now() item.save() @staticmethod # @cache.memoize(timeout=5) def bool(key, value=False, name=None): """ 取值(布尔类型) """ item = Item.objects(key=key).first() if item: return True if item.value in ['true', 'True', True] else False Item(key=key, data_type=Item.TYPE.BOOLEAN, value=value, name=name).save() return value @staticmethod def set_bool(key, value=False, name=None): """ 设置值(布尔类型) """ item = Item.objects(key=key).first() if not item: item = Item(key=key) if name is not None: item.name = name item.data_type = Item.TYPE.BOOLEAN item.value = value item.updated_at = datetime.now() item.save() return True if value in ['true', 'True', True] else False @staticmethod def choice(key, value='', name=None, sep='|', coerce=str): return coerce(random.choice(Item.text(key, value, name).split(sep))) @staticmethod def list(key, value='', name=None, sep='|', coerce=int): return [coerce(x) for x in Item.text(key, value, name).split(sep)] @staticmethod def group(key, value='', name=None, sep='|', sub='-', coerce=int): texts = Item.text(key, value, name).split(sep) return [[coerce(y) for y in x.split(sub)] for x in texts] @staticmethod def hour(key, value='', name=None, sep='|', sub='-', default=None): h = datetime.now().hour for x in Item.group(key, value, name, sep, sub): if x[0] <= h <= x[1]: return x return default @staticmethod def time(key, value='', name=None): format_str = "%Y-%m-%d %H:%M:%S" value = Item.text(key, datetime.now().strftime(format_str), name) try: value = datetime.strptime(value, format_str) except: pass return value
class StatsLog(db.Document): """ 统计日志 """ MENU_ICON = 'bar-chart' TYPE = db.choices(INT='整数', FLOAT='浮点数', STRING='字符串', BOOLEAN='布尔值') name = db.StringField(max_length=40, verbose_name='名称') key = db.StringField(max_length=128, verbose_name='键名') uid = db.StringField(max_length=128, verbose_name='用户ID') xid = db.StringField(max_length=128, verbose_name='其他ID') data_type = db.StringField(default=TYPE.INT, choices=TYPE.CHOICES, verbose_name='类型') label = db.StringField(max_length=128, verbose_name='标签') day = db.StringField(max_length=20, verbose_name='日期') hour = db.IntField(default=0, verbose_name='小时') value = db.DynamicField(verbose_name='值') created_at = db.DateTimeField(default=datetime.now, verbose_name='创建时间') updated_at = db.DateTimeField(default=datetime.now, verbose_name='更新时间') meta = dict( indexes=[ 'key', '-created_at', '-updated_at', ('key', 'uid'), ('key', 'day', 'hour'), ('key', 'uid', 'xid', 'label', 'day', 'hour'), ], ordering=['-created_at'], ) @staticmethod def get(key, uid='', xid='', label='', day=lambda: today(), hour=-1, value=0, name=None, save=True): """ 取值(整型/浮点型) """ if callable(day): day = day() day = str(day)[:10] log = StatsLog.objects(key=key, uid=uid, xid=xid, label=label, day=day, hour=hour).first() if log: if name is not None: log.name = name log.save() try: if log.data_type == StatsLog.TYPE.INT: return int(log.value) if log.data_type == StatsLog.TYPE.FLOAT: return float(log.value) except ValueError as e: return 0 if save: data_type = StatsLog.TYPE.FLOAT if type( value) is float else StatsLog.TYPE.INT StatsLog(key=key, data_type=data_type, uid=uid, xid=xid, label=label, day=day, hour=hour, value=value, name=name).save() return value return None @staticmethod def set(key, uid='', xid='', label='', day=lambda: today(), hour=-1, value=0, name=None, save=True): """ 设置值(整型/浮点型) """ if callable(day): day = day() day = str(day)[:10] data_type = StatsLog.TYPE.FLOAT if type( value) is float else StatsLog.TYPE.INT params = dict(set__value=value, set__data_type=data_type, set__updated_at=datetime.now()) if name is not None: params['set__name'] = name log = StatsLog.objects(key=key, uid=uid, xid=xid, label=label, day=day, hour=hour).modify(**params) if log: return value if save: StatsLog(key=key, data_type=data_type, uid=uid, xid=xid, label=label, day=day, hour=hour, value=value, name=name).save() return value return None @staticmethod def inc(key, uid='', xid='', label='', day=lambda: today(), hour=-1, start=0, value=1, name=None, save=True): """ 递增(整型/浮点型) """ if callable(day): day = day() day = str(day)[:10] data_type = StatsLog.TYPE.FLOAT if type( value) is float else StatsLog.TYPE.INT params = dict(inc__value=value, set__data_type=data_type, set__updated_at=datetime.now()) if name is not None: params['set__name'] = name log = StatsLog.objects(key=key, uid=uid, xid=xid, label=label, day=day, hour=hour).modify(**params) if log: return log.value + value if save: StatsLog(key=key, data_type=data_type, uid=uid, xid=xid, label=label, day=day, hour=hour, value=start + value, name=name).save() return start + value return None @staticmethod def text(key, uid='', xid='', label='', day=lambda: today(), hour=-1, value='', name=None, save=True): """ 取值(字符串) """ if callable(day): day = day() day = str(day)[:10] log = StatsLog.objects(key=key, uid=uid, xid=xid, label=label, day=day, hour=hour).first() if log: if name is not None: log.name = name log.save() return log.value if save: StatsLog(key=key, data_type=StatsLog.TYPE.STRING, uid=uid, xid=xid, label=label, day=day, hour=hour, value=value, name=name).save() return value return None @staticmethod def set_text(key, uid='', xid='', label='', day=lambda: today(), hour=-1, value='', name=None, save=True): """ 设置值(字符串) """ if callable(day): day = day() day = str(day)[:10] params = dict(set__value=value, set__updated_at=datetime.now()) if name is not None: params['set__name'] = name log = StatsLog.objects(key=key, uid=uid, xid=xid, label=label, day=day, hour=hour).modify(**params) if log: return value if save: StatsLog(key=key, data_type=StatsLog.TYPE.STRING, uid=uid, xid=xid, label=label, day=day, hour=hour, value=value, name=name).save() return value return None @staticmethod def bool(key, uid='', xid='', label='', day=lambda: today(), hour=-1, value=False, name=None, save=True): """ 取值(布尔值) """ if type(value) is not bool: raise ValueError('The type of value is not bool.') if callable(day): day = day() day = str(day)[:10] log = StatsLog.objects(key=key, uid=uid, xid=xid, label=label, day=day, hour=hour).first() if log: if name is not None: log.name = name log.save() return True if log.value in ['true', 'True', True] else False if save: StatsLog(key=key, data_type=StatsLog.TYPE.BOOLEAN, uid=uid, xid=xid, label=label, day=day, hour=hour, value=value, name=name).save() return value return None @staticmethod def set_bool(key, uid='', xid='', label='', day=lambda: today(), hour=-1, value=False, name=None, save=True): """ 设置值(布尔值) """ if type(value) is not bool: raise ValueError('The type of value is not bool.') if callable(day): day = day() day = str(day)[:10] params = dict(set__value=value, set__updated_at=datetime.now()) if name is not None: params['set__name'] = name log = StatsLog.objects(key=key, uid=uid, xid=xid, label=label, day=day, hour=hour).modify(**params) if log: return value if save: StatsLog(key=key, data_type=StatsLog.TYPE.BOOLEAN, uid=uid, xid=xid, label=label, day=day, hour=hour, value=value, name=name).save() return value return None @staticmethod def xget(key, uid='', xid='', label='', day='', hour=-1, value=0, name=None, save=True): """ 取值 """ return StatsLog.get(key, uid, xid, label, day, hour, value, name, save) @staticmethod def xset(key, uid='', xid='', label='', day='', hour=-1, value=0, name=None, save=True): """ 设置值 """ return StatsLog.set(key, uid, xid, label, day, hour, value, name, save) @staticmethod def xinc(key, uid='', xid='', label='', day='', hour=-1, start=0, value=1, name=None, save=True): """ 递增 """ return StatsLog.inc(key, uid, xid, label, day, hour, start, value, name, save) @staticmethod def xtext(key, uid='', xid='', label='', day='', hour=-1, value='', name=None, save=True): """ 取值(字符串) """ return StatsLog.text(key, uid, xid, label, day, hour, value, name, save) @staticmethod def xset_text(key, uid='', xid='', label='', day='', hour=-1, value='', name=None, save=True): """ 设置值(字符串) """ return StatsLog.set_text(key, uid, xid, label, day, hour, value, name, save) @staticmethod def xbool(key, uid='', xid='', label='', day='', hour=-1, value=False, name=None, save=True): """ 取值(布尔值) """ return StatsLog.bool(key, uid, xid, label, day, hour, value, name, save) @staticmethod def xset_bool(key, uid='', xid='', label='', day='', hour=-1, value=False, name=None, save=True): """ 设置值(布尔值) """ return StatsLog.set_bool(key, uid, xid, label, day, hour, value, name, save) @staticmethod def choice(key, uid='', xid='', label='', day=lambda: today(), hour=-1, value='', name=None, save=True, sep='|', coerce=str): return coerce( random.choice( StatsLog.text(key, uid=uid, xid=xid, label=label, day=day, hour=hour, value=value, name=name, save=save).split(sep))) @staticmethod def xchoice(key, uid='', xid='', label='', day='', hour=-1, value='', name=None, save=True, sep='|', coerce=str): return coerce( random.choice( StatsLog.xtext(key, uid=uid, xid=xid, label=label, day=day, hour=hour, value=value, name=name, save=save).split(sep))) @staticmethod def list(key, uid='', xid='', label='', day=lambda: today(), hour=-1, value='', name=None, save=True, sep='|', coerce=int): """ 将特定格式的字符串转为一维数组 """ """ 例如, 字符串格式为'1|2|3|4', 返回的是[1, 2, 3, 4] """ text = StatsLog.text(key, uid=uid, xid=xid, label=label, day=day, hour=hour, value=value, name=name, save=save) if not text: return None texts = text.split(sep) return [coerce(x) for x in texts] @staticmethod def xlist(key, uid='', xid='', label='', day='', hour=-1, value='', name=None, save=True, sep='|', coerce=int): """ 将特定格式的字符串转为一维数组 """ """ 例如, 字符串格式为'1|2|3|4', 返回的是[1, 2, 3, 4] """ text = StatsLog.xtext(key, uid=uid, xid=xid, label=label, day=day, hour=hour, value=value, name=name, save=save) if not text: return None texts = text.split(sep) return [coerce(x) for x in texts] @staticmethod def group(key, uid='', xid='', label='', day=lambda: today(), hour=-1, value='', name=None, save=True, sep='|', sub='-', coerce=int): """ 将特定格式的字符串转为二维数组 """ """ 例如, 字符串格式为'1-3|4-9|10-32|64-128', 返回的是[[1, 3], [4, 9], [10, 32], [64, 128]] """ text = StatsLog.text(key, uid=uid, xid=xid, label=label, day=day, hour=hour, value=value, name=name, save=save) if not text: return None texts = text.split(sep) return [[coerce(y) for y in x.split(sub)] for x in texts] @staticmethod def xgroup(key, uid='', xid='', label='', day='', hour=-1, value='', name=None, save=True, sep='|', sub='-', coerce=int): """ 将特定格式的字符串转为二维数组 """ """ 例如, 字符串格式为'1-3|4-9|10-32|64-128', 返回的是[[1, 3], [4, 9], [10, 32], [64, 128]] """ text = StatsLog.xtext(key, uid=uid, xid=xid, label=label, day=day, hour=hour, value=value, name=name, save=save) texts = text.split(sep) return [[coerce(y) for y in x.split(sub)] for x in texts] @staticmethod def hour_range(key, uid='', xid='', label='', day=lambda: today(), hour=-1, value='', name=None, save=True, sep='|', sub='-', default=None): """ 获取当前时间整点所在的整点区间 """ """ 例如, 当前时间的整点是10点, value为'3-8|9-14|15-23', 则10在区间[9, 14]之间, 返回的值是[9, 14] """ groups = StatsLog.group(key, uid=uid, xid=xid, label=label, day=day, hour=hour, value=value, name=name, save=save, sep=sep, sub=sub) if not groups: return None h = datetime.now().hour for x in groups: if x[0] <= h <= x[1]: return x return default @staticmethod def xhour_range(key, uid='', xid='', label='', day='', hour=-1, value='', name=None, save=True, sep='|', sub='-', default=None): """ 获取当前时间整点所在的整点区间 """ """ 例如, 当前时间的整点是10点, value为'3-8|9-14|15-23', 则10在区间[9, 14]之间, 返回的值是[9, 14] """ groups = StatsLog.xgroup(key, uid=uid, xid=xid, label=label, day=day, hour=hour, value=value, name=name, save=save, sep=sep, sub=sub) if not groups: return None h = datetime.now().hour for x in groups: if x[0] <= h <= x[1]: return x return default