import json
import logging
import time
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:
tso = LTS(id=ts)
if not tso.data:
log.warning("%s has no data: Skipping", tso.id)
continue
if tso.data[-1][0] < time.time() - 86400:
log.info("%s too old; Skipping", tso.id)
continue
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}}: | {{v}} |
",
d=d, v=v)
s += "
"
return Markup(s)