Sprint 5 Dynamic Checkpoint Blog
layout: page title: Sprint 5 Dynamic Checkpoint Blog course: compsci week: 23 type: hacks —
Full Stack Feature Implementation: Interests Management
Purpose of the Program
The purpose of this program is to provide a full-stack solution for managing user interests. Users can add, update, and delete their interests through a web interface, and the changes are reflected in the backend database.
Purpose of the Feature
The feature allows users to:
- Add new interests
- Update existing interests
- Delete specific interests
Input/Output Requests
Demo: Input to Full Stack Feature
Using Frontend to Show API Request and Present API Response
<!-- filepath: /home/yashunix/nighthawk/prism/prism_frontend/navigation/authentication/profile.md -->
<script type="module">
import { pythonURI, fetchOptions } from '/yash_2025/assets/js/api/config.js';
// Function to update user interests
async function updateProfile(field, value) {
try {
if (field === 'interests' && value) {
const response = await fetch(pythonURI + "/api/user", fetchOptions);
const userData = await response.json();
const currentInterests = userData.interests ? userData.interests.split(',').map(i => i.trim()) : [];
const newInterests = value.split(',').map(i => i.trim());
const combinedInterests = [...new Set([...currentInterests, ...newInterests])];
value = combinedInterests.join(', ');
const updateResponse = await fetch(pythonURI + "/api/interests", {
...fetchOptions,
method: 'PUT',
body: JSON.stringify({ interests: value })
});
if (!updateResponse.ok) {
throw new Error('Failed to update interests');
}
showError('Interests updated successfully', 'green');
updateUserInfo();
return;
}
} catch (error) {
console.error('Error updating profile:', error);
showError('Error updating profile');
}
}
// Attach the updateProfile function to the window object
window.updateProfile = updateProfile;
</script>
Using Postman to Show Raw API Request and RESTful Response
POST Request to Add Interests:
- URL:
http://localhost:5000/api/interests
- Method:
POST
- Body:
{ "interests": "Reading, Coding, Hiking" }
Response:
- Status:
200 OK
- Body:
{ "message": "Interests added successfully", "interests": "Reading, Coding, Hiking" }
DELETE Request to Remove an Interest:
- URL:
http://localhost:5000/api/interests
- Method:
DELETE
- Body:
{ "interest": "Coding" }
Response:
- Status:
200 OK
- Body:
{ "message": "Interest deleted successfully", "interests": "Reading, Hiking" }
CRUD Postman Response
Using db_init
# filepath: /home/yashunix/nighthawk/prism/prism_backend/db_init.py
from __init__ import db
from model.user import User
# Initialize the database
db.create_all()
# Add sample data
user = User(name="John Doe", uid="johndoe", interests="Reading, Coding, Hiking")
db.session.add(user)
db.session.commit()
print("Database initialized with sample data.")
Using db_backup
# Backup the old database
def backup_database(db_uri, backup_uri):
"""Backup the current database."""
if backup_uri:
db_path = db_uri.replace('sqlite:///', 'instance/')
backup_path = backup_uri.replace('sqlite:///', 'instance/')
shutil.copyfile(db_path, backup_path)
print(f"Database backed up to {backup_path}")
else:
print("Backup not supported for production database.")
Using db_restore
@staticmethod
def restore(data):
users = {}
for user_data in data:
_ = user_data.pop('id', None) # Remove 'id' from user_data and store it in user_id
uid = user_data.get("uid", None)
user = User.query.filter_by(_uid=uid).first()
if user:
user.update(user_data)
else:
user = User(**user_data)
user.create()
return users
List Requests
Use of List, Dictionaries, and Database
Code Description:
- Lists: Used to manage user interests.
- Dictionaries: Used to handle JSON data in API requests and responses.
- Database: Used to store user data, including interests.
Formatting Response Data (JSON) from API into DOM:
// filepath: /home/yashunix/nighthawk/prism/prism_frontend/navigation/authentication/profile.md
function createInterestCards(interests) {
const interestsSection = document.getElementById('interestsSection');
interestsSection.innerHTML = '';
interests.forEach(interest => {
const card = document.createElement('div');
card.className = 'card';
card.innerHTML = `
<h4>${interest}</h4>
<img src="/yash_2025/assets/images/placeholder.png" alt="${interest}">
<button onclick="deleteInterest('${interest}')">Delete</button>
`;
interestsSection.appendChild(card);
});
}
Queries from Database: - These are provided by SQLAlchemy ORM, a third-party library, which simplifies the process of using Python to interact with databases.
The filter_by method, provided by SQLAlchemy, retrieves all interests associated with a UID (user id).
# filepath: /home/yashunix/nighthawk/prism/prism_backend/api/interests.py
@token_required()
def get(self):
"""
Return the interests of the authenticated user as a JSON object.
"""
current_user = g.current_user
user = User.query.filter_by(_uid=current_user.uid).first()
if not user or not user.interests:
return {'message': 'No interests found for this user'}, 404
return jsonify(user.interests)
Methods in Class to Work with Columns:
# filepath: /home/yashunix/nighthawk/prism/prism_backend/api/interests.py
@token_required()
def post(self):
"""
Create interests for the authenticated user.
"""
current_user = g.current_user
user = User.query.filter_by(_uid=current_user.uid).first()
if not user:
return {'message': 'User not found'}, 404
body = request.get_json()
new_interests = body.get('interests')
if not new_interests:
return {'message': 'No interests provided'}, 400
formatted_interests = re.sub(r'\s*,\s*', ', ', new_interests.strip())
user.interests = formatted_interests
user.update({'interests': user.interests})
return jsonify(user.interests)
Algorithmic Code Request
Definition of Code Blocks to Handle a Request
API Class to Perform CRUD Methods:
These methods handle the Create, Read, Update, and Delete operations for user interests. They interact with the database to manage the interests associated with a user. The methods are protected because they require a token to ensure that only authenticated users can access them.
# filepath: /home/yashunix/nighthawk/prism/prism_backend/api/interests.py
class _CRUD(Resource):
"""
Interests API operation for Create, Read, Update, Delete.
"""
@token_required()
def get(self):
"""
Return the interests of the authenticated user as a JSON object.
"""
current_user = g.current_user
user = User.query.filter_by(_uid=current_user.uid).first()
if not user or not user.interests:
return {'message': 'No interests found for this user'}, 404
return jsonify(user.interests)
@token_required()
def post(self):
"""
Create interests for the authenticated user.
"""
current_user = g.current_user
user = User.query.filter_by(_uid=current_user.uid).first()
if not user:
return {'message': 'User not found'}, 404
body = request.get_json()
new_interests = body.get('interests')
if not new_interests:
return {'message': 'No interests provided'}, 400
formatted_interests = re.sub(r'\s*,\s*', ', ', new_interests.strip())
user.interests = formatted_interests
user.update({'interests': user.interests})
return jsonify(user.interests)
@token_required()
def put(self):
"""
Update and add to the interests of the authenticated user.
"""
current_user = g.current_user
user = User.query.filter_by(_uid=current_user.uid).first()
if not user:
return {'message': 'User not found'}, 404
body = request.get_json()
new_interests = body.get('interests')
if not new_interests:
return {'message': 'No new interests provided'}, 400
formatted_new_interests = re.sub(r'\s*,\s*', ', ', new_interests.strip())
current_interests = user.interests.split(', ') if user.interests else []
combined_interests = list(set(current_interests + formatted_new_interests.split(', ')))
user.interests = ', '.join(combined_interests)
user.update({'interests': user.interests})
return jsonify(user.interests)
@token_required()
def delete(self):
"""
Delete a specified interest of the authenticated user.
"""
body = request.get_json()
if not body or 'interest' not in body:
return {'message': 'No interest provided'}, 400
current_user = g.current_user
interest_to_delete = body.get('interest')
interests = current_user.interests.split(', ')
if interest_to_delete not in interests:
return {'message': 'Interest not found'}, 404
interests.remove(interest_to_delete)
current_user.interests = ', '.join(interests)
current_user.update({'interests': current_user.interests})
return {'message': 'Interest deleted successfully'}, 200
CRUD Postman Response
Method/Procedure in Class with Sequencing, Selection, and Iteration
Example:
# filepath: /home/yashunix/nighthawk/prism/prism_backend/api/user.py
@token_required()
def put(self):
"""
Update a user.
"""
current_user = g.current_user
body = request.get_json()
if 'followers' in body:
new_followers = body['followers'].split(',')
valid_followers = []
for follower in new_followers:
follower = follower.strip()
if User.query.filter_by(_uid=follower).first():
valid_followers.append(follower)
else:
return {'message': f'Follower {follower} does not exist'}, 400
body['followers'] = ', '.join(valid_followers)
user.update(body)
return jsonify(user.read())
Parameters and Return Type
Parameters:
body
: JSON object containing the interest to be deleted.
Return Type:
- JSON response with a message indicating the result of the operation.
Call to Algorithm Request
Definition of Code Block to Make a Request
Frontend Fetch to Endpoint:
// filepath: /home/yashunix/nighthawk/prism/prism_frontend/navigation/authentication/profile.md
async function deleteInterest(interest) {
try {
const response = await fetch(pythonURI + "/api/interests", {
...fetchOptions,
method: 'DELETE',
body: JSON.stringify({ interest: interest })
});
if (!response.ok) {
throw new Error('Failed to delete interest');
}
showError('Interest deleted successfully', 'green');
updateUserInfo();
} catch (error) {
console.error('Error deleting interest:', error);
showError('Error deleting interest');
}
}
window.deleteInterest = deleteInterest;
Discuss the Call/Request to the Method with Algorithm
Call/Request:
- The
deleteInterest
function sends a DELETE request to the/api/interests
endpoint with the interest to be deleted.
Return/Response:
- The response from the backend is handled by checking the status code and updating the UI accordingly.
Handling Data and Error Conditions
Normal Conditions:
- The interest is successfully deleted, and the UI is updated to reflect the change.
Error Conditions:
- If the interest is not found or the request fails, an error message is displayed.
College Board CPT Requirements
The implementation of the full-stack feature for managing user interests meets the following College Board CPT requirements:
Requirement | A list or database | A procedure | A call to the procedure | Selection | Iteration |
---|---|---|---|---|---|
User Profile Map | X | X | X | X | X |
Conclusion
This blog demonstrates how to implement a full-stack feature for managing user interests. The feature includes adding, updating, and deleting interests, with proper handling of input/output requests, database interactions, and error conditions. By following the outlined steps, users can easily manage their interests through a web interface, with changes reflected in the backend database.