Skip to content

Commit

Permalink
move flask handler.
Browse files Browse the repository at this point in the history
  • Loading branch information
anarkiwi committed Jun 6, 2024
1 parent 23fbac1 commit 689becc
Show file tree
Hide file tree
Showing 2 changed files with 229 additions and 221 deletions.
224 changes: 224 additions & 0 deletions gamutrfwaterfall/gamutrfwaterfall/flask_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
import datetime
import json
import logging
import multiprocessing
import os
import time
import requests
import zmq
from flask import (
Flask,
send_file,
render_template,
send_from_directory,
request,
redirect,
)


def get_scanner_args(api_endpoint, config_vars):
try:
response = requests.get(f"http://{api_endpoint}/getconf", timeout=30)
response.raise_for_status()

Check warning on line 22 in gamutrfwaterfall/gamutrfwaterfall/flask_handler.py

View check run for this annotation

Codecov / codecov/patch

gamutrfwaterfall/gamutrfwaterfall/flask_handler.py#L20-L22

Added lines #L20 - L22 were not covered by tests

for name, val in json.loads(response.text).items():
config_vars[name] = val

Check warning on line 25 in gamutrfwaterfall/gamutrfwaterfall/flask_handler.py

View check run for this annotation

Codecov / codecov/patch

gamutrfwaterfall/gamutrfwaterfall/flask_handler.py#L24-L25

Added lines #L24 - L25 were not covered by tests

except (

Check warning on line 27 in gamutrfwaterfall/gamutrfwaterfall/flask_handler.py

View check run for this annotation

Codecov / codecov/patch

gamutrfwaterfall/gamutrfwaterfall/flask_handler.py#L27

Added line #L27 was not covered by tests
requests.exceptions.ConnectionError,
requests.exceptions.HTTPError,
requests.exceptions.ConnectTimeout,
) as err:
logging.error(err)

Check warning on line 32 in gamutrfwaterfall/gamutrfwaterfall/flask_handler.py

View check run for this annotation

Codecov / codecov/patch

gamutrfwaterfall/gamutrfwaterfall/flask_handler.py#L32

Added line #L32 was not covered by tests


def write_scanner_args(config_vars_path, config_vars):
tmpfile = os.path.join(

Check warning on line 36 in gamutrfwaterfall/gamutrfwaterfall/flask_handler.py

View check run for this annotation

Codecov / codecov/patch

gamutrfwaterfall/gamutrfwaterfall/flask_handler.py#L36

Added line #L36 was not covered by tests
os.path.dirname(config_vars_path),
"." + os.path.basename(config_vars_path),
)
with open(tmpfile, "w", encoding="utf8") as f:
json.dump(config_vars, f)
os.rename(tmpfile, config_vars_path)

Check warning on line 42 in gamutrfwaterfall/gamutrfwaterfall/flask_handler.py

View check run for this annotation

Codecov / codecov/patch

gamutrfwaterfall/gamutrfwaterfall/flask_handler.py#L40-L42

Added lines #L40 - L42 were not covered by tests


