From 21a11211c39e42dc75738dc9a26c9b8f7c7eeab4 Mon Sep 17 00:00:00 2001 From: Seppl Date: Mon, 25 Aug 2025 23:57:12 +0200 Subject: [PATCH] edit tt display, tweak css --- app.py | 13 ++-- src/iris_api.py | 1 + static/styles.css | 62 ++++++++------- static/update.js | 177 ++++++++++++++++++++++++++++++++++++++++++ static/update_time.js | 171 ---------------------------------------- templates/base.html | 47 ++++++++++- 6 files changed, 263 insertions(+), 208 deletions(-) create mode 100644 static/update.js delete mode 100644 static/update_time.js diff --git a/app.py b/app.py index 6c3b581..c008678 100644 --- a/app.py +++ b/app.py @@ -1,4 +1,4 @@ -from flask import Flask, render_template, url_for, jsonify, send_file +from flask import Flask, render_template, url_for, jsonify, send_file, request from datetime import datetime from pathlib import Path @@ -18,6 +18,8 @@ def compile_javascript(base_path = "static"): return js_files eva = 8002377 +# eva = 8004158 + app = Flask(__name__) @@ -28,19 +30,16 @@ def favicon(): @app.route("/") def main_page(): # print(compile_javascript()) + tt = Timetable(eva) + tt.get_changes() return render_template( 'base.html', - title="testin", - station="Gröbenzell", + station=tt.station, current_time_str=datetime.now().strftime(r"%H:%M:%S"), js_files=compile_javascript() ) -# TODO: add a text entry field to change the EVA -# @app.route("/setup") -# def setup(): -# return "" @app.route("/update", methods=['POST']) def update(): diff --git a/src/iris_api.py b/src/iris_api.py index e58ce42..161f291 100644 --- a/src/iris_api.py +++ b/src/iris_api.py @@ -118,6 +118,7 @@ class Timetable: stop_dict["until_departure"] = stop_dict["departure_time"] - self.timestamp.timestamp() stop_dict["until_arrival"] = stop_dict["arrival_time"] - self.timestamp.timestamp() stop_dict["timestamp"] = self.timestamp.timestamp() + stop_dict["station"] = self.station stops.append(stop_dict) return stops diff --git a/static/styles.css b/static/styles.css index 66399e4..a9b1c7f 100644 --- a/static/styles.css +++ b/static/styles.css @@ -22,7 +22,7 @@ html, body { font-weight: 500; font-style: normal; background: #282a35; - color: #899194; + color: #b5b5b3; } .header-table-container { @@ -43,7 +43,7 @@ html, body { } .header-station { - width: 79%; + width: 86.5%; } /* .header-clock { @@ -51,31 +51,35 @@ html, body { } */ .line { - width: 6%; - /* text-align: left; */ - /* padding: 0 0 0 0; */ - vertical-align: 25%; + width: 8%; + vertical-align: inherit; } -.destination{ +/* .destination{ padding: 100em; -} +} */ .departure_time { width: 12%; - text-align: center; + text-align: left; + vertical-align: inherit; + } .departure_delay { width: 10%; text-align: left; font-size: 50%; - color: #646a6c + color: #646a6c; + vertical-align: inherit; + } .until_departure { width: 16%; text-align: right; + vertical-align: inherit; + } .departure-table-container { @@ -93,6 +97,7 @@ html, body { border-collapse: collapse; table-layout: fixed; font-size: 200%; + vertical-align: center; } @@ -116,55 +121,55 @@ html, body { display: inline-flex; align-items: center; justify-content: center; - padding: 0.2em 0.5em; /* relative padding */ + padding: 0.2em 0.5em; /* relative padding */ font-weight: bold; - font-size: 0.75em; + font-size: 100%; border-radius: 9999px; /* pill shape adjusts automatically */ - height: 100%; /* takes most of cell height */ - max-height: 100%; /* don’t overflow cell */ - width: 100%; - max-width: 100%; /* stay within cell */ + /* height: 50%; */ + max-height: 50%; + /* width: 100%; */ + max-width: 100%; aspect-ratio: 2 / 1; box-sizing: border-box; white-space: nowrap; /* prevent text wrapping */ - overflow: hidden; /* hide overflow if text is too long */ + overflow: hidden; text-overflow: ellipsis; /* show "..." if necessary */ } /* Colors for different lines */ .line-S1 { background-color: #14bae7; - color: #b5b5b5; + /* color: #b5b5b5; */ } .line-S2 { background-color: #75b728; - color: #b5b5b5; + /* color: #b5b5b5; */ } .line-S3 { background-color: #951781; - color: #b5b5b5; + /* color: #b5b5b5; */ } .line-S4 { background-color: #e30b1b; - color: #b5b5b5; + /* color: #b5b5b5; */ } .line-S5 { background-color: #00517f; - color: #b5b5b5; + /* color: #b5b5b5; */ } .line-S6 { background-color: #008c58; - color: #b5b5b5; + /* color: #b5b5b5; */ } .line-S7 { background-color: #882d22; - color: #b5b5b5; + /* color: #b5b5b5; */ } .line-S8 { @@ -174,7 +179,7 @@ html, body { .line-S20 { background-color: #ea516c; - color: #b5b5b5; + /* color: #b5b5b5; */ } .line-default { @@ -190,10 +195,11 @@ html, body { color: #5bc812 } +.until-medium { + color: #dc9e21 +} + .until-low { color: #dc4d21 } -.until-medium { - color: #dc9e21 -} diff --git a/static/update.js b/static/update.js new file mode 100644 index 0000000..01e86d4 --- /dev/null +++ b/static/update.js @@ -0,0 +1,177 @@ +(function () { + function updateClock(clock) { + clock.innerHTML = new Date().toLocaleTimeString("de-DE", { + hour: "2-digit", + minute: "2-digit", + }); + } + + function ensureTableSize(table, x, y) { + // Adjust number of rows + while (table.rows.length < x) { + table.insertRow(); + } + while (table.rows.length > x) { + table.deleteRow(table.rows.length - 1); + } + + // Ensure each row has y cells + for (let row of table.rows) { + while (row.cells.length < y) { + row.insertCell(); + } + while (row.cells.length > y) { + row.deleteCell(row.cells.length - 1); + } + } + } + + function formatWithSign(num) { + if (num > 0) { + return `+ ${num}`; + } else if (num < 0) { + return `- ${Math.abs(num)}`; + } else { + return ""; + } + } + + function cssClassExists(className) { + return Array.from(document.styleSheets).some((sheet) => { + try { + return Array.from(sheet.cssRules).some( + (rule) => rule.selectorText === `.${className}` + ); + } catch (e) { + // Ignore CORS-restricted stylesheets + return false; + } + }); + } + + function fill_table(data, table) { + // const res = JSON.parse(result) + const columns = { + line: 0, + destination: 1, + departure_time: 2, + departure_delay: 3, + until_departure: 4, + }; + + // console.log(data) + + // ensureTableSize(table, Math.min(data.length, 8), 5); + + for (let i = 0; i < Math.min(data.length, table.rows.length); i++) { + const row = table.rows[i]; + console.log(row); + + // row.classList.add("departure-table-row") + // row.style.height = `$10%`; + const train = Object.entries(data[i]); + for (let [key, value] of train) { + if (key in columns) { + const cell = row.cells[columns[key]]; + let newContent = ""; + let newClassList = []; + + if (key in columns) { + newClassList.push(key); + } + + if (key == "line") { + const lineClass = `line-${value}`; + const appliedClass = cssClassExists(lineClass) + ? lineClass + : "line-default"; + newContent = `${value}`; + } + if (key == "destination") { + let dest = String(value) + .replace(/\s*\(.*?\)\s*/g, "") + .replace(/^München\s*/i, "") + .replace(/^[\s-]+|[\s-]+$/g, "") + .replace("Ost", "Ostbahnhof") + .replace("Hbf", "Hauptbahnhof") + .replace("Gl.", "Gl. "); + newContent = dest; + } + if (key == "departure_time") { + if (train["canceled"]) { + newContent = "Fällt aus :("; + newClassList.push("train-canceled"); + } else { + const date = new Date(value * 1000); + newContent = date.toLocaleTimeString("de-DE", { + hour: "2-digit", + minute: "2-digit", + }); + } + } + if (key == "departure_delay") { + if (!train["canceled"]) { + newContent = formatWithSign(value); + if (value >= 5) newClassList.push("delay-high"); + else if (value < 0) newClassList.push("delay-neg"); + } + } + if (key == "until_departure") { + if (!train["canceled"]) { + const minutes = Math.round((value + 0) / 60); + newContent = String(minutes) + " min"; + if (minutes <= 6) newClassList.push("until-low"); + else if (minutes <= 10) newClassList.push("until-medium"); + } + } + + // Only update if content changed + if ( + (cell.innerHTML !== newContent && key === "line") || + (cell.textContent !== newContent && key !== "line") + ) { + if (key === "line") { + cell.innerHTML = newContent; + } else { + cell.textContent = newContent; + } + } + + // Update classes only if changed + cell.className = ""; // Clear all classes + for (const cls of newClassList) cell.classList.add(cls); + } + } + } + // document.querySelector('.departure-table') + // .style.setProperty('--row-count', table.rows.length); + } + + function updateTable(table) { + fetch("/update", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + // TODO: pass number of rows we want + }) + .then((response) => response.json()) + .then((result) => { + // console.log(result); + fill_table(result.table_data, table); + // const res = JSON.parse(result); + // console.log(res.table_data); + // for (const elem of res.table_data) { + // console.log(elem); + // } + }); + } + + var clockElement = document.getElementById("clock"); + var tableElement = document.getElementById("departures"); + + setInterval(function () { + updateClock(clockElement); + updateTable(tableElement); + }, 1000); +})(); diff --git a/static/update_time.js b/static/update_time.js deleted file mode 100644 index 3e86aaf..0000000 --- a/static/update_time.js +++ /dev/null @@ -1,171 +0,0 @@ -(function () { - - - function updateClock ( clock ) { - clock.innerHTML = new Date().toLocaleTimeString(); - } - - function ensureTableSize(table, x, y) { - // Adjust number of rows - while (table.rows.length < x) { - table.insertRow(); - } - while (table.rows.length > x) { - table.deleteRow(table.rows.length - 1); - } - - // Ensure each row has y cells - for (let row of table.rows) { - while (row.cells.length < y) { - row.insertCell(); - } - while (row.cells.length > y) { - row.deleteCell(row.cells.length - 1); - } - } - } - - function formatWithSign(num) { - if (num > 0) { - return `+ ${num}`; - } else if (num < 0) { - return `- ${Math.abs(num)}`; - } else { - return ""; - } - } - - function cssClassExists(className) { - return Array.from(document.styleSheets).some(sheet => { - try { - return Array.from(sheet.cssRules).some(rule => - rule.selectorText === `.${className}` - ); - } catch (e) { - // Ignore CORS-restricted stylesheets - return false; - } - }); - } - - function redraw_table(data, table) { - // const res = JSON.parse(result) - const columns = { - "line": 0, - "destination": 1, - "departure_time": 2, - "departure_delay": 3, - "until_departure": 4 - } - - // console.log(data) - - ensureTableSize(table, Math.min(data.length, 8), 5); - - for (let i = 0; i < Math.min(data.length, 8); i++) { - const row = table.rows[i]; - // row.classList.add("departure-table-row") - // row.style.height = `$10%`; - const train = Object.entries(data[i]); - for (let [key, value] of train) { - if (key in columns) { - const cell = row.cells[columns[key]]; // Get the cell once - cell.classList.add(key); // Add class based on column name - if (key == "line") { - // cell.textContent = String(value); - const lineClass = `line-${value}`; - const appliedClass = cssClassExists(lineClass) ? lineClass : "line-default"; - cell.innerHTML = `${value}`; - const pill = document.querySelector('.line-pill'); - - const cellHeight = cell.clientHeight; - const cellWidth = cell.clientWidth; - - // Set maximum sizes dynamically - pill.style.maxWidth = `${cellHeight * 2}px`; - pill.style.maxHeight = `${cellWidth * 0.5}px`; - } - if (key == "destination") { - dest = String(value); - dest = dest.replace(/\s*\(.*?\)\s*/g, "") - .replace(/^München\s*/i, "") - .replace(/^[\s-]+|[\s-]+$/g, ""); - cell.textContent = dest; - } - if (key == "departure_time") { - if (train["canceled"]) { - cell.textContent = "Fällt aus :("; - cell.classList.add("train-canceled"); - } else { - const date = new Date(value*1000); - cell.textContent = date.toLocaleTimeString("de-DE", - { - hour: "2-digit", - minute: "2-digit" - } - ); - } - } - if (key == "departure_delay") { - if (train["canceled"]) { - cell.textContent = ""; - } else { - cell.textContent = formatWithSign(value); - if (value >= 5) { - cell.classList.add("delay-high"); - } else if (value < 0) { - cell.classList.add("delay-neg"); - } - } - } - if (key == "until_departure") { - if (train["canceled"]) { - cell.textContent = ""; - } else { - const minutes = Math.round(value/60); - cell.textContent = String(minutes) + " min"; - if (minutes <= 6) { - cell.classList.add("until-low"); - } else if (minutes <= 10) { - cell.classList.add("until-medium"); - } - } - } - // cell.innerHTML.classList.add("scrolling-text") - } - } - } - // document.querySelector('.departure-table') - // .style.setProperty('--row-count', table.rows.length); - } - - function updateTable( table ){ - fetch('/update', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - // TODO: pass number of rows we want - }) - .then(response => response.json()) - .then(result => { - // console.log(result); - redraw_table(result.table_data, table); - // const res = JSON.parse(result); - // console.log(res.table_data); - // for (const elem of res.table_data) { - // console.log(elem); - // } - }); - } - - var clockElement = document.getElementById( "clock" ); - var tableElement = document.getElementById( "departures" ); - - setInterval(function () { - updateClock( clockElement ); - updateTable( tableElement); - }, 1000); - -}()); - diff --git a/templates/base.html b/templates/base.html index ce3ee47..050d7d6 100644 --- a/templates/base.html +++ b/templates/base.html @@ -15,7 +15,7 @@
- +