Skip to the content.

Full Stack Review

This is a blog for the full stack review

Collaboration

Group’s purpose

We wanted to create a travel planner with a varities of functions for the user: “To make a budget-friendly travel planner that will allow users to find activities, hotels, restaurants, and much more.”

It would include info for 5 different cities:

  • Paris
  • Hong Kong
  • New York
  • Mumbai
  • London

It would include these functionalities:

  • Activity Planner: users can search for activities in the city they are visiting and ask questions to a chatbot for help.
  • Lodging Listings: users can search for hotels in their city and give them a star rating.
  • Packing Portal: users can select which items to carry with them while travelling.
  • Cuisine Chronicles: users can view the menu of restaurants and give reviews.
  • Fair Fares: users can search for flights to and from their destination and compare prices.
  • Budget Brilliance: users can set their budget for the trip and see suggestions.
  • Wellness waypoints: users can search for hospitals in their area in the case of an emergency.

My purpose

I need to build a section of the website that helps the user plan their travel budget with functional and user-friendly frontend and dynamically updating APIs that connect to the backend. It will include these planned features.

  • Budget Plan Assister:
    • Will take in user inputed location, budget and any hard set attractions or travel items with predetermined price, user can then also input what they need help with finding (ex. hotels)
    • Outputs approximated remaining budget and will suggest things to do with remaining budget
    • integrates smoothly into the overall daily planning system. The user should be able to add budgeting concerns and details to their daily itinerary and travel plan
  • Pricing Reviews Posts:
    • Post API system that allows user to look at and make posts about a certain activity or place on a trip with a review on how much value said item was (1 to 5 stars)
  • Budget Guide:
    • Guide on how to budget in case users want to budget on their own
  • Budgeting form template:
    • Have a template and interface system which will allow user to format their budgeting plan neatly and share with others when they are done

List Requests

Formatting Response Data (JSON) from API into DOM

Example of JSON data from API:

[
    {"expense": "Museum visit", "cost": 25, "category": "Activities"},
    {"expense": "Hotel stay", "cost": 100, "category": "Hotels"},
    {"expense": "Dinner", "cost": 30, "category": "Food"}
]

Each row in the database is a list that contains key-value pairs. Each column in the database, which is the label like “expense” is a dictionary.

