Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 116 additions & 0 deletions app/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

from datetime import datetime
from dateutil import parser
import plotly.graph_objs as go
import plotly.io as pio

from flask import Blueprint, abort, jsonify, g, redirect, request, session
from sqlalchemy import text, or_
Expand Down Expand Up @@ -280,6 +282,55 @@ def _get_mysql_raw_sql(interval, table_name, field, start, end, limit):
return raw_sql


# 新增動態處理函數
@api.route('/dynamic_datas', methods=['GET'])
@utils.required_login
def api_dynamic_datas():
"""
:args f: fields, like ['flower', 'orange']
:args s: sensors, like ['Temperature', 'AtPressure']
:args st: start_time, any time format
:args et: end_time, any time format
:args i: interval, only allow `second`, `minute`, `hour`, `day`, default `hour`
:args l: limit, query limit, default config.QUERY_LIMIT

example:
http://your.domain/api/dynamic_datas?f=flower&f=orange&s=Temperature&s=AtPressure&st=2018-06-26&et=2018-06-27&i=second
"""
stime = datetime.now()

fields = request.args.getlist('f')
sensors = request.args.getlist('s')
start_time = request.args.get('st')
end_time = request.args.get('et')
interval = request.args.get('i', 'hour')
limit = int(request.args.get('l')) if request.args.get('l') else None

if not fields or not sensors or not start_time or not end_time:
abort(404)

start = parser.parse(start_time).strftime('%Y-%m-%d %H:%M:%S')
end = parser.parse(end_time).strftime('%Y-%m-%d %H:%M:%S')

result = {}

for field, sensor in zip(fields, sensors):
tablename = sensor.replace('-O', '')
if not hasattr(db.models, tablename):
abort(404)
table = getattr(db.models, tablename)

if sensor not in result:
result[sensor] = {}

data = _query_data(interval, table.__tablename__, field, start, end, limit)
result[sensor].update({field: data})

etime = datetime.now()
log.debug((etime - stime).total_seconds())
return jsonify(result)


@api.route('/user/pwd', methods=['POST'])
@utils.required_login
def api_user_change_pwd():
Expand Down Expand Up @@ -747,3 +798,68 @@ def api_field():
return 'ok'

abort(404)

#plotly繪圖api
@api.route('/plotly', methods=['POST'])
def plotly_api():
try:
# 從前端的請求中獲取資料
traces = request.json['traces']
plot_method = request.json.get('plotMethod', 'line')

all_traces = []
for trace_data in traces:
timestamps = trace_data['timestamps']
values = trace_data['values']
field = trace_data.get('field', 'Unknown')
sensor = trace_data.get('sensor', 'Unknown')

# 創建單個 trace
trace = create_trace(plot_method, timestamps, values, field, sensor)
all_traces.append(trace)

layout = {
'xaxis': {'title': 'Time', 'tickformat': '%Y-%m-%d %H:%M:%S', 'tickangle': 45},
'yaxis': {'title': 'Value'}
}

# 生成圖表
fig = go.Figure(data=all_traces, layout=layout)
graph_json = pio.to_json(fig)

return jsonify(graph_json), 200

except Exception as e:
# 印出錯誤訊息以幫助診斷問題
print(f"Error: {str(e)}")
return jsonify({'error': str(e)}), 400




# 生成trace的函式
def create_trace(plot_method, timestamps, values, field, sensor):
trace = {}

if plot_method == 'heatmap':
trace = go.Heatmap(
x=timestamps,
y=[f"{field} - {sensor}"] * len(timestamps),
z=values,
colorscale='Viridis'
)
else:
trace = go.Scatter(
x=timestamps,
y=values,
name=f"{field} - {sensor}",
mode='lines+markers' if plot_method == 'line' else 'markers'
)
if plot_method == 'box':
trace = go.Box(y=values, name=f"{field} - {sensor}")
elif plot_method == 'bar':
trace = go.Bar(x=timestamps, y=values, name=f"{field} - {sensor}")
elif plot_method == 'histogram':
trace = go.Histogram(x=values, name=f"{field} - {sensor}")

return trace
67 changes: 67 additions & 0 deletions app/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
from flask import Flask, render_template, jsonify, g, abort
import logging
import json
from datetime import datetime
from dateutil import parser
from sqlalchemy import text, or_

from api import api # Import the Blueprint from api.py

app = Flask(__name__)
app.register_blueprint(api, url_prefix='/api') # Register the Blueprint

# 配置記錄
logging.basicConfig(level=logging.DEBUG)

@app.route('/')
def index():
return 'Hello, World!'

@app.route('/en/plot_number')
def plot_number():
fields = get_fields()
return render_template('plot_number_of_datas.html', fields=json.dumps(fields))

def get_fields():
fields = []
query_fields = g.session.query(db.models.field).order_by(db.models.field.id).all()
for field in query_fields:
temp_field = utils.row2dict(field)
query_field_sensor = (
g.session
.query(
db.models.sensor.name,
db.models.field_sensor.sensor,
db.models.field_sensor.df_name,
db.models.field_sensor.alias,
db.models.field_sensor.unit,
db.models.field_sensor.icon,
db.models.field_sensor.bg_color,
db.models.field_sensor.alert_min,
db.models.field_sensor.alert_max
)
.select_from(db.models.field_sensor)
.join(db.models.sensor)
.filter(db.models.field_sensor.field == field.id)
.order_by(db.models.field_sensor.id)
.all()
)
temp_field['sensors'] = []
for sensor in query_field_sensor:
temp_sensor = {
'name': sensor.name,
'sensor': sensor.sensor,
'df_name': sensor.df_name,
'alias': sensor.alias,
'unit': sensor.unit,
'icon': sensor.icon,
'bg_color': sensor.bg_color,
'alert_min': sensor.alert_min,
'alert_max': sensor.alert_max,
}
temp_field['sensors'].append(temp_sensor)
fields.append(temp_field)
return fields

if __name__ == '__main__':
app.run(debug=True)
1 change: 1 addition & 0 deletions app/static/js/axios.min.map

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions app/templates/base/sidebar.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<li><a href="/{{ g.get('lang_code') }}/dashboard"><i class="fa fa-dashboard"></i> <span>{{ _('Dashboard') }}</span></a></li>
<li><a href="/{{ g.get('lang_code') }}/history"><i class="fa fa-book"></i> <span>{{ _('History') }}</span></a></li>
<li><a href="/{{ g.get('lang_code') }}/compare"><i class="fa fa-signal"></i> <span>{{ _('Compare') }}</span></a></li>
<li><a href="/{{ g.get('lang_code') }}/plot"><i class="fa fa-signal"></i> <span>{{ _('Visualization') }}</span></a></li>
{% if is_superuser %}
<li><a href="/{{ g.get('lang_code') }}/management"><i class="fa fa-cogs"></i> <span>{{ _('Management') }}</span></a></li>
{% endif %}
Expand Down
Loading