Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create Webhooks Framework #314

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
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
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM python:3.6.9
FROM python:3.7

ENV HOST=0.0.0.0
ENV PORT=80
Expand Down
17 changes: 17 additions & 0 deletions app/main/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ def __init__(self, machineType=None):
self.remaining_time = None
self.is_pico = True if machineType in [MachineType.PICOBREW, MachineType.PICOBREW_C] else False
self.data = []
self.webhooks = []

def cleanup(self):
if self.file and self.filepath:
Expand All @@ -77,6 +78,7 @@ def cleanup(self):
self.recovery = ''
self.remaining_time = None
self.data = []
self.webhooks = []


class PicoStillSession:
Expand All @@ -93,6 +95,7 @@ def __init__(self, uid=None):
self.session = '' # session guid
self.polling_thread = None
self.data = []
self.webhooks = []

def cleanup(self):
if self.file and self.filepath:
Expand All @@ -107,6 +110,7 @@ def cleanup(self):
self.polling_thread = None
self.session = ''
self.data = []
self.webhooks = []

def start_still_polling(self):
connect_failure = False
Expand Down Expand Up @@ -150,6 +154,7 @@ def __init__(self):
self.voltage = '-'
self.start_time = None
self.data = []
self.webhooks = []

def cleanup(self):
if self.file and self.filepath:
Expand All @@ -161,6 +166,7 @@ def cleanup(self):
self.voltage = '-'
self.start_time = None
self.data = []
self.webhooks = []


class iSpindelSession:
Expand All @@ -173,6 +179,7 @@ def __init__(self):
self.voltage = '-'
self.start_time = None
self.data = []
self.webhooks = []

def cleanup(self):
if self.file and self.filepath:
Expand All @@ -185,6 +192,7 @@ def cleanup(self):
self.voltage = '-'
self.start_time = None
self.data = []
self.webhooks = []


class TiltSession:
Expand All @@ -198,6 +206,7 @@ def __init__(self):
self.rssi = None
self.start_time = None
self.data = []
self.webhooks = []

def cleanup(self):
if self.file and self.filepath:
Expand All @@ -210,6 +219,14 @@ def cleanup(self):
self.rssi = None
self.start_time = None
self.data = []
self.webhooks = []


class Webhook:
def __init__(self, url=None, enabled=None, status="disabled"):
self.url = url
self.enabled = enabled
self.status = status if not enabled and status == "disabled" else "enabled"


class SupportObject:
Expand Down
184 changes: 149 additions & 35 deletions app/main/routes_frontend.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import csv
import io
import json
import os
import uuid
from datetime import timedelta
from datetime import datetime, timedelta
from flask import current_app, escape, make_response, request, send_file, render_template
from pathlib import Path
from ruamel.yaml import YAML
Expand All @@ -13,15 +15,15 @@
zymatic_recipe_path, zseries_recipe_path, pico_recipe_path,
brew_archive_sessions_path, ferm_archive_sessions_path, still_archive_sessions_path, iSpindel_archive_sessions_path, tilt_archive_sessions_path)
from .frontend_common import render_template_with_defaults
from .model import Webhook
from .recipe_import import import_recipes
from .recipe_parser import PicoBrewRecipe, ZymaticRecipe, ZSeriesRecipe
from .session_parser import (_paginate_sessions, list_session_files,
load_ferm_session, load_still_session, load_iSpindel_session, load_tilt_session,
dirty_sessions_since_clean, last_session_metadata, BrewSessionType,
get_brew_graph_data, get_ferm_graph_data, get_still_graph_data, get_iSpindel_graph_data, get_tilt_graph_data,
active_brew_sessions, active_ferm_sessions, active_still_sessions, active_iSpindel_sessions, active_tilt_sessions,
add_invalid_session, get_invalid_sessions, load_brew_sessions)

add_invalid_session, get_invalid_sessions, load_brew_session, load_brew_sessions)

file_glob_pattern = "[!._]*.json"
yaml = YAML()
Expand Down Expand Up @@ -211,17 +213,7 @@ def update_zseries_recipe():
@main.route('/device/<uid>/sessions/<session_type>', methods=['PUT'])
def update_device_session(uid, session_type):
update = request.get_json()
valid_session = True
if session_type == 'ferm':
session = active_ferm_sessions[uid]
elif session_type == 'iSpindel':
session = active_iSpindel_sessions[uid]
elif session_type == 'tilt':
session = active_tilt_sessions[uid]
elif session_type == 'still':
session = active_still_sessions[uid]
else:
valid_session = False
session, valid_session = active_session(uid, session_type)

