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") 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}%)" 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]) max_value = max(max_value, 1) # ensure positive 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 >= 365.25 * 86400: value /= 365.25 * 86400 unit = "years" elif 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)