class FlaskHandler:
def __init__(
self,
savefig_path,
tempdir,
predictions,
port,
refresh,
inference_server,
inference_port,
api_endpoint,
config_vars,
config_vars_path,
):
self.inference_addr = f"tcp://{inference_server}:{inference_port}"
self.savefig_path = savefig_path
self.config_vars = config_vars
self.config_vars_path = config_vars_path
self.tempdir = tempdir
self.predictions_file = "predictions.html"
self.refresh = refresh
self.predictions = predictions
self.api_endpoint = api_endpoint
self.app = Flask(__name__, template_folder="templates", static_folder="static")
self.savefig_file = os.path.basename(self.savefig_path)
self.app.add_url_rule("/", "index", self.serve_waterfall_page)
self.app.add_url_rule(

Check warning on line 71 in gamutrfwaterfall/gamutrfwaterfall/flask_handler.py

View check run for this annotation

Codecov / codecov/patch

gamutrfwaterfall/gamutrfwaterfall/flask_handler.py#L59-L71

Added lines #L59 - L71 were not covered by tests
"/waterfall", "serve_waterfall_page", self.serve_waterfall_page
)
self.app.add_url_rule(

Check warning on line 74 in gamutrfwaterfall/gamutrfwaterfall/flask_handler.py

View check run for this annotation

Codecov / codecov/patch

gamutrfwaterfall/gamutrfwaterfall/flask_handler.py#L74

Added line #L74 was not covered by tests
"/waterfall_img", "serve_waterfall_img", self.serve_waterfall_img
)
self.app.add_url_rule(

Check warning on line 77 in gamutrfwaterfall/gamutrfwaterfall/flask_handler.py

View check run for this annotation

Codecov / codecov/patch

gamutrfwaterfall/gamutrfwaterfall/flask_handler.py#L77

Added line #L77 was not covered by tests
"/config_form", "config_form", self.config_form, methods=["POST", "GET"]
)
self.app.add_url_rule(

Check warning on line 80 in gamutrfwaterfall/gamutrfwaterfall/flask_handler.py

View check run for this annotation

Codecov / codecov/patch

gamutrfwaterfall/gamutrfwaterfall/flask_handler.py#L80

Added line #L80 was not covered by tests
"/predictions", "serve_predictions_page", self.serve_predictions_page
)
self.app.add_url_rule(

Check warning on line 83 in gamutrfwaterfall/gamutrfwaterfall/flask_handler.py

View check run for this annotation

Codecov / codecov/patch

gamutrfwaterfall/gamutrfwaterfall/flask_handler.py#L83

Added line #L83 was not covered by tests
"/predictions_content",
"serve_predictions_content",
self.serve_predictions_content,
)
self.app.add_url_rule("/<path:path>", "", self.serve)
self.process = multiprocessing.Process(

Check warning on line 89 in gamutrfwaterfall/gamutrfwaterfall/flask_handler.py

View check run for this annotation

Codecov / codecov/patch

gamutrfwaterfall/gamutrfwaterfall/flask_handler.py#L88-L89

Added lines #L88 - L89 were not covered by tests
target=self.app.run,
kwargs={"host": "0.0.0.0", "port": port}, # nosec
)
self.zmq_process = multiprocessing.Process(target=self.poll_zmq)
self.write_predictions_content("no predictions yet")
self.read_config_vars()

Check warning on line 95 in gamutrfwaterfall/gamutrfwaterfall/flask_handler.py

View check run for this annotation

Codecov / codecov/patch

gamutrfwaterfall/gamutrfwaterfall/flask_handler.py#L93-L95

Added lines #L93 - L95 were not covered by tests

def start(self):
self.process.start()
self.zmq_process.start()

Check warning on line 99 in gamutrfwaterfall/gamutrfwaterfall/flask_handler.py

View check run for this annotation

Codecov / codecov/patch

gamutrfwaterfall/gamutrfwaterfall/flask_handler.py#L98-L99

Added lines #L98 - L99 were not covered by tests

def write_predictions_content(self, content):
tmpfile = os.path.join(self.tempdir, "." + self.predictions_file)
with open(tmpfile, "w", encoding="utf8") as f:
f.write(f"{content}")
os.rename(tmpfile, os.path.join(self.tempdir, self.predictions_file))

Check warning on line 105 in gamutrfwaterfall/gamutrfwaterfall/flask_handler.py

View check run for this annotation

Codecov / codecov/patch

gamutrfwaterfall/gamutrfwaterfall/flask_handler.py#L102-L105

Added lines #L102 - L105 were not covered by tests

def poll_zmq(self):
zmq_context = zmq.Context()
socket = zmq_context.socket(zmq.SUB)
socket.connect(self.inference_addr)
socket.setsockopt_string(zmq.SUBSCRIBE, "")
DELIM = "\n\n"
json_buffer = ""
item_buffer = []

