import asyncio
import getpass
import sys
import websockets
import nest_asyncio

nest_asyncio.apply() # Required to make asyncio work in Jupyter Notebook

async def question_with_response(prompt, websocket): # Function used for each question
    await websocket.send("Question: " + prompt)
    msg = await websocket.recv()
    return msg

async def quiz(websocket, path): # Main quiz function
    questions = 5
    correct = 0

    await websocket.send('Hello, ' + getpass.getuser() + " running " + sys.executable)
    await websocket.send("You will be asked " + str(questions) + " questions.")
    await websocket.send("Question: " + "Are you ready to take a test?")
    rsp = await websocket.recv()
    if rsp.lower() == "no":
        await websocket.send("Alright then, later I guess?")
        await websocket.close()
    else:
        await websocket.send("Let's go!")
    rsp = await question_with_response("What command is used to include other functions that were previously developed?", websocket)
    if rsp.lower() == "import":
        await websocket.send(rsp + " is correct!")
        correct += 1
    else:
        await websocket.send(rsp + " is incorrect!")

    rsp = await question_with_response("What command is used to evaluate correct or incorrect response in this example?", websocket)
    if rsp.lower() == "if":
        await websocket.send(rsp + " is correct!")
        correct += 1
    else:
        await websocket.send(rsp + " is incorrect!")

    rsp = await question_with_response("Each 'if' command contains an '_________' to determine a true or false condition?", websocket)
    if rsp.lower() == "expression":
        await websocket.send(rsp + " is correct!")
        correct += 1
    else:
        await websocket.send(rsp + " is incorrect!")

    rsp = await question_with_response("Which command is the opposite of if?", websocket)
    if rsp.lower() == "else":
        await websocket.send(rsp + " is correct!")
        correct += 1
    else:
        await websocket.send(rsp + " is not correct!")

    rsp = await question_with_response("What are we using to run the ipynb files?", websocket)
    if rsp.lower() == "jupyter" or rsp.lower() == "jupyter notebook":
        await websocket.send(rsp + " is correct!")
        correct += 1
    else:
        await websocket.send(rsp +  " is not correct!")

    score = (correct * 100) / questions # Percentage calculation

    await websocket.send(str(score) + "% This is your percentage")
    await websocket.send(getpass.getuser() + " you scored " + str(correct) + "/" + str(questions))

    await websocket.close()

start_server = websockets.serve(quiz, "localhost", 8765)

async def main(): # Function to start the websocket
    await start_server

if __name__ == "__main__": # Actually start the websocket
    asyncio.run(main())

connection handler failed
Traceback (most recent call last):
  File "/home/aashrayr/.local/lib/python3.10/site-packages/websockets/legacy/server.py", line 240, in handler
    await self.ws_handler(self)
  File "/home/aashrayr/.local/lib/python3.10/site-packages/websockets/legacy/server.py", line 1186, in _ws_handler
    return await cast(
  File "/tmp/ipykernel_1104/908790144.py", line 21, in quiz
    rsp = await websocket.recv()
  File "/home/aashrayr/.local/lib/python3.10/site-packages/websockets/legacy/protocol.py", line 568, in recv
    await self.ensure_open()
  File "/home/aashrayr/.local/lib/python3.10/site-packages/websockets/legacy/protocol.py", line 944, in ensure_open
    raise self.connection_closed_exc()
websockets.exceptions.ConnectionClosedOK: received 1000 (OK); then sent 1000 (OK)
connection handler failed
Traceback (most recent call last):
  File "/home/aashrayr/.local/lib/python3.10/site-packages/websockets/legacy/server.py", line 240, in handler
    await self.ws_handler(self)
  File "/home/aashrayr/.local/lib/python3.10/site-packages/websockets/legacy/server.py", line 1186, in _ws_handler
    return await cast(
  File "/tmp/ipykernel_1104/908790144.py", line 21, in quiz
    rsp = await websocket.recv()
  File "/home/aashrayr/.local/lib/python3.10/site-packages/websockets/legacy/protocol.py", line 568, in recv
    await self.ensure_open()
  File "/home/aashrayr/.local/lib/python3.10/site-packages/websockets/legacy/protocol.py", line 944, in ensure_open
    raise self.connection_closed_exc()
websockets.exceptions.ConnectionClosedOK: received 1000 (OK); then sent 1000 (OK)

Brief Code Explanation

We started with researching how to host a websocket in python. The two necessary modules were asyncio, which provides asynchronous I/O functionality, and websockets, which actually runs/serves the websocket. Using these modules, it was relatively easy to replace all the print() functions with await websocket.send(). The hardest part was to serve the websocket through Jupyter without issues. Jupyter doesn’t run python the same way as if you were just running it normally through Bash shell, so we had to add the nest_asyncio module to fix some problems with asyncio looping. After a little more tweaking, we were finally able to get the websocket to work and close when the quiz finished.

ChatGPT Code Review

Your code appears to be a simple WebSocket-based quiz game running in a Jupyter Notebook. Overall, it seems to be well-structured and functional. However, there are a few improvements and considerations you might want to take into account:

Error Handling: You should consider adding error handling for cases where the WebSocket connection fails or other exceptions occur. This will make your code more robust and user-friendly.

Code Organization: You might want to break down your code into smaller functions to improve readability and maintainability. For example, you can create a function to ask a single question, and another one to calculate the score.

Hardcoded Values: There are some hardcoded values in your code, such as the number of questions (5) and the server address (“localhost” and port 8765). It would be better to make these configurable options, either as function arguments or as constants at the beginning of your code.

User Experience: Consider providing clearer instructions to the user and formatting the messages more neatly. For instance, you could provide a welcome message and display the question number to make the quiz more user-friendly.

User Input Sanitization: Depending on the context in which this code will be used, you might want to add some input sanitization or validation to ensure that the responses received from the WebSocket client are valid.

Logging: Consider adding logging statements to help with debugging and monitoring.