summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authoruser <user@node5.net>2024-07-19 23:32:40 +0200
committeruser <user@node5.net>2024-07-19 23:32:40 +0200
commit78e3f6ea427a10edaeac1c056f8a3ab5640e2a0c (patch)
tree0bd7a010f00fb07c15577e7fea9fb2128239b82f /src
Initial commit - displays events
Diffstat (limited to 'src')
-rw-r--r--src/app.py69
-rwxr-xr-xsrc/program_parser.py104
-rw-r--r--src/static/favicon.icobin0 -> 15406 bytes
-rw-r--r--src/static/main.css72
-rw-r--r--src/static/main.js31
-rw-r--r--src/static/news_headline.pngbin0 -> 2597 bytes
-rw-r--r--src/static/title_headline.pngbin0 -> 2255 bytes
-rw-r--r--src/templates/base.html24
-rw-r--r--src/templates/event.html1
-rw-r--r--src/templates/index.html24
10 files changed, 325 insertions, 0 deletions
diff --git a/src/app.py b/src/app.py
new file mode 100644
index 0000000..b063bec
--- /dev/null
+++ b/src/app.py
@@ -0,0 +1,69 @@
+import logging
+import os
+import datetime
+
+import flask
+import yaml
+
+import program_parser
+
+#import db_handler
+
+class CustomFormatter(logging.Formatter):
+ grey = "\x1b[90;20m"
+ blue = "\x1b[34;20m"
+ yellow = "\x1b[33;20m"
+ red = "\x1b[31;20m"
+ bold_red = "\x1b[31;1m"
+ reset = "\x1b[0m"
+ format = "%(asctime)s,%(msecs)03d %(levelname)-8s [%(filename)s:%(lineno)d] %(message)s"
+
+ FORMATS = {
+ logging.DEBUG: grey + format + reset,
+ logging.INFO: blue + format + reset,
+ logging.WARNING: yellow + format + reset,
+ logging.ERROR: red + format + reset,
+ logging.CRITICAL: bold_red + format + reset
+ }
+
+ def format(self, record):
+ log_fmt = self.FORMATS.get(record.levelno)
+ formatter = logging.Formatter(log_fmt)
+ return formatter.format(record)
+
+logger = logging.getLogger(__name__)
+if os.environ.get('FLASK_DEBUG') == '1': # Local development
+ logger.root.setLevel(logging.DEBUG)
+else: # Production
+ logger.root.setLevel(logging.INFO)
+
+stream_handler = logging.StreamHandler()
+stream_handler.setFormatter(CustomFormatter())
+logger.root.addHandler(stream_handler)
+
+app = flask.Flask(__name__, template_folder='templates', static_folder='static', static_url_path='')
+
+@app.route('/top')
+def top():
+ return f'BornHack S105 {datetime.datetime.now().strftime("%a %d %b %R:%S")} '
+
+@app.route('/program.json')
+def program():
+ events_to_show: list[Event] = program_parser.get_events_to_show(program_parser.program, programs_to_show_count = 16)
+ #{event.title} {day} {start_time}-{end_time} {icon}
+ json = [{'title': a.title, 'day': a.day, 'time': a.time, 'progress': a.progress, 'icon': a.icon} for a in events_to_show]
+ return json
+'''
+@app.route('/news')
+def news():
+ with open(news_file_path, "r") as news_file:
+ return news_file.read().replace('\n', '<br>')
+'''
+@app.route('/text')
+def text() -> dict[str, str]:
+ return {part: eval(part)() for part in ['top', 'program']}
+
+@app.route('/')
+def index():
+ return flask.render_template('index.html', parts=text())
+
diff --git a/src/program_parser.py b/src/program_parser.py
new file mode 100755
index 0000000..59f1159
--- /dev/null
+++ b/src/program_parser.py
@@ -0,0 +1,104 @@
+#!/run/current-system/sw/bin/python
+import os
+import json
+import datetime
+import copy
+import dataclasses
+
+input_file_path = '../../content/program.json'
+tmp_file_path = '/tmp/teletext/bh_events_to_show_TMP'
+output_file_path = '/tmp/teletext/bh_events_to_show'
+
+title_max_length = 30
+#current_date = datetime.datetime(year=2024, month=7, day=18, hour=14, minute=59) # DEBUG
+
+@dataclasses.dataclass
+class Event:
+ title: str = None
+ room: str = None
+ day: str = None
+ start_date: datetime.datetime = None
+ end_date: datetime.datetime = None
+ icon: str = '?'
+ time: str = None
+ duration: datetime.timedelta = None
+ pretty: str = None
+ progress: int = None
+
+icons = {
+ 'Startent at Info Desk' : "⭐",
+ 'Speakers Tent': "🏕",
+ 'Workshop Room': "🛠️",
+ 'Bar Meetup Area': "🍺",
+ 'Bar Area': "🍺",
+ 'External': "🚗",
+ 'Pyjam.as Village in Party Area': "🐍"
+}
+
+
+def get_program() -> list[Event]:
+ program: list[Event] = []
+ # Parse program into data format (interpret datetime, and such)
+ with open(input_file_path, "r") as json_file:
+ program_raw = json.load(json_file)
+ for event_raw in program_raw:
+ event = Event()
+ #print(event_raw["title"])
+ for d in ["title", "room"]: # 1 to 1 mapping
+ setattr(event, d, event_raw[d])
+ event.start_date = datetime.datetime.strptime(event_raw["date"], "%Y-%m-%dT%H:%M:%S+00:00")
+ event.start_date += datetime.timedelta(hours=2) # Hvad er vinter tid?
+ end_bogus = datetime.datetime.strptime(event_raw["duration"],"%H:%M:%S")
+ event.duration = datetime.timedelta(hours=end_bogus.hour, minutes=end_bogus.minute, seconds=end_bogus.second)
+ event.end_date = event.start_date + event.duration
+ #print('Start date: ', event["start_date"])
+ #print("End date: ", event["end_date"])
+ #print()
+ program.append(event)
+
+ program = sorted(program, key=lambda x: x.start_date)
+ return program
+
+def get_events_to_show(program: list[Event], programs_to_show_count=4) -> list[Event]:
+ current_date = datetime.datetime.now()
+ # Get events to show
+ events_to_show = []
+ for event in program:
+ if len(events_to_show) >= programs_to_show_count:
+ break
+ if event.end_date > current_date:
+ events_to_show.append(event)
+
+ def format_program(event: Event, cli=False) -> str:
+ if len(event.title) > title_max_length:
+ event.title = f'{event.title[:title_max_length - 3]}...' # Truncate title by deleting was 1-3 chars and putting ...
+ else:
+ event.title += " " * (title_max_length - len(event.title)) # Pad title
+ event.day = event.start_date.strftime('%a')
+ start_time = event.start_date.strftime('%H:%M')
+ end_time = event.end_date.strftime('%H:%M')
+ event.icon = icons[event.room]
+ event.time = f"{start_time}-{end_time}"
+ return f"{event.title} {event.day} {start_time}-{end_time} {event.icon}"
+
+ for event_to_show in events_to_show:
+ event_to_show.pretty = format_program(event_to_show)
+ duration_sec = (event_to_show.end_date - event_to_show.start_date).total_seconds()
+ progress_sec = (datetime.datetime.now() - event_to_show.end_date).total_seconds()
+ event_to_show.progress = 100 + (progress_sec / duration_sec) * 100
+
+ return events_to_show
+
+program: list[Event] = get_program()
+events_to_show: list[Event] = get_events_to_show(program)
+
+# Convert to the output string
+events_to_show_string = '\n'.join([a.pretty for a in events_to_show])
+print(f"{events_to_show_string}\n")
+
+# Write to output file
+with open(tmp_file_path, 'w') as tmp_file:
+ tmp_file.write(events_to_show_string)
+os.rename(tmp_file_path, output_file_path)
+print(f'Written to {output_file_path} length: {len(events_to_show_string)}')
+
diff --git a/src/static/favicon.ico b/src/static/favicon.ico
new file mode 100644
index 0000000..653155d
--- /dev/null
+++ b/src/static/favicon.ico
Binary files differ
diff --git a/src/static/main.css b/src/static/main.css
new file mode 100644
index 0000000..e4d5aef
--- /dev/null
+++ b/src/static/main.css
@@ -0,0 +1,72 @@
+body {
+ color: white;
+ background: black;
+ image-rendering: pixelated;
+ zoom: 2;
+}
+
+#root-container {
+ margin: auto;
+ text-align: center;
+ width: min-content;
+}
+
+.content {
+ margin-top: 0.1em;
+}
+
+#top {
+ font-size: 1.36em;
+ color: grey;
+}
+
+#program {
+ font-size: 1.64em;
+}
+
+#news {
+ font-size: 1.36em;
+ text-align: left;
+}
+
+.grey {
+ color: grey !important;
+}
+
+input {
+ width: 80%;
+ height: 200%;
+ font-size: 1em;
+}
+
+#news_input_label {
+ font-size: 0.7em;
+}
+
+input[type=submit] {
+ width: fit-content;
+}
+
+
+progress {
+ -webkit-appearance: none;
+ background: #000;
+ border: none;
+ width: 93%;
+}
+
+progress::-moz-progress-bar { background: #006688; }
+
+#progress-bar span {
+ position: absolute;
+ display: inline-block;
+ color: #fff;
+ text-align: right;
+}
+
+#progress-bar {
+ display: block;
+ position: relative;
+ width: 100%;
+}
+
diff --git a/src/static/main.js b/src/static/main.js
new file mode 100644
index 0000000..7bea389
--- /dev/null
+++ b/src/static/main.js
@@ -0,0 +1,31 @@
+const url = '/text';
+const parts = ["top", "program"];
+const elements = {};
+
+function get_element(part){
+ elements[part] = document.getElementById(part);
+}
+
+const program_element = parts.forEach(get_element);
+
+function set_element_text(part, text){
+ elements[part].innerHTML = text;
+}
+
+async function getEvents() {
+ try {
+ const response = await fetch(url);
+ if (!response.ok) {
+ throw new Error(`Response status: ${response.status}`);
+ }
+
+ const events = await response.json();
+ console.log(events)
+
+ } catch (error) {
+ console.error(error.message);
+ }
+}
+
+getEvents();
+
diff --git a/src/static/news_headline.png b/src/static/news_headline.png
new file mode 100644
index 0000000..f9876a1
--- /dev/null
+++ b/src/static/news_headline.png
Binary files differ
diff --git a/src/static/title_headline.png b/src/static/title_headline.png
new file mode 100644
index 0000000..dec80be
--- /dev/null
+++ b/src/static/title_headline.png
Binary files differ
diff --git a/src/templates/base.html b/src/templates/base.html
new file mode 100644
index 0000000..ac28685
--- /dev/null
+++ b/src/templates/base.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ {% block head %}
+ <link rel="stylesheet" href="/main.css"/>
+ <title>Text TV - Bornhack 24</title>
+ <link rel="icon" type="image/x-icon" href="favicon.ico">
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <script src="main.js" defer></script>
+ {% endblock %}
+ <meta http-equiv="refresh" content="10">
+</head>
+<body>
+
+ <div id="root-container">
+
+ {% block content %}
+ {% endblock %}
+
+ <div>
+
+</body>
+</html>
diff --git a/src/templates/event.html b/src/templates/event.html
new file mode 100644
index 0000000..efe4cba
--- /dev/null
+++ b/src/templates/event.html
@@ -0,0 +1 @@
+<div id="progress-bar"><span data-value="60" style="width: 60%;">{{event.title}} {{event.day}} {{ event.time}} {{ event.icon }}</span><progress value="{{event.progress}}" max="100"></progress></div>
diff --git a/src/templates/index.html b/src/templates/index.html
new file mode 100644
index 0000000..6e94876
--- /dev/null
+++ b/src/templates/index.html
@@ -0,0 +1,24 @@
+{% extends "base.html" %}
+
+{% block content %}
+
+ <pre class="content" id="top">{{parts.top}}</pre>
+
+ <img src="title_headline.png">
+
+ <pre class="content" id="program">{% for event in parts.program %}{% include 'event.html' %}{% endfor %}</pre>
+{#
+ <img src="news_headline.png">
+
+ <pre class="content" id="news">{{parts.news}}</pre>
+ -->
+ <br>
+<!--
+ <form>
+ <label id="news_input_label" for="news_input">Post your own news, or anything you'd like here:</label><br>
+ <input type="text" id="news_input" name="news_input" maxlength="{{ news_max_length }}" placeholder="Type text here">
+ <input type="submit" value="Post" />
+ </form>
+#}
+
+{% endblock %}