Check warning on line 114 in gamutrfwaterfall/gamutrfwaterfall/flask_handler.py

View check run for this annotation

Codecov / codecov/patch

gamutrfwaterfall/gamutrfwaterfall/flask_handler.py#L108-L114

Added lines #L108 - L114 were not covered by tests

while True:
try:
while True:
sock_txt = socket.recv(flags=zmq.NOBLOCK).decode("utf8")
json_buffer += sock_txt
except zmq.error.Again:
pass
while True:
delim_pos = json_buffer.find(DELIM)
if delim_pos == -1:
break
raw_item = json_buffer[:delim_pos]
json_buffer = json_buffer[delim_pos + len(DELIM) :]
try:
item = json.loads(raw_item)
except json.decoder.JSONDecodeError:
continue
ts = float(item["metadata"]["ts"])
if "predictions_image_path" not in item["metadata"]:
continue
ts = float(item["metadata"]["ts"])
item_buffer.append((ts, item))
item_buffer = item_buffer[-self.predictions :]
predictions = sorted(item_buffer, key=lambda x: x[0], reverse=True)
images = []
now = time.time()
for ts, item in predictions:
image = item["metadata"]["predictions_image_path"]
age = now - ts
style = ""
if age > 3 * self.refresh:
style = 'style="color:red;"'
images.append(

Check warning on line 148 in gamutrfwaterfall/gamutrfwaterfall/flask_handler.py

View check run for this annotation

Codecov / codecov/patch

gamutrfwaterfall/gamutrfwaterfall/flask_handler.py#L116-L148

Added lines #L116 - L148 were not covered by tests
"<p %s>%s (age %.1fs)</p><p %s><img src=%s></img></p>"
% (style, image, age, style, image)
)
if images:
self.write_predictions_content(

Check warning on line 153 in gamutrfwaterfall/gamutrfwaterfall/flask_handler.py

View check run for this annotation

Codecov / codecov/patch

gamutrfwaterfall/gamutrfwaterfall/flask_handler.py#L152-L153

Added lines #L152 - L153 were not covered by tests
f"<p>{datetime.datetime.now().isoformat()}</p>" + "".join(images)
)
time.sleep(0.1)

Check warning on line 156 in gamutrfwaterfall/gamutrfwaterfall/flask_handler.py

View check run for this annotation

Codecov / codecov/patch

gamutrfwaterfall/gamutrfwaterfall/flask_handler.py#L156

Added line #L156 was not covered by tests

def serve(self, path):
if path:
full_path = os.path.realpath(os.path.join("/", path))
if os.path.exists(full_path):
return send_file(full_path, mimetype="image/png")
return "%s: not found" % full_path, 404
if os.path.exists(self.savefig_path):
return (

Check warning on line 165 in gamutrfwaterfall/gamutrfwaterfall/flask_handler.py

View check run for this annotation

Codecov / codecov/patch

gamutrfwaterfall/gamutrfwaterfall/flask_handler.py#L159-L165

Added lines #L159 - L165 were not covered by tests
'<html><head><meta http-equiv="refresh" content="%u"></head><body><img src="%s"></img></body></html>'
% (self.refresh, self.savefig_file),
200,
)
return (

Check warning on line 170 in gamutrfwaterfall/gamutrfwaterfall/flask_handler.py

View check run for this annotation

Codecov / codecov/patch

gamutrfwaterfall/gamutrfwaterfall/flask_handler.py#L170

Added line #L170 was not covered by tests
'<html><head><meta http-equiv="refresh" content="%u"></head><body>waterfall initializing, please wait or reload...</body></html>'
% self.refresh,
200,
)

def serve_predictions_content(self):
return send_from_directory(self.tempdir, self.predictions_file)

Check warning on line 177 in gamutrfwaterfall/gamutrfwaterfall/flask_handler.py

View check run for this annotation

Codecov / codecov/patch

gamutrfwaterfall/gamutrfwaterfall/flask_handler.py#L177