async function createBudgetingTable() {
    const table = document.getElementById("budgeting-table");
    table.innerHTML = ""; // Clear existing table content

    try {
        const response = await fetch(`${pythonURI}/api/budgeting`, fetchOptions);
        const data = await response.json();

        if (data.length === 0) {
            table.innerHTML = "<tr><td colspan='5'>No budgeting entries available.</td></tr>";
            return;
        }

        // Create table header
        const header = document.createElement("thead");
        header.innerHTML = `
            <tr>
                <th>Expense</th>
                <th>Cost</th>
                <th>Category</th>
                <th>User ID</th>
                <th>Actions</th>
            </tr>`;
        table.appendChild(header);

        // Create table body
        const body = document.createElement("tbody");
        data.forEach((entry, index) => {
            const row = document.createElement("tr");

            row.innerHTML = `
                <td>${entry.expense}</td>
                <td>${entry.cost}</td>
                <td>${entry.category}</td>
                <td>${entry.user_id}</td>
                <td>
                    <button class="action-btn" id="update-btn-${index}">Update</button>
                    <button class="action-btn" id="delete-btn-${index}">Delete</button>
                </td>
            `;

            body.appendChild(row);
        });
        table.appendChild(body);

This script creates column titles Expense, Cost, Category, User ID and takes the response data from the API (which is a list of budget entries) and dynamically creates rows for each entry in an HTML table, appending them to the table headers. For each entry, the row’s cells are populated with the expense, cost, category, and user_id fields from the API response.

Queries from Database: Extracting a Python List (Rows)

In the budgeting API, we interact with a database using SQLAlchemy, and we utilize queries to retrieve data stored in the database.

all_budgeting = Budgeting.query.all()
return jsonify([budgeting.read() for budgeting in all_budgeting])

budgeting = Budgeting.query.get(budgeting_id)

Budgeting.query.get(budgeting_id): This method retrieves a specific row from the budgeting table by its id. Budgeting.query.all() fetches all records (rows) from the budgeting table, jsonifies it.

Methods in Class to Work with Columns (CRUD Operations)

def post(self):
    data = request.get_json()

    if not data or 'expense' not in data or 'cost' not in data or 'category' not in data or 'user_id' not in data:
        return {'message': 'Expense, cost, category, and user_id are required'}, 400

    budgeting = Budgeting(
        expense=data.get('expense'),
        cost=data.get('cost'),
        category=data.get('category'),
        user_id=data.get('user_id')
    )

    try:
        budgeting.create()  # This saves the entry to the database
        return jsonify(budgeting.read())  # Return the newly created entry as JSON
    except Exception as e:
        return {'message': f'Error saving budgeting entry: {e}'}, 500

Post method extracts data from the JSON payload sent in the request body and saves the new record to the database.

def get(self):
    budgeting_id = request.args.get('id')

    if budgeting_id:
        budgeting = Budgeting.query.get(budgeting_id)
        if not budgeting:
            return {'message': 'Budgeting entry not found'}, 404
        return jsonify(budgeting.read())  # Return the specific entry as JSON

    all_budgeting = Budgeting.query.all()
    return jsonify([budgeting.read() for budgeting in all_budgeting])  # Return all entries as JSON

Get retrieves budgeting entries from the database, either search by id, or return all. Returns entries as a JSON list

def put(self):
    data = request.get_json()

    if not data or 'id' not in data:
        return {'message': 'ID is required for updating a budgeting entry'}, 400

    budgeting = Budgeting.query.get(data['id'])
    if not budgeting:
        return {'message': 'Budgeting entry not found'}, 404

    try:
        budgeting.update(data)  # This updates the entry in the database
        return jsonify(budgeting.read())  # Return the updated entry as JSON
    except Exception as e:
        return {'message': f'Error updating budgeting entry: {e}'}, 500

Update method updates an existing budgeting entry by searching for the id provided, looking for that row in the database, and updating the values with the new JSON data provided.

def delete(self):
    data = request.get_json()

    if not data or 'id' not in data:
        return {'message': 'ID is required for deleting a budgeting entry'}, 400

    budgeting = Budgeting.query.get(data['id'])
    if not budgeting:
        return {'message': 'Budgeting entry not found'}, 404

    try:
        budgeting.delete()  # This deletes the entry from the database
        return {'message': 'Budgeting entry deleted successfully'}, 200
    except Exception as e:
        return {'message': f'Error deleting budgeting entry: {e}'}, 500

Delete method required id for the particular row, then deletes it.

Algorithmic Code Request

Budgeting API Class:

class BudgetingAPI:

    class _CRUD(Resource):
        
        # POST: Create a new budgeting entry
        def post(self):
            data = request.get_json()  # Get data from request body

            # Validate the input data
            if not data or 'expense' not in data or 'cost' not in data or 'category' not in data or 'user_id' not in data:
                return {'message': 'Expense, cost, category, and user_id are required'}, 400

            # Create a new budgeting entry
            budgeting = Budgeting(
                expense=data.get('expense'),
                cost=data.get('cost'),
                category=data.get('category'),
                user_id=data.get('user_id')
            )

            try:
                budgeting.create()  # Save the entry to the database
                return jsonify(budgeting.read())  # Return the new entry as JSON
            except Exception as e:
                return {'message': f'Error saving budgeting entry: {e}'}, 500
        
        # GET: Retrieve budgeting entries
        def get(self):
            budgeting_id = request.args.get('id')  # Check for 'id' in query parameters

            if budgeting_id:
                # Get a specific budgeting entry by ID
                budgeting = Budgeting.query.get(budgeting_id)
                if not budgeting:
                    return {'message': 'Budgeting entry not found'}, 404
                return jsonify(budgeting.read())  # Return the entry as JSON

            # Get all budgeting entries if no specific ID is provided
            all_budgeting = Budgeting.query.all()
            return jsonify([budgeting.read() for budgeting in all_budgeting])  # Return all entries as JSON

        # PUT: Update an existing budgeting entry
        def put(self):
            data = request.get_json()  # Get data from request body

            if not data or 'id' not in data:
                return {'message': 'ID is required for updating a budgeting entry'}, 400

            budgeting = Budgeting.query.get(data['id'])  # Find the entry by ID
            if not budgeting:
                return {'message': 'Budgeting entry not found'}, 404

            try:
                budgeting.update(data)  # Update the entry with new data
                return jsonify(budgeting.read())  # Return the updated entry as JSON
            except Exception as e:
                return {'message': f'Error updating budgeting entry: {e}'}, 500

        # DELETE: Delete a budgeting entry
        def delete(self):
            data = request.get_json()  # Get data from request body

            if not data or 'id' not in data:
                return {'message': 'ID is required for deleting a budgeting entry'}, 400

            budgeting = Budgeting.query.get(data['id'])  # Find the entry by ID
            if not budgeting:
                return {'message': 'Budgeting entry not found'}, 404

            try:
                budgeting.delete()  # Delete the entry from the database
                return {'message': 'Budgeting entry deleted successfully'}, 200
            except Exception as e:
                return {'message': f'Error deleting budgeting entry: {e}'}, 500

    api.add_resource(_CRUD, '/budgeting')

All CRUD operations were explained above.

Get method is example of Sequencing, Selection, and Iteration Sequencing: The method starts by retrieving the id from the request parameters. Selection: It checks if an id is provided. If so, it retrieves a specific budgeting entry using Budgeting.query.get(). Iteration: If no id is provided, it fetches all entries and iterates over them using a list comprehension to return all entries in JSON format.

Parameters body: JSON data (shown above) Response: console.log { “message”: “Budget deleted successfully” }

Call To Algorithim Request

async function submitBudgeting(expense, cost, category) {
    try {
        const response = await fetch(`${pythonURI}/api/budgeting`, {
            ...fetchOptions,
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ expense, cost, category, user_id: 1 }),
        });

        if (response.ok) {
            createBudgetingTable(); // Refresh the table after adding a new entry
            updateRemainingBudget(); // Update the remaining budget
        } else {
            console.error('Failed to submit budgeting entry:', await response.json());
        }
    } catch (error) {
        console.error("Error creating new budgeting entry:", error);
    }
}

