From 8eed49067b82c7ec017ace069427185a2e135e41 Mon Sep 17 00:00:00 2001 From: Joel Klinghed Date: Wed, 18 Dec 2024 00:49:41 +0100 Subject: Add display and fix many issues --- .gitignore | 3 +- demo/index.html | 4 + package-lock.json | 1112 +++++++++++++++++++++++++++++++++++++++- package.json | 8 +- patches/heatmap.js+2.0.5.patch | 13 + requirements.txt | 1 + src/clicks.ts | 70 ++- src/clicks_display.ts | 137 +++++ src/display.html | 61 +++ src/server.py | 117 +++++ tsconfig.json | 5 +- 11 files changed, 1492 insertions(+), 39 deletions(-) create mode 100644 patches/heatmap.js+2.0.5.patch create mode 100644 requirements.txt create mode 100644 src/clicks_display.ts create mode 100644 src/display.html create mode 100644 src/server.py diff --git a/.gitignore b/.gitignore index 5dc3401..ce27360 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ build/* -node_modules/ \ No newline at end of file +node_modules/ +.venv/ diff --git a/demo/index.html b/demo/index.html index 9ccd0de..0e09b12 100644 --- a/demo/index.html +++ b/demo/index.html @@ -1,6 +1,10 @@ + Clicks Demo + + + +
+ +
+ +
+
+
+ + diff --git a/src/server.py b/src/server.py new file mode 100644 index 0000000..0ab0e89 --- /dev/null +++ b/src/server.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python + +import argparse +import asyncio +import json +import sys + +from datetime import datetime, timezone +from pathlib import Path +from websockets.asyncio.server import serve +from websockets.exceptions import ConnectionClosedOK + +class Context(): + def __init__(self, output): + self.output = output + output.mkdir(parents = True, exist_ok = True) + + def next(self): + ts = datetime.now(timezone.utc).timestamp() + return self.output / f"session-{ts}.json" + + +def quote(instr): + return json.dumps(instr) + +def parse_header(message): + parts = message.split('|') + if len(parts) < 3: + return None + return { + 'width': float(parts[0]), + 'height': float(parts[1]), + 'brands': '|'.join(parts[2:]), + } + +def parse_event(message): + parts = message.split('|') + if parts[0] == 'u' and len(parts) > 1: + return { + 'type': 'url', + 'url': '|'.join(parts[1:]), + } + if len(parts) < 3: + return None + return { + 'type': 'click', + 'x': float(parts[0]), + 'y': float(parts[1]), + 'tag': parts[2], + } + +async def handler(context, websocket): + filename = context.next() + with open(filename, "w", encoding="utf-8") as session_file: + try: + message = await websocket.recv() + except ConnectionClosedOK: + filename.unlink() + return + + header = parse_header(message) + if not header: + print(f"Bad header: {message}", file=sys.stderr) + filename.unlink() + return + + print(f'''{{ + "format":"clicks", + "version":"1.0", + "width": {header['width']}, + "height": {header['height']}, + "brands": {quote(header['brands'])}, + "events": [''', + file=session_file) + + comma = '' + + while True: + try: + message = await websocket.recv() + event = parse_event(message) + if event: + if event['type'] == 'click': + print(f'''{comma}{{ +"x": {event['x']}, "y": {event['y']}, "tag": {quote(event['tag'])} +}}''', + file=session_file) + else: + print(f'''{comma}{{"url": {quote(event['url'])}}}''', + file=session_file) + + if not comma: + comma = ',' + except ConnectionClosedOK: + break + + print(']}', file=session_file) + + +async def server(output): + context = Context(output) + async with serve(lambda x: handler(context, x), "", 8001): + await asyncio.get_running_loop().create_future() + +def main(): + parser = argparse.ArgumentParser( + description='Example server') + + parser.add_argument('output', type=Path, help='output directory') + + args = parser.parse_args() + + asyncio.run(server(args.output)) + +if __name__ == "__main__": + main() + diff --git a/tsconfig.json b/tsconfig.json index a2ac0d2..bb7291a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,10 +1,11 @@ { "compilerOptions": { "target": "es2016", - "module": "commonjs", + "module": "es6", "rootDir": "./src", "outDir": "./build", - "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "esModuleInterop": false, "strict": true, "skipLibCheck": true, "types": [ -- cgit v1.2.3-70-g09d2