作者 杨再宏

新增邮件与短信发送;代码优化

  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<module version="4">
  3 + <component name="Flask">
  4 + <option name="enabled" value="true" />
  5 + </component>
  6 + <component name="PyDocumentationSettings">
  7 + <option name="format" value="PLAIN" />
  8 + <option name="myDocStringFormat" value="Plain" />
  9 + </component>
  10 + <component name="TemplatesService">
  11 + <option name="TEMPLATE_CONFIGURATION" value="Jinja2" />
  12 + <option name="TEMPLATE_FOLDERS">
  13 + <list>
  14 + <option value="$MODULE_DIR$/app/templates" />
  15 + </list>
  16 + </option>
  17 + </component>
  18 +</module>
  1 +<component name="InspectionProjectProfileManager">
  2 + <profile version="1.0">
  3 + <option name="myName" value="Project Default" />
  4 + <inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
  5 + <option name="ignoredPackages">
  6 + <value>
  7 + <list size="24">
  8 + <item index="0" class="java.lang.String" itemvalue="alembic" />
  9 + <item index="1" class="java.lang.String" itemvalue="python-dateutil" />
  10 + <item index="2" class="java.lang.String" itemvalue="SQLAlchemy" />
  11 + <item index="3" class="java.lang.String" itemvalue="setuptools" />
  12 + <item index="4" class="java.lang.String" itemvalue="MarkupSafe" />
  13 + <item index="5" class="java.lang.String" itemvalue="python-editor" />
  14 + <item index="6" class="java.lang.String" itemvalue="Jinja2" />
  15 + <item index="7" class="java.lang.String" itemvalue="pip" />
  16 + <item index="8" class="java.lang.String" itemvalue="itsdangerous" />
  17 + <item index="9" class="java.lang.String" itemvalue="Flask" />
  18 + <item index="10" class="java.lang.String" itemvalue="email-validator" />
  19 + <item index="11" class="java.lang.String" itemvalue="dnspython" />
  20 + <item index="12" class="java.lang.String" itemvalue="six" />
  21 + <item index="13" class="java.lang.String" itemvalue="Flask-Script" />
  22 + <item index="14" class="java.lang.String" itemvalue="Werkzeug" />
  23 + <item index="15" class="java.lang.String" itemvalue="wheel" />
  24 + <item index="16" class="java.lang.String" itemvalue="Flask-WTF" />
  25 + <item index="17" class="java.lang.String" itemvalue="click" />
  26 + <item index="18" class="java.lang.String" itemvalue="Flask-SQLAlchemy" />
  27 + <item index="19" class="java.lang.String" itemvalue="WTForms" />
  28 + <item index="20" class="java.lang.String" itemvalue="Flask-Migrate" />
  29 + <item index="21" class="java.lang.String" itemvalue="PyMySQL" />
  30 + <item index="22" class="java.lang.String" itemvalue="Mako" />
  31 + <item index="23" class="java.lang.String" itemvalue="idna" />
  32 + </list>
  33 + </value>
  34 + </option>
  35 + </inspection_tool>
  36 + <inspection_tool class="PyPep8Inspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
  37 + <option name="ignoredErrors">
  38 + <list>
  39 + <option value="W292" />
  40 + <option value="E501" />
  41 + </list>
  42 + </option>
  43 + </inspection_tool>
  44 + <inspection_tool class="PyPep8NamingInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
  45 + <option name="ignoredErrors">
  46 + <list>
  47 + <option value="N801" />
  48 + </list>
  49 + </option>
  50 + </inspection_tool>
  51 + <inspection_tool class="PyShadowingBuiltinsInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
  52 + <option name="ignoredNames">
  53 + <list>
  54 + <option value="list" />
  55 + <option value="sum" />
  56 + </list>
  57 + </option>
  58 + </inspection_tool>
  59 + </profile>
  60 +</component>
  1 +<component name="InspectionProjectProfileManager">
  2 + <settings>
  3 + <option name="USE_PROJECT_PROFILE" value="false" />
  4 + <version value="1.0" />
  5 + </settings>
  6 +</component>
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<project version="4">
  3 + <component name="Black">
  4 + <option name="sdkName" value="Python 3.12 (IOTMS)" />
  5 + </component>
  6 + <component name="ProjectRootManager" version="2" project-jdk-name="Python 3.12 (IOTMS)" project-jdk-type="Python SDK" />
  7 +</project>
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<project version="4">
  3 + <component name="VcsDirectoryMappings">
  4 + <mapping directory="$PROJECT_DIR$" vcs="Git" />
  5 + </component>
  6 +</project>
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<project version="4">
  3 + <component name="AutoImportSettings">
  4 + <option name="autoReloadType" value="SELECTIVE" />
  5 + </component>
  6 + <component name="ChangeListManager">
  7 + <list default="true" id="6667d1e0-67b9-4911-9383-6ccd47455133" name="更改" comment="提交项目">
  8 + <change beforePath="$PROJECT_DIR$/app/admin/views.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/admin/views.py" afterDir="false" />
  9 + <change beforePath="$PROJECT_DIR$/app/api/response.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/api/response.py" afterDir="false" />
  10 + <change beforePath="$PROJECT_DIR$/app/models.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/models.py" afterDir="false" />
  11 + <change beforePath="$PROJECT_DIR$/app/templates/admin/1001_index.html" beforeDir="false" afterPath="$PROJECT_DIR$/app/templates/admin/1001_index.html" afterDir="false" />
  12 + <change beforePath="$PROJECT_DIR$/app/templates/admin/3001_index.html" beforeDir="false" afterPath="$PROJECT_DIR$/app/templates/admin/3001_index.html" afterDir="false" />
  13 + <change beforePath="$PROJECT_DIR$/app/templates/admin/4001_index.html" beforeDir="false" afterPath="$PROJECT_DIR$/app/templates/admin/4001_index.html" afterDir="false" />
  14 + <change beforePath="$PROJECT_DIR$/app/templates/admin/5001_index.html" beforeDir="false" afterPath="$PROJECT_DIR$/app/templates/admin/5001_index.html" afterDir="false" />
  15 + <change beforePath="$PROJECT_DIR$/app/templates/admin/6001_index.html" beforeDir="false" afterPath="$PROJECT_DIR$/app/templates/admin/6001_index.html" afterDir="false" />
  16 + <change beforePath="$PROJECT_DIR$/app/templates/admin/7001_index.html" beforeDir="false" afterPath="$PROJECT_DIR$/app/templates/admin/7001_index.html" afterDir="false" />
  17 + <change beforePath="$PROJECT_DIR$/app/templates/admin/8001_index.html" beforeDir="false" afterPath="$PROJECT_DIR$/app/templates/admin/8001_index.html" afterDir="false" />
  18 + <change beforePath="$PROJECT_DIR$/app/templates/admin/index.html" beforeDir="false" afterPath="$PROJECT_DIR$/app/templates/admin/index.html" afterDir="false" />
  19 + <change beforePath="$PROJECT_DIR$/app/templates/admin/menu.html" beforeDir="false" afterPath="$PROJECT_DIR$/app/templates/admin/menu.html" afterDir="false" />
  20 + <change beforePath="$PROJECT_DIR$/manage.py" beforeDir="false" afterPath="$PROJECT_DIR$/manage.py" afterDir="false" />
  21 + <change beforePath="$PROJECT_DIR$/test.py" beforeDir="false" />
  22 + </list>
  23 + <option name="SHOW_DIALOG" value="false" />
  24 + <option name="HIGHLIGHT_CONFLICTS" value="true" />
  25 + <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
  26 + <option name="LAST_RESOLUTION" value="IGNORE" />
  27 + </component>
  28 + <component name="FileTemplateManagerImpl">
  29 + <option name="RECENT_TEMPLATES">
  30 + <list>
  31 + <option value="Flask Main" />
  32 + <option value="HTML File" />
  33 + <option value="Python Script" />
  34 + </list>
  35 + </option>
  36 + </component>
  37 + <component name="FlaskConsoleOptions" custom-start-script="import sys&#10;sys.path.extend([WORKING_DIR_AND_PYTHON_PATHS])&#10;from flask.cli import ScriptInfo&#10;locals().update(ScriptInfo(create_app=None).load_app().make_shell_context())&#10;print(&quot;Python %s on %s\nApp: %s [%s]\nInstance: %s&quot; % (sys.version, sys.platform, app.import_name, app.env, app.instance_path))">
  38 + <envs>
  39 + <env key="FLASK_APP" value="app" />
  40 + </envs>
  41 + <option name="myCustomStartScript" value="import sys&#10;sys.path.extend([WORKING_DIR_AND_PYTHON_PATHS])&#10;from flask.cli import ScriptInfo&#10;locals().update(ScriptInfo(create_app=None).load_app().make_shell_context())&#10;print(&quot;Python %s on %s\nApp: %s [%s]\nInstance: %s&quot; % (sys.version, sys.platform, app.import_name, app.env, app.instance_path))" />
  42 + <option name="myEnvs">
  43 + <map>
  44 + <entry key="FLASK_APP" value="app" />
  45 + </map>
  46 + </option>
  47 + </component>
  48 + <component name="Git.Settings">
  49 + <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
  50 + </component>
  51 + <component name="MarkdownSettingsMigration">
  52 + <option name="stateVersion" value="1" />
  53 + </component>
  54 + <component name="ProjectColorInfo">{
  55 + &quot;associatedIndex&quot;: 4
  56 +}</component>
  57 + <component name="ProjectId" id="2euAaW8tC7vFanl3HDgjL0fAoQP" />
  58 + <component name="ProjectLevelVcsManager" settingsEditedManually="true" />
  59 + <component name="ProjectViewState">
  60 + <option name="hideEmptyMiddlePackages" value="true" />
  61 + <option name="showLibraryContents" value="true" />
  62 + </component>
  63 + <component name="PropertiesComponent">{
  64 + &quot;keyToString&quot;: {
  65 + &quot;DefaultHtmlFileTemplate&quot;: &quot;HTML File&quot;,
  66 + &quot;RunOnceActivity.OpenProjectViewOnStart&quot;: &quot;true&quot;,
  67 + &quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
  68 + &quot;WebServerToolWindowFactoryState&quot;: &quot;false&quot;,
  69 + &quot;git-widget-placeholder&quot;: &quot;master&quot;,
  70 + &quot;last_opened_file_path&quot;: &quot;E:/python12.0Project/flask/IOTMS/app/templates/admin&quot;,
  71 + &quot;node.js.detected.package.eslint&quot;: &quot;true&quot;,
  72 + &quot;node.js.detected.package.tslint&quot;: &quot;true&quot;,
  73 + &quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;,
  74 + &quot;node.js.selected.package.tslint&quot;: &quot;(autodetect)&quot;,
  75 + &quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;
  76 + }
  77 +}</component>
  78 + <component name="RecentsManager">
  79 + <key name="CopyFile.RECENT_KEYS">
  80 + <recent name="E:\python12.0Project\flask\IOTMS\app\templates\admin" />
  81 + </key>
  82 + <key name="MoveFile.RECENT_KEYS">
  83 + <recent name="E:\python12.0Project\flask\IOTMS\app" />
  84 + </key>
  85 + </component>
  86 + <component name="RunManager">
  87 + <configuration name="IOTMS" type="PythonConfigurationType" factoryName="Python">
  88 + <module name="IOTMS" />
  89 + <option name="INTERPRETER_OPTIONS" value="" />
  90 + <option name="PARENT_ENVS" value="true" />
  91 + <envs>
  92 + <env name="PYTHONUNBUFFERED" value="1" />
  93 + </envs>
  94 + <option name="SDK_HOME" value="" />
  95 + <option name="WORKING_DIRECTORY" value="" />
  96 + <option name="IS_MODULE_SDK" value="false" />
  97 + <option name="ADD_CONTENT_ROOTS" value="true" />
  98 + <option name="ADD_SOURCE_ROOTS" value="true" />
  99 + <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
  100 + <option name="SCRIPT_NAME" value="$PROJECT_DIR$/app/__init__.py" />
  101 + <option name="PARAMETERS" value="" />
  102 + <option name="SHOW_COMMAND_LINE" value="false" />
  103 + <option name="EMULATE_TERMINAL" value="false" />
  104 + <option name="MODULE_MODE" value="false" />
  105 + <option name="REDIRECT_INPUT" value="false" />
  106 + <option name="INPUT_FILE" value="" />
  107 + <method v="2" />
  108 + </configuration>
  109 + <configuration name="IOTMS" type="Python.FlaskServer">
  110 + <module name="IOTMS" />
  111 + <option name="target" value="$PROJECT_DIR$/app/__init__.py" />
  112 + <option name="targetType" value="PATH" />
  113 + <option name="INTERPRETER_OPTIONS" value="" />
  114 + <option name="PARENT_ENVS" value="true" />
  115 + <option name="SDK_HOME" value="" />
  116 + <option name="WORKING_DIRECTORY" value="" />
  117 + <option name="IS_MODULE_SDK" value="false" />
  118 + <option name="ADD_CONTENT_ROOTS" value="true" />
  119 + <option name="ADD_SOURCE_ROOTS" value="true" />
  120 + <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
  121 + <option name="launchJavascriptDebuger" value="false" />
  122 + <method v="2" />
  123 + </configuration>
  124 + </component>
  125 + <component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="应用程序级" UseSingleDictionary="true" transferred="true" />
  126 + <component name="TaskManager">
  127 + <task active="true" id="Default" summary="默认任务">
  128 + <changelist id="6667d1e0-67b9-4911-9383-6ccd47455133" name="更改" comment="" />
  129 + <created>1712744153440</created>
  130 + <option name="number" value="Default" />
  131 + <option name="presentableId" value="Default" />
  132 + <updated>1712744153440</updated>
  133 + <workItem from="1712744178234" duration="539000" />
  134 + <workItem from="1712797526665" duration="19598000" />
  135 + <workItem from="1712883238837" duration="10276000" />
  136 + <workItem from="1712911806583" duration="1622000" />
  137 + <workItem from="1713147036316" duration="20687000" />
  138 + <workItem from="1713230674276" duration="11238000" />
  139 + <workItem from="1713256268965" duration="5659000" />
  140 + <workItem from="1713316224129" duration="26153000" />
  141 + <workItem from="1713402221547" duration="16235000" />
  142 + <workItem from="1713489035662" duration="10873000" />
  143 + <workItem from="1713754186717" duration="13996000" />
  144 + <workItem from="1713921132189" duration="1437000" />
  145 + <workItem from="1713925651307" duration="10390000" />
  146 + <workItem from="1714007621362" duration="13881000" />
  147 + <workItem from="1714095253280" duration="1258000" />
  148 + <workItem from="1714266356827" duration="1983000" />
  149 + <workItem from="1714352589052" duration="6416000" />
  150 + <workItem from="1714381984518" duration="2081000" />
  151 + <workItem from="1714439832284" duration="11106000" />
  152 + <workItem from="1715062841268" duration="15311000" />
  153 + <workItem from="1715130405561" duration="23708000" />
  154 + <workItem from="1715216726319" duration="20414000" />
  155 + <workItem from="1715302475700" duration="24941000" />
  156 + <workItem from="1715389298807" duration="12116000" />
  157 + <workItem from="1715562838288" duration="17298000" />
  158 + <workItem from="1715649238545" duration="25313000" />
  159 + <workItem from="1715735641302" duration="12530000" />
  160 + <workItem from="1715822154508" duration="16862000" />
  161 + <workItem from="1715907880472" duration="10486000" />
  162 + <workItem from="1715928408269" duration="11056000" />
  163 + <workItem from="1716167939911" duration="21674000" />
  164 + <workItem from="1716270839068" duration="5119000" />
  165 + <workItem from="1716346370983" duration="2490000" />
  166 + <workItem from="1716771942520" duration="15082000" />
  167 + <workItem from="1716861607617" duration="5149000" />
  168 + <workItem from="1716881478181" duration="638000" />
  169 + <workItem from="1716883475984" duration="306000" />
  170 + <workItem from="1716883858098" duration="1211000" />
  171 + <workItem from="1716885478533" duration="6120000" />
  172 + <workItem from="1716945001118" duration="10963000" />
  173 + <workItem from="1717038536867" duration="2734000" />
  174 + <workItem from="1717050517193" duration="2002000" />
  175 + <workItem from="1717054719755" duration="2594000" />
  176 + <workItem from="1717057571743" duration="5979000" />
  177 + </task>
  178 + <servers />
  179 + </component>
  180 + <component name="TypeScriptGeneratedFilesManager">
  181 + <option name="version" value="3" />
  182 + </component>
  183 + <component name="VcsManagerConfiguration">
  184 + <MESSAGE value="提交项目" />
  185 + <option name="LAST_COMMIT_MESSAGE" value="提交项目" />
  186 + </component>
  187 + <component name="XDebuggerManager">
  188 + <breakpoint-manager>
  189 + <breakpoints>
  190 + <line-breakpoint enabled="true" suspend="THREAD" type="python-line">
  191 + <url>file://$PROJECT_DIR$/app/admin/views.py</url>
  192 + <line>1292</line>
  193 + <option name="timeStamp" value="4" />
  194 + </line-breakpoint>
  195 + <line-breakpoint enabled="true" suspend="THREAD" type="python-line">
  196 + <url>file://$PROJECT_DIR$/app/admin/views.py</url>
  197 + <line>1380</line>
  198 + <option name="timeStamp" value="8" />
  199 + </line-breakpoint>
  200 + <line-breakpoint enabled="true" suspend="THREAD" type="python-line">
  201 + <url>file://$PROJECT_DIR$/app/admin/views.py</url>
  202 + <line>1240</line>
  203 + <option name="timeStamp" value="9" />
  204 + </line-breakpoint>
  205 + <line-breakpoint enabled="true" suspend="THREAD" type="python-line">
  206 + <url>file://$PROJECT_DIR$/app/admin/views.py</url>
  207 + <line>450</line>
  208 + <option name="timeStamp" value="10" />
  209 + </line-breakpoint>
  210 + <line-breakpoint enabled="true" suspend="THREAD" type="python-line">
  211 + <url>file://$PROJECT_DIR$/app/admin/views.py</url>
  212 + <line>1646</line>
  213 + <option name="timeStamp" value="12" />
  214 + </line-breakpoint>
  215 + <line-breakpoint enabled="true" suspend="THREAD" type="python-line">
  216 + <url>file://$PROJECT_DIR$/app/admin/views.py</url>
  217 + <line>981</line>
  218 + <option name="timeStamp" value="31" />
  219 + </line-breakpoint>
  220 + <line-breakpoint enabled="true" type="javascript">
  221 + <url>file://$PROJECT_DIR$/app/templates/admin/alarm_list.html</url>
  222 + <line>262</line>
  223 + <option name="timeStamp" value="32" />
  224 + </line-breakpoint>
  225 + </breakpoints>
  226 + </breakpoint-manager>
  227 + </component>
  228 + <component name="com.intellij.coverage.CoverageDataManagerImpl">
  229 + <SUITE FILE_PATH="coverage/IOTMS$views.coverage" NAME="views 覆盖结果" MODIFIED="1717061804597" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/app/admin" />
  230 + <SUITE FILE_PATH="coverage/IOTMS$Flask__manage_py_.coverage" NAME="Flask (manage.py) 覆盖结果" MODIFIED="1717062026498" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="" />
  231 + <SUITE FILE_PATH="coverage/IOTMS$IOTMS.coverage" NAME="IOTMS 覆盖结果" MODIFIED="1717061297780" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="" />
  232 + </component>
  233 +</project>
