aboutsummaryrefslogtreecommitdiffstats
path: root/snag/graphs.py
diff options
context:
space:
mode:
Diffstat (limited to 'snag/graphs.py')
-rw-r--r--snag/graphs.py315
1 files changed, 315 insertions, 0 deletions
diff --git a/snag/graphs.py b/snag/graphs.py
new file mode 100644
index 0000000..2905d20
--- /dev/null
+++ b/snag/graphs.py
@@ -0,0 +1,315 @@
+# snag
+# Matt Kohls
+# (c) 2023
+
+import functools
+from flask import (
+ Blueprint, flash, g, redirect, render_template, request, session, url_for, Response
+)
+from werkzeug.exceptions import abort
+from datetime import datetime, timedelta
+from dateutil import parser
+from io import BytesIO
+from matplotlib.figure import Figure
+import matplotlib.dates as mdates
+from snag.db import get_db
+
+bp = Blueprint('graph', __name__, url_prefix='/graph')
+
+def get_table(env):
+ if env == 'outdoor':
+ return 'env_outdoor'
+ elif env == 'indoor':
+ return 'env_indoor'
+ elif env == 'enclosed':
+ return 'env_enclosed'
+ else:
+ return None
+
+def get_current(requestArgs, columns, table):
+ # start and end should be formatted YYYYMMDDHHMMSS
+ start = requestArgs.get('start')
+ end = requestArgs.get('end')
+ env = requestArgs.get('type')
+ deviceId = request.args.get('deviceId')
+
+ # Sets default dates of information to be fetched, starttime will default to one day behind endtime, which defaults to now
+ if end is None:
+ endtime = datetime.now()
+ else:
+ endtime = datetime.strptime(end, "%Y%m%d%H%M%S")
+ if start is None:
+ starttime = endtime - timedelta(days=1)
+ else:
+ starttime = datetime.strptime(start, "%Y%m%d%H%M%S")
+
+ if endtime < starttime:
+ temp = starttime
+ starttime = endtime
+ endtime = temp
+
+ if deviceId is None:
+ deviceId = 1
+
+ db = get_db()
+
+ if table is not None:
+ sql = (f"SELECT {columns} "
+ f"FROM {table} "
+ "WHERE (date BETWEEN ? AND ?) AND deviceId = ?")
+ subset = db.execute(sql, [starttime, endtime, deviceId])
+ else:
+ subset = None
+
+ return subset
+
+def single_line_graph(xData, yData, xLabel, yLabel, title, lineColor):
+ fig = Figure()
+ ax = fig.subplots()
+ locator = mdates.AutoDateLocator(minticks=3, maxticks=7)
+ formatter = mdates.ConciseDateFormatter(locator)
+
+ ax.xaxis.set_major_locator(locator)
+ ax.xaxis.set_major_formatter(formatter)
+ ax.plot(xData, yData, color=lineColor)
+ ax.set_xlabel(xLabel)
+ ax.set_ylabel(yLabel)
+ ax.set_title(title)
+
+ buf = BytesIO()
+ fig.savefig(buf, format="png")
+
+ return buf
+
+def double_line_graph(xData, yData1, yData2, xLabel, yLabel1, yLabel2, title, line1Color, line2Color, line1Label, line2Label):
+ fig = Figure()
+ ax = fig.subplots()
+ bx = ax.twinx()
+ locator = mdates.AutoDateLocator(minticks=3, maxticks=7)
+ formatter = mdates.ConciseDateFormatter(locator)
+
+ ax.xaxis.set_major_locator(locator)
+ ax.xaxis.set_major_formatter(formatter)
+ bx.xaxis.set_major_locator(locator)
+ bx.xaxis.set_major_formatter(formatter)
+ aline, = ax.plot(xData, yData1, color=line1Color, label=line1Label)
+ ax.set_xlabel(xLabel)
+ ax.set_ylabel(yLabel1)
+ ax.set_title(title)
+ bline, = bx.plot(xData, yData2, color=line2Color, label=line2Label)
+ bx.set_ylabel(yLabel2)
+
+ lines = [aline, bline]
+ ax.legend(lines, [l.get_label() for l in lines])
+
+ buf = BytesIO()
+ fig.savefig(buf, format="png")
+
+ return buf
+
+def double_y_graph(xData, yData1, yData2, xLabel, yLabel1, yLabel2, title, line1Color):
+ fig = Figure()
+ ax = fig.subplots()
+ bx = ax.twinx()
+ locator = mdates.AutoDateLocator(minticks=3, maxticks=7)
+ formatter = mdates.ConciseDateFormatter(locator)
+
+ ax.xaxis.set_major_locator(locator)
+ ax.xaxis.set_major_formatter(formatter)
+ bx.xaxis.set_major_locator(locator)
+ bx.xaxis.set_major_formatter(formatter)
+ ax.plot(xData, yData1, color=line1Color)
+ ax.set_xlabel(xLabel)
+ ax.set_ylabel(yLabel1)
+ ax.set_title(title)
+ bx.plot(xData, yData2, visible=False)
+ bx.set_ylabel(yLabel2)
+
+ buf = BytesIO()
+ fig.savefig(buf, format="png")
+
+ return buf
+
+@bp.route('/temperature.png', methods=['GET'])
+def draw_t_graph():
+ cur = get_current(request.args, "date, temperature", get_table(request.args['type']))
+
+ if cur is None:
+ abort(404, "Data not found")
+
+ data = cur.fetchall()
+ dates = []
+ tempsC = []
+ tempsF = []
+
+ if data is None:
+ abort(404, "Data not found")
+
+ for row in data:
+ if isinstance(row['date'], str):
+ dates.append(parser.parse(row['date']))
+ else:
+ dates.append(row['date'])
+ tempsC.append(row['temperature'])
+ tempsF.append(row['temperature'] * 1.8 + 32)
+
+ buf = double_y_graph(dates, tempsC, tempsF, "Timestamps", "Celsius", "Fahrenheit", "Temperature", "C1")
+
+ return Response(buf.getvalue(), mimetype='image/png')
+
+@bp.route('/humidity.png', methods=['GET'])
+def draw_h_graph():
+ cur = get_current(request.args, "date, humidity", get_table(request.args['type']))
+
+ if cur is None:
+ abort(404, "Data not found")
+
+ data = cur.fetchall()
+ dates = []
+ humidities = []
+
+ if data is None:
+ abort(404, "Data not found")
+
+ for row in data:
+ if isinstance(row['date'], str):
+ dates.append(parser.parse(row['date']))
+ else:
+ dates.append(row['date'])
+ humidities.append(row['humidity'])
+
+ buf = single_line_graph(dates, humidities, "Timestamps", "Percent", "Relative Humidity", "C0")
+
+ return Response(buf.getvalue(), mimetype='image/png')
+
+@bp.route('/humidity-temperature.png', methods=['GET'])
+def draw_ht_graph():
+ cur = get_current(request.args, "date, temperature, humidity", get_table(request.args['type']))
+
+ if cur is None:
+ abort(404, "Data not found")
+
+ data = cur.fetchall()
+ dates = []
+ humidities = []
+ #tempsC = []
+ tempsF = []
+
+ if data is None:
+ abort(404, "Data not found")
+
+ for row in data:
+ if isinstance(row['date'], str):
+ dates.append(parser.parse(row['date']))
+ else:
+ dates.append(row['date'])
+ # tempsC.append(row['temperature'])
+ tempsF.append(row['temperature'] * 1.8 + 32)
+ humidities.append(row['humidity'])
+
+ buf = double_line_graph(dates, humidities, tempsF, "Timestamps", "Percent", "Fahrenheit", "Humidity and Temperature", "C0", "C1", "Relative Humidity", "Temperature")
+
+ return Response(buf.getvalue(), mimetype='image/png')
+
+@bp.route('/pressure.png', methods=['GET'])
+def draw_p_graph():
+ cur = get_current(request.args, "date, pressure", get_table(request.args['type']))
+
+ if cur is None:
+ abort(404, "Data not found")
+
+ data = cur.fetchall()
+ dates = []
+ pressures = []
+
+ if data is None:
+ abort(404, "Data not found")
+
+ for row in data:
+ if isinstance(row['date'], str):
+ dates.append(parser.parse(row['date']))
+ else:
+ dates.append(row['date'])
+ pressures.append(row['pressure'])
+
+ buf = single_line_graph(dates, pressures, "Timestamps", "hPa", "Barometric Pressure", "C2")
+
+ return Response(buf.getvalue(), mimetype='image/png')
+
+@bp.route('/battery.png', methods=['GET'])
+def draw_b_graph():
+ cur = get_current(request.args, "date, batteryCharge", "device_status")
+
+ if cur is None:
+ abort(404, "Data not found")
+
+ data = cur.fetchall()
+ dates = []
+ batteries = []
+
+ if data is None:
+ abort(404, "Data not found")
+
+ for row in data:
+ if isinstance(row['date'], str):
+ dates.append(parser.parse(row['date']))
+ else:
+ dates.append(row['date'])
+ batteries.append(row['batteryCharge'])
+
+ buf = single_line_graph(dates, batteries, "Timestamps", "Volts", "Battery Voltage", "C3")
+
+ return Response(buf.getvalue(), mimetype='image/png')
+
+@bp.route('/light.png', methods=['GET'])
+def draw_l_graph():
+ env = request.args['type']
+
+ if (env is not None) and (env == 'outdoor'):
+ cur = get_current(request.args, "date, uv_intensity, lux", get_table(env))
+
+ if cur is None:
+ abort(404, "Data not found")
+
+ data = cur.fetchall()
+
+ if data is None:
+ abort(404, "Data not found")
+
+ dates = []
+ uv = []
+ lux = []
+ for row in data:
+ if isinstance(row['date'], str):
+ dates.append(parser.parse(row['date']))
+ else:
+ dates.append(row['date'])
+ uv.append(row['uv_intensity'])
+ lux.append(row['lux'])
+
+ buf = double_line_graph(dates, uv, lux, "Timestamps", "UV Index", "Lux", "UV and Light Intensity", "C4", "C5", "UV Light", "Light Intensity")
+
+ else:
+ cur = get_current(request.args, "date, light", get_table(env))
+
+ if cur is None:
+ abort(404, "Data not found")
+
+ data = cur.fetchall()
+
+ if data is None:
+ abort(404, "Data not found")
+
+ dates = []
+ light = []
+ for row in data:
+ if isinstance(row['date'], str):
+ dates.append(parser.parse(row['date']))
+ else:
+ dates.append(row['date'])
+ light.append(row['light'])
+
+ buf = single_line_graph(dates, light, "Timestamps", "Raw Light Reading", "Light Intensity", "C5")
+
+ return Response(buf.getvalue(), mimetype='image/png')
+