From 61d20d1c7bf97b1a8bcbeafcb840513abf3a8df0 Mon Sep 17 00:00:00 2001 From: Chris Bayer Date: Sun, 15 Mar 2026 14:42:02 +0100 Subject: [PATCH] somehow working --- src/open_cups/app.py | 17 +++++++++++++++ src/open_cups/leave_server.py | 41 +++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 src/open_cups/leave_server.py diff --git a/src/open_cups/app.py b/src/open_cups/app.py index 3612037..55f7fd7 100644 --- a/src/open_cups/app.py +++ b/src/open_cups/app.py @@ -2,8 +2,10 @@ import qrcode import streamlit as st +import streamlit.components.v1 as components_v1 from streamlit_autorefresh import st_autorefresh +from open_cups import leave_server from open_cups.plots import show_room_statistics, show_status_history_chart from open_cups.state_provider import ( ClientState, @@ -223,6 +225,7 @@ def show_active_room_host(host_state: HostState) -> None: st.divider() show_open_questions(host_state) + _inject_leave_beacon(host_state._session_id) def show_active_room_client(client_state: ClientState) -> None: @@ -254,12 +257,26 @@ def handle_question_submit() -> None: ) show_open_questions(client_state) + _inject_leave_beacon(client_state._session_id) + + +def _inject_leave_beacon(session_id: str) -> None: + components_v1.html( + f"""""", + height=0, + ) def run() -> None: + st_autorefresh(interval=AUTOREFRESH_INTERVAL_MS, key="data_refresh") state_provider = StateProvider() + leave_server.start(state_provider.context.application_state) cleanup = state_provider.get_cleanup(USER_REMOVAL_TIMEOUT_SECONDS) cleanup.cleanup_all() diff --git a/src/open_cups/leave_server.py b/src/open_cups/leave_server.py new file mode 100644 index 0000000..5a3e208 --- /dev/null +++ b/src/open_cups/leave_server.py @@ -0,0 +1,41 @@ +import threading +from http.server import BaseHTTPRequestHandler, HTTPServer + +from open_cups.application_state import ApplicationState + +_started = False + + +def start(application_state: ApplicationState, port: int = 8502) -> None: + global _started + if _started: + return + _started = True + + def handler_factory(*args): # type: ignore[no-untyped-def] + return _Handler(*args, app_state=application_state) + + thread = threading.Thread( + target=lambda: HTTPServer(("", port), handler_factory).serve_forever(), + daemon=True, + ) + thread.start() + + +class _Handler(BaseHTTPRequestHandler): + def __init__(self, *args, app_state: ApplicationState, **kwargs) -> None: # type: ignore[no-untyped-def] + self._app_state = app_state + super().__init__(*args, **kwargs) + + def do_POST(self) -> None: + length = int(self.headers.get("Content-Length", 0)) + session_id = self.rfile.read(length).decode().strip() + room = self._app_state.get_session_room(session_id) + if room: + room.add_question("system", f"User {session_id[:8]}… left") + self.send_response(200) + self.send_header("Access-Control-Allow-Origin", "*") + self.end_headers() + + def log_message(self, *args: object) -> None: + pass