To create a new budgeting entry, a POST request is made to the /api/budgeting endpoint

document.getElementById('entryForm').addEventListener('submit', async function(e) {
    e.preventDefault();
    const category = document.getElementById('category').value;
    const expense = document.getElementById('expense').value;
    const cost = parseFloat(document.getElementById('entryAmount').value);

    if (expense && cost && category) {
        await submitBudgeting(expense, cost, category);
    } else {
        alert("Please enter valid data.");
    }
});

User inputs data into html form, which is then recived via eventlistener, and that data is jsonifed in submitBudgeitng function. Response: The server processes this data and creates a new budgeting entry. If successful, we call createBudgetingTable() to refresh the displayed table and updateRemainingBudget() to calculate the remaining budget. If there’s an error, the error is logged

async function fetchAndDisplayBudgeting() {
    try {
        const response = await fetch(`${pythonURI}/api/budgeting`, fetchOptions);
        const data = await response.json();

        const displayElement = document.getElementById('budgeting-display');
        if (data.length === 0) {
            displayElement.textContent = "No budgeting entries available.";
        } else {
            displayElement.textContent = "Budgeting Entries: ";
            data.forEach(entry => {
                displayElement.innerHTML += `<br>Expense: ${entry.expense}, Cost: ${entry.cost}, Category: ${entry.category}, User ID: ${entry.user_id}`;
            });
        }

        // Update the remaining budget
        updateRemainingBudget();
    } catch (error) {
        console.error("Error fetching budgeting entries:", error);
        document.getElementById('budgeting-display').textContent = "Failed to load budgeting entries.";
    }
}

GET request is sent for all entries. The response is expected to return a list of entries. We dynamically update the DOM by iterating through the data and displaying each entry. If there are no entries, we inform the user. Additionally, we call updateRemainingBudget() to update the remaining budget

async function updateBudgeting(id, expense, cost, category) {
    try {
        const response = await fetch(`${pythonURI}/api/budgeting`, {
            method: 'PUT',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ id, expense, cost, category, user_id: 1 }),
        });

        if (response.ok) {
            createBudgetingTable(); // Refresh the table after updating an entry
            updateRemainingBudget(); // Update the remaining budget
        } else {
            console.error('Failed to update budgeting entry:', await response.json());
        }
    } catch (error) {
        console.error("Error updating budgeting entry:", error);
    }
}

send a PUT request to the /api/budgeting endpoint with the id of the entry to be updated and the new data (expense, cost, category). If the update is successful, we refresh the table using createBudgetingTable() and update the remaining budget with updateRemainingBudget().

async function deleteBudgeting(id) {
    try {
        const response = await fetch(`${pythonURI}/api/budgeting`, {
            method: 'DELETE',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ id }),
        });

        if (response.ok) {
            createBudgetingTable(); // Refresh the table after deleting an entry
            updateRemainingBudget(); // Update the remaining budget
        } else {
            console.error('Failed to delete budgeting entry:', await response.json());
        }
    } catch (error) {
        console.error("Error deleting budgeting entry:", error);
    }
}

send a DELETE request to the /api/budgeting endpoint with the id of the entry to be deleted. If the delete operation is successful, the table is refreshed by calling createBudgetingTable() and the remaining budget is updated

Data Flow & Handling of Responses

Response Handling: Success: When the response is successful (response.ok), we update the DOM accordingly. For example, after creating a new entry, the table is refreshed, and the remaining budget is recalculated. After updating or deleting an entry, the table and remaining budget are updated as well.

Error Conditions: If there’s an issue with the request, the error is logged to the console, and we provide feedback to the user in the UI (e.g., “Failed to update budgeting entry”).

Example of successful request: POSTMAN 200 message

{
    "message": "Budgeting entry deleted successfully"
}

Example of failed request: POSTMAN 400 Bad Request message

{
    "message": "ID is required for deleting a budgeting entry"
}