Skip to content

Commit

Permalink
Split synchronous demos into two readable, separate subfolders
Browse files Browse the repository at this point in the history
  • Loading branch information
garrettmflynn committed Mar 14, 2024
1 parent 388d4be commit fb38daa
Show file tree
Hide file tree
Showing 13 changed files with 309 additions and 173 deletions.
37 changes: 37 additions & 0 deletions src/tqdm_publisher/_demo/_client.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@

html, body {
font-family: sans-serif;
}

h1 {
margin: 0;
padding: 0;
font-size: 1.5rem;
}

header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px;
}

#bars {
display: flex;
justify-content: center;
align-items: center;
gap: 20px;
flex-wrap: wrap;
}

.progress {
width: 100%;
height: 20px;
background-color: #ddd;
}

.progress div {
height: 100%;
background-color: #4caf50;
width: 0%;
}
75 changes: 0 additions & 75 deletions src/tqdm_publisher/_demo/_client.html

This file was deleted.

72 changes: 0 additions & 72 deletions src/tqdm_publisher/_demo/_client.js

This file was deleted.

35 changes: 25 additions & 10 deletions src/tqdm_publisher/_demo/_demo_command_line_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,18 @@
import sys
from pathlib import Path

from ._server import run_demo
import webbrowser

DEMOS = {
"single": "_single",
"multiple": "_multiple",
# "parallel": "_parallel",
}

DEMO_BASE_FOLDER_PATH = Path(__file__).parent

CLIENT_FILE_PATH = DEMO_BASE_FOLDER_PATH / "_client.html"
SERVER_FILE_PATH = DEMO_BASE_FOLDER_PATH / "_server.py"
CLIENT_PORT = 1234
RELATIVE_DEMO_BASE_FOLDER_PATH = DEMO_BASE_FOLDER_PATH.relative_to(Path.cwd())


def _command_line_interface():
Expand All @@ -30,14 +36,23 @@ def _command_line_interface():
print(f"No flags are accepted at this time, but flags {flags_list} were received.")
return

if command == "demo":
# For convenience - automatically pop-up a browser window on the locally hosted HTML page
if sys.platform == "win32":
os.system(f'start "" "{CLIENT_FILE_PATH}"')
else:
subprocess.run(["open", CLIENT_FILE_PATH])
if command in DEMOS:

subpath = DEMOS[command]

# if command == "parallel":
# client_relative_path = Path(subpath) / "_client.py"
# subprocess.Popen(['python', str(DEMO_BASE_FOLDER_PATH / subpath / "_server.py")])
# subprocess.Popen(['python', str(DEMO_BASE_FOLDER_PATH / subpath / "_client.py")])

# else:

client_relative_path = Path(subpath) / "_client.html"
subprocess.Popen(['python', '-m', 'http.server', str(CLIENT_PORT), "-d", DEMO_BASE_FOLDER_PATH])

webbrowser.open_new_tab(f"http://localhost:{CLIENT_PORT}/{client_relative_path}")

run_demo()
subprocess.run(['python', str(DEMO_BASE_FOLDER_PATH / subpath / "_server.py")])

else:
print(f"{command} is an invalid command.")
36 changes: 36 additions & 0 deletions src/tqdm_publisher/_demo/_multiple/_client.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8">

<meta http-equiv="X-UA-Compatible" content="IE=edge">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>Multiple Bar Demo</title>

<!-- Basic Page Styling -->
<link rel="stylesheet" href="../_client.css">
<script src="./_client.js" type="module" defer></script>

</head>

<!-- Basic Page Structure -->
<body>
<header>
<div>
<h1>tqdm_progress</h1>
<i><small>Create multiple progress bars to test concurrent subscriptions</small></i>
</div>

<!-- Declare a button to create progress bars -->
<button>Create Progress Bar</button>
</header>

<!-- Declare a container to hold the progress bars -->
<div id="bars"></div>
</body>
</html>
21 changes: 21 additions & 0 deletions src/tqdm_publisher/_demo/_multiple/_client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { WebSocketManager } from '../utils/WebSocketManager.js';
import { createProgressBar } from '../utils/elements.js';

const bars = {} // Track progress bars

// Update the specified progress bar when a message is received from the server
const onProgressUpdate = (event) => {
const { request_id, format_dict } = JSON.parse(event.data);
bars[request_id].style.width = 100 * (format_dict.n / format_dict.total) + '%';
}

// Create a new WebSocket client
const client = new WebSocketManager({ onmessage: onProgressUpdate });

// Declare that the HTML Button should create a new progress bar when clicked
const button = document.querySelector('button');
button.addEventListener('click', () => {
const request_id = Math.random().toString(36).substring(7); // Create a unique ID for the progress bar
bars[request_id] = createProgressBar(); // Create and render a progress bar
client.socket.send(JSON.stringify({ command: 'start', request_id })); // Send a message to the server to start the progress bar
})
69 changes: 69 additions & 0 deletions src/tqdm_publisher/_demo/_multiple/_server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import asyncio
import json
import threading
import time

import websockets

import tqdm_publisher


async def handler(websocket: websockets.WebSocketServerProtocol) -> None:
"""Handle messages from the client and manage the client connections."""

class WebSocketProgressBar(threading.Thread):

def __init__(self, request_id: str):
super().__init__()
self.request_id = request_id

def update(self, format_dict) -> None:
"""
This is the function that will run on every update of the TQDM object.
It will forward the progress to the client.
"""
asyncio.run(
websocket.send(message=json.dumps(obj=dict(request_id=self.request_id, format_dict=format_dict)))
)

def run(self):
"""
Emulate running the specified number of tasks by sleeping the specified amount of time on each iteration.
Defaults are chosen for a deterministic and regular update period of one second for a total time of one minute.
"""
all_task_durations_in_seconds = [.1 for _ in range(100)] # Ten seconds of one hundred tasks
progress_bar = self.progress_bar = tqdm_publisher.TQDMPublisher(iterable=all_task_durations_in_seconds)
progress_bar.subscribe(callback=self.update)

for task_duration in progress_bar:
time.sleep(task_duration)

# def start(self):
# thread = threading.Thread(target=self.run_progress_bar)
# thread.start()


# Wait for messages from the client
async for message in websocket:
message_from_client = json.loads(message)

if message_from_client["command"] == "start":
progress_bar = WebSocketProgressBar(request_id=message_from_client["request_id"])
progress_bar.start()


async def spawn_server() -> None:
"""Spawn the server asynchronously."""
async with websockets.serve(ws_handler=handler, host="", port=8000):
await asyncio.Future()


def run_demo() -> None:
"""Trigger the execution of the asynchronous spawn."""
asyncio.run(spawn_server())


if __name__ == "__main__":
run_demo()
25 changes: 25 additions & 0 deletions src/tqdm_publisher/_demo/_single_bar/_client.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<!DOCTYPE html>

<html lang="en">

<head>
<title>Single Bar Demo</title>
<link rel="stylesheet" href="../_client.css">
<script src="./_client.js" type="module" defer></script>
</head>

<body>
<header>
<div>
<h1>tqdm_progress</h1>
<i><small>Single Bar Demo</small></i>
</div>

<!-- Declare a button to create progress bars -->
<button>Create Progress Bar</button>
</header>

<!-- Declare a container to hold the progress bars -->
<div id="bars"></div>
</body>
</html>
Loading

0 comments on commit fb38daa

Please sign in to comment.