Added line #L177 was not covered by tests

def serve_predictions_page(self):
# return send_from_directory(self.tempdir, self.predictions_file)
return render_template("predictions.html")

Check warning on line 181 in gamutrfwaterfall/gamutrfwaterfall/flask_handler.py

View check run for this annotation

Codecov / codecov/patch

gamutrfwaterfall/gamutrfwaterfall/flask_handler.py#L181

Added line #L181 was not covered by tests

def read_config_vars(self):
try:
with open(self.config_vars_path, encoding="utf8") as f:
self.config_vars = json.load(f)
except FileNotFoundError:
pass

Check warning on line 188 in gamutrfwaterfall/gamutrfwaterfall/flask_handler.py

View check run for this annotation

Codecov / codecov/patch

gamutrfwaterfall/gamutrfwaterfall/flask_handler.py#L184-L188

Added lines #L184 - L188 were not covered by tests

def serve_waterfall_page(self):
self.read_config_vars()
return render_template("waterfall.html", config_vars=self.config_vars)

Check warning on line 192 in gamutrfwaterfall/gamutrfwaterfall/flask_handler.py

View check run for this annotation

Codecov / codecov/patch

gamutrfwaterfall/gamutrfwaterfall/flask_handler.py#L191-L192

Added lines #L191 - L192 were not covered by tests

# @app.route("/file/<path:filename>")
# def serve_file(self, filename):
# return send_from_directory(self.tempdir, filename)

def serve_waterfall_img(self):
return send_from_directory(self.tempdir, self.savefig_file)

Check warning on line 199 in gamutrfwaterfall/gamutrfwaterfall/flask_handler.py

View check run for this annotation

Codecov / codecov/patch

gamutrfwaterfall/gamutrfwaterfall/flask_handler.py#L199

Added line #L199 was not covered by tests

def config_form(self):
for var in self.config_vars:
self.config_vars[var] = request.form.get(var, self.config_vars[var])
write_scanner_args(self.config_vars_path, self.config_vars)
reset = request.form.get("reset", None)
if reset == "reset":
reconf_query_str = "&".join(

Check warning on line 207 in gamutrfwaterfall/gamutrfwaterfall/flask_handler.py

View check run for this annotation

Codecov / codecov/patch

gamutrfwaterfall/gamutrfwaterfall/flask_handler.py#L202-L207

Added lines #L202 - L207 were not covered by tests
[f"{k}={v}" for k, v in self.config_vars.items()]
)
logging.info(f"\n\n{reconf_query_str=}\n\n")
try:
response = requests.get(

Check warning on line 212 in gamutrfwaterfall/gamutrfwaterfall/flask_handler.py

View check run for this annotation

Codecov / codecov/patch

gamutrfwaterfall/gamutrfwaterfall/flask_handler.py#L210-L212

Added lines #L210 - L212 were not covered by tests
f"http://{self.api_endpoint}/reconf?{reconf_query_str}",
timeout=30,
)
logging.info(f"\n\n{response=}\n\n")
logging.info(f"\n\n{response.text=}\n\n")
except (

Check warning on line 218 in gamutrfwaterfall/gamutrfwaterfall/flask_handler.py

View check run for this annotation

Codecov / codecov/patch

gamutrfwaterfall/gamutrfwaterfall/flask_handler.py#L216-L218

Added lines #L216 - L218 were not covered by tests
requests.exceptions.ConnectionError,
requests.exceptions.HTTPError,
requests.exceptions.ConnectTimeout,
) as err:
logging.error(str(err))
return redirect("/", code=302)

Check warning on line 224 in gamutrfwaterfall/gamutrfwaterfall/flask_handler.py

View check run for this annotation

Codecov / codecov/patch

gamutrfwaterfall/gamutrfwaterfall/flask_handler.py#L223-L224

Added lines #L223 - L224 were not covered by tests
Loading

0 comments on commit 689becc

Please sign in to comment.