Sprint 5 Tech Talk Notes | SQLAlchemy Code | SQLConnect Code | API Tech Talk 2 Notes | Sprint 5 CRUD Review | Big Idea 4 Learnings | Final Exam Blog | 2020 MC Review | Personalized Project Repository |
Sprint 5 CRUD Contributions
What I Have Done as a Part of Sprint 5
Purpose of Programming
Our group is building a camping and national park review page. Users can:
- Take a personality test that assigns a national park to the user based on their personality
- Receive camping tips for the assigned location with chatroom feature for discussion
- Navigate to national park home page where user’s assigned national park is displayed
- Rate and review the national park assigned (after the user goes to the park)
My specific segment revolves around the following two tasks:
- Users can create a review for each individual national park
- Users can select an overall star rating for the parks they have been to
Input/Output Requests
Frontend Live Demo: I will display a submission of stars, how a user can fetch their previously-selected star posts, and delete their post if they wish to do so.
College Board Interaction Requirement:
- Fulfilled by user interaction
- Users work with frontend interface to save data to the backend database
Raw API Requests and Responses Demo:
Upon running db_init, restore, and backup, the data from the ratings table remains constant.
List Requests:
In my frontend code, JSON data from API is converted to DOM using the segment below. Here, the fetch data received from the backend gets converted into a certain number of stars to fill in the frontend.
This meets College Board requirements of collecting data and storing it in lists, as well as data abstraction to store a much higher volume of code.
async function fetchAndFillOverallStars(channelId) {
try {
// Fetch overall rating from the endpoint
const fetchingRatingData = {
user_id: userId,
channel_id: channelId
};
const response = await fetch(`${pythonURI}/api/rating`, {
...fetchOptions,
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(fetchingRatingData)
});
if (!response.ok) {
throw new Error('Failed to fetch overall rating: ' + response.statusText);
}
// Parse the response to get the star rating
const { stars } = await response.json();
// Find the existing stars for overall rating and fill them
document.querySelectorAll('.star[data-rating-type="overall"]').forEach((star, index) => {
star.style.color = (index < stars) ? '#ff0' : '#bbb'; // Fill stars with yellow for selected, grey for the rest
});
} catch (error) {
console.error('Error fetching overall stars:', error);
}
}
Extracting as Python List (row in the database) occurs during this fetch function as well. This software uses Flask to accomplish this task, seeing as the data associated with the user and the channel is selected (singular row and instance).
CRUD Methods
I created all four CRUD methods for my star review system.
def create(self):
"""Save the rating to the database."""
db.session.add(self)
db.session.commit()
def read(self):
"""Convert the rating object to a dictionary for JSON serialization."""
return {
"id": self.id,
"stars": self.stars,
"user_id": self.user_id,
"channel_id": self.channel_id,
"timestamp": self.timestamp.isoformat()
}
def update(self):
"""Update the rating in the database."""
db.session.add(self) # Explicitly add it to the session (mark it as modified)
db.session.commit()
def delete(self):
"""Delete the rating from the database."""
db.session.delete(self)
db.session.commit()
Algorithmic Code Request
Get, Post, Put, and Delete methods are also included in my Star API code.
class _RATING(Resource):
@token_required()
def post(self):
"""Handle both storing and fetching ratings."""
current_user = g.current_user
data = request.get_json()
# If 'stars' is in the request body, handle storing a rating
if 'stars' in data:
# Validate required fields
if not data or 'stars' not in data or 'channel_id' not in data:
return {'message': 'Missing required fields (stars, channel_id)'}, 400
stars = data['stars']
channel_id = data['channel_id']
# Validate stars
if not isinstance(stars, int) or stars < 1 or stars > 5:
return {'message': 'Invalid star rating. Must be an integer between 1 and 5.'}, 400
# Check if the channel exists
channel = Channel.query.get(channel_id)
if not channel:
return {'message': 'Channel not found'}, 404
# Create or update the rating
rating = Rating.query.filter_by(user_id=current_user.id, channel_id=channel.id).first()
if rating:
rating.stars = stars # Update the stars if the rating already exists
else:
rating = Rating(stars=stars, user_id=current_user.id, channel_id=channel.id)
db.session.add(rating)
db.session.commit()
return {'message': 'Rating submitted successfully', 'rating': rating.read()}, 201
# If 'stars' is NOT in the request body, assume it's a fetch request
elif 'user_id' in data and 'channel_id' in data:
user_id = data.get('user_id')
channel_id = data.get('channel_id')
# Validate request data
if not user_id or not channel_id:
return {'message': 'Missing user_id or channel_id in request body'}, 400
# If user_id is a string (e.g., a name like "toby"), map it to its ID
if isinstance(user_id, str): # If user_id is passed as a name
print(f"Searching for user with name: {user_id}")
user = Frostbyte.query.filter_by(_uid=user_id).first()
if not user:
print(f"User '{user_id}' not found in the database.")
return {'message': f'User "{user_id}" not found'}, 404
user_id = user.id
print(f"Found user: {user.name} with ID: {user.id}")
# Query the Rating table for the user's rating for the given channel
rating = Rating.query.filter_by(user_id=user_id, channel_id=channel_id).first()
if not rating:
return {'message': 'No rating found for the specified user and channel'}, 404
return jsonify({'stars': rating.stars})
# If neither case matches, return an error
return {'message': 'Invalid request'}, 400
@token_required()
def get(self):
"""Retrieve all ratings for a post."""
data = request.get_json()
if not data or 'channel_id' not in data:
return {'message': 'Channel ID is required'}, 400
ratings = Rating.query.filter_by(channel_id=data['channel_id']).all()
if not ratings:
return {'message': 'No ratings found for this channel'}, 404
return jsonify({
"ratings": [rating.read() for rating in ratings]
})
@token_required()
def delete(self):
"""Delete all ratings by a specific user."""
data = request.get_json()
user_id = data.get('user_id')
# Validate user_id
if not user_id:
return {'message': 'Missing user_id in request body'}, 400
# Query the User table to ensure the user exists
user = Frostbyte.query.filter_by(_uid=user_id).first() # Match by _uid
if not user:
return {'message': f'User "{user_id}" not found'}, 404
# Delete all ratings by the user
deleted_count = Rating.query.filter_by(user_id=user.id).delete()
db.session.commit()
if deleted_count == 0:
return {'message': 'No ratings found for the specified user'}, 404
return {'message': f'Deleted {deleted_count} rating(s) for user "{user_id}"'}, 200
Sequencing and Selection
This occurs within the GET request, as the program iterates many times through the database table to identify the stars, channels, and users associated with each submission.
JSONify occurs whenever the frontend must fetch a response from the backend, so the JSONIFY function is built into the features above.
Call to Algorithm Request
Endpoint fetches occur in the frontend scripts. Each new fetch retrieves updated code from the backend database.
Code Snippet Below:
import { pythonURI, fetchOptions } from '/risha_guha_2025_1/assets/js/api/config.js';
document.getElementById('postForm').addEventListener('submit', async function(event) {
event.preventDefault();
// Extract data from form
const title = document.getElementById('title').value;
const comment = document.getElementById('comment').value;
const groupId = document.getElementById('group_id').value;
const channelId = document.getElementById('channel_id').value;
// Create API payload
const postData = {
title: title,
comment: comment,
content: review_rating,
group_id: groupId,
channel_id: channelId
};
try {
// Send POST request to backend
const response = await fetch(`${pythonURI}/api/post`, {
...fetchOptions,
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(postData)
});
if (!response.ok) {
throw new Error('Failed to add post: ' + response.statusText);
}
alert('Post added successfully!');
document.getElementById('postForm').reset();
fetchData(channelId);
} catch (error) {
console.error('Error adding post:', error);
alert('Error adding post: ' + error.message);
}
});