diff options
Diffstat (limited to 'snag/graphs.py')
-rw-r--r-- | snag/graphs.py | 315 |
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') + |