Skip to the content.

Sprint5_review_ipynb_2_

Our Project

Group

The purpose of our group’s program is to create a multiplayer drawing and guessing game inspired by Scribble.io. This program allows players to take turns drawing while others guess the word or phrase being illustrated. The goal is to provide an entertaining and interactive way for people to engage in creative gameplay, fostering collaboration, quick thinking, and creativity.

Individual

My individual feature focuses on implementing the real-time guessing system. This feature enables players to submit guesses while others draw, providing instant feedback on whether the guess is correct. The purpose of this feature is to ensure smooth and engaging gameplay by allowing seamless interaction between players and keeping the game dynamic and competitive.

List Requests

In our program, we make use of lists, dictionaries, and a database to handle the application’s data. Specifically:

  • Lists are used to store rows of guesses
  • Dictionaries are used to handle the JSON api requests and responses
  • Database (SQLite) is used to store the user data(the guesses)

The Guess function uses the following requests;

  • Post: add new guesses
  • Get: Retrieveing new entries
  • Put: To update existing guesses
  • Delete: deleting guesses

We use lists to handle guesses and dictionaires to represent individual entries.Those database rows are then coverted for JSON Responses

Formatting JSON Response from API to DOM

@token_required()  # Require authentication before accessing this route
def get(self):
    """
    Return the guesses of the authenticated user as a JSON object.
    """
    
    # Get the current authenticated user from the token.
    # This ensures that only the logged-in user's data is accessed.
    current_user = g.current_user  
    
    # Query the database to find all guesses associated with the current user.
    guesses = Guess.query.filter_by(user_id=current_user.id).all()  

    # If the user has no recorded guesses, return a 404 response with a JSON message.
    # This prevents returning an empty list and provides meaningful feedback.
    if not guesses:
        return {'message': 'No guesses found for this user'}, 404  

    # Format the retrieved guesses into a list of dictionaries.
    # Each dictionary represents a single guess and contains:
    # - 'guess': the actual guessed word
    # - 'is_correct': a boolean indicating if the guess was correct or not
    guesses_data = [{'guess': guess.guess, 'is_correct': guess.is_correct} for guess in guesses]

    # Convert the list of dictionaries into a JSON response.
    # This ensures that the frontend receives structured data that can be easily parsed.
    return jsonify(guesses_data)  

Database queries

Query the database using SQLAlchemy to find all guesses for the current user - SQLAlchemy is a third-party library that allows us to interact with the database using Python objects instead of raw SQL queries.

@token_required()  # Require authentication before accessing this route
def get(self):
    """
    Return the guesses of the authenticated user as a JSON object.
    """
    
    # Get the current authenticated user from the token
    current_user = g.current_user  
    
    # Query the database to find all guesses that match the current user's ID
    guesses = Guess.query.filter_by(user_id=current_user.id).all()  

    # If the user has no guesses recorded, return a 404 response with a message
    if not guesses:
        return {'message': 'No guesses found for this user'}, 404  

    # Iterate through each guess object and create a list of dictionaries 
    # Each dictionary contains the guess and whether it was correct
    guesses_data = [{'guess': guess.guess, 'is_correct': guess.is_correct} for guess in guesses]

    # Convert the list into a JSON response and return it
    return jsonify(guesses_data)


Methods in Class to Work with Columns:

