diff --git a/app.py b/app.py index 1cc655f..65645da 100644 --- a/app.py +++ b/app.py @@ -8,6 +8,7 @@ import os from flask import (Flask, request, jsonify, abort, render_template) from ltsdb_json import LTS +from dashboard import Dashboard import config @@ -125,3 +126,13 @@ def visualize(): ts = LTS(id=id) timeseries_data.append(ts) return render_template("visualize.html", ts=timeseries_data) + +@app.get("/dashboard/") +def dashboard_index(): + d = Dashboard("dashboards/" + "index" + ".json") + return d.as_html() + +@app.get("/dashboard/") +def dashboard_file(dashboard): + d = Dashboard("dashboards/" + dashboard + ".json") + return d.as_html() diff --git a/dashboard.py b/dashboard.py new file mode 100644 index 0000000..968c684 --- /dev/null +++ b/dashboard.py @@ -0,0 +1,133 @@ +import json +import logging + +from flask import (Flask, request, jsonify, abort, render_template_string, render_template) +from markupsafe import Markup + +from ltsdb_json import LTS + +log = logging.getLogger(__name__) + +class Dashboard: + def __init__(self, filename="dashboard.json"): + with open (filename) as fh: + d = json.load(fh) + self.title = d["title"] + log.debug("title = ā€œ%sā€", self.title) + self.description = d["description"] + self.widgets = [] + for w in d["widgets"]: + if w["type"] == "timeseries": + self.widgets.append(TimeSeries(w)) + elif w["type"] == "gauge": + if w.get("multi"): + ts_list = LTS.find(w["data"][0]) + for ts in ts_list: + w1 = {**w, "data": [ts]} + self.widgets.append(Gauge(w1)) + else: + self.widgets.append(Gauge(w)) + else: + self.widgets.append(Widget(w)) + + def as_html(self): + return render_template("dashboard.html", dashboard=self) + + + +class Widget: + def __init__(self, d): + self.type = d["type"] + self.stops = d["stops"] + log.debug("data = %s", d["data"]) + self.lts = LTS(id=d["data"][0]) # by default we handle only one data source + pass + + def as_html(self): + log.debug("") + self.lastvalue = self.lts.data[-1][1] + return Markup(render_template("widget.html", widget=self)) + + @property + def criticalcolor(self): + log.debug("stops = %s", self.stops) + brightness = 30 + if self.stops[0] < self.stops[2]: + if self.lastvalue < self.stops[0]: + log.debug("definitely ok") + return f"hsl(120, 100%, {brightness}%)" + elif self.lastvalue < self.stops[1]: + log.debug("mostly ok") + hue = 120 - round( + (self.lastvalue - self.stops[0]) + / (self.stops[1] - self.stops[0]) + * 60 + ) + return f"hsl({hue}, 100%, {brightness}%)" + elif self.lastvalue < self.stops[2]: + log.debug("maybe fail") + hue = 60 - round( + (self.lastvalue - self.stops[1]) + / (self.stops[2] - self.stops[1]) + * 60 + ) + return f"hsl({hue}, 100%, {brightness}%)" + else: + log.debug("definitely fail") + return f"hsl(0, 100%, {brightness}%)" + else: + log.debug("the other side") + return "#CCC" + +class TimeSeries(Widget): + def __init__(self, d): + super().__init__(d) + pass + +class Gauge(Widget): + def __init__(self, d): + super().__init__(d) + pass + + gaugesize = 100 + + @property + def gaugepos(self): + max_value = max([d[1] for d in self.lts.data]) + log.debug("max_value = %s", max_value) + return self.lastvalue / max_value * self.gaugesize + + def as_html(self): + log.debug("") + self.lastvalue = self.lts.data[-1][1] + value = self.lastvalue + unit = self.lts.description["unit"] + log.debug("unit = %s", unit) + if unit == "s": + if value >= 86400: + value /= 86400 + unit = "days" + elif value >= 3600: + value /= 3600 + unit = "h" + elif value >= 60: + value /= 60 + unit = "m" + elif value >= 1: + pass + elif value >= 0.001: + value *= 1000 + unit = "ms" + self.lastvalue_formatted = Markup(f"{value:.2f}{unit}") + return Markup(render_template("gauge.html", widget=self)) + + @property + def description_formatted(self): + s = "" + for d, v in self.lts.description.items(): + if v: + s += render_template_string( + "", + d=d, v=v) + s += "
{{d}}:{{v}}
" + return Markup(s) diff --git a/templates/dashboard.html b/templates/dashboard.html new file mode 100644 index 0000000..7d7782c --- /dev/null +++ b/templates/dashboard.html @@ -0,0 +1,63 @@ + + + + + + + + + + +

{{ dashboard.title }}

+

+ {{ dashboard.description }} +

+
+ {% for widget in dashboard.widgets %} + {{ widget.as_html() }} + {% endfor %} +
+ + + diff --git a/templates/gauge.html b/templates/gauge.html new file mode 100644 index 0000000..9deb2d7 --- /dev/null +++ b/templates/gauge.html @@ -0,0 +1,15 @@ +
+
+
+
+
+
+ + {{ widget.lastvalue_formatted}} + +
+ {{ widget.description_formatted }} +
diff --git a/templates/widget.html b/templates/widget.html new file mode 100644 index 0000000..d3736dc --- /dev/null +++ b/templates/widget.html @@ -0,0 +1,8 @@ +
+

Unknown widget type {{ widget.type }}.

+

+ Last value: + {{ widget.lastvalue}} +

+ +