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 0000000..5b3788c Binary files /dev/null and b/__pycache__/main.cpython-313.pyc differ 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