if valid_session:
if update['active'] == False:
Expand Down Expand Up @@ -260,6 +252,27 @@ def allowed_extension(filename):
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS


@main.route('/device/<uid>/sessions/<session_type>/webhooks', methods=['POST'])
def update_session_webhooks(uid, session_type):
body = request.get_json()

# current_app.logger.error(f'request_body : {webhooks}')
session, valid_session = active_session(uid, session_type)

if valid_session:
# save/update webhook definition(s)
webhooks = []
for webhook in body['webhooks']:
webhooks.append(Webhook(webhook['url'], webhook['enabled']))

session.webhooks = webhooks
current_app.logger.debug(f'configured {len(session.webhooks)} webhook to the active {session_type} session {uid}')
return 'configuration of webhooks successful', 200
else:
current_app.logger.error(f'unable to locate valid active session : {session_type} and {uid}')
return f'Invalid session type or device id provided {session_type}, {uid}', 418


def recipe_dirpath(machine_type):
dirpath = None
if machine_type == "picobrew" or machine_type == "pico":
Expand Down Expand Up @@ -323,8 +336,8 @@ def download_recipe(machine_type, id, name):
return f'Download Recipe: Failed to find recipe id "{id}" with name "{name}"', 418


@main.route('/sessions/<session_type>/<filename>', methods=['GET'])
def download_session(session_type, filename):
@main.route('/sessions/<session_type>/<filename>.<extension>', methods=['GET'])
def download_session(session_type, filename, extension):
session_dirpath = ""
if session_type == "brew":
session_dirpath = brew_archive_sessions_path()
Expand All @@ -341,11 +354,90 @@ def download_session(session_type, filename):
filepath = session_dirpath.joinpath(filename)

for f in files:
if f.name == filename:
response = make_response(send_file(filepath))
# custom content-type will force a download vs rendering with window.location
response.headers['Content-Type'] = 'application/octet-stream'
return response
if f.stem == filename:
if extension == "json":
response = make_response(send_file(f'{filepath}.json'))
# custom content-type will force a download vs rendering with window.location
response.headers['Content-Type'] = 'application/octet-stream'
return response
elif extension == "csv":
session = load_brew_session(f)

output = io.StringIO()
writer = csv.writer(output)

data_map = dict()
if session['is_pico']: # pico
data_map = {
"#PicoSessionID": "SessionID",
"PicoSessionLogID": "SessionLogID", # not in JSON data
"LogDate": "timeStr",
"WorkTemp": "wort",
"ThermoBlockTemp": "therm",
"Event": "event",
"ErrorCode": "errorCode",
"ShuttleScaler": None,
}
elif len(session['data']) >= 2 and 'board' in session['data'][1]: # zymatic
data_map = {
"#SessionID": "SessionID",
"SessionLogID": "SessionLogID", # not in JSON data
"Date": "timeStr",
"Event": "event",
"Heat": "heat1",
"Wort": "wort",
"Board": "board",
"Heat2": "heat2",
}
elif len(session['data']) >= 1 and 'ambient' in session['data'][0]: # zseries
data_map = {
"#ZSessionID": "SessionID",
"ID": "SessionLogID", # not in JSON data
"LogDate": "timeStr",
"StepName": "step",
"TargetTemp": "target",
"WortTemp": "wort",
"ThermoBlockTemp": "therm",
"AmbientTemp": "ambient",
"DrainTemp": "drain",
"ValvePosition": "position",
"KegPumpOn": False,
"DrainPumpOn": False,
"ErrorCode": 0,
"PauseReason": 0,
"rssi": 0,
"netSend": 0,
"netWait": 0,
"netRecv": 0
}

writer.writerow(data_map.keys())

for index, log_data in enumerate(session['data']):
data = []
for key, data_key in data_map.items():
if data_key == "SessionID": # not in log data
data.append(session['session'])
elif data_key == "SessionLogID": # not in log data
data.append(index)
elif data_key == "timeStr":
log_time = datetime.strptime(log_data[data_key], '%Y-%m-%dT%H:%M:%S.%f')
data.append(log_time.strftime("%m/%d/%Y %H:%M:%S %p"))
elif type(data_key) is not str:
data.append(data_key)
elif type(data_key) is str:
data.append(log_data[data_key])