class GuessesAPI:
    class _CRUD(Resource):
        @token_required()
        def post(self):
            """
            Add a new guess for the authenticated user.
            """
            current_user = g.current_user  # Get the current authenticated user
            body = request.get_json()  # Get the request body as JSON
            guess_value = body.get('guess')  # Extract the 'guess' field from the request body

            if not guess_value:
                return {'message': 'No guess provided'}, 400  # Return an error if no guess is provided

            # Create a new Guess object and assume it's incorrect initially
            new_guess = Guess(user_id=current_user.id, guess=guess_value, is_correct=False)

            # Add the new guess to the database session and commit it
            db.session.add(new_guess)
            db.session.commit()

            return jsonify({'message': 'Guess added successfully', 'guess': new_guess.guess}), 201
        
        @token_required()
        def get(self):
            """
            Retrieve all guesses made by the authenticated user.
            """
            current_user = g.current_user  # Get the authenticated user
            
            # Fetch all guesses associated with the current user from the database
            guesses = Guess.query.filter_by(user_id=current_user.id).all()

            if not guesses:
                return {'message': 'No guesses found for this user'}, 404  # Handle case where no guesses exist
            
            # Convert guesses into a structured JSON response
            guesses_data = [{'id': guess.id, 'guess': guess.guess, 'is_correct': guess.is_correct} for guess in guesses]

            return jsonify(guesses_data)  # Return the JSON response
        
        @token_required()
        def put(self):
            """
            Update an existing guess's correctness status.
            """
            current_user = g.current_user  # Get the authenticated user
            body = request.get_json()  # Parse the request body
            guess_id = body.get('id')  # Get the guess ID
            is_correct = body.get('is_correct')  # Get the new correctness status

            if guess_id is None or is_correct is None:
                return {'message': 'Guess ID and correctness status are required'}, 400  # Validate input

            # Find the guess entry belonging to the user
            guess = Guess.query.filter_by(id=guess_id, user_id=current_user.id).first()

            if not guess:
                return {'message': 'Guess not found'}, 404  # Return error if guess doesn't exist
            
            # Update the correctness status
            guess.is_correct = bool(is_correct)
            db.session.commit()  # Save changes to the database

            return jsonify({'message': 'Guess updated successfully', 'id': guess.id, 'is_correct': guess.is_correct})

        @token_required()
        def delete(self):
            """
            Delete a guess made by the authenticated user.
            """
            current_user = g.current_user  # Get the authenticated user
            body = request.get_json()  # Parse the request body
            guess_id = body.get('id')  # Get the ID of the guess to be deleted

            if not guess_id:
                return {'message': 'Guess ID is required'}, 400  # Ensure ID is provided

            # Find the guess entry that belongs to the user
            guess = Guess.query.filter_by(id=guess_id, user_id=current_user.id).first()

            if not guess:
                return {'message': 'Guess not found'}, 404  # Handle case where guess doesn't exist

            # Delete the guess and commit changes to the database
            db.session.delete(guess)
            db.session.commit()

            return jsonify({'message': 'Guess deleted successfully', 'id': guess_id})

Algorithmic code request.

Definition of Code Blocks to Handle Requests

Api CRUD Methods:

class GuessesAPI:
    class _CRUD(Resource):
        @token_required()
        def post(self):
            """
            Add a new guess for the authenticated user.
            """
            current_user = g.current_user  # Get the authenticated user
            body = request.get_json()  # Extract JSON data from request
            new_guess = body.get('guess')  # Retrieve 'guess' value from request body

            if not new_guess:
                return {'message': 'No guess provided'}, 400  # Validate input
            
            # Create a new Guess object with the provided guess value
            new_guess_obj = Guess(user_id=current_user.id, guess=new_guess, is_correct=False)

            # Add and commit the new guess to the database
            db.session.add(new_guess_obj)
            db.session.commit()

            return jsonify({'message': 'Guess added successfully', 'guess': new_guess_obj.guess}), 201
        
        @token_required()
        def get(self):
            """
            Retrieve all guesses made by the authenticated user.
            """
            current_user = g.current_user  # Get the authenticated user
            
            # Fetch all guesses associated with the user
            guesses = Guess.query.filter_by(user_id=current_user.id).all()

            if not guesses:
                return {'message': 'No guesses found for this user'}, 404  # Handle case where no guesses exist
            
            # Convert guesses into a structured JSON response
            guesses_data = [{'id': guess.id, 'guess': guess.guess, 'is_correct': guess.is_correct} for guess in guesses]

            return jsonify(guesses_data)  # Return the JSON response
        
        @token_required()
        def put(self):
            """
            Update an existing guess for the authenticated user.
            """
            current_user = g.current_user  # Get the authenticated user
            body = request.get_json()  # Extract JSON data from request
            updated_guess = body.get('guess')  # Retrieve updated guess value
            guess_id = body.get('guess_id')  # Retrieve ID of the guess to be updated

            if not updated_guess or not guess_id:
                return {'message': 'No guess or guess_id provided'}, 400  # Validate input

            # Find the guess by ID and ensure it belongs to the current user
            guess = Guess.query.filter_by(id=guess_id, user_id=current_user.id).first()
            if not guess:
                return {'message': 'Guess not found or does not belong to the user'}, 404

            # Update the guess value
            guess.guess = updated_guess
            db.session.commit()  # Save changes to the database

            return jsonify({'message': 'Guess updated successfully', 'guess': guess.guess}), 200

        @token_required()
        def delete(self):
            """
            Delete a specified guess of the authenticated user.
            """
            body = request.get_json()  # Extract JSON data from request

            if not body or 'guess_id' not in body:
                return {'message': 'No guess_id provided'}, 400  # Validate input
            
            current_user = g.current_user  # Get the authenticated user
            guess_id = body.get('guess_id')  # Retrieve ID of the guess to be deleted

            # Find the guess by ID and ensure it belongs to the current user
            guess = Guess.query.filter_by(id=guess_id, user_id=current_user.id).first()
            if not guess:
                return {'message': 'Guess not found or does not belong to the user'}, 404

            # Delete the guess and commit changes to the database
            db.session.delete(guess)
            db.session.commit()

            return {'message': 'Guess deleted successfully'}, 200

