177 lines
6.5 KiB
Python
177 lines
6.5 KiB
Python
import os
|
|
from tempfile import NamedTemporaryFile
|
|
|
|
from PIL import Image, ImageDraw, ImageFont
|
|
|
|
class Config:
|
|
|
|
def __init__(self):
|
|
if os.path.exists('/usr/share/fonts/source-pro/'):
|
|
self.fnt_R_path = "/usr/share/fonts/source-pro/SourceSansPro-Regular.otf"
|
|
self.fnt_RI_path = "/usr/share/fonts/source-pro/SourceSansPro-It.otf"
|
|
self.fnt_RB_path = "/usr/share/fonts/source-pro/SourceSansPro-Bold.otf"
|
|
elif os.path.exists('/usr/share/fonts/libertine'):
|
|
self.fnt_R_path = "/usr/share/fonts/libertine/LinBiolinum_Rah.ttf"
|
|
self.fnt_RI_path = "/usr/share/fonts/libertine/LinBiolinum_RIah.ttf"
|
|
self.fnt_RB_path = "/usr/share/fonts/libertine/LinBiolinum_RBah.ttf"
|
|
else:
|
|
self.fnt_R_path = "/usr/share/fonts/TTF/LinBiolinum_Rah.ttf"
|
|
self.fnt_RI_path = "/usr/share/fonts/TTF/LinBiolinum_RIah.ttf"
|
|
self.fnt_RB_path = "/usr/share/fonts/TTF/LinBiolinum_RBah.ttf"
|
|
|
|
self.css_overlay = NamedTemporaryFile()
|
|
with open(self.css_overlay.name, 'w') as f:
|
|
f.write('''
|
|
{{ id }} .bound {
|
|
font-size: 0.9em;
|
|
}
|
|
{{ id }} .gauge path {
|
|
fill-opacity: 1 !important;
|
|
}
|
|
{{ id }} .series .line {
|
|
stroke-width: 2 !important;
|
|
}
|
|
{{ id }} .x .guides .line {
|
|
stroke-width: 0 !important;
|
|
}
|
|
{{ id }} .y .guides .line {
|
|
stroke: #555;
|
|
stroke-width: 0.7;
|
|
}
|
|
{{ id }} .y .guides .line.axis {
|
|
stroke: #000;
|
|
stroke-width: 1;
|
|
}
|
|
''')
|
|
|
|
import pygal
|
|
self.pygal_config = pygal.Config()
|
|
self.pygal_config.css.append('file://' + self.css_overlay.name)
|
|
|
|
from pygal.style import Style
|
|
self.pygal_custom_style = Style(
|
|
background='white',
|
|
foreground='black',
|
|
font_family='Source Code Pro',
|
|
gauge_background="red",
|
|
colors=('#000', '#777'))
|
|
|
|
self.charts_opts = {'style': self.pygal_custom_style, 'show_legend': False, 'margin_right': 7, 'margin_left': 2, 'margin_bottom': 4, 'margin_top': 1}
|
|
|
|
|
|
class RuleModule:
|
|
|
|
def __init__(self):
|
|
self.coeff = 0.92
|
|
|
|
def draw_module(self, config, width, height):
|
|
image = Image.new('RGB', (width, height), 'white')
|
|
draw = ImageDraw.Draw(image)
|
|
|
|
draw.rectangle((int((1-self.coeff) * width), 0, int(self.coeff * width), height), fill="black")
|
|
|
|
return image
|
|
|
|
|
|
class AlertsModule:
|
|
|
|
def __init__(self, alerts=[]):
|
|
self.icon_size = 50
|
|
self.alerts = alerts
|
|
|
|
def draw_module(self, config, width, height, font_size=16):
|
|
image = Image.new('RGBA', (width, height), "#000")
|
|
draw = ImageDraw.Draw(image)
|
|
align = 0
|
|
|
|
more_alerts = 0
|
|
if len(self.alerts) > 0:
|
|
fnt_R = ImageFont.truetype(config.fnt_R_path, font_size)
|
|
fnt_B = ImageFont.truetype(config.fnt_RB_path, font_size)
|
|
|
|
align = 9
|
|
for alert in self.alerts:
|
|
if alert["icon"] is not None:
|
|
if callable(alert["icon"]):
|
|
icon_img = alert["icon"](size=int(font_size*self.icon_size/16))
|
|
image.paste(icon_img, (int(self.icon_size / 2 - font_size*self.icon_size/32), align), icon_img)
|
|
else:
|
|
color_img = Image.new('RGB', (self.icon_size, self.icon_size), "#fff")
|
|
icon_img = Image.open("icons/" + alert["icon"]).resize((self.icon_size, self.icon_size))
|
|
image.paste(color_img, (0, align), icon_img)
|
|
|
|
if "title" in alert:
|
|
draw.text(
|
|
((self.icon_size if alert["icon"] is not None else 0) - 5, align),
|
|
alert["title"],
|
|
fill="white", anchor="lt", font=fnt_B
|
|
)
|
|
if "subtitle" in alert and alert["subtitle"]:
|
|
draw.text(
|
|
((self.icon_size if alert["icon"] is not None else 0) + (fnt_B.getsize(alert["title"])[0] if "title" in alert else 0), align + 3),
|
|
alert["subtitle"],
|
|
fill="white", anchor="lt", font=fnt_R
|
|
)
|
|
|
|
if "title" in alert:
|
|
align += fnt_B.getsize(alert["title"])[1]
|
|
|
|
align += display_longtext(
|
|
draw,
|
|
(self.icon_size - 5, align),
|
|
alert["description"],
|
|
fill="white", font=fnt_R,
|
|
maxwidth=width-self.icon_size-5
|
|
)
|
|
align += 7
|
|
if align > height:
|
|
more_alerts += 1
|
|
|
|
image = image.crop((0,0,width, min(align, height)))
|
|
|
|
if more_alerts > 0 and font_size > 12:
|
|
return self.draw_module(config, width, height, font_size-1)
|
|
elif more_alerts > 0:
|
|
draw = ImageDraw.Draw(image)
|
|
draw.rectangle((0, height-font_size, width, height), fill="black")
|
|
draw.text(
|
|
(width - 3, height),
|
|
("%d alertes supplémentaires" if more_alerts > 1 else "%d alerte supplémentaire") % more_alerts,
|
|
fill="white", anchor="rb", font=fnt_B
|
|
)
|
|
|
|
return image
|
|
|
|
def display_longtext(draw, pos, text, font, anchor="lt", maxwidth=9999, **kwargs):
|
|
x,y = pos
|
|
lines = []
|
|
|
|
mainlines = text.split("\n")
|
|
for line in mainlines:
|
|
words = line.split(" ")
|
|
|
|
cur = ""
|
|
while len(words) > 0:
|
|
added = (" " if len(cur) > 0 else "") + words[0]
|
|
width = font.getsize(cur + added)[0]
|
|
if width < maxwidth:
|
|
words.pop(0)
|
|
cur += added
|
|
else:
|
|
lines.append(cur)
|
|
cur = words.pop(0)
|
|
|
|
if len(cur) > 0:
|
|
lines.append(cur)
|
|
|
|
line_height = font.getsize("test")[1]
|
|
if anchor[1] == "m":
|
|
y -= line_height * len(lines) / 2
|
|
elif anchor[1] == "b":
|
|
y -= line_height * len(lines)
|
|
for line in lines:
|
|
draw.text((x,y), line, font=font, anchor=anchor, **kwargs)
|
|
y += line_height
|
|
|
|
return line_height * len(lines)
|