@@ -2,6 +2,8 @@ @@ -2,6 +2,8 @@
2 import os 2 import os
3 import uuid 3 import uuid
4 from datetime import datetime 4 from datetime import datetime
  5 +
  6 +import app.api.response
5 from app import db 7 from app import db
6 from . import admin 8 from . import admin
7 from flask import render_template, redirect, url_for, flash, session, request, g, abort, make_response, current_app 9 from flask import render_template, redirect, url_for, flash, session, request, g, abort, make_response, current_app
@@ -15,7 +17,6 @@ from functools import wraps @@ -15,7 +17,6 @@ from functools import wraps
15 from app.config import DeviceType as DT 17 from app.config import DeviceType as DT
16 from app.api.stack_func import get_stack 18 from app.api.stack_func import get_stack
17 19
18 -  
19 current_user = "" 20 current_user = ""
20 21
21 22
@@ -73,7 +74,8 @@ def change_filename(filename): @@ -73,7 +74,8 @@ def change_filename(filename):
73 @admin.route("/") 74 @admin.route("/")
74 @admin_login 75 @admin_login
75 def index(): 76 def index():
76 - days = datetime.strptime(f'{datetime.now().year}-{datetime.now().month}-{datetime.now().day} 00:00', "%Y-%m-%d %H:%M") 77 + days = datetime.strptime(f'{datetime.now().year}-{datetime.now().month}-{datetime.now().day} 00:00',
  78 + "%Y-%m-%d %H:%M")