Method/Procedure in Class with Sequencing, Selection, and Iteration

@token_required()
def update_guess():
    """
    API endpoint to update a user's guess. This function follows:
    - **Sequencing**: Each step is executed in a logical order.
    - **Selection**: Decisions are made based on input validity and conditions.
    - **Iteration**: Updates are applied to stored guesses iteratively.
    """
    try:
        # ========== SEQUENCING: Step 1 - Parse JSON input ==========
        data = request.json  # Extract the JSON request body
        if not data:
            return jsonify({"error": "Invalid or missing JSON payload."}), 400  # Selection: Handle missing data

        print("Incoming update request:", data)  # Debugging output

        # ========== SEQUENCING: Step 2 - Validate required keys ==========
        required_keys = {'user', 'guess_id', 'new_guess', 'is_correct'}
        is_valid, error_message = validate_request_data(data, required_keys)

        if not is_valid:
            print("Validation failed:", error_message)  # Debugging
            return jsonify({"error": error_message}), 400  # Selection: Handle invalid data

        # ========== SEQUENCING: Step 3 - Extract values from JSON ==========
        user = data['user']
        guess_id = data['guess_id']
        new_guess = data['new_guess']
        is_correct = data['is_correct']

        # ========== SEQUENCING: Step 4 - Validate data types ==========
        if not isinstance(user, str) or not isinstance(new_guess, str) or not isinstance(is_correct, bool):
            return jsonify({"error": "Invalid data types for user, guess, or is_correct."}), 400  # Selection

        # ========== SEQUENCING: Step 5 - Find and update the guess ==========
        existing_guess = Guess.query.filter_by(id=guess_id, guesser_name=user).first()  # Selection: Locate the guess

        if not existing_guess:
            return jsonify({"error": "Guess not found or does not belong to the user."}), 404  # Selection

        # ========== SEQUENCING: Step 6 - Update the guess in the database ==========
        existing_guess.guess = new_guess  # Modify guess value
        existing_guess.is_correct = is_correct  # Modify correctness status
        db.session.commit()  # Save changes

        # ========== SEQUENCING: Step 7 - Update user stats ==========
        if user in user_stats:
            user_stats[user]["guesses"] = [  # Iteration: Modify guess list
                {"guess": new_guess, "is_correct": is_correct} if g["guess"] == existing_guess.guess else g
                for g in user_stats[user]["guesses"]
            ]
            
            # Recalculate correctness statistics
            user_stats[user]["correct"] = sum(1 for g in user_stats[user]["guesses"] if g["is_correct"])  # Iteration
            user_stats[user]["wrong"] = sum(1 for g in user_stats[user]["guesses"] if not g["is_correct"])  # Iteration

        # ========== SEQUENCING: Step 8 - Return response ==========
        response_data = {
            "User": user,
            "Updated Guess": {
                "Guess": new_guess,
                "Is Correct": is_correct
            },
            "Updated Stats": {
                "Correct Guesses": user_stats[user]["correct"],
                "Wrong Guesses": user_stats[user]["wrong"],
                "Total Guesses": len(user_stats[user]["guesses"])  # Iteration: Count guesses
            }
        }

        return jsonify(response_data), 200  # Return success response

    except Exception as e:
        print("General Exception:", str(e))  # Debugging
        return jsonify({"error": f"Internal server error: {str(e)}"}), 500  # Selection: Handle unexpected errors

Parameters and Return Type

  • Parameters The body of the request is in JSON format, which contains fields like:

      guesser_name: Name of the guesser (string).
      guess: The guessed value (string).
      is_correct: Whether the guess is correct (boolean).
    
  • Return Type The functions return a JSON response, created using jsonify(), which ensures the response is in proper JSON format.

    { “id”: 1, “guesser_name”: “JohnDoe”, “guess”: “apple”, “is_correct”: true }

Call to Algorithm request

Definition of Code Block to Make a Request

Frontend Fetch to Endpoint:

async function fetchGuesses() {
  try {
    // Step 1: Make a GET request to the server to fetch the list of guesses
    const response = await fetch('http://127.0.0.1:8887/api/guesses', { method: 'GET' });
    
    // Step 2: Check if the response is successful (status code 200-299)
    if (!response.ok) throw new Error('Failed to fetch guesses');  // If not, throw an error
    
    // Step 3: Parse the response body as JSON
    const guesses = await response.json(); // The server is expected to respond with an array of guesses
    
    // Step 4: Update the UI with the fetched guesses
    updateGuessTable(guesses);  // Call the function to update the guesses table in the UI

  } catch (error) {
    // Step 5: Handle any errors that occurred during the request or processing
    console.error('Error fetching guesses:', error);  // Log the error to the console
    messageArea.textContent = `Error fetching guesses: ${error.message}`;  // Display the error message to the user
  }
}

Discuss the Call/Request to the Method with Algorithim

  • Call/Request:
    • The delete CRUD method sends delete request to api/guesses endpoint for guess deletion
@token_required()  # Ensures the user is authenticated before accessing this endpoint
def delete(self):
    """
    Delete a specified guess of the authenticated user.
    """

    # Step 1: Retrieve JSON body from the request
    body = request.get_json()  

    # Step 2: Check if the request contains a 'guess_id'
    if not body or 'guess_id' not in body:  
        return {'message': 'No guess_id provided'}, 400  # Return error if missing

    # Step 3: Get the authenticated user (provided by token_required)
    current_user = g.current_user  

    # Step 4: Extract the 'guess_id' from the JSON body
    guess_id = body.get('guess_id')  

    # Step 5: Query the database using SQLAlchemy to find the guess
    # This query is provided by SQLAlchemy (a third-party ORM) and translates to:
    # SELECT * FROM guesses WHERE id = <guess_id> AND user_id = <current_user.id>;
    guess = Guess.query.filter_by(id=guess_id, user_id=current_user.id).first()  

    # Step 6: If no guess is found (either it doesn't exist or doesn't belong to the user), return an error
    if not guess:  
        return {'message': 'Guess not found or does not belong to the user'}, 404

    # Step 7: Delete the guess from the database
    db.session.delete(guess)  # SQLAlchemy ORM handles the deletion
    db.session.commit()  # Commit the transaction to make changes permanent

    # Step 8: Return a success response
    return {'message': 'Guess deleted successfully'}, 200  

  • Return/Response:
    • The response from the backend is handled by checking the status code and updating the UI accordingly.

Success Response: 200 OK

{
  "message": "Guess deleted successfully"
}

Error response: 400 Missing guess_id

{
  "message": "No guess_id provided"
}

Error Response: 404 guess not found

{
  "message": "Guess not found or does not belong to the user"
}

Handling Data and Error Conditions

  • Normal Conditions:
    • The guess is successfully deleted, and the UI is updated to reflect the change.
      • 200 OK: Guess successfully deleted
  • Error Conditions:
    • If the guess is not found or the request fails, an error message is displayed.
      • error 404: Guess not found
      • error 400: missing guess_id provided