generate website
This commit is contained in:
296
main.py
296
main.py
@@ -1,271 +1,47 @@
|
||||
from dataclasses import dataclass, field
|
||||
from src.api import Timetable, Stop
|
||||
|
||||
from flask import Flask
|
||||
import fire
|
||||
|
||||
import cv2
|
||||
from PIL import ImageFont, ImageDraw, Image
|
||||
import numpy as np
|
||||
import time
|
||||
from htpy import body, h1, head, html, title, table, tr, td, div, script
|
||||
|
||||
badge_colors = {
|
||||
"S1": {"fill": "#18bae7", "text": "#C6D3D7"},
|
||||
"S2": {"fill": "#74b72b", "text": "#C6D3D7"},
|
||||
"S3": {"fill": "#941d81", "text": "#C6D3D7"},
|
||||
"S4": {"fill": "#e20b1c", "text": "#C6D3D7"},
|
||||
"S5": {"fill": "#005280", "text": "#C6D3D7"},
|
||||
"S6": {"fill": "#008c59", "text": "#C6D3D7"},
|
||||
"S7": {"fill": "#892f24", "text": "#C6D3D7"},
|
||||
"S8": {"fill": "#0d0d11", "text": "#eeaa00"},
|
||||
"S20": {"fill": "#e8526d", "text": "#C6D3D7"},
|
||||
}
|
||||
app = Flask(__name__)
|
||||
|
||||
@dataclass
|
||||
class ImageSetup:
|
||||
width: int = 1920
|
||||
height: int = 1080
|
||||
bg_color: str = "#282a35"
|
||||
fonts: dict = field(
|
||||
default_factory=lambda: {
|
||||
"title": ("./fonts/Rubik-Bold.ttf", 80),
|
||||
"badge": ("./fonts/Rubik-SemiBold.ttf", 70),
|
||||
"column_title": ("./fonts/Rubik-Regular.ttf", 50),
|
||||
"default": ("./fonts/Rubik-Regular.ttf", 60),
|
||||
}
|
||||
def run_server(debug=False):
|
||||
app.run(debug=debug)
|
||||
|
||||
@app.route("/")
|
||||
def main_page():
|
||||
return build_page(None)
|
||||
|
||||
def build_page(tt, debug=True):
|
||||
_ = tt
|
||||
from datetime import datetime
|
||||
|
||||
lines = (
|
||||
("S1", "somewhere", datetime.now().strftime(r"%H:%M:%S"), 4, "3 min"),
|
||||
("S2", "else", datetime.now().strftime(r"%H:%M:%S"), 4, "3 min"),
|
||||
)
|
||||
|
||||
text_colors: dict = field(
|
||||
default_factory=lambda: {
|
||||
"title": "#899194",
|
||||
"column_title": "#555A5C",
|
||||
"default": "#899194"
|
||||
}
|
||||
)
|
||||
margins: tuple = (60, 60, 60, 60) # t, b, l, r
|
||||
retval = html[
|
||||
head[title["today's menu"]],
|
||||
body[
|
||||
div(style="clear: both")[
|
||||
h1(style="float: left")["Gröbenzell"],
|
||||
h1(style="float: right", id="clock")[datetime.now().strftime(r"%H:%M:%S")],
|
||||
],
|
||||
table[
|
||||
(tr[
|
||||
(td[element] for element in line)
|
||||
] for line in lines)
|
||||
],
|
||||
]
|
||||
]
|
||||
|
||||
retval = str(retval)
|
||||
|
||||
def denormalize(coords, setup: ImageSetup, use_margins=True):
|
||||
(x, y) = coords
|
||||
margins = setup.margins if use_margins else (0, 0, 0, 0)
|
||||
return (
|
||||
int(x * (setup.width - margins[2] - margins[3]) + margins[2]),
|
||||
int(y * (setup.height - margins[0] - margins[1]) + margins[0]),
|
||||
)
|
||||
|
||||
def create_image(tt: Timetable, setup: ImageSetup | None = None):
|
||||
if setup is None:
|
||||
setup = ImageSetup()
|
||||
|
||||
# img = np.zeros((setup.height, setup.width, 3), dtype=np.uint8)
|
||||
img = Image.new("RGBA", (setup.width, setup.height), setup.bg_color)
|
||||
|
||||
# # fill with background color
|
||||
# for i, c in enumerate(hex2color(setup.bg_color)):
|
||||
# img[:, :, i] = int(c * 255)
|
||||
|
||||
### setup done ###
|
||||
|
||||
draw = ImageDraw.Draw(img)
|
||||
|
||||
title_font = ImageFont.truetype(*setup.fonts["title"])
|
||||
ctitle_font = ImageFont.truetype(*setup.fonts["column_title"])
|
||||
badge_font = ImageFont.truetype(*setup.fonts["badge"])
|
||||
default_font = ImageFont.truetype(*setup.fonts["default"])
|
||||
|
||||
draw.text(
|
||||
xy=denormalize((0, 0), setup),
|
||||
text=tt.station,
|
||||
font=title_font,
|
||||
anchor="la",
|
||||
fill=setup.text_colors["title"]
|
||||
)
|
||||
|
||||
draw.text(
|
||||
xy=denormalize((0.815, 0), setup),
|
||||
text=tt.timestamp.strftime(r"%H:%M:%S"),
|
||||
font=title_font,
|
||||
anchor="la",
|
||||
fill=setup.text_colors["title"]
|
||||
)
|
||||
|
||||
|
||||
|
||||
line_badge_size = (2*setup.fonts["badge"][1], setup.fonts["badge"][1])
|
||||
line_height = setup.fonts["badge"][1]*1.75
|
||||
|
||||
xy_start = denormalize((0,0.15), setup)
|
||||
dest_x = denormalize((0.12, 0), setup)[0]
|
||||
dep_x = denormalize((0.5, 0), setup)[0]
|
||||
delay_x = denormalize((0.6, 0), setup)[0]
|
||||
max_dest_len = dep_x - dest_x - 60
|
||||
until_x = denormalize((0.5+0.5-0.12, 0), setup)[0]
|
||||
# col_title_y = denormalize((0, 0.15), setup)[1]
|
||||
|
||||
# draw.text(
|
||||
# xy=(dest_x, col_title_y),
|
||||
# text="Richtung",
|
||||
# font=ctitle_font,
|
||||
# anchor="lm",
|
||||
# fill=setup.text_colors["column_title"],
|
||||
# )
|
||||
|
||||
# draw.text(
|
||||
# xy=(dep_x, col_title_y),
|
||||
# text="Zeit",
|
||||
# font=ctitle_font,
|
||||
# anchor="lm",
|
||||
# fill=setup.text_colors["column_title"],
|
||||
# )
|
||||
|
||||
# draw.text(
|
||||
# xy=(until_x, col_title_y),
|
||||
# text="fährt in",
|
||||
# font=ctitle_font,
|
||||
# anchor="lm",
|
||||
# fill=setup.text_colors["column_title"],
|
||||
# )
|
||||
|
||||
current_xys_line_badge = [*xy_start, xy_start[0]+line_badge_size[0], xy_start[1]+line_badge_size[1]]
|
||||
for stop in tt.stops[:min(tt.min_stop_count, len(tt.stops), 7)]:
|
||||
stop: Stop = stop
|
||||
draw.rounded_rectangle(
|
||||
current_xys_line_badge,
|
||||
radius=min(current_xys_line_badge[2]-current_xys_line_badge[0], current_xys_line_badge[3]-current_xys_line_badge[1])//2,
|
||||
fill=badge_colors[stop.line]["fill"],
|
||||
outline=badge_colors[stop.line]["fill"],
|
||||
width=10
|
||||
)
|
||||
|
||||
center_xy = ((current_xys_line_badge[0]+current_xys_line_badge[2])//2, (current_xys_line_badge[1]+current_xys_line_badge[3])//2)
|
||||
|
||||
draw.text(
|
||||
xy=center_xy,
|
||||
text=stop.line,
|
||||
font=badge_font,
|
||||
anchor="mm",
|
||||
fill=badge_colors[stop.line]["text"],
|
||||
)
|
||||
|
||||
dest_text :str = stop.destination
|
||||
dest_text = dest_text.replace("München-", "")
|
||||
dest_text = dest_text.replace("München ", "")
|
||||
paren_index = dest_text.find("(")
|
||||
if paren_index != -1:
|
||||
dest_text = dest_text[:paren_index]
|
||||
|
||||
cutoff = 0
|
||||
while default_font.getlength(dest_text) > max_dest_len:
|
||||
dest_text = (dest_text[:len(dest_text)-cutoff+1]+ ("." if cutoff > 0 else "")).replace(" .", "")
|
||||
cutoff += 1
|
||||
|
||||
dep_text = stop.departure_time.strftime(r"%H:%M")
|
||||
|
||||
time_until = round((stop.departure_time - tt.timestamp).total_seconds()/60)
|
||||
|
||||
until_text = f"{time_until:d} min"
|
||||
|
||||
until_color = setup.text_colors["default"]
|
||||
delay_color = setup.text_colors["column_title"]
|
||||
dep_color = setup.text_colors["default"]
|
||||
dest_color = setup.text_colors["default"]
|
||||
|
||||
delay_text = ""
|
||||
|
||||
if stop.departure_delay is not None:
|
||||
delay_text = f"{"+" if stop.departure_delay >= 0 else "-"} {abs(stop.departure_delay):d}"
|
||||
if stop.departure_delay > 5:
|
||||
delay_color = "#c8683f"
|
||||
elif stop.departure_delay < 0:
|
||||
delay_color = "#48c144"
|
||||
|
||||
if time_until <= 6:
|
||||
until_color = "#c8683f"
|
||||
elif 6 < time_until <= 10:
|
||||
until_color = "#b79b46"
|
||||
|
||||
if stop.departure_canceled:
|
||||
dep_color = "#c83f4e"
|
||||
dest_color = "#c83f4e"
|
||||
dep_text = "Fällt aus :("
|
||||
until_text = "----"
|
||||
delay_text = ""
|
||||
|
||||
draw.text(
|
||||
xy=(dest_x, center_xy[1]),
|
||||
text=dest_text,
|
||||
font=default_font,
|
||||
anchor="lm",
|
||||
fill=dest_color,
|
||||
)
|
||||
|
||||
draw.text(
|
||||
xy=(dep_x, center_xy[1]),
|
||||
text=dep_text,
|
||||
font=default_font,
|
||||
anchor="lm",
|
||||
fill=dep_color,
|
||||
)
|
||||
|
||||
draw.text(
|
||||
xy=(delay_x, center_xy[1]),
|
||||
text=delay_text,
|
||||
font=ctitle_font,
|
||||
anchor="lm",
|
||||
fill=delay_color,
|
||||
)
|
||||
|
||||
draw.text(
|
||||
xy=(until_x, center_xy[1]),
|
||||
text=until_text,
|
||||
font=default_font,
|
||||
anchor="lm",
|
||||
fill=until_color,
|
||||
)
|
||||
|
||||
current_xys_line_badge[1] += line_height
|
||||
current_xys_line_badge[3] += line_height
|
||||
|
||||
|
||||
# if gif is not None:
|
||||
# gif = Image.open(gif)
|
||||
# gif.seek(gif_frame % gif.n_frames)
|
||||
# frame = gif.convert("RGBA")
|
||||
# frame_size = frame.size
|
||||
# frame_anchor = denormalize((1, 1), setup, use_margins=False)
|
||||
# img.paste(frame, (frame_anchor[0]-frame_size[0], frame_anchor[1]-frame_size[1]), frame)
|
||||
|
||||
# last step, convert from rgb to bgr
|
||||
img = img.convert("RGB")
|
||||
img = np.array(img)
|
||||
img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
|
||||
return img
|
||||
|
||||
|
||||
def main(eva=None):
|
||||
eva = 8002377 if eva is None else eva
|
||||
# eva = 8098263
|
||||
# eva = 8004158
|
||||
|
||||
cv2.namedWindow("fs", cv2.WND_PROP_FULLSCREEN)
|
||||
cv2.setWindowProperty("fs", cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)
|
||||
# cv2.moveWindow("fs", -1920, 0)
|
||||
|
||||
tt = Timetable(eva=eva)
|
||||
|
||||
while True:
|
||||
start = time.time()
|
||||
tt.get_stops()
|
||||
img = create_image(tt)
|
||||
cv2.imshow("fs", img)
|
||||
|
||||
if cv2.waitKey(1) & 0xFF == ord("q"):
|
||||
break
|
||||
|
||||
elapsed = time.time() - start
|
||||
time.sleep(max(0, 1.0 - elapsed))
|
||||
|
||||
cv2.destroyAllWindows()
|
||||
if debug:
|
||||
print(retval)
|
||||
|
||||
return retval
|
||||
|
||||
if __name__ == "__main__":
|
||||
fire.Fire(main)
|
||||
fire.Fire(run_server)
|
||||
Reference in New Issue
Block a user