Skip to content

Conversation

@TsasAL
Copy link
Owner

@TsasAL TsasAL commented Dec 1, 2025

This pull request introduces persistent storage for extracurricular activities, adds participant management features, and enhances both the frontend and backend to support viewing and removing activity participants. It also includes new styling for participant lists and adds automated tests to ensure correct behavior.

Backend Improvements:

  • Switched activity data from in-memory to persistent storage using a new activities.json file, with thread-safe load and save functions for reading and writing activities. (src/app.py, src/activities.json) [1] [2]
  • Added a new DELETE endpoint /activities/{activity_name}/unregister to allow unregistering a participant from an activity, updating the persistent store accordingly. (src/app.py)

Frontend Enhancements:

  • Displayed the list of participants for each activity, including a delete icon next to each participant. Clicking the icon triggers a request to remove the participant via the new backend endpoint. (src/static/app.js)
  • Automatically refreshes the activities list after a successful signup or participant removal to reflect changes in real time. (src/static/app.js)

Styling Updates:

  • Added new CSS styles for participant lists, delete icons, and the "no participants" state to improve the user interface. (src/static/styles.css)

Testing:

  • Introduced automated tests for activity listing, signup, and unregister functionality to ensure API correctness. (tests/test_app.py)

@TsasAL TsasAL requested a review from Copilot December 1, 2025 12:20
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request migrates the student activity registration system from in-memory storage to persistent file-based storage and adds participant management capabilities. The changes enable administrators to view and remove participants from activities through an enhanced UI with real-time updates.

Key Changes:

  • Implemented persistent storage using activities.json with thread-safe read/write operations
  • Added DELETE endpoint for unregistering participants with proper validation
  • Enhanced frontend to display participant lists with delete functionality and automatic list refresh

Reviewed changes

Copilot reviewed 5 out of 6 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/app.py Replaced in-memory storage with file-based persistence, added thread-safe load/save functions, implemented unregister endpoint with duplicate signup validation
src/activities.json New persistent data file containing all activity information and participant lists
src/static/app.js Added participant list rendering with delete icons and event handlers, implemented automatic refresh after signup/removal
src/static/styles.css Added styling for participant sections, delete buttons, and empty state display
tests/test_app.py New test suite covering activity listing, signup, and unregister functionality

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +30 to +33
def load_activities():
with _activities_lock:
with open(DATA_FILE, "r", encoding="utf-8") as f:
return json.load(f)
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The load_activities() function should handle the case where the data file doesn't exist or contains invalid JSON. Without error handling, the application will crash on startup if activities.json is missing or corrupted. Consider adding a try-except block to return default data or create the file if it doesn't exist.

Copilot uses AI. Check for mistakes.
Comment on lines +57 to +78
setTimeout(() => {
activityCard.querySelectorAll('.delete-participant').forEach((icon) => {
icon.addEventListener('click', async (e) => {
const activity = icon.getAttribute('data-activity');
const email = icon.getAttribute('data-email');
if (!confirm(`Remove ${email} from ${activity}?`)) return;
try {
const response = await fetch(`/activities/${encodeURIComponent(activity)}/unregister?email=${encodeURIComponent(email)}`, {
method: 'DELETE',
});
const result = await response.json();
if (response.ok) {
fetchActivities();
} else {
alert(result.detail || 'Failed to remove participant.');
}
} catch (err) {
alert('Failed to remove participant.');
}
});
});
}, 0);
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using setTimeout with 0ms delay is unnecessary here since the elements are already in the DOM after innerHTML assignment. The event listeners can be attached immediately without the setTimeout wrapper, improving code clarity and reducing potential timing issues.

Suggested change
setTimeout(() => {
activityCard.querySelectorAll('.delete-participant').forEach((icon) => {
icon.addEventListener('click', async (e) => {
const activity = icon.getAttribute('data-activity');
const email = icon.getAttribute('data-email');
if (!confirm(`Remove ${email} from ${activity}?`)) return;
try {
const response = await fetch(`/activities/${encodeURIComponent(activity)}/unregister?email=${encodeURIComponent(email)}`, {
method: 'DELETE',
});
const result = await response.json();
if (response.ok) {
fetchActivities();
} else {
alert(result.detail || 'Failed to remove participant.');
}
} catch (err) {
alert('Failed to remove participant.');
}
});
});
}, 0);
activityCard.querySelectorAll('.delete-participant').forEach((icon) => {
icon.addEventListener('click', async (e) => {
const activity = icon.getAttribute('data-activity');
const email = icon.getAttribute('data-email');
if (!confirm(`Remove ${email} from ${activity}?`)) return;
try {
const response = await fetch(`/activities/${encodeURIComponent(activity)}/unregister?email=${encodeURIComponent(email)}`, {
method: 'DELETE',
});
const result = await response.json();
if (response.ok) {
fetchActivities();
} else {
alert(result.detail || 'Failed to remove participant.');
}
} catch (err) {
alert('Failed to remove participant.');
}
});
});

Copilot uses AI. Check for mistakes.
Comment on lines +58 to +59
if email in activity["participants"]:
raise HTTPException(status_code=400, detail="Student already signed up for this activity")
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The duplicate signup validation logic added in the signup endpoint lacks test coverage. Consider adding a test case that attempts to sign up the same email twice and verifies that a 400 error is returned with the appropriate error message.

Copilot uses AI. Check for mistakes.
# Add student
if email in activity["participants"]:
raise HTTPException(status_code=400, detail="Student already signed up for this activity")
activity["participants"].append(email)
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The signup endpoint doesn't validate whether the activity has reached max_participants. Participants can be added beyond the maximum capacity, making the 'spots left' display in the UI inaccurate. Add a check to compare len(activity['participants']) against activity['max_participants'] before appending.

Copilot uses AI. Check for mistakes.
@TsasAL TsasAL merged commit 7753d74 into main Dec 1, 2025
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants