Skip to the content.

Sprint 5 Reflection

Tri 2 Documentation

Intro Requests

Site Purpose

"Empowering growth, celebrating achievements, connecting journeys, inspiring brighter futures"

  • Empowering platform: Focused on helping individuals overcome challenges and celebrate personal growth.
  • Profile achievements: Users can highlight their progress and milestones on their profiles.
  • Anonymous chat feature: Allows users to connect with others who have faced similar struggles in a safe, private, and supportive environment.
  • Hobbies log: Users can track and share their interests, encouraging exploration of new passions and creative pursuits.
  • Step tracker: Promotes healthy habits by helping users monitor their physical activity and work toward fitness goals.
  • Bucket list feature: Enables users to set and document aspirations, providing inspiration and accountability for pursuing dreams.
  • Quotes log: Offers a personalized space to collect motivational quotes, uplifting users and encouraging them to stay focused on their journey.
  • Dynamic space for connection and growth: Combining these features to create a platform for personal development and celebration.

Individual Hobbies Feature Purpose

Along with the achievements page where users can post achievements they have done in many activities, my feature is a hobbies log, which lets users anonymously add, update, delete, and view hobbies through the CRUD method for motivation to engage in those hobbies. They can then strive to achieve something great in each hobby they find interest in, posting their achievements in the achievements page, letting everyone know that they got inspiration from this site’s hobbies log.

List Requests

JSON Data from API into DOM

const data = await response.json();
const hobbiesList = document.getElementById('hobbies-list');
hobbiesList.innerHTML = "";

data.hobbies.forEach(hobby => {
    const listItem = document.createElement('li');
    listItem.textContent = hobby;
    hobbiesList.appendChild(listItem);
});
  1. const data = await response.json();. Parses the JSON response from the API into a JavaScript object (data).
  2. const hobbiesList = document.getElementById('hobbies-list'); Gets the <ul> element where the hobbies will be displayed.
  3. hobbiesList.innerHTML = ""; Clears any existing list items from the <ul>.
  4. data.hobbies.forEach(hobby => { ... }); Loops through the array of hobbies.
  5. const listItem = document.createElement('li'); Creates a new <li> element for each hobby.
  6. listItem.textContent = hobby; Sets the text of the <li> to the hobby name.
  7. hobbiesList.appendChild(listItem); Adds the new <li> to the <ul> in the DOM. Results in hobbies being displayed as a list in the browser.

Use of Lists and Dictionaries, Code Descriptions

Lists

hobbies = Hobby.query.filter_by(category=category).all()

  1. Hobby refers to the Hobby class in the model file, mapping the hobby table to the database.
  2. .query is a query object provided by SQLAlchemy, which is a 3rd party library, allowing me to write queries to interact with the database, such as retrieving, filtering, or sorting rows.
  3. .filter_by(category=category) is an SQLAlchemy method filtering results. It generates an SQL WHERE clause, filtering the hobbies table to select a hobby based on a category, provided by category=category, where the first category is the column of the database and the second category is the variable telling which category the hobby is from.
  4. .all() is an SQLAlchemy method executing the query, and returning all matching rows as a Python list. Each list contains the values from each column, id, name, category.

Dictionaries

Create:

def create(self):
    try:
        db.session.add(self)
        db.session.commit()
        return True
    except IntegrityError:
        db.session.rollback()
        return False
  • Adds a row(list of data) as a hobby with identifiers for each column

Read:

def read(self):
    return {"id": self.id, "name": self.name, "category": self.category}
  • Returns dictionary representation of hobby(columns as keys)

Update:

def update(self):
    try:
        db.session.commit()
        return True
    except IntegrityError:
        db.session.rollback()
        return False
  • Updates column values for an existing row.

Delete:

def delete(self):
    try:
        db.session.delete(self)
        db.session.commit()
        return True
    except IntegrityError:
        db.session.rollback()
        return False
  • Deletes a row from the table.

These four operations are CRUD operations, allowing for addition, retrieval, updating, and deletion of specific rows and columns(dictionaries) in the database.

Algorithmic Code Request

class HobbyResource(Resource):
    def get(self):
        category = request.args.get('category', 'general')
        hobbies = Hobby.query.filter_by(category=category).all()
        if hobbies:
            return jsonify({"category": category, "hobbies": [hobby.name for hobby in hobbies]})
        else:
            return jsonify({"message": "Category not found"}), 404
    
    def post(self):
        data = request.get_json()
        if not data or not data.get('name') or not data.get('category'):
            return jsonify({"message": "Hobby name and category are required"}), 400
        
        hobby = Hobby(name=data['name'], category=data['category'])
        if hobby.create():
            return jsonify({"message": "Hobby created", "hobby": hobby.name, "category": hobby.category})
        else:
            return jsonify({"message": "Error creating hobby"}), 500

    def put(self):
        data = request.get_json()
        if not data or not data.get('name') or not data.get('category') or not data.get('old_name'):
            return jsonify({"message": "Hobby name, old name, and category are required to update"}), 400
        
        hobby = Hobby.query.filter_by(name=data['old_name'], category=data['category']).first()
        if not hobby:
            return jsonify({"message": "Hobby not found"}), 404
        
        hobby.name = data['name']
        if hobby.update():
            return jsonify({"message": "Hobby updated", "old_name": data['old_name'], "new_name": hobby.name})
        else:
            return jsonify({"message": "Error updating hobby"}), 500

    def delete(self):
        data = request.get_json()
        if not data or not data.get('name') or not data.get('category'):
            return jsonify({"message": "Hobby name and category are required to delete"}), 400
        
        hobby = Hobby.query.filter_by(name=data['name'], category=data['category']).first()
        if not hobby:
            return jsonify({"message": "Hobby not found"}), 404
        
        if hobby.delete():
            return jsonify({"message": "Hobby deleted", "name": hobby.name})
        else:
            return jsonify({"message": "Error deleting hobby"}), 500

Requests

  1. GET: Fetches hobbies filtered by category and returns them as a JSON list.
  2. POST: Adds a new hobby to the database, requiring name and category in the request body.
  3. PUT: Updates an existing hobby in the database, identified by old_name, and modifies its name and category.
  4. DELETE: Deletes a hobby from the database, identified by its name and category.

This is the API code with the GET, POST, PUT, and DELETE requests to work with the hobbies list based on each category. It allows for hobbies to be retrieved, added, updated, and deleted from each separate categorical list.

GET Method

def get(self):
    category = request.args.get('category', 'general')  # Sequencing
    hobbies = Hobby.query.filter_by(category=category).all()  # Sequencing
    if hobbies:  # Selection
        return jsonify({"category": category, "hobbies": [hobby.name for hobby in hobbies]})  # Iteration
    else:
        return jsonify({"message": "Category not found"}), 404  # Selection
  1. Sequencing: The first two lines of code perform sequencing, because the two lines are performed in order. The first line retrieves the category parameter from the request, and then the second line fetches the list of hobbies from the database based on the category parameter that was retrieved.
  2. Selection: The if statement is a selection being performed, because it is choosing between JSON messages to return. If there is a hobby in the hobbies list, it returns a JSON message with the name and category of the hobby. Otherwise, it returns a 404 not found error saying there isn’t a category that is found.
  3. Iteration: The line right below if hobbies is an iteration statement, because it runs through the hobby.name for hobby in hobbies loop to iterate over the hobby list multiple times and extract the name of each hobby in the JSON response.

Parameters and Return Type

  1. Parameters: request.args.get('category', 'general') retrieves the query parameter category from the request url /api/hobby?category=.... If a parameter isn’t provided, it defaults to the general category. request.get_json() is used for POST, PUT, and DELETE methods, and it retrieves the body of an HTTP request expected to be in JSON. This JSON comes from user input.
  2. Return Type: jsonify() is used in all methods in order to take a Python dictionary and return it as a JSON response that can be sent to the user.

Call to Algorithm Request

    const pythonURI = 'http://127.0.0.1:8887';
    const fetchOptions = {
        headers: {
            'Content-Type': 'application/json'
        }
    };

    async function fetchHobbies() {
        try {
            const category = document.getElementById('category').value;
            const response = await fetch(`${pythonURI}/api/hobby?category=${category}`, {
                ...fetchOptions,
                method: 'GET'
            });

            if (!response.ok) {
                throw new Error('Failed to fetch hobbies: ' + response.statusText);
            }
            const data = await response.json();
            const hobbiesList = document.getElementById('hobbies-list');
            hobbiesList.innerHTML = "";

            data.hobbies.forEach(hobby => {
                const listItem = document.createElement('li');
                listItem.textContent = hobby;
                hobbiesList.appendChild(listItem);
            });
        } catch (error) {
            console.error('Error fetching hobbies:', error);
        }
    }

    async function addHobby() {
        const hobbyName = document.getElementById('new-hobby-name').value;
        const category = document.getElementById('new-hobby-category').value;
        try {
            const response = await fetch(`${pythonURI}/api/hobby`, {
                ...fetchOptions,
                method: 'POST',
                body: JSON.stringify({ name: hobbyName, category: category })
            });

            if (!response.ok) {
                throw new Error('Failed to add hobby: ' + response.statusText);
            }

            alert('Hobby added successfully!');
            document.getElementById('new-hobby-name').value = ''; // Clear input
            fetchHobbies(); // Refresh hobbies list
        } catch (error) {
            console.error('Error adding hobby:', error);
            alert('Error adding hobby: ' + error.message);
        }
    }

    async function updateHobby() {
        const oldHobbyName = document.getElementById('old-hobby-name').value;
        const updatedHobbyName = document.getElementById('updated-hobby-name').value;
        const category = document.getElementById('update-hobby-category').value;
        try {
            const response = await fetch(`${pythonURI}/api/hobby`, {
                ...fetchOptions,
                method: 'PUT',
                body: JSON.stringify({ old_name: oldHobbyName, name: updatedHobbyName, category: category })
            });

            if (!response.ok) {
                throw new Error('Failed to update hobby: ' + response.statusText);
            }

            alert('Hobby updated successfully!');
            document.getElementById('old-hobby-name').value = ''; // Clear input
            document.getElementById('updated-hobby-name').value = ''; // Clear input
            fetchHobbies(); // Refresh hobbies list
        } catch (error) {
            console.error('Error updating hobby:', error);
            alert('Error updating hobby: ' + error.message);
        }
    }

    async function deleteHobby() {
        const hobbyName = document.getElementById('delete-hobby-name').value;
        const category = document.getElementById('delete-hobby-category').value;
        try {
            const response = await fetch(`${pythonURI}/api/hobby`, {
                ...fetchOptions,
                method: 'DELETE',
                body: JSON.stringify({ name: hobbyName, category: category })
            });

            if (!response.ok) {
                throw new Error('Failed to delete hobby: ' + response.statusText);
            }

            alert('Hobby deleted successfully!');
            document.getElementById('delete-hobby-name').value = ''; // Clear input
            fetchHobbies(); // Refresh hobbies list
        } catch (error) {
            console.error('Error deleting hobby:', error);
            alert('Error deleting hobby: ' + error.message);
        }
    }

    document.getElementById('category').addEventListener('change', fetchHobbies);
    document.getElementById('add-hobby-btn').addEventListener('click', addHobby);
    document.getElementById('update-hobby-btn').addEventListener('click', updateHobby);
    document.getElementById('delete-hobby-btn').addEventListener('click', deleteHobby);
    fetchHobbies();

1. Call/Request to the Method (Algorithm)

When a user interacts with the interface (e.g., changing the category, adding, updating, or deleting a hobby), the JavaScript code sends an HTTP request to a Flask backend API.

fetchHobbies() function (GET request):

  • The user selects a category from a dropdown.
  • The function fetches hobbies of that category by calling the endpoint: GET /api/hobby?category=selectedCategory.
Algorithm:
  1. Get the selected category value from the dropdown.
  2. Build the GET request URL using that category.
  3. Send the request to the backend API using fetch.
  4. Handle the response when the request succeeds or fails.

addHobby() function (POST request):

  • The user provides a new hobby name and category, and clicks a button to add it.
  • The function sends the new hobby data to the backend using a POST request to the endpoint: POST /api/hobby.
Algorithm:
  1. Retrieve the new hobby’s name and category from the input fields.
  2. Prepare the request body as a JSON object ({ name: hobbyName, category: category }).
  3. Send the POST request to the backend API.
  4. If the request is successful, alert the user and refresh the hobby list.

updateHobby() function (PUT request):

  • The user provides the name of the hobby to be updated and the new name.
  • The function sends the update data to the backend using a PUT request to the endpoint: PUT /api/hobby.
Algorithm:
  1. Retrieve the old hobby name, new name, and category from the input fields.
  2. Prepare the request body as a JSON object ({ old_name, name, category }).
  3. Send the PUT request to the backend API.
  4. If successful, alert the user and refresh the hobby list.

deleteHobby() function (DELETE request):

  • The user provides the hobby name and category to delete.
  • The function sends the deletion data to the backend using a DELETE request to the endpoint: DELETE /api/hobby.
Algorithm:
  1. Retrieve the hobby name and category from the input fields.
  2. Prepare the request body as a JSON object ({ name, category }).
  3. Send the DELETE request to the backend API.
  4. If successful, alert the user and refresh the hobby list.

2. Return/Response from the Method (Algorithm) and Data Handling

After each request is made, the API responds with data, and the front-end handles it appropriately.

fetchHobbies() (GET):

  • Return Type: The response will be a JSON object with a list of hobbies from the requested category, e.g., { "category": "sports", "hobbies": ["Football", "Basketball"] }.
  • Handling Data: The JSON response is parsed, and the hobbies are extracted and displayed as list items in the DOM.

addHobby() (POST):

  • Return Type: If successful, the response is a JSON object like { "message": "Hobby created", "hobby": "Soccer", "category": "sports" }.
  • Handling Data: The success message is displayed as an alert, and the input fields are cleared. The hobby list is then refreshed by calling fetchHobbies().

updateHobby() (PUT):

  • Return Type: If successful, the response is a JSON object like { "message": "Hobby updated", "old_name": "Soccer", "new_name": "Football" }.
  • Handling Data: The success message is displayed as an alert, and the input fields are cleared. The hobby list is then refreshed by calling fetchHobbies().

deleteHobby() (DELETE):

  • Return Type: If successful, the response is a JSON object like { "message": "Hobby deleted", "name": "Football" }.
  • Handling Data: The success message is displayed as an alert, and the input fields are cleared. The hobby list is then refreshed by calling fetchHobbies().

3. Changing Data or Method Triggers Different Responses (Normal and Error Conditions)

Normal Conditions:

  • When the backend API processes a valid request successfully, the response is a success message with the relevant data.
Example:
  • Adding a hobby: { "message": "Hobby created", "hobby": "Soccer", "category": "sports" }.
  • Updating a hobby: { "message": "Hobby updated", "old_name": "Soccer", "new_name": "Football" }.
  • Deleting a hobby: { "message": "Hobby deleted", "name": "Football" }.

In these cases, the response is handled successfully, and the data is used to update the UI (e.g., refreshing the hobby list).

Error Conditions:

  • If there is an issue with the request (e.g., missing data, invalid category), the API will return a failure message with an error status.
Example:
  • Invalid hobby name: { "message": "Hobby name and category are required" }.
  • Invalid category: { "message": "Category not found" }.

In these cases, the error message is displayed to the user via an alert, and the UI remains unchanged.

Example of Changing Data/Method Triggering Different Responses:

1. Normal Condition:

  • A user adds a hobby, and the backend returns { "message": "Hobby created", "hobby": "Soccer", "category": "sports" }.
  • The hobby is added successfully, and the hobby list is refreshed.

2. Error Condition:

  • A user tries to add a hobby without a name or category.
  • The backend returns { "message": "Hobby name and category are required" }.
  • The user is alerted with the error message, and no changes are made to the hobby list.