77 79
78 device_data = Device.query.group_by( 80 device_data = Device.query.group_by(
79 Device.devicetype_id 81 Device.devicetype_id
@@ -81,10 +83,7 @@ def index(): @@ -81,10 +83,7 @@ def index():
81 Device.devicetype_id.asc() 83 Device.devicetype_id.asc()
82 ).all() 84 ).all()
83 for d in device_data: 85 for d in device_data:
84 - if d.devicetype.type == 7001:  
85 - for dd in d.devicetype.device:  
86 -  
87 - print(dd.title) 86 + print(d.devicetype.type)
88 87
89 video = Alarm.query.join( 88 video = Alarm.query.join(
90 Device 89 Device
@@ -143,7 +142,7 @@ def index(): @@ -143,7 +142,7 @@ def index():
143 radar = None 142 radar = None
144 143
145 return render_template("admin/index.html", 144 return render_template("admin/index.html",
146 - video=video, gasc=gasc, pressure=pressure, 145 + video=video, gasc=gasc, pressure=pressure,
147 device_data=device_data, radar=radar, 146 device_data=device_data, radar=radar,
148 s_url=request.host_url, year=datetime.now().year) 147 s_url=request.host_url, year=datetime.now().year)
149 148
@@ -269,6 +268,60 @@ def user_list(): @@ -269,6 +268,60 @@ def user_list():
269 return render_template("admin/user_list.html", page_data=page_data, year=datetime.now().year) 268 return render_template("admin/user_list.html", page_data=page_data, year=datetime.now().year)
270 269
271 270
  271 +@admin.route("/email/notification/", methods=["GET"])
  272 +@admin_login
  273 +def email_notification():
  274 + """
  275 + 邮件通知
  276 + """
  277 + page = request.args.get('page', 1, type=int) # 获取page参数值
  278 + keyword = request.args.get('keyword', '', type=str)
  279 +
  280 + if keyword:
  281 + # 根据姓名或者邮箱查询
  282 + filters = or_(User.email == keyword)
  283 + page_data = User.query.filter(
  284 + User.email is not None
  285 + ).filter(filters).order_by(
  286 + User.addtime.desc()
  287 + ).paginate(page=page, per_page=10)
  288 + else:
  289 + page_data = User.query.filter(
  290 + User.email is not None
  291 + ).order_by(
  292 + User.addtime.desc()
  293 + ).paginate(page=page, per_page=10)
  294 +
  295 + return render_template("admin/email_notification.html", page_data=page_data, year=datetime.now().year)
  296 +
  297 +
  298 +@admin.route("/phone/notification/", methods=["GET"])
  299 +@admin_login
  300 +def phone_notification():
  301 + """
  302 + 邮件通知
  303 + """
  304 + page = request.args.get('page', 1, type=int) # 获取page参数值
  305 + keyword = request.args.get('keyword', '', type=str)
  306 +
  307 + if keyword:
  308 + # 根据姓名或者邮箱查询
  309 + filters = or_(User.phone == keyword)
  310 + page_data = User.query.filter(
  311 + User.phone is not None
  312 + ).filter(filters).order_by(
  313 + User.addtime.desc()
  314 + ).paginate(page=page, per_page=10)
  315 + else:
  316 + page_data = User.query.filter(
  317 + User.phone is not None
  318 + ).order_by(
  319 + User.addtime.desc()
  320 + ).paginate(page=page, per_page=10)
  321 +
  322 + return render_template("admin/phone_notification.html", page_data=page_data, year=datetime.now().year)
  323 +
  324 +
272 @admin.route('/power/add/', methods=["GET", "POST"]) 325 @admin.route('/power/add/', methods=["GET", "POST"])
273 @admin_login 326 @admin_login
274 def power_add(): 327 def power_add():
@@ -977,7 +1030,8 @@ def alarm_list(): @@ -977,7 +1030,8 @@ def alarm_list():
977 ).order_by( 1030 ).order_by(
978 Alarm.addtime.desc() 1031 Alarm.addtime.desc()
979 ).paginate(page=page, per_page=10) 1032 ).paginate(page=page, per_page=10)
980 - return render_template("admin/alarm_list.html", key=last_key, st=last_st, et=last_et, page_data=page_data, year=datetime.now().year) 1033 + return render_template("admin/alarm_list.html", key=last_key, st=last_st, et=last_et, page_data=page_data,
  1034 + year=datetime.now().year)
