From f34125d67bdb4e9da4cf2c53d33dd61eb29c53a2 Mon Sep 17 00:00:00 2001 From: Accusedbold Date: Sat, 2 May 2026 18:26:02 -0400 Subject: [PATCH] Added Websocket stuff --- .gitignore | 1 + __pycache__/main.cpython-313.pyc | Bin 0 -> 4923 bytes main.py | 73 ++++++++++++++++++++-------- www/data/data.json | 1 - www/overlay.html | 7 +-- www/problem.js | 79 ------------------------------- www/{ => scripts}/background.js | 0 www/scripts/problem.js | 50 +++++++++++++++++++ www/scripts/web_sockets.js | 31 ++++++++++++ www/{ => style}/styles.css | 0 10 files changed, 140 insertions(+), 102 deletions(-) create mode 100644 .gitignore create mode 100644 __pycache__/main.cpython-313.pyc delete mode 100644 www/data/data.json delete mode 100644 www/problem.js rename www/{ => scripts}/background.js (100%) create mode 100644 www/scripts/problem.js create mode 100644 www/scripts/web_sockets.js rename www/{ => style}/styles.css (100%) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c69b11e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +app.log \ No newline at end of file diff --git a/__pycache__/main.cpython-313.pyc b/__pycache__/main.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5b3788cf4dff2223fd2953b86e1955a4735700a0 GIT binary patch literal 4923 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 literal 0 HcmV?d00001 diff --git a/main.py b/main.py index 0dacccd..1b17bf0 100644 --- a/main.py +++ b/main.py @@ -1,26 +1,65 @@ #All Rights Reserved John Salguero #Starts the backend to my Youtube stream +import json +import asyncio +import logging from problem_generator import generate_problem from steps_generator import generate_steps from sympy import init_printing, sympify -import time -import json +from fastapi import FastAPI, WebSocket, WebSocketDisconnect -#define the entry point to the programs -def main(): - init_printing(order='lex') - count = -1 - no_problem = True - - problem = generate_problem() - problem["steps"] = generate_steps(problem); +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s [%(levelname)s] %(message)s", + handlers=[ + logging.FileHandler("app.log"), + logging.StreamHandler() # prints to console + ] +) +log = logging.getLogger(__name__) - no_problem = check_solution(problem["steps"][-1]["after"], problem["solution"]) - if no_problem: - with open("www/data/data.json", "w") as f: - json.dump(problem, f) +app = FastAPI() + +clients = [] + +@app.websocket("/ws") +async def websocket_endpoint(ws: WebSocket): + await ws.accept() + clients.append(ws) + + try: + while True: + data = json.loads(await ws.receive_text()) + + log.info(f"Received from browser: {data}") + + if data.get("type") == "query_problem": + problem = generate_problem_message() + await ws.send_text(json.dumps(problem)) + + except WebSocketDisconnect: + log.info("Client disconnected") + except Exception as e: + log.error(f"Unexpected error: {e}") + finally: + if ws in clients: + clients.remove(ws) + +# example loop +def generate_problem_message(): + problem_solved = False + problem = {} + while not problem_solved: + problem["type"] = "generated_problem" + problem["data"] = generate_problem() + problem["data"]["steps"] = generate_steps(problem["data"]); + problem_solved = check_solution(problem["data"]["steps"][-1]["after"], problem["data"]["solution"]) + if not problem_solved: + log.warning(f"issue with problem: {problem}") + + return problem def check_solution(got, solution): values = set([sympify(r.split("=")[1].strip()) for r in got.split(",")]) @@ -53,8 +92,4 @@ def is_iterable(obj): iter(obj) return True except TypeError: - return False - -#Starts the program -if __name__ == "__main__": - main() \ No newline at end of file + return False \ No newline at end of file diff --git a/www/data/data.json b/www/data/data.json deleted file mode 100644 index 43d4989..0000000 --- a/www/data/data.json +++ /dev/null @@ -1 +0,0 @@ -{"type": "two_sides", "problem": "-4*x - 3 = x + 32", "solution": -7, "steps": [{"before": "-4*x - 3 = x + 32", "after": "-5*x - 3 = 32", "step": "Subtract x from both sides", "rule": "Subtraction Property of Equality"}, {"before": "-5*x - 3 = 32", "after": "-5*x = 35", "step": "Add 3 to both sides", "rule": "Addition Property of Equality"}, {"before": "-5*x = 35", "after": "x = -7", "step": "Divide both sides by -5", "rule": "Division Property of Equality"}]} \ No newline at end of file diff --git a/www/overlay.html b/www/overlay.html index 140c2b1..187503f 100644 --- a/www/overlay.html +++ b/www/overlay.html @@ -2,7 +2,7 @@ - + @@ -10,7 +10,8 @@
Loading...
- - + + + \ No newline at end of file diff --git a/www/problem.js b/www/problem.js deleted file mode 100644 index 0308ecb..0000000 --- a/www/problem.js +++ /dev/null @@ -1,79 +0,0 @@ -let state = "problem"; // problem | steps | done -let stepIndex = 0; -let currentData = null; - -// Load the data to draw -async function loadData() { - const res = await fetch("data/data.json"); - currentData = await res.json(); - - state = "problem"; - stepIndex = 0; - - render(); -} - -function render() { - if (!currentData) return; - - const problemEl = document.getElementById("problem"); - const stepsEl = document.getElementById("steps"); - - if (state === "problem") { - problemEl.innerHTML = `\\(${currentData.problem}\\)`; - stepsEl.innerHTML = ""; - } - - if (state === "step") { - problemEl.innerHTML = `\\(${currentData.problem}\\)`; - - let html = ""; - for (let i = 0; i <= stepIndex; i++) { - const s = currentData.steps[i]; - html += ` -
-
\\(${s.before}\\)
-
${s.step}
-
\\(${s.after}\\)
-
-
- `; - } - - stepsEl.innerHTML = html; - } - - MathJax.typesetPromise(); -} - -function startSequence() { - // 1. show problem - state = "problem"; - render(); - - setTimeout(() => { - state = "step"; - stepIndex = 0; - render(); - - let interval = setInterval(() => { - stepIndex++; - - render(); - - if (stepIndex >= currentData.steps.length - 1) { - clearInterval(interval); - - setTimeout(() => { - loadData(); // next problem - }, 3000); - } - }, 2000); // time between steps - - }, 4000); // thinking time before reveal -} - -loadData().then(() => { - startSequence(); -}); - diff --git a/www/background.js b/www/scripts/background.js similarity index 100% rename from www/background.js rename to www/scripts/background.js diff --git a/www/scripts/problem.js b/www/scripts/problem.js new file mode 100644 index 0000000..3b2d947 --- /dev/null +++ b/www/scripts/problem.js @@ -0,0 +1,50 @@ +let state = "problem"; // problem | steps | done + +function showProblem() { + const problemEl = document.getElementById("problem"); + const stepsEl = document.getElementById("steps"); + + problemEl.innerHTML = `\\(${problemData.problem}\\)`; + stepsEl.innerHTML = ""; + + MathJax.typesetPromise(); +} + +function startSequence() { + setTimeout(() => { + showNextStep(); + }, 3000); // thinking time +} + +function showNextStep() { + const stepsEl = document.getElementById("steps"); + + if (stepIndex >= problemData.steps.length) { + // finished → request next problem + setTimeout(() => { + requestNextProblem(); + }, 3000); + + return; + } + + const s = problemData.steps[stepIndex]; + + const html = ` +
+
\\(${s.before}\\)
+
${s.step}
+
\\(${s.after}\\)
+
+
+ `; + + stepsEl.innerHTML += html; + + MathJax.typesetPromise(); + + stepIndex++; + + setTimeout(showNextStep, 2000); // time between steps +} + diff --git a/www/scripts/web_sockets.js b/www/scripts/web_sockets.js new file mode 100644 index 0000000..e46101a --- /dev/null +++ b/www/scripts/web_sockets.js @@ -0,0 +1,31 @@ +const socket = new WebSocket("ws://localhost:8000/ws"); +let problemData = null; +let stepIndex = 0; + +socket.onopen = () => { + console.log("Connected to server"); + + socket.send(JSON.stringify({ + type: "query_problem" + })); +}; + +socket.onmessage = (event) => { + const msg = JSON.parse(event.data); + + if (msg.type === "generated_problem") { + problemData = msg.data; + stepIndex = 0; + + showProblem(); + + // start animation sequence + startSequence(); + } +}; + +function requestNextProblem() { + socket.send(JSON.stringify({ + type: "query_problem" + })); +} \ No newline at end of file diff --git a/www/styles.css b/www/style/styles.css similarity index 100% rename from www/styles.css rename to www/style/styles.css