├── .gitattributes ├── .gitignore ├── LICENSE ├── MANIFEST.in ├── README.md ├── demo_TF_MIST.py ├── model_log ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-37.pyc │ ├── modellog.cpython-37.pyc │ └── modellog_web.cpython-37.pyc ├── init_db.sql ├── metric_list1.pkl ├── metric_list2.pkl ├── model_log.db ├── modellog.py ├── modellog_web.py ├── static │ ├── css │ │ ├── admin.css │ │ ├── amazeui.min.css │ │ ├── app.css │ │ ├── bootstrap.min.css │ │ └── style.css │ ├── fonts │ │ ├── FontAwesome.otf │ │ ├── fontawesome-webfont.eot │ │ ├── fontawesome-webfont.ttf │ │ ├── fontawesome-webfont.woff │ │ └── fontawesome-webfont.woff2 │ ├── i │ │ ├── app-icon72x72@2x.png │ │ ├── examples │ │ │ ├── admin-chrome.png │ │ │ ├── admin-firefox.png │ │ │ ├── admin-ie.png │ │ │ ├── admin-opera.png │ │ │ ├── admin-safari.png │ │ │ ├── adminPage.png │ │ │ ├── blogPage.png │ │ │ ├── landing.png │ │ │ ├── landingPage.png │ │ │ ├── loginPage.png │ │ │ └── sidebarPage.png │ │ ├── favicon.png │ │ └── startup-640x1096.png │ ├── img │ │ ├── logo.png │ │ └── syncfusion-icons-white.png │ └── js │ │ ├── amazeui.min.js │ │ ├── app.js │ │ ├── echarts.min.js │ │ ├── gsap.min.js │ │ ├── iscroll.js │ │ ├── jquery.min.js │ │ └── script.js ├── templates │ ├── alert.html │ ├── index.html │ └── model_detail.html └── tf_param.pkl └── setup.py /.gitattributes: -------------------------------------------------------------------------------- 1 | *.js linguist-language=python 2 | *.css linguist-language=python 3 | *.png linguist-language=python 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include model_log * -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![img](https://gitee.com/kkweishe/images1/raw/master/ML/wechat/model_log_logo.png) 2 | 3 | 4 | 5 | ### 1. Model Log 介绍 6 | 7 | Model Log 是一款基于 Python3 的轻量级机器学习(Machine Learning)、深度学习(Deep Learning)模型训练评估指标可视化工具,与 TensorFlow、Pytorch、PaddlePaddle结合使用,可以记录模型训练过程当中的**超参数、Loss、Accuracy、Precision、F1值等,并以曲线图的形式进行展现对比**,轻松三步即可实现。 8 | 9 | 通过调节超参数的方式多次训练模型,并使用 Model Log 工具进行记录,可以很直观的进行模型对比,堪称调参神器。以下是使用工具后模型训练时 Loss 的变化曲线图。访问线上体验版:[http://mantchs.com/model_log.html](http://mantchs.com/model_log.html) 10 | 11 | ![img](https://gitee.com/kkweishe/images1/raw/master/ML/wechat/loss.gif) 12 | 13 | 14 | 15 | 通过上图可以很清晰的看出两个模型的训练效果,而且在表格当中高亮显示修改过的超参数,方便进行模型分析。 16 | 17 | 18 | 19 | ### 2. Model Log 特性 20 | 21 | - 轻量级、无需任何配置、极简API、开箱即用。 22 | - 只需要把模型的超参数和评估指标数据通过API添加即可,轻松三步即可实现。 23 | - 高亮显示修改过的超参数,方便进行模型分析。 24 | - 自动检测和获取正在训练的模型数据,并进行可视化,无需人工参与。 25 | - 使用 SQLite 轻量级本地数据库存储,可供多个用户同时使用,保证每个用户看到的数据是独立的。 26 | - 可视化组件采用 Echarts 框架,交互式曲线图设计,可清晰看到每个 epoch 周期的指标数据和变化趋势。 27 | 28 | 29 | 30 | ### 3. Model Log 演示地址 31 | 32 | 访问线上体验版:[http://mantchs.com/model_log.html](http://mantchs.com/model_log.html) 33 | 34 | 35 | 36 | ### 4. Model Log 安装 37 | 38 | Python3 版本以上,通过 pip 进行安装即可。 39 | 40 | ```shell 41 | pip install model-log 42 | ``` 43 | 44 | 45 | 46 | **注意**:若安装的过程中出现以下情况,说明 **model-log** 命令已经安装到Python下的bin目录中,如果直接输入 model-log 可能会出现 command not found,可以直接到bin目录下执行。 47 | 48 | ![img](https://gitee.com/kkweishe/images1/raw/master/ML/wechat/mistake.png) 49 | 50 | 51 | 52 | 53 | 54 | ### 5. Model Log 使用 55 | 56 | #### 5.1 启动 web 端 57 | 58 | Model Log 安装成功后,Linux、Mac用户直接终端输入以下命令,Windows用户在cmd窗口输入: 59 | 60 | ```shell 61 | model-log 62 | ``` 63 | 64 | 默认启动 **5432端口**,可以在启动命令上使用参数 **-p=5000** 指定端口号。若提示命令不存在,可以直接到Python/3.7/bin目录下执行。 65 | 66 | 启动后可在浏览器输入网址进入:http://127.0.0.1:5432 67 | 68 | 也可访问线上体验版:[http://mantchs.com/model_log.html](http://mantchs.com/model_log.html) 69 | 70 | 71 | 72 | - web首页是项目列表,一个项目可以有多个模型,这些模型可以在曲线图中直观比较。 73 | 74 | - web 端会自动检测是否有新模型开始训练,如果有,直接会跳转到相应的 loss 等评价指标页,同时会自动获取指标数据进行呈现。 75 | 76 | - 可供多个用户使用,添加昵称即可,SQLite 轻量级本地数据库存储,保证每个用户看到的数据是独立的。 77 | 78 | - 通过点击曲线图下方的图例,可切换不同模型的评估曲线。 79 | 80 | ![img](https://gitee.com/kkweishe/images1/raw/master/ML/wechat/tuli.png) 81 | 82 | 83 | 84 | 85 | 86 | #### 5.2 Model Log API使用 87 | 88 | 轻松三步即可使用 89 | 90 | 91 | 92 | 1. **第一步**:先创建 ModelLog 类,并添加必要的属性 93 | 94 | ```python 95 | from model_log.modellog import ModelLog 96 | """ 97 | :param nick_name: str,昵称,多人使用下可起到数据隔离。 98 | :param project_name: str,项目名称。 99 | :param project_remark: str,项目备注,默认为空。 100 | 101 | 项目名称如不存在会新建 102 | """ 103 | model_log = ModelLog(nick_name='mantch', project_name='demo实体识别', project_remark='') 104 | 105 | """ 106 | :param model_name: str,模型名称 107 | """ 108 | model_log.add_model_name(model_name='BILSTM_CRF模型') 109 | 110 | """ 111 | :param remark: str,模型备注 112 | """ 113 | model_log.add_model_remark(remark='模型备注') 114 | 115 | """ 116 | :param param_dict: dict,训练参数字典 117 | :param param_type: str,参数类型,例如:TF参数、Word2Vec参数等。 118 | """ 119 | model_log.add_param(param_dict={'lr':0.01}, param_type='tf_param') 120 | ``` 121 | 122 | 123 | 124 | 2. **第二步**:模型训练的每次 epoch (周期)可以添加评估指标数据,评估指标可以进行以下选择。 125 | 126 | 第一次调用该 API 时,会把以上设置的数据(模型名称、备注等)持久化到 SQLite 数据库,并且 web 端会自动获取评估指标数据进行图形化展示。 127 | 128 | ```python 129 | """ 130 | :param metric_name: str,评估指标名称, 131 | 可选择['train_loss', 'test_loss', 'train_acc', 'test_acc', 'train_recall', 'test_recall', 'train_precision', 'test_precision', 'train_F1', 'test_F1'] 132 | 133 | :param metric_value: float,评估指标数值。 134 | :param epoch: int,训练周期 135 | 136 | metric_name 参数只可以选择以上十种 137 | 第一次调用该 API 时,会把以上设置的数据(模型名称、备注等)持久化到 SQLite 数据库,并且 web 端会自动获取数据进行图形化展示。 138 | 可以在每个 epoch 周期的最后使用该 API 添加训练集和测试集的评估指标,web 端会自动获取该数据。 139 | """ 140 | model_log.add_metric(metric_name='train_loss', metric_value=4.5646, epoch=1) 141 | ``` 142 | 143 | 144 | 145 | 3. **第三步**:模型训练完成后,可以添加最好的一次评估数据。 146 | 147 | ```python 148 | """ 149 | :param best_name: str,最佳评估指标名称, 150 | :param best_value: float,最佳评估指标数值。 151 | :param best_epoch: int,训练周期 152 | 153 | 添加当前模型训练中最佳的评估数据,一般放到模型训练的最后进行添加。 154 | """ 155 | model_log.add_best_result(best_name='best_loss', best_value=1.2122, best_epoch=30) 156 | model_log.finish_model() 157 | 158 | """ 159 | 关闭 SQLite 数据库连接 160 | """ 161 | model_log.close() 162 | ``` 163 | 164 | 165 | 166 | 167 | 168 | #### 5.3 Model Log 使用示例 169 | 170 | MIST手写数字识别:[https://github.com/NLP-LOVE/Model_Log/blob/master/demo_TF_MIST.py](https://github.com/NLP-LOVE/Model_Log/blob/master/demo_TF_MIST.py) 171 | 172 | 173 | 174 | ![img](https://gitee.com/kkweishe/images1/raw/master/ML/wechat/QRcode.gif) 175 | 176 | -------------------------------------------------------------------------------- /demo_TF_MIST.py: -------------------------------------------------------------------------------- 1 | 2 | # 下载MNIST数据并解压 3 | import os 4 | current_path = os.path.dirname(__file__) 5 | from six.moves import urllib 6 | import zipfile 7 | 8 | DATA_URL = 'http://mantchs.com/data/MNIST.zip' 9 | DATA_DIR = os.path.join(current_path, 'dataset') 10 | FILE = 'MNIST.zip' 11 | 12 | if not os.path.exists(DATA_DIR): 13 | os.mkdir(DATA_DIR) 14 | 15 | global n 16 | n = 0 17 | def reporthook(blocks_read,block_size,total_size): 18 | global n 19 | if not blocks_read: 20 | print("Connection opened") 21 | if blocks_read*block_size/1024.0 > n: 22 | n += 1000 23 | print("downloading MNIST: %d KB, totalsize: %d KB" % (blocks_read*block_size/1024.0,total_size/1024.0)) 24 | 25 | # 下载MNIST数据并解压 26 | filepath, _ = urllib.request.urlretrieve(DATA_URL, os.path.join(DATA_DIR, FILE), reporthook) 27 | with zipfile.ZipFile(os.path.join(DATA_DIR, FILE), 'r') as zip: 28 | zip.extractall(DATA_DIR) 29 | 30 | 31 | 32 | from tensorflow.examples.tutorials.mnist import input_data 33 | mnist = input_data.read_data_sets(DATA_DIR, one_hot=True) 34 | mnist_train_num = mnist.train.images.shape[0] 35 | 36 | import tensorflow as tf 37 | from model_log.modellog import ModelLog 38 | import numpy as np 39 | 40 | # Parameters 41 | learning_rate = 0.001 42 | epochs = 500 43 | batch_size = 1000 44 | early_stop = 10 45 | display_step = 1 46 | 47 | # Network Parameters 48 | n_hidden_1 = 128 # 1st layer number of neurons 49 | n_hidden_2 = 0 # 2nd layer number of neurons 50 | num_input = 784 # MNIST data input (img shape: 28*28) 51 | num_classes = 10 # MNIST total classes (0-9 digits) 52 | 53 | # 初始化 ModelLog 54 | model_log = ModelLog('mantch', 'MNIST手写数字识别') 55 | model_log.add_model_name('nn神经网络') 56 | 57 | params = {'learning_rate':learning_rate, 'epochs':epochs, 'batch_size':batch_size, 'n_hidden_1':n_hidden_1, 58 | 'n_hidden_2':n_hidden_2, 'num_input':num_input, 'num_classes':num_classes, 'early_stop':early_stop} 59 | model_log.add_param(params, 'tf_params') 60 | 61 | # tf Graph input 62 | X = tf.placeholder("float", [None, num_input]) 63 | Y = tf.placeholder("float", [None, num_classes]) 64 | 65 | # Store layers weight & bias 66 | weights = { 67 | 'h1': tf.Variable(tf.random_normal([num_input, n_hidden_1])), 68 | 'out1': tf.Variable(tf.random_normal([n_hidden_1, num_classes])) 69 | } 70 | biases = { 71 | 'b1': tf.Variable(tf.random_normal([n_hidden_1])), 72 | 'out': tf.Variable(tf.random_normal([num_classes])) 73 | } 74 | 75 | 76 | # Create model 77 | def neural_net(x): 78 | # Hidden fully connected layer with 256 neurons 79 | layer_1 = tf.add(tf.matmul(x, weights['h1']), biases['b1']) 80 | out_layer = tf.matmul(layer_1, weights['out1']) + biases['out'] 81 | return out_layer 82 | 83 | # Construct model 84 | logits = neural_net(X) 85 | prediction = tf.nn.softmax(logits) 86 | 87 | # Define loss and optimizer 88 | loss_op = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits( 89 | logits=logits, labels=Y)) 90 | optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate) 91 | train_op = optimizer.minimize(loss_op) 92 | 93 | # Evaluate model 94 | correct_pred = tf.equal(tf.argmax(prediction, 1), tf.argmax(Y, 1)) 95 | accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32)) 96 | 97 | # Initialize the variables (i.e. assign their default value) 98 | init = tf.global_variables_initializer() 99 | 100 | # Start training 101 | with tf.Session() as sess: 102 | 103 | # Run the initializer 104 | sess.run(init) 105 | best_loss = 100000 106 | best_epoch = 0 107 | 108 | for epoch in range(1, epochs + 1): 109 | 110 | loss = [] 111 | for i in range(int(mnist_train_num / batch_size)): 112 | batch_x, batch_y = mnist.train.next_batch(batch_size) 113 | # Run optimization op (backprop) 114 | _, batch_loss = sess.run([train_op, loss_op], feed_dict={X: batch_x, Y: batch_y}) 115 | loss.append(batch_loss) 116 | 117 | if epoch % display_step == 0 or epoch == 1: 118 | # Calculate batch loss and accuracy 119 | test_loss, acc = sess.run([loss_op, accuracy], feed_dict={X: mnist.test.images, 120 | Y: mnist.test.labels}) 121 | 122 | loss = np.mean(loss) 123 | print("Epoch " + str(epoch) + ", Minibatch Loss= " + \ 124 | "{:.4f}".format(loss) + ", Training Accuracy= " + \ 125 | "{:.3f}".format(acc)) 126 | 127 | # ModelLog 添加评估指标 128 | model_log.add_metric('train_loss', loss, epoch) 129 | model_log.add_metric('test_loss', test_loss, epoch) 130 | model_log.add_metric('test_acc', acc, epoch) 131 | 132 | 133 | if test_loss < best_loss: 134 | best_loss = test_loss 135 | best_epoch = epoch 136 | 137 | # 早停 138 | if epoch - best_epoch > 10: 139 | break 140 | 141 | 142 | # ModelLog 添加最好参数 143 | model_log.add_best_result('best_loss', best_loss, best_epoch) 144 | model_log.add_best_result('best_step', best_epoch, best_epoch) 145 | 146 | print("Optimization Finished!") 147 | 148 | # Calculate accuracy for MNIST test images 149 | print("Testing Accuracy:", \ 150 | sess.run(accuracy, feed_dict={X: mnist.test.images, 151 | Y: mnist.test.labels})) 152 | -------------------------------------------------------------------------------- /model_log/__init__.py: -------------------------------------------------------------------------------- 1 | print('n') -------------------------------------------------------------------------------- /model_log/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NLP-LOVE/Model_Log/fc0a46afe9702d6cb5ba508fac3a0dc3bd386e5f/model_log/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /model_log/__pycache__/modellog.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NLP-LOVE/Model_Log/fc0a46afe9702d6cb5ba508fac3a0dc3bd386e5f/model_log/__pycache__/modellog.cpython-37.pyc -------------------------------------------------------------------------------- /model_log/__pycache__/modellog_web.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NLP-LOVE/Model_Log/fc0a46afe9702d6cb5ba508fac3a0dc3bd386e5f/model_log/__pycache__/modellog_web.cpython-37.pyc -------------------------------------------------------------------------------- /model_log/init_db.sql: -------------------------------------------------------------------------------- 1 | 2 | drop table if exists project; 3 | drop table if exists sub_model; 4 | drop table if exists model_param; 5 | drop table if exists model_metric; 6 | drop table if exists best_result; 7 | 8 | 9 | create table project 10 | ( 11 | project_id integer not null 12 | constraint model_pk 13 | primary key autoincrement, 14 | project_name text not null, 15 | project_remark text, 16 | nick_name text, 17 | create_time datetime not null, 18 | del_flag integer default 0 not null 19 | ); 20 | 21 | create table model_metric 22 | ( 23 | metric_id integer not null 24 | constraint model_metric_pk 25 | primary key autoincrement, 26 | sub_model_id integer not null, 27 | metric_name text not null, 28 | metric_type text not null, 29 | epoch integer not null, 30 | metric_value float not null, 31 | create_time datetime not null 32 | ); 33 | 34 | create table model_param 35 | ( 36 | param_id integer not null 37 | constraint model_param_pk 38 | primary key autoincrement, 39 | sub_model_id integer not null, 40 | param_type text not null, 41 | param_name text not null, 42 | param_value text not null, 43 | create_time datetime not null 44 | ); 45 | 46 | create table sub_model 47 | ( 48 | sub_model_id integer not null 49 | constraint sub_model_pk 50 | primary key autoincrement, 51 | project_id integer not null, 52 | sub_model_sequence integer not null, 53 | sub_model_name text not null, 54 | sub_model_remark text, 55 | nick_name text, 56 | create_time datetime not null, 57 | del_flag integer default 0 not null 58 | ); 59 | 60 | create table best_result 61 | ( 62 | best_result_id integer not null constraint best_result_pk primary key autoincrement, 63 | sub_model_id integer not null, 64 | best_name text not null, 65 | best_value float not null, 66 | best_epoch integer not null, 67 | create_time datetime not null 68 | ); 69 | 70 | -------------------------------------------------------------------------------- /model_log/metric_list1.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NLP-LOVE/Model_Log/fc0a46afe9702d6cb5ba508fac3a0dc3bd386e5f/model_log/metric_list1.pkl -------------------------------------------------------------------------------- /model_log/metric_list2.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NLP-LOVE/Model_Log/fc0a46afe9702d6cb5ba508fac3a0dc3bd386e5f/model_log/metric_list2.pkl -------------------------------------------------------------------------------- /model_log/model_log.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NLP-LOVE/Model_Log/fc0a46afe9702d6cb5ba508fac3a0dc3bd386e5f/model_log/model_log.db -------------------------------------------------------------------------------- /model_log/modellog.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # _*_ coding: utf-8 _*_ 3 | # @Time : 2020/6/10 10:04 4 | # @Author : mantch 5 | # @Version:V 1.0 6 | # @desc : https://github.com/NLP-LOVE/Model_Log 7 | 8 | import time 9 | import sqlite3 10 | import os 11 | current_path = os.path.dirname(__file__) 12 | 13 | def check_str(s, type): 14 | if not isinstance(s, str): 15 | raise Exception(type + ' not string!') 16 | elif s == '': 17 | raise Exception(type + ' is null!') 18 | 19 | class ModelLog(object): 20 | 21 | """ 22 | :param nick_name: str,昵称,多人使用下可起到数据隔离。 23 | :param project_name: str,项目名称。 24 | :param project_remark: str,项目备注,默认为空。 25 | 26 | 项目名称如不存在会新建 27 | """ 28 | def __init__(self, nick_name, project_name, project_remark=''): 29 | 30 | self.conn = sqlite3.connect(os.path.join(current_path, 'model_log.db')) 31 | 32 | self.nick_name = nick_name 33 | self.project_name = project_name 34 | self.project_remark = project_remark 35 | self.start_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) 36 | self.is_add_model_data = True 37 | self.param_dict = {} 38 | self.model_name = '' 39 | self.remark = '' 40 | 41 | check_str(project_name, 'project_name') 42 | check_str(nick_name, 'nick_name') 43 | 44 | 45 | # 检查project name 是否存在 46 | def __is_exist_project_name(self, project_name): 47 | 48 | sql = "select 1 from project m where m.project_name = '%s' and m.nick_name='%s'"%(project_name, self.nick_name) 49 | project_table = self.conn.execute(sql).fetchall() 50 | 51 | if len(project_table) != 0: 52 | return True 53 | else: 54 | return False 55 | 56 | # 检查model name 是否存在 57 | def __is_exist_model_name(self, model_name, project_id): 58 | 59 | sql = "select 1 from sub_model sm where sm.project_id = %d and sm.sub_model_name = '%s'" % (project_id, model_name) 60 | project_table = self.conn.execute(sql).fetchall() 61 | 62 | if len(project_table) != 0: 63 | return True 64 | else: 65 | return False 66 | 67 | 68 | """ 69 | :param param_dict: dict,训练参数字典 70 | :param param_type: str,参数类型,例如:TF参数、word2vec参数等。 71 | """ 72 | def add_param(self, param_dict, param_type): 73 | 74 | check_str(param_type, 'param_type') 75 | 76 | self.param_dict[param_type] = param_dict 77 | 78 | """ 79 | :param model_name: str,模型名称 80 | """ 81 | def add_model_name(self, model_name): 82 | 83 | check_str(model_name, 'model_name') 84 | 85 | self.model_name = model_name 86 | 87 | """ 88 | :param remark: str,模型备注 89 | """ 90 | def add_model_remark(self, remark): 91 | 92 | check_str(remark, 'remark') 93 | 94 | self.remark = remark 95 | 96 | 97 | """ 98 | :param metric_name: str,评估指标名称,可选择['train_loss', 'test_loss', 'test_acc', 'test_recall', 'test_precision', 'test_F1'] 99 | :param metric_value: float,评估指标数值。 100 | :param epoch: int,训练周期 101 | 102 | 第一次调用该 API 时,会把以上设置的数据持久化到 SQLite 数据库。 103 | 可以在每个 epoch 周期的最后使用该 API添加训练集和测试集的评估指标,web端会自动获取该数据。 104 | """ 105 | def add_metric(self, metric_name, metric_value, epoch): 106 | 107 | check_str(metric_name, 'metric_name') 108 | 109 | if metric_name not in ['train_loss', 'test_loss', 'train_acc', 'test_acc', 'train_recall', 'test_recall', 'train_precision', 'test_precision', 'train_F1', 'test_F1']: 110 | raise Exception("Your metric_name:%s, not in ['train_loss', 'test_loss', 'test_acc', 'test_recall', 'test_precision', 'test_F1']" % (metric_name)) 111 | 112 | if self.is_add_model_data: 113 | self.__add_model_data() 114 | self.is_add_model_data = False 115 | 116 | create_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) 117 | sql = "insert into model_metric values (null, ?, ?, 'line', ?, ?, ?)" 118 | self.conn.execute(sql, (self.sub_model_id, metric_name, epoch, '%.4f'%(metric_value), create_time)) 119 | 120 | self.conn.commit() 121 | 122 | 123 | """ 124 | :param best_name: str,最佳评估指标名称, 125 | :param best_value: float,最佳评估指标数值。 126 | :param best_epoch: int,训练周期 127 | 128 | 添加当前模型训练中最佳的评估数据,一般放到模型训练的最后进行添加。 129 | """ 130 | def add_best_result(self, best_name, best_value, best_epoch): 131 | 132 | check_str(best_name, 'best_name') 133 | 134 | create_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) 135 | sql = "insert into best_result values (null, ?, ?, ?, ?, ?)" 136 | self.conn.execute(sql, (self.sub_model_id, best_name, '%.4f' % (best_value), best_epoch, create_time)) 137 | 138 | self.conn.commit() 139 | 140 | def finish_model(self): 141 | sql = f"update sub_model set is_finish=1 where sub_model_id={self.sub_model_id}" 142 | self.conn.execute(sql) 143 | 144 | self.conn.commit() 145 | 146 | 147 | # 检查model_name是否重复 148 | def __check_model_name(self, model_name, sub_model_count, project_id): 149 | 150 | if model_name == '': 151 | model_name = self.project_name + '_' + str(sub_model_count + 1) 152 | 153 | else: 154 | 155 | # 判断是否有model_name 156 | if self.__is_exist_model_name(model_name, project_id): 157 | model_name = model_name + '_' + str(sub_model_count + 1) 158 | else: 159 | model_name = self.model_name 160 | 161 | if self.__is_exist_model_name(model_name, project_id): 162 | return self.__check_model_name(model_name, sub_model_count, project_id) 163 | else: 164 | return model_name 165 | 166 | 167 | # 添加模型数据 168 | def __add_model_data(self): 169 | 170 | # 插入model 171 | if not self.__is_exist_project_name(self.project_name): 172 | sql = "insert into project values (null, ?, ?, ?, ?, 0)" 173 | self.conn.execute(sql, (self.project_name, self.project_remark, self.nick_name, self.start_time)) 174 | self.conn.commit() 175 | 176 | sql = "select project_id from project m where m.project_name = '%s' and m.nick_name='%s'"%(self.project_name, self.nick_name) 177 | project_id = self.conn.execute(sql).fetchall()[0][0] 178 | 179 | sql = "select count(1) from sub_model sm where sm.project_id = %d"%(project_id) 180 | sub_model_count = self.conn.execute(sql).fetchall()[0][0] 181 | 182 | # 插入sub model 183 | model_name = self.__check_model_name(self.model_name, sub_model_count, project_id) 184 | sql = "insert into sub_model values (null, ?, ?, ?, ?, ?, ?, 0, 0)" 185 | self.conn.execute(sql, (project_id, sub_model_count + 1, model_name, self.remark, self.nick_name, self.start_time)) 186 | self.conn.commit() 187 | 188 | sql = "select sub_model_id from sub_model sm where sm.project_id = ? and sm.sub_model_name = ?" 189 | self.sub_model_id = self.conn.execute(sql, (project_id, model_name)).fetchall()[0][0] 190 | 191 | # 插入model param 192 | for param_type, value in self.param_dict.items(): 193 | 194 | for param_name, param_value in value.items(): 195 | sql = "insert into model_param values (null, ?, ?, ?, ?, ?)" 196 | self.conn.execute(sql, (self.sub_model_id, param_type, param_name, str(param_value), self.start_time)) 197 | 198 | # db数据库初始化 199 | def __init_db(self): 200 | 201 | sql_script = open(os.path.join(current_path, 'init_db.sql'), 'r', encoding='utf-8').read() 202 | 203 | self.conn.executescript(sql_script) 204 | 205 | self.conn.commit() 206 | 207 | """ 208 | 关闭 SQLite 数据库连接 209 | """ 210 | def close(self): 211 | self.conn.close() 212 | 213 | 214 | if __name__ == '__main__': 215 | 216 | 217 | 218 | model_log = ModelLog('test项目', 'test备注') 219 | ''' 220 | # 训练参数 221 | tf_param = {} 222 | tf_param['model'] = 'BILSTM_Attention' 223 | tf_param['optimizer'] = 'adam' 224 | tf_param['num_classes'] = 10 225 | tf_param['lr'] = 0.001 226 | tf_param['training_steps'] = 1000000 227 | tf_param['display_step'] = 1 228 | tf_param['batch_size'] = 100 229 | tf_param['num_hidden'] = 128 230 | tf_param['embedding_dim'] = 128 231 | tf_param['drop_out'] = 0.5 232 | 233 | model_log.add_model_name('bilstm_crf') 234 | model_log.add_model_remark('bilstm_crf备注') 235 | model_log.add_param(tf_param, 'tf_param') 236 | 237 | for i in range(1, 21): 238 | model_log.add_diagram('train_loss', 25.0 - i, i) 239 | model_log.add_diagram('test_loss', 24.0 - i, i) 240 | model_log.add_diagram('test_acc', 0.3 + 0.02 * i, i) 241 | 242 | model_log.add_best_result('best_loss', 2.34, 20) 243 | model_log.add_best_result('best_acc', 0.99, 20) 244 | 245 | ''' 246 | 247 | #model_log.init_db() 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | -------------------------------------------------------------------------------- /model_log/modellog_web.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # _*_ coding: utf-8 _*_ 3 | # @Time : 2020/6/10 10:04 4 | # @Author : mantch 5 | # @Version:V 1.0 6 | # @desc : https://github.com/NLP-LOVE/Model_Log 7 | 8 | from flask import Flask, request, jsonify, redirect, session 9 | from flask import render_template 10 | import webbrowser 11 | import argparse 12 | import signal 13 | import sys 14 | import os 15 | import sqlite3 16 | import pickle 17 | from model_log.modellog import ModelLog 18 | 19 | current_path = os.path.dirname(__file__) 20 | app = Flask(__name__) 21 | app.config["SECRET_KEY"] = "model_log" 22 | page_size = 15 23 | 24 | 25 | # 数据库连接 26 | def get_conn(): 27 | return sqlite3.connect(os.path.join(current_path, 'model_log.db')) 28 | 29 | 30 | # 构造表头 31 | def generate_table_head(conn, project_id): 32 | sql = "select sm.sub_model_id, sm.sub_model_name, sm.sub_model_remark, sm.create_time, sm.is_finish from sub_model sm where sm.project_id=%d and sm.del_flag=0" % (project_id) 33 | sub_model_result = conn.execute(sql).fetchall() 34 | 35 | # 构造表头 36 | table_head = {} 37 | table_length = {} 38 | sub_model_param = {} 39 | 40 | for sub_model in sub_model_result: 41 | 42 | sql = "select mp.param_type, mp.param_name, mp.param_value from model_param mp where mp.sub_model_id=%d" % ( 43 | sub_model[0]) 44 | model_param_result = conn.execute(sql) 45 | 46 | for model_param in model_param_result: 47 | 48 | # 添加 table head 49 | if model_param[0] not in table_head: 50 | table_head[model_param[0]] = [model_param[1]] 51 | else: 52 | if model_param[1] not in table_head[model_param[0]]: 53 | table_head[model_param[0]].append(model_param[1]) 54 | 55 | # sub_model_param 56 | if sub_model[0] not in sub_model_param: 57 | sub_model_param[sub_model[0]] = {model_param[1]: model_param[2]} 58 | else: 59 | sub_model_param[sub_model[0]][model_param[1]] = model_param[2] 60 | 61 | for type, param_list in table_head.items(): 62 | table_length[type] = len(param_list) 63 | 64 | 65 | return sub_model_result, sub_model_param, table_head, table_length 66 | 67 | 68 | 69 | # 构造页面数据 70 | def generate_table_data(conn, table_head, sub_model_result, sub_model_param): 71 | table_data = [] 72 | best_head = [] 73 | best_data = {} 74 | first_param = {} 75 | modify_head = [] 76 | id = 0 77 | for i, sub_model in enumerate(sub_model_result): 78 | 79 | # 评估指标数据 80 | sql = "select br.best_name, br.best_value from best_result br where br.sub_model_id=%d" % (sub_model[0]) 81 | best_result = conn.execute(sql) 82 | dic = {} 83 | 84 | for best in best_result: 85 | if best[0] not in best_head: 86 | best_head.append(best[0]) 87 | 88 | dic[best[0]] = best[1] 89 | 90 | best_data[sub_model[0]] = dic 91 | 92 | id += 1 93 | dic = {} 94 | 95 | dic['id'] = id 96 | dic['sub_model_id'] = sub_model[0] 97 | dic['sub_model_name'] = sub_model[1] 98 | dic['sub_model_remark'] = sub_model[2] 99 | dic['create_time'] = sub_model[3] 100 | dic['finished_train'] = False if sub_model[4] == 0 else True 101 | 102 | # 超参数 103 | for _, param_list in table_head.items(): 104 | for param_name in param_list: 105 | 106 | try: 107 | dic[param_name] = sub_model_param[sub_model[0]][param_name] 108 | except: 109 | dic[param_name] = '' 110 | 111 | if i == 0: # 记录第一次训练的超参数 112 | first_param[param_name] = dic[param_name] 113 | else: 114 | if dic[param_name] != first_param[param_name]: 115 | modify_head.append(param_name) 116 | 117 | # 评估指标数据 118 | for name in best_head: 119 | try: 120 | dic[name] = best_data[sub_model[0]][name] 121 | except: 122 | dic[name] = '' 123 | 124 | table_data.append(dic) 125 | 126 | 127 | return table_data, best_head, modify_head 128 | 129 | 130 | # 构造loss画图数据 131 | def generate_loss_data(conn, project_id, sub_model_result): 132 | sql = "select max(md.epoch) from model_metric md " \ 133 | "left join sub_model sm on sm.sub_model_id=md.sub_model_id " \ 134 | "left join project m on m.project_id = sm.project_id " \ 135 | "where m.project_id=%d" % (project_id) 136 | max_step = conn.execute(sql).fetchall()[0][0] 137 | x_value = [i for i in range(1, max_step + 1)] 138 | 139 | legend = {} 140 | series = [] 141 | for i, sub_model in enumerate(sub_model_result): 142 | 143 | if i + 1 == len(sub_model_result): 144 | legend[sub_model[1] + '_train'] = 'true' 145 | legend[sub_model[1] + '_test'] = 'true' 146 | else: 147 | legend[sub_model[1] + '_train'] = 'false' 148 | legend[sub_model[1] + '_test'] = 'false' 149 | 150 | sql = "select md.metric_value from model_metric md where md.sub_model_id=%d and md.metric_name='train_loss'" % ( 151 | sub_model[0]) 152 | train_value = [value[0] for value in conn.execute(sql)] 153 | data_dic = {'name': sub_model[1] + '_train', 'data': str(train_value)} 154 | series.append(data_dic) 155 | 156 | sql = "select md.metric_value from model_metric md where md.sub_model_id=%d and md.metric_name='test_loss'" % ( 157 | sub_model[0]) 158 | test_value = [value[0] for value in conn.execute(sql)] 159 | data_dic = {'name': sub_model[1] + '_test', 'data': str(test_value)} 160 | series.append(data_dic) 161 | 162 | x_value = str(x_value) 163 | 164 | return legend, x_value, series 165 | 166 | 167 | def generate_train_test_data(conn, sub_model_result, type): 168 | 169 | legend = {} 170 | series = [] 171 | for i, sub_model in enumerate(sub_model_result): 172 | 173 | if i + 1 == len(sub_model_result): 174 | legend[sub_model[1] + '_train'] = 'true' 175 | legend[sub_model[1] + '_test'] = 'true' 176 | else: 177 | legend[sub_model[1] + '_train'] = 'false' 178 | legend[sub_model[1] + '_test'] = 'false' 179 | 180 | sql = f"select md.metric_value from model_metric md where md.sub_model_id={sub_model[0]} and md.metric_name='train_{type}'" 181 | train_value = [value[0] for value in conn.execute(sql)] 182 | data_dic = {'name': sub_model[1] + '_train', 'data': str(train_value)} 183 | series.append(data_dic) 184 | 185 | sql = f"select md.metric_value from model_metric md where md.sub_model_id={sub_model[0]} and md.metric_name='test_{type}'" 186 | test_value = [value[0] for value in conn.execute(sql)] 187 | data_dic = {'name': sub_model[1] + '_test', 'data': str(test_value)} 188 | series.append(data_dic) 189 | 190 | return legend, series 191 | 192 | def generate_new_loss(conn, sub_model_id, sub_model_name, type): 193 | 194 | sql = f"select md.metric_value from model_metric md where md.sub_model_id={sub_model_id} and md.metric_name='train_{type}'" 195 | train_value = [value[0] for value in conn.execute(sql)] 196 | 197 | sql = f"select md.metric_value from model_metric md where md.sub_model_id={sub_model_id} and md.metric_name='test_{type}'" 198 | test_value = [value[0] for value in conn.execute(sql)] 199 | 200 | data_list_loss = {'xAxis':{'data':[i for i in range(1, len(train_value) + 1)]}, 201 | 'series':[{'name':sub_model_name + '_train', 'data':train_value}, 202 | {'name':sub_model_name + '_test', 'data':test_value}]} 203 | 204 | return data_list_loss 205 | 206 | 207 | # 构造 acc画图数据 208 | def generate_indicater_data(conn, sub_model_result, type): 209 | legend_acc = {} 210 | series_acc = [] 211 | for i, sub_model in enumerate(sub_model_result): 212 | 213 | if i + 1 == len(sub_model_result): 214 | legend_acc[sub_model[1] + '_test'] = 'true' 215 | else: 216 | legend_acc[sub_model[1] + '_test'] = 'false' 217 | 218 | sql = "select md.metric_value from model_metric md where md.sub_model_id=%d and md.metric_name='%s'" % ( 219 | sub_model[0], type) 220 | test_value = [value[0] for value in conn.execute(sql)] 221 | data_dic = {'name': sub_model[1] + '_test', 'data': str(test_value)} 222 | series_acc.append(data_dic) 223 | 224 | return legend_acc, series_acc 225 | 226 | def generate_new_indicater_data(conn, sub_model_id, sub_model_name, type): 227 | 228 | sql = "select md.metric_value from model_metric md where md.sub_model_id=%d and md.metric_name='%s'" % (sub_model_id, type) 229 | value_list = [value[0] for value in conn.execute(sql)] 230 | 231 | data_dict = {'xAxis': {'data': [i for i in range(1, len(value_list) + 1)]}, 232 | 'series': [{'name': sub_model_name + '_test', 'data': value_list}]} 233 | 234 | return data_dict 235 | 236 | # 删除model 237 | def delete_model(conn, project_id): 238 | sql = "delete from project where project_id=%d" % (project_id) 239 | conn.execute(sql) 240 | conn.commit() 241 | 242 | 243 | def delete_sub_model(del_set): 244 | sql = "delete from sub_model where sub_model_id in %s" % (del_set) 245 | conn = get_conn() 246 | conn.execute(sql) 247 | 248 | sql = "delete from model_param where sub_model_id in %s" % (del_set) 249 | conn.execute(sql) 250 | 251 | sql = "delete from model_metric where sub_model_id in %s" % (del_set) 252 | conn.execute(sql) 253 | 254 | sql = "delete from best_result where sub_model_id in %s" % (del_set) 255 | conn.execute(sql) 256 | 257 | conn.commit() 258 | conn.close() 259 | 260 | ## 模型总数 261 | def get_model_num(conn, nick_name): 262 | sql = "select count(1) from sub_model sm where sm.nick_name='%s'" % (nick_name) 263 | model_num = conn.execute(sql).fetchall()[0][0] 264 | return model_num 265 | 266 | # 检查是否登录 267 | def check_login(sess): 268 | 269 | if 'nick_name' not in session: 270 | return False 271 | elif session['nick_name'] == '': 272 | return False 273 | else: 274 | return True 275 | 276 | 277 | # 进行初始化 278 | def init_db(nick_name): 279 | 280 | for i in ['1','2']: 281 | with open(os.path.join(current_path, 'tf_param.pkl'), 'rb') as file: 282 | tf_param = pickle.load(file) 283 | if i == '1': 284 | tf_param['learning_rate'] = 0.001 285 | 286 | with open(os.path.join(current_path, 'metric_list' + i + '.pkl'), 'rb') as file: 287 | metric_list = pickle.load(file) 288 | 289 | model_log = ModelLog(nick_name, 'demo命名实体识别') 290 | model_log.add_model_name('BILSTM_CRF模型') 291 | model_log.add_param(tf_param, 'tf_param') 292 | 293 | for n, item in enumerate(metric_list): 294 | model_log.add_metric('train_loss', item['train_loss'], n + 1) 295 | model_log.add_metric('test_loss', item['test_loss'], n + 1) 296 | model_log.add_metric('test_acc', item['test_acc'], n + 1) 297 | model_log.add_metric('test_recall', item['test_recall'], n + 1) 298 | model_log.add_metric('test_precision', item['test_precision'], n + 1) 299 | model_log.add_metric('test_F1', item['test_F1'], n + 1) 300 | 301 | if i == '1': 302 | model_log.add_best_result('best_loss', 4.9491, 14) 303 | model_log.add_best_result('best_acc', 0.8937, 14) 304 | model_log.add_best_result('best_precision', 0.8315, 14) 305 | model_log.add_best_result('best_F1', 0.8615, 14) 306 | model_log.add_best_result('best_step', 14, 14) 307 | else: 308 | model_log.add_best_result('best_loss', 2.7031, 29) 309 | model_log.add_best_result('best_acc', 0.8937, 29) 310 | model_log.add_best_result('best_precision', 0.8285, 29) 311 | model_log.add_best_result('best_F1', 0.8598, 29) 312 | model_log.add_best_result('best_step', 29, 29) 313 | 314 | del model_log 315 | 316 | 317 | @app.before_request 318 | def check_platform(): 319 | 320 | phone = ['android', 'iphone', 'ipad'] 321 | 322 | platform = request.user_agent.platform 323 | platform = platform.lower() 324 | 325 | if platform in phone: 326 | return render_template('alert.html') 327 | 328 | 329 | 330 | 331 | @app.route('/') 332 | def to_index(): 333 | 334 | conn = get_conn() 335 | model_num = 0 336 | 337 | is_login = check_login(session) 338 | 339 | ''' 340 | if is_login: 341 | del session['nick_name'] 342 | is_login = False 343 | ''' 344 | 345 | nick_name = '' 346 | if is_login: 347 | nick_name = session['nick_name'] 348 | model_num = get_model_num(conn, nick_name) 349 | 350 | conn.close() 351 | return render_template('index.html', model_num=model_num, is_login=is_login, nick_name=nick_name) 352 | 353 | @app.route('/login', methods=['POST']) 354 | def login(): 355 | nick_name = request.get_json()['nick_name'] 356 | 357 | session['nick_name'] = nick_name 358 | message = {} 359 | message['is_success'] = True 360 | 361 | ## 判断是否进行初始化数据 362 | conn = get_conn() 363 | sql = "select count(1) from project p where p.nick_name='%s'" % (nick_name) 364 | num = conn.execute(sql).fetchall()[0][0] 365 | 366 | if num == 0: 367 | init_db(nick_name) 368 | 369 | conn.close() 370 | return jsonify(message) 371 | 372 | 373 | 374 | @app.route('/project_detail') 375 | def project_detail(): 376 | 377 | try: 378 | project_id = int(request.args.get('project_id')) 379 | except: 380 | raise Exception('project id 错误!') 381 | 382 | conn = get_conn() 383 | 384 | # 构造表头 385 | sub_model_result, sub_model_param, table_head, table_length = generate_table_head(conn, project_id) 386 | 387 | # 删除model 388 | if len(sub_model_result) == 0: 389 | delete_model(conn, project_id) 390 | 391 | conn.close() 392 | return redirect('/') 393 | 394 | # 构造页面数据 395 | table_data, best_head, modify_head = generate_table_data(conn, table_head, sub_model_result, sub_model_param) 396 | 397 | # 构造loss画图数据 398 | legend, x_value, series = generate_loss_data(conn, project_id, sub_model_result) 399 | 400 | # 构造 acc画图数据 401 | legend_acc, series_acc = generate_train_test_data(conn, sub_model_result, 'acc') 402 | 403 | # 构造recall画图数据 404 | legend_recall, series_recall = generate_train_test_data(conn, sub_model_result, 'recall') 405 | 406 | # 构造 precision画图数据 407 | legend_precision, series_precision = generate_train_test_data(conn, sub_model_result, 'precision') 408 | 409 | # 构造 F1画图数据 410 | legend_F1, series_F1 = generate_train_test_data(conn, sub_model_result, 'F1') 411 | 412 | # 模型个数 413 | if 'nick_name' not in session: 414 | return redirect('/') 415 | nick_name = session['nick_name'] 416 | model_num = get_model_num(conn, nick_name) 417 | sub_model_num = len(sub_model_result) 418 | 419 | # 是否训练完成 420 | is_finished_train = table_data[-1]['finished_train'] 421 | 422 | sql = "select p.project_name from project p where p.project_id=%d" % (project_id) 423 | project_name = conn.execute(sql).fetchall()[0][0] 424 | 425 | conn.close() 426 | return render_template('model_detail.html', table_head=table_head, table_data=table_data, 427 | table_length=table_length, x_value=x_value, legend=legend, series=series, 428 | legend_acc=legend_acc, series_acc=series_acc, best_head=best_head, 429 | modify_head=modify_head, legend_precision=legend_precision, 430 | series_precision=series_precision, legend_F1=legend_F1, 431 | series_F1=series_F1, project_id=project_id, model_num=model_num, 432 | sub_model_num=sub_model_num, is_finished_train=is_finished_train, nick_name=nick_name, 433 | legend_recall=legend_recall, series_recall=series_recall, project_name=project_name) 434 | 435 | # 动态获取最新数据 436 | @app.route('/get_new_data', methods=['POST']) 437 | def get_new_data(): 438 | 439 | project_id = request.get_json()['project_id'] 440 | message = {} 441 | conn = get_conn() 442 | 443 | 444 | try: 445 | 446 | 447 | sql = "select max(sm.sub_model_id), sm.sub_model_name from sub_model sm where sm.project_id=%d" % (project_id) 448 | sub_model_id = conn.execute(sql).fetchall()[0][0] 449 | sub_model_name = conn.execute(sql).fetchall()[0][1] 450 | 451 | # loss 452 | data_list_loss = generate_new_loss(conn, sub_model_id, sub_model_name, 'loss') 453 | 454 | # acc 455 | data_list_acc = generate_new_loss(conn, sub_model_id, sub_model_name, 'acc') 456 | 457 | # recall 458 | data_list_recall = generate_new_loss(conn, sub_model_id, sub_model_name, 'recall') 459 | 460 | # precision 461 | data_list_precision = generate_new_loss(conn, sub_model_id, sub_model_name, 'precision') 462 | 463 | # F1 464 | data_list_F1 = generate_new_loss(conn, sub_model_id, sub_model_name, 'F1') 465 | 466 | # 判断是否训练完成 467 | sql = "select count(1) from best_result br where br.sub_model_id=%d" % (sub_model_id) 468 | best_count = conn.execute(sql).fetchall()[0][0] 469 | 470 | if best_count == 0: 471 | message['finished_train'] = False 472 | else: 473 | message['finished_train'] = True 474 | 475 | message['is_success'] = True 476 | message['data'] = {'loss': data_list_loss, 'acc': data_list_acc, 'recall': data_list_recall, 'precision': data_list_precision, 477 | 'F1': data_list_F1} 478 | 479 | 480 | except: 481 | message['is_success'] = False 482 | message['msg'] = '程序内部开小差啦!' 483 | 484 | conn.close() 485 | return jsonify(message) 486 | 487 | 488 | # 检测是否有模型开始训练 489 | @app.route('/check_new_model', methods=['POST']) 490 | def check_new_model(): 491 | 492 | model_num = request.get_json()['model_num'] 493 | message = {} 494 | conn = get_conn() 495 | 496 | 497 | try: 498 | 499 | 500 | nick_name = session['nick_name'] 501 | current_model_num = get_model_num(conn, nick_name) 502 | 503 | if model_num != current_model_num: 504 | sql = "select max(sm.sub_model_id), sm.project_id from sub_model sm" 505 | project_id = conn.execute(sql).fetchall()[0][1] 506 | message['is_jump'] = True 507 | message['project_id'] = project_id 508 | else: 509 | message['is_jump'] = False 510 | 511 | message['is_success'] = True 512 | 513 | 514 | 515 | except: 516 | message['is_success'] = False 517 | message['msg'] = '程序内部开小差啦!' 518 | 519 | conn.close() 520 | return jsonify(message) 521 | 522 | 523 | 524 | # 删除model 525 | @app.route('/del_project', methods=['POST']) 526 | def del_model(): 527 | del_list = request.get_json()['del_list'] 528 | message = {} 529 | conn = get_conn() 530 | 531 | try: 532 | del_set = set() 533 | for id in del_list: 534 | del_set.add(int(id)) 535 | 536 | del_set = str(del_set) 537 | del_set = del_set.replace('{', '(') 538 | del_set = del_set.replace('}', ')') 539 | 540 | 541 | sql = "select sm.sub_model_id from sub_model sm where sm.project_id in %s" % (del_set) 542 | sub_model_id_list = [id[0] for id in conn.execute(sql)] 543 | sub_model_id_list = str(sub_model_id_list) 544 | sub_model_id_list = sub_model_id_list.replace('[', '(') 545 | sub_model_id_list = sub_model_id_list.replace(']', ')') 546 | 547 | delete_sub_model(sub_model_id_list) 548 | 549 | # 删除model 550 | sql = "delete from project where project_id in %s" % (del_set) 551 | conn.execute(sql) 552 | 553 | conn.commit() 554 | message['is_success'] = True 555 | 556 | 557 | except Exception as e: 558 | message['is_success'] = False 559 | message['msg'] = '选中id错误!' 560 | print(e) 561 | 562 | conn.close() 563 | return jsonify(message) 564 | 565 | 566 | @app.route('/del_sub_model', methods=['POST']) 567 | def del_sub_model(): 568 | 569 | del_list = request.get_json()['del_list'] 570 | message = {} 571 | 572 | try: 573 | del_set = set() 574 | for id in del_list: 575 | del_set.add(int(id)) 576 | 577 | del_set = str(del_set) 578 | del_set = del_set.replace('{', '(') 579 | del_set = del_set.replace('}', ')') 580 | 581 | delete_sub_model(del_set) 582 | 583 | message['is_success'] = True 584 | 585 | except Exception as e: 586 | message['is_success'] = False 587 | message['msg'] = '选中id错误!' 588 | print(e) 589 | 590 | 591 | return jsonify(message) 592 | 593 | @app.route('/finish_model', methods=['POST']) 594 | def finish_model(): 595 | sub_model_id = request.get_json()['sub_model_id'] 596 | message = {} 597 | conn = get_conn() 598 | 599 | try: 600 | sql = f'update sub_model set is_finish=1 where sub_model_id={sub_model_id}' 601 | conn.execute(sql) 602 | conn.commit() 603 | message['is_success'] = True 604 | 605 | except Exception as e: 606 | message['is_success'] = False 607 | message['msg'] = '选中id错误!' 608 | print(e) 609 | 610 | conn.close() 611 | return jsonify(message) 612 | 613 | # db operation================================================================= 614 | 615 | 616 | # 查询项目列表 617 | @app.route('/get_project_list') 618 | def get_project_list(): 619 | 620 | try: 621 | page = int(request.args.get('page')) 622 | except: 623 | raise Exception('页码参数错误!') 624 | 625 | conn = get_conn() 626 | nick_name = session['nick_name'] 627 | 628 | sql = "select m.project_name, m.project_remark, m.create_time, m.project_id from project m where m.del_flag = 0 and m.nick_name=? order by m.create_time desc limit ?,?" 629 | result = conn.execute(sql, (nick_name, (page - 1) * page_size, page_size)) 630 | project_list = [] 631 | 632 | id = (page - 1) * page_size 633 | for item in result: 634 | id += 1 635 | map_ = {'id':id, 'project_name':item[0], 'project_remark':item[1], 'create_time':item[2], 'project_id':item[3]} 636 | project_list.append(map_) 637 | 638 | conn.close() 639 | return jsonify(project_list) 640 | 641 | # 查询项目总页数 642 | @app.route('/get_page_num') 643 | def get_page_num(): 644 | 645 | if not check_login(session): 646 | return '-1' 647 | 648 | conn = get_conn() 649 | 650 | sql = "select count(1) from project m where m.del_flag = 0 and m.nick_name='%s'" % (session['nick_name']) 651 | result = conn.execute(sql).fetchall()[0][0] 652 | 653 | page_num = result / page_size 654 | 655 | if int(page_num) < page_num: 656 | page_num = int(page_num) + 1 657 | else: 658 | page_num = int(page_num) 659 | 660 | conn.close() 661 | return str(page_num) 662 | 663 | @app.route('/alert') 664 | def alert(): 665 | return render_template('alert.html') 666 | 667 | 668 | 669 | 670 | def my_exit(signum, frame): 671 | print() 672 | print('Good By!') 673 | sys.exit(0) 674 | 675 | def main(): 676 | signal.signal(signal.SIGINT, my_exit) 677 | signal.signal(signal.SIGTERM, my_exit) 678 | 679 | parser = argparse.ArgumentParser() 680 | parser.add_argument("-p", type=int, default=5432, help="指定端口号") 681 | args = parser.parse_args() 682 | 683 | try: 684 | webbrowser.open('http://127.0.0.1:%d/' % (args.p)) 685 | app.run(host='0.0.0.0', port=args.p) 686 | except Exception as e: 687 | print(str(args.p) + '端口已占用,请使用 model-log -p=5000 指定端口号,或关闭' + str(args.p) + '端口!') 688 | 689 | 690 | if __name__ == '__main__': 691 | main() 692 | 693 | 694 | -------------------------------------------------------------------------------- /model_log/static/css/admin.css: -------------------------------------------------------------------------------- 1 | /** 2 | * admin.css 3 | */ 4 | 5 | 6 | /* 7 | fixed-layout 固定头部和边栏布局 8 | */ 9 | 10 | html, 11 | body { 12 | height: 100%; 13 | overflow: hidden; 14 | } 15 | 16 | ul { 17 | margin-top: 0; 18 | } 19 | 20 | .admin-icon-yellow { 21 | color: #ffbe40; 22 | } 23 | 24 | .admin-header { 25 | position: fixed; 26 | top: 0; 27 | left: 0; 28 | right: 0; 29 | z-index: 1500; 30 | font-size: 1.4rem; 31 | margin-bottom: 0; 32 | } 33 | 34 | .admin-header-list a:hover :after { 35 | content: none; 36 | } 37 | 38 | .admin-main { 39 | position: relative; 40 | height: 100%; 41 | padding-top: 51px; 42 | background: #f3f3f3; 43 | } 44 | 45 | .admin-menu { 46 | position: fixed; 47 | z-index: 10; 48 | bottom: 30px; 49 | right: 20px; 50 | } 51 | 52 | .admin-sidebar { 53 | width: 260px; 54 | min-height: 100%; 55 | float: left; 56 | border-right: 1px solid #cecece; 57 | } 58 | 59 | .admin-sidebar.am-active { 60 | z-index: 1600; 61 | } 62 | 63 | .admin-sidebar-list { 64 | margin-bottom: 0; 65 | } 66 | 67 | .admin-sidebar-list li a { 68 | color: #5c5c5c; 69 | padding-left: 24px; 70 | } 71 | 72 | .admin-sidebar-list li:first-child { 73 | border-top: none; 74 | } 75 | 76 | .admin-sidebar-sub { 77 | margin-top: 0; 78 | margin-bottom: 0; 79 | box-shadow: 0 16px 8px -15px #e2e2e2 inset; 80 | background: #ececec; 81 | padding-left: 24px; 82 | } 83 | 84 | .admin-sidebar-sub li:first-child { 85 | border-top: 1px solid #dedede; 86 | } 87 | 88 | .admin-sidebar-panel { 89 | margin: 10px; 90 | } 91 | 92 | .admin-content { 93 | display: -webkit-box; 94 | display: -webkit-flex; 95 | display: -ms-flexbox; 96 | display: flex; 97 | -webkit-box-orient: vertical; 98 | -webkit-box-direction: normal; 99 | -webkit-flex-direction: column; 100 | -ms-flex-direction: column; 101 | flex-direction: column; 102 | background: #fff; 103 | } 104 | 105 | .admin-content, 106 | .admin-sidebar { 107 | height: 100%; 108 | overflow-x: hidden; 109 | overflow-y: scroll; 110 | -webkit-overflow-scrolling: touch; 111 | } 112 | 113 | .admin-content-body { 114 | -webkit-box-flex: 1; 115 | -webkit-flex: 1 0 auto; 116 | -ms-flex: 1 0 auto; 117 | flex: 1 0 auto; 118 | } 119 | 120 | .admin-content-footer { 121 | font-size: 85%; 122 | color: #777; 123 | } 124 | 125 | .admin-content-list { 126 | border: 1px solid #e9ecf1; 127 | margin-top: 0; 128 | } 129 | 130 | .admin-content-list li { 131 | border: 1px solid #e9ecf1; 132 | border-width: 0 1px; 133 | margin-left: -1px; 134 | } 135 | 136 | .admin-content-list li:first-child { 137 | border-left: none; 138 | } 139 | 140 | .admin-content-list li:last-child { 141 | border-right: none; 142 | } 143 | 144 | .admin-content-table a { 145 | color: #535353; 146 | } 147 | .admin-content-file { 148 | margin-bottom: 0; 149 | color: #666; 150 | } 151 | 152 | .admin-content-file p { 153 | margin: 0 0 5px 0; 154 | font-size: 1.4rem; 155 | } 156 | 157 | .admin-content-file li { 158 | padding: 10px 0; 159 | } 160 | 161 | .admin-content-file li:first-child { 162 | border-top: none; 163 | } 164 | 165 | .admin-content-file li:last-child { 166 | border-bottom: none; 167 | } 168 | 169 | .admin-content-file li .am-progress { 170 | margin-bottom: 4px; 171 | } 172 | 173 | .admin-content-file li .am-progress-bar { 174 | line-height: 14px; 175 | } 176 | 177 | .admin-content-task { 178 | margin-bottom: 0; 179 | } 180 | 181 | .admin-content-task li { 182 | padding: 5px 0; 183 | border-color: #eee; 184 | } 185 | 186 | .admin-content-task li:first-child { 187 | border-top: none; 188 | } 189 | 190 | .admin-content-task li:last-child { 191 | border-bottom: none; 192 | } 193 | 194 | .admin-task-meta { 195 | font-size: 1.2rem; 196 | color: #999; 197 | } 198 | 199 | .admin-task-bd { 200 | font-size: 1.4rem; 201 | margin-bottom: 5px; 202 | } 203 | 204 | .admin-content-comment { 205 | margin-bottom: 0; 206 | } 207 | 208 | .admin-content-comment .am-comment-bd { 209 | font-size: 1.4rem; 210 | } 211 | 212 | .admin-content-pagination { 213 | margin-bottom: 0; 214 | } 215 | .admin-content-pagination li a { 216 | padding: 4px 8px; 217 | } 218 | 219 | @media only screen and (min-width: 641px) { 220 | .admin-sidebar { 221 | display: block; 222 | position: static; 223 | background: none; 224 | } 225 | 226 | .admin-offcanvas-bar { 227 | position: static; 228 | width: auto; 229 | background: none; 230 | -webkit-transform: translate3d(0, 0, 0); 231 | -ms-transform: translate3d(0, 0, 0); 232 | transform: translate3d(0, 0, 0); 233 | overflow-y: visible; 234 | min-height: 100%; 235 | } 236 | .admin-offcanvas-bar:after { 237 | content: none; 238 | } 239 | } 240 | 241 | @media only screen and (max-width: 640px) { 242 | .admin-sidebar { 243 | width: inherit; 244 | } 245 | 246 | .admin-offcanvas-bar { 247 | background: #f3f3f3; 248 | } 249 | 250 | .admin-offcanvas-bar:after { 251 | background: #BABABA; 252 | } 253 | 254 | .admin-sidebar-list a:hover, .admin-sidebar-list a:active{ 255 | -webkit-transition: background-color .3s ease; 256 | -moz-transition: background-color .3s ease; 257 | -ms-transition: background-color .3s ease; 258 | -o-transition: background-color .3s ease; 259 | transition: background-color .3s ease; 260 | background: #E4E4E4; 261 | } 262 | 263 | .admin-content-list li { 264 | padding: 10px; 265 | border-width: 1px 0; 266 | margin-top: -1px; 267 | } 268 | 269 | .admin-content-list li:first-child { 270 | border-top: none; 271 | } 272 | 273 | .admin-content-list li:last-child { 274 | border-bottom: none; 275 | } 276 | 277 | .admin-form-text { 278 | text-align: left !important; 279 | } 280 | 281 | } 282 | 283 | /* 284 | * user.html css 285 | */ 286 | .user-info { 287 | margin-bottom: 15px; 288 | } 289 | 290 | .user-info .am-progress { 291 | margin-bottom: 4px; 292 | } 293 | 294 | .user-info p { 295 | margin: 5px; 296 | } 297 | 298 | .user-info-order { 299 | font-size: 1.4rem; 300 | } 301 | 302 | /* 303 | * errorLog.html css 304 | */ 305 | 306 | .error-log .am-pre-scrollable { 307 | max-height: 40rem; 308 | } 309 | 310 | /* 311 | * table.html css 312 | */ 313 | 314 | .table-main { 315 | font-size: 1.4rem; 316 | padding: .5rem; 317 | } 318 | 319 | .table-main button { 320 | background: #fff; 321 | } 322 | 323 | .table-check { 324 | width: 30px; 325 | } 326 | 327 | .table-id { 328 | width: 50px; 329 | } 330 | 331 | @media only screen and (max-width: 640px) { 332 | .table-select { 333 | margin-top: 10px; 334 | margin-left: 5px; 335 | } 336 | } 337 | 338 | /* 339 | gallery.html css 340 | */ 341 | 342 | .gallery-list li { 343 | padding: 10px; 344 | } 345 | 346 | .gallery-list a { 347 | color: #666; 348 | } 349 | 350 | .gallery-list a:hover { 351 | color: #3bb4f2; 352 | } 353 | 354 | .gallery-title { 355 | margin-top: 6px; 356 | font-size: 1.4rem; 357 | } 358 | 359 | .gallery-desc { 360 | font-size: 1.2rem; 361 | margin-top: 4px; 362 | } 363 | 364 | /* 365 | 404.html css 366 | */ 367 | 368 | .page-404 { 369 | background: #fff; 370 | border: none; 371 | width: 200px; 372 | margin: 0 auto; 373 | } 374 | -------------------------------------------------------------------------------- /model_log/static/css/app.css: -------------------------------------------------------------------------------- 1 | /* Write your styles */ 2 | 3 | html, 4 | body { 5 | background: #e9ecf3; 6 | overflow: inherit; 7 | } 8 | 9 | ul, 10 | li { 11 | margin: 0; 12 | padding: 0; 13 | list-style: none; 14 | } 15 | 16 | input { 17 | border: none; 18 | } 19 | 20 | a { 21 | color: #337ab7; 22 | } 23 | 24 | a:hover { 25 | cursor: pointer; 26 | color: #23527c; 27 | } 28 | 29 | .am-breadcrumb { 30 | padding: 0; 31 | margin-top: 10px; 32 | } 33 | 34 | .am-topbar-inverse { 35 | background: #fff; 36 | border-color: #e9ecf3; 37 | } 38 | 39 | .am-topbar-brand { 40 | color: #337ab7; 41 | margin-right: 20px; 42 | } 43 | 44 | .am-topbar-brand { 45 | height: 75px; 46 | line-height: 75px; 47 | } 48 | 49 | .am-topbar { 50 | min-height: 75px; 51 | line-height: 75px; 52 | } 53 | 54 | .am-topbar-inverse .am-topbar-nav>li>a { 55 | height: 75px; 56 | line-height: 75px; 57 | padding: 0 12px; 58 | } 59 | 60 | .am-topbar-inverse .am-topbar-nav>li>a:focus, 61 | .am-topbar-inverse .am-topbar-nav>li>a:hover { 62 | background: none; 63 | color: initial; 64 | } 65 | 66 | .am-topbar-inverse .am-topbar-nav>li>a:after { 67 | border-bottom: none; 68 | } 69 | 70 | .am-topbar-inverse .am-topbar-nav>li>a:focus:after, 71 | .am-topbar-inverse .am-topbar-nav>li>a:hover:after { 72 | border-bottom: none; 73 | } 74 | 75 | .am-topbar-inverse .am-topbar-nav>li>a {} 76 | 77 | .am-nav-pills>li+li { 78 | margin-left: 0; 79 | } 80 | 81 | .am-topbar-inverse .am-topbar-nav>li.am-active>a, 82 | .am-topbar-inverse .am-topbar-nav>li.am-active>a:focus, 83 | .am-topbar-inverse .am-topbar-nav>li.am-active>a:hover { 84 | background-color: #f9fafc; 85 | } 86 | 87 | ul.am-dropdown-content>li>a:focus, 88 | ul.am-dropdown-content>li>a:hover { 89 | background: none; 90 | } 91 | 92 | ul.am-dropdown-content>li>a { 93 | color: #96a5aa; 94 | padding: 12px 8px 12px 18px; 95 | white-space: initial; 96 | font-size: 12px; 97 | display: table-cell; 98 | } 99 | 100 | .admin-sidebar-list li a { 101 | color: #485a6a; 102 | font-size: 14px; 103 | } 104 | 105 | .tpl-color-success { 106 | color: #36c6d3; 107 | } 108 | 109 | .tpl-color-danger { 110 | color: #ed6b75; 111 | } 112 | 113 | .tpl-color-warning { 114 | color: #F1C40F; 115 | } 116 | 117 | .tpl-color-primary { 118 | color: #8E44AD; 119 | } 120 | 121 | .tpl-badge-success { 122 | background-color: #36c6d3!important; 123 | } 124 | 125 | .tpl-badge-danger { 126 | background-color: #ed6b75!important; 127 | } 128 | 129 | .tpl-badge-warning { 130 | background-color: #F1C40F!important; 131 | } 132 | 133 | .tpl-badge-primary { 134 | background-color: #8E44AD!important; 135 | } 136 | 137 | .tpl-header-list li { 138 | color: #999; 139 | border-bottom: 1px solid #F1F4F7; 140 | } 141 | 142 | .tpl-header-list li:last-child { 143 | border-bottom: none; 144 | } 145 | 146 | .tpl-header-list li:hover { 147 | background: #f9fafc; 148 | } 149 | 150 | .tpl-header-list-link { 151 | color: #999!important; 152 | } 153 | 154 | .tpl-header-list-user-nick { 155 | color: #7FB0DA; 156 | } 157 | 158 | .tpl-header-list-user-nick { 159 | color: #7FB0DA; 160 | } 161 | 162 | .tpl-header-list-user-ico img { 163 | margin-left: 5px; 164 | margin-top: -4px; 165 | height: 39px; 166 | display: inline-block; 167 | border-radius: 50%; 168 | } 169 | 170 | .tpl-header-list-ico-out-size { 171 | font-size: 16px; 172 | } 173 | 174 | ul.tpl-dropdown-content { 175 | width: 260px; 176 | padding: 8px; 177 | } 178 | 179 | ul.tpl-dropdown-content li>a.tpl-dropdown-content-message { 180 | padding: 16px 12px; 181 | display: block; 182 | border-bottom: 1px solid #F1F4F7; 183 | } 184 | 185 | ul.tpl-dropdown-content li>a.tpl-dropdown-content-message:last-child { 186 | border-bottom: none; 187 | } 188 | 189 | .tpl-dropdown-content-photo { 190 | float: left; 191 | margin: 0 6px 6px 0; 192 | } 193 | 194 | .tpl-dropdown-content-photo img { 195 | height: 40px; 196 | width: 40px; 197 | -webkit-border-radius: 50%!important; 198 | -moz-border-radius: 50%!important; 199 | -ms-border-radius: 50%!important; 200 | -o-border-radius: 50%!important; 201 | border-radius: 50%!important; 202 | } 203 | 204 | .tpl-dropdown-content-subject { 205 | display: block; 206 | margin-left: 46px; 207 | } 208 | 209 | .tpl-dropdown-content-external { 210 | display: block; 211 | overflow: hidden; 212 | padding: 10px; 213 | letter-spacing: .5px; 214 | border-bottom: 1px solid #F1F4F7; 215 | } 216 | 217 | .tpl-dropdown-content-external a { 218 | padding: 0!important; 219 | display: block!important; 220 | float: right; 221 | clear: none!important; 222 | } 223 | 224 | .tpl-dropdown-content-external h3 { 225 | margin: 0; 226 | padding: 0; 227 | font-size: 13px; 228 | color: #96a5aa; 229 | font-weight: normal; 230 | /*display: inline-block;*/ 231 | float: left; 232 | } 233 | 234 | .tpl-dropdown-content-external h3 span { 235 | font-weight: 600; 236 | font-size: 16px; 237 | } 238 | 239 | .tpl-dropdown-content-from { 240 | font-size: 13px; 241 | font-weight: 600; 242 | } 243 | 244 | .tpl-dropdown-content-time { 245 | font-size: 12px; 246 | font-weight: 400; 247 | opacity: .5; 248 | filter: alpha(opacity=50); 249 | float: right; 250 | } 251 | 252 | .tpl-dropdown-content-font { 253 | display: block!important; 254 | font-size: 12px; 255 | line-height: 22px; 256 | margin-left: 46px; 257 | } 258 | 259 | .tpl-dropdown-ico-btn-size { 260 | width: 18px; 261 | height: 18px; 262 | line-height: 18px; 263 | font-size: 8px; 264 | color: #fff; 265 | } 266 | 267 | .tpl-dropdown-list-fl { 268 | width: 70%; 269 | position: relative; 270 | } 271 | 272 | .tpl-dropdown-list-fr { 273 | width: 30%; 274 | display: table-cell; 275 | font-size: 12px; 276 | text-align: right; 277 | line-height: initial; 278 | vertical-align: middle; 279 | padding-right: 20px; 280 | } 281 | 282 | .tpl-dropdown-list-bdbc { 283 | border-bottom: 1px solid #F1F4F7; 284 | } 285 | 286 | .tpl-dropdown-list-bdbc:last-child { 287 | border-bottom: none; 288 | } 289 | 290 | .tpl-logo { 291 | display: inline-block; 292 | vertical-align: middle; 293 | margin-top: -5px; 294 | padding-left: 18px; 295 | width: 160px; 296 | } 297 | 298 | .tpl-logo img { 299 | display: block; 300 | width: 100%; 301 | } 302 | 303 | .tpl-dropdown-content .task { 304 | margin-bottom: 5px; 305 | } 306 | 307 | .tpl-dropdown-content .desc { 308 | font-size: 13px; 309 | font-weight: 300; 310 | } 311 | 312 | .tpl-dropdown-content .percent { 313 | float: right; 314 | font-weight: 600; 315 | display: inline-block; 316 | } 317 | 318 | .tpl-dropdown-content-progress { 319 | display: block!important; 320 | } 321 | 322 | .tpl-dropdown-content .progress { 323 | display: block; 324 | background-color: #f5f5f5; 325 | margin: 8px 0 2px; 326 | } 327 | 328 | .tpl-progress { 329 | height: 16px; 330 | margin-bottom: 0; 331 | } 332 | 333 | .tpl-dropdown-content .progress-bar { 334 | float: left; 335 | width: 0%; 336 | height: 100%; 337 | font-size: 12px; 338 | line-height: 20px; 339 | color: #fff; 340 | text-align: center; 341 | background-color: #337ab7; 342 | -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); 343 | box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); 344 | -webkit-transition: width 0.6s ease; 345 | -o-transition: width 0.6s ease; 346 | transition: width 0.6s ease; 347 | } 348 | 349 | .tpl-page-container { 350 | margin: 0; 351 | padding: 20px 20px 0; 352 | } 353 | 354 | .tpl-page-header-fixed { 355 | margin-top: 75px; 356 | } 357 | 358 | .tpl-navbar-collapse { 359 | width: 235px; 360 | float: left; 361 | background: #fff; 362 | border-radius: 6px; 363 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1); 364 | padding-bottom: 20px; 365 | } 366 | 367 | .admin-offcanvas-bar { 368 | border-radius: 2px; 369 | overflow: hidden; 370 | } 371 | 372 | .tpl-page-container-nav-heading { 373 | height: 50px; 374 | padding: 25px 15px 10px; 375 | color: #5C9ACF; 376 | font-size: 13px; 377 | font-weight: 600; 378 | } 379 | 380 | .admin-sidebar-sub { 381 | background: none; 382 | padding-left: 0; 383 | } 384 | 385 | .admin-sidebar-list .admin-sidebar-sub li { 386 | padding-left: 0; 387 | } 388 | 389 | .admin-sidebar-sub li:first-child { 390 | border-top: none; 391 | } 392 | 393 | .admin-sidebar-list li { 394 | position: relative; 395 | border: none; 396 | padding-left: 24px; 397 | border-left: 3px solid #fff; 398 | } 399 | 400 | .admin-sidebar-list .admin-parent li {} 401 | 402 | .tpl-page-container-ico { 403 | font-size: 20px; 404 | position: absolute; 405 | top: 6px; 406 | left: 18px; 407 | padding-right: 10px; 408 | color: #a7bdcd; 409 | } 410 | 411 | .tpl-sidebar-list {} 412 | 413 | .tpl-sidebar-list li.tpl-sidebar-list-hover:hover a, 414 | .tpl-sidebar-list li.tpl-sidebar-list-hover.active a { 415 | color: #5b9bd1; 416 | } 417 | 418 | .tpl-sidebar-list li.tpl-sidebar-list-hover:hover .tpl-page-container-ico, 419 | .tpl-sidebar-list li.tpl-sidebar-list-hover.active .tpl-page-container-ico { 420 | color: #5b9bd1; 421 | } 422 | 423 | .tpl-sidebar-list li.tpl-sidebar-list-hover:hover, 424 | .tpl-sidebar-list li.tpl-sidebar-list-hover.active { 425 | background: #f2f6f9; 426 | border-left: 3px solid #5C9ACF!important; 427 | } 428 | 429 | .tpl-sidebar-list-hover-show .tpl-sidebar-list-hover-show-font:hover, 430 | .tpl-sidebar-list-hover-show .tpl-sidebar-list-hover-show-font:hover .tpl-page-container-ico { 431 | color: #5b9bd1; 432 | } 433 | 434 | .tpl-sidebar-list-hover-show.active .tpl-sidebar-list-hover-show-font, 435 | .tpl-sidebar-list-hover-show.active .tpl-sidebar-list-hover-show-font .tpl-page-container-ico { 436 | color: #5b9bd1; 437 | } 438 | 439 | .tpl-sidebar-list-hover-show:hover, 440 | .tpl-sidebar-list-hover-show.active { 441 | background: #f2f6f9; 442 | border-left: 3px solid #f2f6f9; 443 | } 444 | 445 | .admin-sidebar-sub li:hover, 446 | .admin-sidebar-sub li.active { 447 | background: #f2f6f9; 448 | border-left: 3px solid #f2f6f9; 449 | } 450 | 451 | .admin-sidebar-sub li:hover a, 452 | .admin-sidebar-sub li.active a { 453 | color: #5b9bd1; 454 | } 455 | 456 | .tpl-topbar-list-button { 457 | float: left; 458 | color: #C0CDDC; 459 | font-size: 18px; 460 | } 461 | 462 | .tpl-topbar-list-button span { 463 | cursor: pointer; 464 | } 465 | 466 | .tpl-content-wrapper { 467 | padding-left: 255px; 468 | padding-top: 10px; 469 | } 470 | 471 | .tpl-content-wrapper-hover { 472 | padding-left: 0px; 473 | } 474 | 475 | .tpl-content-page-title { 476 | color: #697882; 477 | font-size: 22px; 478 | font-weight: 400; 479 | } 480 | 481 | .tpl-left-nav { 482 | width: 235px; 483 | float: left; 484 | background: #fff; 485 | border-radius: 6px; 486 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1); 487 | padding-bottom: 20px; 488 | } 489 | 490 | .tpl-left-nav-title { 491 | height: 50px; 492 | padding: 25px 15px 10px; 493 | color: #5C9ACF; 494 | font-size: 13px; 495 | font-weight: 600; 496 | } 497 | 498 | .tpl-left-nav-list { 499 | width: 235px; 500 | } 501 | 502 | .tpl-left-nav-item {} 503 | 504 | .tpl-left-nav-item .nav-link { 505 | display: block; 506 | position: relative; 507 | margin: 1px 0 0; 508 | border: 0; 509 | padding: 12px 15px; 510 | padding-top: 6px; 511 | text-decoration: none; 512 | color: #485a6a; 513 | font-size: 14px; 514 | } 515 | 516 | .tpl-left-nav-item .nav-link span, 517 | .tpl-left-nav-sub-menu a span { 518 | font-size: 14px; 519 | font-weight: 400; 520 | color: #485a6a; 521 | } 522 | 523 | .tpl-left-nav-item .nav-link i, 524 | .tpl-left-nav-sub-menu a i { 525 | font-size: 20px; 526 | position: relative; 527 | text-shadow: none; 528 | font-weight: 300; 529 | top: 2px; 530 | margin-left: 1px; 531 | margin-right: 6px; 532 | color: #a7bdcd; 533 | } 534 | 535 | .tpl-left-nav-item .nav-link:hover { 536 | background: #f2f6f9; 537 | color: #5b9bd1; 538 | border-left: 3px solid #5C9ACF!important; 539 | margin-left: -3px; 540 | padding-left: 15px; 541 | } 542 | 543 | .tpl-left-nav-item .nav-link:hover i, 544 | .tpl-left-nav-sub-menu a:hover i { 545 | color: #5b9bd1; 546 | } 547 | 548 | .tpl-left-nav-item .nav-link:hover span, 549 | .tpl-left-nav-sub-menu a:hover span { 550 | color: #5b9bd1; 551 | } 552 | 553 | .tpl-left-nav-item .nav-link.active { 554 | border-left: 3px solid #5C9ACF!important; 555 | background: #f2f6f9; 556 | margin-left: -3px; 557 | padding-left: 15px; 558 | } 559 | 560 | .tpl-left-nav-item .nav-link.active span, 561 | .tpl-left-nav-item .nav-link.active i { 562 | color: #5b9bd1; 563 | } 564 | 565 | .tpl-left-nav-sub-menu { 566 | list-style: none; 567 | display: none; 568 | padding: 0; 569 | margin: 0; 570 | } 571 | 572 | .tpl-header-nav-hover-ico { 573 | color: #C0CDDC!important; 574 | font-size: 19px!important; 575 | } 576 | 577 | .tpl-left-nav-more-ico { 578 | -webkit-transition: all 300ms; 579 | transition: all 300ms; 580 | font-size: 16px!important; 581 | top: 4px!important; 582 | } 583 | 584 | .tpl-left-nav-more-ico-rotate { 585 | -webkit-transform: rotate(90deg); 586 | transform: rotate(90deg); 587 | -webkit-transition: all 300ms; 588 | transition: all 300ms; 589 | } 590 | 591 | .tpl-left-nav-sub-menu a { 592 | display: block; 593 | margin: 0; 594 | padding: 4px 14px 9px 30px; 595 | text-decoration: none; 596 | font-size: 14px; 597 | font-weight: 400; 598 | background: 0 0; 599 | } 600 | 601 | .tpl-left-nav-sub-menu a:hover, 602 | .tpl-left-nav-sub-menu a.active { 603 | color: #5b9bd1; 604 | background: #f2f6f9!important; 605 | } 606 | 607 | .tpl-left-nav-sub-menu a.active i, 608 | .tpl-left-nav-sub-menu a.active span { 609 | color: #5b9bd1; 610 | background: #f2f6f9!important; 611 | } 612 | 613 | .tpl-left-nav-content-ico { 614 | font-size: 14px!important; 615 | position: relative; 616 | top: 8px!important; 617 | color: #ffbe40!important; 618 | } 619 | 620 | .tpl-left-nav-content { 621 | background-color: #36c6d3; 622 | border-radius: 1000px; 623 | color: #fff!important; 624 | padding: 0px 8px!important; 625 | float: right; 626 | position: relative; 627 | font-style: normal; 628 | font-family: "微软雅黑"; 629 | top: 10px!important; 630 | font-size: 12px!important; 631 | } 632 | 633 | .tpl-content-scope {} 634 | 635 | .note { 636 | margin: 0 0 20px; 637 | padding: 15px 30px 15px 15px; 638 | border-left: 5px solid #eee; 639 | border-radius: 0 4px 4px 0; 640 | font-size: 13px; 641 | } 642 | 643 | .note h3 { 644 | margin-bottom: 10px; 645 | font-size: 20px; 646 | font-weight: 500; 647 | } 648 | 649 | .note p { 650 | margin: 0; 651 | font-size: 14px; 652 | line-height: 26px; 653 | } 654 | 655 | .note-info { 656 | background-color: #f5f8fd; 657 | border-color: #8bb4e7; 658 | color: #010407; 659 | } 660 | 661 | .label-danger { 662 | background-color: #ed6b75; 663 | } 664 | 665 | .label { 666 | display: inline; 667 | padding: 0.2em 0.6em 0.3em; 668 | font-size: 75%; 669 | font-weight: bold; 670 | line-height: 1; 671 | color: #fff; 672 | text-align: center; 673 | white-space: nowrap; 674 | vertical-align: baseline; 675 | border-radius: .25em; 676 | } 677 | 678 | .dashboard-stat .visual { 679 | width: 80px; 680 | height: 80px; 681 | display: block; 682 | float: left; 683 | padding-top: 10px; 684 | padding-left: 15px; 685 | margin-bottom: 15px; 686 | font-size: 35px; 687 | line-height: 35px; 688 | } 689 | 690 | .dashboard-stat.blue .visual>i { 691 | color: #FFF; 692 | opacity: .1; 693 | filter: alpha(opacity=10); 694 | } 695 | 696 | .dashboard-stat .visual>i { 697 | margin-left: -35px; 698 | font-size: 110px; 699 | line-height: 110px; 700 | } 701 | 702 | .dashboard-stat .details { 703 | position: absolute; 704 | right: 15px; 705 | padding-right: 15px; 706 | } 707 | 708 | .dashboard-stat.blue .details .number { 709 | color: #FFF; 710 | } 711 | 712 | .dashboard-stat .details .number { 713 | padding-top: 25px; 714 | text-align: right; 715 | font-size: 34px; 716 | line-height: 36px; 717 | letter-spacing: -1px; 718 | margin-bottom: 0; 719 | font-weight: 300; 720 | } 721 | 722 | .dashboard-stat.blue .details .desc { 723 | color: #FFF; 724 | opacity: 1; 725 | filter: alpha(opacity=100); 726 | } 727 | 728 | .dashboard-stat .details .desc { 729 | text-align: right; 730 | font-size: 16px; 731 | letter-spacing: 0; 732 | font-weight: 300; 733 | } 734 | 735 | .dashboard-stat.blue .more { 736 | color: #FFF; 737 | background-color: #258fd7; 738 | } 739 | 740 | .dashboard-stat { 741 | display: block; 742 | margin-bottom: 25px; 743 | overflow: hidden; 744 | border-radius: 4px; 745 | } 746 | 747 | .dashboard-stat .more { 748 | clear: both; 749 | display: block; 750 | padding: 6px 10px; 751 | position: relative; 752 | text-transform: uppercase; 753 | font-weight: 300; 754 | font-size: 11px; 755 | opacity: .7; 756 | filter: alpha(opacity=70); 757 | } 758 | 759 | .row { 760 | margin-left: -18px; 761 | margin-right: -18px; 762 | overflow: hidden; 763 | } 764 | 765 | .row-mb { 766 | margin-bottom: 25px; 767 | } 768 | 769 | .dashboard-stat.blue { 770 | background-color: #3598dc; 771 | } 772 | 773 | .dashboard-stat.red { 774 | background-color: #e7505a; 775 | } 776 | 777 | .dashboard-stat.green { 778 | background-color: #32c5d2; 779 | } 780 | 781 | .dashboard-stat.purple { 782 | background-color: #8E44AD; 783 | } 784 | 785 | .dashboard-stat .more>i { 786 | display: inline-block; 787 | margin-top: 1px; 788 | float: right; 789 | } 790 | 791 | .m-icon-swapright { 792 | background-position: -27px -10px; 793 | } 794 | 795 | .m-icon-white { 796 | background-image: url(../img/syncfusion-icons-white.png); 797 | } 798 | 799 | .dashboard-stat.red .more { 800 | color: #fff; 801 | background-color: #e53e49; 802 | } 803 | 804 | .dashboard-stat.red .visual>i { 805 | color: #fff; 806 | opacity: .1; 807 | filter: alpha(opacity=10); 808 | } 809 | 810 | .dashboard-stat.red .details .number { 811 | color: #fff; 812 | } 813 | 814 | .dashboard-stat.red .details .desc { 815 | color: #fff; 816 | opacity: 1; 817 | filter: alpha(opacity=100); 818 | } 819 | 820 | [class^=m-icon-] { 821 | width: 14px; 822 | height: 14px; 823 | margin-top: 3px; 824 | line-height: 14px; 825 | vertical-align: top; 826 | } 827 | 828 | .dashboard-stat.green .details .number { 829 | color: #FFF; 830 | } 831 | 832 | .dashboard-stat.green .details .desc { 833 | color: #FFF; 834 | opacity: 1; 835 | filter: alpha(opacity=100); 836 | } 837 | 838 | .dashboard-stat.green .more { 839 | color: #FFF; 840 | background-color: #2bb8c4; 841 | } 842 | 843 | .dashboard-stat.green .visual>i { 844 | color: #FFF; 845 | opacity: .1; 846 | filter: alpha(opacity=10); 847 | } 848 | 849 | .dashboard-stat.purple .visual>i { 850 | color: #fff; 851 | opacity: .1; 852 | filter: alpha(opacity=10); 853 | } 854 | 855 | .dashboard-stat.purple .details .number { 856 | color: #fff; 857 | } 858 | 859 | .dashboard-stat.purple .details .desc { 860 | color: #fff; 861 | opacity: 1; 862 | filter: alpha(opacity=100); 863 | } 864 | 865 | .dashboard-stat.purple .more { 866 | color: #fff; 867 | background-color: #823e9e; 868 | } 869 | 870 | .tpl-portlet { 871 | padding: 12px 20px 15px; 872 | background-color: #fff; 873 | border-radius: 4px; 874 | } 875 | 876 | .tpl-portlet-title { 877 | padding: 0; 878 | min-height: 48px; 879 | border-bottom: 1px solid #eef1f5; 880 | margin-bottom: 10px; 881 | overflow: hidden; 882 | } 883 | 884 | .tpl-caption { 885 | color: #666; 886 | padding: 10px 0; 887 | float: left; 888 | display: inline-block; 889 | font-size: 16px; 890 | line-height: 18px; 891 | } 892 | 893 | .font-green { 894 | color: #32c5d2!important; 895 | } 896 | 897 | .font-red { 898 | color: #e7505a!important; 899 | } 900 | 901 | .bold { 902 | font-weight: 700!important; 903 | } 904 | 905 | .actions { 906 | float: right; 907 | display: inline-block; 908 | padding: 6px 0 14px; 909 | } 910 | 911 | .actions-btn { 912 | width: 100%; 913 | } 914 | 915 | .actions-btn li { 916 | display: inline-block; 917 | padding: 4px 14px; 918 | font-size: 12px; 919 | line-height: 1.5; 920 | color: #e7505a; 921 | border: 1px solid #e7505a; 922 | border-radius: 60px; 923 | cursor: pointer; 924 | } 925 | 926 | .actions-btn li:hover { 927 | transition: all .3s; 928 | } 929 | 930 | .actions-btn li.red { 931 | border-color: #e7505a; 932 | color: #e7505a; 933 | background: 0 0; 934 | } 935 | 936 | .actions-btn li.red:hover, 937 | .actions-btn li.red-on { 938 | border-color: #e7505a; 939 | color: #fff; 940 | background-color: #e7505a; 941 | } 942 | 943 | .actions-btn li.green { 944 | border-color: #32c5d2; 945 | color: #32c5d2; 946 | background: 0 0; 947 | } 948 | 949 | .actions-btn li.green:hover, 950 | .actions-btn li.green-on { 951 | border-color: #32c5d2; 952 | color: #FFF; 953 | background-color: #32c5d2; 954 | } 955 | 956 | .actions-btn li.purple { 957 | border-color: #8E44AD; 958 | color: #8E44AD; 959 | background: 0 0; 960 | } 961 | 962 | .actions-btn li.purple:hover, 963 | .actions-btn li.purple-on { 964 | border-color: #8E44AD; 965 | color: #FFF; 966 | background-color: #8E44AD; 967 | } 968 | 969 | .actions-btn li.dark { 970 | border-color: #2f353b; 971 | color: #2f353b; 972 | background: 0 0; 973 | } 974 | 975 | .actions-btn li.dark:hover, 976 | .actions-btn li.dark-on { 977 | border-color: #2f353b; 978 | color: #FFF; 979 | background-color: #2f353b; 980 | } 981 | 982 | .actions-btn li.blue { 983 | border-color: #3598dc; 984 | color: #3598dc; 985 | background: 0 0; 986 | } 987 | 988 | .actions-btn li.blue:hover, 989 | .actions-btn li.blue-on { 990 | border-color: #3598dc; 991 | color: #FFF; 992 | background-color: #3598dc; 993 | } 994 | 995 | .tpl-echarts { 996 | width: 100%; 997 | min-height: 400px; 998 | } 999 | 1000 | .tpl-scrollable { 1001 | width: 100%; 1002 | min-height: 400px; 1003 | } 1004 | 1005 | .number-stats {} 1006 | 1007 | .number-stats { 1008 | padding: 10px 0 16px; 1009 | overflow: hidden; 1010 | } 1011 | 1012 | .number-stats .stat-number .title { 1013 | font-size: 13px; 1014 | margin-bottom: 3px; 1015 | color: #B8C3C7; 1016 | } 1017 | 1018 | .number-stats .stat-number .number { 1019 | font-size: 27px; 1020 | line-height: 27px; 1021 | } 1022 | 1023 | .tpl-table-uppercase { 1024 | font-weight: 600; 1025 | font-size: 13px; 1026 | color: #93a2a9; 1027 | border: 0; 1028 | border-bottom: none; 1029 | } 1030 | 1031 | .tpl-table-uppercase>th { 1032 | border: none!important; 1033 | } 1034 | 1035 | .tpl-table-uppercase td { 1036 | border-top: 1px solid #F2F5F8!important; 1037 | } 1038 | 1039 | .tpl-table .user-pic { 1040 | display: inline-block; 1041 | vertical-align: middle; 1042 | height: 30px; 1043 | -webkit-border-radius: 100%; 1044 | -moz-border-radius: 100%; 1045 | -ms-border-radius: 100%; 1046 | -o-border-radius: 100%; 1047 | border-radius: 100%; 1048 | } 1049 | 1050 | .tpl-table .user-name { 1051 | font-size: 12px; 1052 | } 1053 | 1054 | .tpl-table { 1055 | margin-bottom: 0; 1056 | } 1057 | 1058 | .am-table>tbody>tr>td, 1059 | .am-table>tbody>tr>th, 1060 | .am-table>tfoot>tr>td, 1061 | .am-table>tfoot>tr>th, 1062 | .am-table>thead>tr>td, 1063 | .am-table>thead>tr>th { 1064 | border-top: 1px solid #F2F5F8; 1065 | color: #93a2a9; 1066 | } 1067 | 1068 | .font-green { 1069 | color: #32c5d2!important; 1070 | } 1071 | 1072 | .caption-helper { 1073 | padding: 0; 1074 | margin: 0; 1075 | line-height: 13px; 1076 | color: #9eacb4; 1077 | font-size: 13px; 1078 | font-weight: 400; 1079 | } 1080 | 1081 | .input-inline { 1082 | display: inline-block; 1083 | width: auto; 1084 | vertical-align: middle; 1085 | } 1086 | 1087 | .input-small { 1088 | width: 145px!important; 1089 | } 1090 | 1091 | .input-icon { 1092 | position: relative; 1093 | left: 0; 1094 | } 1095 | 1096 | .input-icon i { 1097 | font-size: 14px; 1098 | margin-top: 9px; 1099 | } 1100 | 1101 | .input-icon>i { 1102 | color: #ccc; 1103 | position: absolute; 1104 | margin-top: 2px; 1105 | z-index: 3; 1106 | width: 16px; 1107 | font-size: 16px; 1108 | text-align: center; 1109 | left: 0; 1110 | left: auto; 1111 | right: 8px; 1112 | float: right; 1113 | } 1114 | 1115 | .form-control { 1116 | display: block; 1117 | width: 100%; 1118 | height: 34px; 1119 | padding: 6px 12px; 1120 | font-size: 14px; 1121 | line-height: 1.42857; 1122 | color: #4d6b8a; 1123 | background-color: #fff; 1124 | background-image: none; 1125 | border: 1px solid #c2cad8; 1126 | border-radius: 4px; 1127 | -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); 1128 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); 1129 | -webkit-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; 1130 | -o-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; 1131 | transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; 1132 | outline: none; 1133 | } 1134 | 1135 | .form-control { 1136 | height: 30px; 1137 | padding: 2px 26px 3px 10px; 1138 | font-size: 13px; 1139 | } 1140 | 1141 | .input-icon.right { 1142 | left: auto; 1143 | right: 0; 1144 | } 1145 | 1146 | .tpl-portlet-input { 1147 | float: right; 1148 | display: inline-block; 1149 | padding: 4px 0; 1150 | } 1151 | 1152 | .wrapper { 1153 | z-index: 1; 1154 | height: 400px; 1155 | width: 100%; 1156 | background: #fff; 1157 | overflow: hidden; 1158 | position: relative; 1159 | } 1160 | 1161 | .scroller { 1162 | position: absolute; 1163 | z-index: 1; 1164 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 1165 | width: 100%; 1166 | -webkit-transform: translateZ(0); 1167 | -moz-transform: translateZ(0); 1168 | -ms-transform: translateZ(0); 1169 | -o-transform: translateZ(0); 1170 | transform: translateZ(0); 1171 | -webkit-touch-callout: none; 1172 | -webkit-user-select: none; 1173 | -moz-user-select: none; 1174 | -ms-user-select: none; 1175 | user-select: none; 1176 | -webkit-text-size-adjust: none; 1177 | -moz-text-size-adjust: none; 1178 | -ms-text-size-adjust: none; 1179 | -o-text-size-adjust: none; 1180 | text-size-adjust: none; 1181 | } 1182 | 1183 | .scroller ul { 1184 | list-style: none; 1185 | padding: 0; 1186 | margin: 0; 1187 | width: 100%; 1188 | text-align: left; 1189 | } 1190 | 1191 | .scroller li { 1192 | padding: 0 10px; 1193 | height: 40px; 1194 | line-height: 40px; 1195 | font-size: 14px; 1196 | } 1197 | 1198 | .iScrollIndicator { 1199 | background: rgb(215, 220, 226)!important; 1200 | border: none!important; 1201 | border-radius: 0!important; 1202 | opacity: 1!important; 1203 | } 1204 | 1205 | .iScrollVerticalScrollbar { 1206 | opacity: 1!important; 1207 | } 1208 | 1209 | .tpl-task-list { 1210 | list-style: none; 1211 | padding: 0; 1212 | margin: 0; 1213 | } 1214 | 1215 | .tpl-task-list li { 1216 | position: relative; 1217 | padding: 10px!important; 1218 | border-bottom: 1px solid #F4F6F9; 1219 | height: auto!important; 1220 | font-size: 14px!important; 1221 | line-height: 22px!important; 1222 | } 1223 | 1224 | .task-checkbox { 1225 | float: left; 1226 | width: 30px; 1227 | } 1228 | 1229 | .task-title { 1230 | color: #838FA1; 1231 | margin-right: 10px; 1232 | } 1233 | 1234 | .task-title-sp { 1235 | margin-right: 5px; 1236 | } 1237 | 1238 | .label-sm { 1239 | font-size: 13px; 1240 | padding: 2px 5px; 1241 | } 1242 | 1243 | .label-success { 1244 | background-color: #36c6d3; 1245 | } 1246 | 1247 | .label { 1248 | text-shadow: none!important; 1249 | font-size: 14px; 1250 | font-weight: 300; 1251 | padding: 3px 6px; 1252 | color: #fff; 1253 | } 1254 | 1255 | .task-config { 1256 | display: none; 1257 | position: absolute; 1258 | top: 1px; 1259 | right: 10px; 1260 | } 1261 | 1262 | .tpl-task-list li:hover .task-config { 1263 | display: block; 1264 | margin-bottom: 0!important; 1265 | } 1266 | 1267 | .tpl-task-list-hover { 1268 | padding: 2px 12px; 1269 | color: #666; 1270 | border-radius: 3px; 1271 | background-color: #e1e5ec; 1272 | border-color: #e1e5ec; 1273 | } 1274 | 1275 | .tpl-task-list-hover:hover { 1276 | color: #999; 1277 | } 1278 | 1279 | .tpl-task-list-dropdown { 1280 | position: absolute; 1281 | top: 10px; 1282 | left: -85px; 1283 | } 1284 | 1285 | .tpl-task-list-dropdown-ul { 1286 | width: 80px!important; 1287 | min-width: 80px!important; 1288 | } 1289 | 1290 | .tpl-task-list-dropdown-ul li { 1291 | /*padding: 0!important;*/ 1292 | text-align: center; 1293 | } 1294 | 1295 | .tpl-task-list>li:hover { 1296 | background: #F4F6F9; 1297 | } 1298 | 1299 | .tpl-task-list-dropdown-ul li a { 1300 | padding: 0!important; 1301 | display: inherit!important; 1302 | } 1303 | 1304 | .label-danger { 1305 | background-color: #ed6b75; 1306 | } 1307 | 1308 | .label-warning { 1309 | background-color: #F1C40F; 1310 | } 1311 | 1312 | .label-default { 1313 | background-color: #bac3d0; 1314 | } 1315 | 1316 | .tpl-index-tabs { 1317 | position: relative; 1318 | } 1319 | 1320 | .tpl-index-tabs .am-nav-tabs { 1321 | border: none; 1322 | position: absolute; 1323 | top: -55px; 1324 | right: 0; 1325 | } 1326 | 1327 | .tpl-index-tabs .am-nav-tabs li a { 1328 | color: #333; 1329 | font-size: 13px; 1330 | border: none; 1331 | padding-bottom: 16px; 1332 | } 1333 | 1334 | .tpl-index-tabs .am-nav-tabs li a:hover { 1335 | background: transparent; 1336 | border: none; 1337 | border-bottom: 4px solid #36c6d3; 1338 | } 1339 | 1340 | .tpl-index-tabs .am-nav-tabs>li.am-active>a, 1341 | .tpl-index-tabs .am-nav-tabs>li.am-active>a:focus, 1342 | .tpl-index-tabs .am-nav-tabs>li.am-active>a:hover { 1343 | border: none; 1344 | } 1345 | 1346 | .tpl-index-tabs .am-nav-tabs li.am-active { 1347 | border: none; 1348 | border-bottom: 4px solid #36c6d3; 1349 | } 1350 | 1351 | .tpl-index-tabs .am-tabs-bd .am-tab-panel { 1352 | padding: 0; 1353 | } 1354 | 1355 | .tpl-index-tabs .am-tabs-bd { 1356 | border: none; 1357 | } 1358 | 1359 | .tpl-task-remind {} 1360 | 1361 | .tpl-task-remind li { 1362 | color: #82949a; 1363 | margin-bottom: 7px; 1364 | } 1365 | 1366 | .tpl-task-remind li .cosA { 1367 | margin-right: 80px; 1368 | } 1369 | 1370 | .tpl-task-remind li .cosB { 1371 | float: right; 1372 | width: 75px; 1373 | margin-left: -75px; 1374 | text-align: right; 1375 | font-style: italic; 1376 | color: #c1cbd0; 1377 | } 1378 | 1379 | .tpl-label-info { 1380 | font-size: 13px; 1381 | padding: 2px 5px; 1382 | background-color: #659be0; 1383 | color: #fff; 1384 | text-align: center; 1385 | white-space: nowrap; 1386 | vertical-align: baseline; 1387 | border-radius: .25em; 1388 | } 1389 | 1390 | .tpl-task-remind li .cosA .cosIco { 1391 | display: inline-block; 1392 | width: 24px; 1393 | height: 24px; 1394 | vertical-align: middle; 1395 | color: #fff; 1396 | text-align: center; 1397 | border-radius: 3px; 1398 | background-color: #36c6d3; 1399 | } 1400 | 1401 | .label-danger { 1402 | background-color: #ed6b75!important; 1403 | } 1404 | 1405 | .label-info { 1406 | background-color: #659be0!important; 1407 | } 1408 | 1409 | .label-warning { 1410 | background-color: #F1C40F!important; 1411 | } 1412 | 1413 | .tpl-portlet-components { 1414 | border: 1px solid #e7ecf1; 1415 | padding: 12px 20px 15px; 1416 | background-color: #fff; 1417 | margin-top: 0; 1418 | margin-bottom: 25px; 1419 | overflow: hidden; 1420 | } 1421 | 1422 | .tpl-portlet-components .portlet-title { 1423 | border-bottom: 1px solid #eef1f5; 1424 | padding: 0; 1425 | min-height: 48px; 1426 | margin-bottom: 10px; 1427 | } 1428 | 1429 | .tpl-portlet-components .portlet-title .caption { 1430 | float: left; 1431 | display: inline-block; 1432 | font-size: 18px; 1433 | line-height: 18px; 1434 | color: #666; 1435 | padding: 10px 0; 1436 | } 1437 | 1438 | .tpl-block { 1439 | padding-top: 10px; 1440 | padding-bottom: 10px; 1441 | font-size: 18px; 1442 | } 1443 | 1444 | .tpl-alert { 1445 | padding: 15px; 1446 | margin-bottom: 20px; 1447 | border: 1px solid transparent; 1448 | border-radius: 4px; 1449 | } 1450 | 1451 | .tpl-fz-ml { 1452 | margin-left: 30px; 1453 | } 1454 | 1455 | .tpl-table-fz-check { 1456 | border: 1px solid transparent; 1457 | height: 12px; 1458 | width: 12px; 1459 | background: #fff; 1460 | } 1461 | 1462 | .am-table-striped>tbody>tr:nth-child(odd)>td, 1463 | .am-table-striped>tbody>tr:nth-child(odd)>th { 1464 | background: #f3f4f6; 1465 | } 1466 | 1467 | .am-selected-btn.am-btn-default { 1468 | border-radius: 3px; 1469 | } 1470 | 1471 | .am-input-group { 1472 | border-radius: 3px; 1473 | overflow: hidden; 1474 | } 1475 | 1476 | .tpl-am-btn-success { 1477 | border-color: #5eb95e!important; 1478 | } 1479 | 1480 | .tpl-pagination .am-disabled a, 1481 | .tpl-pagination li a { 1482 | color: #23abf0; 1483 | border-radius: 3px; 1484 | padding: 6px 12px; 1485 | } 1486 | 1487 | .tpl-pagination .am-active a { 1488 | background: #23abf0; 1489 | color: #fff; 1490 | border: 1px solid #23abf0; 1491 | padding: 6px 12px; 1492 | } 1493 | 1494 | .tpl-table-images {} 1495 | 1496 | .tpl-table-images-content { 1497 | width: 100%; 1498 | border: 1px solid #e7ecf1; 1499 | padding: 26px; 1500 | margin-bottom: 30px; 1501 | } 1502 | 1503 | .tpl-table-images-content-i { 1504 | position: relative; 1505 | display: block; 1506 | width: 100%; 1507 | } 1508 | 1509 | .tpl-table-images-content-i-time { 1510 | width: 100%; 1511 | font-size: 12px; 1512 | color: #666; 1513 | padding-bottom: 10px; 1514 | border-bottom: 1px solid #e7ecf1; 1515 | margin-bottom: 10px; 1516 | } 1517 | 1518 | .tpl-table-images-content-i-shadow { 1519 | background: url(../img/lbbg.png) bottom repeat-x; 1520 | position: absolute; 1521 | left: 0; 1522 | right: 0; 1523 | bottom: 0; 1524 | top: 0; 1525 | } 1526 | 1527 | .tpl-table-images-content-i-info { 1528 | position: absolute; 1529 | left: 10px; 1530 | right: 0; 1531 | bottom: 10px; 1532 | z-index: 2; 1533 | } 1534 | 1535 | .tpl-table-images-content-i-info span.ico { 1536 | line-height: 40px; 1537 | display: inline-block; 1538 | color: #fff; 1539 | font-size: 14px; 1540 | } 1541 | 1542 | .tpl-table-images-content-i-info span.ico img { 1543 | border-radius: 50%; 1544 | width: 40px; 1545 | display: inline-block; 1546 | margin-right: 10px; 1547 | } 1548 | 1549 | .tpl-table-images-content-i img { 1550 | display: block; 1551 | width: 100%; 1552 | } 1553 | 1554 | .tpl-table-images-content-block { 1555 | width: 100%; 1556 | padding-top: 10px; 1557 | color: #333; 1558 | } 1559 | 1560 | .tpl-table-images-content .tpl-i-title { 1561 | font-size: 14px; 1562 | padding-bottom: 10px; 1563 | } 1564 | 1565 | .tpl-table-images-content .tpl-i-font { 1566 | font-size: 14px; 1567 | color: #666; 1568 | overflow: hidden; 1569 | text-overflow: ellipsis; 1570 | display: -webkit-box; 1571 | -webkit-box-orient: vertical; 1572 | line-height: 1.6em; 1573 | -webkit-line-clamp: 2; 1574 | max-height: 3em; 1575 | } 1576 | 1577 | .tpl-table-images-content .tpl-i-more {} 1578 | 1579 | .tpl-table-images-content .tpl-i-more ul { 1580 | border-top: 1px solid #e7ecf1; 1581 | border-bottom: 1px solid #e7ecf1; 1582 | margin-top: 10px; 1583 | width: 100%; 1584 | overflow: hidden; 1585 | padding: 10px 0px; 1586 | } 1587 | 1588 | .tpl-table-images-content .tpl-i-more li { 1589 | text-align: center; 1590 | width: 33.3333%; 1591 | font-size: 14px; 1592 | float: left; 1593 | } 1594 | 1595 | .tpl-edit-content-btn { 1596 | width: 100%; 1597 | } 1598 | 1599 | .tpl-edit-content-btn button { 1600 | width: 25%!important; 1601 | } 1602 | 1603 | .tpl-form-body { 1604 | padding: 20px; 1605 | } 1606 | 1607 | .tpl-form-line {} 1608 | 1609 | .tpl-form-line-form {} 1610 | 1611 | .tpl-form-line-form input[type=number]:focus, 1612 | .tpl-form-line-form input[type=search]:focus, 1613 | .tpl-form-line-form input[type=text]:focus, 1614 | .tpl-form-line-form input[type=password]:focus, 1615 | .tpl-form-line-form input[type=datetime]:focus, 1616 | .tpl-form-line-form input[type=datetime-local]:focus, 1617 | .tpl-form-line-form input[type=date]:focus, 1618 | .tpl-form-line-form input[type=month]:focus, 1619 | .tpl-form-line-form input[type=time]:focus, 1620 | .tpl-form-line-form input[type=week]:focus, 1621 | .tpl-form-line-form input[type=email]:focus, 1622 | .tpl-form-line-form input[type=url]:focus, 1623 | .tpl-form-line-form input[type=tel]:focus, 1624 | .tpl-form-line-form input[type=color]:focus, 1625 | .tpl-form-line-form select:focus, 1626 | .tpl-form-line-form textarea:focus, 1627 | .am-form-field:focus { 1628 | -webkit-box-shadow: none; 1629 | box-shadow: none; 1630 | } 1631 | 1632 | .tpl-form-line-form input[type=number], 1633 | .tpl-form-line-form input[type=search], 1634 | .tpl-form-line-form input[type=text], 1635 | .tpl-form-line-form input[type=password], 1636 | .tpl-form-line-form input[type=datetime], 1637 | .tpl-form-line-form input[type=datetime-local], 1638 | .tpl-form-line-form input[type=date], 1639 | .tpl-form-line-form input[type=month], 1640 | .tpl-form-line-form input[type=time], 1641 | .tpl-form-line-form input[type=week], 1642 | .tpl-form-line-form input[type=email], 1643 | .tpl-form-line-form input[type=url], 1644 | .tpl-form-line-form input[type=tel], 1645 | .tpl-form-line-form input[type=color], 1646 | .tpl-form-line-form select, 1647 | .tpl-form-line-form textarea, 1648 | .am-form-field { 1649 | display: block; 1650 | width: 100%; 1651 | padding: 6px 12px; 1652 | font-size: 14px; 1653 | line-height: 1.42857; 1654 | color: #4d6b8a; 1655 | background-color: #fff; 1656 | background-image: none; 1657 | border: 1px solid #c2cad8; 1658 | border-radius: 4px; 1659 | -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); 1660 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); 1661 | -webkit-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; 1662 | -o-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; 1663 | transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; 1664 | background: 0 0; 1665 | border: 0; 1666 | border-bottom: 1px solid #c2cad8; 1667 | -webkit-border-radius: 0; 1668 | -moz-border-radius: 0; 1669 | -ms-border-radius: 0; 1670 | -o-border-radius: 0; 1671 | border-radius: 0; 1672 | color: #555; 1673 | box-shadow: none; 1674 | padding-left: 0; 1675 | padding-right: 0; 1676 | font-size: 14px; 1677 | } 1678 | 1679 | .tpl-amazeui-form {} 1680 | 1681 | .tpl-amazeui-form .am-form-label { 1682 | color: #999; 1683 | font-weight: normal; 1684 | font-size: 14px; 1685 | } 1686 | 1687 | .tpl-amazeui-form input::-webkit-input-placeholder { 1688 | font-size: 12px; 1689 | } 1690 | 1691 | .tpl-amazeui-form textarea::-webkit-input-placeholder { 1692 | font-size: 12px; 1693 | } 1694 | 1695 | .tpl-amazeui-form small { 1696 | font-size: 12px; 1697 | } 1698 | 1699 | .tpl-form-line-form .am-checkbox, 1700 | .tpl-form-line-form .am-checkbox-inline, 1701 | .tpl-form-line-form .am-form-label, 1702 | .tpl-form-line-form .am-radio, 1703 | .tpl-form-line-form .am-radio-inline { 1704 | margin-top: 0; 1705 | margin-bottom: 0; 1706 | } 1707 | 1708 | .tpl-form-line-form .am-form-group:after { 1709 | clear: both; 1710 | } 1711 | 1712 | .tpl-form-line-form .am-form-group:after, 1713 | .tpl-form-line-form .am-form-group:before { 1714 | content: " "; 1715 | display: table; 1716 | } 1717 | 1718 | .tpl-form-line-form .am-form-label { 1719 | padding-top: 5px; 1720 | font-size: 16px; 1721 | color: #888; 1722 | font-weight: inherit; 1723 | text-align: right; 1724 | } 1725 | 1726 | .tpl-form-line-form .am-form-group { 1727 | /*padding: 20px 0;*/ 1728 | } 1729 | 1730 | .tpl-form-line-form .am-form-label .tpl-form-line-small-title { 1731 | color: #999; 1732 | font-size: 12px; 1733 | } 1734 | 1735 | .tpl-form-no-bg { 1736 | background: none!important; 1737 | } 1738 | 1739 | .tpl-switch input[type="checkbox"] { 1740 | position: absolute; 1741 | opacity: 0; 1742 | } 1743 | 1744 | .tpl-switch input[type="checkbox"].ios-switch+div { 1745 | vertical-align: middle; 1746 | width: 40px; 1747 | height: 20px; 1748 | border-radius: 999px; 1749 | background-color: rgba(0, 0, 0, 0.1); 1750 | -webkit-transition-duration: .4s; 1751 | -webkit-transition-property: background-color, box-shadow; 1752 | margin-top: 6px; 1753 | } 1754 | 1755 | .tpl-switch input[type="checkbox"].ios-switch:checked+div { 1756 | width: 40px; 1757 | background-position: 0 0; 1758 | background-color: #36c6d3; 1759 | } 1760 | 1761 | .tpl-switch input[type="checkbox"].tinyswitch.ios-switch+div { 1762 | width: 34px; 1763 | height: 18px; 1764 | } 1765 | 1766 | .tpl-switch input[type="checkbox"].bigswitch.ios-switch+div { 1767 | width: 50px; 1768 | height: 25px; 1769 | } 1770 | 1771 | .tpl-switch input[type="checkbox"].green.ios-switch:checked+div { 1772 | background-color: #00e359; 1773 | border: 1px solid rgba(0, 162, 63, 1); 1774 | box-shadow: inset 0 0 0 10px rgba(0, 227, 89, 1); 1775 | } 1776 | 1777 | .tpl-switch input[type="checkbox"].ios-switch+div>div { 1778 | float: left; 1779 | width: 18px; 1780 | height: 18px; 1781 | border-radius: inherit; 1782 | background: #ffffff; 1783 | -webkit-transition-timing-function: cubic-bezier(.54, 1.85, .5, 1); 1784 | -webkit-transition-duration: 0.4s; 1785 | -webkit-transition-property: transform, background-color, box-shadow; 1786 | -moz-transition-timing-function: cubic-bezier(.54, 1.85, .5, 1); 1787 | -moz-transition-duration: 0.4s; 1788 | -moz-transition-property: transform, background-color; 1789 | pointer-events: none; 1790 | margin-top: 1px; 1791 | margin-left: 1px; 1792 | } 1793 | 1794 | .tpl-switch input[type="checkbox"].ios-switch:checked+div>div { 1795 | -webkit-transform: translate3d(20px, 0, 0); 1796 | -moz-transform: translate3d(20px, 0, 0); 1797 | background-color: #ffffff; 1798 | } 1799 | 1800 | .tpl-switch input[type="checkbox"].tinyswitch.ios-switch+div>div { 1801 | width: 16px; 1802 | height: 16px; 1803 | margin-top: 1px; 1804 | } 1805 | 1806 | .tpl-switch input[type="checkbox"].tinyswitch.ios-switch:checked+div>div { 1807 | -webkit-transform: translate3d(16px, 0, 0); 1808 | -moz-transform: translate3d(16px, 0, 0); 1809 | box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.3), 0px 0px 0 1px rgba(8, 80, 172, 1); 1810 | } 1811 | 1812 | .tpl-switch input[type="checkbox"].bigswitch.ios-switch+div>div { 1813 | width: 23px; 1814 | height: 23px; 1815 | margin-top: 1px; 1816 | } 1817 | 1818 | .tpl-switch input[type="checkbox"].bigswitch.ios-switch:checked+div>div { 1819 | -webkit-transform: translate3d(25px, 0, 0); 1820 | -moz-transform: translate3d(16px, 0, 0); 1821 | } 1822 | 1823 | .tpl-switch input[type="checkbox"].green.ios-switch:checked+div>div { 1824 | box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.3), 0 0 0 1px rgba(0, 162, 63, 1); 1825 | } 1826 | 1827 | .tpl-btn-bg-color-success { 1828 | background-color: #36c6d3!important; 1829 | border: none; 1830 | } 1831 | 1832 | .tpl-form-file-img { 1833 | width: 300px; 1834 | margin-bottom: 10px; 1835 | } 1836 | 1837 | .tpl-form-file-img img { 1838 | width: 100%; 1839 | display: block; 1840 | } 1841 | 1842 | .myapp-login { 1843 | background: #334054; 1844 | background-size: 100%; 1845 | height: 100%; 1846 | } 1847 | 1848 | .myapp-login-logo-block { 1849 | width: 100%; 1850 | } 1851 | 1852 | .myapp-login-logo { 1853 | width: 100%; 1854 | text-align: center; 1855 | padding-top: 30px; 1856 | } 1857 | 1858 | .myapp-login-logo i { 1859 | color: #eb602e; 1860 | font-size: 120px; 1861 | display: inline-block; 1862 | } 1863 | 1864 | .myapp-login-logo-text { 1865 | padding-top: 30px; 1866 | font-family: Helvetica, Arial, "Microsoft YaHei", FreeSans, Arimo, "Droid Sans", "wenquanyi micro hei", "Hiragino Sans GB", "Hiragino Sans GB W3", FontAwesome, sans-serif; 1867 | color: #fff; 1868 | font-weight: bold; 1869 | font-size: 40px; 1870 | text-align: center; 1871 | width: 100%; 1872 | } 1873 | 1874 | .myapp-login-logo-text span { 1875 | color: #53d192; 1876 | } 1877 | 1878 | .myapp-login-logo-text i { 1879 | color: #53d192; 1880 | font-size: 50px; 1881 | } 1882 | 1883 | .myapp-login-logo-text .info { 1884 | padding-bottom: 30px; 1885 | border-bottom: 1px solid #4d4d4d; 1886 | font-family: FreeSans, Arimo, "Droid Sans", "wenquanyi micro hei", "Hiragino Sans GB", "Hiragino Sans GB W3", FontAwesome, sans-serif; 1887 | width: 100%; 1888 | font-weight: normal; 1889 | font-size: 14px; 1890 | color: #fff; 1891 | } 1892 | 1893 | .login-font { 1894 | font-size: 12px; 1895 | font-family: "wenquanyi micro hei", "Hiragino Sans GB", "Hiragino Sans GB W3", FontAwesome, sans-serif; 1896 | width: 100%; 1897 | color: #5e5e5e; 1898 | text-align: center; 1899 | padding: 20px 0; 1900 | padding-top: 10px; 1901 | } 1902 | 1903 | .login-font i { 1904 | color: #53d192; 1905 | font-style: normal; 1906 | } 1907 | 1908 | .login-font span { 1909 | color: #fff; 1910 | } 1911 | 1912 | .myapp-login .am-form-group { 1913 | margin-bottom: 0; 1914 | } 1915 | 1916 | .login-am-center { 1917 | margin: 0 auto; 1918 | float: none; 1919 | } 1920 | 1921 | .login-am-center .am-form input { 1922 | background: #fff; 1923 | border: none; 1924 | font-size: 12px; 1925 | line-height: 30px; 1926 | text-indent: 10px; 1927 | border-radius: 0px 0px 6px 6px; 1928 | } 1929 | 1930 | .login-am-center .am-form .am-form-group:first-child input { 1931 | border-radius: 6px 6px 0px 0px; 1932 | } 1933 | 1934 | .login-am-center .am-btn-default { 1935 | width: 100%; 1936 | border-radius: 6px; 1937 | background: #53d192; 1938 | border: none; 1939 | color: #fff; 1940 | font-size: 14px; 1941 | line-height: 30px; 1942 | } 1943 | 1944 | .tpl-login-max { 1945 | max-width: 640px; 1946 | margin: 0 auto; 1947 | } 1948 | 1949 | .am-topbar-btn { 1950 | margin-top: 21px; 1951 | } 1952 | 1953 | .tpl-chart-mb { 1954 | margin-top: 20px; 1955 | margin-bottom: 40px; 1956 | } 1957 | 1958 | @media screen and (max-width: 1000px) { 1959 | .tpl-left-nav-hover { 1960 | display: none; 1961 | width: 100%; 1962 | margin-bottom: 10px; 1963 | } 1964 | .tpl-left-nav-list { 1965 | width: 100%; 1966 | } 1967 | .tpl-content-wrapper { 1968 | padding-left: 0; 1969 | } 1970 | } -------------------------------------------------------------------------------- /model_log/static/css/style.css: -------------------------------------------------------------------------------- 1 | @import url("https://fonts.googleapis.com/css?family=Nunito+Sans"); 2 | :root { 3 | --blue: #0e0620; 4 | --white: #fff; 5 | --green: #2ccf6d; 6 | } 7 | 8 | html, 9 | body { 10 | height: 100%; 11 | } 12 | 13 | body { 14 | display: -webkit-box; 15 | display: flex; 16 | -webkit-box-align: center; 17 | align-items: center; 18 | -webkit-box-pack: center; 19 | justify-content: center; 20 | font-family: "Nunito Sans"; 21 | color: var(--blue); 22 | font-size: 1em; 23 | } 24 | 25 | button { 26 | font-family: "Nunito Sans"; 27 | } 28 | 29 | ul { 30 | list-style-type: none; 31 | -webkit-padding-start: 35px; 32 | padding-inline-start: 35px; 33 | } 34 | 35 | svg { 36 | width: 100%; 37 | visibility: hidden; 38 | } 39 | 40 | h1 { 41 | font-size: 7.5em; 42 | margin: 15px 0px; 43 | font-weight: bold; 44 | } 45 | 46 | h2 { 47 | font-weight: bold; 48 | } 49 | 50 | .hamburger-menu { 51 | position: absolute; 52 | top: 0; 53 | left: 0; 54 | padding: 35px; 55 | z-index: 2; 56 | } 57 | .hamburger-menu button { 58 | position: relative; 59 | width: 30px; 60 | height: 22px; 61 | border: none; 62 | background: none; 63 | padding: 0; 64 | cursor: pointer; 65 | } 66 | .hamburger-menu button span { 67 | position: absolute; 68 | height: 3px; 69 | background: #000; 70 | width: 100%; 71 | left: 0px; 72 | top: 0px; 73 | -webkit-transition: 0.1s ease-in; 74 | transition: 0.1s ease-in; 75 | } 76 | .hamburger-menu button span:nth-child(2) { 77 | top: 9px; 78 | } 79 | .hamburger-menu button span:nth-child(3) { 80 | top: 18px; 81 | } 82 | .hamburger-menu [data-state="open"] span:first-child { 83 | -webkit-transform: rotate(45deg); 84 | transform: rotate(45deg); 85 | top: 10px; 86 | } 87 | .hamburger-menu [data-state="open"] span:nth-child(2) { 88 | width: 0%; 89 | opacity: 0; 90 | } 91 | .hamburger-menu [data-state="open"] span:nth-child(3) { 92 | -webkit-transform: rotate(-45deg); 93 | transform: rotate(-45deg); 94 | top: 10px; 95 | } 96 | 97 | nav { 98 | position: absolute; 99 | height: 100%; 100 | top: 0; 101 | left: 0; 102 | background: var(--green); 103 | color: var(--blue); 104 | width: 300px; 105 | z-index: 1; 106 | padding-top: 80px; 107 | -webkit-transform: translateX(-100%); 108 | transform: translateX(-100%); 109 | -webkit-transition: 0.24s cubic-bezier(0.52, 0.01, 0.8, 1); 110 | transition: 0.24s cubic-bezier(0.52, 0.01, 0.8, 1); 111 | } 112 | nav li { 113 | -webkit-transform: translateX(-5px); 114 | transform: translateX(-5px); 115 | -webkit-transition: 0.16s cubic-bezier(0.44, 0.09, 0.46, 0.84); 116 | transition: 0.16s cubic-bezier(0.44, 0.09, 0.46, 0.84); 117 | opacity: 0; 118 | } 119 | nav a { 120 | display: block; 121 | font-size: 1.75em; 122 | font-weight: bold; 123 | text-decoration: none; 124 | color: inherit; 125 | -webkit-transition: 0.24s ease-in-out; 126 | transition: 0.24s ease-in-out; 127 | } 128 | nav a:hover { 129 | text-decoration: none; 130 | color: var(--white); 131 | } 132 | nav[data-state="open"] { 133 | -webkit-transform: translateX(0%); 134 | transform: translateX(0%); 135 | } 136 | nav[data-state="open"] ul li:nth-child(1) { 137 | -webkit-transition-delay: 0.16s; 138 | transition-delay: 0.16s; 139 | -webkit-transform: translateX(0px); 140 | transform: translateX(0px); 141 | opacity: 1; 142 | } 143 | nav[data-state="open"] ul li:nth-child(2) { 144 | -webkit-transition-delay: 0.32s; 145 | transition-delay: 0.32s; 146 | -webkit-transform: translateX(0px); 147 | transform: translateX(0px); 148 | opacity: 1; 149 | } 150 | nav[data-state="open"] ul li:nth-child(3) { 151 | -webkit-transition-delay: 0.48s; 152 | transition-delay: 0.48s; 153 | -webkit-transform: translateX(0px); 154 | transform: translateX(0px); 155 | opacity: 1; 156 | } 157 | nav[data-state="open"] ul li:nth-child(4) { 158 | -webkit-transition-delay: 0.64s; 159 | transition-delay: 0.64s; 160 | -webkit-transform: translateX(0px); 161 | transform: translateX(0px); 162 | opacity: 1; 163 | } 164 | 165 | .btn { 166 | z-index: 1; 167 | overflow: hidden; 168 | background: transparent; 169 | position: relative; 170 | padding: 8px 50px; 171 | border-radius: 30px; 172 | cursor: pointer; 173 | font-size: 1em; 174 | letter-spacing: 2px; 175 | -webkit-transition: 0.2s ease; 176 | transition: 0.2s ease; 177 | font-weight: bold; 178 | margin: 5px 0px; 179 | } 180 | .btn.green { 181 | border: 4px solid var(--green); 182 | color: var(--blue); 183 | } 184 | .btn.green:before { 185 | content: ""; 186 | position: absolute; 187 | left: 0; 188 | top: 0; 189 | width: 0%; 190 | height: 100%; 191 | background: var(--green); 192 | z-index: -1; 193 | -webkit-transition: 0.2s ease; 194 | transition: 0.2s ease; 195 | } 196 | .btn.green:hover { 197 | color: var(--white); 198 | background: var(--green); 199 | -webkit-transition: 0.2s ease; 200 | transition: 0.2s ease; 201 | } 202 | .btn.green:hover:before { 203 | width: 100%; 204 | } 205 | 206 | @media screen and (max-width: 768px) { 207 | body { 208 | display: block; 209 | } 210 | 211 | .container { 212 | margin-top: 70px; 213 | margin-bottom: 70px; 214 | } 215 | } -------------------------------------------------------------------------------- /model_log/static/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NLP-LOVE/Model_Log/fc0a46afe9702d6cb5ba508fac3a0dc3bd386e5f/model_log/static/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /model_log/static/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NLP-LOVE/Model_Log/fc0a46afe9702d6cb5ba508fac3a0dc3bd386e5f/model_log/static/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /model_log/static/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NLP-LOVE/Model_Log/fc0a46afe9702d6cb5ba508fac3a0dc3bd386e5f/model_log/static/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /model_log/static/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NLP-LOVE/Model_Log/fc0a46afe9702d6cb5ba508fac3a0dc3bd386e5f/model_log/static/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /model_log/static/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NLP-LOVE/Model_Log/fc0a46afe9702d6cb5ba508fac3a0dc3bd386e5f/model_log/static/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /model_log/static/i/app-icon72x72@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NLP-LOVE/Model_Log/fc0a46afe9702d6cb5ba508fac3a0dc3bd386e5f/model_log/static/i/app-icon72x72@2x.png -------------------------------------------------------------------------------- /model_log/static/i/examples/admin-chrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NLP-LOVE/Model_Log/fc0a46afe9702d6cb5ba508fac3a0dc3bd386e5f/model_log/static/i/examples/admin-chrome.png -------------------------------------------------------------------------------- /model_log/static/i/examples/admin-firefox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NLP-LOVE/Model_Log/fc0a46afe9702d6cb5ba508fac3a0dc3bd386e5f/model_log/static/i/examples/admin-firefox.png -------------------------------------------------------------------------------- /model_log/static/i/examples/admin-ie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NLP-LOVE/Model_Log/fc0a46afe9702d6cb5ba508fac3a0dc3bd386e5f/model_log/static/i/examples/admin-ie.png -------------------------------------------------------------------------------- /model_log/static/i/examples/admin-opera.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NLP-LOVE/Model_Log/fc0a46afe9702d6cb5ba508fac3a0dc3bd386e5f/model_log/static/i/examples/admin-opera.png -------------------------------------------------------------------------------- /model_log/static/i/examples/admin-safari.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NLP-LOVE/Model_Log/fc0a46afe9702d6cb5ba508fac3a0dc3bd386e5f/model_log/static/i/examples/admin-safari.png -------------------------------------------------------------------------------- /model_log/static/i/examples/adminPage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NLP-LOVE/Model_Log/fc0a46afe9702d6cb5ba508fac3a0dc3bd386e5f/model_log/static/i/examples/adminPage.png -------------------------------------------------------------------------------- /model_log/static/i/examples/blogPage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NLP-LOVE/Model_Log/fc0a46afe9702d6cb5ba508fac3a0dc3bd386e5f/model_log/static/i/examples/blogPage.png -------------------------------------------------------------------------------- /model_log/static/i/examples/landing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NLP-LOVE/Model_Log/fc0a46afe9702d6cb5ba508fac3a0dc3bd386e5f/model_log/static/i/examples/landing.png -------------------------------------------------------------------------------- /model_log/static/i/examples/landingPage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NLP-LOVE/Model_Log/fc0a46afe9702d6cb5ba508fac3a0dc3bd386e5f/model_log/static/i/examples/landingPage.png -------------------------------------------------------------------------------- /model_log/static/i/examples/loginPage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NLP-LOVE/Model_Log/fc0a46afe9702d6cb5ba508fac3a0dc3bd386e5f/model_log/static/i/examples/loginPage.png -------------------------------------------------------------------------------- /model_log/static/i/examples/sidebarPage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NLP-LOVE/Model_Log/fc0a46afe9702d6cb5ba508fac3a0dc3bd386e5f/model_log/static/i/examples/sidebarPage.png -------------------------------------------------------------------------------- /model_log/static/i/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NLP-LOVE/Model_Log/fc0a46afe9702d6cb5ba508fac3a0dc3bd386e5f/model_log/static/i/favicon.png -------------------------------------------------------------------------------- /model_log/static/i/startup-640x1096.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NLP-LOVE/Model_Log/fc0a46afe9702d6cb5ba508fac3a0dc3bd386e5f/model_log/static/i/startup-640x1096.png -------------------------------------------------------------------------------- /model_log/static/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NLP-LOVE/Model_Log/fc0a46afe9702d6cb5ba508fac3a0dc3bd386e5f/model_log/static/img/logo.png -------------------------------------------------------------------------------- /model_log/static/img/syncfusion-icons-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NLP-LOVE/Model_Log/fc0a46afe9702d6cb5ba508fac3a0dc3bd386e5f/model_log/static/img/syncfusion-icons-white.png -------------------------------------------------------------------------------- /model_log/static/js/app.js: -------------------------------------------------------------------------------- 1 | 2 | $(function() { 3 | var $fullText = $('.admin-fullText'); 4 | $('#admin-fullscreen').on('click', function() { 5 | $.AMUI.fullscreen.toggle(); 6 | }); 7 | 8 | $(document).on($.AMUI.fullscreen.raw.fullscreenchange, function() { 9 | $fullText.text($.AMUI.fullscreen.isFullscreen ? '退出全屏' : '开启全屏'); 10 | }); 11 | 12 | 13 | var dataType = $('body').attr('data-type'); 14 | for (key in pageData) { 15 | if (key == dataType) { 16 | pageData[key](); 17 | } 18 | } 19 | 20 | $('.tpl-switch').find('.tpl-switch-btn-view').on('click', function() { 21 | $(this).prev('.tpl-switch-btn').prop("checked", function() { 22 | if ($(this).is(':checked')) { 23 | return false 24 | } else { 25 | return true 26 | } 27 | }) 28 | // console.log('123123123') 29 | 30 | }) 31 | }) 32 | // ========================== 33 | // 侧边导航下拉列表 34 | // ========================== 35 | 36 | $('.tpl-left-nav-link-list').on('click', function() { 37 | $(this).siblings('.tpl-left-nav-sub-menu').slideToggle(80) 38 | .end() 39 | .find('.tpl-left-nav-more-ico').toggleClass('tpl-left-nav-more-ico-rotate'); 40 | }) 41 | // ========================== 42 | // 头部导航隐藏菜单 43 | // ========================== 44 | 45 | $('.tpl-header-nav-hover-ico').on('click', function() { 46 | $('.tpl-left-nav').toggle(); 47 | $('.tpl-content-wrapper').toggleClass('tpl-content-wrapper-hover'); 48 | }) 49 | 50 | 51 | // 页面数据 52 | var pageData = { 53 | // =============================================== 54 | // 图表页 55 | // =============================================== 56 | 'chart': function chartData() { 57 | // ========================== 58 | // 百度图表A http://echarts.baidu.com/ 59 | // ========================== 60 | 61 | var echartsC = echarts.init(document.getElementById('tpl-echarts-C')); 62 | 63 | 64 | optionC = { 65 | tooltip: { 66 | trigger: 'axis' 67 | }, 68 | toolbox: { 69 | top: '0', 70 | feature: { 71 | dataView: { show: true, readOnly: false }, 72 | magicType: { show: true, type: ['line', 'bar'] }, 73 | restore: { show: true }, 74 | saveAsImage: { show: true } 75 | } 76 | }, 77 | legend: { 78 | data: ['蒸发量', '降水量', '平均温度'] 79 | }, 80 | xAxis: [{ 81 | type: 'category', 82 | data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'] 83 | }], 84 | yAxis: [{ 85 | type: 'value', 86 | name: '水量', 87 | min: 0, 88 | max: 250, 89 | interval: 50, 90 | axisLabel: { 91 | formatter: '{value} ml' 92 | } 93 | }, 94 | { 95 | type: 'value', 96 | name: '温度', 97 | min: 0, 98 | max: 25, 99 | interval: 5, 100 | axisLabel: { 101 | formatter: '{value} °C' 102 | } 103 | } 104 | ], 105 | series: [{ 106 | name: '蒸发量', 107 | type: 'bar', 108 | data: [2.0, 4.9, 7.0, 23.2, 25.6, 76.7, 135.6, 162.2, 32.6, 20.0, 6.4, 3.3] 109 | }, 110 | { 111 | name: '降水量', 112 | type: 'bar', 113 | data: [2.6, 5.9, 9.0, 26.4, 28.7, 70.7, 175.6, 182.2, 48.7, 18.8, 6.0, 2.3] 114 | }, 115 | { 116 | name: '平均温度', 117 | type: 'line', 118 | yAxisIndex: 1, 119 | data: [2.0, 2.2, 3.3, 4.5, 6.3, 10.2, 20.3, 23.4, 23.0, 16.5, 12.0, 6.2] 120 | } 121 | ] 122 | }; 123 | 124 | echartsC.setOption(optionC); 125 | 126 | } 127 | } -------------------------------------------------------------------------------- /model_log/static/js/script.js: -------------------------------------------------------------------------------- 1 | gsap.set("svg", { visibility: "visible" }); 2 | gsap.to("#headStripe", { 3 | y: 0.5, 4 | rotation: 1, 5 | yoyo: true, 6 | repeat: -1, 7 | ease: "sine.inOut", 8 | duration: 1 }); 9 | 10 | gsap.to("#spaceman", { 11 | y: 0.5, 12 | rotation: 1, 13 | yoyo: true, 14 | repeat: -1, 15 | ease: "sine.inOut", 16 | duration: 1 }); 17 | 18 | gsap.to("#craterSmall", { 19 | x: -3, 20 | yoyo: true, 21 | repeat: -1, 22 | duration: 1, 23 | ease: "sine.inOut" }); 24 | 25 | gsap.to("#craterBig", { 26 | x: 3, 27 | yoyo: true, 28 | repeat: -1, 29 | duration: 1, 30 | ease: "sine.inOut" }); 31 | 32 | gsap.to("#planet", { 33 | rotation: -2, 34 | yoyo: true, 35 | repeat: -1, 36 | duration: 1, 37 | ease: "sine.inOut", 38 | transformOrigin: "50% 50%" }); 39 | 40 | 41 | gsap.to("#starsBig g", { 42 | rotation: "random(-30,30)", 43 | transformOrigin: "50% 50%", 44 | yoyo: true, 45 | repeat: -1, 46 | ease: "sine.inOut" }); 47 | 48 | gsap.fromTo( 49 | "#starsSmall g", 50 | { scale: 0, transformOrigin: "50% 50%" }, 51 | { scale: 1, transformOrigin: "50% 50%", yoyo: true, repeat: -1, stagger: 0.1 }); 52 | 53 | gsap.to("#circlesSmall circle", { 54 | y: -4, 55 | yoyo: true, 56 | duration: 1, 57 | ease: "sine.inOut", 58 | repeat: -1 }); 59 | 60 | gsap.to("#circlesBig circle", { 61 | y: -2, 62 | yoyo: true, 63 | duration: 1, 64 | ease: "sine.inOut", 65 | repeat: -1 }); 66 | 67 | 68 | gsap.set("#glassShine", { x: -68 }); 69 | 70 | gsap.to("#glassShine", { 71 | x: 80, 72 | duration: 2, 73 | rotation: -30, 74 | ease: "expo.inOut", 75 | transformOrigin: "50% 50%", 76 | repeat: -1, 77 | repeatDelay: 8, 78 | delay: 2 }); 79 | 80 | 81 | const burger = document.querySelector('.burger'); 82 | const nav = document.querySelector('nav'); 83 | 84 | burger.addEventListener('click', e => { 85 | burger.dataset.state === 'closed' ? burger.dataset.state = "open" : burger.dataset.state = "closed"; 86 | nav.dataset.state === "closed" ? nav.dataset.state = "open" : nav.dataset.state = "closed"; 87 | }); -------------------------------------------------------------------------------- /model_log/templates/alert.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 电脑端访问 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 |
20 |
21 | 22 | 23 | 24 | 25 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 43 | 44 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 144 | 145 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 158 | 159 | 163 | 164 | 165 | 166 | 169 | 170 | 174 | 175 | 176 | 177 | 181 | 183 | 184 | 185 | 186 | 187 | 190 | 191 | 192 | 193 | 194 | 195 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 207 | 208 | 212 | 213 | 215 | 216 | 217 | 218 | 219 | 220 |
221 |
222 |

Ծ‸Ծ

223 |

UH OH!

224 |

电脑端访问效果更佳!

225 |

226 |
227 |
228 |
229 |
230 | 231 | 232 | 233 | 234 | 235 | 236 | -------------------------------------------------------------------------------- /model_log/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Model Log 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 |
25 | 28 |
29 |
30 | 31 |
32 | 33 | 34 | 35 | {% if is_login %} 36 |
37 | 欢迎 {{ nick_name }}!    38 | 39 |
40 | {% endif %} 41 | 42 |
43 | 44 |
45 |
46 | 47 | 48 | 49 | 50 | 51 | 52 |
53 |
54 |
项目列表
55 |
    56 |
  1. 首页
  2. 57 |
  3. 项目列表
  4. 58 |
59 |
60 |
61 |
62 |
63 | 项目列表
64 |
65 |
66 |
67 |
68 |
69 |
70 | 72 |
73 |
74 |
75 |
76 |
77 |
78 | 79 | 80 | 81 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 |
82 | ID项目备注创建时间操作
92 | 93 | 96 | 97 | 100 | 101 | 102 |
103 |
104 |
    105 | 106 | 107 |
  • 108 | 上一页 109 |
  • 110 | 111 | 112 |
  • 113 | 115 |
  • 116 | 117 | 118 |
  • 119 | 下一页 120 |
  • 121 | 122 |
123 | 124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 | 136 | 152 | 153 | 154 | 155 |
156 |
157 |
消息
158 |
159 | 160 |
161 | 164 |
165 |
166 | 167 | 168 |
169 |
170 |
警告
171 |
172 | 你,确定要删除这些记录吗? 173 |
174 | 178 |
179 |
180 | 181 | 182 |
183 |
184 |
Login
185 |
186 | 昵称 187 | 188 |
189 | 193 |
194 |
195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 511 | 512 | 513 | -------------------------------------------------------------------------------- /model_log/templates/model_detail.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Model Log 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 |
26 | 28 |
29 | 33 | 34 | 35 | 项目列表 36 | 37 | 38 | 39 | 40 | 41 | 42 |   {{ nick_name }} !   43 | 欢迎 44 | 45 | 46 |
47 |
48 |
49 |
50 |
模型参数数据
51 |
    52 |
  1. 53 | 首页
  2. 54 |
  3. 55 | 项目列表
  4. 56 |
  5. 模型参数
57 |
58 |
59 | 60 |
61 | {{ project_name }} 62 |
63 | 64 |
65 |
66 |
67 |
68 |
69 | 71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 | 79 | 80 | 81 | 83 | 84 | 85 | 86 | 87 | {% for type in table_head %} 88 | 89 | 90 | {% endfor %} 91 | 92 | {% for name in best_head %} 93 | 94 | {% endfor %} 95 | 96 | 97 | {% for type in table_head %} {% for param_name in table_head[type] %} 98 | {% endfor %} {% endfor %} 99 | {% for item in table_data %} 100 | 101 | 103 | 104 | 106 | 115 | 116 | {% for type in table_head %} {% for param_name in table_head[type] %} 117 | {% endfor %} 118 | 119 | {% endfor %} 120 | 121 | {% for name in best_head %} 122 | 123 | {% endfor %} 124 | 125 | {% endfor %} 126 | 127 |
82 | ID模型名称_备注创建时间{{type}}{{name}}
{{param_name}}
102 | {{item.id}} 105 | {{item.sub_model_name}} 107 | {% if item.finished_train %} 108 | 完成 109 | {% else %} 110 | 111 | 点击完成 112 | 113 | {% endif %} 114 | {{item.sub_model_remark}}{{item.create_time}}{{item[param_name]}}{{item[name]}}
128 |
129 |
130 |
131 | 132 |
133 |
134 | 135 |
136 |
137 |
138 |
139 |
140 | 141 |
142 |
143 |
144 |
145 |
146 | 147 |
148 |
149 |
150 |
151 |
152 | 153 |
154 |
155 |
156 |
157 |
158 | 159 |
160 | 161 |
162 |
163 |
164 |
165 |
166 |
167 | 168 | 169 | 185 | 186 | 187 |
188 |
189 |
消息
190 |
191 | 192 |
193 | 196 |
197 |
198 | 199 | 200 |
201 |
202 |
警告
203 |
204 | 你,确定要删除这些记录吗? 205 |
206 | 210 |
211 |
212 | 213 | 214 |
215 |
216 |
Login
217 |
218 | 昵称 219 | 220 |
221 | 225 |
226 |
227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 816 | 817 | 818 | -------------------------------------------------------------------------------- /model_log/tf_param.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NLP-LOVE/Model_Log/fc0a46afe9702d6cb5ba508fac3a0dc3bd386e5f/model_log/tf_param.pkl -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #-*- encoding: UTF-8 -*- 2 | from setuptools import setup, find_packages 3 | """ 4 | 打包的用的setup必须引入, 5 | """ 6 | 7 | 8 | VERSION = '1.1.9' 9 | 10 | with open('README.md', encoding='utf-8') as fp: 11 | readme = fp.read() 12 | 13 | setup(name='model-log', 14 | version=VERSION, 15 | description="test description", 16 | long_description=readme, 17 | classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers 18 | keywords='python ML DL model log', 19 | author='mantch', 20 | author_email='mantchs@163.com', 21 | url='https://github.com/NLP-LOVE', 22 | license='MIT', 23 | packages=['model_log'], 24 | include_package_data=True, 25 | zip_safe=True, 26 | install_requires=[ 27 | 'flask >= 0.11' 28 | ], 29 | entry_points={ 30 | 'console_scripts':[ 31 | 'model-log = model_log.modellog_web:main' 32 | ] 33 | } 34 | ) --------------------------------------------------------------------------------