# add data to csv
writer.writerow(data)

response = make_response(output.getvalue())
response.headers["Content-Disposition"] = f'attachment; filename={filename}.{extension}'
response.headers["Content-type"] = "text/html"
return response
else:
return f'Download Session: Failed due to unsupported file extension "{extension}"', 418

return f'Download Session: Failed to find session with filename "{filename}"', 418


Expand Down Expand Up @@ -561,7 +653,8 @@ def load_active_brew_sessions():
'graph': get_brew_graph_data(uid, active_brew_sessions[uid].name,
active_brew_sessions[uid].step,
active_brew_sessions[uid].data,
active_brew_sessions[uid].is_pico)}
active_brew_sessions[uid].is_pico),
'webhooks': active_brew_sessions[uid].webhooks}

if len(session_data) > 0:
if 'timeLeft' in session_data[-1]:
Expand Down Expand Up @@ -599,7 +692,8 @@ def load_active_ferm_sessions():
'active': active_ferm_sessions[uid].active,
'date': active_ferm_sessions[uid].start_time or None,
'graph': get_ferm_graph_data(uid, active_ferm_sessions[uid].voltage,
active_ferm_sessions[uid].data)})
active_ferm_sessions[uid].data),
'webhooks': active_ferm_sessions[uid].webhooks})
return ferm_sessions


Expand All @@ -624,11 +718,13 @@ def load_active_still_sessions():
still_sessions = []
for uid in active_still_sessions:
still_sessions.append({'alias': active_still_sessions[uid].alias,
'uid': uid,
'ip_address': active_still_sessions[uid].ip_address,
'active': active_still_sessions[uid].active,
'date': active_still_sessions[uid].created_at or None,
'graph': get_still_graph_data(uid, active_still_sessions[uid].name, active_still_sessions[uid].data)})
'uid': uid,
'ip_address': active_still_sessions[uid].ip_address,
'active': active_still_sessions[uid].active,
'date': active_still_sessions[uid].created_at or None,
'graph': get_still_graph_data(uid, active_still_sessions[uid].name,
active_still_sessions[uid].data),
'webhooks': active_still_sessions[uid].webhooks})
return still_sessions


Expand Down Expand Up @@ -657,7 +753,8 @@ def load_active_iSpindel_sessions():
'active': active_iSpindel_sessions[uid].active,
'date': active_iSpindel_sessions[uid].start_time or None,
'graph': get_iSpindel_graph_data(uid, active_iSpindel_sessions[uid].voltage,
active_iSpindel_sessions[uid].data)})
active_iSpindel_sessions[uid].data),
'webhooks': active_iSpindel_sessions[uid].webhooks})
return iSpindel_sessions


Expand All @@ -681,12 +778,13 @@ def load_active_tilt_sessions():
tilt_sessions = []
for uid in active_tilt_sessions:
tilt_sessions.append({'alias': active_tilt_sessions[uid].alias,
'uid': uid,
'color': active_tilt_sessions[uid].color,
'active': active_tilt_sessions[uid].active,
'date': active_tilt_sessions[uid].start_time or None,
'graph': get_tilt_graph_data(uid, active_tilt_sessions[uid].rssi,
active_tilt_sessions[uid].data)})
'uid': uid,
'color': active_tilt_sessions[uid].color,
'active': active_tilt_sessions[uid].active,
'date': active_tilt_sessions[uid].start_time or None,
'graph': get_tilt_graph_data(uid, active_tilt_sessions[uid].rssi,
active_tilt_sessions[uid].data),
'webhooks': active_tilt_sessions[uid].webhooks})
return tilt_sessions


Expand Down Expand Up @@ -739,3 +837,19 @@ def increment_zseries_recipe_id():
recipe_id += 1

return recipe_id


def active_session(uid, session_type):
session = None
if session_type == 'brew':
session = active_brew_sessions[uid]
elif session_type == 'ferm':
session = active_ferm_sessions[uid]
elif session_type == 'iSpindel':
session = active_iSpindel_sessions[uid]
elif session_type == 'tilt':
session = active_tilt_sessions[uid]
elif session_type == 'still':
session = active_still_sessions[uid]

return session, session != None
Loading