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"
}