作者 杨再宏

提交代码

正在显示 43 个修改的文件 包含 4634 行增加0 行删除

要显示太多修改。

为保证性能只显示 43 of 43+ 个文件。

  1 +#project out
  2 +
  3 +alarms/
不能预览此文件类型
不能预览此文件类型
  1 +# _*_ Coding:utf-8 _*_
  2 +
  3 +# from flask_login import LoginManager
  4 +from flask import Flask
  5 +from flask_sqlalchemy import SQLAlchemy
  6 +from config import config
  7 +
  8 +db = SQLAlchemy()
  9 +
  10 +
  11 +def create_app(config_name):
  12 + app = Flask(__name__)
  13 + app.config.from_object(config[config_name])
  14 + config[config_name].init_app(app)
  15 +
  16 + # login_manager = LoginManager()
  17 + # login_manager.init_app(app)
  18 + db.init_app(app)
  19 + # 注册蓝图
  20 + from app.admin import admin as admin_blueprint
  21 + from app.api import api as api_blueprint
  22 + app.register_blueprint(admin_blueprint, url_prefix="/")
  23 + app.register_blueprint(api_blueprint, url_prefix="/api")
  24 +
  25 + return app
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
  1 +# _*_ Coding:utf-8 _*_
  2 +
  3 +from flask import Blueprint
  4 +
  5 +admin = Blueprint("admin", __name__)
  6 +
  7 +import app.admin.views
  1 +# _*_ coding: utf-8 _*_
  2 +
  3 +from flask_wtf import FlaskForm
  4 +from flask_wtf.file import FileAllowed
  5 +from wtforms import StringField, PasswordField, SubmitField, FileField, TextAreaField, RadioField, SelectField, BooleanField, EmailField
  6 +from wtforms.validators import DataRequired, ValidationError, EqualTo, Email, InputRequired, Regexp, Length, Optional
  7 +from app.models import Admin
  8 +
  9 +
  10 +class LoginForm(FlaskForm):
  11 + """
  12 + 管理员登录表单
  13 + """
  14 + account = StringField(
  15 + label="账号",
  16 + validators=[
  17 + DataRequired("账号不能为空")
  18 + ],
  19 + description="账号",
  20 + render_kw={
  21 + "class": "form-control",
  22 + "placeholder": "请输入账号!",
  23 + }
  24 + )
  25 + pwd = PasswordField(
  26 + label="密码",
  27 + validators=[
  28 + DataRequired("密码不能为空")
  29 + ],
  30 + description="密码",
  31 + render_kw={
  32 + "class": "form-control",
  33 + "placeholder": "请输入密码!",
  34 + }
  35 + )
  36 + submit = SubmitField(
  37 + '登录',
  38 + render_kw={
  39 + "class": "btn btn-primary btn-block btn-flat",
  40 + }
  41 + )
  42 +
  43 + # 验证账号,命名规则:validate_ + 字段名。如果要验证密码,则可以创建函数validate_pwd
  44 + def validate_account(self, field):
  45 + account = field.data
  46 + admin = Admin.query.filter_by(name=account).count()
  47 + if admin == 0:
  48 + raise ValidationError("账号不存在! ")
  49 +
  50 +
  51 +class PwdForm(FlaskForm):
  52 + old_pwd = PasswordField(
  53 + label="旧密码",
  54 + validators=[
  55 + DataRequired("旧密码不能为空!")
  56 + ],
  57 + description="旧密码",
  58 + render_kw={
  59 + "class": "form-control",
  60 + "placeholder": "请输入旧密码!",
  61 + }
  62 + )
  63 + new_pwd = PasswordField(
  64 + label="新密码",
  65 + validators=[
  66 + DataRequired("新密码不能为空!")
  67 + ],
  68 + description="新密码",
  69 + render_kw={
  70 + "class": "form-control",
  71 + "placeholder": "请输入新密码!",
  72 + }
  73 + )
  74 + submit = SubmitField(
  75 + '保存',
  76 + render_kw={
  77 + "class": "btn btn-primary",
  78 + }
  79 + )
  80 +
  81 + def validate_old_pwd(self, field):
  82 + from flask import session
  83 + pwd = field.data
  84 + name = session["admin"]
  85 + admin = Admin.query.filter_by(
  86 + name=name
  87 + ).first()
  88 + if not admin.check_pwd(pwd):
  89 + raise ValidationError("旧密码错误!")
  90 +
  91 +
  92 +class AreaForm(FlaskForm):
  93 + """添加/编辑地区的表单"""
  94 + name = StringField(
  95 + label="名称",
  96 + validators=[
  97 + DataRequired("地区名不能为空")
  98 + ],
  99 + description="地区",
  100 + render_kw={
  101 + "class": "form-control",
  102 + "placeholder": "请输入地区名称!"
  103 + }
  104 + )
  105 + is_recommended = RadioField(
  106 + label='是否推荐',
  107 + description="是否推荐",
  108 + coerce=int,
  109 + choices=[(0, '否'), (1, '是')], default=0,
  110 + )
  111 + introduction = TextAreaField(
  112 + label="简介",
  113 + validators=[
  114 + DataRequired("简介不能为空!")
  115 + ],
  116 + description="简介",
  117 + render_kw={
  118 + "class": "form-control",
  119 + "rows": 5
  120 + }
  121 + )
  122 +
  123 + submit = SubmitField(
  124 + '添加',
  125 + render_kw={
  126 + "class": "btn btn-primary",
  127 + }
  128 + )
  129 +
  130 +
  131 +class DeviceTypeForm(FlaskForm):
  132 + """添加/编辑地区的表单"""
  133 + name = StringField(
  134 + label="设备名称",
  135 + validators=[
  136 + DataRequired("设备名称不能为空!")
  137 + ],
  138 + description="设备名称",
  139 + render_kw={
  140 + "class": "form-control",
  141 + "placeholder": "请输入设备名称!"
  142 + }
  143 + )
  144 +
  145 + type = StringField(
  146 + label="设备编号",
  147 + validators=[
  148 + DataRequired("设备编号不能为空!")
  149 + ],
  150 + description="设备编号",
  151 + render_kw={
  152 + "class": "form-control",
  153 + "placeholder": "请输入整数类型的设备编号!"
  154 + }
  155 + )
  156 +
  157 + submit = SubmitField(
  158 + '添加',
  159 + render_kw={
  160 + "class": "btn btn-primary",
  161 + }
  162 + )
  163 +
  164 +
  165 +class DetectingPointForm(FlaskForm):
  166 + """添加/编辑检测点的表单"""
  167 + name = StringField(
  168 + label="检测点名称",
  169 + validators=[
  170 + DataRequired("检测点名称不能为空!")
  171 + ],
  172 + description="检测点名称",
  173 + render_kw={
  174 + "class": "form-control",
  175 + "placeholder": "请输入检测点名称!"
  176 + }
  177 + )
  178 +
  179 + no = StringField(
  180 + label="检测点编号",
  181 + validators=[
  182 + DataRequired("检测点编号不能为空!")
  183 + ],
  184 + description="检测点编号",
  185 + render_kw={
  186 + "class": "form-control",
  187 + "placeholder": "请输入整数类型的设备编号!"
  188 + }
  189 + )
  190 +
  191 + submit = SubmitField(
  192 + '添加',
  193 + render_kw={
  194 + "class": "btn btn-primary",
  195 + }
  196 + )
  197 +
  198 +
  199 +class PowerForm(FlaskForm):
  200 + """添加/编辑权限的表单"""
  201 + name = StringField(
  202 + label="权限名称",
  203 + validators=[
  204 + DataRequired("权限名称不能为空!")
  205 + ],
  206 + description="权限名称",
  207 + render_kw={
  208 + "class": "form-control",
  209 + "placeholder": "请输入权限名称!"
  210 + }
  211 + )
  212 +
  213 + submit = SubmitField(
  214 + '添加',
  215 + render_kw={
  216 + "class": "btn btn-primary",
  217 + }
  218 + )
  219 +
  220 +
  221 +class RoleForm(FlaskForm):
  222 + """添加/编辑权限的表单"""
  223 + name = StringField(
  224 + label="角色名称",
  225 + validators=[
  226 + DataRequired("角色名称不能为空!")
  227 + ],
  228 + description="角色名称",
  229 + render_kw={
  230 + "class": "form-control",
  231 + "placeholder": "请输入角色名称!"
  232 + }
  233 + )
  234 +
  235 + submit = SubmitField(
  236 + '添加',
  237 + render_kw={
  238 + "class": "btn btn-primary",
  239 + }
  240 + )
  241 +
  242 +
  243 +class ScenicForm(FlaskForm):
  244 + title = StringField(
  245 + label="景区名称",
  246 + validators=[
  247 + DataRequired("景区名称不能为空!")
  248 + ],
  249 + description="景区名称",
  250 + render_kw={
  251 + "class": "form-control",
  252 + "placeholder": "请输入景区名称!"
  253 + }
  254 + )
  255 + address = StringField(
  256 + label="景区地址",
  257 + validators=[
  258 + DataRequired("景区地址不能为空!")
  259 + ],
  260 + description="景区地址",
  261 + render_kw={
  262 + "class": "form-control",
  263 + "placeholder": "请输入景区地址!"
  264 + }
  265 + )
  266 + star = SelectField(
  267 + label="星级",
  268 + validators=[
  269 + DataRequired("请选择星级!")
  270 + ],
  271 + coerce=int,
  272 + choices=[(1, "1星"), (2, "2星"), (3, "3星"), (4, "4星"), (5, "5星")], default=5,
  273 + description="星级",
  274 + render_kw={
  275 + "class": "form-control",
  276 + }
  277 + )
  278 +
  279 + logo = FileField(
  280 + label="封面",
  281 + validators=[
  282 + DataRequired("请上传封面!"),
  283 + FileAllowed(['jpg', 'png'], '请上传jpg或png格式图片!')
  284 + ],
  285 + description="封面",
  286 + )
  287 +
  288 + is_hot = RadioField(
  289 + label='是否热门',
  290 + description="是否热门",
  291 + coerce=int,
  292 + choices=[(0, '否'), (1, '是')], default=0,
  293 + )
  294 + is_recommended = RadioField(
  295 + label='是否推荐',
  296 + description="是否推荐",
  297 + coerce=int,
  298 + choices=[(0, '否'), (1, '是')], default=0,
  299 + )
  300 + introduction = TextAreaField(
  301 + label="景区简介",
  302 + validators=[
  303 + DataRequired("简介不能为空!")
  304 + ],
  305 + description="简介",
  306 + render_kw={
  307 + "class": "form-control",
  308 + "rows": 5
  309 + }
  310 + )
  311 + content = TextAreaField(
  312 + label="景区内容",
  313 + validators=[
  314 + DataRequired("景区内容不能为空!")
  315 + ],
  316 + description="景区内容",
  317 + render_kw={
  318 + "class": "form-control ckeditor",
  319 + "rows": 10
  320 + }
  321 + )
  322 + area_id = SelectField(
  323 + label="所属地区",
  324 + validators=[
  325 + DataRequired("请选择标签!")
  326 + ],
  327 + coerce=int,
  328 + description="所属地区",
  329 + render_kw={
  330 + "class": "form-control",
  331 + }
  332 + )
  333 + submit = SubmitField(
  334 + '添加',
  335 + render_kw={
  336 + "class": "btn btn-primary",
  337 + }
  338 + )
  339 +
  340 +
  341 +class AdminForm(FlaskForm):
  342 + def __init__(self, edit, **kwargs):
  343 + super().__init__(**kwargs)
  344 +
  345 + self.name.render_kw = {
  346 + "class": "form-control",
  347 + "placeholder": "请输入管理员名称!",
  348 + "readonly": edit,
  349 + }
  350 +
  351 + name = StringField(
  352 + label="管理员名称",
  353 + validators=[
  354 + DataRequired("管理员名称不能为空!")
  355 + ],
  356 +
  357 + description="管理员名称",
  358 + render_kw={
  359 + "class": "form-control",
  360 + "placeholder": "请输入管理员名称!",
  361 + }
  362 + )
  363 +
  364 + pwd = PasswordField(
  365 + label="密码",
  366 + validators=[
  367 + DataRequired("密码不能为空")
  368 + ],
  369 + description="密码",
  370 + render_kw={
  371 + "class": "form-control",
  372 + "placeholder": "请输入密码!",
  373 + }
  374 + )
  375 + repwd = PasswordField(
  376 + label="确认密码",
  377 + validators=[
  378 + DataRequired("确认密码不能为空"),
  379 + EqualTo('pwd', message="两次输入密码不一致"),
  380 + ],
  381 + description="确认密码",
  382 + render_kw={
  383 + "class": "form-control",
  384 + "placeholder": "请输入确认密码!",
  385 + }
  386 + )
  387 + role_id = SelectField(
  388 + label="角色",
  389 + validators=[
  390 + DataRequired("请选择角色!")
  391 + ],
  392 + coerce=int,
  393 + description="角色",
  394 + render_kw={
  395 + "class": "form-control",
  396 + }
  397 + )
  398 + power_id = SelectField(
  399 + label="权限",
  400 + validators=[
  401 + DataRequired("请选择权限!")
  402 + ],
  403 + coerce=int,
  404 + description="权限",
  405 + render_kw={
  406 + "class": "form-control",
  407 + }
  408 + )
  409 + submit = SubmitField(
  410 + '添加',
  411 + render_kw={
  412 + "class": "btn btn-primary",
  413 + }
  414 + )
  415 +
  416 +
  417 +class UserForm(FlaskForm):
  418 + def __init__(self, edit, **kwargs):
  419 + super().__init__(**kwargs)
  420 +
  421 + self.username.render_kw = {
  422 + "class": "form-control",
  423 + "placeholder": "请输入用户名!",
  424 + "readonly": edit,
  425 + }
  426 +
  427 + username = StringField(
  428 + label="用户名",
  429 + validators=[
  430 + DataRequired("用户名不能为空!")
  431 + ],
  432 +
  433 + description="用户名",
  434 + render_kw={
  435 + "class": "form-control",
  436 + "placeholder": "请输入用户名!",
  437 + }
  438 + )
  439 + email = EmailField(
  440 + label="邮箱",
  441 + validators=[
  442 + Optional(),
  443 + Email()
  444 + ],
  445 +
  446 + description="邮箱",
  447 + render_kw={
  448 + "class": "form-control",
  449 + "placeholder": "请输入邮箱地址!",
  450 + }
  451 + )
  452 +
  453 + phone = StringField(
  454 + label="手机号码",
  455 + validators=[
  456 + Optional(),
  457 + Length(11, 11),
  458 + Regexp('^1[3-9]\\d{9}$', 0, '手机号码格式错误')
  459 + ],
  460 +
  461 + description="手机号码",
  462 + render_kw={
  463 + "class": "form-control",
  464 + "placeholder": "请输入手机号码!",
  465 + }
  466 + )
  467 +
  468 + pwd = PasswordField(
  469 + label="密码",
  470 + validators=[
  471 + DataRequired("密码不能为空")
  472 + ],
  473 + description="密码",
  474 + render_kw={
  475 + "class": "form-control",
  476 + "placeholder": "请输入密码!",
  477 + }
  478 + )
  479 + repwd = PasswordField(
  480 + label="确认密码",
  481 + validators=[
  482 + DataRequired("确认密码不能为空"),
  483 + EqualTo('pwd', message="两次输入密码不一致"),
  484 + ],
  485 + description="确认密码",
  486 + render_kw={
  487 + "class": "form-control",
  488 + "placeholder": "请输入确认密码!",
  489 + }
  490 + )
  491 + role_id = SelectField(
  492 + label="角色",
  493 + validators=[
  494 + DataRequired("请选择角色!")
  495 + ],
  496 + coerce=int,
  497 + description="角色",
  498 + render_kw={
  499 + "class": "form-control",
  500 + }
  501 + )
  502 + power_id = SelectField(
  503 + label="权限",
  504 + validators=[
  505 + DataRequired("请选择权限!")
  506 + ],
  507 + coerce=int,
  508 + description="权限",
  509 + render_kw={
  510 + "class": "form-control",
  511 + }
  512 + )
  513 + submit = SubmitField(
  514 + '添加',
  515 + render_kw={
  516 + "class": "btn btn-primary",
  517 + }
  518 + )
  519 +
  520 +
  521 +class DeviceForm(FlaskForm):
  522 +
  523 + def __init__(self, edit, **kwargs):
  524 + super().__init__(**kwargs)
  525 +
  526 + self.title.render_kw = {
  527 + "class": "form-control",
  528 + "placeholder": "请输入设备名称!",
  529 + "readonly": edit,
  530 + }
  531 +
  532 + self.devicetype_id.render_kw = {
  533 + "class": "form-control",
  534 + "readonly": edit,
  535 + }
  536 +
  537 + title = StringField(
  538 + label="设备名称",
  539 + validators=[
  540 + DataRequired("设备名称不能为空!")
  541 + ],
  542 +
  543 + description="设备名称",
  544 + render_kw={
  545 + "class": "form-control",
  546 + "placeholder": "请输入设备名称!",
  547 + }
  548 + )
  549 + address = StringField(
  550 + label="设备地址",
  551 + validators=[
  552 + DataRequired("设备地址不能为空!")
  553 + ],
  554 + description="设备地址",
  555 + render_kw={
  556 + "class": "form-control",
  557 + "placeholder": "请输入设备地址!",
  558 +
  559 + }
  560 + )
  561 + threshold = StringField(
  562 + label="报警阈值",
  563 +
  564 + description="报警阈值",
  565 + render_kw={
  566 + "class": "form-control",
  567 + "placeholder": "请输入报警阈值!",
  568 +
  569 + }
  570 + )
  571 + logo = FileField(
  572 + label="封面",
  573 + validators=[
  574 + # DataRequired("请上传封面!"),
  575 + FileAllowed(['jpg', 'png'], '请上传jpg或png格式图片!')
  576 + ],
  577 + description="封面",
  578 + )
  579 +
  580 + introduction = TextAreaField(
  581 + label="备注信息",
  582 +
  583 + description="备注",
  584 + render_kw={
  585 + "class": "form-control",
  586 + "rows": 5
  587 + }
  588 + )
  589 +
  590 + devicetype_id = SelectField(
  591 + label="设备类型",
  592 + validators=[
  593 + DataRequired("请选择设备类型!")
  594 + ],
  595 + coerce=int,
  596 + description="设备类型",
  597 + render_kw={
  598 + "class": "form-control",
  599 + }
  600 + )
  601 + detectingpoint_id = SelectField(
  602 + label="检测点",
  603 + validators=[
  604 + DataRequired("请选择检测点!")
  605 + ],
  606 + coerce=int,
  607 + description="检测点",
  608 + render_kw={
  609 + "class": "form-control",
  610 + }
  611 + )
  612 + submit = SubmitField(
  613 + '添加',
  614 + render_kw={
  615 + "class": "btn btn-primary",
  616 + }
  617 + )
  618 +
  619 +
  620 +class TravelsForm(FlaskForm):
  621 + title = StringField(
  622 + label="标题",
  623 + validators=[
  624 + DataRequired("标题不能为空!")
  625 + ],
  626 + description="标题",
  627 + render_kw={
  628 + "class": "form-control",
  629 + "placeholder": "请输入标题!"
  630 + }
  631 + )
  632 + author = StringField(
  633 + label="作者",
  634 + validators=[
  635 + DataRequired("作者不能为空!")
  636 + ],
  637 + description="作者",
  638 + render_kw={
  639 + "class": "form-control",
  640 + "placeholder": "请输入作者!"
  641 + }
  642 + )
  643 + content = TextAreaField(
  644 + label="游记内容",
  645 + validators=[
  646 + DataRequired("游记内容不能为空!")
  647 + ],
  648 + description="游记内容",
  649 + render_kw={
  650 + "class": "form-control ckeditor",
  651 + }
  652 + )
  653 + scenic_id = SelectField(
  654 + label="所属景区",
  655 + validators=[
  656 + DataRequired("请选择景区!")
  657 + ],
  658 + coerce=int,
  659 + description="所属景区",
  660 + render_kw={
  661 + "class": "form-control",
  662 + }
  663 + )
  664 + submit = SubmitField(
  665 + '添加',
  666 + render_kw={
  667 + "class": "btn btn-primary",
  668 + }
  669 + )
  1 +<!DOCTYPE html>
  2 +<html>
  3 +<head>
  4 + <meta charset="UTF-8">
  5 + <title>Awesome-pyecharts</title>
  6 + <script type="text/javascript" src="https://assets.pyecharts.org/assets/echarts.min.js"></script>
  7 +
  8 +</head>
  9 +<body>
  10 + <div id="9169d22a710c4474bfb38ee93c197ab9" class="chart-container" style="width:900px; height:500px;"></div>
  11 + <script>
  12 + var chart_9169d22a710c4474bfb38ee93c197ab9 = echarts.init(
  13 + document.getElementById('9169d22a710c4474bfb38ee93c197ab9'), 'white', {renderer: 'canvas'});
  14 + var option_9169d22a710c4474bfb38ee93c197ab9 = {
  15 + "baseOption": {
  16 + "series": [
  17 + {
  18 + "type": "line",
  19 + "name": "\u793a\u4f8b",
  20 + "connectNulls": false,
  21 + "symbolSize": 4,
  22 + "showSymbol": true,
  23 + "smooth": false,
  24 + "clip": true,
  25 + "step": false,
  26 + "data": [
  27 + [
  28 + "A",
  29 + 120
  30 + ],
  31 + [
  32 + "B",
  33 + 200
  34 + ],
  35 + [
  36 + "C",
  37 + 150
  38 + ],
  39 + [
  40 + "D",
  41 + 80
  42 + ],
  43 + [
  44 + "E",
  45 + 70
  46 + ],
  47 + [
  48 + "F",
  49 + 110
  50 + ]
  51 + ],
  52 + "hoverAnimation": true,
  53 + "label": {
  54 + "show": true,
  55 + "position": "top",
  56 + "margin": 8
  57 + },
  58 + "lineStyle": {
  59 + "show": true,
  60 + "width": 1,
  61 + "opacity": 1,
  62 + "curveness": 0,
  63 + "type": "solid"
  64 + },
  65 + "areaStyle": {
  66 + "opacity": 0
  67 + },
  68 + "zlevel": 0,
  69 + "z": 0
  70 + },
  71 + {
  72 + "type": "line",
  73 + "name": "\u793a\u4f8b",
  74 + "connectNulls": false,
  75 + "symbolSize": 4,
  76 + "showSymbol": true,
  77 + "smooth": false,
  78 + "clip": true,
  79 + "step": false,
  80 + "data": [
  81 + [
  82 + "A",
  83 + 80
  84 + ],
  85 + [
  86 + "B",
  87 + 70
  88 + ],
  89 + [
  90 + "C",
  91 + 110
  92 + ],
  93 + [
  94 + "D",
  95 + 120
  96 + ],
  97 + [
  98 + "E",
  99 + 200
  100 + ],
  101 + [
  102 + "F",
  103 + 150
  104 + ]
  105 + ],
  106 + "hoverAnimation": true,
  107 + "label": {
  108 + "show": true,
  109 + "position": "top",
  110 + "margin": 8
  111 + },
  112 + "lineStyle": {
  113 + "show": true,
  114 + "width": 1,
  115 + "opacity": 1,
  116 + "curveness": 0,
  117 + "type": "solid"
  118 + },
  119 + "areaStyle": {
  120 + "opacity": 0
  121 + },
  122 + "zlevel": 0,
  123 + "z": 0
  124 + }
  125 + ],
  126 + "timeline": {
  127 + "axisType": "category",
  128 + "orient": "horizontal",
  129 + "autoPlay": false,
  130 + "controlPosition": "left",
  131 + "loop": true,
  132 + "rewind": false,
  133 + "show": true,
  134 + "inverse": false,
  135 + "bottom": "-5px",
  136 + "data": [
  137 + "10"
  138 + ]
  139 + },
  140 + "xAxis": [
  141 + {
  142 + "show": true,
  143 + "scale": false,
  144 + "nameLocation": "end",
  145 + "nameGap": 15,
  146 + "gridIndex": 0,
  147 + "inverse": false,
  148 + "offset": 0,
  149 + "splitNumber": 5,
  150 + "minInterval": 0,
  151 + "splitLine": {
  152 + "show": false,
  153 + "lineStyle": {
  154 + "show": true,
  155 + "width": 1,
  156 + "opacity": 1,
  157 + "curveness": 0,
  158 + "type": "solid"
  159 + }
  160 + },
  161 + "data": [
  162 + "A",
  163 + "B",
  164 + "C",
  165 + "D",
  166 + "E",
  167 + "F"
  168 + ]
  169 + }
  170 + ],
  171 + "yAxis": [
  172 + {
  173 + "show": true,
  174 + "scale": false,
  175 + "nameLocation": "end",
  176 + "nameGap": 15,
  177 + "gridIndex": 0,
  178 + "inverse": false,
  179 + "offset": 0,
  180 + "splitNumber": 5,
  181 + "minInterval": 0,
  182 + "splitLine": {
  183 + "show": false,
  184 + "lineStyle": {
  185 + "show": true,
  186 + "width": 1,
  187 + "opacity": 1,
  188 + "curveness": 0,
  189 + "type": "solid"
  190 + }
  191 + }
  192 + }
  193 + ]
  194 + },
  195 + "options": [
  196 + {
  197 + "legend": [
  198 + {
  199 + "data": [
  200 + "\u793a\u4f8b",
  201 + "\u793a\u4f8b"
  202 + ],
  203 + "selected": {
  204 + "\u793a\u4f8b": true
  205 + },
  206 + "show": true,
  207 + "padding": 5,
  208 + "itemGap": 10,
  209 + "itemWidth": 25,
  210 + "itemHeight": 14
  211 + }
  212 + ],
  213 + "series": [
  214 + {
  215 + "type": "line",
  216 + "name": "\u793a\u4f8b",
  217 + "connectNulls": false,
  218 + "symbolSize": 4,
  219 + "showSymbol": true,
  220 + "smooth": false,
  221 + "clip": true,
  222 + "step": false,
  223 + "data": [
  224 + [
  225 + "A",
  226 + 120
  227 + ],
  228 + [
  229 + "B",
  230 + 200
  231 + ],
  232 + [
  233 + "C",
  234 + 150
  235 + ],
  236 + [
  237 + "D",
  238 + 80
  239 + ],
  240 + [
  241 + "E",
  242 + 70
  243 + ],
  244 + [
  245 + "F",
  246 + 110
  247 + ]
  248 + ],
  249 + "hoverAnimation": true,
  250 + "label": {
  251 + "show": true,
  252 + "position": "top",
  253 + "margin": 8
  254 + },
  255 + "lineStyle": {
  256 + "show": true,
  257 + "width": 1,
  258 + "opacity": 1,
  259 + "curveness": 0,
  260 + "type": "solid"
  261 + },
  262 + "areaStyle": {
  263 + "opacity": 0
  264 + },
  265 + "zlevel": 0,
  266 + "z": 0
  267 + },
  268 + {
  269 + "type": "line",
  270 + "name": "\u793a\u4f8b",
  271 + "connectNulls": false,
  272 + "symbolSize": 4,
  273 + "showSymbol": true,
  274 + "smooth": false,
  275 + "clip": true,
  276 + "step": false,
  277 + "data": [
  278 + [
  279 + "A",
  280 + 80
  281 + ],
  282 + [
  283 + "B",
  284 + 70
  285 + ],
  286 + [
  287 + "C",
  288 + 110
  289 + ],
  290 + [
  291 + "D",
  292 + 120
  293 + ],
  294 + [
  295 + "E",
  296 + 200
  297 + ],
  298 + [
  299 + "F",
  300 + 150
  301 + ]
  302 + ],
  303 + "hoverAnimation": true,
  304 + "label": {
  305 + "show": true,
  306 + "position": "top",
  307 + "margin": 8
  308 + },
  309 + "lineStyle": {
  310 + "show": true,
  311 + "width": 1,
  312 + "opacity": 1,
  313 + "curveness": 0,
  314 + "type": "solid"
  315 + },
  316 + "areaStyle": {
  317 + "opacity": 0
  318 + },
  319 + "zlevel": 0,
  320 + "z": 0
  321 + }
  322 + ],
  323 + "xAxis": [
  324 + {
  325 + "show": true,
  326 + "scale": false,
  327 + "nameLocation": "end",
  328 + "nameGap": 15,
  329 + "gridIndex": 0,
  330 + "inverse": false,
  331 + "offset": 0,
  332 + "splitNumber": 5,
  333 + "minInterval": 0,
  334 + "splitLine": {
  335 + "show": false,
  336 + "lineStyle": {
  337 + "show": true,
  338 + "width": 1,
  339 + "opacity": 1,
  340 + "curveness": 0,
  341 + "type": "solid"
  342 + }
  343 + },
  344 + "data": [
  345 + "A",
  346 + "B",
  347 + "C",
  348 + "D",
  349 + "E",
  350 + "F"
  351 + ]
  352 + }
  353 + ],
  354 + "yAxis": [
  355 + {
  356 + "show": true,
  357 + "scale": false,
  358 + "nameLocation": "end",
  359 + "nameGap": 15,
  360 + "gridIndex": 0,
  361 + "inverse": false,
  362 + "offset": 0,
  363 + "splitNumber": 5,
  364 + "minInterval": 0,
  365 + "splitLine": {
  366 + "show": false,
  367 + "lineStyle": {
  368 + "show": true,
  369 + "width": 1,
  370 + "opacity": 1,
  371 + "curveness": 0,
  372 + "type": "solid"
  373 + }
  374 + }
  375 + }
  376 + ],
  377 + "title": [
  378 + {
  379 + "text": "\u6298\u7ebf\u56fe\u793a\u4f8b",
  380 + "padding": 5,
  381 + "itemGap": 10
  382 + }
  383 + ],
  384 + "tooltip": {
  385 + "show": true,
  386 + "trigger": "item",
  387 + "triggerOn": "mousemove|click",
  388 + "axisPointer": {
  389 + "type": "line"
  390 + },
  391 + "showContent": true,
  392 + "alwaysShowContent": false,
  393 + "showDelay": 0,
  394 + "hideDelay": 100,
  395 + "textStyle": {
  396 + "fontSize": 14
  397 + },
  398 + "borderWidth": 0,
  399 + "padding": 5
  400 + },
  401 + "color": [
  402 + "#c23531",
  403 + "#2f4554",
  404 + "#61a0a8",
  405 + "#d48265",
  406 + "#749f83",
  407 + "#ca8622",
  408 + "#bda29a",
  409 + "#6e7074",
  410 + "#546570",
  411 + "#c4ccd3",
  412 + "#f05b72",
  413 + "#ef5b9c",
  414 + "#f47920",
  415 + "#905a3d",
  416 + "#fab27b",
  417 + "#2a5caa",
  418 + "#444693",
  419 + "#726930",
  420 + "#b2d235",
  421 + "#6d8346",
  422 + "#ac6767",
  423 + "#1d953f",
  424 + "#6950a1",
  425 + "#918597"
  426 + ]
  427 + }
  428 + ]
  429 +};
  430 + chart_9169d22a710c4474bfb38ee93c197ab9.setOption(option_9169d22a710c4474bfb38ee93c197ab9);
  431 + </script>
  432 +</body>
  433 +</html>
  1 +# _*_ coding:utf-8 _*_
  2 +import os
  3 +import uuid
  4 +from datetime import datetime
  5 +from app import db
  6 +from . import admin
  7 +from flask import render_template, redirect, url_for, flash, session, request, g, abort, make_response, current_app
  8 +from app.admin.forms import LoginForm, PwdForm, AreaForm, ScenicForm, TravelsForm, DeviceForm, DeviceTypeForm, \
  9 + DetectingPointForm, AdminForm, PowerForm, RoleForm, UserForm
  10 +from app.models import Admin, Adminlog, Oplog, Userlog, Area, User, Suggestion, Scenic, Travels, DeviceType, Device, \
  11 + DetectingPoint, Role, Power, Alarm, Notification
  12 +from werkzeug.utils import secure_filename
  13 +from sqlalchemy import or_, and_, func
  14 +from functools import wraps
  15 +from app.config import DeviceType as DT
  16 +from app.api.stack_func import get_stack
  17 +
  18 +
  19 +current_user = ""
  20 +
  21 +
  22 +def get_generate_password_hash(pwd):
  23 + from werkzeug.security import generate_password_hash
  24 + return generate_password_hash(password=pwd)
  25 +
  26 +
  27 +def is_valid_time_format(time_str):
  28 + # print("time_str="+time_str)
  29 + try:
  30 + datetime.strptime(time_str, "%Y-%m-%d %H:%M")
  31 + return True
  32 + except ValueError:
  33 + return False
  34 +
  35 +
  36 +def admin_login(f):
  37 + """
  38 + 登录装饰器
  39 + """
  40 +
  41 + @wraps(f)
  42 + def decorated_function(*args, **kwargs):
  43 + if "admin" not in session:
  44 + return redirect(url_for("admin.login", next=request.url))
  45 + return f(*args, **kwargs)
  46 +
  47 + return decorated_function
  48 +
  49 +
  50 +def addOplog(reason):
  51 + oplog = Oplog(
  52 + admin_id=session["admin_id"],
  53 + ip=request.remote_addr,
  54 + reason=reason
  55 + )
  56 + db.session.add(oplog)
  57 + db.session.commit()
  58 +
  59 +
  60 +def gen_rnd_filename():
  61 + return datetime.now().strftime("%Y%m%d%H%M%S") + str(uuid.uuid4().hex)
  62 +
  63 +
  64 +def change_filename(filename):
  65 + """
  66 + 修改文件名称
  67 + """
  68 + fileinfo = os.path.splitext(filename)
  69 + filename = gen_rnd_filename() + fileinfo[-1]
  70 + return filename
  71 +
  72 +
  73 +@admin.route("/")
  74 +@admin_login
  75 +def index():
  76 + days = datetime.strptime(f'{datetime.now().year}-{datetime.now().month}-{datetime.now().day} 00:00', "%Y-%m-%d %H:%M")
  77 +
  78 + device_data = Device.query.group_by(
  79 + Device.devicetype_id
  80 + ).order_by(
  81 + Device.devicetype_id.asc()
  82 + ).all()
  83 + for d in device_data:
  84 + if d.devicetype.type == 7001:
  85 + for dd in d.devicetype.device:
  86 +
  87 + print(dd.title)
  88 +
  89 + video = Alarm.query.join(
  90 + Device
  91 + ).join(
  92 + DeviceType
  93 + ).filter(
  94 + Alarm.device_id == Device.id
  95 + ).filter(
  96 + Device.devicetype_id == DT.Video
  97 + ).filter(
  98 + days < Alarm.addtime
  99 + ).order_by(
  100 + Alarm.addtime.desc()
  101 + ).paginate(page=1, per_page=10)
  102 +
  103 + gasc = Alarm.query.join(
  104 + Device
  105 + ).filter(
  106 + Device.id == Alarm.device_id,
  107 + ).filter(
  108 + days < Alarm.addtime
  109 + ).filter(
  110 + Device.devicetype_id == DT.GasConcentration
  111 + ).order_by(
  112 + Alarm.addtime.desc()
  113 + ).paginate(page=1, per_page=3)
  114 + if gasc.items is None or len(gasc.items) == 0:
  115 + gasc = None
  116 +
  117 + pressure = Alarm.query.join(
  118 + Device
  119 + ).filter(
  120 + Device.id == Alarm.device_id,
  121 + ).filter(
  122 + days < Alarm.addtime
  123 + ).filter(
  124 + Device.devicetype_id == DT.Pressure
  125 + ).order_by(
  126 + Alarm.addtime.desc()
  127 + ).paginate(page=1, per_page=3)
  128 + if pressure.items is None or len(pressure.items) == 0:
  129 + pressure = None
  130 +
  131 + radar = Alarm.query.join(
  132 + Device
  133 + ).filter(
  134 + Device.id == Alarm.device_id,
  135 + ).filter(
  136 + days < Alarm.addtime
  137 + ).filter(
  138 + Device.devicetype_id == DT.Radar
  139 + ).order_by(
  140 + Alarm.addtime.desc()
  141 + ).paginate(page=1, per_page=3)
  142 + if radar.items is None or len(radar.items) == 0:
  143 + radar = None
  144 +
  145 + return render_template("admin/index.html",
  146 + video=video, gasc=gasc, pressure=pressure,
  147 + device_data=device_data, radar=radar,
  148 + s_url=request.host_url, year=datetime.now().year)
  149 +
  150 +
  151 +@admin.route("/login/", methods=["GET", "POST"])
  152 +def login():
  153 + """
  154 + 登录功能
  155 + """
  156 + form = LoginForm() # 实例化登录表单
  157 + if form.validate_on_submit(): # 验证提交表单
  158 + data = form.data # 接收数据
  159 + admin = Admin.query.filter_by(name=data["account"]).first() # 查找Admin表数据
  160 + # 密码错误时,check_pwd返回false,则此时not check_pwd(data["pwd"])为真。
  161 + if not admin.check_pwd(data["pwd"]):
  162 + flash("密码错误!", "err") # 闪存错误信息
  163 + return redirect(url_for("admin.login")) # 跳转到后台登录页
  164 + # 如果是正确的,就要定义session的会话进行保存。
  165 + session["admin"] = data["account"] # 存入session
  166 + session["admin_id"] = admin.id # 存入session
  167 + # 创建数据
  168 + adminlog = Adminlog(
  169 + admin_id=admin.id,
  170 + ip=request.remote_addr,
  171 + )
  172 + db.session.add(adminlog) # 添加数据
  173 + db.session.commit() # 提交数据
  174 + next_url = request.args.get('next') or url_for('admin.index')
  175 + global current_user
  176 + current_user = (data['account'])
  177 +
  178 + return redirect(next_url)
  179 + # return redirect(url_for("admin.index")) # 返回后台主页
  180 +
  181 + return render_template("admin/login.html", form=form, year=datetime.now().year)
  182 +
  183 +
  184 +@admin.route('/logout/')
  185 +@admin_login
  186 +def logout():
  187 + """
  188 + 后台注销登录
  189 + """
  190 + session.pop("admin", None)
  191 + session.pop("admin_id", None)
  192 + return redirect(url_for("admin.login"))
  193 +
  194 +
  195 +@admin.route('/pwd/', methods=["GET", "POST"])
  196 +@admin_login
  197 +def pwd():
  198 + """
  199 + 后台密码修改
  200 + """
  201 + form = PwdForm()
  202 + if form.validate_on_submit():
  203 + data = form.data
  204 + admin = Admin.query.filter_by(name=session["admin"]).first()
  205 + from werkzeug.security import generate_password_hash
  206 + admin.pwd = generate_password_hash(data["new_pwd"])
  207 + db.session.add(admin)
  208 + db.session.commit()
  209 + flash("修改密码成功,请重新登录!", "ok")
  210 + return redirect(url_for('admin.logout'))
  211 + return render_template("admin/pwd.html", form=form, year=datetime.now().year)
  212 +
  213 +
  214 +@admin.route("/user/add/", methods=["GET", "POST"])
  215 +@admin_login
  216 +def user_add():
  217 + form = UserForm(False)
  218 + form.role_id.choices = [(v.id, v.name) for v in Role.query.order_by(func.length(Role.name)).all()] # 为role_id添加属性
  219 + form.power_id.choices = [(v.id, v.name) for v in Power.query.filter(Power.name.notlike("%管理员%")).order_by(
  220 + func.length(Power.name)).all()] # 为power_id添加属性
  221 +
  222 + if form.validate_on_submit():
  223 + data = form.data # 接收数据
  224 + print(data['pwd'])
  225 + count = User.query.filter_by(username=data["username"]).count()
  226 + # 说明已存在
  227 + if count == 1:
  228 + flash("用户名已存在", "err")
  229 + return redirect(url_for("admin.user_add"))
  230 +
  231 + user = User(
  232 + username=data["username"],
  233 + email=data["email"],
  234 + phone=data["phone"],
  235 + pwd=get_generate_password_hash(data['pwd']),
  236 +
  237 + role_id=data['role_id'],
  238 + power_id=data['power_id'],
  239 + )
  240 + db.session.add(user)
  241 + db.session.commit()
  242 + addOplog("添加用户" + data["username"]) # 添加日志
  243 + flash("用户添加成功", "ok")
  244 + return redirect(url_for("admin.user_add"))
  245 +
  246 + return render_template("admin/user_add.html", form=form, year=datetime.now().year)
  247 +
  248 +
  249 +@admin.route("/user/list/", methods=["GET"])
  250 +@admin_login
  251 +def user_list():
  252 + """
  253 + 会员列表
  254 + """
  255 + page = request.args.get('page', 1, type=int) # 获取page参数值
  256 + keyword = request.args.get('keyword', '', type=str)
  257 +
  258 + if keyword:
  259 + # 根据姓名或者邮箱查询
  260 + filters = or_(User.username == keyword, User.email == keyword)
  261 + page_data = User.query.filter(filters).order_by(
  262 + User.addtime.desc()
  263 + ).paginate(page=page, per_page=5)
  264 + else:
  265 + page_data = User.query.order_by(
  266 + User.addtime.desc()
  267 + ).paginate(page=page, per_page=5)
  268 +
  269 + return render_template("admin/user_list.html", page_data=page_data, year=datetime.now().year)
  270 +
  271 +
  272 +@admin.route('/power/add/', methods=["GET", "POST"])
  273 +@admin_login
  274 +def power_add():
  275 + """
  276 + 添加权限
  277 + """
  278 + form = PowerForm()
  279 + if form.validate_on_submit():
  280 + data = form.data # 接收数据
  281 + count = Power.query.filter_by(name=data["name"]).count()
  282 + # 说明已经有这个类型名称了
  283 + if count == 1:
  284 + flash("权限名称已存在", "err")
  285 + return redirect(url_for("admin.power_add"))
  286 + power = Power(
  287 + name=data["name"],
  288 + )
  289 + db.session.add(power)
  290 + db.session.commit()
  291 + addOplog("添加权限 " + data["name"]) # 添加日志
  292 + flash("权限添加成功", "ok")
  293 + return redirect(url_for("admin.power_add"))
  294 + return render_template("admin/power_add.html", form=form, year=datetime.now().year)
  295 +
  296 +
  297 +@admin.route("/power/list/", methods=["GET", "POST"])
  298 +@admin_login
  299 +def power_list():
  300 + """
  301 + 管理员列表
  302 + """
  303 + page = request.args.get('page', 1, type=int) # 获取page参数值
  304 + keywords = request.args.get('keywords', '', type=str)
  305 +
  306 + if keywords:
  307 + # 根据姓名或者邮箱查询
  308 + filters = or_(Power.name.like("%{0}%".format(keywords)))
  309 + page_data = Power.query.filter(filters).order_by(
  310 + Power.addtime.desc()
  311 + ).paginate(page=page, per_page=5)
  312 + else:
  313 + page_data = Power.query.order_by(
  314 + Power.addtime.desc()
  315 + ).paginate(page=page, per_page=5)
  316 +
  317 + return render_template("admin/power_list.html", page_data=page_data, year=datetime.now().year)
  318 +
  319 +
  320 +@admin.route("/power/edit/<int:id>", methods=["GET", "POST"])
  321 +@admin_login
  322 +def power_edit(id=None):
  323 + """
  324 + 编辑权限页面
  325 + """
  326 +
  327 + form = PowerForm() # 实例化
  328 + form.submit.label.text = "修改" # 修改提交按钮的文字
  329 +
  330 + power = Power.query.get_or_404(int(id)) # 根据ID查找
  331 +
  332 + if request.method == "GET": # 如果以GET方式提交,获取所有设备信息
  333 + form.name.data = power.name
  334 +
  335 + if form.validate_on_submit(): # 如果提交表单
  336 + data = form.data # 获取表单数据
  337 +
  338 + count = Power.query.filter_by(name=data["name"]).count()
  339 + if count == 1:
  340 + flash("权限名称已存在", "err")
  341 + return redirect(url_for("admin.power_edit", id=id))
  342 +
  343 + # 属性赋值
  344 + power.name = data["name"]
  345 +
  346 + db.session.commit() # 提交数据
  347 + flash("修改权限成功!", "ok")
  348 + return redirect(url_for('admin.power_edit', id=id)) # 跳转到编辑页面
  349 + return render_template("admin/power_edit.html", form=form, year=datetime.now().year) # 渲染模板,传递变量
  350 +
  351 +
  352 +@admin.route("/power/del/<int:id>", methods=["GET", "POST"])
  353 +@admin_login
  354 +def power_del(id=None):
  355 + """
  356 + 删除管理员
  357 + """
  358 + page = request.args.get('page', 1, type=int)
  359 + power = Power.query.get_or_404(int(id))
  360 + db.session.delete(power)
  361 + db.session.commit()
  362 + addOplog("删除权限 " + power.name) # 添加日志
  363 + flash("删除权限成功!", "ok")
  364 + return redirect(url_for('admin.power_list', page=page))
  365 +
  366 +
  367 +@admin.route('/role/add/', methods=["GET", "POST"])
  368 +@admin_login
  369 +def role_add():
  370 + """
  371 + 添加权限
  372 + """
  373 + form = RoleForm()
  374 + if form.validate_on_submit():
  375 + data = form.data # 接收数据
  376 + count = Role.query.filter_by(name=data["name"]).count()
  377 + # 说明已经有这个类型名称了
  378 + if count == 1:
  379 + flash("角色名称已存在", "err")
  380 + return redirect(url_for("admin.role_add"))
  381 + role = Role(
  382 + name=data["name"],
  383 + )
  384 + db.session.add(role)
  385 + db.session.commit()
  386 + addOplog("添加角色 " + data["name"]) # 添加日志
  387 + flash("角色添加成功", "ok")
  388 + return redirect(url_for("admin.role_add"))
  389 + return render_template("admin/role_add.html", form=form, year=datetime.now().year)
  390 +
  391 +
  392 +@admin.route("/role/list/", methods=["GET", "POST"])
  393 +@admin_login
  394 +def role_list():
  395 + """
  396 + 管理员列表
  397 + """
  398 + page = request.args.get('page', 1, type=int) # 获取page参数值
  399 + keywords = request.args.get('keywords', '', type=str)
  400 +
  401 + if keywords:
  402 + # 根据姓名或者邮箱查询
  403 + filters = or_(Role.name.like("%{0}%".format(keywords)))
  404 + page_data = Role.query.filter(filters).order_by(
  405 + Role.addtime.desc()
  406 + ).paginate(page=page, per_page=5)
  407 + else:
  408 + page_data = Role.query.order_by(
  409 + Role.addtime.desc()
  410 + ).paginate(page=page, per_page=5)
  411 +
  412 + return render_template("admin/role_list.html", page_data=page_data, year=datetime.now().year)
  413 +
  414 +
  415 +@admin.route("/role/edit/<int:id>", methods=["GET", "POST"])
  416 +@admin_login
  417 +def role_edit(id=None):
  418 + """
  419 + 编辑管理员页面
  420 + """
  421 +
  422 + form = RoleForm() # 实例化
  423 + form.submit.label.text = "修改" # 修改提交按钮的文字
  424 +
  425 + role = Role.query.get_or_404(int(id)) # 根据ID查找
  426 +
  427 + if request.method == "GET": # 如果以GET方式提交,获取所有设备信息
  428 + form.name.data = role.name
  429 +
  430 + if form.validate_on_submit(): # 如果提交表单
  431 + data = form.data # 获取表单数据
  432 +
  433 + count = Role.query.filter_by(name=data["name"]).count()
  434 + if count == 1:
  435 + flash("角色名称已存在", "err")
  436 + return redirect(url_for("admin.power_edit", id=id))
  437 +
  438 + # 属性赋值
  439 + role.name = data["name"]
  440 +
  441 + db.session.commit() # 提交数据
  442 + flash("修改角色成功!", "ok")
  443 + return redirect(url_for('admin.role_edit', id=id)) # 跳转到编辑页面
  444 + return render_template("admin/role_edit.html", form=form, year=datetime.now().year) # 渲染模板,传递变量
  445 +
  446 +
  447 +@admin.route("/role/del/<int:id>", methods=["GET", "POST"])
  448 +@admin_login
  449 +def role_del(id=None):
  450 + """
  451 + 删除管理员
  452 + """
  453 + page = request.args.get('page', 1, type=int)
  454 + role = Role.query.get_or_404(int(id))
  455 + db.session.delete(role)
  456 + db.session.commit()
  457 + addOplog("删除角色" + role.name) # 添加日志
  458 + flash("删除管理员成功!", "ok")
  459 + return redirect(url_for('admin.role_list', page=page))
  460 +
  461 +
  462 +@admin.route("/admin/list/", methods=["GET", "POST"])
  463 +@admin_login
  464 +def admin_list():
  465 + """
  466 + 管理员列表
  467 + """
  468 + page = request.args.get('page', 1, type=int) # 获取page参数值
  469 + keywords = request.args.get('keywords', '', type=str)
  470 +
  471 + if keywords:
  472 + # 根据姓名或者邮箱查询
  473 + filters = or_(Admin.name.like("%{0}%".format(keywords)))
  474 + page_data = Admin.query.filter(filters).order_by(
  475 + Admin.addtime.desc()
  476 + ).paginate(page=page, per_page=10)
  477 + else:
  478 + page_data = Admin.query.order_by(
  479 + Admin.addtime.desc()
  480 + ).paginate(page=page, per_page=10)
  481 +
  482 + return render_template("admin/admin_list.html", page_data=page_data, year=datetime.now().year)
  483 +
  484 +
  485 +@admin.route("/admin/add/", methods=["GET", "POST"])
  486 +@admin_login
  487 +def admin_add():
  488 + """
  489 + 添加管理员
  490 + """
  491 + form = AdminForm(False)
  492 + form.role_id.choices = [(v.id, v.name) for v in Role.query.order_by(func.length(Role.name)).all()] # 为role_id添加属性
  493 + form.power_id.choices = [(v.id, v.name) for v in Power.query.filter(Power.name.like("%管理员%")).order_by(
  494 + func.length(Power.name)).all()] # 为power_id添加属性
  495 +
  496 + if form.validate_on_submit():
  497 + data = form.data # 接收数据
  498 + admin_count = Admin.query.filter_by(name=data["name"]).count()
  499 + # 说明已存在
  500 + if admin_count == 1:
  501 + flash("管理员名称已存在", "err")
  502 + return redirect(url_for("admin.admin_add"))
  503 +
  504 + admin = Admin(
  505 + name=data["name"],
  506 + pwd=get_generate_password_hash(data['pwd']),
  507 +
  508 + role_id=data['role_id'],
  509 + power_id=data['power_id'],
  510 + )
  511 +
  512 + power = Power.query.filter_by(id=data["power_id"]).all()
  513 + if power[0].name == "超级管理员":
  514 + admin.is_super = 1
  515 + else:
  516 + admin.is_super = 0
  517 +
  518 + db.session.add(admin)
  519 + db.session.commit()
  520 + addOplog("添加管理员" + data["name"]) # 添加日志
  521 + flash("管理员添加成功", "ok")
  522 + return redirect(url_for("admin.admin_add"))
  523 + return render_template("admin/admin_add.html", form=form, year=datetime.now().year)
  524 +
  525 +
  526 +@admin.route("/admin/edit/<int:id>", methods=["GET", "POST"])
  527 +@admin_login
  528 +def admin_edit(id=None):
  529 + """
  530 + 编辑管理员页面
  531 + """
  532 +
  533 + form = AdminForm(True) # 实例化DeviceForm类
  534 + form.power_id.choices = [(v.id, v.name) for v in Power.query.all()] # 为power_id添加属性
  535 + form.role_id.choices = [(v.id, v.name) for v in Role.query.all()] # 为role_id添加属性
  536 + form.submit.label.text = "修改" # 修改提交按钮的文字
  537 +
  538 + admin = Admin.query.get_or_404(int(id)) # 根据ID查找
  539 +
  540 + if request.method == "GET": # 如果以GET方式提交,获取所有设备信息
  541 + form.name.data = admin.name
  542 + form.pwd.data = admin.pwd
  543 + form.repwd.data = admin.pwd
  544 + form.power_id.data = admin.power_id
  545 + form.role_id.data = admin.role_id
  546 +
  547 + if form.validate_on_submit(): # 如果提交表单
  548 + data = form.data # 获取表单数据
  549 +
  550 + # 属性赋值
  551 + admin.name = data["name"]
  552 + admin.pwd = get_generate_password_hash(data["pwd"])
  553 + admin.role_id = int(data["role_id"])
  554 + admin.power_id = int(data["power_id"])
  555 +
  556 + db.session.commit() # 提交数据
  557 + flash("修改管理员成功!", "ok")
  558 + return redirect(url_for('admin.admin_edit', id=id)) # 跳转到编辑页面
  559 + return render_template("admin/admin_edit.html", form=form, admin=admin, year=datetime.now().year) # 渲染模板,传递变量
  560 +
  561 +
  562 +@admin.route("/admin/del/<int:id>", methods=["GET", "POST"])
  563 +@admin_login
  564 +def admin_del(id=None):
  565 + """
  566 + 删除管理员
  567 + """
  568 + page = request.args.get('page', 1, type=int)
  569 + user = Admin.query.get_or_404(int(id))
  570 + db.session.delete(user)
  571 + db.session.commit()
  572 + addOplog("删除管理员" + user.name) # 添加日志
  573 + flash("删除管理员成功!", "ok")
  574 + return redirect(url_for('admin.admin_list', page=page))
  575 +
  576 +
  577 +@admin.route("/user/view/<int:id>/", methods=["GET"])
  578 +@admin_login
  579 +def user_view(id=None):
  580 + """
  581 + 查看会员详情
  582 + """
  583 + from_page = request.args.get('fp')
  584 + if not from_page:
  585 + from_page = 1
  586 + user = User.query.get_or_404(int(id))
  587 + return render_template("admin/user_view.html", user=user, from_page=from_page, year=datetime.now().year)
  588 +
  589 +
  590 +@admin.route("/user/del/<int:id>/", methods=["GET"])
  591 +@admin_login
  592 +def user_del(id=None):
  593 + """
  594 + 删除会员
  595 + """
  596 + page = request.args.get('page', 1, type=int)
  597 + user = User.query.get_or_404(int(id))
  598 + db.session.delete(user)
  599 + db.session.commit()
  600 + addOplog("删除会员" + user.username) # 添加日志
  601 + flash("删除会员成功!", "ok")
  602 + return redirect(url_for('admin.user_list', page=page))
  603 +
  604 +
  605 +@admin.route("/suggestion_list/list/", methods=["GET"])
  606 +@admin_login
  607 +def suggestion_list():
  608 + """
  609 + 意见建议列表
  610 + """
  611 + page = request.args.get('page', 1, type=int) # 获取page参数值
  612 + page_data = Suggestion.query.order_by(
  613 + Suggestion.addtime.desc()
  614 + ).paginate(page=page, per_page=5)
  615 + return render_template("admin/suggestion_list.html", page_data=page_data, year=datetime.now().year)
  616 +
  617 +
  618 +@admin.route("/suggestion/del/<int:id>/", methods=["GET"])
  619 +@admin_login
  620 +def suggestion_del(id=None):
  621 + """
  622 + 删除会员
  623 + """
  624 + page = request.args.get('page', 1, type=int)
  625 + suggestion = Suggestion.query.get_or_404(int(id))
  626 + db.session.delete(suggestion)
  627 + db.session.commit()
  628 + addOplog("删除意见建议") # 添加日志
  629 + flash("删除成功!", "ok")
  630 + return redirect(url_for('admin.suggestion_list', page=page))
  631 +
  632 +
  633 +@admin.route('/area/add/', methods=["GET", "POST"])
  634 +@admin_login
  635 +def area_add():
  636 + """
  637 + 添加地区
  638 + """
  639 + form = AreaForm()
  640 + if form.validate_on_submit():
  641 + data = form.data # 接收数据
  642 + area = Area.query.filter_by(name=data["name"]).count()
  643 + # 说明已经有这个地区了
  644 + if area == 1:
  645 + flash("地区已存在", "err")
  646 + return redirect(url_for("admin.area_add"))
  647 + area = Area(
  648 + name=data["name"],
  649 + is_recommended=data['is_recommended'],
  650 + introduction=data['introduction']
  651 + )
  652 + db.session.add(area)
  653 + db.session.commit()
  654 + addOplog("添加地区" + data["name"]) # 添加日志
  655 + flash("地区添加成功", "ok")
  656 + return redirect(url_for("admin.area_add"))
  657 + return render_template("admin/area_add.html", form=form, year=datetime.now().year)
  658 +
  659 +
  660 +@admin.route('/devicetype/add/', methods=["GET", "POST"])
  661 +@admin_login
  662 +def devicetype_add():
  663 + """
  664 + 添加地区
  665 + """
  666 + form = DeviceTypeForm()
  667 + if form.validate_on_submit():
  668 + data = form.data # 接收数据
  669 + devicetype = DeviceType.query.filter_by(name=data["name"]).count()
  670 + # 说明已经有这个设备类型名称了
  671 + if devicetype == 1:
  672 + flash("设备类型已存在", "err")
  673 + return redirect(url_for("admin.devicetype_add"))
  674 + devicetype = DeviceType(
  675 + name=data["name"],
  676 + type=data['type'],
  677 + )
  678 + db.session.add(devicetype)
  679 + db.session.commit()
  680 + addOplog("添加设备类型" + data["name"] + " " + data["type"]) # 添加日志
  681 + flash("设备类型添加成功", "ok")
  682 + return redirect(url_for("admin.devicetype_add"))
  683 + return render_template("admin/devicetype_add.html", form=form, year=datetime.now().year)
  684 +
  685 +
  686 +@admin.route('/detectingpoint/add/', methods=["GET", "POST"])
  687 +@admin_login
  688 +def detectingpoint_add():
  689 + """
  690 + 添加地区
  691 + """
  692 + form = DetectingPointForm()
  693 + if form.validate_on_submit():
  694 + data = form.data # 接收数据
  695 + detectingpoint = DetectingPoint.query.filter_by(no=data["no"]).filter_by(name=data["name"]).count()
  696 + # 说明已经有这个类型名称了
  697 + if detectingpoint == 1:
  698 + flash("检测点已存在", "err")
  699 + return redirect(url_for("admin.detectingpoint_add"))
  700 + detectingpoint = DetectingPoint(
  701 + name=data["name"],
  702 + no=data['no'],
  703 + )
  704 + db.session.add(detectingpoint)
  705 + db.session.commit()
  706 + addOplog("添加检测点" + data["name"] + " " + data["no"]) # 添加日志
  707 + flash("检测点添加成功", "ok")
  708 + return redirect(url_for("admin.detectingpoint_add"))
  709 + return render_template("admin/detectingpoint_add.html", form=form, year=datetime.now().year)
  710 +
  711 +
  712 +@admin.route("/area/edit/<int:id>", methods=["GET", "POST"])
  713 +@admin_login
  714 +def area_edit(id=None):
  715 + """
  716 + 地区编辑
  717 + """
  718 + form = AreaForm()
  719 + form.submit.label.text = "修改"
  720 + area = Area.query.get_or_404(id)
  721 + if request.method == "GET":
  722 + form.name.data = area.name
  723 + form.is_recommended.data = area.is_recommended
  724 + form.introduction.data = area.introduction
  725 + if form.validate_on_submit():
  726 + data = form.data
  727 + area_count = Area.query.filter_by(name=data["name"]).count()
  728 + if area.name != data["name"] and area_count == 1:
  729 + flash("景区已存在", "err")
  730 + return redirect(url_for("admin.area_edit", id=area.id))
  731 + area.name = data["name"]
  732 + area.is_recommended = int(data["is_recommended"])
  733 + area.introduction = data["introduction"]
  734 + db.session.add(area)
  735 + db.session.commit()
  736 + flash("标签修改成功", "ok")
  737 + return redirect(url_for("admin.area_edit", id=area.id))
  738 + return render_template("admin/area_edit.html", form=form, area=area, year=datetime.now().year)
  739 +
  740 +
  741 +@admin.route("/devicetype/edit/<int:type>", methods=["GET", "POST"])
  742 +@admin_login
  743 +def devicetype_edit(type=None):
  744 + """
  745 + 地区编辑
  746 + """
  747 + form = DeviceTypeForm()
  748 + form.submit.label.text = "修改"
  749 + devicetype = DeviceType.query.get_or_404(type)
  750 + if request.method == "GET":
  751 + form.name.data = devicetype.name
  752 + form.type.data = devicetype.type
  753 + if form.validate_on_submit():
  754 + data = form.data
  755 + devicetype_count = DeviceType.query.filter_by(name=data["name"]).count()
  756 + if devicetype.name != data["name"] and devicetype_count == 1:
  757 + flash("设备已存在", "err")
  758 + return redirect(url_for("admin.devicetype_edit", type=devicetype.type))
  759 + devicetype.name = data["name"]
  760 + devicetype.type = int(data["type"])
  761 + db.session.add(devicetype)
  762 + db.session.commit()
  763 + flash("设备类型修改成功", "ok")
  764 + return redirect(url_for("admin.devicetype_edit", type=devicetype.type))
  765 + return render_template("admin/devicetype_edit.html", form=form, devicetype=devicetype, year=datetime.now().year)
  766 +
  767 +
  768 +@admin.route("/detectingpoint/edit/<int:id>", methods=["GET", "POST"])
  769 +@admin_login
  770 +def detectingpoint_edit(id=None):
  771 + """
  772 + 检测点编辑
  773 + """
  774 + form = DetectingPointForm()
  775 + form.submit.label.text = "修改"
  776 + detectingpoint = DetectingPoint.query.get_or_404(id)
  777 + if request.method == "GET":
  778 + form.name.data = detectingpoint.name
  779 + form.no.data = detectingpoint.no
  780 + if form.validate_on_submit():
  781 + data = form.data
  782 + detectingpoint_count = DetectingPoint.query.filter_by(name=data["name"]).filter_by(no=data["no"]).count()
  783 + if detectingpoint_count == 1:
  784 + flash("检测点已存在", "err")
  785 + return redirect(url_for("admin.detectingpoint_edit", id=detectingpoint.id))
  786 + detectingpoint.name = data["name"]
  787 + detectingpoint.no = int(data["no"])
  788 + db.session.add(detectingpoint)
  789 + db.session.commit()
  790 + flash("检测点修改成功", "ok")
  791 + return redirect(url_for("admin.detectingpoint_edit", id=detectingpoint.id))
  792 + return render_template("admin/detectingpoint_edit.html", form=form, detectingpoint=detectingpoint,
  793 + year=datetime.now().year)
  794 +
  795 +
  796 +@admin.route("/area/list/", methods=["GET"])
  797 +@admin_login
  798 +def area_list():
  799 + """
  800 + 标签列表
  801 + """
  802 + name = request.args.get('name', type=str) # 获取name参数值
  803 + page = request.args.get('page', 1, type=int) # 获取page参数值
  804 + if name: # 搜索功能
  805 + page_data = Area.query.filter_by(name=name).order_by(
  806 + Area.addtime.desc()
  807 + ).paginate(page=page, per_page=5)
  808 + else:
  809 + # 查找数据
  810 + page_data = Area.query.order_by(
  811 + Area.addtime.desc()
  812 + ).paginate(page=page, per_page=5)
  813 + return render_template("admin/area_list.html", page_data=page_data, year=datetime.now().year) # 渲染模板
  814 +
  815 +
  816 +@admin.route("/devicetype/list/", methods=["GET"])
  817 +@admin_login
  818 +def devicetype_list():
  819 + """
  820 + 标签列表
  821 + """
  822 + name = request.args.get('name', type=str) # 获取name参数值
  823 + page = request.args.get('page', 1, type=int) # 获取page参数值
  824 + if name: # 搜索功能
  825 + page_data = DeviceType.query.filter_by(name=name).order_by(
  826 + DeviceType.addtime.desc()
  827 + ).paginate(page=page, per_page=5)
  828 + else:
  829 + # 查找数据
  830 + page_data = DeviceType.query.order_by(
  831 + DeviceType.addtime.desc()
  832 + ).paginate(page=page, per_page=5)
  833 + return render_template("admin/devicetype_list.html", page_data=page_data, year=datetime.now().year) # 渲染模板
  834 +
  835 +
  836 +@admin.route("/detectingpoint/list/", methods=["GET"])
  837 +@admin_login
  838 +def detectingpoint_list():
  839 + """
  840 + 标签列表
  841 + """
  842 + name = request.args.get('name', type=str) # 获取name参数值
  843 + page = request.args.get('page', 1, type=int) # 获取page参数值
  844 + if name: # 搜索功能
  845 + page_data = DetectingPoint.query.filter_by(name=name).order_by(
  846 + DetectingPoint.addtime.desc()
  847 + ).paginate(page=page, per_page=5)
  848 + else:
  849 + # 查找数据
  850 + page_data = DetectingPoint.query.order_by(
  851 + DetectingPoint.addtime.desc()
  852 + ).paginate(page=page, per_page=5)
  853 + return render_template("admin/detectingpoint_list.html", page_data=page_data, year=datetime.now().year) # 渲染模板
  854 +
  855 +
  856 +@admin.route("/area/del/<int:id>/", methods=["GET"])
  857 +@admin_login
  858 +def area_del(id=None):
  859 + """
  860 + 标签删除
  861 + """
  862 + # filter_by在查不到或多个的时候并不会报错,get会报错。
  863 + area = Area.query.filter_by(id=id).first_or_404()
  864 + db.session.delete(area)
  865 + db.session.commit()
  866 + addOplog("删除地区" + area.name) # 添加日志
  867 + flash("地区<<{0}>>删除成功".format(area.name), "ok")
  868 + return redirect(url_for("admin.area_list"))
  869 +
  870 +
  871 +@admin.route("/devicetype/del/<int:type>/", methods=["GET"])
  872 +@admin_login
  873 +def devicetype_del(type=None):
  874 + """
  875 + 标签删除
  876 + """
  877 + # filter_by在查不到或多个的时候并不会报错,get会报错。
  878 + devicetype = DeviceType.query.filter_by(type=type).first_or_404()
  879 + db.session.delete(devicetype)
  880 + db.session.commit()
  881 + addOplog("删除设备类型" + devicetype.name) # 添加日志
  882 + flash("设备类型<<{0}>>删除成功".format(devicetype.name), "ok")
  883 + return redirect(url_for("admin.devicetype_list"))
  884 +
  885 +
  886 +@admin.route("/detectingpoint/del/<int:id>/", methods=["GET"])
  887 +@admin_login
  888 +def detectingpoint_del(id=None):
  889 + """
  890 + 标签删除
  891 + """
  892 + # filter_by在查不到或多个的时候并不会报错,get会报错。
  893 + detectingpoint = DetectingPoint.query.filter_by(id=id).first_or_404()
  894 + db.session.delete(detectingpoint)
  895 + db.session.commit()
  896 + addOplog("删除检测点" + detectingpoint.name + " " + str(detectingpoint.no)) # 添加日志
  897 + flash("检测点<<{0} {1}>>删除成功".format(detectingpoint.name, detectingpoint.no), "ok")
  898 + return redirect(url_for("admin.detectingpoint_list"))
  899 +
  900 +
  901 +@admin.route("/oplog/list/", methods=["GET"])
  902 +@admin_login
  903 +def oplog_list():
  904 + """
  905 + 操作日志管理
  906 + """
  907 + page = request.args.get('page', 1, type=int) # 获取page参数值
  908 + page_data = Oplog.query.join(
  909 + Admin
  910 + ).filter(
  911 + Admin.id == Oplog.admin_id,
  912 + ).order_by(
  913 + Oplog.addtime.desc()
  914 + ).paginate(page=page, per_page=10)
  915 + return render_template("admin/oplog_list.html", page_data=page_data, year=datetime.now().year)
  916 +
  917 +
  918 +last_st = ""
  919 +last_et = ""
  920 +last_key = ""
  921 +
  922 +
  923 +@admin.route("/alarm/list/", methods=["GET", "POST"])
  924 +@admin_login
  925 +def alarm_list():
  926 + """
  927 + 报警列表
  928 + """
  929 + st = datetime.now().strftime('%Y-%m-%d 00:00')
  930 + et = datetime.now().strftime('%Y-%m-%d 23:59')
  931 +
  932 + global last_st
  933 + global last_et
  934 + global last_key
  935 +
  936 + if request.method == "POST":
  937 + dt1 = request.form.get('datepicker1', '', type=str).replace("T", " ") # 获取查询标题
  938 + dt2 = request.form.get('datepicker2', '', type=str).replace("T", " ") # 获取查询标题
  939 +
  940 + print(f'dt1={dt1}')
  941 + print(f'dt2={dt2}')
  942 +
  943 + if is_valid_time_format(dt1.replace("T", " ")):
  944 + st = dt1
  945 +
  946 + if is_valid_time_format(dt2.replace("T", " ")):
  947 + et = dt2
  948 +
  949 + last_st = st
  950 + last_et = et
  951 +
  952 + print(f'last_st={last_st}')
  953 + print(f'last_et={last_et}')
  954 + last_key = request.form.get('keywords', '', type=str) # 获取关键字
  955 + page = request.form.get('page', 1, type=int) # 获取page参数值
  956 + else:
  957 +
  958 + if last_st == "":
  959 + last_st = st
  960 +
  961 + if last_et == "":
  962 + last_et = et
  963 +
  964 + keywords = request.args.get('keywords', '', type=str) # 获取关键字
  965 + page = request.args.get('page', 1, type=int) # 获取page参数值
  966 +
  967 + page_data = Alarm.query.join(
  968 + Device
  969 + ).filter(
  970 + Device.id == Alarm.device_id,
  971 + ).filter(
  972 + datetime.strptime(last_st, "%Y-%m-%d %H:%M") < Alarm.addtime
  973 + ).filter(
  974 + Alarm.addtime < datetime.strptime(last_et, "%Y-%m-%d %H:%M")
  975 + ).filter(
  976 + Alarm.msg.like("%" + last_key + "%")
  977 + ).order_by(
  978 + Alarm.addtime.desc()
  979 + ).paginate(page=page, per_page=10)
  980 + return render_template("admin/alarm_list.html", key=last_key, st=last_st, et=last_et, page_data=page_data, year=datetime.now().year)
  981 +
  982 +
  983 +@admin.route("/alarm/edit/", methods=["GET"])
  984 +@admin_login
  985 +def alarm_edit():
  986 + """
  987 + 报警处理
  988 + """
  989 + print("edit")
  990 + no = request.args.get('no')
  991 + msg = request.args.get('msg')
  992 + alarm = Alarm.query.get_or_404(int(no)) # 根据ID查找记录是否存在
  993 +
  994 + print(f'no={no}')
  995 + print(f'msg={msg}')
  996 +
  997 + alarm.remark = msg
  998 + alarm.handle = 1
  999 + alarm.handle_user = current_user
  1000 + db.session.commit()
  1001 +
  1002 + return "1"
  1003 +
  1004 +
  1005 +@admin.route("/alarm/del/<int:id>/", methods=["GET"])
  1006 +@admin_login
  1007 +def alarm_del(id=None):
  1008 + """
  1009 + 删除报警记录
  1010 + """
  1011 + alarm = Alarm.query.get_or_404(id) # 根据设备ID查找数据
  1012 + db.session.delete(alarm) # 删除数据
  1013 + db.session.commit() # 提交数据
  1014 + flash("记录删除成功", "ok") # 使用flash存储成功信息
  1015 + try:
  1016 + addOplog("删除报警记录:" + str(alarm.id) + "(" + alarm.msg + ")") # 添加日志
  1017 + except ValueError:
  1018 + pass
  1019 + return redirect(url_for('admin.alarm_list', page=1)) # 渲染模板
  1020 +
  1021 +
  1022 +@admin.route("/alarm/statistics/", methods=["GET", "POST"])
  1023 +@admin_login
  1024 +def alarm_statistics():
  1025 + """
  1026 + 报警统计
  1027 + """
  1028 + st = datetime.now().strftime('%Y-%m-%d 00:00')
  1029 + et = datetime.now().strftime('%Y-%m-%d 23:59')
  1030 + if request.method == "GET":
  1031 + dt1 = request.args.get('datepicker1', '', type=str) # 获取查询标题
  1032 + dt2 = request.args.get('datepicker2', '', type=str) # 获取查询标题
  1033 +
  1034 + if is_valid_time_format(dt1.replace("T", " ")):
  1035 + st = dt1
  1036 + if is_valid_time_format(dt2.replace("T", " ")):
  1037 + et = dt2
  1038 +
  1039 + else:
  1040 + dt1 = request.form['datepicker1'] # 获取查询标题
  1041 + dt2 = request.form['datepicker2'] # 获取查询标题
  1042 + print(dt1)
  1043 + if is_valid_time_format(dt1.replace("T", " ")):
  1044 + st = dt1
  1045 + if is_valid_time_format(dt2.replace("T", " ")):
  1046 + et = dt2
  1047 + print(datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
  1048 + return render_template("admin/alarm_statistics.html", s_url=request.host_url, st=st, et=et, year=datetime.now().year)
  1049 +
  1050 +
  1051 +@admin.route("/notification/list/", methods=["GET"])
  1052 +@admin_login
  1053 +def notification_list():
  1054 + page = request.args.get("page", 1, type=int)
  1055 + keywords = request.args.get("keywords", "", type=str)
  1056 + page_data = Notification.query.order_by(
  1057 + Notification.addtime.desc()
  1058 + ).paginate(page=page, per_page=10)
  1059 +
  1060 + return render_template("admin/notification_list.html", page_data="", year=datetime.now().year)
  1061 +
  1062 +
  1063 +@admin.route("/adminloginlog/list/", methods=["GET"])
  1064 +@admin_login
  1065 +def adminloginlog_list(page=None):
  1066 + """
  1067 + 管理员登录日志
  1068 + """
  1069 + page = request.args.get('page', 1, type=int) # 获取page参数值
  1070 + page_data = Adminlog.query.join(
  1071 + Admin
  1072 + ).filter(
  1073 + Admin.id == Adminlog.admin_id,
  1074 + ).order_by(
  1075 + Adminlog.addtime.desc()
  1076 + ).paginate(page=page, per_page=10)
  1077 + return render_template("admin/adminloginlog_list.html", page_data=page_data, year=datetime.now().year)
  1078 +
  1079 +
  1080 +@admin.route("/userloginlog/list/", methods=["GET"])
  1081 +@admin_login
  1082 +def userloginlog_list(page=None):
  1083 + """
  1084 + 会员登录日志列表
  1085 + """
  1086 + if page is None:
  1087 + page = 1
  1088 + page_data = Userlog.query.join(
  1089 + User
  1090 + ).filter(
  1091 + User.id == Userlog.user_id,
  1092 + ).order_by(
  1093 + Userlog.addtime.desc()
  1094 + ).paginate(page=page, per_page=2)
  1095 + return render_template("admin/userloginlog_list.html", page_data=page_data, year=datetime.now().year)
  1096 +
  1097 +
  1098 +@admin.route("/scenic/add/", methods=["GET", "POST"])
  1099 +@admin_login
  1100 +def scenic_add():
  1101 + """
  1102 + 添加景区页面
  1103 + """
  1104 + form = ScenicForm() # 实例化form表单
  1105 + form.area_id.choices = [(v.id, v.name) for v in Area.query.all()] # 为area_id添加属性
  1106 + if form.validate_on_submit():
  1107 + data = form.data
  1108 + # 判断景区是否存在
  1109 + scenic_count = Scenic.query.filter_by(title=data["title"]).count()
  1110 + # 判断是否有重复数据。
  1111 + if scenic_count == 1:
  1112 + flash("景点已经存在!", "err")
  1113 + return redirect(url_for('admin.scenic_add'))
  1114 +
  1115 + file_logo = secure_filename(form.logo.data.filename) # 确保文件名
  1116 + if not os.path.exists(current_app.config["UP_DIR"]):
  1117 + # 创建一个多级目录
  1118 + os.makedirs(current_app.config["UP_DIR"]) # 创建文件夹
  1119 + os.chmod(current_app.config["UP_DIR"], "rw") # 设置权限
  1120 + logo = change_filename(file_logo) # 更改名称
  1121 + form.logo.data.save(current_app.config["UP_DIR"] + logo) # 保存文件
  1122 + # 为Scenic类属性赋值
  1123 + scenic = Scenic(
  1124 + title=data["title"],
  1125 + logo=logo,
  1126 + star=int(data["star"]),
  1127 + address=data["address"],
  1128 + is_hot=int(data["is_hot"]),
  1129 + is_recommended=int(data["is_recommended"]),
  1130 + area_id=data["area_id"],
  1131 + introduction=data["introduction"],
  1132 + content=data["content"],
  1133 + )
  1134 + db.session.add(scenic) # 添加数据
  1135 + db.session.commit() # 提交数据
  1136 + addOplog("添加景区" + data["title"]) # 添加日志
  1137 + flash("添加景区成功!", "ok") # 使用flash保存添加成功信息
  1138 + return redirect(url_for('admin.scenic_add')) # 页面跳转
  1139 + return render_template("admin/scenic_add.html", form=form, year=datetime.now().year) # 渲染模板
  1140 +
  1141 +
  1142 +@admin.route("/device/add/", methods=["GET", "POST"])
  1143 +@admin_login
  1144 +def device_add():
  1145 + """
  1146 + 添加设备页面
  1147 + """
  1148 + form = DeviceForm(False) # 实例化form表单
  1149 + form.devicetype_id.choices = [(v.type, v.name) for v in DeviceType.query.all()] # 为devicetype_id添加属性
  1150 + form.detectingpoint_id.choices = [(v.id, v.name + str(v.no)) for v in
  1151 + DetectingPoint.query.all()] # 为detectingpoint_id添加属性
  1152 + if form.validate_on_submit():
  1153 + data = form.data
  1154 + # 判断设备是否存在
  1155 + # 设备地址以及设备id唯一
  1156 + device_count = Device.query.filter_by(devicetype_id=data["devicetype_id"]).filter_by(
  1157 + address=data["address"]).count()
  1158 + # 判断是否有重复数据。
  1159 + if device_count == 1:
  1160 + flash("设备已经存在!", "err")
  1161 + return redirect(url_for('admin.device_add'))
  1162 +
  1163 + file_logo = secure_filename(form.logo.data.filename) # 确保文件名
  1164 + if not os.path.exists(current_app.config["UP_DIR"]):
  1165 + # 创建一个多级目录
  1166 + os.makedirs(current_app.config["UP_DIR"]) # 创建文件夹
  1167 + os.chmod(current_app.config["UP_DIR"], "rw") # 设置权限
  1168 + logo = change_filename(file_logo) # 更改名称
  1169 + form.logo.data.save(current_app.config["UP_DIR"] + logo) # 保存文件
  1170 + # 为Scenic类属性赋值
  1171 + device = Device(
  1172 + title=data["title"],
  1173 + logo=logo,
  1174 + address=data["address"],
  1175 + threshold=data["threshold"],
  1176 + devicetype_id=data["devicetype_id"],
  1177 + detectingpoint_id=data["detectingpoint_id"],
  1178 + introduction=data["introduction"],
  1179 + )
  1180 + db.session.add(device) # 添加数据
  1181 + db.session.commit() # 提交数据
  1182 + addOplog("添加设备名称:" + data["title"] + ",地址:" + data["address"]) # 添加日志
  1183 + flash("添加设备成功!", "ok") # 使用flash保存添加成功信息
  1184 + return redirect(url_for('admin.device_add')) # 页面跳转
  1185 + return render_template("admin/device_add.html", form=form, year=datetime.now().year) # 渲染模板
  1186 +
  1187 +
  1188 +@admin.route("/scenic/list/", methods=["GET"])
  1189 +@admin_login
  1190 +def scenic_list():
  1191 + """
  1192 + 景区列表页面
  1193 + """
  1194 + title = request.args.get('title', '', type=str) # 获取查询标题
  1195 + page = request.args.get('page', 1, type=int) # 获取page参数值
  1196 + if title: # 根据标题搜索景区
  1197 + page_data = Scenic.query.filter_by(title=title).order_by(
  1198 + Scenic.addtime.desc() # 根据添加时间降序
  1199 + ).paginate(page=page, per_page=5) # 分页
  1200 + else: # 显示全部景区
  1201 + page_data = Scenic.query.order_by(
  1202 + Scenic.addtime.desc() # 根据添加时间降序
  1203 + ).paginate(page=page, per_page=5) # 分页
  1204 + return render_template("admin/scenic_list.html", page_data=page_data, year=datetime.now().year) # 渲染模板
  1205 +
  1206 +
  1207 +@admin.route("/device/list/", methods=["GET"])
  1208 +@admin_login
  1209 +def device_list():
  1210 + """
  1211 + 设备列表页面
  1212 + """
  1213 + keywords = request.args.get('keywords', '', type=str) # 获取查询标题
  1214 + page = request.args.get('page', 1, type=int) # 获取page参数值
  1215 + if keywords: # 根据设备名称搜索
  1216 + page_data = Device.query.filter(Device.title.like("%" + keywords + "%")).order_by(
  1217 + Device.addtime.desc() # 根据添加时间降序
  1218 + ).paginate(page=page, per_page=5) # 分页
  1219 + else: # 显示全部设备
  1220 + page_data = Device.query.order_by(
  1221 + Device.addtime.desc() # 根据添加时间降序
  1222 + ).paginate(page=page, per_page=5) # 分页
  1223 + return render_template("admin/device_list.html", page_data=page_data, year=datetime.now().year) # 渲染模板
  1224 +
  1225 +
  1226 +@admin.route("/scenic/edit/<int:id>/", methods=["GET", "POST"])
  1227 +@admin_login
  1228 +def scenic_edit(id=None):
  1229 + """
  1230 + 编辑景区页面
  1231 + """
  1232 + form = ScenicForm() # 实例化ScenicForm类
  1233 + form.area_id.choices = [(v.id, v.name) for v in Area.query.all()] # 为area_id添加属性
  1234 + form.submit.label.text = "修改" # 修改提交按钮的文字
  1235 + form.logo.validators = [] # 初始化为空
  1236 + scenic = Scenic.query.get_or_404(int(id)) # 根据ID查找景区是否存在
  1237 + if request.method == "GET": # 如果以GET方式提交,获取所有景区信息
  1238 + form.is_recommended.data = scenic.is_recommended
  1239 + form.is_hot.data = scenic.is_hot
  1240 + form.area_id.data = scenic.area_id
  1241 + form.star.data = scenic.star
  1242 + form.content.data = scenic.content
  1243 + form.introduction.data = scenic.introduction
  1244 + if form.validate_on_submit(): # 如果提交表单
  1245 + data = form.data # 获取表单数据
  1246 + scenic_count = Scenic.query.filter_by(title=data["title"]).count() # 判断标题是否重复
  1247 + # 判断是否有重复数据
  1248 + if scenic_count == 1:
  1249 + flash("景点已经存在!", "err")
  1250 + return redirect(url_for('admin.scenic_edit', id=id))
  1251 + if not os.path.exists(current_app.config["UP_DIR"]): # 判断目录是否存在
  1252 + os.makedirs(current_app.config["UP_DIR"]) # 创建目录
  1253 + os.chmod(current_app.config["UP_DIR"], "rw") # 设置读写权限
  1254 + # 上传图片
  1255 + if form.logo.data.filename != "":
  1256 + file_logo = secure_filename(form.logo.data.filename) # 确保文件名安全
  1257 + scenic.logo = change_filename(file_logo) # 更改文件名
  1258 + form.logo.data.save(current_app.config["UP_DIR"] + scenic.logo) # 保存文件
  1259 + # 属性赋值
  1260 + scenic.title = data["title"]
  1261 + scenic.address = data["address"]
  1262 + scenic.area_id = data["area_id"]
  1263 + scenic.star = int(data["star"])
  1264 + scenic.is_hot = int(data["is_hot"])
  1265 + scenic.is_recommended = int(data["is_recommended"])
  1266 + scenic.introduction = data["introduction"]
  1267 + scenic.content = data["content"]
  1268 +
  1269 + db.session.add(scenic) # 添加数据
  1270 + db.session.commit() # 提交数据
  1271 + flash("修改景区成功!", "ok")
  1272 + return redirect(url_for('admin.scenic_edit', id=id)) # 跳转到编辑页面
  1273 + return render_template("admin/scenic_edit.html", form=form, scenic=scenic, year=datetime.now().year) # 渲染模板,传递变量
  1274 +
  1275 +
  1276 +@admin.route("/device/edit/<int:id>/", methods=["GET", "POST"])
  1277 +@admin_login
  1278 +def device_edit(id=None):
  1279 + """
  1280 + 编辑设备页面
  1281 + """
  1282 + form = DeviceForm(True) # 实例化DeviceForm类
  1283 + form.devicetype_id.choices = [(v.type, v.name) for v in DeviceType.query.all()] # 为devicetype_id添加属性
  1284 + form.detectingpoint_id.choices = [(v.id, v.name + str(v.no)) for v in
  1285 + DetectingPoint.query.all()] # 为detectingpoint_id添加属性
  1286 + form.submit.label.text = "修改" # 修改提交按钮的文字
  1287 + form.logo.validators = [] # 初始化为空
  1288 + device = Device.query.get_or_404(int(id)) # 根据ID查找设备是否存在
  1289 + print("================")
  1290 + print(form)
  1291 + print("================")
  1292 + if request.method == "GET": # 如果以GET方式提交,获取所有设备信息
  1293 + form.title.data = device.title
  1294 + form.address.data = device.address
  1295 + form.threshold.data = device.threshold
  1296 + form.logo.data = device.logo
  1297 + form.introduction.data = device.introduction
  1298 + form.devicetype_id.data = device.devicetype_id
  1299 + form.detectingpoint_id.data = device.detectingpoint_id
  1300 + if form.validate_on_submit(): # 如果提交表单
  1301 + data = form.data # 获取表单数据
  1302 +
  1303 + if not os.path.exists(current_app.config["UP_DIR"]): # 判断目录是否存在
  1304 + os.makedirs(current_app.config["UP_DIR"]) # 创建目录
  1305 + os.chmod(current_app.config["UP_DIR"], "rw") # 设置读写权限
  1306 + # 上传图片
  1307 + if form.logo.data.filename != "":
  1308 + file_logo = secure_filename(form.logo.data.filename) # 确保文件名安全
  1309 + device.logo = change_filename(file_logo) # 更改文件名
  1310 + form.logo.data.save(current_app.config["UP_DIR"] + device.logo) # 保存文件
  1311 + # 属性赋值
  1312 + device.title = data["title"]
  1313 + device.address = data["address"]
  1314 + device.threshold = data["threshold"]
  1315 + device.devicetype_id = int(data["devicetype_id"])
  1316 + device.detectingpoint_id = int(data["detectingpoint_id"])
  1317 + device.introduction = data["introduction"]
  1318 +
  1319 + db.session.commit() # 提交数据
  1320 + flash("修改设备成功!", "ok")
  1321 + return redirect(url_for('admin.device_edit', id=id)) # 跳转到编辑页面
  1322 + return render_template("admin/device_edit.html", form=form, device=device, year=datetime.now().year) # 渲染模板,传递变量
  1323 +
  1324 +
  1325 +@admin.route("/scenic/del/<int:id>/", methods=["GET"])
  1326 +@admin_login
  1327 +def scenic_del(id=None):
  1328 + """
  1329 + 景区删除
  1330 + """
  1331 + scenic = Scenic.query.get_or_404(id) # 根据景区ID查找数据
  1332 + db.session.delete(scenic) # 删除数据
  1333 + db.session.commit() # 提交数据
  1334 + flash("景区删除成功", "ok") # 使用flash存储成功信息
  1335 + addOplog("删除景区" + scenic.title) # 添加日志
  1336 + return redirect(url_for('admin.scenic_list', page=1)) # 渲染模板
  1337 +
  1338 +
  1339 +@admin.route("/device/del/<int:id>/", methods=["GET"])
  1340 +@admin_login
  1341 +def device_del(id=None):
  1342 + """
  1343 + 设备删除
  1344 + """
  1345 + device = Device.query.get_or_404(id) # 根据设备ID查找数据
  1346 + db.session.delete(device) # 删除数据
  1347 + db.session.commit() # 提交数据
  1348 + flash("设备删除成功", "ok") # 使用flash存储成功信息
  1349 + addOplog("删除设备" + device.title + " " + device.address) # 添加日志
  1350 + return redirect(url_for('admin.device_list', page=1)) # 渲染模板
  1351 +
  1352 +
  1353 +@admin.route("/travels/add/", methods=["GET", "POST"])
  1354 +@admin_login
  1355 +def travels_add():
  1356 + """
  1357 + 添加游记
  1358 + """
  1359 + form = TravelsForm()
  1360 + form.scenic_id.choices = [(v.id, v.title) for v in Scenic.query.all()]
  1361 + if form.validate_on_submit():
  1362 + data = form.data
  1363 + # 判断游记是否存在
  1364 + travels_count = Travels.query.filter_by(title=data["title"]).count()
  1365 + # 判断是否有重复数据。
  1366 + if travels_count == 1:
  1367 + flash("景点已经存在!", "err")
  1368 + return redirect(url_for('admin.travels_add'))
  1369 + travels = Travels(
  1370 + title=data["title"],
  1371 + author=data["author"],
  1372 + scenic_id=data["scenic_id"],
  1373 + content=data["content"],
  1374 + )
  1375 + db.session.add(travels)
  1376 + db.session.commit()
  1377 + addOplog("添加游记" + data["title"]) # 添加日志
  1378 + flash("添加游记成功!", "ok")
  1379 + return redirect(url_for('admin.travels_add'))
  1380 + return render_template("admin/travels_add.html", form=form, year=datetime.now().year)
  1381 +
  1382 +
  1383 +@admin.route("/travels/list/", methods=["GET"])
  1384 +@admin_login
  1385 +def travels_list():
  1386 + """
  1387 + 景区列表页面
  1388 + """
  1389 + keywords = request.args.get('keywords', '', type=str)
  1390 + page = request.args.get('page', 1, type=int) # 获取page参数值
  1391 + if keywords:
  1392 + # 使用like实现模糊查询
  1393 + page_data = Travels.query.filter(Travels.title.like("%" + keywords + "%")).order_by(
  1394 + Travels.addtime.desc()
  1395 + ).paginate(page=page, per_page=5)
  1396 + else:
  1397 + page_data = Travels.query.order_by(
  1398 + Travels.addtime.desc()
  1399 + ).paginate(page=page, per_page=5)
  1400 + return render_template("admin/travels_list.html", page_data=page_data, year=datetime.now().year)
  1401 +
  1402 +
  1403 +@admin.route("/travels/edit/<int:id>/", methods=["GET", "POST"])
  1404 +@admin_login
  1405 +def travels_edit(id=None):
  1406 + """
  1407 + 编辑游记
  1408 + """
  1409 + form = TravelsForm()
  1410 + form.scenic_id.choices = [(v.id, v.title) for v in Scenic.query.all()]
  1411 + form.submit.label.text = "修改"
  1412 + travels = Travels.query.get_or_404(int(id))
  1413 + if request.method == "GET":
  1414 + form.scenic_id.data = travels.scenic_id
  1415 + form.content.data = travels.content
  1416 + if form.validate_on_submit():
  1417 + data = form.data
  1418 + travels_count = Travels.query.filter_by(title=data["title"]).count()
  1419 + # 判断是否有重复数据
  1420 + if travels_count == 1 and travels.title != data["title"]:
  1421 + flash("游记已经存在!", "err")
  1422 + return redirect(url_for('admin.travels_edit', id=id))
  1423 +
  1424 + travels.title = data["title"]
  1425 + travels.scenic_id = data["scenic_id"]
  1426 + travels.author = data["author"]
  1427 + travels.content = data["content"]
  1428 +
  1429 + db.session.add(travels)
  1430 + db.session.commit()
  1431 + flash("修改景区成功!", "ok")
  1432 + return redirect(url_for('admin.travels_edit', id=id))
  1433 + return render_template("admin/travels_edit.html", form=form, travels=travels, year=datetime.now().year)
  1434 +
  1435 +
  1436 +@admin.route("/travels/del/<int:id>/", methods=["GET"])
  1437 +@admin_login
  1438 +def travels_del(id=None):
  1439 + """
  1440 + 景区删除
  1441 + """
  1442 + travels = Travels.query.get_or_404(id)
  1443 + db.session.delete(travels)
  1444 + db.session.commit()
  1445 + flash("游记删除成功", "ok")
  1446 + addOplog("删除游记" + travels.title) # 添加日志
  1447 + return redirect(url_for('admin.travels_list', page=1))
  1448 +
  1449 +
  1450 +@admin.route('/ckupload/', methods=['POST', 'OPTIONS'])
  1451 +@admin_login
  1452 +def ckupload():
  1453 + """CKEditor 文件上传"""
  1454 + error = ''
  1455 + url = ''
  1456 + callback = request.args.get("CKEditorFuncNum")
  1457 +
  1458 + if request.method == 'POST' and 'upload' in request.files:
  1459 + fileobj = request.files['upload']
  1460 + fname, fext = os.path.splitext(fileobj.filename)
  1461 + rnd_name = '%s%s' % (gen_rnd_filename(), fext)
  1462 +
  1463 + filepath = os.path.join(current_app.static_folder, 'uploads/ckeditor', rnd_name)
  1464 + print(filepath)
  1465 + # 检查路径是否存在,不存在则创建
  1466 + dirname = os.path.dirname(filepath)
  1467 + if not os.path.exists(dirname):
  1468 + try:
  1469 + os.makedirs(dirname)
  1470 + except:
  1471 + error = 'ERROR_CREATE_DIR'
  1472 + elif not os.access(dirname, os.W_OK):
  1473 + error = 'ERROR_DIR_NOT_WRITEABLE'
  1474 +
  1475 + if not error:
  1476 + fileobj.save(filepath)
  1477 + url = url_for('static', filename='%s/%s' % ('uploads/ckeditor', rnd_name))
  1478 + else:
  1479 + error = 'post error'
  1480 +
  1481 + res = """<script type="text/javascript">
  1482 + window.parent.CKEDITOR.tools.callFunction(%s, '%s', '%s');
  1483 +</script>""" % (callback, url, error)
  1484 +
  1485 + response = make_response(res)
  1486 + response.headers["Content-Type"] = "text/html"
  1487 + return response
  1488 +
  1489 +
  1490 +from flask.json import jsonify
  1491 +from random import randrange
  1492 +from pyecharts.charts import Bar, Pie, Line, Timeline
  1493 +from pyecharts import options as opts
  1494 +
  1495 +
  1496 +def bar_base() -> Bar:
  1497 + c = (
  1498 + Bar()
  1499 + .add_xaxis(["用电管理", "电表", "浓度", "压力", "急停开关", "安防雷达"])
  1500 + .add_yaxis("报警", [randrange(0, 100) for _ in range(6)])
  1501 + .set_global_opts(title_opts=opts.TitleOpts(title="设备报警柱形图", subtitle="报警次数"))
  1502 + )
  1503 + return c
  1504 +
  1505 +
  1506 +xaxis = []
  1507 +yaxis = []
  1508 +i = 10
  1509 +
  1510 +
  1511 +def get_xaxis():
  1512 + global xaxis
  1513 + if xaxis is None or len(xaxis) == 0:
  1514 + xaxis = ["{}".format(i) for i in range(10)]
  1515 + else:
  1516 + global i
  1517 + c = len(xaxis)
  1518 + del xaxis[c * -1]
  1519 +
  1520 + xaxis.append(i)
  1521 +
  1522 + i = i + 1
  1523 + print(xaxis)
  1524 + return xaxis
  1525 +
  1526 +
  1527 +def get_yaxis():
  1528 + global yaxis
  1529 + if yaxis is None or len(yaxis) == 0:
  1530 + yaxis = [randrange(50, 80) for _ in range(10)]
  1531 + else:
  1532 + i = len(xaxis)
  1533 + del yaxis[i * -1]
  1534 + yaxis.append(randrange(50, 80))
  1535 + print(yaxis)
  1536 + return yaxis
  1537 +
  1538 +
  1539 +def line_base() -> Line:
  1540 + line = (
  1541 + Line()
  1542 + .add_xaxis(get_xaxis())
  1543 + .add_yaxis(
  1544 + series_name="",
  1545 + y_axis=get_yaxis(),
  1546 + is_smooth=True,
  1547 + label_opts=opts.LabelOpts(is_show=False),
  1548 + )
  1549 + .set_global_opts(
  1550 + title_opts=opts.TitleOpts(title="动态数据"),
  1551 + xaxis_opts=opts.AxisOpts(type_="value", min_=xaxis[0]),
  1552 + yaxis_opts=opts.AxisOpts(type_="value"),
  1553 + )
  1554 + )
  1555 + return line
  1556 +
  1557 +
  1558 +@admin.route("/data/forms/")
  1559 +@admin_login
  1560 +def data_forms():
  1561 + return render_template("admin/line_charts.html", s_url="http://127.0.0.1:5000/")
  1562 +
  1563 +
  1564 +@admin.route("/lineChart")
  1565 +def get_line_chart():
  1566 + c = line_base()
  1567 + return c.dump_options_with_quotes()
  1568 +
  1569 +
  1570 +idx = 9
  1571 +
  1572 +
  1573 +@admin.route("/lineDynamicData")
  1574 +def update_line_data():
  1575 + # global idx
  1576 + # idx = idx + 1
  1577 + # return jsonify({"name": idx, "value": randrange(50, 80)})
  1578 + c = line_base()
  1579 + return c.dump_options_with_quotes()
  1580 +
  1581 +
  1582 +@admin.route("/barChart")
  1583 +def get_bar_chart():
  1584 + c = bar_base()
  1585 + return c.dump_options_with_quotes()
  1586 +
  1587 +
  1588 +def get_alarm_statistics(start_time, end_time):
  1589 + pd = Alarm.query.join(
  1590 + Device
  1591 + ).join(
  1592 + DeviceType
  1593 + ).filter(
  1594 + Alarm.device_id == Device.id
  1595 + ).filter(
  1596 + Device.devicetype_id == DeviceType.type
  1597 + ).filter(
  1598 + datetime.strptime(start_time, "%Y-%m-%d %H:%M") < Alarm.addtime
  1599 + ).filter(
  1600 + Alarm.addtime < datetime.strptime(end_time, "%Y-%m-%d %H:%M")
  1601 + ).order_by(
  1602 + Device.devicetype_id.asc()
  1603 + ).group_by(
  1604 + DeviceType.type
  1605 + ).all()
  1606 +
  1607 + return pd
  1608 +
  1609 +
  1610 +def alarm_bar_base(alarm, st, et) -> Bar:
  1611 + # devicetype = DeviceType.query.all()
  1612 +
  1613 + alarm_name = []
  1614 + alarm_count = []
  1615 + for a in alarm:
  1616 + alarm_name.append(a.device.devicetype.name)
  1617 + alarm_count.append(len(a.device.alarm))
  1618 +
  1619 + # for dt in devicetype:
  1620 + # if dt.name in alarm_name:
  1621 + # continue
  1622 + # else:
  1623 + # alarm_name.append(dt.name)
  1624 + # alarm_count.append(0)
  1625 +
  1626 + c = (
  1627 + Bar(
  1628 +
  1629 + )
  1630 + .add_xaxis(
  1631 + alarm_name
  1632 + )
  1633 + .add_yaxis(
  1634 + "报警", alarm_count,
  1635 + bar_max_width=40,
  1636 + bar_min_width=10,
  1637 + # color="indianred"
  1638 + background_style=["red"],
  1639 + )
  1640 + # .add_yaxis(
  1641 + # "预警", alarm_count,
  1642 + # bar_max_width=40,
  1643 + # bar_min_width=20,
  1644 + # )
  1645 + .reversal_axis()
  1646 + .set_series_opts(label_opts=opts.LabelOpts(position="right"))
  1647 +
  1648 + .set_global_opts(
  1649 + title_opts=opts.TitleOpts(title="设备报警柱形图", subtitle="报警次数",),
  1650 + legend_opts=opts.LegendOpts(
  1651 + orient="center", # 水平排布
  1652 + ),
  1653 +
  1654 + )
  1655 + )
  1656 + return c
  1657 +
  1658 +
  1659 +@admin.route("getAlarmStatisticsBar", methods=["GET"])
  1660 +def get_alarm_statistics_bar():
  1661 + st = request.args.get('startTime').replace("T", " ")
  1662 + et = request.args.get('endTime').replace("T", " ")
  1663 + alarm = get_alarm_statistics(st, et)
  1664 + c = alarm_bar_base(alarm, st, et)
  1665 + return c.dump_options_with_quotes()
  1666 +
  1667 +
  1668 +from pyecharts.faker import Faker
  1669 +
  1670 +
  1671 +def alarm_pie_base(alarm) -> Pie:
  1672 + alarm_name = []
  1673 + alarm_count = []
  1674 + for a in alarm:
  1675 + alarm_name.append(a.device.devicetype.name)
  1676 + alarm_count.append(len(a.device.alarm))
  1677 + c = (
  1678 + Pie()
  1679 + .add(
  1680 + "设备报警饼状图",
  1681 + [list(z) for z in zip(alarm_name, alarm_count)],
  1682 + radius=["40%", "75%"],
  1683 + )
  1684 + .set_global_opts(
  1685 + title_opts=opts.TitleOpts(title="设备报警饼状图", subtitle="报警次数"),
  1686 + #legend_opts=opts.LegendOpts(orient="vertical", pos_top="15%", pos_left="2%"),
  1687 + )
  1688 + .set_series_opts(label_opts=opts.LabelOpts(formatter="{b}: {c}"))
  1689 + )
  1690 +
  1691 + return c
  1692 +
  1693 +
  1694 +@admin.route("getAlarmStatisticsPie", methods=["GET"])
  1695 +def get_alarm_statistics_pie():
  1696 + st = request.args.get('startTime').replace("T", " ")
  1697 + et = request.args.get('endTime').replace("T", " ")
  1698 + alarm = get_alarm_statistics(st, et)
  1699 + c = alarm_pie_base(alarm)
  1700 + return c.dump_options_with_quotes()
  1701 +
  1702 +
  1703 +@admin.route("gaugeChart", methods=["GET"])
  1704 +def gauge_chart():
  1705 + from pyecharts.charts import Gauge
  1706 +
  1707 + c = (
  1708 + Gauge(init_opts=opts.InitOpts(width="150px", height="150px"))
  1709 + .add(
  1710 + "电流图",
  1711 + [("漏电流(mA)", 155.5),],
  1712 + max_=1000,
  1713 + split_number=5,
  1714 + axisline_opts=opts.AxisLineOpts(
  1715 + linestyle_opts=opts.LineStyleOpts(
  1716 + color=[(0.3, "#67e0e3"), (0.7, "#37a2da"), (1, "#fd666d")], width=30
  1717 + )
  1718 + ),
  1719 + detail_label_opts=opts.LabelOpts(formatter="{value}"),
  1720 +
  1721 + )
  1722 + .set_global_opts(
  1723 + title_opts=opts.TitleOpts(title=""),
  1724 + legend_opts=opts.LegendOpts(is_show=False),
  1725 + )
  1726 +
  1727 + )
  1728 +
  1729 + return c.dump_options_with_quotes()
  1730 +
  1731 +
  1732 +@admin.route("pieChartV", methods=["GET"])
  1733 +def get_pie_chartV():
  1734 + from pyecharts.charts import Pie
  1735 + from pyecharts.faker import Faker
  1736 +
  1737 + pcm = get_stack("PCM")
  1738 + UA = 0
  1739 + UB = 0
  1740 + UC = 0
  1741 + if len(pcm) > 0:
  1742 + UA = pcm[0].UA
  1743 + UB = pcm[0].UB
  1744 + UC = pcm[0].UC
  1745 + c = (
  1746 + Pie()
  1747 + .add(
  1748 + "电压",
  1749 + # [["A相", "B相", "C相"], [222.3, 220.9, 230.1]],
  1750 + [list(z) for z in zip(["A相", "B相", "C相"], [UA, UB, UC])],
  1751 + radius=["40%", "75%"],
  1752 + )
  1753 + .set_global_opts(
  1754 + title_opts=opts.TitleOpts(title="电压-V"),
  1755 + legend_opts=opts.LegendOpts(orient="vertical", pos_top="15%", pos_left="2%"),
  1756 + )
  1757 + .set_series_opts(label_opts=opts.LabelOpts(formatter="{b}{c}V"))
  1758 +
  1759 + )
  1760 + return c.dump_options_with_quotes()
  1761 +
  1762 +
  1763 +@admin.route("pieChartA", methods=["GET"])
  1764 +def get_pie_chartA():
  1765 + pcm = get_stack("PCM")
  1766 + IA = 0
  1767 + IB = 0
  1768 + IC = 0
  1769 + if len(pcm) > 0:
  1770 + IA = pcm[0].IA
  1771 + IB = pcm[0].IB
  1772 + IC = pcm[0].IC
  1773 + c = (
  1774 + Pie()
  1775 + .add(
  1776 + "电流",
  1777 + [list(z) for z in zip(["A相", "B相", "C相"], [IA, IB, IC])],
  1778 + radius=["40%", "75%"],
  1779 + )
  1780 + .set_global_opts(
  1781 + title_opts=opts.TitleOpts(title="电流-A"),
  1782 + legend_opts=opts.LegendOpts(orient="vertical", pos_top="15%", pos_left="2%"),
  1783 + )
  1784 + .set_series_opts(label_opts=opts.LabelOpts(formatter="{b}{c}A"))
  1785 +
  1786 + )
  1787 + return c.dump_options_with_quotes()
  1788 +
  1789 +
  1790 +@admin.route("liquidChart", methods=["GET"])
  1791 +def get_liquid_chart():
  1792 + from pyecharts.charts import Liquid
  1793 +
  1794 + c = (
  1795 + Liquid(init_opts=opts.InitOpts())
  1796 + .add("lq", [0.6, 0.7, 0.8], is_outline_show=True)
  1797 + .set_global_opts(title_opts=opts.TitleOpts(title="Liquid-无边框"))
  1798 +
  1799 + )
  1800 +
  1801 + return c.dump_options_with_quotes()
  1802 +
  1803 +
  1 +# _*_ Coding:utf-8 _*_
  2 +
  3 +from flask import Blueprint
  4 +
  5 +api = Blueprint("api", __name__)
  6 +
  7 +import app.api.views
  1 +
  2 +from dataclasses import asdict, dataclass
  3 +from json import dumps
  4 +
  5 +
  6 +@dataclass
  7 +class Response:
  8 + code: int
  9 + msg: str
  10 + success: bool
  11 +
  12 + @property
  13 + def __dict__(self):
  14 + return asdict(self)
  15 +
  16 + @property
  17 + def json(self):
  18 + return dumps(self.__dict__)
  19 +
  20 +
  21 +@dataclass
  22 +class DeviceDataHeader:
  23 + cmd: str
  24 + type: int
  25 + factory: str
  26 +
  27 + @property
  28 + def __dict__(self):
  29 + return asdict(self)
  30 +
  31 + @property
  32 + def json(self):
  33 + return dumps(self.__dict__)
  34 +
  35 +
  36 +@dataclass
  37 +class GasConcentration:
  38 + addr: int
  39 + type: int
  40 + value: float
  41 + unit: str
  42 + lowAlarmValue: float
  43 + highAlarmValue: float
  44 + range: float
  45 + type: int
  46 + name: str
  47 + lastUpdateTime: str
  48 +
  49 + @property
  50 + def __dict__(self):
  51 + return asdict(self)
  52 +
  53 + @property
  54 + def json(self):
  55 + return dumps(self.__dict__)
  56 +
  57 +
  58 +@dataclass
  59 +class GasConcentrationDeviceRequest(DeviceDataHeader):
  60 + data: GasConcentration
  61 +
  62 + @property
  63 + def __dict__(self):
  64 + return asdict(self)
  65 +
  66 + @property
  67 + def json(self):
  68 + return dumps(self.__dict__)
  69 +
  70 +
  71 +@dataclass
  72 +class VideoAnalysis:
  73 + addr: int
  74 + type: int
  75 + channelId: int
  76 + channelName: str
  77 + eventName: str
  78 + base64Image: str
  79 + lastUpdateTime: str
  80 +
  81 +
  82 +@dataclass
  83 +class VideoAnalysisRequest(DeviceDataHeader):
  84 + data: VideoAnalysis
  85 +
  86 + @property
  87 + def __dict__(self):
  88 + return asdict(self)
  89 +
  90 + @property
  91 + def json(self):
  92 + return dumps(self.__dict__)
  1 +
  2 +_max = 100
  3 +status_dict = {}
  4 +
  5 +
  6 +def set_stack(cmd, data):
  7 + global status_dict
  8 + if cmd in status_dict:
  9 + list = status_dict[cmd]
  10 + if len(list) >= _max:
  11 + list.pop(0)
  12 + list.append(data)
  13 + else:
  14 + list = [data]
  15 + status_dict[cmd] = list
  16 + # print(status_dict)
  17 +
  18 +
  19 +def get_stack(cmd=None):
  20 + s_list = []
  21 + if cmd is None:
  22 + if len(status_dict) > 0:
  23 + for key in status_dict:
  24 + list = status_dict[key]
  25 + dic = list[len(list)-1]
  26 + s_list.append(dic)
  27 + else:
  28 + if len(status_dict) > 0:
  29 + for key in status_dict:
  30 + if key == cmd:
  31 + list = status_dict[key]
  32 + dic = list[len(list)-1]
  33 + s_list.append(dic)
  34 + break
  35 + return s_list
  1 +from datetime import datetime
  2 +
  3 +from app.models import Device, Alarm
  4 +from app import db
  5 +import app.image as img
  6 +import os
  7 +from flask import current_app
  8 +
  9 +
  10 +def gas_concentration(app, data):
  11 + if data is not None:
  12 + device_type = data.get("type", "")
  13 + device_addr = data.get("addr", "")
  14 + with (app.app_context()):
  15 + device = Device.query.filter_by(address=device_addr).filter_by(devicetype_id=device_type)
  16 + if device.count() == 1:
  17 + v = data.get("value", 0)
  18 +
  19 + if v is not None and v > device[0].threshold and \
  20 + Alarm.query.filter_by(device_id=device[0].id).filter_by(
  21 + addtime=data["lastUpdateTime"]).count() <= 0:
  22 + alarm = Alarm(
  23 + msg="浓度高报警-浓度值:" + str(data["value"]),
  24 + device_id=device[0].id,
  25 + addtime=data["lastUpdateTime"],
  26 + preview=0,
  27 + handle=0,
  28 + )
  29 + db.session.add(db.session.merge(alarm))
  30 + db.session.commit()
  31 +
  32 +
  33 +def pressure(app, data):
  34 + if data is not None:
  35 + device_type = data.get("type", "")
  36 + device_addr = data.get("addr", "")
  37 + with (app.app_context()):
  38 + device = Device.query.filter_by(address=device_addr).filter_by(devicetype_id=device_type)
  39 + if device.count() == 1:
  40 + v = data.get("value", 0)
  41 +
  42 + if v is not None and v > device[0].threshold and \
  43 + Alarm.query.filter_by(device_id=device[0].id).filter_by(
  44 + addtime=data["lastUpdateTime"]).count() <= 0:
  45 + alarm = Alarm(
  46 + msg="压力高报警-压力值:" + str(data["value"]),
  47 + device_id=device[0].id,
  48 + addtime=data["lastUpdateTime"],
  49 + preview=0,
  50 + handle=0,
  51 + day=str(data["lastUpdateTime"]).split(' ')[0].split('-')[2],
  52 + month=str(data["lastUpdateTime"]).split(' ')[0].split('-')[1],
  53 + year=str(data["lastUpdateTime"]).split(' ')[0].split('-')[0],
  54 + date=str(data["lastUpdateTime"]).split(' ')[0],
  55 + )
  56 + db.session.add(db.session.merge(alarm))
  57 + db.session.commit()
  58 +
  59 +
  60 +def switch(app, data):
  61 + if data is not None:
  62 + device_type = data.get("type", "")
  63 + device_addr = data.get("addr", "")
  64 + with (app.app_context()):
  65 + device = Device.query.filter_by(address=device_addr).filter_by(devicetype_id=device_type)
  66 + if device.count() == 1:
  67 + v = data.get("value", 0)
  68 +
  69 + if v is not None and (v == 1 or v == -1) and \
  70 + Alarm.query.filter_by(device_id=device[0].id).filter_by(
  71 + addtime=data["lastUpdateTime"]).count() <= 0:
  72 + if v == 1:
  73 + msg = "急停按钮-打开:"
  74 + else:
  75 + msg = "急停按钮-离线:"
  76 + alarm = Alarm(
  77 + msg=msg,
  78 + device_id=device[0].id,
  79 + addtime=data["lastUpdateTime"],
  80 + preview=0,
  81 + handle=0,
  82 + day=str(data["lastUpdateTime"]).split(' ')[0].split('-')[2],
  83 + month=str(data["lastUpdateTime"]).split(' ')[0].split('-')[1],
  84 + year=str(data["lastUpdateTime"]).split(' ')[0].split('-')[0],
  85 + date=str(data["lastUpdateTime"]).split(' ')[0],
  86 + )
  87 + db.session.add(db.session.merge(alarm))
  88 + db.session.commit()
  89 +
  90 +
  91 +def video(app, data):
  92 + if data is not None:
  93 + device_type = data.get("type", "")
  94 + device_addr = data.get("addr", "")
  95 + # print(f'device_type={device_type}')
  96 + # print(f'device_addr={device_addr}')
  97 + with (app.app_context()):
  98 + device = Device.query.filter_by(address=device_addr).filter_by(devicetype_id=device_type)
  99 + if device.count() == 1:
  100 + if Alarm.query.filter_by(device_id=device[0].id).filter_by(
  101 + addtime=data["lastUpdateTime"]).count() <= 0:
  102 + if not os.path.exists(current_app.config["AL_DIR"]):
  103 + # 创建一个多级目录
  104 + os.makedirs(current_app.config["AL_DIR"]) # 创建文件夹
  105 + os.chmod(current_app.config["AL_DIR"], "rw") # 设置权限
  106 +
  107 + file = str(device_type) + datetime.now().__format__('%Y%m%d%H%M%S%f') + '.png'
  108 + path = current_app.config["AL_DIR"] + file
  109 + img.save_image(img.base64_to_image(data["base64Picture"]), path)
  110 + alarm = Alarm(
  111 + msg=data["eventName"],
  112 + img=file,
  113 + device_id=device[0].id,
  114 + addtime=data["lastUpdateTime"],
  115 + preview=1,
  116 + handle=0,
  117 + day=str(data["lastUpdateTime"]).split(' ')[0].split('-')[2],
  118 + month=str(data["lastUpdateTime"]).split(' ')[0].split('-')[1],
  119 + year=str(data["lastUpdateTime"]).split(' ')[0].split('-')[0],
  120 + date=str(data["lastUpdateTime"]).split(' ')[0],
  121 + )
  122 + db.session.add(db.session.merge(alarm))
  123 + db.session.commit()
  124 +
  125 +
  126 +def radar(app, data):
  127 + if data is not None:
  128 + device_type = data.get("type", "")
  129 + device_addr = data.get("addr", "")
  130 + with (app.app_context()):
  131 + device = Device.query.filter_by(address=device_addr).filter_by(devicetype_id=device_type)
  132 + if device.count() == 1:
  133 + if Alarm.query.filter_by(device_id=device[0].id).filter_by(
  134 + addtime=data["lastUpdateTime"]).count() <= 0:
  135 +
  136 + alarm = Alarm(
  137 + msg=data["eventName"],
  138 + device_id=device[0].id,
  139 + addtime=data["lastUpdateTime"],
  140 + preview=0,
  141 + handle=0,
  142 + day=str(data["lastUpdateTime"]).split(' ')[0].split('-')[2],
  143 + month=str(data["lastUpdateTime"]).split(' ')[0].split('-')[1],
  144 + year=str(data["lastUpdateTime"]).split(' ')[0].split('-')[0],
  145 + date=str(data["lastUpdateTime"]).split(' ')[0],
  146 + )
  147 + db.session.add(db.session.merge(alarm))
  148 + db.session.commit()
  149 +
  150 +
  151 +def pcm(app, data):
  152 + if data is not None:
  153 + device_type = data.get("type", "")
  154 + device_addr = data.get("addr", "")
  155 + with (app.app_context()):
  156 + device = Device.query.filter_by(address=device_addr).filter_by(devicetype_id=device_type)
  157 + if device.count() == 1:
  158 + if Alarm.query.filter_by(device_id=device[0].id).filter_by(
  159 + addtime=data["lastUpdateTime"]).count() <= 0:
  160 +
  161 + msg = None
  162 + IH = data.get("IH", 0)
  163 + IA = data.get("IA", 0)
  164 + IB = data.get("IB", 0)
  165 + IC = data.get("IC", 0)
  166 +
  167 + ILH0 = data.get('ILH0', 0)
  168 + IL = data.get('IL', 0)
  169 + if IH < IA:
  170 + msg = f"A相电流{IA}A大于电流上限{IH}A;"
  171 + if IH < IB:
  172 + msg = f"B相电流{IB}A大于电流上限{IH}A;"
  173 + if IH < IC:
  174 + msg = f"C相电流{IB}A大于电流上限{IH}A;"
  175 + if ILH0 < IL:
  176 + msg = f"漏电流{IL}mA大于漏电流上限{ILH0}mA;"
  177 +
  178 + if msg is not None:
  179 + alarm = Alarm(
  180 + msg=msg,
  181 + device_id=device[0].id,
  182 + addtime=data["lastUpdateTime"],
  183 + preview=0,
  184 + handle=0,
  185 + day=str(data["lastUpdateTime"]).split(' ')[0].split('-')[2],
  186 + month=str(data["lastUpdateTime"]).split(' ')[0].split('-')[1],
  187 + year=str(data["lastUpdateTime"]).split(' ')[0].split('-')[0],
  188 + date=str(data["lastUpdateTime"]).split(' ')[0],
  189 + )
  190 + db.session.add(db.session.merge(alarm))
  191 + db.session.commit()
  1 +import json
  2 +import threading
  3 +import app.api.thread_func as thread_func
  4 +import app.api.stack_func as stack_func
  5 +from . import api
  6 +from flask import request, jsonify
  7 +from app.config import HttpRequestMethod
  8 +from .response import Response, DeviceDataHeader
  9 +from flask import current_app
  10 +
  11 +
  12 +@api.route('/admin/add', methods=["GET", "POST"])
  13 +def add():
  14 + if request.method == "POST":
  15 + a = request.json.get("a")
  16 + b = request.json.get("b")
  17 +
  18 + resp = Response(code=200, msg='ok', success=True)
  19 + print(resp.json)
  20 +
  21 + return jsonify({"code": 200, "data": float(a)+float(b)})
  22 + else:
  23 + a = request.args.get("a")
  24 + b = request.args.get("b")
  25 + return jsonify({"code": 200, "data": float(a)+float(b)})
  26 +
  27 +
  28 +@api.route('/admin/push_data', methods=['GET', 'POST'])
  29 +def push_data():
  30 + if request.method == HttpRequestMethod.GET:
  31 + pass
  32 + elif request.method == HttpRequestMethod.POST:
  33 + json_data = request.json
  34 + _type = json_data["type"]
  35 + _cmd = json_data["cmd"]
  36 + if _type == "OGC": # 油气浓度
  37 + if _cmd == "oil_gas_concentration_value": # 实时油气浓度值上传
  38 + stack_func.set_stack(_type, json_data)
  39 + # print(stack_func.get_stack())
  40 + thread = threading.Thread(target=thread_func.gas_concentration, args=(current_app._get_current_object(), json_data["data"]))
  41 + thread.start()
  42 + elif _type == "PS": # 压力
  43 + if _cmd == "pressure_value": # 压力
  44 + stack_func.set_stack(_type, json_data)
  45 + # print(stack_func.get_stack())
  46 + thread = threading.Thread(target=thread_func.pressure,
  47 + args=(current_app._get_current_object(), json_data["data"]))
  48 + thread.start()
  49 + elif _type == "ScramButton": # 急停按钮
  50 + if _cmd == "switch_value": # 开关值
  51 + stack_func.set_stack(_type, json_data)
  52 + # print(stack_func.get_stack())
  53 + thread = threading.Thread(target=thread_func.switch,
  54 + args=(current_app._get_current_object(), json_data["data"]))
  55 + thread.start()
  56 + elif _type == "Video": # 视频分析
  57 + if _cmd == "video_alarm_data": # 事件信息
  58 + stack_func.set_stack(_type, json_data)
  59 + # print(stack_func.get_stack())
  60 + thread = threading.Thread(target=thread_func.video,
  61 + args=(current_app._get_current_object(), json_data["data"]))
  62 + thread.start()
  63 + elif _type == "Radar": # 雷达
  64 + if _cmd == "target_info_param": # 事件信息
  65 + stack_func.set_stack(_type, json_data)
  66 + # print(stack_func.get_stack())
  67 + thread = threading.Thread(target=thread_func.radar,
  68 + args=(current_app._get_current_object(), json_data["data"]))
  69 + thread.start()
  70 + elif _type == "PCM": # 用电管理
  71 + if _cmd == "power_consumption_param": # 事件信息
  72 + stack_func.set_stack(_type, json_data)
  73 + # print(stack_func.get_stack())
  74 + thread = threading.Thread(target=thread_func.pcm,
  75 + args=(current_app._get_current_object(), json_data["data"]))
  76 + thread.start()
  77 +
  78 + else:
  79 + pass
  80 + return Response(code=200, msg='ok', success=True).json
  1 +
  2 +class HttpRequestMethod:
  3 + GET = "GET"
  4 + POST = "POST"
  5 + PUT = "PUT"
  6 + PATCH = "PATCH"
  7 + DELETE = "DELETE"
  8 + HEAD = "HEAD"
  9 + OPTIONS = "OPTIONS"
  10 +
  11 +
  12 +class DeviceType:
  13 + ThreePhaseSmartEnergyMeter = 1001
  14 + SmartWaterMeter = 2001
  15 + ElectricityConsumptionManagement = 3001
  16 + GasConcentration = 4001
  17 + Pressure = 5001
  18 + EmergencySwitch = 6001
  19 + Video = 7001
  20 + Radar = 8001
  1 +import base64
  2 +from PIL import Image
  3 +import io
  4 +
  5 +
  6 +def base64_to_image(base64_str):
  7 + return base64.b64decode(base64_str)
  8 +
  9 +
  10 +def save_image(img_data, path):
  11 + img = Image.open(io.BytesIO(img_data))
  12 + img.save(path)
  1 +from . import db
  2 +from datetime import datetime
  3 +
  4 +
  5 +# 角色
  6 +class Role(db.Model):
  7 + __tablename__ = "role"
  8 + id = db.Column(db.Integer, primary_key=True)
  9 + name = db.Column(db.String(100)) # 名称
  10 + addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 新增时间
  11 +
  12 + admin = db.relationship("Admin", backref='role') # 外键关系关联
  13 +
  14 +
  15 +# 权限
  16 +class Power(db.Model):
  17 + __tablename__ = "power"
  18 + id = db.Column(db.Integer, primary_key=True)
  19 + name = db.Column(db.String(100)) # 名称
  20 + addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 新增时间
  21 +
  22 + admin = db.relationship("Admin", backref='power') # 外键关系关联
  23 +
  24 +
  25 +# 会员数据模型
  26 +class User(db.Model):
  27 + __tablename__ = "user"
  28 + #__table_args__ = {"useexisting": True}
  29 + id = db.Column(db.Integer, primary_key=True) # 编号
  30 + username = db.Column(db.String(100)) # 用户名
  31 + pwd = db.Column(db.String(1024)) # 密码
  32 + email = db.Column(db.String(100)) # 邮箱
  33 + phone = db.Column(db.String(11)) # 手机号
  34 + info = db.Column(db.Text) # 个性简介
  35 + face = db.Column(db.String(255), unique=True) # 头像
  36 + addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 注册时间
  37 +
  38 + userlogs = db.relationship('Userlog', backref='user') # 会员日志外键关系关联
  39 + collect = db.relationship('Collect', backref='user') # 收藏外键关系关联
  40 +
  41 + role_id = db.Column(db.Integer, db.ForeignKey('role.id')) # 所属标签
  42 + power_id = db.Column(db.Integer, db.ForeignKey('power.id')) # 所属标签
  43 +
  44 + def __repr__(self):
  45 + return '<User %r>' % self.name
  46 +
  47 + def check_pwd(self, pwd):
  48 + """
  49 + 检测密码是否正确
  50 + :param pwd: 密码
  51 + :return: 返回布尔值
  52 + """
  53 + from werkzeug.security import check_password_hash
  54 + return check_password_hash(self.pwd, pwd)
  55 +
  56 +
  57 +# 管理员
  58 +class Admin(db.Model):
  59 + __tablename__ = "admin"
  60 + #__table_args__ = {"useexisting": True}
  61 + id = db.Column(db.Integer, primary_key=True) # 编号
  62 + name = db.Column(db.String(100), unique=True) # 管理员账号
  63 + pwd = db.Column(db.String(100)) # 管理员密码
  64 + is_super = db.Column(db.Integer)
  65 + addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 时间
  66 +
  67 + role_id = db.Column(db.Integer, db.ForeignKey('role.id')) # 所属标签
  68 + power_id = db.Column(db.Integer, db.ForeignKey('power.id')) # 所属标签
  69 + adminlogs = db.relationship("Adminlog", backref='admin') # 管理员登录日志外键关系关联
  70 + oplogs = db.relationship("Oplog", backref='admin') # 管理员操作日志外键关系关联
  71 +
  72 + # handle_user = db.Column(db.Integer, db.ForeignKey('alarm.handle_user')) # 所属标签
  73 + alarm = db.relationship("Alarm", backref='admin') # 外键关系关联
  74 +
  75 + def __repr__(self):
  76 + return "<Admin %r>" % self.name
  77 +
  78 + def check_pwd(self, pwd):
  79 + """
  80 + 检测密码是否正确
  81 + :param pwd: 密码
  82 + :return: 返回布尔值
  83 + """
  84 + from werkzeug.security import check_password_hash
  85 + return check_password_hash(self.pwd, pwd)
  86 +
  87 +
  88 +# 管理员登录日志
  89 +class Adminlog(db.Model):
  90 + __tablename__ = "adminlog"
  91 + #__table_args__ = {"useexisting": True}
  92 + id = db.Column(db.Integer, primary_key=True) # 编号
  93 + admin_id = db.Column(db.Integer, db.ForeignKey('admin.id')) # 所属管理员
  94 + ip = db.Column(db.String(100)) # 登录IP
  95 + addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 登录时间
  96 +
  97 + def __repr__(self):
  98 + return "<Adminlog %r>" % self.id
  99 +
  100 +
  101 +# 操作日志
  102 +class Oplog(db.Model):
  103 + __tablename__ = "oplog"
  104 + #__table_args__ = {"useexisting": True}
  105 + id = db.Column(db.Integer, primary_key=True) # 编号
  106 + admin_id = db.Column(db.Integer, db.ForeignKey('admin.id')) # 所属管理员
  107 + ip = db.Column(db.String(100)) # 操作IP
  108 + reason = db.Column(db.String(600)) # 操作原因
  109 + addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 登录时间
  110 +
  111 + def __repr__(self):
  112 + return "<Oplog %r>" % self.id
  113 +
  114 +
  115 +# 地区
  116 +class Area(db.Model):
  117 + __tablename__ = "area"
  118 + id = db.Column(db.Integer, primary_key=True) # 编号
  119 + name = db.Column(db.String(100), unique=True) # 标题
  120 + addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 添加景区时间
  121 + is_recommended = db.Column(db.Boolean(), default=0) # 是否推荐
  122 + introduction = db.Column(db.Text) # 景区简介
  123 + scenic = db.relationship("Scenic", backref='area') # 外键关系关联
  124 +
  125 + def __repr__(self):
  126 + return "<Area %r>" % self.name
  127 +
  128 +
  129 +# 检测点
  130 +class DetectingPoint(db.Model):
  131 + __tablename__ = "detectingpoint"
  132 + id = db.Column(db.Integer, primary_key=True) # id
  133 + no = db.Column(db.Integer) # 编号
  134 + name = db.Column(db.String(255)) # 名称
  135 + addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 添加时间
  136 + detectingpoint = db.relationship("Device", backref='detectingpoint') # 外键关系关联
  137 +
  138 + def __repr__(self):
  139 + return "<DetectingPoint %r>" % self.name
  140 +
  141 +
  142 +# 会员登录日志
  143 +class Userlog(db.Model):
  144 + __tablename__ = "userlog"
  145 + #__table_args__ = {"useexisting": True}
  146 + id = db.Column(db.Integer, primary_key=True) # 编号
  147 + # 设置外键
  148 + user_id = db.Column(db.Integer, db.ForeignKey('user.id')) # 所属会员
  149 + ip = db.Column(db.String(100)) # ip地址
  150 + addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 登录时间
  151 +
  152 + def __repr__(self):
  153 + return '<User %r>' % self.id
  154 +
  155 +
  156 +# 景区
  157 +class Scenic(db.Model):
  158 + __tablename__ = "scenic"
  159 + id = db.Column(db.Integer, primary_key=True) # 编号
  160 + title = db.Column(db.String(255), unique=True) # 标题
  161 + star = db.Column(db.Integer) # 星级
  162 + logo = db.Column(db.String(255), unique=True) # 封面
  163 + introduction = db.Column(db.Text) # 景区简介
  164 + content = db.Column(db.Text) # 景区内容描述
  165 + address = db.Column(db.Text) # 景区地址
  166 + is_hot = db.Column(db.Boolean(), default=0) # 是否热门
  167 + is_recommended = db.Column(db.Boolean(), default=0) # 是否推荐
  168 +
  169 + # 设置外键
  170 + area_id = db.Column(db.Integer, db.ForeignKey('area.id')) # 所属标签
  171 + addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 添加时间
  172 + collect = db.relationship("Collect", backref='scenic') # 收藏外键关系关联
  173 + travels = db.relationship("Travels", backref='scenic') # 游记外键关系关联
  174 +
  175 + def __repr__(self):
  176 + return "<Scenic %r>" % self.title
  177 +
  178 +
  179 +# 设备类型
  180 +class DeviceType(db.Model):
  181 + __tablename__ = "devicetype"
  182 + type = db.Column(db.Integer, primary_key=True) # 类型
  183 + name = db.Column(db.String(255), unique=True) # 名称
  184 + addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 添加时间
  185 +
  186 + device = db.relationship("Device", backref='devicetype') # 外键关系关联
  187 +
  188 + def __repr__(self):
  189 + return "<DeviceType %r>" % self.name
  190 +
  191 +
  192 +# 设备类型
  193 +class Notification(db.Model):
  194 + __tablename__ = "notification"
  195 + id = db.Column(db.Integer, primary_key=True) # id
  196 + type = db.Column(db.String(255)) # 类型
  197 + enable = db.Column(db.Integer) # 启用标志
  198 + account = db.Column(db.String(255), unique=True) # 名称
  199 + addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 添加时间
  200 +
  201 + def __repr__(self):
  202 + return "<Notification %r>" % self.account
  203 +
  204 +
  205 +# 设备
  206 +class Device(db.Model):
  207 + __tablename__ = "device"
  208 + id = db.Column(db.Integer, primary_key=True) # 编号
  209 + title = db.Column(db.String(255), unique=False) # 标题
  210 + logo = db.Column(db.String(255), unique=True) # 封面
  211 + introduction = db.Column(db.Text) # 简介
  212 +
  213 + address = db.Column(db.String(255)) # 地址
  214 + threshold = db.Column(db.DECIMAL) # 阈值
  215 +
  216 + alarm = db.relationship("Alarm", backref='device') # 外键关系关联
  217 +
  218 + # 设置外键
  219 + devicetype_id = db.Column(db.Integer, db.ForeignKey('devicetype.type')) # 所属标签
  220 + detectingpoint_id = db.Column(db.Integer, db.ForeignKey('detectingpoint.id')) # 所属标签
  221 + addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 添加时间
  222 +
  223 +
  224 + def __repr__(self):
  225 + return "<Device %r>" % self.title
  226 +
  227 +
  228 +# 报警
  229 +class Alarm(db.Model):
  230 + __tablename__ = "alarm"
  231 + id = db.Column(db.Integer, primary_key=True) # 编号
  232 + msg = db.Column(db.String(255)) # 信息
  233 + img = db.Column(db.String(255)) # 图片
  234 + #handle_user = db.Column(db.String(255)) # 处理人
  235 + handle = db.Column(db.Integer) # 处理
  236 + preview = db.Column(db.Integer) # 预览
  237 + remark = db.Column(db.String(255), unique=True) # 备注
  238 +
  239 + day = db.Column(db.Integer)
  240 + month = db.Column(db.Integer)
  241 + year = db.Column(db.Integer)
  242 +
  243 + date = db.Column(db.Date)
  244 +
  245 + # 设置外键
  246 + device_id = db.Column(db.Integer, db.ForeignKey('device.id')) # 所属标签
  247 + handle_user = db.Column(db.Integer, db.ForeignKey('admin.name')) # 所属标签
  248 +
  249 + addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 添加时间
  250 + updatetime = db.Column(db.DateTime, index=True, default=datetime.now) # 添加时间
  251 +
  252 + def __repr__(self):
  253 + return "<Alarm %r>" % self.id
  254 +
  255 +
  256 +# 游记
  257 +class Travels(db.Model):
  258 + __tablename__ = "travels"
  259 + id = db.Column(db.Integer, primary_key=True) # 编号
  260 + title = db.Column(db.String(255), unique=True) # 标题
  261 + author = db.Column(db.String(255)) # 作者
  262 + content = db.Column(db.Text) # 游记内容
  263 + scenic_id = db.Column(db.Integer, db.ForeignKey('scenic.id')) # 所属景区
  264 + addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 添加时间
  265 +
  266 +
  267 +# 景区收藏
  268 +class Collect(db.Model):
  269 + __tablename__ = "collect"
  270 + #__table_args__ = {"useexisting": True}
  271 + id = db.Column(db.Integer, primary_key=True) # 编号
  272 +
  273 + scenic_id = db.Column(db.Integer, db.ForeignKey('scenic.id')) # 所属景区
  274 + user_id = db.Column(db.Integer, db.ForeignKey('user.id')) # 所属用户
  275 + addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 添加时间
  276 +
  277 + def __repr__(self):
  278 + return "<Collect %r>" % self.id
  279 +
  280 +
  281 +# 意见建议
  282 +class Suggestion(db.Model):
  283 + __tablename__ = "suggestion"
  284 + #__table_args__ = {"useexisting": True}
  285 + id = db.Column(db.Integer, primary_key=True) # 编号
  286 + name = db.Column(db.String(255)) # 昵称
  287 + email = db.Column(db.String(100)) # 邮箱
  288 + content = db.Column(db.Text) # 意见内容
  289 + addtime = db.Column(db.DateTime, index=True, default=datetime.now) # 注册时间
  290 +
  291 + def __repr__(self):
  292 + return "<Suggestion %r>" % self.id
  1 +html, body {
  2 +overflow: hidden;
  3 +background: #000;
  4 +padding: 0px; margin: 0px;
  5 +width: 100%; height: 100%;
  6 +}
  7 +img {
  8 +max-width: 100%;
  9 +vertical-align: middle;
  10 +border: 0;
  11 +-ms-interpolation-mode: bicubic;
  12 +}
  13 +a { color: white; text-decoration: none; border-bottom: none; }
  14 +a:hover { color: white; text-decoration: none; }
  15 +h1 {
  16 +background: -webkit-linear-gradient(#5f5287, #8b7cb9);
  17 +font-family: "proxima nova","Roboto","helvetica",Arial,sans-serif;
  18 +-webkit-background-clip: text;
  19 +-webkit-text-fill-color: transparent;
  20 +font-size: 34px;
  21 +font-weight: bold;
  22 +letter-spacing: -2px;
  23 +line-height: 50px;
  24 +}
  25 +h2 {
  26 +color: white;
  27 +font-family: "proxima nova","Roboto","helvetica",Arial,sans-serif;
  28 +font-size: 24px;
  29 +font-weight: normal;
  30 +.opacity(50);
  31 +}
  32 +h1 a,h2 a{color:#505358}
  33 +.fullScreen {
  34 +height: 100%;
  35 +}
  36 +a.logo {
  37 +position: absolute;
  38 +bottom: 50px;
  39 +right: 50px;
  40 +width: 250px;
  41 +text-decoration: none;
  42 +border-bottom: none;
  43 +}
  44 +img.rotating {
  45 +position: absolute;
  46 +left: 50%;
  47 +top: 50%;
  48 +margin-left: -256px;
  49 +margin-top: -256px;
  50 +
  51 +-webkit-transition: opacity 2s ease-in;
  52 +-moz-transition: opacity 2s ease-in;
  53 +-o-transition: opacity 2s ease-in;
  54 +-ms-transition: opacity 2s ease-in;
  55 +transition: opacity 2s ease-in;
  56 +}
  57 +
  58 +@-webkit-keyframes rotating {
  59 +from{
  60 +-webkit-transform: rotate(0deg);
  61 +}
  62 +to{
  63 +-webkit-transform: rotate(360deg);
  64 +}
  65 +}
  66 +
  67 +@-moz-keyframes rotating {
  68 +from{
  69 +-moz-transform: rotate(0deg);
  70 +}
  71 +to{
  72 +-moz-transform: rotate(360deg);
  73 +}
  74 +}
  75 +
  76 +@-o-keyframes rotating {
  77 +from{
  78 +-o-transform: rotate(0deg);
  79 +}
  80 +to{
  81 +-o-transform: rotate(360deg);
  82 +}
  83 +}
  84 +
  85 +@-ms-keyframes rotating {
  86 +from{
  87 +-ms-transform: rotate(0deg);
  88 +}
  89 +to{
  90 +-ms-transform: rotate(360deg);
  91 +}
  92 +}
  93 +
  94 +.rotating {
  95 +-webkit-animation: rotating 120s linear infinite;
  96 +-moz-animation: rotating 120s linear infinite;
  97 +}
  98 +
  99 +div.pagenotfound-text {
  100 +position: absolute;
  101 +bottom: 50px;
  102 +left: 50px;
  103 +}
  1 + /**
  2 + * The stars in our starfield!
  3 + * Stars coordinate system is relative to the CENTER of the canvas
  4 + * @param {number} x
  5 + * @param {number} y
  6 + */
  7 + var Star = function(x, y, maxSpeed) {
  8 + this.x = x;
  9 + this.y = y;
  10 + this.slope = y / x; // This only works because our origin is always (0,0)
  11 + this.opacity = 0;
  12 + this.speed = Math.max(Math.random() * maxSpeed, 1);
  13 + };
  14 +
  15 + /**
  16 + * Compute the distance of this star relative to any other point in space.
  17 + *
  18 + * @param {int} originX
  19 + * @param {int} originY
  20 + *
  21 + * @return {float} The distance of this star to the given origin
  22 + */
  23 + Star.prototype.distanceTo = function(originX, originY) {
  24 + return Math.sqrt(Math.pow(originX - this.x, 2) + Math.pow(originY - this.y, 2));
  25 + };
  26 +
  27 + /**
  28 + * Reinitializes this star's attributes, without re-creating it
  29 + *
  30 + * @param {number} x
  31 + * @param {number} y
  32 + *
  33 + * @return {Star} this star
  34 + */
  35 + Star.prototype.resetPosition = function(x, y, maxSpeed) {
  36 + Star.apply(this, arguments);
  37 + return this;
  38 + };
  39 +
  40 + /**
  41 + * The BigBang factory creates stars (Should be called StarFactory, but that is
  42 + * a WAY LESS COOL NAME!
  43 + * @type {Object}
  44 + */
  45 + var BigBang = {
  46 + /**
  47 + * Returns a random star within a region of the space.
  48 + *
  49 + * @param {number} minX minimum X coordinate of the region
  50 + * @param {number} minY minimum Y coordinate of the region
  51 + * @param {number} maxX maximum X coordinate of the region
  52 + * @param {number} maxY maximum Y coordinate of the region
  53 + *
  54 + * @return {Star} The random star
  55 + */
  56 + getRandomStar: function(minX, minY, maxX, maxY, maxSpeed) {
  57 + var coords = BigBang.getRandomPosition(minX, minY, maxX, maxY);
  58 + return new Star(coords.x, coords.y, maxSpeed);
  59 + },
  60 +
  61 + /**
  62 + * Gets a random (x,y) position within a bounding box
  63 + *
  64 + *
  65 + * @param {number} minX minimum X coordinate of the region
  66 + * @param {number} minY minimum Y coordinate of the region
  67 + * @param {number} maxX maximum X coordinate of the region
  68 + * @param {number} maxY maximum Y coordinate of the region
  69 + *
  70 + * @return {Object} An object with random {x, y} positions
  71 + */
  72 + getRandomPosition: function(minX, minY, maxX, maxY) {
  73 + return {
  74 + x: Math.floor((Math.random() * maxX) + minX),
  75 + y: Math.floor((Math.random() * maxY) + minY)
  76 + };
  77 + }
  78 + };
  79 +
  80 + /**
  81 + * Constructor function of our starfield. This just prepares the DOM nodes where
  82 + * the scene will be rendered.
  83 + *
  84 + * @param {string} canvasId The DOM Id of the <div> containing a <canvas> tag
  85 + */
  86 + var StarField = function(containerId) {
  87 + this.container = document.getElementById(containerId);
  88 + this.canvasElem = this.container.getElementsByTagName('canvas')[0];
  89 + this.canvas = this.canvasElem.getContext('2d');
  90 +
  91 + this.width = this.container.offsetWidth;
  92 + this.height = this.container.offsetHeight;
  93 +
  94 + this.starField = [];
  95 + };
  96 +
  97 + /**
  98 + * Updates the properties for every star for the next frame to be rendered
  99 + */
  100 + StarField.prototype._updateStarField = function() {
  101 + var i,
  102 + star,
  103 + randomLoc,
  104 + increment;
  105 +
  106 + for (i = 0; i < this.numStars; i++) {
  107 + star = this.starField[i];
  108 +
  109 + increment = Math.min(star.speed, Math.abs(star.speed / star.slope));
  110 + star.x += (star.x > 0) ? increment : -increment;
  111 + star.y = star.slope * star.x;
  112 +
  113 + star.opacity += star.speed / 100;
  114 +
  115 + // Recycle star obj if it goes out of the frame
  116 + if ((Math.abs(star.x) > this.width / 2) ||
  117 + (Math.abs(star.y) > this.height / 2)) {
  118 + //randomLoc = BigBang.getRandomPosition(
  119 + // -this.width / 2, -this.height / 2,
  120 + // this.width, this.height
  121 + //);
  122 + randomLoc = BigBang.getRandomPosition(
  123 + -this.width / 10, -this.height / 10,
  124 + this.width / 5, this.height / 5
  125 + );
  126 + star.resetPosition(randomLoc.x, randomLoc.y, this.maxStarSpeed);
  127 + }
  128 + }
  129 + };
  130 +
  131 + /**
  132 + * Renders the whole starfield (background + stars)
  133 + * This method could be made more efficient by just blipping each star,
  134 + * and not redrawing the whole frame
  135 + */
  136 + StarField.prototype._renderStarField = function() {
  137 + var i,
  138 + star;
  139 + // Background
  140 + this.canvas.fillStyle = "rgba(0, 0, 0, .5)";
  141 + this.canvas.fillRect(0, 0, this.width, this.height);
  142 + // Stars
  143 + for (i = 0; i < this.numStars; i++) {
  144 + star = this.starField[i];
  145 + this.canvas.fillStyle = "rgba(188, 213, 236, " + star.opacity + ")";
  146 + this.canvas.fillRect(
  147 + star.x + this.width / 2,
  148 + star.y + this.height / 2,
  149 + 2, 2);
  150 + }
  151 + };
  152 +
  153 + /**
  154 + * Function that handles the animation of each frame. Update the starfield
  155 + * positions and re-render
  156 + */
  157 + StarField.prototype._renderFrame = function(elapsedTime) {
  158 + var timeSinceLastFrame = elapsedTime - (this.prevFrameTime || 0);
  159 +
  160 + window.requestAnimationFrame(this._renderFrame.bind(this));
  161 +
  162 + // Skip frames unless at least 30ms have passed since the last one
  163 + // (Cap to ~30fps)
  164 + if (timeSinceLastFrame >= 30 || !this.prevFrameTime) {
  165 + this.prevFrameTime = elapsedTime;
  166 + this._updateStarField();
  167 + this._renderStarField();
  168 + }
  169 + };
  170 +
  171 + /**
  172 + * Makes sure that the canvas size fits the size of its container
  173 + */
  174 + StarField.prototype._adjustCanvasSize = function(width, height) {
  175 + // Set the canvas size to match the container ID (and cache values)
  176 + this.width = this.canvasElem.width = width || this.container.offsetWidth;
  177 + this.height = this.canvasElem.height = height || this.container.offsetHeight;
  178 + };
  179 +
  180 + /**
  181 + * This listener compares the old container size with the new one, and caches
  182 + * the new values.
  183 + */
  184 + StarField.prototype._watchCanvasSize = function(elapsedTime) {
  185 + var timeSinceLastCheck = elapsedTime - (this.prevCheckTime || 0),
  186 + width,
  187 + height;
  188 +
  189 + window.requestAnimationFrame(this._watchCanvasSize.bind(this));
  190 +
  191 + // Skip frames unless at least 333ms have passed since the last check
  192 + // (Cap to ~3fps)
  193 + if (timeSinceLastCheck >= 333 || !this.prevCheckTime) {
  194 + this.prevCheckTime = elapsedTime;
  195 + width = this.container.offsetWidth;
  196 + height = this.container.offsetHeight;
  197 + if (this.oldWidth !== width || this.oldHeight !== height) {
  198 + this.oldWidth = width;
  199 + this.oldHeight = height;
  200 + this._adjustCanvasSize(width, height);
  201 + }
  202 + }
  203 + };
  204 +
  205 + /**
  206 + * Initializes the scene by resizing the canvas to the appropiate value, and
  207 + * sets up the main loop.
  208 + * @param {int} numStars Number of stars in our starfield
  209 + */
  210 + StarField.prototype._initScene = function(numStars) {
  211 + var i;
  212 + for (i = 0; i < this.numStars; i++) {
  213 + this.starField.push(
  214 + BigBang.getRandomStar(-this.width / 2, -this.height / 2, this.width, this.height, this.maxStarSpeed)
  215 + );
  216 + }
  217 +
  218 + // Intervals not stored because I don't plan to detach them later...
  219 + window.requestAnimationFrame(this._renderFrame.bind(this));
  220 + window.requestAnimationFrame(this._watchCanvasSize.bind(this));
  221 + };
  222 +
  223 + /**
  224 + * Kicks off everything!
  225 + * @param {int} numStars The number of stars to render
  226 + * @param {int} maxStarSpeed Maximum speed of the stars (pixels / frame)
  227 + */
  228 + StarField.prototype.render = function(numStars, maxStarSpeed) {
  229 + this.numStars = numStars || 100;
  230 + this.maxStarSpeed = maxStarSpeed || 3;
  231 +
  232 + this._initScene(this.numStars);
  233 + };
  234 +
  235 + /**
  236 + * requestAnimationFrame shim layer with setTimeout fallback
  237 + * @see http://paulirish.com/2011/requestanimationframe-for-smart-animating
  238 + */
  239 + (function() {
  240 + var lastTime = 0;
  241 + var vendors = ['ms', 'moz', 'webkit', 'o'];
  242 + for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
  243 + window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
  244 + window.cancelAnimationFrame =
  245 + window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame'];
  246 + }
  247 +
  248 + if (!window.requestAnimationFrame)
  249 + window.requestAnimationFrame = function(callback, element) {
  250 + var currTime = new Date().getTime();
  251 + var timeToCall = Math.max(0, 16 - (currTime - lastTime));
  252 + var id = window.setTimeout(function() { callback(currTime + timeToCall); },
  253 + timeToCall);
  254 + lastTime = currTime + timeToCall;
  255 + return id;
  256 + };
  257 +
  258 + if (!window.cancelAnimationFrame)
  259 + window.cancelAnimationFrame = function(id) {
  260 + clearTimeout(id);
  261 + };
  262 + }());
  263 +
  264 + // Kick off!
  265 + var starField = new StarField('fullScreen').render(333, 3);
  1 +<?xml version="1.0" standalone="no"?>
  2 +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
  3 +<svg width="512px" height="512px" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.41421;">
  4 + <g>
  5 + <g>
  6 + <g>
  7 + <path d="M505.455,54.9575C472.32,49.2677 470.312,99.1369 437.177,93.1124C428.475,77.7166 419.773,61.986 411.071,46.5902C444.206,52.2799 446.214,2.4108 479.349,8.43526C488.051,24.1658 496.753,39.5616 505.455,54.9575Z" style="fill:rgb(250,248,209);fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  8 + <path d="M411.406,46.9249C444.541,52.6146 446.549,2.74549 479.683,8.76995C488.385,24.1658 497.087,39.8963 505.789,55.2922C472.655,49.6024 470.647,99.4715 437.512,93.4471" style="fill:rgb(250,248,209);fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  9 + </g>
  10 + <g>
  11 + <path d="M411.406,46.9249L481.022,170.761" style="fill:rgb(255,224,103);fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  12 + </g>
  13 + <g>
  14 + <path d="M452.239,72.3615C450.9,73.3656 449.561,74.3696 448.222,75.039C447.218,73.7002 446.549,72.3615 445.545,71.0227C442.198,73.0309 438.516,74.3696 434.165,75.039C433.831,74.3696 433.496,73.7002 432.826,73.0309C433.496,67.6758 434.165,61.3166 434.165,54.6228C435.839,53.6187 437.177,52.6146 438.851,51.6106C441.528,55.9616 444.541,60.3126 447.218,64.6636C447.888,64.3289 448.557,63.6595 448.892,63.3248C449.561,64.6636 450.565,65.6676 451.235,67.0064C450.565,67.3411 449.896,68.0105 449.561,68.3452C450.23,69.6839 451.235,71.0227 452.239,72.3615ZM438.516,58.9738C438.516,62.6554 438.516,66.337 438.182,70.0186C440.19,69.3492 441.863,68.3452 443.537,67.3411C442.533,66.0023 441.863,64.6636 440.859,63.3248C440.19,61.986 439.186,60.3126 438.516,58.9738L438.516,58.9738Z" style="fill:black;fill-rule:nonzero;stroke-width:1px;stroke-linecap:buttstroke:rgb(25,43,59);"/>
  15 + <path d="M461.61,40.9004C463.283,43.2433 464.622,45.9208 466.296,48.2636C469.308,52.9493 467.634,58.3044 464.288,61.986C460.606,65.6676 455.92,66.0023 452.908,61.3166C451.235,58.9738 449.896,56.2963 448.222,53.9534C445.21,49.6024 446.549,43.9126 450.565,39.8963C453.912,36.5494 458.932,36.5494 461.61,40.9004ZM462.949,52.2799C462.614,51.6106 462.279,51.2759 461.945,50.6065C460.941,48.933 459.937,47.2596 458.932,45.5861C458.598,44.9167 458.263,44.582 457.928,43.9126C456.59,42.2392 454.581,42.5739 453.243,44.2473C451.569,45.9208 450.9,47.9289 451.904,50.2718C452.239,50.9412 452.573,51.6106 452.908,51.9453C453.912,53.6187 454.916,55.2922 455.92,56.9656C456.255,57.635 456.59,57.9697 456.924,58.6391C458.263,60.3126 460.271,59.9779 461.61,58.3044C463.283,56.6309 463.953,54.2881 462.949,52.2799Z" style="fill:black;fill-rule:nonzero;stroke-width:1px;stroke-linecap:buttstroke:rgb(25,43,59);"/>
  16 + <path d="M487.716,45.5861C486.042,45.9208 484.369,46.5902 483.03,46.9249C482.026,45.5861 481.357,44.2473 480.353,42.9086C476.671,44.2473 473.324,46.5902 470.312,48.933C469.977,48.2636 469.643,47.5943 468.973,46.9249C468.638,39.5616 468.638,32.5331 468.638,26.1739C470.312,25.5046 471.985,24.8352 473.994,24.5005C476.671,28.8515 479.683,33.2025 482.361,37.5535C483.03,37.2188 484.034,37.2188 484.704,37.2188C485.373,38.5576 486.377,39.5616 487.047,40.9004C486.042,40.9004 485.373,41.2351 484.704,41.2351C486.042,42.5739 487.047,43.9126 487.716,45.5861ZM473.324,30.8596C472.989,34.5413 472.989,38.2229 472.989,41.9045C474.663,40.9004 476.336,39.8963 478.345,39.2269C477.34,37.8882 476.671,36.5494 475.667,35.2106C474.998,33.5372 474.328,32.1984 473.324,30.8596L473.324,30.8596Z" style="fill:black;fill-rule:nonzero;stroke-width:1px;stroke-linecap:buttstroke:rgb(25,43,59);"/>
  17 + </g>
  18 + <g>
  19 + <path d="M176.787,338.442C170.762,338.442 164.403,337.438 157.375,334.091C140.975,326.393 129.261,308.654 122.901,281.544C117.881,260.459 107.506,247.406 92.1097,243.055C70.6894,237.03 44.5834,249.414 39.2284,254.434C36.2161,257.446 31.5304,257.446 28.5182,254.434C25.506,251.422 25.506,246.736 28.5182,243.724C35.8814,236.026 67.0078,220.295 96.126,228.328C110.852,232.344 129.595,244.393 137.628,277.863C142.983,300.287 151.685,314.344 163.734,320.034C177.122,326.393 191.179,320.034 191.179,320.034C194.86,318.36 199.546,319.699 201.219,323.381C202.893,327.062 201.554,331.748 197.872,333.421C197.538,334.091 188.501,338.442 176.787,338.442Z" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  20 + <path d="M9.77544,247.74C17.1387,242.05 27.5141,243.724 33.2039,251.087C38.8937,258.45 37.2202,268.826 29.857,274.516C24.8366,278.197 18.4774,278.867 13.1224,276.524C10.7795,275.52 9.77544,272.842 10.7795,270.499C11.7836,268.156 14.4611,267.152 16.804,268.156C19.1468,269.161 22.159,268.826 24.5019,267.152C27.8488,264.81 28.5182,259.789 26.1754,256.777C23.8325,253.43 18.8121,252.761 15.7999,255.103C13.7917,256.442 12.7877,258.45 12.7877,260.793C12.7877,263.471 10.4448,265.479 8.10197,265.144C5.42444,265.144 3.41628,262.801 3.75098,260.459C3.41628,255.438 5.75913,250.752 9.77544,247.74Z" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  21 + <path d="M189.17,321.373C189.17,321.373 187.832,328.736 191.848,336.434" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
  22 + <path d="M179.799,323.046C179.799,323.046 174.779,330.744 177.791,340.115" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
  23 + <path d="M169.758,322.377C169.758,322.377 162.395,329.07 163.064,336.099" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
  24 + <path d="M159.048,317.691C159.048,317.691 149.677,324.385 148.673,329.74" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
  25 + <path d="M152.02,310.997C152.02,310.997 143.318,313.34 139.636,319.364" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
  26 + <path d="M145.66,300.956C145.66,300.956 135.285,303.634 132.273,307.985" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
  27 + <path d="M140.975,289.577C140.975,289.577 131.603,289.577 126.918,295.267" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
  28 + <path d="M137.628,278.197C137.628,278.197 128.926,276.858 122.901,281.544" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
  29 + <path d="M133.946,265.814C133.946,265.814 124.91,264.14 119.22,269.83" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
  30 + <path d="M126.918,251.757C126.918,251.757 119.889,252.426 113.53,259.12" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
  31 + <path d="M116.542,239.708C116.542,239.708 107.84,244.728 106.167,250.752" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
  32 + <path d="M104.828,232.01C104.828,232.01 96.4607,236.361 96.7954,245.063" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
  33 + <path d="M88.7628,227.324C88.7628,227.324 85.4159,235.022 86.7547,242.385" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
  34 + <path d="M74.371,226.655C74.371,226.655 72.3629,236.695 74.7057,242.385" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
  35 + <path d="M59.6446,228.997C59.6446,228.997 59.3099,238.704 63.6609,243.724" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
  36 + <path d="M45.5875,234.018C45.5875,234.018 45.5875,240.042 50.9426,247.74" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
  37 + </g>
  38 + <g>
  39 + <path d="M447.218,143.651C446.884,144.655 439.855,169.422 413.08,183.814C394.672,193.855 377.602,206.573 387.978,243.055C391.659,256.777 390.321,268.491 383.627,277.528C371.912,293.593 348.484,294.262 347.48,294.597L347.145,294.597C343.129,294.597 339.782,291.25 339.782,287.234C339.782,283.218 342.794,279.536 347.145,279.536C347.48,279.536 364.215,278.867 371.578,268.826C375.259,263.805 375.929,256.442 373.586,247.071C360.868,201.887 384.296,182.475 406.051,170.761C427.137,159.381 432.826,139.969 432.826,139.635" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  40 + <path d="M456.255,144.32C448.222,149.006 438.182,146.663 433.161,138.631C428.475,130.598 430.818,120.557 438.851,115.537C443.202,112.859 448.222,112.525 452.908,113.863C453.912,114.198 454.916,114.533 455.586,115.202C457.928,116.541 458.598,119.218 457.259,121.561C456.924,122.231 456.59,122.565 456.255,122.9C454.916,123.904 452.908,124.239 450.9,123.235C448.557,121.896 445.545,121.896 443.202,123.235C439.52,125.243 438.516,129.929 440.524,133.61C442.533,137.292 447.218,138.296 450.9,136.288C452.908,134.949 454.247,132.941 454.581,130.933C454.916,128.255 457.259,126.582 459.602,126.916C462.279,127.251 463.953,129.594 463.618,131.937C463.283,137.292 460.606,141.643 456.255,144.32Z" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  41 + <path d="M357.186,277.528C357.186,277.528 358.859,287.569 362.541,291.585" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
  42 + <path d="M366.557,273.512C366.557,273.512 368.566,281.879 374.255,286.23" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
  43 + <path d="M372.917,266.483C372.917,266.483 377.602,271.838 386.304,273.512" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
  44 + <path d="M374.59,258.785C374.59,258.785 379.945,260.793 389.651,258.785" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
  45 + <path d="M373.586,248.41C373.586,248.41 381.284,248.075 388.312,245.063" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
  46 + <path d="M370.908,237.365C370.908,237.365 377.268,237.699 385.635,234.353" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
  47 + <path d="M369.904,223.977C369.904,223.977 372.582,225.651 384.965,223.977" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
  48 + <path d="M371.243,209.92C371.243,209.92 374.925,213.602 384.631,214.606" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
  49 + <path d="M375.929,197.536C375.929,197.536 380.28,202.222 388.647,205.234" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
  50 + <path d="M384.631,185.822C384.631,185.822 388.982,193.52 395.341,196.198" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
  51 + <path d="M394.337,176.451C394.337,176.451 397.014,183.145 403.708,189.504" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
  52 + <path d="M404.712,171.43C404.712,171.43 407.39,179.128 413.08,184.149" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
  53 + <path d="M414.084,165.071C414.084,165.071 418.435,172.769 424.124,176.451" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
  54 + <path d="M421.782,158.043C421.782,158.043 426.467,165.741 433.161,167.749" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
  55 + <path d="M428.475,148.671C428.475,148.671 434.165,155.7 441.194,156.704" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
  56 + </g>
  57 + <g>
  58 + <path d="M357.521,417.429L246.738,443.535L249.415,425.462L347.145,402.703Z" style="fill:rgb(57,177,82);fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  59 + <g>
  60 + <path d="M320.029,499.002C304.372,502.674 291.167,503.462 290.534,500.762C289.901,498.063 302.08,492.898 317.737,489.227C333.394,485.555 346.6,484.767 347.233,487.467C347.866,490.166 335.686,495.331 320.029,499.002Z" style="fill:none;stroke-width:6.69px;stroke:rgb(57,177,82);stroke-opacity:0.211765;"/>
  61 + </g>
  62 + <g>
  63 + <path d="M315.602,480.302C296.886,484.691 281.2,486.06 280.567,483.361C279.934,480.661 294.593,474.915 313.31,470.526C332.026,466.138 347.712,464.768 348.345,467.468C348.978,470.167 334.319,475.913 315.602,480.302Z" style="fill:none;stroke-width:6.69px;stroke:rgb(57,177,82);stroke-opacity:0.501961;"/>
  64 + </g>
  65 + <g>
  66 + <path d="M310.332,458.432C288.377,463.58 270.065,465.566 269.432,462.866C268.799,460.167 286.084,453.805 308.04,448.656C329.996,443.508 348.308,441.523 348.941,444.222C349.574,446.922 332.288,453.283 310.332,458.432Z" style="fill:none;stroke-width:6.69px;stroke:rgb(57,177,82);"/>
  67 + </g>
  68 + <path d="M230.003,96.1246L216.95,99.1369L211.595,62.6554L218.289,60.9819Z" style="fill:rgb(79,160,119);fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  69 + <path d="M366.557,398.017L229.668,430.147L176.117,203.226C167.415,166.41 190.175,129.594 226.991,121.227L229.668,120.557C267.154,111.855 304.639,134.949 313.341,172.434Z" style="fill:rgb(250,248,209);fill-rule:nonzero;stroke-width:1px;stroke-linecap:buttstroke:rgb(25,43,59);"/>
  70 + <path d="M366.557,398.017L229.668,430.147L176.117,203.226C167.415,166.41 190.175,129.594 226.991,121.227L229.668,120.557C267.154,111.855 304.639,134.949 313.341,172.434Z" style="fill:none;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  71 + <g>
  72 + <path d="M221.964,411.513L365.297,377.716L369.905,397.261L226.573,431.059L221.964,411.513Z" style="fill:rgb(139,124,185);stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  73 + </g>
  74 + <path d="M224.313,421.111L250.419,415.086" style="fill:rgb(79,160,119);fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  75 + <path d="M214.607,45.9208C220.522,45.9208 225.317,50.7159 225.317,56.6309C225.317,62.546 220.522,67.3411 214.607,67.3411C208.692,67.3411 203.897,62.546 203.897,56.6309C203.897,50.7159 208.692,45.9208 214.607,45.9208Z" style="fill:rgb(57,177,82);stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  76 + <path d="M210.925,293.258L224.983,351.829" style="fill:none;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  77 + <path d="M214.942,396.009L216.281,402.368" style="fill:none;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  78 + <path d="M191.179,208.581L195.864,227.993" style="fill:none;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  79 + <path d="M198.877,241.046L203.228,260.793" style="fill:none;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  80 + <path d="M241.048,244.728C242.721,251.087 252.427,259.12 261.129,257.112C269.831,255.103 274.852,243.724 273.513,237.03Z" style="fill:white;fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  81 + <path d="M264.142,205.234C264.142,200.883 267.154,196.532 271.839,195.528C276.525,194.524 280.876,196.532 283.219,200.549" style="fill:none;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  82 + <path d="M215.276,216.614C215.276,212.263 218.289,207.912 222.974,206.908C227.66,205.904 232.011,207.912 234.354,211.928" style="fill:none;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  83 + <path d="M173.105,260.793C166.411,262.467 159.718,258.45 158.379,251.757L153.693,231.34C152.02,224.646 156.371,218.287 162.73,216.614L163.064,216.614Z" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  84 + <path d="M341.121,221.3L330.411,177.12L330.745,177.12C337.439,175.447 344.468,179.798 346.141,186.492L350.827,206.238C352.166,212.598 347.815,219.626 341.121,221.3Z" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  85 + <path d="M343.464,232.344C324.051,150.345 318.027,121.561 292.925,101.814C262.468,78.0512 220.632,89.4308 220.632,89.4308L221.301,89.0961C221.301,89.0961 178.795,97.4634 162.06,132.606C148.338,161.39 156.036,189.838 175.448,271.838" style="fill:rgb(197,239,249);fill-opacity:0.298039;fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  86 + <path d="M293.26,101.814C264.476,79.0553 225.317,88.4267 221.301,89.4308C217.285,90.4349 178.126,99.4715 162.395,132.606C148.673,161.39 156.371,189.838 175.783,271.838L189.84,268.491C176.117,209.251 173.775,188.834 183.146,169.088C194.86,143.986 225.987,135.618 231.342,134.614C231.676,134.614 233.35,134.28 233.685,133.945C239.04,132.606 270.166,125.912 291.921,142.982C309.994,157.039 316.688,178.124 330.411,235.357L343.798,232.344C324.386,150.345 318.362,121.561 293.26,101.814Z" style="fill:rgb(128,147,154);fill-rule:nonzero;stroke-width:1px;stroke-linecap:buttstroke:rgb(25,43,59);"/>
  87 + <clipPath id="tag1">
  88 + <path d="M293.26,101.814C264.476,79.0553 225.317,88.4267 221.301,89.4308C217.285,90.4349 178.126,99.4715 162.395,132.606C148.673,161.39 156.371,189.838 175.783,271.838L189.84,268.491C176.117,209.251 173.775,188.834 183.146,169.088C194.86,143.986 225.987,135.618 231.342,134.614C231.676,134.614 233.35,134.28 233.685,133.945C239.04,132.606 270.166,125.912 291.921,142.982C309.994,157.039 316.688,178.124 330.411,235.357L343.798,232.344C324.386,150.345 318.362,121.561 293.26,101.814Z"/>
  89 + </clipPath>
  90 + <g clip-path="url(#tag1)">
  91 + <path d="M145.326,260.459L407.39,199.21" style="fill:rgb(128,147,154);fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  92 + </g>
  93 + <clipPath id="tag2">
  94 + <path d="M293.26,101.814C264.476,79.0553 225.317,88.4267 221.301,89.4308C217.285,90.4349 178.126,99.4715 162.395,132.606C148.673,161.39 156.371,189.838 175.783,271.838L189.84,268.491C176.117,209.251 173.775,188.834 183.146,169.088C194.86,143.986 225.987,135.618 231.342,134.614C231.676,134.614 233.35,134.28 233.685,133.945C239.04,132.606 270.166,125.912 291.921,142.982C309.994,157.039 316.688,178.124 330.411,235.357L343.798,232.344C324.386,150.345 318.362,121.561 293.26,101.814Z"/>
  95 + </clipPath>
  96 + <g clip-path="url(#tag2)">
  97 + <path d="M141.309,244.393L403.374,182.81" style="fill:rgb(128,147,154);fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  98 + </g>
  99 + <path d="M293.26,101.814C264.476,79.0553 225.317,88.4267 221.301,89.4308C217.285,90.4349 178.126,99.4715 162.395,132.606C148.673,161.39 156.371,189.838 175.783,271.838L189.84,268.491C176.117,209.251 173.775,188.834 183.146,169.088C194.86,143.986 225.987,135.618 231.342,134.614C231.676,134.614 233.35,134.28 233.685,133.945C239.04,132.606 270.166,125.912 291.921,142.982C309.994,157.039 316.688,178.124 330.411,235.357L343.798,232.344C324.386,150.345 318.362,121.561 293.26,101.814Z" style="fill:none;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  100 + <g>
  101 + <path d="M171.454,271.511L347.362,230.032L352.739,252.835L176.83,294.313L171.454,271.511Z" style="fill:rgb(255,224,103);stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  102 + </g>
  103 + <g>
  104 + <path d="M196.496,289.883L333.314,257.622L337.155,273.91L200.337,306.171L196.496,289.883Z" style="fill:rgb(232,107,47);stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  105 + </g>
  106 + <path d="M157.375,182.141C169.424,150.01 194.526,126.247 227.995,118.549C262.133,110.516 298.615,119.888 320.37,144.32" style="fill:none;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  107 + <path d="M157.375,182.141C169.424,150.01 194.191,125.912 227.995,118.214C262.133,110.182 298.28,117.88 320.37,143.651L321.039,143.651C313.341,121.227 306.313,112.525 293.929,102.484C263.472,78.7206 221.97,91.4389 221.97,91.4389L222.64,91.1042C222.64,91.1042 179.799,98.4675 163.399,133.61C156.705,148.002 155.032,161.724 157.709,182.81Z" style="fill:rgb(83,145,199);fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  108 + <path d="M217.285,257.112C224.678,257.112 230.672,263.105 230.672,270.499C230.672,277.893 224.678,283.887 217.285,283.887C209.891,283.887 203.897,277.893 203.897,270.499C203.897,263.105 209.891,257.112 217.285,257.112Z" style="fill:rgb(57,177,82);stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  109 + <path d="M306.313,236.026C313.707,236.026 319.7,242.02 319.7,249.414C319.7,256.807 313.707,262.801 306.313,262.801C298.919,262.801 292.925,256.807 292.925,249.414C292.925,242.02 298.919,236.026 306.313,236.026Z" style="fill:rgb(57,177,82);stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  110 + <path d="M221.97,90.4349L228.329,118.214" style="fill:none;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  111 + <path d="M181.807,110.851L187.832,136.957" style="fill:none;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  112 + <path d="M261.129,91.4389L267.154,116.876" style="fill:none;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  113 + <path d="M143.652,243.055C144.761,243.055 145.66,243.954 145.66,245.063C145.66,246.172 144.761,247.071 143.652,247.071C142.543,247.071 141.644,246.172 141.644,245.063C141.644,243.954 142.543,243.055 143.652,243.055Z" style="fill:rgb(204,204,204);stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  114 + <path d="M154.028,242.385L143.987,244.728" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  115 + <path d="M360.868,191.847C361.977,191.847 362.876,192.746 362.876,193.855C362.876,194.964 361.977,195.863 360.868,195.863C359.759,195.863 358.859,194.964 358.859,193.855C358.859,192.746 359.759,191.847 360.868,191.847Z" style="fill:rgb(204,204,204);stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  116 + <path d="M350.157,196.198L360.198,193.855" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  117 + <path d="M173.775,281.209L193.521,276.858" style="fill:rgb(128,147,154);fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  118 + <path d="M242.387,265.144L281.211,256.108" style="fill:rgb(128,147,154);fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  119 + <path d="M330.076,244.393L349.823,239.708" style="fill:rgb(128,147,154);fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  120 + <path d="M172.101,135.953C172.101,135.953 184.485,111.855 214.607,103.153" style="fill:none;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  121 + <path d="" style="fill:none;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  122 + <g>
  123 + <path d="M208.825,144.073C214.612,148.967 211.754,161.858 202.443,172.866C193.132,183.874 180.893,188.831 175.107,183.936C169.321,179.042 172.178,166.151 181.489,155.143C190.8,144.134 203.039,139.178 208.825,144.073Z" style="fill:white;fill-opacity:0.298039;stroke-width:1px;stroke-linecap:buttstroke:rgb(25,43,59);"/>
  124 + </g>
  125 + <path d="M207.913,287.234L211.595,303.299" style="fill:rgb(83,145,199);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
  126 + <path d="M224.313,283.218L227.995,299.618" style="fill:rgb(83,145,199);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
  127 + <path d="M240.378,279.536L244.395,295.601" style="fill:rgb(83,145,199);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
  128 + <path d="M256.778,275.52L260.46,291.92" style="fill:rgb(83,145,199);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
  129 + <path d="M273.178,271.838L276.86,287.903" style="fill:rgb(83,145,199);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
  130 + <path d="M289.243,267.822L293.26,284.222" style="fill:rgb(83,145,199);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
  131 + <path d="M305.643,264.14L309.325,280.205" style="fill:rgb(83,145,199);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
  132 + <path d="M322.043,260.124L325.725,276.524" style="fill:rgb(83,145,199);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
  133 + <path d="M368.566,376.931L218.623,412.409L196.534,319.03C195.195,313.005 198.877,306.646 204.901,305.307L332.419,275.185C338.443,273.846 344.802,277.528 346.141,283.552Z" style="fill:rgb(250,248,209);fill-rule:nonzero;stroke-width:1px;stroke-linecap:buttstroke:rgb(25,43,59);"/>
  134 + <clipPath id="tag3">
  135 + <path d="M368.566,376.931L218.623,412.409L196.534,319.03C195.195,313.005 198.877,306.646 204.901,305.307L332.419,275.185C338.443,273.846 344.802,277.528 346.141,283.552Z"/>
  136 + </clipPath>
  137 + <g clip-path="url(#tag3)">
  138 + <path d="M366.557,353.838L189.84,396.344L195.195,418.768L371.912,376.931Z" style="fill:rgb(255,224,103);fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  139 + </g>
  140 + <path d="M368.566,376.931L218.623,412.409L196.534,319.03C195.195,313.005 198.877,306.646 204.901,305.307L332.419,275.185C338.443,273.846 344.802,277.528 346.141,283.552Z" style="fill:none;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  141 + <g>
  142 + <path d="M241.445,320.505L306.596,305.142L314.278,337.718L249.126,353.08L241.445,320.505Z" style="fill:rgb(139,124,185);stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  143 + </g>
  144 + <path d="M288.909,309.324L296.607,341.789" style="fill:rgb(250,248,209);fill-rule:nonzero;stroke-width:3.35px;stroke:rgb(25,43,59);"/>
  145 + <g>
  146 + <path d="M289.33,311.024L305.618,307.184L307.923,316.957L291.635,320.797L289.33,311.024Z" style="fill:rgb(79,160,119);stroke-width:3.35px;stroke:rgb(25,43,59);"/>
  147 + </g>
  148 + <g>
  149 + <path d="M291.331,320.8L307.619,316.96L309.923,326.733L293.635,330.573L291.331,320.8Z" style="fill:rgb(255,224,103);stroke-width:3.35px;stroke:rgb(25,43,59);"/>
  150 + </g>
  151 + <g>
  152 + <path d="M293.657,330.5L309.945,326.659L312.249,336.432L295.962,340.272L293.657,330.5Z" style="fill:rgb(229,66,48);stroke-width:3.35px;stroke:rgb(25,43,59);"/>
  153 + </g>
  154 + <path d="M212.934,330.744L227.995,392.997" style="fill:none;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  155 + </g>
  156 + <g>
  157 + <path d="M445.879,108.174L462.614,137.961" style="fill:rgb(204,204,204);fill-rule:nonzero;stroke-width:6.69px;stroke:rgb(25,43,59);"/>
  158 + </g>
  159 + </g>
  160 + </g>
  161 +</svg>
  1 +// AdminLTE Gruntfile
  2 +module.exports = function (grunt) {
  3 +
  4 + 'use strict';
  5 +
  6 + grunt.initConfig({
  7 + watch: {
  8 + // If any .less file changes in directory "build/less/" run the "less"-task.
  9 + files: ["build/less/*.less", "build/less/skins/*.less", "dist/js/app.js"],
  10 + tasks: ["less", "uglify"]
  11 + },
  12 + // "less"-task configuration
  13 + // This task will compile all less files upon saving to create both AdminLTE.css and AdminLTE.min.css
  14 + less: {
  15 + // Development not compressed
  16 + development: {
  17 + options: {
  18 + // Whether to compress or not
  19 + compress: false
  20 + },
  21 + files: {
  22 + // compilation.css : source.less
  23 + "dist/css/AdminLTE.css": "build/less/AdminLTE.less",
  24 + //Non minified skin files
  25 + "dist/css/skins/skin-blue.css": "build/less/skins/skin-blue.less",
  26 + "dist/css/skins/skin-black.css": "build/less/skins/skin-black.less",
  27 + "dist/css/skins/skin-yellow.css": "build/less/skins/skin-yellow.less",
  28 + "dist/css/skins/skin-green.css": "build/less/skins/skin-green.less",
  29 + "dist/css/skins/skin-red.css": "build/less/skins/skin-red.less",
  30 + "dist/css/skins/skin-purple.css": "build/less/skins/skin-purple.less",
  31 + "dist/css/skins/skin-blue-light.css": "build/less/skins/skin-blue-light.less",
  32 + "dist/css/skins/skin-black-light.css": "build/less/skins/skin-black-light.less",
  33 + "dist/css/skins/skin-yellow-light.css": "build/less/skins/skin-yellow-light.less",
  34 + "dist/css/skins/skin-green-light.css": "build/less/skins/skin-green-light.less",
  35 + "dist/css/skins/skin-red-light.css": "build/less/skins/skin-red-light.less",
  36 + "dist/css/skins/skin-purple-light.css": "build/less/skins/skin-purple-light.less",
  37 + "dist/css/skins/_all-skins.css": "build/less/skins/_all-skins.less"
  38 + }
  39 + },
  40 + // Production compresses version
  41 + production: {
  42 + options: {
  43 + // Whether to compress or not
  44 + compress: true
  45 + },
  46 + files: {
  47 + // compilation.css : source.less
  48 + "dist/css/AdminLTE.min.css": "build/less/AdminLTE.less",
  49 + // Skins minified
  50 + "dist/css/skins/skin-blue.min.css": "build/less/skins/skin-blue.less",
  51 + "dist/css/skins/skin-black.min.css": "build/less/skins/skin-black.less",
  52 + "dist/css/skins/skin-yellow.min.css": "build/less/skins/skin-yellow.less",
  53 + "dist/css/skins/skin-green.min.css": "build/less/skins/skin-green.less",
  54 + "dist/css/skins/skin-red.min.css": "build/less/skins/skin-red.less",
  55 + "dist/css/skins/skin-purple.min.css": "build/less/skins/skin-purple.less",
  56 + "dist/css/skins/skin-blue-light.min.css": "build/less/skins/skin-blue-light.less",
  57 + "dist/css/skins/skin-black-light.min.css": "build/less/skins/skin-black-light.less",
  58 + "dist/css/skins/skin-yellow-light.min.css": "build/less/skins/skin-yellow-light.less",
  59 + "dist/css/skins/skin-green-light.min.css": "build/less/skins/skin-green-light.less",
  60 + "dist/css/skins/skin-red-light.min.css": "build/less/skins/skin-red-light.less",
  61 + "dist/css/skins/skin-purple-light.min.css": "build/less/skins/skin-purple-light.less",
  62 + "dist/css/skins/_all-skins.min.css": "build/less/skins/_all-skins.less"
  63 + }
  64 + }
  65 + },
  66 + // Uglify task info. Compress the js files.
  67 + uglify: {
  68 + options: {
  69 + mangle: true,
  70 + preserveComments: 'some'
  71 + },
  72 + my_target: {
  73 + files: {
  74 + 'dist/js/app.min.js': ['dist/js/app.js']
  75 + }
  76 + }
  77 + },
  78 + // Build the documentation files
  79 + includes: {
  80 + build: {
  81 + src: ['*.html'], // Source files
  82 + dest: 'documentation/', // Destination directory
  83 + flatten: true,
  84 + cwd: 'documentation/build',
  85 + options: {
  86 + silent: true,
  87 + includePath: 'documentation/build/include'
  88 + }
  89 + }
  90 + },
  91 +
  92 + // Optimize images
  93 + image: {
  94 + dynamic: {
  95 + files: [{
  96 + expand: true,
  97 + cwd: 'build/img/',
  98 + src: ['**/*.{png,jpg,gif,svg,jpeg}'],
  99 + dest: 'dist/img/'
  100 + }]
  101 + }
  102 + },
  103 +
  104 + // Validate JS code
  105 + jshint: {
  106 + options: {
  107 + jshintrc: '.jshintrc'
  108 + },
  109 + core: {
  110 + src: 'dist/js/app.js'
  111 + },
  112 + demo: {
  113 + src: 'dist/js/demo.js'
  114 + },
  115 + pages: {
  116 + src: 'dist/js/pages/*.js'
  117 + }
  118 + },
  119 +
  120 + // Validate CSS files
  121 + csslint: {
  122 + options: {
  123 + csslintrc: 'build/less/.csslintrc'
  124 + },
  125 + dist: [
  126 + 'dist/css/AdminLTE.css',
  127 + ]
  128 + },
  129 +
  130 + // Validate Bootstrap HTML
  131 + bootlint: {
  132 + options: {
  133 + relaxerror: ['W005']
  134 + },
  135 + files: ['pages/**/*.html', '*.html']
  136 + },
  137 +
  138 + // Delete images in build directory
  139 + // After compressing the images in the build/img dir, there is no need
  140 + // for them
  141 + clean: {
  142 + build: ["build/img/*"]
  143 + }
  144 + });
  145 +
  146 + // Load all grunt tasks
  147 +
  148 + // LESS Compiler
  149 + grunt.loadNpmTasks('grunt-contrib-less');
  150 + // Watch File Changes
  151 + grunt.loadNpmTasks('grunt-contrib-watch');
  152 + // Compress JS Files
  153 + grunt.loadNpmTasks('grunt-contrib-uglify');
  154 + // Include Files Within HTML
  155 + grunt.loadNpmTasks('grunt-includes');
  156 + // Optimize images
  157 + grunt.loadNpmTasks('grunt-image');
  158 + // Validate JS code
  159 + grunt.loadNpmTasks('grunt-contrib-jshint');
  160 + // Delete not needed files
  161 + grunt.loadNpmTasks('grunt-contrib-clean');
  162 + // Lint CSS
  163 + grunt.loadNpmTasks('grunt-contrib-csslint');
  164 + // Lint Bootstrap
  165 + grunt.loadNpmTasks('grunt-bootlint');
  166 +
  167 + // Linting task
  168 + grunt.registerTask('lint', ['jshint', 'csslint', 'bootlint']);
  169 +
  170 + // The default task (running "grunt" in console) is "watch"
  171 + grunt.registerTask('default', ['watch']);
  172 +};
  1 +The MIT License (MIT)
  2 +
  3 +Copyright (c) 2014-2016 Abdullah Almsaeed
  4 +
  5 +Permission is hereby granted, free of charge, to any person obtaining a copy of
  6 +this software and associated documentation files (the "Software"), to deal in
  7 +the Software without restriction, including without limitation the rights to
  8 +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
  9 +the Software, and to permit persons to whom the Software is furnished to do so,
  10 +subject to the following conditions:
  11 +
  12 +The above copyright notice and this permission notice shall be included in all
  13 +copies or substantial portions of the Software.
  14 +
  15 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
  17 +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
  18 +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
  19 +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  20 +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  1 +**I apologize to everyone for my slow rate of response and development recently.** This is my final semester and I am very busy. Although I usually work on AdminLTE a few hours a week, there are weeks full of exams and assignments. Thanks for your understanding.
  2 +
  3 +Introduction
  4 +============
  5 +
  6 +![Bower version](https://img.shields.io/bower/v/adminlte.svg)
  7 +[![npm version](https://img.shields.io/npm/v/admin-lte.svg)](https://www.npmjs.com/package/admin-lte)
  8 +[![Packagist](https://img.shields.io/packagist/v/almasaeed2010/adminlte.svg)](https://packagist.org/packages/almasaeed2010/adminlte)
  9 +
  10 +**AdminLTE** -- is a fully responsive admin template. Based on **[Bootstrap 3](https://github.com/twbs/bootstrap)** framework. Highly customizable and easy to use. Fits many screen resolutions from small mobile devices to large desktops. Check out the live preview now and see for yourself.
  11 +
  12 +**Download & Preview on [Almsaeed Studio](https://almsaeedstudio.com)**
  13 +
  14 +Looking for Premium Templates?
  15 +------------------------------
  16 +**Almsaeed studio just opened a new premium templates page. Hand picked to insure the best quality and the most affordable prices. Visit https://almsaeedstudio.com/premium for more information.**
  17 +
  18 +
  19 +!["AdminLTE Presentation"] (https://almsaeedstudio.com/AdminLTE2.png "AdminLTE Presentation")
  20 +
  21 +**AdminLTE** has been carefully coded with clear comments in all of its JS, LESS and HTML files. LESS has been used to increase code customizability.
  22 +
  23 +Installation
  24 +------------
  25 +There are multiple ways to install AdminLTE.
  26 +
  27 +####Download:
  28 +
  29 +Download from Github or [visit Almsaeed Studio](https://almsaeedstudio.com) and download the latest release.
  30 +
  31 +####Using The Command Line:
  32 +
  33 +**Github**
  34 +
  35 +- Fork the repository ([here is the guide](https://help.github.com/articles/fork-a-repo/)).
  36 +- Clone to your machine
  37 +```
  38 +git clone https://github.com/YOUR_USERNAME/AdminLTE.git
  39 +```
  40 +
  41 +**Bower**
  42 +
  43 +```
  44 +bower install admin-lte
  45 +```
  46 +
  47 +**npm**
  48 +
  49 +```
  50 +npm install --save admin-lte
  51 +```
  52 +
  53 +**Composer**
  54 +
  55 +```
  56 +composer require "almasaeed2010/adminlte=~2.0"
  57 +```
  58 +
  59 +Documentation
  60 +-------------
  61 +Visit the [online documentation](https://almsaeedstudio.com/themes/AdminLTE/documentation/index.html) for the most
  62 +updated guide. Information will be added on a weekly basis.
  63 +
  64 +Browser Support
  65 +---------------
  66 +- IE 9+
  67 +- Firefox (latest)
  68 +- Chrome (latest)
  69 +- Safari (latest)
  70 +- Opera (latest)
  71 +
  72 +Contribution
  73 +------------
  74 +Contribution are always **welcome and recommended**! Here is how:
  75 +
  76 +- Fork the repository ([here is the guide](https://help.github.com/articles/fork-a-repo/)).
  77 +- Clone to your machine ```git clone https://github.com/YOUR_USERNAME/AdminLTE.git```
  78 +- Make your changes
  79 +- Create a pull request
  80 +
  81 +#### Contribution Requirements:
  82 +
  83 +- When you contribute, you agree to give a non-exclusive license to Almsaeed Studio to use that contribution in any context as we (Almsaeed Studio) see appropriate.
  84 +- If you use content provided by another party, it must be appropriately licensed using an [open source](http://opensource.org/licenses) license.
  85 +- Contributions are only accepted through Github pull requests.
  86 +- Finally, contributed code must work in all supported browsers (see above for browser support).
  87 +
  88 +License
  89 +-------
  90 +AdminLTE is an open source project by [Almsaeed Studio](https://almsaeedstudio.com) that is licensed under [MIT](http://opensource.org/licenses/MIT). Almsaeed Studio
  91 +reserves the right to change the license of future releases.
  92 +
  93 +Todo List
  94 +---------
  95 +- ~~Light sidebar colors~~ (Done v2.1.0)
  96 +- ~~Right sidebar~~ (Done v2.1.0)
  97 +- ~~Minified main-sidebar~~ (Done v2.1.0)
  98 +- Right to left support
  99 +- ~~Custom pace style~~ (Done v2.3.1)
  100 +
  101 +Legacy Releases
  102 +----------------
  103 +AdminLTE 1.x can be easily upgraded to 2.x using [this guide](https://almsaeedstudio.com/themes/AdminLTE/documentation/index.html#upgrade), but if you intend to keep using AdminLTE 1.x, you can download the latest release from the [releases](https://github.com/almasaeed2010/AdminLTE/releases) section above.
  104 +
  105 +Change log
  106 +----------
  107 +
  108 +**For the most recent change log, visit the [releases page](https://github.com/almasaeed2010/AdminLTE/releases).** We will add a detailed release notes to each new release.
  109 +
  110 +**v2.3.1:**
  111 +- Fix sidebar issue #676
  112 +- Fix BootLint warnings and errors
  113 +- Minor bug fixes and code reformat
  114 +- Added Pace page
  115 +
  116 +**v2.3.0:**
  117 +- Added social widgets (found in the widgets page)
  118 +- Added profile page
  119 +- Fix issue #430 (requires ```.hold-transition``` to be added to ```<body>```)
  120 +- Fix issue #578
  121 +- Fix issue #579
  122 +
  123 +**v2.2.1:**
  124 +- Bug Fixes
  125 +- Removed many ```!important``` statements in css
  126 +- Activate boxWidget automatically when created after the page has loaded
  127 +- Activate sidebar menu treeview links automatically when created after the page has loaded
  128 +- Updated Font Awesome thanks to @Dennis14e
  129 +- Added JSHint to Grunt tasks (Find JS errors)
  130 +- Added CSSLint to Grunt tasks (Find CSS errors)
  131 +- Added Image to Grunt tasks (compress images)
  132 +- Added Clean to Grunt tasks (remove unwanted files like uncompressed images)
  133 +- Updated Bootstrap to 3.3.5
  134 +
  135 +**v2.2.0:**
  136 +- Bug fixes
  137 +- Added support for [Select2](https://select2.github.io/)
  138 +- Updated ChartJS
  139 +
  140 +**v2.1.2:**
  141 +- Added explicit BoxWidget activation function issue #450
  142 +- Crushed some bugs
  143 +
  144 +**v2.1.1:**
  145 +- Fix version error
  146 +
  147 +**v2.1.0:**
  148 +- Update Ion Icons
  149 +- Added right sidebar ```.control-sidebar```
  150 +- Control sidebar has 2 open effects: slide over content and push content
  151 +- Control sidebar converts to always slide over content on small screens
  152 +- Added 6 new light sidebar skins
  153 +- Updated demo menu
  154 +- Added ChartJS preview page
  155 +- Fixed some minor bugs
  156 +- Added light control sidebar skin
  157 +- Added expand on hover option for sidebar mini
  158 +- Added fixed control sidebar layout
  159 +
  160 +**v2.0.5:**
  161 +- Fixed issue #288
  162 +
  163 +**v2.0.4:**
  164 +- Fixed bower.json to pick up newest release.
  165 +
  166 +**v2.0.3**
  167 +- Bug fixes
  168 +- Fixed extra page when printing issue #264
  169 +- Updated documentation and fixed links scrolling issue
  170 +- Created print.less file (this makes it easier if you want to create a seperate CSS file for printing)
  171 +- Fixed sidebar stretching issue #275
  172 +- Fixed checkbox out of bounds issue in WYSIHTML5 editor.
  173 +
  174 +**v2.0.2:**
  175 +- Solved issue with hidden arrow in select inputs.
  176 +
  177 +**v2.0.1:**
  178 +- Updated README.md
  179 +- Fixed versioning issue in CSS, LESS, and JS
  180 +- Updated box-shadow for boxes
  181 +- Updated docs
  182 +
  183 +**v2.0.0:**
  184 +
  185 +- Major layout bug fixes
  186 +- Change in layout mark up
  187 +- Added transitions to the sidebar
  188 +- New skins and modified previous skins
  189 +- Change in color scheme to a more complementing scheme
  190 +- Added footer support
  191 +- Removed pace.js from the main app.js
  192 +- Added support for collapsed sidebar as an initial state (add .sidebar-collapse to the body tag)
  193 +- Added boxed layout (.layout-boxed)
  194 +- Enhanced consistency in padding and margining
  195 +- Updated Bootstrap to 3.3.2
  196 +- Fixed navbar dropdown menu on small screens positioning issues.
  197 +- Updated Ion Icons to 2.0.0
  198 +- Updated FontAwesome to 4.3.0
  199 +- Added ChartJS 1.0.1
  200 +- Removed iCheck dependency
  201 +- Created Dashboard 2.0
  202 +- Created new Chat widget (DirectChat)
  203 +- Added transitions to DirectChat
  204 +- Added contacts pane to DirectChat
  205 +- Changed .right-side to .content-wrapper
  206 +- Changed .navbar-right to .navbar-custom-menu
  207 +- Removed unused files
  208 +- Updated lockscreen style (HTML markup changed!)
  209 +- Updated Login & Registration pages (HTML markup changed!)
  210 +- Updated buttons style.
  211 +- Enhanced border-radius consistency
  212 +- Added mailbox: inbox, read, and compose pages
  213 +- Bootstrap & jQuery are now hosted locally
  214 +- Created documentation.
  215 +
  216 +**ver 1.2.0:**
  217 +
  218 +- Fixed the sidebar scroll issue when using the fixed layout.
  219 +- Added [Bootstrap Social Buttons](http://lipis.github.io/bootstrap-social/ "Bootstrap Social") plugin.
  220 +- Fixed RequireJS bug. Thanks to [StaticSphere](https://github.com/StaticSphere "github user").
  221 +
  222 +**ver 1.1.0:**
  223 +
  224 +- Added new skin. class: .skin-black
  225 +- Added [pace](http://github.hubspot.com/pace/docs/welcome/ "pace") plugin.
  226 +
  227 +Image Credits
  228 +-------------
  229 +[Pixeden](http://www.pixeden.com/psd-web-elements/flat-responsive-showcase-psd)
  230 +
  231 +[Graphicsfuel](http://www.graphicsfuel.com/2013/02/13-high-resolution-blur-backgrounds/)
  232 +
  233 +[Pickaface](http://pickaface.net/)
  234 +
  235 +[Unsplash](https://unsplash.com/)
  236 +
  237 +[Uifaces](http://uifaces.com/)
  238 +
  239 +Donations
  240 +---------
  241 +Donations are **greatly appreciated!**
  242 +
  243 +[![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif "AdminLTE Presentation")](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=629XCUSXBHCBC "Donate")
  1 +{"version":3,"sources":["less/normalize.less","less/print.less","bootstrap.css","dist/css/bootstrap.css","less/glyphicons.less","less/scaffolding.less","less/mixins/vendor-prefixes.less","less/mixins/tab-focus.less","less/mixins/image.less","less/type.less","less/mixins/text-emphasis.less","less/mixins/background-variant.less","less/mixins/text-overflow.less","less/code.less","less/grid.less","less/mixins/grid.less","less/mixins/grid-framework.less","less/tables.less","less/mixins/table-row.less","less/forms.less","less/mixins/forms.less","less/buttons.less","less/mixins/buttons.less","less/mixins/opacity.less","less/component-animations.less","less/dropdowns.less","less/mixins/nav-divider.less","less/mixins/reset-filter.less","less/button-groups.less","less/mixins/border-radius.less","less/input-groups.less","less/navs.less","less/navbar.less","less/mixins/nav-vertical-align.less","less/utilities.less","less/breadcrumbs.less","less/pagination.less","less/mixins/pagination.less","less/pager.less","less/labels.less","less/mixins/labels.less","less/badges.less","less/jumbotron.less","less/thumbnails.less","less/alerts.less","less/mixins/alerts.less","less/progress-bars.less","less/mixins/gradients.less","less/mixins/progress-bar.less","less/media.less","less/list-group.less","less/mixins/list-group.less","less/panels.less","less/mixins/panels.less","less/responsive-embed.less","less/wells.less","less/close.less","less/modals.less","less/tooltip.less","less/mixins/reset-text.less","less/popovers.less","less/carousel.less","less/mixins/clearfix.less","less/mixins/center-block.less","less/mixins/hide-text.less","less/responsive-utilities.less","less/mixins/responsive-visibility.less"],"names":[],"mappings":"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DG,edkkDH,MAAA,aclkDG,edqkDH,MAAA,acrkDG,edwkDH,MAAA,IcxkDG,ed2kDH,MAAA,ac3kDG,ed8kDH,MAAA,ac9kDG,edilDH,MAAA,IcjlDG,edolDH,MAAA,ac/kDG,edklDH,MAAA,YcjmDG,edomDH,MAAA,KcpmDG,gBdumDH,KAAA,KcvmDG,gBd0mDH,KAAA,ac1mDG,gBd6mDH,KAAA,ac7mDG,edgnDH,KAAA,IchnDG,edmnDH,KAAA,acnnDG,edsnDH,KAAA,actnDG,edynDH,KAAA,IcznDG,ed4nDH,KAAA,ac5nDG,ed+nDH,KAAA,ac/nDG,edkoDH,KAAA,IcloDG,edqoDH,KAAA,achoDG,edmoDH,KAAA,YcpnDG,edunDH,KAAA,KcvnDG,kBd0nDH,YAAA,Kc1nDG,kBd6nDH,YAAA,ac7nDG,kBdgoDH,YAAA,achoDG,iBdmoDH,YAAA,IcnoDG,iBdsoDH,YAAA,actoDG,iBdyoDH,YAAA,aczoDG,iBd4oDH,YAAA,Ic5oDG,iBd+oDH,YAAA,ac/oDG,iBdkpDH,YAAA,aclpDG,iBdqpDH,YAAA,IcrpDG,iBdwpDH,YAAA,acxpDG,iBd2pDH,YAAA,Yc7rDG,iBACE,YAAA,EAOJ,yBACE,UAAA,WAAA,WAAA,WAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,Ud2rDD,MAAA,Kc3rDC,Wd8rDD,MAAA,Kc9rDC,WdisDD,MAAA,acjsDC,WdosDD,MAAA,acpsDC,UdusDD,MAAA,IcvsDC,Ud0sDD,MAAA,ac1sDC,Ud6sDD,MAAA,ac7sDC,UdgtDD,MAAA,IchtDC,UdmtDD,MAAA,acntDC,UdstDD,MAAA,acttDC,UdytDD,MAAA,IcztDC,Ud4tDD,MAAA,ac7sDC,UdgtDD,MAAA,YchtDC,gBdmtDD,MAAA,KcntDC,gBdstDD,MAAA,acttDC,gBdytDD,MAAA,acztDC,ed4tDD,MAAA,Ic5tDC,ed+tDD,MAAA,ac/tDC,edkuDD,MAAA,acluDC,edquDD,MAAA,IcruDC,edwuDD,MAAA,acxuDC,ed2uDD,MAAA,ac3uDC,ed8uDD,MAAA,Ic9uDC,edivDD,MAAA,ac5uDC,ed+uDD,MAAA,Yc9vDC,ediwDD,MAAA,KcjwDC,gBdowDD,KAAA,KcpwDC,gBduwDD,KAAA,acvwDC,gBd0wDD,KAAA,ac1wDC,ed6wDD,KAAA,Ic7wDC,edgxDD,KAAA,achxDC,edmxDD,KAAA,acnxDC,edsxDD,KAAA,IctxDC,edyxDD,KAAA,aczxDC,ed4xDD,KAAA,ac5xDC,ed+xDD,KAAA,Ic/xDC,edkyDD,KAAA,ac7xDC,edgyDD,KAAA,YcjxDC,edoxDD,KAAA,KcpxDC,kBduxDD,YAAA,KcvxDC,kBd0xDD,YAAA,ac1xDC,kBd6xDD,YAAA,ac7xDC,iBdgyDD,YAAA,IchyDC,iBdmyDD,YAAA,acnyDC,iBdsyDD,YAAA,actyDC,iBdyyDD,YAAA,IczyDC,iBd4yDD,YAAA,ac5yDC,iBd+yDD,YAAA,ac/yDC,iBdkzDD,YAAA,IclzDC,iBdqzDD,YAAA,acrzDC,iBdwzDD,YAAA,YY/yDD,iBE3CE,YAAA,GAQF,yBACE,UAAA,WAAA,WAAA,WAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,Udy1DD,MAAA,Kcz1DC,Wd41DD,MAAA,Kc51DC,Wd+1DD,MAAA,ac/1DC,Wdk2DD,MAAA,acl2DC,Udq2DD,MAAA,Icr2DC,Udw2DD,MAAA,acx2DC,Ud22DD,MAAA,ac32DC,Ud82DD,MAAA,Ic92DC,Udi3DD,MAAA,acj3DC,Udo3DD,MAAA,acp3DC,Udu3DD,MAAA,Icv3DC,Ud03DD,MAAA,ac32DC,Ud82DD,MAAA,Yc92DC,gBdi3DD,MAAA,Kcj3DC,gBdo3DD,MAAA,acp3DC,gBdu3DD,MAAA,acv3DC,ed03DD,MAAA,Ic13DC,ed63DD,MAAA,ac73DC,edg4DD,MAAA,ach4DC,edm4DD,MAAA,Icn4DC,eds4DD,MAAA,act4DC,edy4DD,MAAA,acz4DC,ed44DD,MAAA,Ic54DC,ed+4DD,MAAA,ac14DC,ed64DD,MAAA,Yc55DC,ed+5DD,MAAA,Kc/5DC,gBdk6DD,KAAA,Kcl6DC,gBdq6DD,KAAA,acr6DC,gBdw6DD,KAAA,acx6DC,ed26DD,KAAA,Ic36DC,ed86DD,KAAA,ac96DC,edi7DD,KAAA,acj7DC,edo7DD,KAAA,Icp7DC,edu7DD,KAAA,acv7DC,ed07DD,KAAA,ac17DC,ed67DD,KAAA,Ic77DC,edg8DD,KAAA,ac37DC,ed87DD,KAAA,Yc/6DC,edk7DD,KAAA,Kcl7DC,kBdq7DD,YAAA,Kcr7DC,kBdw7DD,YAAA,acx7DC,kBd27DD,YAAA,ac37DC,iBd87DD,YAAA,Ic97DC,iBdi8DD,YAAA,acj8DC,iBdo8DD,YAAA,acp8DC,iBdu8DD,YAAA,Icv8DC,iBd08DD,YAAA,ac18DC,iBd68DD,YAAA,ac78DC,iBdg9DD,YAAA,Ich9DC,iBdm9DD,YAAA,acn9DC,iBds9DD,YAAA,YY18DD,iBE9CE,YAAA,GAQF,0BACE,UAAA,WAAA,WAAA,WAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,UAAA,Udu/DD,MAAA,Kcv/DC,Wd0/DD,MAAA,Kc1/DC,Wd6/DD,MAAA,ac7/DC,WdggED,MAAA,achgEC,UdmgED,MAAA,IcngEC,UdsgED,MAAA,actgEC,UdygED,MAAA,aczgEC,Ud4gED,MAAA,Ic5gEC,Ud+gED,MAAA,ac/gEC,UdkhED,MAAA,aclhEC,UdqhED,MAAA,IcrhEC,UdwhED,MAAA,aczgEC,Ud4gED,MAAA,Yc5gEC,gBd+gED,MAAA,Kc/gEC,gBdkhED,MAAA,aclhEC,gBdqhED,MAAA,acrhEC,edwhED,MAAA,IcxhEC,ed2hED,MAAA,ac3hEC,ed8hED,MAAA,ac9hEC,ediiED,MAAA,IcjiEC,edoiED,MAAA,acpiEC,eduiED,MAAA,acviEC,ed0iED,MAAA,Ic1iEC,ed6iED,MAAA,acxiEC,ed2iED,MAAA,Yc1jEC,ed6jED,MAAA,Kc7jEC,gBdgkED,KAAA,KchkEC,gBdmkED,KAAA,acnkEC,gBdskED,KAAA,actkEC,edykED,KAAA,IczkEC,ed4kED,KAAA,ac5kEC,ed+kED,KAAA,ac/kEC,edklED,KAAA,IcllEC,edqlED,KAAA,acrlEC,edwlED,KAAA,acxlEC,ed2lED,KAAA,Ic3lEC,ed8lED,KAAA,aczlEC,ed4lED,KAAA,Yc7kEC,e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pC,cAGA,ejB8DA,wBACQ,OAAA,YJ65FT,OAAA,kBmB56FG,mBAAA,KlBw7FM,WAAY,KkBt7FhB,QAAA,IASN,eC3DE,yBACA,eAAA,KpBo+FD,aoBj+FC,MAAA,KnB6+FA,iBAAkB,KmB3+FhB,aAAA,KpBq+FH,mBoBn+FO,mBAEN,MAAA,KACE,iBAAA,QACA,aAAA,QpBo+FH,mBoBj+FC,MAAA,KnB6+FA,iBAAkB,QAClB,aAAc,QmBz+FR,oBADJ,oBpBo+FH,mCoBj+FG,MAAA,KnB6+FF,iBAAkB,QAClB,aAAc,QmBz+FN,0BnB++FV,0BAHA,0BmB7+FM,0BnB++FN,0BAHA,0BDFC,yCoB3+FK,yCnB++FN,yCmB1+FE,MAAA,KnBk/FA,iBAAkB,QAClB,aAAc,QmB3+FZ,oBpBm+FH,oBoBn+FG,mCnBg/FF,iBAAkB,KmB5+FV,4BnBi/FV,4BAHA,4BDHC,6BCOD,6BAHA,6BkB99FA,sCClBM,sCnBi/FN,sCmB3+FI,iBAAA,KACA,aAAA,KDcJ,oBC9DE,MAAA,KACA,iBAAA,KpB6hGD,aoB1hGC,MAAA,KnBsiGA,iBAAkB,QmBpiGhB,aAAA,QpB8hGH,mBoB5hGO,mBAEN,MAAA,KACE,iBAAA,QACA,aAAA,QpB6hGH,mBoB1hGC,MAAA,KnBsiGA,iBAAkB,QAClB,aAAc,QmBliGR,oBADJ,oBpB6hGH,mCoB1hGG,MAAA,KnBsiGF,iBAAkB,QAClB,aAAc,QmBliGN,0BnBwiGV,0BAHA,0BmBtiGM,0BnBwiGN,0BAHA,0BDFC,yCoBpiGK,yCnBwiGN,yCmBniGE,MAAA,KnB2iGA,iBAAkB,QAClB,aAAc,QmBpiGZ,oBpB4hGH,oBoB5hGG,mCnByiGF,iBAAkB,KmBriGV,4BnB0iGV,4BAHA,4BDHC,6BCOD,6BAHA,6BkBphGA,sCCrBM,sCnB0iGN,sCmBpiGI,iBAAA,QACA,aAAA,QDkBJ,oBClEE,MAAA,QACA,iBAAA,KpBslGD,aoBnlGC,MAAA,KnB+lGA,iBAAkB,QmB7lGhB,aAAA,QpBulGH,mBoBrlGO,mBAEN,MAAA,KACE,iBAAA,QACA,aAAA,QpBslGH,mBoBnlGC,MAAA,KnB+lGA,iBAAkB,QAClB,aAAc,QmB3lGR,oBADJ,oBpBslGH,mCoBnlGG,MAAA,KnB+lGF,iBAAkB,QAClB,aAAc,QmB3lGN,0BnBimGV,0BAHA,0BmB/lGM,0BnBimGN,0BAHA,0BDFC,yCoB7lGK,yCnBimGN,yCmB5lGE,MAAA,KnBomGA,iBAAkB,QAClB,aAAc,QmB7lGZ,oBpBqlGH,oBoBrlGG,mCnBkmGF,iBAAkB,KmB9lGV,4BnBmmGV,4BAHA,4BDHC,6BCOD,6BAHA,6BkBzkGA,sCCzBM,sCnBmmGN,sCmB7lGI,iBAAA,QACA,aAAA,QDsBJ,oBCtEE,MAAA,QACA,iBAAA,KpB+oGD,UoB5oGC,MAAA,KnBwpGA,iBAAkB,QmBtpGhB,aAAA,QpBgpGH,gBoB9oGO,gBAEN,MAAA,KACE,iBAAA,QACA,aAAA,QpB+oGH,gBoB5oGC,MAAA,KnBwpGA,iBAAkB,QAClB,aAAc,QmBppGR,iBADJ,iBpB+oGH,gCoB5oGG,MAAA,KnBwpGF,iBAAkB,QAClB,aAAc,QmBppGN,uBnB0pGV,uBAHA,uBmBxpGM,uBnB0pGN,uBAHA,uBDFC,sCoBtpGK,sCnB0pGN,sCmBrpGE,MAAA,KnB6pGA,iBAAkB,QAClB,aAAc,QmBtpGZ,iBpB8oGH,iBoB9oGG,gCnB2pGF,iBAAkB,KmBvpGV,yBnB4pGV,yBAHA,yBDHC,0BCOD,0BAHA,0BkB9nGA,mCC7BM,mCnB4pGN,mCmBtpGI,iBAAA,QACA,aAAA,QD0BJ,iBC1EE,MAAA,QACA,iBAAA,KpBwsGD,aoBrsGC,MAAA,KnBitGA,iBAAkB,QmB/sGhB,aAAA,QpBysGH,mBoBvsGO,mBAEN,MAAA,KACE,iBAAA,QACA,aAAA,QpBwsGH,mBoBrsGC,MAAA,KnBitGA,iBAAkB,QAClB,aAAc,QmB7sGR,oBADJ,oBpBwsGH,mCoBrsGG,MAAA,KnBitGF,iBAAkB,QAClB,aAAc,QmB7sGN,0BnBmtGV,0BAHA,0BmBjtGM,0BnBmtGN,0BAHA,0BDFC,yCoB/sGK,yCnBmtGN,yCmB9sGE,MAAA,KnBstGA,iBAAkB,QAClB,aAAc,QmB/sGZ,oBpBusGH,oBoBvsGG,mCnBotGF,iBAAkB,KmBh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mBAKJ,qBAEE,MAAA,QvBvCA,cACC,QAAA,KAAA,K3By0KF,iBAAA,QkDxxKC,WAAY,IAAI,MAAM,KjDozKtB,2BAA4B,IiDjzK1B,0BAAA,IAHJ,mBAAA,mCAMM,cAAA,ElD2xKL,oCkDtxKG,oDjDkzKF,aAAc,IAAI,EiDhzKZ,cAAA,EvBtEL,4D3Bg2KF,4EkDpxKG,WAAA,EjDgzKF,uBAAwB,IiD9yKlB,wBAAA,IvBtEL,0D3B81KF,0EkD7yKC,cAAe,EvB1Df,2BAAA,IACC,0BAAA,IuB0FH,+EAEI,uBAAA,ElDixKH,wBAAA,EkD7wKC,wDlDgxKD,iBAAA,EC4BD,0BACE,iBAAkB,EiDryKpB,8BlD6wKC,ckD7wKD,gCjD0yKE,cAAe,EiD1yKjB,sCAQM,sBlD2wKL,wCC4BC,cAAe,K0Bx5Kf,aAAA,KuByGF,wDlDwxKC,0BC4BC,uBAAwB,IACxB,wBAAyB,IiDrzK3B,yFAoBQ,yFlD2wKP,2DkD5wKO,2DjDwyKN,uBAAwB,IACxB,wBAAyB,IAK3B,wGiDj0KA,wGjD+zKA,wGDtBC,wGCuBD,0EiDh0KA,0EjD8zKA,0EiDtyKU,0EjD8yKR,uBAAwB,IAK1B,uGiD30KA,uGjDy0KA,uGDtBC,uGCuBD,yEiD10KA,yEjDw0KA,yEiD5yKU,yEvB7HR,wBAAA,IuBiGF,sDlDwzKC,yBC4BC,2BAA4B,IAC5B,0BAA2B,IiD3yKrB,qFA1CR,qFAyCQ,wDlDsxKP,wDC4BC,2BAA4B,IAC5B,0BAA2B,IAG7B,oGDtBC,oGCwBD,oGiDj2KA,oGjD81KA,uEiDhzKU,uEjDkzKV,uEiDh2KA,uEjDs2KE,0BAA2B,IAG7B,mGDtBC,mGCwBD,mGiD32KA,mGjDw2KA,sEiDtzKU,sEjDwzKV,sEiD12KA,sEjDg3KE,2BAA4B,IiDrzK1B,0BlD8xKH,qCkDz1KD,0BAAA,qCA+DI,WAAA,IAAA,MAAA,KA/DJ,kDAAA,kDAmEI,WAAA,EAnEJ,uBAAA,yCjD83KE,OAAQ,EiDpzKA,+CjDwzKV,+CiDl4KA,+CjDo4KA,+CAEA,+CANA,+CDjBC,iECoBD,iEiDn4KA,iEjDq4KA,iEAEA,iEANA,iEAWE,YAAa,EiD9zKL,8CjDk0KV,8CiDh5KA,8CjDk5KA,8CAEA,8CANA,8CDjBC,gECoBD,gEiDj5KA,gEjDm5KA,gEAEA,gEANA,gEAWE,aAAc,EAIhB,+CiD95KA,+CjD45KA,+CiDr0KU,+CjDw0KV,iEiD/5KA,iEjD65KA,iEDtBC,iEC6BC,cAAe,EAEjB,8CiDt0KU,8CjDw0KV,8CiDx6KA,8CjDu6KA,gEDtBC,gECwBD,gEiDn0KI,gEACA,cAAA,EAUJ,yBACE,cAAA,ElDsyKD,OAAA,EkDlyKG,aACA,cAAA,KANJ,oBASM,cAAA,ElDqyKL,cAAA,IkDhyKG,2BlDmyKH,WAAA,IC4BD,4BiD3zKM,cAAA,EAKF,wDAvBJ,wDlDwzKC,WAAA,IAAA,MAAA,KkD/xKK,2BlDkyKL,WAAA,EmDrhLC,uDnDwhLD,cAAA,IAAA,MAAA,KmDrhLG,eACA,aAAA,KnDyhLH,8BmD3hLC,MAAA,KAMI,iBAAA,QnDwhLL,aAAA,KmDrhLK,0DACA,iBAAA,KAGJ,qCAEI,MAAA,QnDshLL,iBAAA,KmDviLC,yDnD0iLD,oBAAA,KmDviLG,eACA,aAAA,QnD2iLH,8BmD7iLC,MAAA,KAMI,iBAAA,QnD0iLL,aAAA,QmDviLK,0DACA,iBAAA,QAGJ,qCAEI,MAAA,QnDwiLL,iBAAA,KmDzjLC,yDnD4jLD,oBAAA,QmDzjLG,eACA,aAAA,QnD6jLH,8BmD/jLC,MAAA,QAMI,iBAAA,QnD4jLL,aAAA,QmDzjLK,0DACA,iBAAA,QAGJ,qCAEI,MAAA,QnD0jLL,iBAAA,QmD3kLC,yDnD8kLD,oBAAA,QmD3kLG,YACA,aAAA,QnD+kLH,2BmDjlLC,MAAA,QAMI,iBAAA,QnD8kLL,aAAA,QmD3kLK,uDACA,iBAAA,QAGJ,kCAEI,MAAA,QnD4kLL,iBAAA,QmD7lLC,sDnDgmLD,oBAAA,QmD7lLG,eACA,aAAA,QnDimLH,8BmDnmLC,MAAA,QAMI,iBAAA,QnDgmLL,aAAA,QmD7lLK,0DACA,iBAAA,QAGJ,qCAEI,MAAA,QnD8lLL,iBAAA,QmD/mLC,yDnDknLD,oBAAA,QmD/mLG,cACA,aAAA,QnDmnLH,6BmDrnLC,MAAA,QAMI,iBAAA,QnDknLL,aAAA,QmD/mLK,yDACA,iBAAA,QAGJ,oCAEI,MAAA,QnDgnLL,iBAAA,QoD/nLC,wDACA,oBAAA,QAEA,kBACA,SAAA,SpDkoLD,QAAA,MoDvoLC,OAAQ,EnDmqLR,QAAS,EACT,SAAU,OAEZ,yCmDzpLI,wBADA,yBAEA,yBACA,wBACA,SAAA,SACA,IAAA,EACA,OAAA,EpDkoLH,KAAA,EoD7nLC,MAAO,KACP,OAAA,KpD+nLD,OAAA,EoD1nLC,wBpD6nLD,eAAA,OqDvpLC,uBACA,eAAA,IAEA,MACA,WAAA,KACA,QAAA,KjDwDA,cAAA,KACQ,iBAAA,QJmmLT,OAAA,IAAA,MAAA,QqDlqLC,cAAe,IASb,mBAAA,MAAA,EAAA,IAAA,IAAA,gBACA,WAAA,MAAA,EAAA,IAAA,IAAA,gBAKJ,iBACE,aAAA,KACA,aAAA,gBAEF,SACE,QAAA,KACA,cAAA,ICtBF,SACE,QAAA,IACA,cAAA,IAEA,OACA,MAAA,MACA,UAAA,KjCRA,YAAA,IAGA,YAAA,ErBwrLD,MAAA,KsDhrLC,YAAA,EAAA,IAAA,EAAA,KrD4sLA,OAAQ,kBqD1sLN,QAAA,GjCbF,aiCeE,ajCZF,MAAA,KrBgsLD,gBAAA,KsD5qLC,OAAA,QACE,OAAA,kBACA,QAAA,GAEA,aACA,mBAAA,KtD8qLH,QAAA,EuDnsLC,OAAQ,QACR,WAAA,IvDqsLD,OAAA,EuDhsLC,YACA,SAAA,OAEA,OACA,SAAA,MACA,IAAA,EACA,MAAA,EACA,OAAA,EACA,KAAA,EAIA,QAAA,KvDgsLD,QAAA,KuD7rLC,SAAA,OnD+GA,2BAAA,MACI,QAAA,EAEI,0BAkER,mBAAA,kBAAA,IAAA,SAEK,cAAA,aAAA,IAAA,SACG,WAAA,UAAA,IAAA,SJghLT,kBAAA,kBuDnsLC,cAAA,kBnD2GA,aAAA,kBACI,UAAA,kBAEI,wBJ2lLT,kBAAA,euDvsLK,cAAe,eACnB,aAAA,eACA,UAAA,eAIF,mBACE,WAAA,OACA,WAAA,KvDwsLD,cuDnsLC,SAAU,SACV,MAAA,KACA,OAAA,KAEA,eACA,SAAA,SnDaA,iBAAA,KACQ,wBAAA,YmDZR,gBAAA,YtD+tLA,OsD/tLA,IAAA,MAAA,KAEA,OAAA,IAAA,MAAA,evDqsLD,cAAA,IuDjsLC,QAAS,EACT,mBAAA,EAAA,IAAA,IAAA,eACA,WAAA,EAAA,IAAA,IAAA,eAEA,gBACA,SAAA,MACA,IAAA,EACA,MAAA,EvDmsLD,OAAA,EuDjsLC,KAAA,ElCrEA,QAAA,KAGA,iBAAA,KkCmEA,qBlCtEA,OAAA,iBAGA,QAAA,EkCwEF,mBACE,OAAA,kBACA,QAAA,GAIF,cACE,QAAA,KvDmsLD,cAAA,IAAA,MAAA,QuD9rLC,qBACA,WAAA,KAKF,aACE,OAAA,EACA,YAAA,WAIF,YACE,SAAA,SACA,QAAA,KvD6rLD,cuD/rLC,QAAS,KAQP,WAAA,MACA,WAAA,IAAA,MAAA,QATJ,wBAaI,cAAA,EvDyrLH,YAAA,IuDrrLG,mCvDwrLH,YAAA,KuDlrLC,oCACA,YAAA,EAEA,yBACA,SAAA,SvDqrLD,IAAA,QuDnqLC,MAAO,KAZP,OAAA,KACE,SAAA,OvDmrLD,yBuDhrLD,cnDvEA,MAAA,MACQ,OAAA,KAAA,KmD2ER,eAAY,mBAAA,EAAA,IAAA,KAAA,evDkrLX,WAAA,EAAA,IAAA,KAAA,euD5qLD,UAFA,MAAA,OvDorLD,yBwDl0LC,UACA,MAAA,OCNA,SAEA,SAAA,SACA,QAAA,KACA,QAAA,MACA,YAAA,iBAAA,UAAA,MAAA,WACA,UAAA,KACA,WAAA,OACA,YAAA,IACA,YAAA,WACA,WAAA,KACA,WAAA,MACA,gBAAA,KACA,YAAA,KACA,eAAA,KACA,eAAA,ODHA,WAAA,OnCVA,aAAA,OAGA,UAAA,OrBy1LD,YAAA,OwD90LC,OAAA,iBnCdA,QAAA,ErBg2LD,WAAA,KwDj1LY,YAAmB,OAAA,kBxDq1L/B,QAAA,GwDp1LY,aAAmB,QAAA,IAAA,ExDw1L/B,WAAA,KwDv1LY,eAAmB,QAAA,EAAA,IxD21L/B,YAAA,IwD11LY,gBAAmB,QAAA,IAAA,ExD81L/B,WAAA,IwDz1LC,cACA,QAAA,EAAA,IACA,YAAA,KAEA,eACA,UAAA,MxD41LD,QAAA,IAAA,IwDx1LC,MAAO,KACP,WAAA,OACA,iBAAA,KACA,cAAA,IAEA,exD01LD,SAAA,SwDt1LC,MAAA,EACE,OAAA,EACA,aAAA,YACA,aAAA,MAEA,4BxDw1LH,OAAA,EwDt1LC,KAAA,IACE,YAAA,KACA,aAAA,IAAA,IAAA,EACA,iBAAA,KAEA,iCxDw1LH,MAAA,IwDt1LC,OAAA,EACE,cAAA,KACA,aAAA,IAAA,IAAA,EACA,iBAAA,KAEA,kCxDw1LH,OAAA,EwDt1LC,KAAA,IACE,cAAA,KACA,aAAA,IAAA,IAAA,EACA,iBAAA,KAEA,8BxDw1LH,IAAA,IwDt1LC,KAAA,EACE,WAAA,KACA,aAAA,IAAA,IAAA,IAAA,EACA,mBAAA,KAEA,6BxDw1LH,IAAA,IwDt1LC,MAAA,EACE,WAAA,KACA,aAAA,IAAA,EAAA,IAAA,IACA,kBAAA,KAEA,+BxDw1LH,IAAA,EwDt1LC,KAAA,IACE,YAAA,KACA,aAAA,EAAA,IAAA,IACA,oBAAA,KAEA,oCxDw1LH,IAAA,EwDt1LC,MAAA,IACE,WAAA,KACA,aAAA,EAAA,IAAA,IACA,oBAAA,KAEA,qCxDw1LH,IAAA,E0Dr7LC,KAAM,IACN,WAAA,KACA,aAAA,EAAA,IAAA,IACA,oBAAA,KAEA,SACA,SAAA,SACA,IAAA,EDXA,KAAA,EAEA,QAAA,KACA,QAAA,KACA,UAAA,MACA,QAAA,IACA,YAAA,iBAAA,UAAA,MAAA,WACA,UAAA,KACA,WAAA,OACA,YAAA,IACA,YAAA,WACA,WAAA,KACA,WAAA,MACA,gBAAA,KACA,YAAA,KACA,eAAA,KCAA,eAAA,OAEA,WAAA,OACA,aAAA,OAAA,UAAA,OACA,YAAA,OACA,iBAAA,KACA,wBAAA,YtD8CA,gBAAA,YACQ,OAAA,IAAA,MAAA,KJq5LT,OAAA,IAAA,MAAA,e0Dh8LC,cAAA,IAAY,mBAAA,EAAA,IAAA,KAAA,e1Dm8Lb,WAAA,EAAA,IAAA,KAAA,e0Dl8La,WAAA,KACZ,aAAY,WAAA,MACZ,eAAY,YAAA,KAGd,gBACE,WAAA,KAEA,cACA,YAAA,MAEA,e1Dw8LD,QAAA,IAAA,K0Dr8LC,OAAQ,EACR,UAAA,K1Du8LD,iBAAA,Q0D/7LC,cAAA,IAAA,MAAA,QzD49LA,cAAe,IAAI,IAAI,EAAE,EyDz9LvB,iBACA,QAAA,IAAA,KAEA,gBACA,sB1Di8LH,SAAA,S0D97LC,QAAS,MACT,MAAA,E1Dg8LD,OAAA,E0D97LC,aAAc,YACd,aAAA,M1Di8LD,gB0D57LC,aAAA,KAEE,sBACA,QAAA,GACA,aAAA,KAEA,oB1D87LH,OAAA,M0D77LG,KAAA,IACE,YAAA,MACA,iBAAA,KACA,iBAAA,gBACA,oBAAA,E1Dg8LL,0B0D57LC,OAAA,IACE,YAAA,MACA,QAAA,IACA,iBAAA,KACA,oBAAA,EAEA,sB1D87LH,IAAA,I0D77LG,KAAA,MACE,WAAA,MACA,mBAAA,KACA,mBAAA,gBACA,kBAAA,E1Dg8LL,4B0D57LC,OAAA,MACE,KAAA,IACA,QAAA,IACA,mBAAA,KACA,kBAAA,EAEA,uB1D87LH,IAAA,M0D77LG,KAAA,IACE,YAAA,MACA,iBAAA,EACA,oBAAA,KACA,oBAAA,gB1Dg8LL,6B0D37LC,IAAA,IACE,YAAA,MACA,QAAA,IACA,iBAAA,EACA,oBAAA,KAEA,qB1D67LH,IAAA,I0D57LG,MAAA,MACE,WAAA,MACA,mBAAA,EACA,kBAAA,KACA,kBAAA,gB1D+7LL,2B2DvjMC,MAAO,IACP,OAAA,M3DyjMD,QAAA,I2DtjMC,mBAAoB,EACpB,kBAAA,KAEA,U3DwjMD,SAAA,S2DrjMG,gBACA,SAAA,SvD6KF,MAAA,KACK,SAAA,OJ64LN,sB2DlkMC,SAAU,S1D+lMV,QAAS,K0DjlML,mBAAA,IAAA,YAAA,K3DwjML,cAAA,IAAA,YAAA,K2D9hMC,WAAA,IAAA,YAAA,KvDmKK,4BAFL,0BAGQ,YAAA,EA3JA,qDA+GR,sBAEQ,mBAAA,kBAAA,IAAA,YJi7LP,cAAA,aAAA,IAAA,Y2D5jMG,WAAA,UAAA,IAAA,YvDmHJ,4BAAA,OACQ,oBAAA,OuDjHF,oBAAA,O3D+jML,YAAA,OI/8LD,mCHy+LA,2BGx+LQ,KAAA,EuD5GF,kBAAA,sB3DgkML,UAAA,sBC2BD,kCADA,2BG/+LA,KAAA,EACQ,kBAAA,uBuDtGF,UAAA,uBArCN,6B3DumMD,gC2DvmMC,iC1DkoME,KAAM,E0DrlMN,kBAAA,mB3D+jMH,UAAA,oBAGA,wB2D/mMD,sBAAA,sBAsDI,QAAA,MAEA,wB3D6jMH,KAAA,E2DzjMG,sB3D4jMH,sB2DxnMC,SAAU,SA+DR,IAAA,E3D4jMH,MAAA,KC0BD,sB0DllMI,KAAA,KAnEJ,sBAuEI,KAAA,MAvEJ,2BA0EI,4B3D2jMH,KAAA,E2DljMC,6BACA,KAAA,MAEA,8BACA,KAAA,KtC3FA,kBsC6FA,SAAA,SACA,IAAA,EACA,OAAA,EACA,KAAA,EACA,MAAA,I3DsjMD,UAAA,K2DjjMC,MAAA,KdnGE,WAAA,OACA,YAAA,EAAA,IAAA,IAAA,eACA,iBAAA,cAAA,OAAA,kBACA,QAAA,G7CwpMH,uB2DrjMC,iBAAA,sEACE,iBAAA,iEACA,iBAAA,uFdxGA,iBAAA,kEACA,OAAA,+GACA,kBAAA,SACA,wBACA,MAAA,E7CgqMH,KAAA,K2DvjMC,iBAAA,sE1DmlMA,iBAAiB,iE0DjlMf,iBAAA,uFACA,iBAAA,kEACA,OAAA,+GtCvHF,kBAAA,SsCyFF,wB3DylMC,wBC4BC,MAAO,KACP,gBAAiB,KACjB,OAAQ,kB0DhlMN,QAAA,EACA,QAAA,G3D2jMH,0C2DnmMD,2CA2CI,6BADA,6B1DqlMF,SAAU,S0DhlMR,IAAA,IACA,QAAA,E3DwjMH,QAAA,a2DxmMC,WAAY,MAqDV,0CADA,6B3DyjMH,KAAA,I2D7mMC,YAAa,MA0DX,2CADA,6BAEA,MAAA,IACA,aAAA,MAME,6BADF,6B3DsjMH,MAAA,K2DjjMG,OAAA,KACE,YAAA,M3DmjML,YAAA,E2DxiMC,oCACA,QAAA,QAEA,oCACA,QAAA,QAEA,qBACA,SAAA,SACA,OAAA,K3D2iMD,KAAA,I2DpjMC,QAAS,GAYP,MAAA,IACA,aAAA,EACA,YAAA,KACA,WAAA,OACA,WAAA,KAEA,wBACA,QAAA,aAWA,MAAA,KACA,OAAA,K3DiiMH,OAAA,I2DhkMC,YAAa,OAkCX,OAAA,QACA,iBAAA,OACA,iBAAA,cACA,OAAA,IAAA,MAAA,K3DiiMH,cAAA,K2DzhMC,6BACA,MAAA,KACA,OAAA,KACA,OAAA,EACA,iBAAA,KAEA,kBACA,SAAA,SACA,MAAA,IACA,OAAA,K3D4hMD,KAAA,I2D3hMC,QAAA,GACE,YAAA,K3D6hMH,eAAA,K2Dp/LC,MAAO,KAhCP,WAAA,O1DijMA,YAAa,EAAE,IAAI,IAAI,eAEzB,uB0D9iMM,YAAA,KAEA,oCACA,0C3DshMH,2C2D9hMD,6BAAA,6BAYI,MAAA,K3DshMH,OAAA,K2DliMD,WAAA,M1D8jME,UAAW,KDxBZ,0C2DjhMD,6BACE,YAAA,MAEA,2C3DmhMD,6B2D/gMD,aAAA,M3DkhMC,kBACF,MAAA,I4DhxMC,KAAA,I3D4yME,eAAgB,KAElB,qBACE,OAAQ,MAkBZ,qCADA,sCADA,mBADA,oBAXA,gBADA,iBAOA,uBADA,wBADA,iBADA,kBADA,wBADA,yBASA,mCADA,oC2DvzME,oBAAA,qBAAA,oBAAA,qB3D8zMF,WADA,YAOA,uBADA,wBADA,qBADA,sBADA,cADA,e2Dl0MI,a3Dw0MJ,cDvBC,kB4DhzMG,mB3DwzMJ,WADA,YAwBE,QAAS,MACT,QAAS,IASX,qCADA,mBANA,gBAGA,uBADA,iBADA,wBAIA,mCDhBC,oB6Dl1MC,oB5Dq2MF,W+B/1MA,uBhCu0MC,qB4D/zMG,cChBF,aACA,kB5Dk2MF,W+Bx1ME,MAAO,KhC40MR,cgCz0MC,QAAS,MACT,aAAA,KhC20MD,YAAA,KgCl0MC,YhCq0MD,MAAA,gBgCl0MC,WhCq0MD,MAAA,egCl0MC,MhCq0MD,QAAA,e8D51MC,MACA,QAAA,gBAEA,WACA,WAAA,O9B8BF,WACE,KAAA,EAAA,EAAA,EhCm0MD,MAAA,YgC5zMC,YAAa,KACb,iBAAA,YhC8zMD,OAAA,E+D91MC,Q/Di2MD,QAAA,eC4BD,OACE,SAAU,M+Dt4MV,chE+2MD,MAAA,aC+BD,YADA,YADA,YADA,YAIE,QAAS,e+Dv5MT,kBhEy4MC,mBgEx4MD,yBhEo4MD,kB+Dr1MD,mBA6IA,yB9D+tMA,kBACA,mB8Dp3ME,yB9Dg3MF,kBACA,mBACA,yB+D15MY,QAAA,eACV,yBAAU,YhE64MT,QAAA,gBC4BD,iB+Dv6MU,QAAA,gBhEg5MX,c+D/1MG,QAAS,oB/Dm2MV,c+Dr2MC,c/Ds2MH,QAAA,sB+Dj2MG,yB/Dq2MD,kBACF,QAAA,iB+Dj2MG,yB/Dq2MD,mBACF,QAAA,kBgEn6MC,yBhEu6MC,yBgEt6MD,QAAA,wBACA,+CAAU,YhE26MT,QAAA,gBC4BD,iB+Dr8MU,QAAA,gBhE86MX,c+Dx2MG,QAAS,oB/D42MV,c+D92MC,c/D+2MH,QAAA,sB+D12MG,+C/D82MD,kBACF,QAAA,iB+D12MG,+C/D82MD,mBACF,QAAA,kBgEj8MC,+ChEq8MC,yBgEp8MD,QAAA,wBACA,gDAAU,YhEy8MT,QAAA,gBC4BD,iB+Dn+MU,QAAA,gBhE48MX,c+Dj3MG,QAAS,oB/Dq3MV,c+Dv3MC,c/Dw3MH,QAAA,sB+Dn3MG,gD/Du3MD,kBACF,QAAA,iB+Dn3MG,gD/Du3MD,mBACF,QAAA,kBgE/9MC,gDhEm+MC,yBgEl+MD,QAAA,wBACA,0BAAU,YhEu+MT,QAAA,gBC4BD,iB+DjgNU,QAAA,gBhE0+MX,c+D13MG,QAAS,oB/D83MV,c+Dh4MC,c/Di4MH,QAAA,sB+D53MG,0B/Dg4MD,kBACF,QAAA,iB+D53MG,0B/Dg4MD,mBACF,QAAA,kBgEr/MC,0BhEy/MC,yBACF,QAAA,wBgE1/MC,yBhE8/MC,WACF,QAAA,gBgE//MC,+ChEmgNC,WACF,QAAA,gBgEpgNC,gDhEwgNC,WACF,QAAA,gBAGA,0B+Dn3MC,WA4BE,QAAS,gBC5LX,eAAU,QAAA,eACV,aAAU,ehE4hNT,QAAA,gBC4BD,oB+DtjNU,QAAA,gBhE+hNX,iB+Dj4MG,QAAS,oBAMX,iB/D83MD,iB+Dz2MG,QAAS,sB/D82MZ,qB+Dl4MC,QAAS,e/Dq4MV,a+D/3MC,qBAcE,QAAS,iB/Ds3MZ,sB+Dn4MC,QAAS,e/Ds4MV,a+Dh4MC,sBAOE,QAAS,kB/D83MZ,4B+D/3MC,QAAS,eCpLT,ahEujNC,4BACF,QAAA,wBC6BD,aACE,cACE,QAAS"}