981 1035
982 1036
983 @admin.route("/alarm/edit/", methods=["GET"]) 1037 @admin.route("/alarm/edit/", methods=["GET"])
@@ -1045,7 +1099,8 @@ def alarm_statistics(): @@ -1045,7 +1099,8 @@ def alarm_statistics():
1045 if is_valid_time_format(dt2.replace("T", " ")): 1099 if is_valid_time_format(dt2.replace("T", " ")):
1046 et = dt2 1100 et = dt2
1047 print(datetime.now().strftime('%Y-%m-%d %H:%M:%S')) 1101 print(datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
1048 - return render_template("admin/alarm_statistics.html", s_url=request.host_url, st=st, et=et, year=datetime.now().year) 1102 + return render_template("admin/alarm_statistics.html", s_url=request.host_url, st=st, et=et,
  1103 + year=datetime.now().year)
1049 1104
1050 1105
1051 @admin.route("/notification/list/", methods=["GET"]) 1106 @admin.route("/notification/list/", methods=["GET"])
@@ -1060,6 +1115,40 @@ def notification_list(): @@ -1060,6 +1115,40 @@ def notification_list():
1060 return render_template("admin/notification_list.html", page_data="", year=datetime.now().year) 1115 return render_template("admin/notification_list.html", page_data="", year=datetime.now().year)
1061 1116
1062 1117
  1118 +@admin.route("/emailnotification_edit", methods=["GET"])
  1119 +def emailnotification_edit():
  1120 + try:
  1121 +
  1122 + id = request.args.get("id", 0)
  1123 + user = User.query.get_or_404(int(id)) # 根据ID查找设备是否存在
  1124 +
  1125 + # 属性赋值
  1126 + user.emailnotification = request.args.get('email', 0)
  1127 +
  1128 + db.session.commit() # 提交数据
  1129 +
  1130 + return app.api.response.Response(code=200, msg='ok', success=True).json
  1131 + except:
  1132 + return app.api.response.Response(code=400, msg='err', success=False).json
  1133 +
  1134 +
  1135 +@admin.route("/phonenotification_edit", methods=["GET"])
  1136 +def phonenotification_edit():
  1137 + try:
  1138 +
  1139 + id = request.args.get("id", 0)
  1140 + user = User.query.get_or_404(int(id)) # 根据ID查找设备是否存在
  1141 +
  1142 + # 属性赋值
  1143 + user.phonenotification = request.args.get('phone', 0)
  1144 +
  1145 + db.session.commit() # 提交数据
  1146 +
  1147 + return app.api.response.Response(code=200, msg='ok', success=True).json
  1148 + except:
  1149 + return app.api.response.Response(code=400, msg='err', success=False).json
  1150 +
  1151 +
1063 @admin.route("/adminloginlog/list/", methods=["GET"]) 1152 @admin.route("/adminloginlog/list/", methods=["GET"])
1064 @admin_login 1153 @admin_login
1065 def adminloginlog_list(page=None): 1154 def adminloginlog_list(page=None):
@@ -1570,13 +1659,12 @@ def get_line_chart(): @@ -1570,13 +1659,12 @@ def get_line_chart():
1570 idx = 9 1659 idx = 9
1571 1660
1572 1661
1573 -@admin.route("/lineDynamicData")  
1574 -def update_line_data():  
1575 - # global idx  
1576 - # idx = idx + 1  
1577 - # return jsonify({"name": idx, "value": randrange(50, 80)})  
1578 - c = line_base()  
1579 - return c.dump_options_with_quotes() 1662 +@admin.route("/getDynamicData")
  1663 +def update_dynamic_data():
  1664 + list = app.api.stack_func.get_stack()
  1665 + # print(type(list))
  1666 + # print(list)
  1667 + return list
1580 1668
1581 1669
1582 @admin.route("/barChart") 1670 @admin.route("/barChart")
@@ -1586,25 +1674,36 @@ def get_bar_chart(): @@ -1586,25 +1674,36 @@ def get_bar_chart():
1586 1674
1587 1675
1588 def get_alarm_statistics(start_time, end_time): 1676 def get_alarm_statistics(start_time, end_time):
1589 - pd = Alarm.query.join(  
1590 - Device  
1591 - ).join(  
1592 - DeviceType  
1593 - ).filter(  
1594 - Alarm.device_id == Device.id  
1595 - ).filter(  
1596 - Device.devicetype_id == DeviceType.type  
1597 - ).filter(  
1598 - datetime.strptime(start_time, "%Y-%m-%d %H:%M") < Alarm.addtime  
1599 - ).filter(  
1600 - Alarm.addtime < datetime.strptime(end_time, "%Y-%m-%d %H:%M")  
1601 - ).order_by(  
1602 - Device.devicetype_id.asc()  
1603 - ).group_by(  
1604 - DeviceType.type  
1605 - ).all()  
1606 -  
1607 - return pd 1677 + # pd = Alarm.query.join(
  1678 + # Device
  1679 + # ).join(
  1680 + # DeviceType
  1681 + # ).filter(
  1682 + # Alarm.device_id == Device.id
  1683 + # ).filter(
  1684 + # Device.devicetype_id == DeviceType.type
  1685 + # ).filter(
  1686 + # datetime.strptime(start_time, "%Y-%m-%d %H:%M") < Alarm.addtime
  1687 + # ).filter(
  1688 + # Alarm.addtime < datetime.strptime(end_time, "%Y-%m-%d %H:%M")
  1689 + # ).order_by(
  1690 + # Device.devicetype_id.asc()
  1691 + # ).group_by(
  1692 + # DeviceType.type
  1693 + # ).all()
  1694 +
  1695 + from sqlalchemy import text
  1696 + sql = text(
  1697 + f"SELECT count(a.id) as c, dt.`name` as n FROM alarm as a, device as d, devicetype as dt WHERE a.addtime>'{start_time}' \
  1698 + and a.addtime<'{end_time}' AND a.device_id=d.id and d.devicetype_id=dt.type \
  1699 + GROUP BY dt.type ORDER BY c desc")
  1700 +
  1701 + res = db.session.execute(sql).fetchall()
  1702 +
  1703 + for r in res:
  1704 + print(r)
  1705 +
  1706 + return res
1608 1707
1609 1708
1610 def alarm_bar_base(alarm, st, et) -> Bar: 1709 def alarm_bar_base(alarm, st, et) -> Bar:
@@ -1613,8 +1712,8 @@ def alarm_bar_base(alarm, st, et) -> Bar: @@ -1613,8 +1712,8 @@ def alarm_bar_base(alarm, st, et) -> Bar:
1613 alarm_name = [] 1712 alarm_name = []
1614 alarm_count = [] 1713 alarm_count = []
1615 for a in alarm: 1714 for a in alarm:
1616 - alarm_name.append(a.device.devicetype.name)  
1617 - alarm_count.append(len(a.device.alarm)) 1715 + alarm_name.append(a[1])
  1716 + alarm_count.append(a[0])
1618 1717
1619 # for dt in devicetype: 1718 # for dt in devicetype:
1620 # if dt.name in alarm_name: 1719 # if dt.name in alarm_name:
@@ -1646,7 +1745,7 @@ def alarm_bar_base(alarm, st, et) -> Bar: @@ -1646,7 +1745,7 @@ def alarm_bar_base(alarm, st, et) -> Bar:
1646 .set_series_opts(label_opts=opts.LabelOpts(position="right")) 1745 .set_series_opts(label_opts=opts.LabelOpts(position="right"))
1647 1746
1648 .set_global_opts( 1747 .set_global_opts(
1649 - title_opts=opts.TitleOpts(title="设备报警柱形图", subtitle="报警次数",), 1748 + title_opts=opts.TitleOpts(title="设备报警柱形图", subtitle="报警次数", ),
1650 legend_opts=opts.LegendOpts( 1749 legend_opts=opts.LegendOpts(
1651 orient="center", # 水平排布 1750 orient="center", # 水平排布
1652 ), 1751 ),
@@ -1672,8 +1771,8 @@ def alarm_pie_base(alarm) -> Pie: @@ -1672,8 +1771,8 @@ def alarm_pie_base(alarm) -> Pie:
1672 alarm_name = [] 1771 alarm_name = []
1673 alarm_count = [] 1772 alarm_count = []
1674 for a in alarm: 1773 for a in alarm:
1675 - alarm_name.append(a.device.devicetype.name)  
1676 - alarm_count.append(len(a.device.alarm)) 1774 + alarm_name.append(a[1])
  1775 + alarm_count.append(a[0])
1677 c = ( 1776 c = (
1678 Pie() 1777 Pie()
1679 .add( 1778 .add(
@@ -1683,7 +1782,7 @@ def alarm_pie_base(alarm) -> Pie: @@ -1683,7 +1782,7 @@ def alarm_pie_base(alarm) -> Pie:
1683 ) 1782 )
1684 .set_global_opts( 1783 .set_global_opts(
1685 title_opts=opts.TitleOpts(title="设备报警饼状图", subtitle="报警次数"), 1784 title_opts=opts.TitleOpts(title="设备报警饼状图", subtitle="报警次数"),
1686 - #legend_opts=opts.LegendOpts(orient="vertical", pos_top="15%", pos_left="2%"), 1785 + # legend_opts=opts.LegendOpts(orient="vertical", pos_top="15%", pos_left="2%"),
1687 ) 1786 )
1688 .set_series_opts(label_opts=opts.LabelOpts(formatter="{b}: {c}")) 1787 .set_series_opts(label_opts=opts.LabelOpts(formatter="{b}: {c}"))
1689 ) 1788 )
@@ -1708,7 +1807,7 @@ def gauge_chart(): @@ -1708,7 +1807,7 @@ def gauge_chart():
1708 Gauge(init_opts=opts.InitOpts(width="150px", height="150px")) 1807 Gauge(init_opts=opts.InitOpts(width="150px", height="150px"))
1709 .add( 1808 .add(
1710 "电流图", 1809 "电流图",
1711 - [("漏电流(mA)", 155.5),], 1810 + [("漏电流(mA)", 155.5), ],
1712 max_=1000, 1811 max_=1000,
1713 split_number=5, 1812 split_number=5,
1714 axisline_opts=opts.AxisLineOpts( 1813 axisline_opts=opts.AxisLineOpts(
@@ -1799,5 +1898,3 @@ def get_liquid_chart(): @@ -1799,5 +1898,3 @@ def get_liquid_chart():
1799 ) 1898 )
1800 1899
1801 return c.dump_options_with_quotes() 1900 return c.dump_options_with_quotes()
1802 -  
1803 -  
@@ -41,9 +41,9 @@ class GasConcentration: @@ -41,9 +41,9 @@ class GasConcentration:
41 unit: str 41 unit: str
42 lowAlarmValue: float 42 lowAlarmValue: float
43 highAlarmValue: float 43 highAlarmValue: float
44 - range: float  
45 - type: int  
46 - name: str 44 + gasRange: float
  45 + gasType: int
  46 + gasName: str
47 lastUpdateTime: str 47 lastUpdateTime: str
48 48
49 @property 49 @property
  1 +import zmail
  2 +import urllib.request
  3 +from urllib import parse
  4 +
  5 +
  6 +def send_sms(mobile, sms_info):
  7 + try:
  8 + """
  9 + #python3
  10 + #接口类型:互亿无线触发短信接口,支持发送验证码短信、订单通知短信等。
  11 + #账户注册:请通过该地址开通账户https://user.ihuyi.com/new/register.html
  12 + #注意事项:
  13 + #(1)调试期间,请用默认的模板进行测试,默认模板详见接口文档;
  14 + #(2)请使用 用户名 及 APIkey来调用接口,APIkey在会员中心可以获取;
  15 + #(3)该代码仅供接入互亿无线短信接口参考使用,客户可根据实际需要自行编写;
  16 + """
  17 + # 接口地址
  18 + url = 'http://106.ihuyi.com/webservice/sms.php?method=Submit'
  19 +
  20 + # 定义请求的数据
  21 + values = {
  22 + 'account': 'C61245995',
  23 + 'password': 'b018ad136d2005b1b687dd16b119e78f',
  24 + 'mobile': mobile,
  25 + 'content': sms_info,
  26 + 'format': 'json',
  27 + }
  28 +
  29 + # 将数据进行编码
  30 + data = urllib.parse.urlencode(values).encode(encoding='UTF8')
  31 +
  32 + # 发起请求
  33 + req = urllib.request.Request(url, data)
  34 + response = urllib.request.urlopen(req)
  35 + res = response.read()
  36 + return res
  37 + except Exception as e:
  38 + print(e)
  39 +
  40 +
  41 +def send_email(recv_email, title, content):
  42 + try:
  43 + # 设置邮件信息
  44 + mail_content = {
  45 + 'subject': title, # 邮件标题
  46 + 'content_text': content # 邮件内容
  47 + }
  48 + # 发件人的用户名和密码
  49 + server = zmail.server('yangzaihong@163.com', 'OTGKSKPJIMBXYLUK') # 发件人
  50 + # 发送邮件
  51 + server.send_mail(recv_email, mail_content) # 收件人
  52 + print('邮件已发送完毕')
  53 + except Exception as e:
  54 + print(e)
  55 +
@@ -27,6 +27,8 @@ class User(db.Model): @@ -27,6 +27,8 @@ class User(db.Model):
27 __tablename__ = "user" 27 __tablename__ = "user"
28 #__table_args__ = {"useexisting": True} 28 #__table_args__ = {"useexisting": True}
29 id = db.Column(db.Integer, primary_key=True) # 编号 29 id = db.Column(db.Integer, primary_key=True) # 编号
  30 + emailnotification = db.Column(db.Integer) # 邮件通知
  31 + phonenotification = db.Column(db.Integer) # 短信通知
30 username = db.Column(db.String(100)) # 用户名 32 username = db.Column(db.String(100)) # 用户名
31 pwd = db.Column(db.String(1024)) # 密码 33 pwd = db.Column(db.String(1024)) # 密码
32 email = db.Column(db.String(100)) # 邮箱 34 email = db.Column(db.String(100)) # 邮箱
@@ -69,7 +69,7 @@ @@ -69,7 +69,7 @@
69 </div> 69 </div>
70 <div class="product-info"> 70 <div class="product-info">
71 <a href="javascript:void(0)" class="product-title">{{dd.title}} 71 <a href="javascript:void(0)" class="product-title">{{dd.title}}
72 - <span class="label label-success pull-right">在线</span> 72 + <span class="label label-success pull-right" id="span-{{ d.devicetype.type }}-{{ dd.address }}">在线</span>
73 </a> 73 </a>
74 <span class="product-description"> 74 <span class="product-description">
75 检测区域:{{ dd.detectingpoint.no }}#{{ dd.detectingpoint.name }} 75 检测区域:{{ dd.detectingpoint.no }}#{{ dd.detectingpoint.name }}
@@ -69,7 +69,7 @@ @@ -69,7 +69,7 @@
69 </div> 69 </div>
70 <div class="product-info"> 70 <div class="product-info">
71 <a href="javascript:void(0)" class="product-title">{{dd.title}} 71 <a href="javascript:void(0)" class="product-title">{{dd.title}}
72 - <span class="label label-success pull-right">在线</span> 72 + <span class="label label-success pull-right" id="span-{{ d.devicetype.type }}-{{ dd.address }}">在线</span>
73 </a> 73 </a>
74 <span class="product-description"> 74 <span class="product-description">
75 检测区域:{{ dd.detectingpoint.no }}#{{ dd.detectingpoint.name }} 75 检测区域:{{ dd.detectingpoint.no }}#{{ dd.detectingpoint.name }}
@@ -56,7 +56,7 @@ @@ -56,7 +56,7 @@
56 </div> 56 </div>
57 <div class="product-info"> 57 <div class="product-info">
58 <a href="javascript:void(0)" class="product-title">{{dd.title}} 58 <a href="javascript:void(0)" class="product-title">{{dd.title}}
59 - <span class="label label-success pull-right">在线</span> 59 + <span class="label label-success pull-right" id="span-{{ d.devicetype.type }}-{{ dd.address }}">在线</span>
60 </a> 60 </a>
61 <span class="product-description"> 61 <span class="product-description">
62 检测区域:{{ dd.detectingpoint.no }}#{{ dd.detectingpoint.name }} 62 检测区域:{{ dd.detectingpoint.no }}#{{ dd.detectingpoint.name }}
@@ -57,7 +57,7 @@ @@ -57,7 +57,7 @@
57 </div> 57 </div>
58 <div class="product-info"> 58 <div class="product-info">
59 <a href="javascript:void(0)" class="product-title">{{dd.title}} 59 <a href="javascript:void(0)" class="product-title">{{dd.title}}
60 - <span class="label label-success pull-right">在线</span> 60 + <span class="label label-success pull-right" id="span-{{ d.devicetype.type }}-{{ dd.address }}">在线</span>
61 </a> 61 </a>
62 <span class="product-description"> 62 <span class="product-description">
63 检测区域:{{ dd.detectingpoint.no }}#{{ dd.detectingpoint.name }} 63 检测区域:{{ dd.detectingpoint.no }}#{{ dd.detectingpoint.name }}
@@ -57,7 +57,7 @@ @@ -57,7 +57,7 @@
57 </div> 57 </div>
58 <div class="product-info"> 58 <div class="product-info">
59 <a href="javascript:void(0)" class="product-title">{{dd.title}} 59 <a href="javascript:void(0)" class="product-title">{{dd.title}}
60 - <span class="label label-success pull-right">在线</span> 60 + <span class="label label-success pull-right" id="span-{{ d.devicetype.type }}-{{ dd.address }}">在线</span>
61 </a> 61 </a>
62 <span class="product-description"> 62 <span class="product-description">
63 检测区域:{{ dd.detectingpoint.no }}#{{ dd.detectingpoint.name }} 63 检测区域:{{ dd.detectingpoint.no }}#{{ dd.detectingpoint.name }}
@@ -112,7 +112,7 @@ @@ -112,7 +112,7 @@
112 </div> 112 </div>
113 <div class="product-info"> 113 <div class="product-info">
114 <a href="javascript:void(0)" class="product-title">{{dd.title}} 114 <a href="javascript:void(0)" class="product-title">{{dd.title}}
115 - <span class="label label-success pull-right">在线</span> 115 + <span class="label label-success pull-right" id="span-{{ d.devicetype.type }}-{{ dd.address }}">在线</span>
116 </a> 116 </a>
117 <span class="product-description"> 117 <span class="product-description">
118 检测区域:{{ dd.detectingpoint.no }}#{{ dd.detectingpoint.name }} 118 检测区域:{{ dd.detectingpoint.no }}#{{ dd.detectingpoint.name }}
@@ -57,7 +57,7 @@ @@ -57,7 +57,7 @@
57 </div> 57 </div>
58 <div class="product-info"> 58 <div class="product-info">
59 <a href="javascript:void(0)" class="product-title">{{dd.title}} 59 <a href="javascript:void(0)" class="product-title">{{dd.title}}
60 - <span class="label label-success pull-right">在线</span> 60 + <span class="label label-success pull-right" id="span-{{ d.devicetype.type }}-{{ dd.address }}">在线</span>
61 </a> 61 </a>
62 <span class="product-description"> 62 <span class="product-description">
63 检测区域:{{ dd.detectingpoint.no }}#{{ dd.detectingpoint.name }} 63 检测区域:{{ dd.detectingpoint.no }}#{{ dd.detectingpoint.name }}
  1 +{% extends 'admin/admin.html' %}
  2 +{% import "common/admin_page.html" as pg %}
  3 +{% block content %}
  4 +<!--内容-->
  5 +<section class="content-header">
  6 + <h1>安监物联管理系统</h1>
  7 + <ol class="breadcrumb">
  8 + <li><a href="#"><i class="fa fa-dashboard"></i> 报警管理</a></li>
  9 + <li class="active">邮件通知</li>
  10 + </ol>
  11 +</section>
  12 +<section class="content" id="showcontent">
  13 + <div class="row">
  14 + <div class="col-md-12">
  15 + <div class="box box-primary">
  16 + <div class="box-header">
  17 + <h3 class="box-title">通知列表</h3>
  18 +
  19 + <div class="box-tools">
  20 + <form action="" method="GET">
  21 + <div class="input-group input-group-sm" style="width: 150px;">
  22 + <input type="text" name="keyword" class="form-control pull-right"
  23 + placeholder="请输入邮箱">
  24 +
  25 + <div class="input-group-btn">
  26 + <button type="submit" class="btn btn-default"><i class="fa fa-search"></i>
  27 + </button>
  28 + </div>
  29 + </div>
  30 + </form>
  31 + </div>
  32 + </div>
  33 + <div class="box-body table-responsive no-padding">
  34 + {% for msg in get_flashed_messages(category_filter=["ok"]) %}
  35 + <div class="alert alert-success alert-dismissible">
  36 + <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×
  37 + </button>
  38 + <h4><i class="icon fa fa-check"></i> 操作成功</h4>
  39 + {{ msg }}
  40 + </div>
  41 + {% endfor %}
  42 + <table class="table table-hover" id="table">
  43 + <tbody>
  44 + <tr>
  45 + <th>编号</th>
  46 + <th>昵称</th>
  47 + <th>邮箱</th>
  48 + <th>头像</th>
  49 + <th>选择</th>
  50 + <th>操作</th>
  51 + </tr>
  52 + {% for v in page_data.items %}
  53 + <tr>
  54 + <td>{{ v.id }}</td>
  55 + <td>{{ v.username }}</td>
  56 + <td>{{ v.email }}</td>
  57 + <td>
  58 + <img style="width: 50px;height: 50px;"
  59 + src="{{ url_for('static', filename='uploads/users/huazai.jpg') }}"
  60 + class="img-responsive center-block" alt="">
  61 + </td>
  62 +
  63 + <td>
  64 + {% if v.emailnotification == 1 %}
  65 + <input type="checkbox" id="check{{ v.id }}" checked="checked">
  66 + {% else %}
  67 + <input type="checkbox" id="check{{ v.id }}">
  68 + {% endif %}
  69 + </td>
  70 + <td>
  71 +
  72 + <button onclick="submit_btn(this)" id="{{ v.id }}" class="btn btn-info btn-xs">保存</button>
  73 + </td>
  74 + </tr>
  75 + {% endfor %}
  76 + </tbody>
  77 + </table>
  78 + </div>
  79 + <div class="box-footer clearfix">
  80 + {{ pg.page(page_data,'admin.user_list') }}
  81 + </div>
  82 + </div>
  83 + </div>
  84 + </div>
  85 +</section>
  86 +<!--内容-->
  87 +{% endblock %}
  88 +
  89 +{% block js %}
  90 +<script>
  91 + $(document).ready(function(){
  92 + $("#g-5").addClass("active");
  93 + $("#g-5-2").addClass("active");
  94 + });
  95 +
  96 + function submit_btn(obj) {
  97 +
  98 +
  99 + var id = $(obj).attr("id");
  100 +
  101 + try {
  102 + var check = document.getElementById("check"+id).checked;
  103 + var c = 1;
  104 + if (check)
  105 + {
  106 + c = 1;
  107 + }
  108 + else
  109 + {
  110 + c = 0;
  111 + }
  112 + var s_url = "{{ s_url }}";
  113 + $.ajax({
  114 + type: "GET",
  115 + url: s_url + "/emailnotification_edit",
  116 + data: {id:id, email:c},
  117 + dataType: "json",
  118 + success: function (result) {
  119 + alert("保存成功");
  120 + },
  121 + error: function (e) {
  122 + alert("error");
  123 + }
  124 + });
  125 +
  126 + }
  127 +
  128 + catch (e) {
  129 + alert(e);
  130 + }
  131 +
  132 + }
  133 +
  134 +</script>
  135 +{% endblock %}
@@ -30,27 +30,38 @@ @@ -30,27 +30,38 @@
30 30
31 {% block js %} 31 {% block js %}
32 <script> 32 <script>
33 - $(document).ready(function(){ 33 + $(document).ready(function() {
34 $("#g-1").addClass("active"); 34 $("#g-1").addClass("active");
35 $("#g-1-1").addClass("active"); 35 $("#g-1-1").addClass("active");
36 36
37 37
38 }); 38 });
39 - $(document).ready(function(){  
40 -  
41 - var charta = echarts.init(document.getElementById('pie_a'), 'white', {renderer: 'canvas'});  
42 - var chartv = echarts.init(document.getElementById('pie_v'), 'white', {renderer: 'canvas'});  
43 - //var chartl = echarts.init(document.getElementById('liquid'), 'white', {renderer: 'canvas'}); 39 + $(document).ready(function() {
44 40
45 var s_url = "{{ s_url }}"; 41 var s_url = "{{ s_url }}";
46 - $(  
47 - function () {  
48 - fetchDataA(charta);  
49 - fetchDataV(chartv);  
50 - //fetchDataL(chartl);  
51 - //setInterval(getDynamicData, 2000);  
52 - }  
53 - ); 42 +
  43 + var pie_a = document.getElementById('pie_a');
  44 + var pie_v = document.getElementById('pie_v');
  45 +
  46 + if (pie_a) {
  47 + var charta = echarts.init(pie_a, 'white', {renderer: 'canvas'});
  48 +
  49 + $(
  50 + function () {
  51 + fetchDataA(charta);
  52 + }
  53 + );
  54 + }
  55 + if (pie_v) {
  56 + var chartv = echarts.init(pie_v, 'white', {renderer: 'canvas'});
  57 +
  58 + $(
  59 + function () {
  60 + fetchDataV(chartv);
  61 +
  62 + }
  63 + );
  64 + }
54 65
55 function fetchDataA() { 66 function fetchDataA() {
56 $.ajax({ 67 $.ajax({
@@ -87,25 +98,40 @@ @@ -87,25 +98,40 @@
87 }); 98 });
88 } 99 }
89 100
  101 + setInterval(getDynamicData, 10000);
  102 +
90 function getDynamicData() { 103 function getDynamicData() {
91 $.ajax({ 104 $.ajax({
92 type: "GET", 105 type: "GET",
93 - url: s_url + "/lineDynamicData", 106 + url: s_url + "/getDynamicData",
94 dataType: "json", 107 dataType: "json",
95 success: function (result) { 108 success: function (result) {
96 - chart.setOption(result);  
97 - //old_data.push([result.name, result.value]);  
98 - //if (old_data.length > 20)  
99 - //alert(old_data);  
100 - //old_data.shift();  
101 - //alert(old_data);  
102 - //chart.setOption({  
103 - // series: [{data: old_data}]  
104 - //}); 109 + dataAnalysis(result);
105 } 110 }
106 }); 111 });
107 } 112 }
  113 +
  114 + function dataAnalysis(res) {
  115 + if (res == null) return;
  116 +
  117 + try {
  118 + if (res.length> 0) {
  119 + for (var i = 0; i < res.length; i++) {
  120 + var r = res[i];
  121 +
  122 + }
  123 + }
  124 + }
  125 + catch (e) {
  126 + alert(e);
  127 + }
  128 +
  129 + }
  130 +
108 }); 131 });
  132 +
  133 +
  134 +
109 </script> 135 </script>
110 <script src="{{url_for('static',filename='base/js/jquery.easing.1.3.js')}}"></script> 136 <script src="{{url_for('static',filename='base/js/jquery.easing.1.3.js')}}"></script>
111 137
@@ -109,12 +109,12 @@ @@ -109,12 +109,12 @@
109 <ul class="treeview-menu"> 109 <ul class="treeview-menu">
110 110
111 <li id="g-5-1"> 111 <li id="g-5-1">
112 - <a href="#"> 112 + <a href="{{ url_for('admin.phone_notification') }}">
113 <i class="fa fa-circle-o"></i> 短信通知 113 <i class="fa fa-circle-o"></i> 短信通知
114 </a> 114 </a>
115 </li> 115 </li>
116 <li id="g-5-2"> 116 <li id="g-5-2">
117 - <a href="#"> 117 + <a href="{{ url_for('admin.email_notification') }}">
118 <i class="fa fa-circle-o"></i> 邮件通知 118 <i class="fa fa-circle-o"></i> 邮件通知
119 </a> 119 </a>
120 </li> 120 </li>
  1 +{% extends 'admin/admin.html' %}
  2 +{% import "common/admin_page.html" as pg %}
  3 +{% block content %}
  4 +<!--内容-->
  5 +<section class="content-header">
  6 + <h1>安监物联管理系统</h1>
  7 + <ol class="breadcrumb">
  8 + <li><a href="#"><i class="fa fa-dashboard"></i> 报警管理</a></li>
  9 + <li class="active">短信通知</li>
  10 + </ol>
  11 +</section>
  12 +<section class="content" id="showcontent">
  13 + <div class="row">
  14 + <div class="col-md-12">
  15 + <div class="box box-primary">
  16 + <div class="box-header">
  17 + <h3 class="box-title">通知列表</h3>
  18 +
  19 + <div class="box-tools">
  20 + <form action="" method="GET">
  21 + <div class="input-group input-group-sm" style="width: 150px;">
  22 + <input type="text" name="keyword" class="form-control pull-right"
  23 + placeholder="请输入手机号">
  24 +
  25 + <div class="input-group-btn">
  26 + <button type="submit" class="btn btn-default"><i class="fa fa-search"></i>
  27 + </button>
  28 + </div>
  29 + </div>
  30 + </form>
  31 + </div>
  32 + </div>
  33 + <div class="box-body table-responsive no-padding">
  34 + {% for msg in get_flashed_messages(category_filter=["ok"]) %}
  35 + <div class="alert alert-success alert-dismissible">
  36 + <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×
  37 + </button>
  38 + <h4><i class="icon fa fa-check"></i> 操作成功</h4>
  39 + {{ msg }}
  40 + </div>
  41 + {% endfor %}
  42 + <table class="table table-hover" id="table">
  43 + <tbody>
  44 + <tr>
  45 + <th>编号</th>
  46 + <th>昵称</th>
  47 + <th>手机</th>
  48 + <th>头像</th>
  49 + <th>选择</th>
  50 + <th>操作</th>
  51 + </tr>
  52 + {% for v in page_data.items %}
  53 + <tr>
  54 + <td>{{ v.id }}</td>
  55 + <td>{{ v.username }}</td>
  56 + <td>{{ v.phone }}</td>
  57 + <td>
  58 + <img style="width: 50px;height: 50px;"
  59 + src="{{ url_for('static', filename='uploads/users/huazai.jpg') }}"
  60 + class="img-responsive center-block" alt="">
  61 + </td>
  62 +
  63 + <td>
  64 + {% if v.phonenotification == 1 %}
  65 + <input type="checkbox" id="check{{ v.id }}" checked="checked">
  66 + {% else %}
  67 + <input type="checkbox" id="check{{ v.id }}">
  68 + {% endif %}
  69 + </td>
  70 + <td>
  71 +
  72 + <button onclick="submit_btn(this)" id="{{ v.id }}" class="btn btn-info btn-xs">保存</button>
  73 + </td>
  74 + </tr>
  75 + {% endfor %}
  76 + </tbody>
  77 + </table>
  78 + </div>
  79 + <div class="box-footer clearfix">
  80 + {{ pg.page(page_data,'admin.user_list') }}
  81 + </div>
  82 + </div>
  83 + </div>
  84 + </div>
  85 +</section>
  86 +<!--内容-->
  87 +{% endblock %}
  88 +
  89 +{% block js %}
  90 +<script>
  91 + $(document).ready(function(){
  92 + $("#g-5").addClass("active");
  93 + $("#g-5-1").addClass("active");
  94 + });
  95 +
  96 + function submit_btn(obj) {
  97 +
  98 +
  99 + var id = $(obj).attr("id");
  100 +
  101 + try {
  102 + var check = document.getElementById("check"+id).checked;
  103 + var c = 1;
  104 + if (check)
  105 + {
  106 + c = 1;
  107 + }
  108 + else
  109 + {
  110 + c = 0;
  111 + }
  112 + var s_url = "{{ s_url }}";
  113 + $.ajax({
  114 + type: "GET",
  115 + url: s_url + "/phonenotification_edit",
  116 + data: {id:id, phone:c},
  117 + dataType: "json",
  118 + success: function (result) {
  119 + alert("保存成功");
  120 + },
  121 + error: function (e) {
  122 + alert("error");
  123 + }
  124 + });
  125 +
  126 + }
  127 +
  128 + catch (e) {
  129 + alert(e);
  130 + }
  131 +
  132 + }
  133 +
  134 +</script>
  135 +{% endblock %}
@@ -6,28 +6,6 @@ import zmail @@ -6,28 +6,6 @@ import zmail
6 app = create_app("default") 6 app = create_app("default")
7 7
8 8
9 -def send_email(title, content):  
10 - try:  
11 - # 设置邮件信息  
12 - mail_content = {  
13 - 'subject': title, # 邮件标题  
14 - 'content_text': content # 邮件内容  
15 - }  
16 - # 发件人的用户名和密码  
17 - server = zmail.server('yangzaihong@163.com', 'OTGKSKPJIMBXYLUK') # 发件人  
18 - # 发送邮件  
19 - server.send_mail('905462018@qq.com', mail_content) # 收件人  
20 - print('邮件已发送完毕')  
21 - except Exception as e:  
22 - print(e)  
23 -  
24 -  
25 -def get_generate_password_hash(pwd):  
26 - from werkzeug.security import generate_password_hash  
27 - return generate_password_hash(password=pwd)  
28 -  
29 -  
30 if __name__ == '__main__': 9 if __name__ == '__main__':
31 - # print(get_generate_password_hash("123456"))  
32 - # send_email('测试', '测试') 10 +
33 app.run(host='0.0.0.0', port=5000, debug=True) 11 app.run(host='0.0.0.0', port=5000, debug=True)
1 -import pyecharts  
2 -print(pyecharts.__version__) # 输出 '2.0.5'  
3 -#print(pyecharts.__python_version__) # 输出 pyecharts依赖的Python版本