From a1c0e85891980a732035f04f164903773d533c0a Mon Sep 17 00:00:00 2001 From: Fleur Gahimbare Date: Mon, 5 Feb 2024 16:31:23 -0500 Subject: [PATCH 01/58] push branch --- app/templates/main/serviceTranscript.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/templates/main/serviceTranscript.html b/app/templates/main/serviceTranscript.html index be54fc663..b1a1b1cd8 100644 --- a/app/templates/main/serviceTranscript.html +++ b/app/templates/main/serviceTranscript.html @@ -75,3 +75,5 @@
No Volunteer Record
{% endblock %} + + From 3c4eb34578d8b53c72bec4c3e41ed41f16f78b40 Mon Sep 17 00:00:00 2001 From: Fleur Gahimbare Date: Mon, 5 Feb 2024 18:05:30 -0500 Subject: [PATCH 02/58] add checkbox in modal --- app/static/js/userProfile.js | 7 +++++++ app/templates/main/userProfile.html | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/app/static/js/userProfile.js b/app/static/js/userProfile.js index 8c7214f78..41a08a324 100644 --- a/app/static/js/userProfile.js +++ b/app/static/js/userProfile.js @@ -304,3 +304,10 @@ function updateManagers(el, volunteer_username ){// retrieve the data of the stu }) } +function handleCheckbox() { + var removeFromTranscript = document.getElementById('removeFromTranscriptCheckbox').checked; + // You can now use this variable in your logic or send it to the backend. +} + + + diff --git a/app/templates/main/userProfile.html b/app/templates/main/userProfile.html index 36a6f4621..a82afba6a 100644 --- a/app/templates/main/userProfile.html +++ b/app/templates/main/userProfile.html @@ -560,6 +560,10 @@
Reason for ban:
End Date:  
+
+ + +
From 6c848b4f6d199940e8a656a2d4771a0a2a02762d Mon Sep 17 00:00:00 2001 From: Karina Agliullova Date: Wed, 7 Feb 2024 17:01:49 -0500 Subject: [PATCH 03/58] Added functionality for removing banned users from transcript, still working on it --- app/logic/users.py | 13 ++++++++++++ app/static/js/userProfile.js | 31 ++++++++++++++++++++++------- app/templates/main/userProfile.html | 20 +++++++++---------- 3 files changed, 47 insertions(+), 17 deletions(-) diff --git a/app/logic/users.py b/app/logic/users.py index 745f5a83a..a22d8db88 100644 --- a/app/logic/users.py +++ b/app/logic/users.py @@ -5,6 +5,7 @@ from app.models.note import Note from app.models.user import User from app.models.profileNote import ProfileNote +from app.models.programBan import ProgramBan from app.models.backgroundCheck import BackgroundCheck from app.models.backgroundCheckType import BackgroundCheckType from app.logic.volunteers import addUserBackgroundCheck @@ -51,6 +52,18 @@ def removeUserInterest(program_id, username): interestToDelete.delete_instance() return True +def removeBannedUserFromTranscript(program_id, username): + """ + This function is used to add an interest to . + Parameters: + program_id: id of the program the user is interested in + username: username of the user showing interest + """ + removeBannedUser = ProgramBan.get_or_none(ProgramBan.user == username) + if removeBannedUser: + removeBannedUser.delete_instance() + return True + def getBannedUsers(program): """ This function returns users banned from a program. diff --git a/app/static/js/userProfile.js b/app/static/js/userProfile.js index 41a08a324..961c9934c 100644 --- a/app/static/js/userProfile.js +++ b/app/static/js/userProfile.js @@ -8,7 +8,7 @@ $(document).ready(function(){ $(".notifyInput").click(function updateInterest(){ var programID = $(this).data("programid"); var username = $(this).data('username'); - + var interest = $(this).is(':checked'); var routeUrl = interest ? "addInterest" : "removeInterest"; @@ -26,6 +26,26 @@ $(document).ready(function(){ }); }); + $(".removeFromTranscriptCheckbox").click(function updateCheckbox(){ + var programID = $(this).data("programid"); + var username = $(this).data('username'); + + var removeCheckbox = $(this).is(':checked'); + var routeUrl = removeCheckbox ? "removeBannedUserFromTranscript" : "removeBannedUserFromTranscript"; + removeCheckboxUrl = "/" + username + "/" + routeUrl + "/" + programID ; + $.ajax({ + method: "POST", + url: removeCheckboxUrl, + success: function(response) { + reloadWithAccordion("programTable") // Reloading page after user clicks on the show interest checkbox + }, + error: function(request, status, error) { + console.log(status,error); + location.reload(); + } + }); + }); + function changeAction(e){ let profileAction = $(this).val() let username = $(this).data('username') @@ -131,9 +151,9 @@ $(document).ready(function(){ $("#noteModal").modal("toggle") }); - $("#addVisibility").click(function() { + $("#addVisibility").click(function() { var bonnerChecked = $("input[name='bonner']:checked").val() - + if (bonnerChecked == 'on') { bonnerNoteOn() } else { @@ -181,7 +201,7 @@ $(document).ready(function(){ * Background Check Functionality */ // Updates the Background check of a volunteer in the database - $(".savebtn").click(function () { + $(".savebtn").click(function () { $(this).prop("disabled", true); let bgCheckType = $(this).data("id") @@ -308,6 +328,3 @@ function handleCheckbox() { var removeFromTranscript = document.getElementById('removeFromTranscriptCheckbox').checked; // You can now use this variable in your logic or send it to the backend. } - - - diff --git a/app/templates/main/userProfile.html b/app/templates/main/userProfile.html index a82afba6a..db935d97a 100644 --- a/app/templates/main/userProfile.html +++ b/app/templates/main/userProfile.html @@ -25,7 +25,7 @@

{{volunteer.firstName}} {{volunteer.lastName}}

{{volunteer.major}}
{% endif %} {% if volunteer.classLevel -%} -
{{volunteer.classLevel}}
+
{{volunteer.classLevel}}
{% endif %}
@@ -51,7 +51,7 @@

{{volunteer.firstName}} {{volunteer.lastName}}

- +
@@ -220,7 +220,7 @@

CELTS Labor History:
{% for program, term in participatedInLabor.items() %} -

{{term}}: {{program}}

+

{{term}}: {{program}}

{% endfor %}
{% endif %} @@ -502,9 +502,9 @@

- -
+ +
{% if g.current_user.isBonnerScholar and volunteer.isBonnerScholar %}
@@ -522,7 +522,7 @@
{% endif %}
- +
@@ -561,9 +561,9 @@
Reason for ban:
End Date:  
- + -
+
From fa020dda745c287a10dd94bacfa88c877c125b08 Mon Sep 17 00:00:00 2001 From: Fleur Gahimbare Date: Fri, 9 Feb 2024 16:40:34 -0500 Subject: [PATCH 04/58] add function to handle checkbox --- app/controllers/main/routes.py | 14 +++++++++++++ app/logic/transcript.py | 37 ++++++++++++++++++++++++++++++---- app/static/js/userProfile.js | 19 +++++++++++++++-- 3 files changed, 64 insertions(+), 6 deletions(-) diff --git a/app/controllers/main/routes.py b/app/controllers/main/routes.py index 513f1f6d2..f38119b3a 100644 --- a/app/controllers/main/routes.py +++ b/app/controllers/main/routes.py @@ -447,6 +447,20 @@ def serviceTranscript(username): startDate = startDate, userData = user) +@app.route('/update-transcript', methods=['POST']) +def update_transcript(): + # Get the data sent from the client-side JavaScript + data = request.json + + # Get the username and removeFromTranscript values from the request data + username = data.get('username') + removeFromTranscript = data.get('removeFromTranscript') + + # Return username and removeFromTranscript + response_data = {'username': username, 'removeFromTranscript': removeFromTranscript} + return jsonify(response_data) + + @main_bp.route('/searchUser/', methods = ['GET']) def searchUser(query): diff --git a/app/logic/transcript.py b/app/logic/transcript.py index 7dfb75cf8..a3a39a268 100644 --- a/app/logic/transcript.py +++ b/app/logic/transcript.py @@ -9,25 +9,54 @@ from app.models.eventParticipant import EventParticipant from app.models.event import Event -def getProgramTranscript(username): +from app.logic.users import isEligibleForProgram + + +# def getProgramTranscript(username): +# """ +# Returns a Program query object containing all the programs for +# current user. +# """ +# # Add up hours earned in a term for each program they've participated in + +# EventData = (Event.select(Event, fn.SUM(EventParticipant.hoursEarned).alias("hoursEarned")) +# .join(EventParticipant) +# .where(EventParticipant.user == username) +# .group_by(Event.program, Event.term) +# .order_by(Event.term) +# .having(fn.SUM(EventParticipant.hoursEarned > 0))) +# transcriptData = {} +# for event in EventData: +# if event.program in transcriptData: +# transcriptData[event.program].append([event.term.description, event.hoursEarned]) +# else: +# transcriptData[event.program] = [[event.term.description, event.hoursEarned]] +# return transcriptData + +def getProgramTranscript(username, removeFromTranscript=False): """ Returns a Program query object containing all the programs for - current user. + current user. Optionally removes programs based on the checkbox status. """ - # Add up hours earned in a term for each program they've participated in - EventData = (Event.select(Event, fn.SUM(EventParticipant.hoursEarned).alias("hoursEarned")) .join(EventParticipant) .where(EventParticipant.user == username) .group_by(Event.program, Event.term) .order_by(Event.term) .having(fn.SUM(EventParticipant.hoursEarned > 0))) + transcriptData = {} for event in EventData: if event.program in transcriptData: transcriptData[event.program].append([event.term.description, event.hoursEarned]) else: transcriptData[event.program] = [[event.term.description, event.hoursEarned]] + + if removeFromTranscript: + for program in list(transcriptData.keys()): + if not isEligibleForProgram(program, username): + transcriptData.pop(program) + return transcriptData def getSlCourseTranscript(username): diff --git a/app/static/js/userProfile.js b/app/static/js/userProfile.js index 41a08a324..825c6d2b8 100644 --- a/app/static/js/userProfile.js +++ b/app/static/js/userProfile.js @@ -36,6 +36,7 @@ $(document).ready(function(){ } else if(profileAction == "Print Travel Form"){ printDocument(`/profile/${username}/travelForm`) } else if (profileAction == "View Service Transcript"){ + handleCheckbox(username); window.location.href = `/profile/${username}/serviceTranscript` } else if (profileAction == "Manage CCE Minor") { window.location.href = `/profile/${username}/cceMinor` @@ -305,9 +306,23 @@ function updateManagers(el, volunteer_username ){// retrieve the data of the stu } function handleCheckbox() { - var removeFromTranscript = document.getElementById('removeFromTranscriptCheckbox').checked; - // You can now use this variable in your logic or send it to the backend. + var removeFromTranscript = $('#removeFromTranscriptCheckbox').prop('checked'); // Using jQuery to get the checkbox state + var username = $(this).data('username'); + + $.ajax({ + type: "POST", + url: "/update-transcript", + contentType: "application/json", + data: JSON.stringify({ username: username, removeFromTranscript: removeFromTranscript }), + success: function(updatedTranscriptData) { + console.log(updatedTranscriptData); + }, + error: function(xhr, status, error) { + console.error("An error occurred:", error); + } + }); } + From 4b1d9eebae743660ba336e38015c241e4538d584 Mon Sep 17 00:00:00 2001 From: Karina Agliullova Date: Tue, 13 Feb 2024 14:21:34 -0500 Subject: [PATCH 05/58] Fixed merge conflict --- app/static/js/userProfile.js | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/app/static/js/userProfile.js b/app/static/js/userProfile.js index ed678dc38..ddbe72d6c 100644 --- a/app/static/js/userProfile.js +++ b/app/static/js/userProfile.js @@ -328,7 +328,7 @@ function updateManagers(el, volunteer_username ){// retrieve the data of the stu function handleCheckbox() { var removeFromTranscript = $('#removeFromTranscriptCheckbox').prop('checked'); // Using jQuery to get the checkbox state var username = $(this).data('username'); - + $.ajax({ type: "POST", url: "/update-transcript", @@ -342,10 +342,3 @@ function handleCheckbox() { } }); } -<<<<<<< HEAD - - - - -======= ->>>>>>> 6c848b4f6d199940e8a656a2d4771a0a2a02762d From 793e87b6b5086be338bb171fd7c7edd8f188aeee Mon Sep 17 00:00:00 2001 From: Fleur Gahimbare Date: Mon, 19 Feb 2024 18:04:54 -0500 Subject: [PATCH 06/58] add remove from transcript field --- app/controllers/main/routes.py | 75 +++++++++++++++++++++++++++++ app/logic/transcript.py | 51 ++++++++++++++++++-- app/models/programBan.py | 3 ++ app/static/js/userProfile.js | 42 ++++++++++++++-- app/templates/main/userProfile.html | 4 ++ 5 files changed, 165 insertions(+), 10 deletions(-) diff --git a/app/controllers/main/routes.py b/app/controllers/main/routes.py index 513f1f6d2..e21a59a7e 100644 --- a/app/controllers/main/routes.py +++ b/app/controllers/main/routes.py @@ -447,6 +447,81 @@ def serviceTranscript(username): startDate = startDate, userData = user) +@main_bp.route('/profile//updateTranscript/', methods=['POST']) +def update_transcript(username, program_id): + # Check user permissions + user = User.get_or_none(User.username == username) + if user is None: + abort(404) + if user != g.current_user and not g.current_user.isAdmin: + abort(403) + + # Get the data sent from the client-side JavaScript + data = request.json + + # Retrieve removeFromTranscript value from the request data + removeFromTranscript = data.get('removeFromTranscript') + + # Update the ProgramBan object matching the program_id and username + try: + program_ban = ProgramBan.get((ProgramBan.program == program_id) & (ProgramBan.user == user)) + program_ban.removeFromTranscript = removeFromTranscript + program_ban.save() + return jsonify({'status': 'success'}) + except ProgramBan.DoesNotExist: + return jsonify({'status': 'error', 'message': 'ProgramBan not found'}) + + + +# @main_bp.route('/profile//serviceTranscript', methods=['GET', 'POST']) +# def service_transcript(username): +# if request.method == 'GET': +# user = User.get_or_none(User.username == username) +# if user is None: +# abort(404) +# if user != g.current_user and not g.current_user.isAdmin: +# abort(403) + +# # Retrieve stored values from session +# removeFromTranscript = session.get('removeFromTranscript', False) +# programID = session.get('programID', None) + +# slCourses = getSlCourseTranscript(username) +# totalHours = getTotalHours(username) +# startDate = getStartYear(username) +# allEventTranscript = getProgramTranscript(username, removeFromTranscript, programID) +# return render_template('main/serviceTranscript.html', +# allEventTranscript=allEventTranscript, +# slCourses=slCourses.objects(), +# totalHours=totalHours, +# startDate=startDate, +# userData=user) + +# elif request.method == 'POST': +# # Get the data sent from the client-side JavaScript +# data = request.json + +# # Set default value for removeFromTranscript +# removeFromTranscript = False + +# # Get the removeFromTranscript value from the request data +# if data: +# removeFromTranscript = data.get('removeFromTranscript') +# programID = data.get('programID') # Retrieve programID from the POST request + +# # Store values in session +# session['removeFromTranscript'] = removeFromTranscript +# session['programID'] = programID + +# # Call getProgramTranscript with the received data +# allEventTranscript = getProgramTranscript(username, removeFromTranscript, programID) + +# # Return any response data if necessary +# response_data = {'status': 'success'} +# return jsonify(response_data) + + + @main_bp.route('/searchUser/', methods = ['GET']) def searchUser(query): diff --git a/app/logic/transcript.py b/app/logic/transcript.py index 7dfb75cf8..3ec546999 100644 --- a/app/logic/transcript.py +++ b/app/logic/transcript.py @@ -8,11 +8,12 @@ from app.models.term import Term from app.models.eventParticipant import EventParticipant from app.models.event import Event +from app.models.programBan import ProgramBan def getProgramTranscript(username): """ Returns a Program query object containing all the programs for - current user. + the current user. """ # Add up hours earned in a term for each program they've participated in @@ -22,14 +23,54 @@ def getProgramTranscript(username): .group_by(Event.program, Event.term) .order_by(Event.term) .having(fn.SUM(EventParticipant.hoursEarned > 0))) + + # Fetch all ProgramBan objects for the user + program_bans = ProgramBan.select().where(ProgramBan.user == username) + + # Create a set of program IDs to remove from transcript + programs_to_remove = {program_ban.program_id for program_ban in program_bans if program_ban.removeFromTranscript} + + # Initialize transcriptData dictionary transcriptData = {} + + # Iterate through EventData and populate transcriptData for event in EventData: - if event.program in transcriptData: - transcriptData[event.program].append([event.term.description, event.hoursEarned]) - else: - transcriptData[event.program] = [[event.term.description, event.hoursEarned]] + if event.program not in programs_to_remove: # Check if program should be included + if event.program in transcriptData: + transcriptData[event.program].append([event.term.description, event.hoursEarned]) + else: + transcriptData[event.program] = [[event.term.description, event.hoursEarned]] + return transcriptData + +# def getProgramTranscript(username): +# """ +# Returns a Program query object containing all the programs for +# current user. If removeFromTranscript is True, remove the corresponding program. +# """ +# # Retrieve transcript data as before +# EventData = (Event.select(Event, fn.SUM(EventParticipant.hoursEarned).alias("hoursEarned")) +# .join(EventParticipant) +# .where(EventParticipant.user == username) +# .group_by(Event.program, Event.term) +# .order_by(Event.term) +# .having(fn.SUM(EventParticipant.hoursEarned > 0))) + +# transcriptData = {} +# for event in EventData: +# # Check if removeFromTranscript is True and if the programID matches the current event's program ID +# if removeFromTranscript and programID and event.program.id == programID: +# continue # Skip this event if it matches the conditions +# # Only include the program if removeFromTranscript is False or if it's eligible +# if not removeFromTranscript: +# if event.program in transcriptData: +# transcriptData[event.program].append([event.term.description, event.hoursEarned]) +# else: +# transcriptData[event.program] = [[event.term.description, event.hoursEarned]] +# return transcriptData + + def getSlCourseTranscript(username): """ Returns a SLCourse query object containing all the training events for diff --git a/app/models/programBan.py b/app/models/programBan.py index 45a58d064..f94c6100d 100644 --- a/app/models/programBan.py +++ b/app/models/programBan.py @@ -9,3 +9,6 @@ class ProgramBan(baseModel): endDate = DateField(null=True) banNote = ForeignKeyField(Note, null=False) unbanNote = ForeignKeyField(Note, null=True) + removeFromTranscript = BooleanField(default=False) + + diff --git a/app/static/js/userProfile.js b/app/static/js/userProfile.js index 8c7214f78..1afc39e79 100644 --- a/app/static/js/userProfile.js +++ b/app/static/js/userProfile.js @@ -8,7 +8,7 @@ $(document).ready(function(){ $(".notifyInput").click(function updateInterest(){ var programID = $(this).data("programid"); var username = $(this).data('username'); - + var interest = $(this).is(':checked'); var routeUrl = interest ? "addInterest" : "removeInterest"; @@ -26,6 +26,33 @@ $(document).ready(function(){ }); }); +$('.removeFromTranscriptCheckbox').click(function updateShowOnTranscriptStatus() { + var removeFromTranscript = $(this).is(':checked'); + console.log('Checkbox state:', removeFromTranscript); + var username = $(this).data('username'); + console.log(username); + console.log(programID); + + + $.ajax({ + type: "POST", + url: `/profile/${username}/updateTranscript/${programID}`, + contentType: "application/json", + data: JSON.stringify({ username: username, removeFromTranscript: removeFromTranscript, programID: programID }), + success: function(response) { + console.log(response); + // Handle success response if needed + }, + error: function(error) { + console.error("An error occurred:", error); + // Handle error if needed + } + }); +}); + + + + function changeAction(e){ let profileAction = $(this).val() let username = $(this).data('username') @@ -42,6 +69,8 @@ $(document).ready(function(){ } $(this).val('') } + + // This function is to disable all the dates before current date in the ban modal End Date picker $(function(){ @@ -57,6 +86,7 @@ $(document).ready(function(){ /* * Ban Functionality */ + var programID; // Declare programID variable outside of the function scope $(".ban").click(function() { var banButton = $("#banButton") var banEndDateDiv = $("#banEndDate") // Div containing the datepicker in the ban modal @@ -66,7 +96,7 @@ $(document).ready(function(){ var banNote = $("#banNote") banButton.text($(this).val() + " Volunteer"); - banButton.data("programID", $(this).data("programid")) + programID = $(this).data("programid"); // Assign value to programID variable banButton.data("username", $("#notifyInput").data("username")) banButton.data("banOrUnban", $(this).val()); banEndDateDiv.show(); @@ -131,9 +161,9 @@ $(document).ready(function(){ $("#noteModal").modal("toggle") }); - $("#addVisibility").click(function() { + $("#addVisibility").click(function() { var bonnerChecked = $("input[name='bonner']:checked").val() - + if (bonnerChecked == 'on') { bonnerNoteOn() } else { @@ -162,6 +192,7 @@ $(document).ready(function(){ reloadWithAccordion(target) } }); + }); $(".deleteNoteButton").click(function() { @@ -181,7 +212,7 @@ $(document).ready(function(){ * Background Check Functionality */ // Updates the Background check of a volunteer in the database - $(".savebtn").click(function () { + $(".savebtn").click(function () { $(this).prop("disabled", true); let bgCheckType = $(this).data("id") @@ -304,3 +335,4 @@ function updateManagers(el, volunteer_username ){// retrieve the data of the stu }) } + diff --git a/app/templates/main/userProfile.html b/app/templates/main/userProfile.html index 36a6f4621..3b65ae8fd 100644 --- a/app/templates/main/userProfile.html +++ b/app/templates/main/userProfile.html @@ -560,6 +560,10 @@
Reason for ban:
End Date:  
+
+ + +
From 3205cf746137d65c53047fa62389fda18da6c3a7 Mon Sep 17 00:00:00 2001 From: Karina Agliullova Date: Tue, 20 Feb 2024 23:31:32 -0500 Subject: [PATCH 07/58] Fixed logic --- app/controllers/main/routes.py | 84 +++++++--------------------------- app/logic/transcript.py | 9 ++-- 2 files changed, 23 insertions(+), 70 deletions(-) diff --git a/app/controllers/main/routes.py b/app/controllers/main/routes.py index e21a59a7e..c9efb62fe 100644 --- a/app/controllers/main/routes.py +++ b/app/controllers/main/routes.py @@ -54,7 +54,7 @@ def landingPage(): .distinct() .execute()) # Ensure only unique programs are included - return render_template("/main/landingPage.html", + return render_template("/main/landingPage.html", managerProgramDict=managerProgramDict, term=g.current_term, programsWithEventsList=programsWithEventsList) @@ -72,20 +72,20 @@ def events(selectedTerm, activeTab, programID): if selectedTerm: currentTerm = selectedTerm currentTime = datetime.datetime.now() - + listOfTerms = Term.select() participantRSVP = EventRsvp.select(EventRsvp, Event).join(Event).where(EventRsvp.user == g.current_user) rsvpedEventsID = [event.event.id for event in participantRSVP] - term = Term.get_by_id(currentTerm) - + term = Term.get_by_id(currentTerm) + currentEventRsvpAmount = getEventRsvpCountsForTerm(term) studentLedEvents = getStudentLedEvents(term) countUpcomingStudentLedEvents = getUpcomingStudentLedCount(term, currentTime) trainingEvents = getTrainingEvents(term, g.current_user) bonnerEvents = getBonnerEvents(term) otherEvents = getOtherEvents(term) - + managersProgramDict = getManagerProgramDict(g.current_user) return render_template("/events/event_list.html", @@ -145,7 +145,7 @@ def viewUsersProfile(username): ProgramBan.program == program, ProgramBan.endDate > datetime.datetime.now()).execute()) userParticipatedTrainingEvents = getParticipationStatusForTrainings(program, [volunteer], g.current_term) - try: + try: allTrainingsComplete = False not in [attended for event, attended in userParticipatedTrainingEvents[username]] # Did volunteer attend all events except KeyError: allTrainingsComplete = False @@ -162,7 +162,7 @@ def viewUsersProfile(username): managersProgramDict = getManagerProgramDict(g.current_user) managersList = [id[1] for id in managersProgramDict.items()] participatedInLabor = getCeltsLaborHistory(volunteer) - + return render_template ("/main/userProfile.html", programs = programs, programsInterested = programsInterested, @@ -199,7 +199,7 @@ def emergencyContactInfo(username): contactInfo=contactInfo, readOnly=readOnly ) - + elif request.method == 'POST': if g.current_user.username != username: abort(403) @@ -208,8 +208,8 @@ def emergencyContactInfo(username): if not rowsUpdated: EmergencyContact.create(user = username, **request.form) createAdminLog(f"{g.current_user} updated {username}'s emergency contact information.") - flash('Emergency contact information saved successfully!', 'success') - + flash('Emergency contact information saved successfully!', 'success') + if request.args.get('action') == 'exit': return redirect (f"/profile/{username}") else: @@ -241,7 +241,7 @@ def insuranceInfo(username): if not rowsUpdated: InsuranceInfo.create(user = username, **request.form) createAdminLog(f"{g.current_user} updated {username}'s emergency contact information.") - flash('Insurance information saved successfully!', 'success') + flash('Insurance information saved successfully!', 'success') if request.args.get('action') == 'exit': return redirect (f"/profile/{username}") @@ -252,7 +252,7 @@ def insuranceInfo(username): def travelForm(username): if not (g.current_user.username == username or g.current_user.isCeltsAdmin): abort(403) - + user = (User.select(User, EmergencyContact, InsuranceInfo) .join(EmergencyContact, JOIN.LEFT_OUTER).switch() .join(InsuranceInfo, JOIN.LEFT_OUTER) @@ -447,7 +447,7 @@ def serviceTranscript(username): startDate = startDate, userData = user) -@main_bp.route('/profile//updateTranscript/', methods=['POST']) +@main_bp.route('/profile//updateTranscript/', methods=['POST']) def update_transcript(username, program_id): # Check user permissions user = User.get_or_none(User.username == username) @@ -464,7 +464,7 @@ def update_transcript(username, program_id): # Update the ProgramBan object matching the program_id and username try: - program_ban = ProgramBan.get((ProgramBan.program == program_id) & (ProgramBan.user == user)) + program_ban = ProgramBan.get(ProgramBan.program == program_id, ProgramBan.user == user) program_ban.removeFromTranscript = removeFromTranscript program_ban.save() return jsonify({'status': 'success'}) @@ -472,56 +472,6 @@ def update_transcript(username, program_id): return jsonify({'status': 'error', 'message': 'ProgramBan not found'}) - -# @main_bp.route('/profile//serviceTranscript', methods=['GET', 'POST']) -# def service_transcript(username): -# if request.method == 'GET': -# user = User.get_or_none(User.username == username) -# if user is None: -# abort(404) -# if user != g.current_user and not g.current_user.isAdmin: -# abort(403) - -# # Retrieve stored values from session -# removeFromTranscript = session.get('removeFromTranscript', False) -# programID = session.get('programID', None) - -# slCourses = getSlCourseTranscript(username) -# totalHours = getTotalHours(username) -# startDate = getStartYear(username) -# allEventTranscript = getProgramTranscript(username, removeFromTranscript, programID) -# return render_template('main/serviceTranscript.html', -# allEventTranscript=allEventTranscript, -# slCourses=slCourses.objects(), -# totalHours=totalHours, -# startDate=startDate, -# userData=user) - -# elif request.method == 'POST': -# # Get the data sent from the client-side JavaScript -# data = request.json - -# # Set default value for removeFromTranscript -# removeFromTranscript = False - -# # Get the removeFromTranscript value from the request data -# if data: -# removeFromTranscript = data.get('removeFromTranscript') -# programID = data.get('programID') # Retrieve programID from the POST request - -# # Store values in session -# session['removeFromTranscript'] = removeFromTranscript -# session['programID'] = programID - -# # Call getProgramTranscript with the received data -# allEventTranscript = getProgramTranscript(username, removeFromTranscript, programID) - -# # Return any response data if necessary -# response_data = {'status': 'success'} -# return jsonify(response_data) - - - @main_bp.route('/searchUser/', methods = ['GET']) def searchUser(query): @@ -559,12 +509,12 @@ def getDietInfo(): dietaryInfo = request.form user = dietaryInfo["user"] dietInfo = dietaryInfo["dietInfo"] - + if (g.current_user.username == user) or g.current_user.isAdmin: updateDietInfo(user, dietInfo) - userInfo = User.get(User.username == user) + userInfo = User.get(User.username == user) if len(dietInfo) > 0: - createAdminLog(f"Updated {userInfo.fullName}'s dietary restrictions to {dietInfo}.") if dietInfo.strip() else None + createAdminLog(f"Updated {userInfo.fullName}'s dietary restrictions to {dietInfo}.") if dietInfo.strip() else None else: createAdminLog(f"Deleted all {userInfo.fullName}'s dietary restrictions dietary restrictions.") diff --git a/app/logic/transcript.py b/app/logic/transcript.py index 3ec546999..f1622af8f 100644 --- a/app/logic/transcript.py +++ b/app/logic/transcript.py @@ -16,7 +16,7 @@ def getProgramTranscript(username): the current user. """ # Add up hours earned in a term for each program they've participated in - + EventData = (Event.select(Event, fn.SUM(EventParticipant.hoursEarned).alias("hoursEarned")) .join(EventParticipant) .where(EventParticipant.user == username) @@ -29,13 +29,16 @@ def getProgramTranscript(username): # Create a set of program IDs to remove from transcript programs_to_remove = {program_ban.program_id for program_ban in program_bans if program_ban.removeFromTranscript} - + print("---------------------------------------", programs_to_remove) # Initialize transcriptData dictionary transcriptData = {} # Iterate through EventData and populate transcriptData for event in EventData: - if event.program not in programs_to_remove: # Check if program should be included + if event.program.id not in programs_to_remove: # Check if program should be included + print(event.program in programs_to_remove, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@") + print('+++++++++++++++++++++++++++++++', event.program) + print(type(event.program.id), "??????????????????????????????????????????????????") if event.program in transcriptData: transcriptData[event.program].append([event.term.description, event.hoursEarned]) else: From f17f91be7ada5f67ccbe00c82ec9870449e1aee8 Mon Sep 17 00:00:00 2001 From: Fleur Gahimbare Date: Fri, 23 Feb 2024 18:07:29 -0500 Subject: [PATCH 08/58] fix checkbox functionality, only show on banned modals --- app/controllers/main/routes.py | 12 +++------ app/logic/transcript.py | 40 +++-------------------------- app/static/js/userProfile.js | 30 ++++++++++++---------- app/templates/main/userProfile.html | 4 +-- 4 files changed, 27 insertions(+), 59 deletions(-) diff --git a/app/controllers/main/routes.py b/app/controllers/main/routes.py index c9efb62fe..f930d9bf0 100644 --- a/app/controllers/main/routes.py +++ b/app/controllers/main/routes.py @@ -1,4 +1,4 @@ -from flask import request, render_template, g, abort, flash, redirect, url_for +from flask import request, render_template, g, abort, flash, redirect, url_for, jsonify from peewee import JOIN from playhouse.shortcuts import model_to_dict import datetime @@ -449,24 +449,20 @@ def serviceTranscript(username): @main_bp.route('/profile//updateTranscript/', methods=['POST']) def update_transcript(username, program_id): - # Check user permissions user = User.get_or_none(User.username == username) if user is None: abort(404) if user != g.current_user and not g.current_user.isAdmin: abort(403) - # Get the data sent from the client-side JavaScript data = request.json - - # Retrieve removeFromTranscript value from the request data removeFromTranscript = data.get('removeFromTranscript') # Update the ProgramBan object matching the program_id and username try: - program_ban = ProgramBan.get(ProgramBan.program == program_id, ProgramBan.user == user) - program_ban.removeFromTranscript = removeFromTranscript - program_ban.save() + programBan = ProgramBan.get(ProgramBan.program == program_id, ProgramBan.user == user) + programBan.removeFromTranscript = removeFromTranscript + programBan.save() return jsonify({'status': 'success'}) except ProgramBan.DoesNotExist: return jsonify({'status': 'error', 'message': 'ProgramBan not found'}) diff --git a/app/logic/transcript.py b/app/logic/transcript.py index f1622af8f..07dd35afa 100644 --- a/app/logic/transcript.py +++ b/app/logic/transcript.py @@ -25,20 +25,16 @@ def getProgramTranscript(username): .having(fn.SUM(EventParticipant.hoursEarned > 0))) # Fetch all ProgramBan objects for the user - program_bans = ProgramBan.select().where(ProgramBan.user == username) + BannedProgramsForParticipant = ProgramBan.select().where(ProgramBan.user == username) # Create a set of program IDs to remove from transcript - programs_to_remove = {program_ban.program_id for program_ban in program_bans if program_ban.removeFromTranscript} - print("---------------------------------------", programs_to_remove) - # Initialize transcriptData dictionary + programsToremoveFromTranscript = {bannedProgram.program_id for bannedProgram in BannedProgramsForParticipant if bannedProgram.removeFromTranscript} + print("---------------------------------------", programsToremoveFromTranscript) transcriptData = {} # Iterate through EventData and populate transcriptData for event in EventData: - if event.program.id not in programs_to_remove: # Check if program should be included - print(event.program in programs_to_remove, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@") - print('+++++++++++++++++++++++++++++++', event.program) - print(type(event.program.id), "??????????????????????????????????????????????????") + if event.program.id not in programsToremoveFromTranscript: # Check if program is not in programs to be removed from transcript if event.program in transcriptData: transcriptData[event.program].append([event.term.description, event.hoursEarned]) else: @@ -46,34 +42,6 @@ def getProgramTranscript(username): return transcriptData - -# def getProgramTranscript(username): -# """ -# Returns a Program query object containing all the programs for -# current user. If removeFromTranscript is True, remove the corresponding program. -# """ -# # Retrieve transcript data as before -# EventData = (Event.select(Event, fn.SUM(EventParticipant.hoursEarned).alias("hoursEarned")) -# .join(EventParticipant) -# .where(EventParticipant.user == username) -# .group_by(Event.program, Event.term) -# .order_by(Event.term) -# .having(fn.SUM(EventParticipant.hoursEarned > 0))) - -# transcriptData = {} -# for event in EventData: -# # Check if removeFromTranscript is True and if the programID matches the current event's program ID -# if removeFromTranscript and programID and event.program.id == programID: -# continue # Skip this event if it matches the conditions -# # Only include the program if removeFromTranscript is False or if it's eligible -# if not removeFromTranscript: -# if event.program in transcriptData: -# transcriptData[event.program].append([event.term.description, event.hoursEarned]) -# else: -# transcriptData[event.program] = [[event.term.description, event.hoursEarned]] -# return transcriptData - - def getSlCourseTranscript(username): """ Returns a SLCourse query object containing all the training events for diff --git a/app/static/js/userProfile.js b/app/static/js/userProfile.js index 1afc39e79..b653a9707 100644 --- a/app/static/js/userProfile.js +++ b/app/static/js/userProfile.js @@ -26,13 +26,17 @@ $(document).ready(function(){ }); }); -$('.removeFromTranscriptCheckbox').click(function updateShowOnTranscriptStatus() { - var removeFromTranscript = $(this).is(':checked'); - console.log('Checkbox state:', removeFromTranscript); - var username = $(this).data('username'); - console.log(username); - console.log(programID); - + var isChecked = localStorage.getItem("removeFromTranscriptChecked"); + $(".removeFromTranscriptCheckbox").prop("checked", isChecked === "true"); + + // Add event listener to save checkbox state on change + $('.removeFromTranscriptCheckbox').click(function() { + var removeFromTranscript = $(this).is(':checked'); + var username = $(this).data('username'); + + // Save checkbox state to local storage + localStorage.setItem("removeFromTranscriptChecked", removeFromTranscript); + $.ajax({ type: "POST", @@ -41,18 +45,15 @@ $('.removeFromTranscriptCheckbox').click(function updateShowOnTranscriptStatus() data: JSON.stringify({ username: username, removeFromTranscript: removeFromTranscript, programID: programID }), success: function(response) { console.log(response); - // Handle success response if needed + // Handle success }, error: function(error) { console.error("An error occurred:", error); - // Handle error if needed + // Handle error } }); }); - - - function changeAction(e){ let profileAction = $(this).val() let username = $(this).data('username') @@ -86,7 +87,7 @@ $('.removeFromTranscriptCheckbox').click(function updateShowOnTranscriptStatus() /* * Ban Functionality */ - var programID; // Declare programID variable outside of the function scope + var programID; // Declare programID variable outside of the function scope to be used for removeFromTranscript checkbox $(".ban").click(function() { var banButton = $("#banButton") var banEndDateDiv = $("#banEndDate") // Div containing the datepicker in the ban modal @@ -97,6 +98,7 @@ $('.removeFromTranscriptCheckbox').click(function updateShowOnTranscriptStatus() banButton.text($(this).val() + " Volunteer"); programID = $(this).data("programid"); // Assign value to programID variable + banButton.data("programID", $(this).data("programid")) banButton.data("username", $("#notifyInput").data("username")) banButton.data("banOrUnban", $(this).val()); banEndDateDiv.show(); @@ -105,12 +107,14 @@ $('.removeFromTranscriptCheckbox').click(function updateShowOnTranscriptStatus() $("#modalProgramName").text("Program: " + $(this).data("name ")); $("#banModal").modal("toggle"); banNoteDiv.hide(); + $("#removeFromTranscriptDiv").hide(); $("#banNoteTxtArea").val(""); $("#banButton").prop("disabled", true); if( $(this).val()=="Unban"){ banEndDateDiv.hide() banEndDatepicker.val("0001-01-01") //This is a placeholder value for the if statement in line 52 to work properly #PLCHLD1 banNoteDiv.show() + $("#removeFromTranscriptDiv").show(); banNote.text($(this).data("note")) } diff --git a/app/templates/main/userProfile.html b/app/templates/main/userProfile.html index 41b48cc35..c53b30ddc 100644 --- a/app/templates/main/userProfile.html +++ b/app/templates/main/userProfile.html @@ -560,13 +560,13 @@
Reason for ban:
End Date:  
-
+
- +
From f9506eb56cf5b36dcf93fc92d1131c7b24d3ae1b Mon Sep 17 00:00:00 2001 From: Fleur Gahimbare Date: Fri, 23 Feb 2024 18:14:09 -0500 Subject: [PATCH 09/58] remove print --- app/controllers/main/routes.py | 4 ++-- app/logic/transcript.py | 1 - app/templates/main/userProfile.html | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/app/controllers/main/routes.py b/app/controllers/main/routes.py index f930d9bf0..0c6e983b2 100644 --- a/app/controllers/main/routes.py +++ b/app/controllers/main/routes.py @@ -448,7 +448,7 @@ def serviceTranscript(username): userData = user) @main_bp.route('/profile//updateTranscript/', methods=['POST']) -def update_transcript(username, program_id): +def updateTranscript(username, program_id): user = User.get_or_none(User.username == username) if user is None: abort(404) @@ -465,7 +465,7 @@ def update_transcript(username, program_id): programBan.save() return jsonify({'status': 'success'}) except ProgramBan.DoesNotExist: - return jsonify({'status': 'error', 'message': 'ProgramBan not found'}) + return jsonify({'status': 'error'}) @main_bp.route('/searchUser/', methods = ['GET']) diff --git a/app/logic/transcript.py b/app/logic/transcript.py index 07dd35afa..5acbb0959 100644 --- a/app/logic/transcript.py +++ b/app/logic/transcript.py @@ -29,7 +29,6 @@ def getProgramTranscript(username): # Create a set of program IDs to remove from transcript programsToremoveFromTranscript = {bannedProgram.program_id for bannedProgram in BannedProgramsForParticipant if bannedProgram.removeFromTranscript} - print("---------------------------------------", programsToremoveFromTranscript) transcriptData = {} # Iterate through EventData and populate transcriptData diff --git a/app/templates/main/userProfile.html b/app/templates/main/userProfile.html index c53b30ddc..1ef5870aa 100644 --- a/app/templates/main/userProfile.html +++ b/app/templates/main/userProfile.html @@ -566,7 +566,7 @@
End Date: &nbs
- +
From c69653ff9b4502e17c076fd8970a0b8aa1ef778d Mon Sep 17 00:00:00 2001 From: Karina Agliullova Date: Tue, 5 Mar 2024 18:36:22 -0500 Subject: [PATCH 10/58] Fixing merge conflicts on transcript.py --- app/logic/transcript.py | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/app/logic/transcript.py b/app/logic/transcript.py index c5dc200a9..67be4fd59 100644 --- a/app/logic/transcript.py +++ b/app/logic/transcript.py @@ -19,7 +19,7 @@ # current user. # """ # # Add up hours earned in a term for each program they've participated in - + # EventData = (Event.select(Event, fn.SUM(EventParticipant.hoursEarned).alias("hoursEarned")) # .join(EventParticipant) # .where(EventParticipant.user == username) @@ -37,54 +37,47 @@ def getProgramTranscript(username, removeFromTranscript=False): """ Returns a Program query object containing all the programs for -<<<<<<< HEAD the current user. """ # Add up hours earned in a term for each program they've participated in -======= - current user. Optionally removes programs based on the checkbox status. - """ ->>>>>>> 4b1d9eebae743660ba336e38015c241e4538d584 + # current user. Optionally removes programs based on the checkbox status. + # """ + EventData = (Event.select(Event, fn.SUM(EventParticipant.hoursEarned).alias("hoursEarned")) .join(EventParticipant) .where(EventParticipant.user == username) .group_by(Event.program, Event.term) .order_by(Event.term) .having(fn.SUM(EventParticipant.hoursEarned > 0))) -<<<<<<< HEAD # Fetch all ProgramBan objects for the user BannedProgramsForParticipant = ProgramBan.select().where(ProgramBan.user == username) # Create a set of program IDs to remove from transcript programsToremoveFromTranscript = {bannedProgram.program_id for bannedProgram in BannedProgramsForParticipant if bannedProgram.removeFromTranscript} -======= - ->>>>>>> 4b1d9eebae743660ba336e38015c241e4538d584 + transcriptData = {} # Iterate through EventData and populate transcriptData for event in EventData: -<<<<<<< HEAD - if event.program.id not in programsToremoveFromTranscript: # Check if program is not in programs to be removed from transcript - if event.program in transcriptData: - transcriptData[event.program].append([event.term.description, event.hoursEarned]) - else: - transcriptData[event.program] = [[event.term.description, event.hoursEarned]] - -======= + # if event.program.id not in programsToremoveFromTranscript: # Check if program is not in programs to be removed from transcript + # if event.program in transcriptData: + # transcriptData[event.program].append([event.term.description, event.hoursEarned]) + # else: + # transcriptData[event.program] = [[event.term.description, event.hoursEarned]] + + if event.program in transcriptData: transcriptData[event.program].append([event.term.description, event.hoursEarned]) else: transcriptData[event.program] = [[event.term.description, event.hoursEarned]] - + if removeFromTranscript: for program in list(transcriptData.keys()): if not isEligibleForProgram(program, username): transcriptData.pop(program) ->>>>>>> 4b1d9eebae743660ba336e38015c241e4538d584 return transcriptData def getSlCourseTranscript(username): From b5e9366e1f9632f517a413c8b8adb6101625f5be Mon Sep 17 00:00:00 2001 From: Karina Agliullova Date: Tue, 5 Mar 2024 23:00:54 -0500 Subject: [PATCH 11/58] Made changes to keep checkbox consistent --- app/controllers/main/routes.py | 1036 ++++++++++++------------ app/logic/transcript.py | 215 ++--- app/static/js/userProfile.js | 724 ++++++++--------- app/templates/main/userProfile.html | 1171 +++++++++++++-------------- 4 files changed, 1527 insertions(+), 1619 deletions(-) diff --git a/app/controllers/main/routes.py b/app/controllers/main/routes.py index 0c6e983b2..2fea65c68 100644 --- a/app/controllers/main/routes.py +++ b/app/controllers/main/routes.py @@ -1,518 +1,518 @@ -from flask import request, render_template, g, abort, flash, redirect, url_for, jsonify -from peewee import JOIN -from playhouse.shortcuts import model_to_dict -import datetime -import json -from http import cookies - -from app import app -from app.models.program import Program -from app.models.event import Event -from app.models.backgroundCheck import BackgroundCheck -from app.models.backgroundCheckType import BackgroundCheckType -from app.models.user import User -from app.models.eventParticipant import EventParticipant -from app.models.interest import Interest -from app.models.programBan import ProgramBan -from app.models.term import Term -from app.models.eventRsvp import EventRsvp -from app.models.note import Note -from app.models.profileNote import ProfileNote -from app.models.programManager import ProgramManager -from app.models.courseInstructor import CourseInstructor -from app.models.certification import Certification -from app.models.emergencyContact import EmergencyContact -from app.models.insuranceInfo import InsuranceInfo -from app.models.celtsLabor import CeltsLabor - -from app.controllers.main import main_bp -from app.logic.loginManager import logout -from app.logic.users import addUserInterest, removeUserInterest, banUser, unbanUser, isEligibleForProgram, getUserBGCheckHistory, addProfileNote, deleteProfileNote, updateDietInfo -from app.logic.participants import unattendedRequiredEvents, trainedParticipants, getParticipationStatusForTrainings, checkUserRsvp, addPersonToEvent -from app.logic.events import * -from app.logic.searchUsers import searchUsers -from app.logic.transcript import * -from app.logic.landingPage import getManagerProgramDict, getActiveEventTab -from app.logic.utils import selectSurroundingTerms -from app.logic.certification import getCertRequirementsWithCompletion -from app.logic.createLogs import createRsvpLog, createAdminLog -from app.logic.celtsLabor import getCeltsLaborHistory - -@main_bp.route('/logout', methods=['GET']) -def redirectToLogout(): - return redirect(logout()) - -@main_bp.route('/', methods=['GET']) -def landingPage(): - - managerProgramDict = getManagerProgramDict(g.current_user) - - # Optimize the query to fetch programs with non-canceled, non-past events in the current term - programsWithEventsList = list(Program.select(Program, Event) - .join(Event) - .where((Event.term == g.current_term) and (Event.isCanceled == False) and (Event.isPast == False)) - .distinct() - .execute()) # Ensure only unique programs are included - - return render_template("/main/landingPage.html", - managerProgramDict=managerProgramDict, - term=g.current_term, - programsWithEventsList=programsWithEventsList) - - -@main_bp.route('/goToEventsList/', methods=['GET']) -def goToEventsList(programID): - return {"activeTab": getActiveEventTab(programID)} - -@main_bp.route('/eventsList/', methods=['GET'], defaults={'activeTab': "studentLedEvents", 'programID': 0}) -@main_bp.route('/eventsList//', methods=['GET'], defaults={'programID': 0}) -@main_bp.route('/eventsList///', methods=['GET']) -def events(selectedTerm, activeTab, programID): - currentTerm = g.current_term - if selectedTerm: - currentTerm = selectedTerm - currentTime = datetime.datetime.now() - - listOfTerms = Term.select() - participantRSVP = EventRsvp.select(EventRsvp, Event).join(Event).where(EventRsvp.user == g.current_user) - rsvpedEventsID = [event.event.id for event in participantRSVP] - - term = Term.get_by_id(currentTerm) - - currentEventRsvpAmount = getEventRsvpCountsForTerm(term) - studentLedEvents = getStudentLedEvents(term) - countUpcomingStudentLedEvents = getUpcomingStudentLedCount(term, currentTime) - trainingEvents = getTrainingEvents(term, g.current_user) - bonnerEvents = getBonnerEvents(term) - otherEvents = getOtherEvents(term) - - managersProgramDict = getManagerProgramDict(g.current_user) - - return render_template("/events/event_list.html", - selectedTerm = term, - studentLedEvents = studentLedEvents, - trainingEvents = trainingEvents, - bonnerEvents = bonnerEvents, - otherEvents = otherEvents, - listOfTerms = listOfTerms, - rsvpedEventsID = rsvpedEventsID, - currentEventRsvpAmount = currentEventRsvpAmount, - currentTime = currentTime, - user = g.current_user, - activeTab = activeTab, - programID = int(programID), - managersProgramDict = managersProgramDict, - countUpcomingStudentLedEvents = countUpcomingStudentLedEvents - ) - -@main_bp.route('/profile/', methods=['GET']) -def viewUsersProfile(username): - """ - This function displays the information of a volunteer to the user - """ - try: - volunteer = User.get(User.username == username) - except Exception as e: - if g.current_user.isAdmin: - flash(f"{username} does not exist! ", category='danger') - return redirect(url_for('admin.studentSearchPage')) - else: - abort(403) # Error 403 if non admin/student-staff user trys to access via url - - if (g.current_user == volunteer) or g.current_user.isAdmin: - upcomingEvents = getUpcomingEventsForUser(volunteer) - participatedEvents = getParticipatedEventsForUser(volunteer) - programs = Program.select() - if not g.current_user.isBonnerScholar and not g.current_user.isAdmin: - programs = programs.where(Program.isBonnerScholars == False) - interests = Interest.select(Interest, Program).join(Program).where(Interest.user == volunteer) - programsInterested = [interest.program for interest in interests] - - rsvpedEventsList = EventRsvp.select(EventRsvp, Event).join(Event).where(EventRsvp.user == volunteer) - rsvpedEvents = [event.event.id for event in rsvpedEventsList] - - programManagerPrograms = ProgramManager.select(ProgramManager, Program).join(Program).where(ProgramManager.user == volunteer) - permissionPrograms = [entry.program.id for entry in programManagerPrograms] - - allBackgroundHistory = getUserBGCheckHistory(volunteer) - backgroundTypes = list(BackgroundCheckType.select()) - - eligibilityTable = [] - for program in programs: - banNotes = list(ProgramBan.select(ProgramBan, Note) - .join(Note, on=(ProgramBan.banNote == Note.id)) - .where(ProgramBan.user == volunteer, - ProgramBan.program == program, - ProgramBan.endDate > datetime.datetime.now()).execute()) - userParticipatedTrainingEvents = getParticipationStatusForTrainings(program, [volunteer], g.current_term) - try: - allTrainingsComplete = False not in [attended for event, attended in userParticipatedTrainingEvents[username]] # Did volunteer attend all events - except KeyError: - allTrainingsComplete = False - noteForDict = banNotes[-1].banNote.noteContent if banNotes else "" - eligibilityTable.append({"program": program, - "completedTraining": allTrainingsComplete, - "trainingList": userParticipatedTrainingEvents, - "isNotBanned": (not banNotes), - "banNote": noteForDict}) - profileNotes = ProfileNote.select().where(ProfileNote.user == volunteer) - - bonnerRequirements = getCertRequirementsWithCompletion(certification=Certification.BONNER, username=volunteer) - - managersProgramDict = getManagerProgramDict(g.current_user) - managersList = [id[1] for id in managersProgramDict.items()] - participatedInLabor = getCeltsLaborHistory(volunteer) - - return render_template ("/main/userProfile.html", - programs = programs, - programsInterested = programsInterested, - upcomingEvents = upcomingEvents, - participatedEvents = participatedEvents, - rsvpedEvents = rsvpedEvents, - permissionPrograms = permissionPrograms, - eligibilityTable = eligibilityTable, - volunteer = volunteer, - backgroundTypes = backgroundTypes, - allBackgroundHistory = allBackgroundHistory, - currentDateTime = datetime.datetime.now(), - profileNotes = profileNotes, - bonnerRequirements = bonnerRequirements, - managersList = managersList, - participatedInLabor = participatedInLabor, - ) - abort(403) - -@main_bp.route('/profile//emergencyContact', methods=['GET', 'POST']) -def emergencyContactInfo(username): - """ - This loads the Emergency Contact Page - """ - if not (g.current_user.username == username or g.current_user.isCeltsAdmin): - abort(403) - - - if request.method == 'GET': - readOnly = g.current_user.username != username - contactInfo = EmergencyContact.get_or_none(EmergencyContact.user_id == username) - return render_template ("/main/emergencyContactInfo.html", - username=username, - contactInfo=contactInfo, - readOnly=readOnly - ) - - elif request.method == 'POST': - if g.current_user.username != username: - abort(403) - - rowsUpdated = EmergencyContact.update(**request.form).where(EmergencyContact.user == username).execute() - if not rowsUpdated: - EmergencyContact.create(user = username, **request.form) - createAdminLog(f"{g.current_user} updated {username}'s emergency contact information.") - flash('Emergency contact information saved successfully!', 'success') - - if request.args.get('action') == 'exit': - return redirect (f"/profile/{username}") - else: - return redirect (f"/profile/{username}/insuranceInfo") - -@main_bp.route('/profile//insuranceInfo', methods=['GET', 'POST']) -def insuranceInfo(username): - """ - This loads the Insurance Information Page - """ - if not (g.current_user.username == username or g.current_user.isCeltsAdmin): - abort(403) - - if request.method == 'GET': - readOnly = g.current_user.username != username - userInsuranceInfo = InsuranceInfo.get_or_none(InsuranceInfo.user == username) - return render_template ("/main/insuranceInfo.html", - username=username, - userInsuranceInfo=userInsuranceInfo, - readOnly=readOnly - ) - - # Save the form data - elif request.method == 'POST': - if g.current_user.username != username: - abort(403) - - rowsUpdated = InsuranceInfo.update(**request.form).where(InsuranceInfo.user == username).execute() - if not rowsUpdated: - InsuranceInfo.create(user = username, **request.form) - createAdminLog(f"{g.current_user} updated {username}'s emergency contact information.") - flash('Insurance information saved successfully!', 'success') - - if request.args.get('action') == 'exit': - return redirect (f"/profile/{username}") - else: - return redirect (f"/profile/{username}/emergencyContact") - -@main_bp.route('/profile//travelForm', methods=['GET', 'POST']) -def travelForm(username): - if not (g.current_user.username == username or g.current_user.isCeltsAdmin): - abort(403) - - user = (User.select(User, EmergencyContact, InsuranceInfo) - .join(EmergencyContact, JOIN.LEFT_OUTER).switch() - .join(InsuranceInfo, JOIN.LEFT_OUTER) - .where(User.username == username).limit(1)) - if not list(user): - abort(404) - userData = list(user.dicts())[0] - userData = {key: value if value else '' for (key, value) in userData.items()} - - return render_template ('/main/travelForm.html', - userData = userData - ) - - -@main_bp.route('/profile/addNote', methods=['POST']) -def addNote(): - """ - This function adds a note to the user's profile. - """ - postData = request.form - try: - note = addProfileNote(postData["visibility"], postData["bonner"] == "yes", postData["noteTextbox"], postData["username"]) - flash("Successfully added profile note", "success") - return redirect(url_for("main.viewUsersProfile", username=postData["username"])) - except Exception as e: - print("Error adding note", e) - flash("Failed to add profile note", "danger") - return "Failed to add profile note", 500 - -@main_bp.route('//deleteNote', methods=['POST']) -def deleteNote(username): - """ - This function deletes a note from the user's profile. - """ - try: - deleteProfileNote(request.form["id"]) - flash("Successfully deleted profile note", "success") - except Exception as e: - print("Error deleting note", e) - flash("Failed to delete profile note", "danger") - return "success" - -# ===========================Ban=============================================== -@main_bp.route('//ban/', methods=['POST']) -def ban(program_id, username): - """ - This function updates the ban status of a username either when they are banned from a program. - program_id: the primary id of the program the student is being banned from - username: unique value of a user to correctly identify them - """ - postData = request.form - banNote = postData["note"] # This contains the note left about the change - banEndDate = postData["endDate"] # Contains the date the ban will no longer be effective - try: - banUser(program_id, username, banNote, banEndDate, g.current_user) - programInfo = Program.get(int(program_id)) - flash("Successfully banned the volunteer", "success") - createAdminLog(f'Banned {username} from {programInfo.programName} until {banEndDate}.') - return "Successfully banned the volunteer." - except Exception as e: - print("Error while updating ban", e) - flash("Failed to ban the volunteer", "danger") - return "Failed to ban the volunteer", 500 - -# ===========================Unban=============================================== -@main_bp.route('//unban/', methods=['POST']) -def unban(program_id, username): - """ - This function updates the ban status of a username either when they are unbanned from a program. - program_id: the primary id of the program the student is being unbanned from - username: unique value of a user to correctly identify them - """ - postData = request.form - unbanNote = postData["note"] # This contains the note left about the change - try: - unbanUser(program_id, username, unbanNote, g.current_user) - programInfo = Program.get(int(program_id)) - createAdminLog(f'Unbanned {username} from {programInfo.programName}.') - flash("Successfully unbanned the volunteer", "success") - return "Successfully unbanned the volunteer" - - except Exception as e: - print("Error while updating Unban", e) - flash("Failed to unban the volunteer", "danger") - return "Failed to unban the volunteer", 500 - - -@main_bp.route('//addInterest/', methods=['POST']) -def addInterest(program_id, username): - """ - This function adds a program to the list of programs a user interested in - program_id: the primary id of the program the student is adding interest of - username: unique value of a user to correctly identify them - """ - try: - success = addUserInterest(program_id, username) - if success: - flash("Successfully added " + Program.get_by_id(program_id).programName + " as an interest", "success") - return "" - else: - flash("Was unable to remove " + Program.get_by_id(program_id).programName + " as an interest.", "danger") - - except Exception as e: - print(e) - return "Error Updating Interest", 500 - -@main_bp.route('//removeInterest/', methods=['POST']) -def removeInterest(program_id, username): - """ - This function removes a program to the list of programs a user interested in - program_id: the primary id of the program the student is adding interest of - username: unique value of a user to correctly identify them - """ - try: - removed = removeUserInterest(program_id, username) - if removed: - flash("Successfully removed " + Program.get_by_id(program_id).programName + " as an interest.", "success") - return "" - else: - flash("Was unable to remove " + Program.get_by_id(program_id).programName + " as an interest.", "danger") - except Exception as e: - print(e) - return "Error Updating Interest", 500 - -@main_bp.route('/rsvpForEvent', methods = ['POST']) -def volunteerRegister(): - """ - This function selects the user ID and event ID and registers the user - for the event they have clicked register for. - """ - event = Event.get_by_id(request.form['id']) - program = event.program - user = g.current_user - - isAdded = checkUserRsvp(user, event) - isEligible = isEligibleForProgram(program, user) - listOfRequirements = unattendedRequiredEvents(program, user) - - personAdded = False - if isEligible: - personAdded = addPersonToEvent(user, event) - if personAdded and listOfRequirements: - reqListToString = ', '.join(listOfRequirements) - flash(f"{user.firstName} {user.lastName} successfully registered. However, the following training may be required: {reqListToString}.", "success") - elif personAdded: - flash("Successfully registered for event!","success") - else: - flash(f"RSVP Failed due to an unknown error.", "danger") - else: - flash(f"Cannot RSVP. Contact CELTS administrators: {app.config['celts_admin_contact']}.", "danger") - - - if 'from' in request.form: - if request.form['from'] == 'ajax': - return '' - return redirect(url_for("admin.eventDisplay", eventId=event.id)) - -@main_bp.route('/rsvpRemove', methods = ['POST']) -def RemoveRSVP(): - """ - This function deletes the user ID and event ID from database when RemoveRSVP is clicked - """ - eventData = request.form - event = Event.get_by_id(eventData['id']) - - currentRsvpParticipant = EventRsvp.get(EventRsvp.user == g.current_user, EventRsvp.event == event) - logBody = "withdrew from the waitlist" if currentRsvpParticipant.rsvpWaitlist else "un-RSVP'd" - currentRsvpParticipant.delete_instance() - createRsvpLog(event.id, f"{g.current_user.fullName} {logBody}.") - flash("Successfully unregistered for event!", "success") - if 'from' in eventData: - if eventData['from'] == 'ajax': - return '' - return redirect(url_for("admin.eventDisplay", eventId=event.id)) - -@main_bp.route('/profile//serviceTranscript', methods = ['GET']) -def serviceTranscript(username): - user = User.get_or_none(User.username == username) - if user is None: - abort(404) - if user != g.current_user and not g.current_user.isAdmin: - abort(403) - - slCourses = getSlCourseTranscript(username) - totalHours = getTotalHours(username) - allEventTranscript = getProgramTranscript(username) - startDate = getStartYear(username) - return render_template('main/serviceTranscript.html', - allEventTranscript = allEventTranscript, - slCourses = slCourses.objects(), - totalHours = totalHours, - startDate = startDate, - userData = user) - -@main_bp.route('/profile//updateTranscript/', methods=['POST']) -def updateTranscript(username, program_id): - user = User.get_or_none(User.username == username) - if user is None: - abort(404) - if user != g.current_user and not g.current_user.isAdmin: - abort(403) - - data = request.json - removeFromTranscript = data.get('removeFromTranscript') - - # Update the ProgramBan object matching the program_id and username - try: - programBan = ProgramBan.get(ProgramBan.program == program_id, ProgramBan.user == user) - programBan.removeFromTranscript = removeFromTranscript - programBan.save() - return jsonify({'status': 'success'}) - except ProgramBan.DoesNotExist: - return jsonify({'status': 'error'}) - - -@main_bp.route('/searchUser/', methods = ['GET']) -def searchUser(query): - - category= request.args.get("category") - - '''Accepts user input and queries the database returning results that matches user search''' - try: - query = query.strip() - search = query.upper() - splitSearch = search.split() - searchResults = searchUsers(query,category) - return searchResults - except Exception as e: - print(e) - return "Error in searching for user", 500 - -@main_bp.route('/contributors',methods = ['GET']) -def contributors(): - return render_template("/contributors.html") - -@main_bp.route('/proposalReview/', methods = ['GET', 'POST']) -def reviewProposal(): - """ - this function gets the submitted course id and returns the its data to the review proposal modal - """ - courseID=request.form - course=Course.get_by_id(courseID["course_id"]) - instructors_data=course.courseInstructors - return render_template('/main/reviewproposal.html', - course=course, - instructors_data=instructors_data) - -@main_bp.route('/updateDietInformation', methods = ['GET', 'POST']) -def getDietInfo(): - dietaryInfo = request.form - user = dietaryInfo["user"] - dietInfo = dietaryInfo["dietInfo"] - - if (g.current_user.username == user) or g.current_user.isAdmin: - updateDietInfo(user, dietInfo) - userInfo = User.get(User.username == user) - if len(dietInfo) > 0: - createAdminLog(f"Updated {userInfo.fullName}'s dietary restrictions to {dietInfo}.") if dietInfo.strip() else None - else: - createAdminLog(f"Deleted all {userInfo.fullName}'s dietary restrictions dietary restrictions.") - - - return " " +from flask import request, render_template, g, abort, flash, redirect, url_for, jsonify +from peewee import JOIN +from playhouse.shortcuts import model_to_dict +import datetime +import json +from http import cookies + +from app import app +from app.models.program import Program +from app.models.event import Event +from app.models.backgroundCheck import BackgroundCheck +from app.models.backgroundCheckType import BackgroundCheckType +from app.models.user import User +from app.models.eventParticipant import EventParticipant +from app.models.interest import Interest +from app.models.programBan import ProgramBan +from app.models.term import Term +from app.models.eventRsvp import EventRsvp +from app.models.note import Note +from app.models.profileNote import ProfileNote +from app.models.programManager import ProgramManager +from app.models.courseInstructor import CourseInstructor +from app.models.certification import Certification +from app.models.emergencyContact import EmergencyContact +from app.models.insuranceInfo import InsuranceInfo +from app.models.celtsLabor import CeltsLabor + +from app.controllers.main import main_bp +from app.logic.loginManager import logout +from app.logic.users import addUserInterest, removeUserInterest, banUser, unbanUser, isEligibleForProgram, getUserBGCheckHistory, addProfileNote, deleteProfileNote, updateDietInfo +from app.logic.participants import unattendedRequiredEvents, trainedParticipants, getParticipationStatusForTrainings, checkUserRsvp, addPersonToEvent +from app.logic.events import * +from app.logic.searchUsers import searchUsers +from app.logic.transcript import * +from app.logic.landingPage import getManagerProgramDict, getActiveEventTab +from app.logic.utils import selectSurroundingTerms +from app.logic.certification import getCertRequirementsWithCompletion +from app.logic.createLogs import createRsvpLog, createAdminLog +from app.logic.celtsLabor import getCeltsLaborHistory + +@main_bp.route('/logout', methods=['GET']) +def redirectToLogout(): + return redirect(logout()) + +@main_bp.route('/', methods=['GET']) +def landingPage(): + + managerProgramDict = getManagerProgramDict(g.current_user) + + # Optimize the query to fetch programs with non-canceled, non-past events in the current term + programsWithEventsList = list(Program.select(Program, Event) + .join(Event) + .where((Event.term == g.current_term) and (Event.isCanceled == False) and (Event.isPast == False)) + .distinct() + .execute()) # Ensure only unique programs are included + + return render_template("/main/landingPage.html", + managerProgramDict=managerProgramDict, + term=g.current_term, + programsWithEventsList=programsWithEventsList) + + +@main_bp.route('/goToEventsList/', methods=['GET']) +def goToEventsList(programID): + return {"activeTab": getActiveEventTab(programID)} + +@main_bp.route('/eventsList/', methods=['GET'], defaults={'activeTab': "studentLedEvents", 'programID': 0}) +@main_bp.route('/eventsList//', methods=['GET'], defaults={'programID': 0}) +@main_bp.route('/eventsList///', methods=['GET']) +def events(selectedTerm, activeTab, programID): + currentTerm = g.current_term + if selectedTerm: + currentTerm = selectedTerm + currentTime = datetime.datetime.now() + + listOfTerms = Term.select() + participantRSVP = EventRsvp.select(EventRsvp, Event).join(Event).where(EventRsvp.user == g.current_user) + rsvpedEventsID = [event.event.id for event in participantRSVP] + + term = Term.get_by_id(currentTerm) + + currentEventRsvpAmount = getEventRsvpCountsForTerm(term) + studentLedEvents = getStudentLedEvents(term) + countUpcomingStudentLedEvents = getUpcomingStudentLedCount(term, currentTime) + trainingEvents = getTrainingEvents(term, g.current_user) + bonnerEvents = getBonnerEvents(term) + otherEvents = getOtherEvents(term) + + managersProgramDict = getManagerProgramDict(g.current_user) + + return render_template("/events/event_list.html", + selectedTerm = term, + studentLedEvents = studentLedEvents, + trainingEvents = trainingEvents, + bonnerEvents = bonnerEvents, + otherEvents = otherEvents, + listOfTerms = listOfTerms, + rsvpedEventsID = rsvpedEventsID, + currentEventRsvpAmount = currentEventRsvpAmount, + currentTime = currentTime, + user = g.current_user, + activeTab = activeTab, + programID = int(programID), + managersProgramDict = managersProgramDict, + countUpcomingStudentLedEvents = countUpcomingStudentLedEvents + ) + +@main_bp.route('/profile/', methods=['GET']) +def viewUsersProfile(username): + """ + This function displays the information of a volunteer to the user + """ + try: + volunteer = User.get(User.username == username) + except Exception as e: + if g.current_user.isAdmin: + flash(f"{username} does not exist! ", category='danger') + return redirect(url_for('admin.studentSearchPage')) + else: + abort(403) # Error 403 if non admin/student-staff user trys to access via url + + if (g.current_user == volunteer) or g.current_user.isAdmin: + upcomingEvents = getUpcomingEventsForUser(volunteer) + participatedEvents = getParticipatedEventsForUser(volunteer) + programs = Program.select() + if not g.current_user.isBonnerScholar and not g.current_user.isAdmin: + programs = programs.where(Program.isBonnerScholars == False) + interests = Interest.select(Interest, Program).join(Program).where(Interest.user == volunteer) + programsInterested = [interest.program for interest in interests] + + rsvpedEventsList = EventRsvp.select(EventRsvp, Event).join(Event).where(EventRsvp.user == volunteer) + rsvpedEvents = [event.event.id for event in rsvpedEventsList] + + programManagerPrograms = ProgramManager.select(ProgramManager, Program).join(Program).where(ProgramManager.user == volunteer) + permissionPrograms = [entry.program.id for entry in programManagerPrograms] + + allBackgroundHistory = getUserBGCheckHistory(volunteer) + backgroundTypes = list(BackgroundCheckType.select()) + + eligibilityTable = [] + for program in programs: + banNotes = list(ProgramBan.select(ProgramBan, Note) + .join(Note, on=(ProgramBan.banNote == Note.id)) + .where(ProgramBan.user == volunteer, + ProgramBan.program == program, + ProgramBan.endDate > datetime.datetime.now()).execute()) + userParticipatedTrainingEvents = getParticipationStatusForTrainings(program, [volunteer], g.current_term) + try: + allTrainingsComplete = False not in [attended for event, attended in userParticipatedTrainingEvents[username]] # Did volunteer attend all events + except KeyError: + allTrainingsComplete = False + noteForDict = banNotes[-1].banNote.noteContent if banNotes else "" + eligibilityTable.append({"program": program, + "completedTraining": allTrainingsComplete, + "trainingList": userParticipatedTrainingEvents, + "isNotBanned": (not banNotes), + "banNote": noteForDict}) + profileNotes = ProfileNote.select().where(ProfileNote.user == volunteer) + + bonnerRequirements = getCertRequirementsWithCompletion(certification=Certification.BONNER, username=volunteer) + + managersProgramDict = getManagerProgramDict(g.current_user) + managersList = [id[1] for id in managersProgramDict.items()] + participatedInLabor = getCeltsLaborHistory(volunteer) + + return render_template ("/main/userProfile.html", + programs = programs, + programsInterested = programsInterested, + upcomingEvents = upcomingEvents, + participatedEvents = participatedEvents, + rsvpedEvents = rsvpedEvents, + permissionPrograms = permissionPrograms, + eligibilityTable = eligibilityTable, + volunteer = volunteer, + backgroundTypes = backgroundTypes, + allBackgroundHistory = allBackgroundHistory, + currentDateTime = datetime.datetime.now(), + profileNotes = profileNotes, + bonnerRequirements = bonnerRequirements, + managersList = managersList, + participatedInLabor = participatedInLabor, + ) + abort(403) + +@main_bp.route('/profile//emergencyContact', methods=['GET', 'POST']) +def emergencyContactInfo(username): + """ + This loads the Emergency Contact Page + """ + if not (g.current_user.username == username or g.current_user.isCeltsAdmin): + abort(403) + + + if request.method == 'GET': + readOnly = g.current_user.username != username + contactInfo = EmergencyContact.get_or_none(EmergencyContact.user_id == username) + return render_template ("/main/emergencyContactInfo.html", + username=username, + contactInfo=contactInfo, + readOnly=readOnly + ) + + elif request.method == 'POST': + if g.current_user.username != username: + abort(403) + + rowsUpdated = EmergencyContact.update(**request.form).where(EmergencyContact.user == username).execute() + if not rowsUpdated: + EmergencyContact.create(user = username, **request.form) + createAdminLog(f"{g.current_user} updated {username}'s emergency contact information.") + flash('Emergency contact information saved successfully!', 'success') + + if request.args.get('action') == 'exit': + return redirect (f"/profile/{username}") + else: + return redirect (f"/profile/{username}/insuranceInfo") + +@main_bp.route('/profile//insuranceInfo', methods=['GET', 'POST']) +def insuranceInfo(username): + """ + This loads the Insurance Information Page + """ + if not (g.current_user.username == username or g.current_user.isCeltsAdmin): + abort(403) + + if request.method == 'GET': + readOnly = g.current_user.username != username + userInsuranceInfo = InsuranceInfo.get_or_none(InsuranceInfo.user == username) + return render_template ("/main/insuranceInfo.html", + username=username, + userInsuranceInfo=userInsuranceInfo, + readOnly=readOnly + ) + + # Save the form data + elif request.method == 'POST': + if g.current_user.username != username: + abort(403) + + rowsUpdated = InsuranceInfo.update(**request.form).where(InsuranceInfo.user == username).execute() + if not rowsUpdated: + InsuranceInfo.create(user = username, **request.form) + createAdminLog(f"{g.current_user} updated {username}'s emergency contact information.") + flash('Insurance information saved successfully!', 'success') + + if request.args.get('action') == 'exit': + return redirect (f"/profile/{username}") + else: + return redirect (f"/profile/{username}/emergencyContact") + +@main_bp.route('/profile//travelForm', methods=['GET', 'POST']) +def travelForm(username): + if not (g.current_user.username == username or g.current_user.isCeltsAdmin): + abort(403) + + user = (User.select(User, EmergencyContact, InsuranceInfo) + .join(EmergencyContact, JOIN.LEFT_OUTER).switch() + .join(InsuranceInfo, JOIN.LEFT_OUTER) + .where(User.username == username).limit(1)) + if not list(user): + abort(404) + userData = list(user.dicts())[0] + userData = {key: value if value else '' for (key, value) in userData.items()} + + return render_template ('/main/travelForm.html', + userData = userData + ) + + +@main_bp.route('/profile/addNote', methods=['POST']) +def addNote(): + """ + This function adds a note to the user's profile. + """ + postData = request.form + try: + note = addProfileNote(postData["visibility"], postData["bonner"] == "yes", postData["noteTextbox"], postData["username"]) + flash("Successfully added profile note", "success") + return redirect(url_for("main.viewUsersProfile", username=postData["username"])) + except Exception as e: + print("Error adding note", e) + flash("Failed to add profile note", "danger") + return "Failed to add profile note", 500 + +@main_bp.route('//deleteNote', methods=['POST']) +def deleteNote(username): + """ + This function deletes a note from the user's profile. + """ + try: + deleteProfileNote(request.form["id"]) + flash("Successfully deleted profile note", "success") + except Exception as e: + print("Error deleting note", e) + flash("Failed to delete profile note", "danger") + return "success" + +# ===========================Ban=============================================== +@main_bp.route('//ban/', methods=['POST']) +def ban(program_id, username): + """ + This function updates the ban status of a username either when they are banned from a program. + program_id: the primary id of the program the student is being banned from + username: unique value of a user to correctly identify them + """ + postData = request.form + banNote = postData["note"] # This contains the note left about the change + banEndDate = postData["endDate"] # Contains the date the ban will no longer be effective + try: + banUser(program_id, username, banNote, banEndDate, g.current_user) + programInfo = Program.get(int(program_id)) + flash("Successfully banned the volunteer", "success") + createAdminLog(f'Banned {username} from {programInfo.programName} until {banEndDate}.') + return "Successfully banned the volunteer." + except Exception as e: + print("Error while updating ban", e) + flash("Failed to ban the volunteer", "danger") + return "Failed to ban the volunteer", 500 + +# ===========================Unban=============================================== +@main_bp.route('//unban/', methods=['POST']) +def unban(program_id, username): + """ + This function updates the ban status of a username either when they are unbanned from a program. + program_id: the primary id of the program the student is being unbanned from + username: unique value of a user to correctly identify them + """ + postData = request.form + unbanNote = postData["note"] # This contains the note left about the change + try: + unbanUser(program_id, username, unbanNote, g.current_user) + programInfo = Program.get(int(program_id)) + createAdminLog(f'Unbanned {username} from {programInfo.programName}.') + flash("Successfully unbanned the volunteer", "success") + return "Successfully unbanned the volunteer" + + except Exception as e: + print("Error while updating Unban", e) + flash("Failed to unban the volunteer", "danger") + return "Failed to unban the volunteer", 500 + + +@main_bp.route('//addInterest/', methods=['POST']) +def addInterest(program_id, username): + """ + This function adds a program to the list of programs a user interested in + program_id: the primary id of the program the student is adding interest of + username: unique value of a user to correctly identify them + """ + try: + success = addUserInterest(program_id, username) + if success: + flash("Successfully added " + Program.get_by_id(program_id).programName + " as an interest", "success") + return "" + else: + flash("Was unable to remove " + Program.get_by_id(program_id).programName + " as an interest.", "danger") + + except Exception as e: + print(e) + return "Error Updating Interest", 500 + +@main_bp.route('//removeInterest/', methods=['POST']) +def removeInterest(program_id, username): + """ + This function removes a program to the list of programs a user interested in + program_id: the primary id of the program the student is adding interest of + username: unique value of a user to correctly identify them + """ + try: + removed = removeUserInterest(program_id, username) + if removed: + flash("Successfully removed " + Program.get_by_id(program_id).programName + " as an interest.", "success") + return "" + else: + flash("Was unable to remove " + Program.get_by_id(program_id).programName + " as an interest.", "danger") + except Exception as e: + print(e) + return "Error Updating Interest", 500 + +@main_bp.route('/rsvpForEvent', methods = ['POST']) +def volunteerRegister(): + """ + This function selects the user ID and event ID and registers the user + for the event they have clicked register for. + """ + event = Event.get_by_id(request.form['id']) + program = event.program + user = g.current_user + + isAdded = checkUserRsvp(user, event) + isEligible = isEligibleForProgram(program, user) + listOfRequirements = unattendedRequiredEvents(program, user) + + personAdded = False + if isEligible: + personAdded = addPersonToEvent(user, event) + if personAdded and listOfRequirements: + reqListToString = ', '.join(listOfRequirements) + flash(f"{user.firstName} {user.lastName} successfully registered. However, the following training may be required: {reqListToString}.", "success") + elif personAdded: + flash("Successfully registered for event!","success") + else: + flash(f"RSVP Failed due to an unknown error.", "danger") + else: + flash(f"Cannot RSVP. Contact CELTS administrators: {app.config['celts_admin_contact']}.", "danger") + + + if 'from' in request.form: + if request.form['from'] == 'ajax': + return '' + return redirect(url_for("admin.eventDisplay", eventId=event.id)) + +@main_bp.route('/rsvpRemove', methods = ['POST']) +def RemoveRSVP(): + """ + This function deletes the user ID and event ID from database when RemoveRSVP is clicked + """ + eventData = request.form + event = Event.get_by_id(eventData['id']) + + currentRsvpParticipant = EventRsvp.get(EventRsvp.user == g.current_user, EventRsvp.event == event) + logBody = "withdrew from the waitlist" if currentRsvpParticipant.rsvpWaitlist else "un-RSVP'd" + currentRsvpParticipant.delete_instance() + createRsvpLog(event.id, f"{g.current_user.fullName} {logBody}.") + flash("Successfully unregistered for event!", "success") + if 'from' in eventData: + if eventData['from'] == 'ajax': + return '' + return redirect(url_for("admin.eventDisplay", eventId=event.id)) + +@main_bp.route('/profile//serviceTranscript', methods = ['GET']) +def serviceTranscript(username): + user = User.get_or_none(User.username == username) + if user is None: + abort(404) + if user != g.current_user and not g.current_user.isAdmin: + abort(403) + + slCourses = getSlCourseTranscript(username) + totalHours = getTotalHours(username) + allEventTranscript = getProgramTranscript(username) + startDate = getStartYear(username) + return render_template('main/serviceTranscript.html', + allEventTranscript = allEventTranscript, + slCourses = slCourses.objects(), + totalHours = totalHours, + startDate = startDate, + userData = user) + +@main_bp.route('/profile//updateTranscript/', methods=['POST']) +def updateTranscript(username, program_id): + user = User.get_or_none(User.username == username) + if user is None: + abort(404) + if user != g.current_user and not g.current_user.isAdmin: + abort(403) + + data = request.json + removeFromTranscript = data.get('removeFromTranscript') + + # Update the ProgramBan object matching the program_id and username + try: + programBan = ProgramBan.get(ProgramBan.program == program_id, ProgramBan.user == user) + programBan.removeFromTranscript = removeFromTranscript + programBan.save() + return jsonify({'status': 'success'}) + except ProgramBan.DoesNotExist: + return jsonify({'status': 'error'}) + + +@main_bp.route('/searchUser/', methods = ['GET']) +def searchUser(query): + + category= request.args.get("category") + + '''Accepts user input and queries the database returning results that matches user search''' + try: + query = query.strip() + search = query.upper() + splitSearch = search.split() + searchResults = searchUsers(query,category) + return searchResults + except Exception as e: + print(e) + return "Error in searching for user", 500 + +@main_bp.route('/contributors',methods = ['GET']) +def contributors(): + return render_template("/contributors.html") + +@main_bp.route('/proposalReview/', methods = ['GET', 'POST']) +def reviewProposal(): + """ + this function gets the submitted course id and returns the its data to the review proposal modal + """ + courseID=request.form + course=Course.get_by_id(courseID["course_id"]) + instructors_data=course.courseInstructors + return render_template('/main/reviewproposal.html', + course=course, + instructors_data=instructors_data) + +@main_bp.route('/updateDietInformation', methods = ['GET', 'POST']) +def getDietInfo(): + dietaryInfo = request.form + user = dietaryInfo["user"] + dietInfo = dietaryInfo["dietInfo"] + + if (g.current_user.username == user) or g.current_user.isAdmin: + updateDietInfo(user, dietInfo) + userInfo = User.get(User.username == user) + if len(dietInfo) > 0: + createAdminLog(f"Updated {userInfo.fullName}'s dietary restrictions to {dietInfo}.") if dietInfo.strip() else None + else: + createAdminLog(f"Deleted all {userInfo.fullName}'s dietary restrictions dietary restrictions.") + + + return " " diff --git a/app/logic/transcript.py b/app/logic/transcript.py index 67be4fd59..2164e0fcd 100644 --- a/app/logic/transcript.py +++ b/app/logic/transcript.py @@ -1,127 +1,88 @@ -from peewee import fn - -from app.models.course import Course -from app.models.courseParticipant import CourseParticipant -from app.models.program import Program -from app.models.courseInstructor import CourseInstructor -from app.models.user import User -from app.models.term import Term -from app.models.eventParticipant import EventParticipant -from app.models.event import Event -from app.models.programBan import ProgramBan - -from app.logic.users import isEligibleForProgram - - -# def getProgramTranscript(username): -# """ -# Returns a Program query object containing all the programs for -# current user. -# """ -# # Add up hours earned in a term for each program they've participated in - -# EventData = (Event.select(Event, fn.SUM(EventParticipant.hoursEarned).alias("hoursEarned")) -# .join(EventParticipant) -# .where(EventParticipant.user == username) -# .group_by(Event.program, Event.term) -# .order_by(Event.term) -# .having(fn.SUM(EventParticipant.hoursEarned > 0))) -# transcriptData = {} -# for event in EventData: -# if event.program in transcriptData: -# transcriptData[event.program].append([event.term.description, event.hoursEarned]) -# else: -# transcriptData[event.program] = [[event.term.description, event.hoursEarned]] -# return transcriptData - -def getProgramTranscript(username, removeFromTranscript=False): - """ - Returns a Program query object containing all the programs for - the current user. - """ - # Add up hours earned in a term for each program they've participated in - - # current user. Optionally removes programs based on the checkbox status. - # """ - - EventData = (Event.select(Event, fn.SUM(EventParticipant.hoursEarned).alias("hoursEarned")) - .join(EventParticipant) - .where(EventParticipant.user == username) - .group_by(Event.program, Event.term) - .order_by(Event.term) - .having(fn.SUM(EventParticipant.hoursEarned > 0))) - - # Fetch all ProgramBan objects for the user - BannedProgramsForParticipant = ProgramBan.select().where(ProgramBan.user == username) - - # Create a set of program IDs to remove from transcript - programsToremoveFromTranscript = {bannedProgram.program_id for bannedProgram in BannedProgramsForParticipant if bannedProgram.removeFromTranscript} - - transcriptData = {} - - # Iterate through EventData and populate transcriptData - for event in EventData: - # if event.program.id not in programsToremoveFromTranscript: # Check if program is not in programs to be removed from transcript - # if event.program in transcriptData: - # transcriptData[event.program].append([event.term.description, event.hoursEarned]) - # else: - # transcriptData[event.program] = [[event.term.description, event.hoursEarned]] - - - if event.program in transcriptData: - transcriptData[event.program].append([event.term.description, event.hoursEarned]) - else: - transcriptData[event.program] = [[event.term.description, event.hoursEarned]] - - if removeFromTranscript: - for program in list(transcriptData.keys()): - if not isEligibleForProgram(program, username): - transcriptData.pop(program) - - return transcriptData - -def getSlCourseTranscript(username): - """ - Returns a SLCourse query object containing all the training events for - current user. - """ - - slCourses = (Course.select(Course, fn.SUM(CourseParticipant.hoursEarned).alias("hoursEarned")) - .join(CourseParticipant, on=(Course.id == CourseParticipant.course)) - .where(CourseParticipant.user == username) - .group_by(Course.courseName, Course.term)) - - return slCourses - -def getTotalHours(username): - """ - Get the toal hours from events and courses combined. - """ - eventHours = (EventParticipant.select(fn.SUM(EventParticipant.hoursEarned)) - .where(EventParticipant.user == username)).scalar() - courseHours = (CourseParticipant.select(fn.SUM(CourseParticipant.hoursEarned)) - .where(CourseParticipant.user == username)).scalar() - - allHours = {"totalEventHours": (eventHours or 0), - "totalCourseHours": (courseHours or 0), - "totalHours": (eventHours or 0) + (courseHours or 0)} - return allHours - -def getStartYear(username): - """ - Returns the users start term for participation in the CELTS organization - """ - - startDate = (EventParticipant.select(Term.year) - .join(Event) - .join(Term).where(EventParticipant.user == username) - + CourseParticipant.select(Term.year) - .join(Course) - .join(Term) - .where(CourseParticipant.user == username) - ).order_by(Event.term.year).first() - - if startDate: - return startDate.event.term.year - - return "N/A" +from peewee import fn + +from app.models.course import Course +from app.models.courseParticipant import CourseParticipant +from app.models.program import Program +from app.models.courseInstructor import CourseInstructor +from app.models.user import User +from app.models.term import Term +from app.models.eventParticipant import EventParticipant +from app.models.event import Event +from app.models.programBan import ProgramBan + +def getProgramTranscript(username): + """ + Returns a Program query object containing all the programs for + the current user. + """ + # Add up hours earned in a term for each program they've participated in + + EventData = (Event.select(Event, fn.SUM(EventParticipant.hoursEarned).alias("hoursEarned")) + .join(EventParticipant) + .where(EventParticipant.user == username) + .group_by(Event.program, Event.term) + .order_by(Event.term) + .having(fn.SUM(EventParticipant.hoursEarned > 0))) + + # Fetch all ProgramBan objects for the user + BannedProgramsForParticipant = ProgramBan.select().where(ProgramBan.user == username) + + # Create a set of program IDs to remove from transcript + programsToremoveFromTranscript = {bannedProgram.program_id for bannedProgram in BannedProgramsForParticipant if bannedProgram.removeFromTranscript} + transcriptData = {} + + # Iterate through EventData and populate transcriptData + for event in EventData: + if event.program.id not in programsToremoveFromTranscript: # Check if program is not in programs to be removed from transcript + if event.program in transcriptData: + transcriptData[event.program].append([event.term.description, event.hoursEarned]) + else: + transcriptData[event.program] = [[event.term.description, event.hoursEarned]] + + return transcriptData + +def getSlCourseTranscript(username): + """ + Returns a SLCourse query object containing all the training events for + current user. + """ + + slCourses = (Course.select(Course, fn.SUM(CourseParticipant.hoursEarned).alias("hoursEarned")) + .join(CourseParticipant, on=(Course.id == CourseParticipant.course)) + .where(CourseParticipant.user == username) + .group_by(Course.courseName, Course.term)) + + return slCourses + +def getTotalHours(username): + """ + Get the toal hours from events and courses combined. + """ + eventHours = (EventParticipant.select(fn.SUM(EventParticipant.hoursEarned)) + .where(EventParticipant.user == username)).scalar() + courseHours = (CourseParticipant.select(fn.SUM(CourseParticipant.hoursEarned)) + .where(CourseParticipant.user == username)).scalar() + + allHours = {"totalEventHours": (eventHours or 0), + "totalCourseHours": (courseHours or 0), + "totalHours": (eventHours or 0) + (courseHours or 0)} + return allHours + +def getStartYear(username): + """ + Returns the users start term for participation in the CELTS organization + """ + + startDate = (EventParticipant.select(Term.year) + .join(Event) + .join(Term).where(EventParticipant.user == username) + + CourseParticipant.select(Term.year) + .join(Course) + .join(Term) + .where(CourseParticipant.user == username) + ).order_by(Event.term.year).first() + + if startDate: + return startDate.event.term.year + + return "N/A" diff --git a/app/static/js/userProfile.js b/app/static/js/userProfile.js index d1ca6e02e..bbde04400 100644 --- a/app/static/js/userProfile.js +++ b/app/static/js/userProfile.js @@ -1,385 +1,339 @@ -$(document).ready(function(){ - $("#printButton").on("click", function() { - let username = $(this).data('username') - printDocument(`/profile/${username}/travelForm`) - }) - $("#actions").on("change", changeAction) - $("#phoneInput").inputmask('(999)-999-9999'); - $(".notifyInput").click(function updateInterest(){ - var programID = $(this).data("programid"); - var username = $(this).data('username'); - - - var interest = $(this).is(':checked'); - var routeUrl = interest ? "addInterest" : "removeInterest"; - interestUrl = "/" + username + "/" + routeUrl + "/" + programID ; - $.ajax({ - method: "POST", - url: interestUrl, - success: function(response) { - reloadWithAccordion("programTable") // Reloading page after user clicks on the show interest checkbox - }, - error: function(request, status, error) { - console.log(status,error); - location.reload(); - } - }); - }); - -<<<<<<< HEAD - var isChecked = localStorage.getItem("removeFromTranscriptChecked"); - $(".removeFromTranscriptCheckbox").prop("checked", isChecked === "true"); - - // Add event listener to save checkbox state on change - $('.removeFromTranscriptCheckbox').click(function() { - var removeFromTranscript = $(this).is(':checked'); - var username = $(this).data('username'); - - // Save checkbox state to local storage - localStorage.setItem("removeFromTranscriptChecked", removeFromTranscript); - - - $.ajax({ - type: "POST", - url: `/profile/${username}/updateTranscript/${programID}`, - contentType: "application/json", - data: JSON.stringify({ username: username, removeFromTranscript: removeFromTranscript, programID: programID }), - success: function(response) { - console.log(response); - // Handle success - }, - error: function(error) { - console.error("An error occurred:", error); - // Handle error - } - }); -}); -======= - $(".removeFromTranscriptCheckbox").click(function updateCheckbox(){ - var programID = $(this).data("programid"); - var username = $(this).data('username'); - - var removeCheckbox = $(this).is(':checked'); - var routeUrl = removeCheckbox ? "removeBannedUserFromTranscript" : "removeBannedUserFromTranscript"; - removeCheckboxUrl = "/" + username + "/" + routeUrl + "/" + programID ; - $.ajax({ - method: "POST", - url: removeCheckboxUrl, - success: function(response) { - reloadWithAccordion("programTable") // Reloading page after user clicks on the show interest checkbox - }, - error: function(request, status, error) { - console.log(status,error); - location.reload(); - } - }); - }); ->>>>>>> 4b1d9eebae743660ba336e38015c241e4538d584 - - function changeAction(e){ - let profileAction = $(this).val() - let username = $(this).data('username') - if (profileAction == "Emergency Contact"){ - window.location.href = `/profile/${username}/emergencyContact` - } else if (profileAction == "Insurance Information"){ - window.location.href = `/profile/${username}/insuranceInfo` - } else if(profileAction == "Print Travel Form"){ - printDocument(`/profile/${username}/travelForm`) - } else if (profileAction == "View Service Transcript"){ - handleCheckbox(username); - window.location.href = `/profile/${username}/serviceTranscript` - } else if (profileAction == "Manage CCE Minor") { - window.location.href = `/profile/${username}/cceMinor` - } - $(this).val('') - } - - - - // This function is to disable all the dates before current date in the ban modal End Date picker - $(function(){ - var banEndDatepicker = $("#banEndDatepicker"); - banEndDatepicker.datepicker({ - changeYear: true, - changeMonth: true, - minDate:+1, - dateFormat: "yy-mm-dd", - }).attr('readonly','readonly'); - }); - - /* - * Ban Functionality - */ - var programID; // Declare programID variable outside of the function scope to be used for removeFromTranscript checkbox - $(".ban").click(function() { - var banButton = $("#banButton") - var banEndDateDiv = $("#banEndDate") // Div containing the datepicker in the ban modal - var banEndDatepicker = $("#banEndDatepicker") // Datepicker in the ban modal - var banNoteDiv = $("#banNoteDiv") // Div containing the note displaying why the user was banned previously - //Should only diplay when the modal is going to unban a user - var banNote = $("#banNote") - - banButton.text($(this).val() + " Volunteer"); - programID = $(this).data("programid"); // Assign value to programID variable - banButton.data("programID", $(this).data("programid")) - banButton.data("username", $("#notifyInput").data("username")) - banButton.data("banOrUnban", $(this).val()); - banEndDateDiv.show(); - banEndDatepicker.val("") - $(".modal-title").text($(this).val() + " Volunteer"); - $("#modalProgramName").text("Program: " + $(this).data("name ")); - $("#banModal").modal("toggle"); - banNoteDiv.hide(); - $("#removeFromTranscriptDiv").hide(); - $("#banNoteTxtArea").val(""); - $("#banButton").prop("disabled", true); - if( $(this).val()=="Unban"){ - banEndDateDiv.hide() - banEndDatepicker.val("0001-01-01") //This is a placeholder value for the if statement in line 52 to work properly #PLCHLD1 - banNoteDiv.show() - $("#removeFromTranscriptDiv").show(); - banNote.text($(this).data("note")) - } - - }); - - - $("#banNoteTxtArea, #banEndDatepicker").on('input' , function (e) { //This is the if statement the placeholder in line 45 is for #PLCHLD1 - var enableButton = ($("#banNoteTxtArea").val() && $("#banEndDatepicker").val()); - $("#banButton").prop("disabled", !enableButton); - }); - - $("#banButton").click(function (){ - $("#banButton").prop("disabled", true) - var username = $(this).data("username") //Expected to be the unique username of a user in the database - var route = ($(this).data("banOrUnban")).toLowerCase() //Expected to be "ban" or "unban" - var program = $(this).data("programID") //Expected to be a program's primary ID - $.ajax({ - method: "POST", - url: "/" + username + "/" + route + "/" + program, - data: {"note": $("#banNoteTxtArea").val(), - "endDate":$("#banEndDatepicker").val() //Expected to be a date in this format YYYY-MM-DD - }, - success: function(response) { - reloadWithAccordion("programTable") - } - }); - }); - - /* - * Note Functionality - */ - function bonnerNoteOff() { - $("#bonnerInput").prop("checked", false); - $("#noteDropdown").show() - $("#bonnerStatement").hide() - $("#visibilityLabel").show() - } - - function bonnerNoteOn() { - $("#bonnerInput").prop("checked", true); - $("#noteDropdown").hide() - $("#bonnerStatement").show() - $("#visibilityLabel").hide() - } - - $("#addNoteButton").click(function() { - bonnerNoteOff() - $("#noteModal").modal("toggle") - }); - - $("#addVisibility").click(function() { - var bonnerChecked = $("input[name='bonner']:checked").val() - - if (bonnerChecked == 'on') { - bonnerNoteOn() - } else { - bonnerNoteOff() - } - }); - - $("#addBonnerNoteButton").click(function() { - bonnerNoteOn() - $("#noteModal").modal("toggle"); - }); - - $('#addNoteForm').submit(function(event) { - event.preventDefault() - let username = $("#notesSaveButton").data('username') - let isBonner = $("#bonnerInput").is(":checked") - $.ajax({ - method: "POST", - url: "/profile/addNote", - data: {"username": username, - "visibility": $("#noteDropdown").val(), - "noteTextbox": $("#addNoteTextArea").val(), - "bonner": isBonner ? "yes" : "no"}, - success: function(response) { - target = isBonner ? "bonner" : "notes" - reloadWithAccordion(target) - } - }); - -}); - - $(".deleteNoteButton").click(function() { - let username = $(this).data('username') - let noteid = $(this).data('noteid') - $.ajax({ - method: "POST", - url: "/" + username + "/deleteNote", - data: {"id": noteid}, - success: function(response) { - reloadWithAccordion("notes") - } - }); - }); - - /* - * Background Check Functionality - */ - // Updates the Background check of a volunteer in the database - $(".savebtn").click(function () { - $(this).prop("disabled", true); - let bgCheckType = $(this).data("id") - - var bgStatusInput = $("#" + bgCheckType) - var bgDateInput = $("#" + bgCheckType + "_date") - - let bgDate = bgDateInput.val() - let bgStatus = $("[data-id=" + bgCheckType + "]").val() - - if (bgStatus == '') { - bgStatusInput.focus() - bgStatusInput.addClass("invalid"); - window.setTimeout(() => bgStatusInput.removeClass("invalid"), 1000); - $(this).prop("disabled", false); - return false - } - - if (bgDate == ''){ - bgDateInput.focus() - bgDateInput.addClass("invalid"); - window.setTimeout(() => bgDateInput.removeClass("invalid"), 1000); - $(this).prop("disabled", false); - return false - } - - let data = { - bgStatus: bgStatus, // Expected to be one of the three background check statuses - user: $(this).data("username"), // Expected to be the username of a volunteer in the database - bgType: $(this).attr("id"), // Expected to be the ID of a background check in the database - bgDate: bgDate // Expected to be the date of the background check completion or '' if field is empty - } - $.ajax({ - url: "/addBackgroundCheck", - type: "POST", - data: data, - success: function(s){ - var date = new Date(data.bgDate + " 12:00").toLocaleDateString() - reloadWithAccordion("background") - }, - error: function(error, status){ - console.log(error, status) - } - }) - }); - - $("#bgHistoryTable").on("click", "#deleteBgHistory", function() { - let data = { - bgID: $(this).data("id"), // Expected to be the ID of a background check in the database - } - $(this).closest("li").remove(); - - $.ajax({ - url: "/deleteBackgroundCheck", - type: "POST", - data: data, - success: function(s){ - msgToast("Background Check", "Successfully deleted background check.") - }, - error: function(error, status){ - console.log(error,status) - } - }) - }); - // Popover functionality - var requiredTraining = $(".trainingPopover"); - requiredTraining.popover({ - trigger: "hover", - sanitize: false, - html: true, - content: function() { - return $(this).attr('data-content'); - } - }); - - setupPhoneNumber("#updatePhone", "#phoneInput") - - $(".saveDiet").on('click', function() { - let data = { - dietInfo: $("#diet").val(), - user: $(this).data("user") - } - $.ajax({ - type: "POST", - url: "/updateDietInformation", - data: data, - success: function(s){ - reloadWithAccordion("dietaryInformation"); - }, - }) - }); - -}); - -function updateManagers(el, volunteer_username ){// retrieve the data of the student staff and program id if the boxes are checked or not - let program_id=$(el).attr('data-programid'); - let programName = $(el).attr('data-programName') - let name = $(el).attr('data-name') - let action= el.checked ? 'add' : 'remove'; - let removeMessage = (name + " is no longer the manager of " + programName + ".") - let addMessage = (name + " is now the manager of " + programName + ".") - - $.ajax({ - method:"POST", - url:"/updateProgramManager", - data : {"user_name":volunteer_username, //student staff: user_name - "program_id":program_id, // program id - "action":action, //action: add or remove - }, - - success: function(s){ - if(action == "add"){ - msgToast("Program manager", addMessage) - } else if(action == 'remove'){ - msgToast("Program manager", removeMessage) - } - }, - error: function(error, status){ - console.log(error, status) - } - }) -} - -<<<<<<< HEAD - -======= -function handleCheckbox() { - var removeFromTranscript = $('#removeFromTranscriptCheckbox').prop('checked'); // Using jQuery to get the checkbox state - var username = $(this).data('username'); - - $.ajax({ - type: "POST", - url: "/update-transcript", - contentType: "application/json", - data: JSON.stringify({ username: username, removeFromTranscript: removeFromTranscript }), - success: function(updatedTranscriptData) { - console.log(updatedTranscriptData); - }, - error: function(xhr, status, error) { - console.error("An error occurred:", error); - } - }); -} ->>>>>>> 4b1d9eebae743660ba336e38015c241e4538d584 +$(document).ready(function(){ + $("#printButton").on("click", function() { + let username = $(this).data('username') + printDocument(`/profile/${username}/travelForm`) + }) + $("#actions").on("change", changeAction) + $("#phoneInput").inputmask('(999)-999-9999'); + $(".notifyInput").click(function updateInterest(){ + var programID = $(this).data("programid"); + var username = $(this).data('username'); + + + var interest = $(this).is(':checked'); + var routeUrl = interest ? "addInterest" : "removeInterest"; + interestUrl = "/" + username + "/" + routeUrl + "/" + programID ; + $.ajax({ + method: "POST", + url: interestUrl, + success: function(response) { + reloadWithAccordion("programTable") // Reloading page after user clicks on the show interest checkbox + }, + error: function(request, status, error) { + console.log(status,error); + location.reload(); + } + }); + }); + + $(".removeFromTranscriptCheckbox").each(function() { + var isChecked = $(this).data('isChecked'); // Assuming you pass this data attribute from the backend + $(this).prop("checked", isChecked); + }); + + // Add event listener to save checkbox state on change + $('.removeFromTranscriptCheckbox').click(function() { + var removeFromTranscript = $(this).is(':checked'); + var username = $(this).data('username'); + + + $.ajax({ + type: "POST", + url: `/profile/${username}/updateTranscript/${programID}`, + contentType: "application/json", + data: JSON.stringify({ username: username, removeFromTranscript: removeFromTranscript, programID: programID }), + success: function(response) { + console.log(response); + // Handle success + }, + error: function(error) { + console.error("An error occurred:", error); + // Handle error + } + }); +}); + + function changeAction(e){ + let profileAction = $(this).val() + let username = $(this).data('username') + if (profileAction == "Emergency Contact"){ + window.location.href = `/profile/${username}/emergencyContact` + } else if (profileAction == "Insurance Information"){ + window.location.href = `/profile/${username}/insuranceInfo` + } else if(profileAction == "Print Travel Form"){ + printDocument(`/profile/${username}/travelForm`) + } else if (profileAction == "View Service Transcript"){ + window.location.href = `/profile/${username}/serviceTranscript` + } else if (profileAction == "Manage CCE Minor") { + window.location.href = `/profile/${username}/cceMinor` + } + $(this).val('') + } + + + + // This function is to disable all the dates before current date in the ban modal End Date picker + $(function(){ + var banEndDatepicker = $("#banEndDatepicker"); + banEndDatepicker.datepicker({ + changeYear: true, + changeMonth: true, + minDate:+1, + dateFormat: "yy-mm-dd", + }).attr('readonly','readonly'); + }); + + /* + * Ban Functionality + */ + var programID; // Declare programID variable outside of the function scope to be used for removeFromTranscript checkbox + $(".ban").click(function() { + var banButton = $("#banButton") + var banEndDateDiv = $("#banEndDate") // Div containing the datepicker in the ban modal + var banEndDatepicker = $("#banEndDatepicker") // Datepicker in the ban modal + var banNoteDiv = $("#banNoteDiv") // Div containing the note displaying why the user was banned previously + //Should only diplay when the modal is going to unban a user + var banNote = $("#banNote") + + banButton.text($(this).val() + " Volunteer"); + programID = $(this).data("programid"); // Assign value to programID variable + banButton.data("programID", $(this).data("programid")) + banButton.data("username", $("#notifyInput").data("username")) + banButton.data("banOrUnban", $(this).val()); + banEndDateDiv.show(); + banEndDatepicker.val("") + $(".modal-title").text($(this).val() + " Volunteer"); + $("#modalProgramName").text("Program: " + $(this).data("name ")); + $("#banModal").modal("toggle"); + banNoteDiv.hide(); + $("#removeFromTranscriptDiv").hide(); + $("#banNoteTxtArea").val(""); + $("#banButton").prop("disabled", true); + if( $(this).val()=="Unban"){ + banEndDateDiv.hide() + banEndDatepicker.val("0001-01-01") //This is a placeholder value for the if statement in line 52 to work properly #PLCHLD1 + banNoteDiv.show() + $("#removeFromTranscriptDiv").show(); + banNote.text($(this).data("note")) + } + + }); + + + $("#banNoteTxtArea, #banEndDatepicker").on('input' , function (e) { //This is the if statement the placeholder in line 45 is for #PLCHLD1 + var enableButton = ($("#banNoteTxtArea").val() && $("#banEndDatepicker").val()); + $("#banButton").prop("disabled", !enableButton); + }); + + $("#banButton").click(function (){ + $("#banButton").prop("disabled", true) + var username = $(this).data("username") //Expected to be the unique username of a user in the database + var route = ($(this).data("banOrUnban")).toLowerCase() //Expected to be "ban" or "unban" + var program = $(this).data("programID") //Expected to be a program's primary ID + $.ajax({ + method: "POST", + url: "/" + username + "/" + route + "/" + program, + data: {"note": $("#banNoteTxtArea").val(), + "endDate":$("#banEndDatepicker").val() //Expected to be a date in this format YYYY-MM-DD + }, + success: function(response) { + reloadWithAccordion("programTable") + } + }); + }); + + /* + * Note Functionality + */ + function bonnerNoteOff() { + $("#bonnerInput").prop("checked", false); + $("#noteDropdown").show() + $("#bonnerStatement").hide() + $("#visibilityLabel").show() + } + + function bonnerNoteOn() { + $("#bonnerInput").prop("checked", true); + $("#noteDropdown").hide() + $("#bonnerStatement").show() + $("#visibilityLabel").hide() + } + + $("#addNoteButton").click(function() { + bonnerNoteOff() + $("#noteModal").modal("toggle") + }); + + $("#addVisibility").click(function() { + var bonnerChecked = $("input[name='bonner']:checked").val() + + if (bonnerChecked == 'on') { + bonnerNoteOn() + } else { + bonnerNoteOff() + } + }); + + $("#addBonnerNoteButton").click(function() { + bonnerNoteOn() + $("#noteModal").modal("toggle"); + }); + + $('#addNoteForm').submit(function(event) { + event.preventDefault() + let username = $("#notesSaveButton").data('username') + let isBonner = $("#bonnerInput").is(":checked") + $.ajax({ + method: "POST", + url: "/profile/addNote", + data: {"username": username, + "visibility": $("#noteDropdown").val(), + "noteTextbox": $("#addNoteTextArea").val(), + "bonner": isBonner ? "yes" : "no"}, + success: function(response) { + target = isBonner ? "bonner" : "notes" + reloadWithAccordion(target) + } + }); + +}); + + $(".deleteNoteButton").click(function() { + let username = $(this).data('username') + let noteid = $(this).data('noteid') + $.ajax({ + method: "POST", + url: "/" + username + "/deleteNote", + data: {"id": noteid}, + success: function(response) { + reloadWithAccordion("notes") + } + }); + }); + + /* + * Background Check Functionality + */ + // Updates the Background check of a volunteer in the database + $(".savebtn").click(function () { + $(this).prop("disabled", true); + let bgCheckType = $(this).data("id") + + var bgStatusInput = $("#" + bgCheckType) + var bgDateInput = $("#" + bgCheckType + "_date") + + let bgDate = bgDateInput.val() + let bgStatus = $("[data-id=" + bgCheckType + "]").val() + + if (bgStatus == '') { + bgStatusInput.focus() + bgStatusInput.addClass("invalid"); + window.setTimeout(() => bgStatusInput.removeClass("invalid"), 1000); + $(this).prop("disabled", false); + return false + } + + if (bgDate == ''){ + bgDateInput.focus() + bgDateInput.addClass("invalid"); + window.setTimeout(() => bgDateInput.removeClass("invalid"), 1000); + $(this).prop("disabled", false); + return false + } + + let data = { + bgStatus: bgStatus, // Expected to be one of the three background check statuses + user: $(this).data("username"), // Expected to be the username of a volunteer in the database + bgType: $(this).attr("id"), // Expected to be the ID of a background check in the database + bgDate: bgDate // Expected to be the date of the background check completion or '' if field is empty + } + $.ajax({ + url: "/addBackgroundCheck", + type: "POST", + data: data, + success: function(s){ + var date = new Date(data.bgDate + " 12:00").toLocaleDateString() + reloadWithAccordion("background") + }, + error: function(error, status){ + console.log(error, status) + } + }) + }); + + $("#bgHistoryTable").on("click", "#deleteBgHistory", function() { + let data = { + bgID: $(this).data("id"), // Expected to be the ID of a background check in the database + } + $(this).closest("li").remove(); + + $.ajax({ + url: "/deleteBackgroundCheck", + type: "POST", + data: data, + success: function(s){ + msgToast("Background Check", "Successfully deleted background check.") + }, + error: function(error, status){ + console.log(error,status) + } + }) + }); + // Popover functionality + var requiredTraining = $(".trainingPopover"); + requiredTraining.popover({ + trigger: "hover", + sanitize: false, + html: true, + content: function() { + return $(this).attr('data-content'); + } + }); + + setupPhoneNumber("#updatePhone", "#phoneInput") + + $(".saveDiet").on('click', function() { + let data = { + dietInfo: $("#diet").val(), + user: $(this).data("user") + } + $.ajax({ + type: "POST", + url: "/updateDietInformation", + data: data, + success: function(s){ + reloadWithAccordion("dietaryInformation"); + }, + }) + }); + +}); + +function updateManagers(el, volunteer_username ){// retrieve the data of the student staff and program id if the boxes are checked or not + let program_id=$(el).attr('data-programid'); + let programName = $(el).attr('data-programName') + let name = $(el).attr('data-name') + let action= el.checked ? 'add' : 'remove'; + let removeMessage = (name + " is no longer the manager of " + programName + ".") + let addMessage = (name + " is now the manager of " + programName + ".") + + $.ajax({ + method:"POST", + url:"/updateProgramManager", + data : {"user_name":volunteer_username, //student staff: user_name + "program_id":program_id, // program id + "action":action, //action: add or remove + }, + + success: function(s){ + if(action == "add"){ + msgToast("Program manager", addMessage) + } else if(action == 'remove'){ + msgToast("Program manager", removeMessage) + } + }, + error: function(error, status){ + console.log(error, status) + } + }) +} diff --git a/app/templates/main/userProfile.html b/app/templates/main/userProfile.html index 389368b57..1a72f61c4 100644 --- a/app/templates/main/userProfile.html +++ b/app/templates/main/userProfile.html @@ -1,589 +1,582 @@ -{%set title ="User Profile"%} -{% extends "base.html"%} - -{% block scripts %} - {{super()}} - - -{% endblock %} - -{% block styles %} - {{super()}} - - -{% endblock %} - -{% block app_content %} -
-

{{volunteer.firstName}} {{volunteer.lastName}}

-
-
{{volunteer.bnumber}}
-
{{volunteer.email}}
-
-
- {% if volunteer.major -%} -
{{volunteer.major}}
- {% endif %} - {% if volunteer.classLevel -%} -
{{volunteer.classLevel}}
- {% endif %} -
-
-
- - Edit -
  -
-
- -
-
- -
-
- - - - -
-

- {% set focus = "open" if visibleAccordion == "upcoming" or (not visibleAccordion and volunteer.username == g.current_user.username) else "collapsed" %} - -

- {% set show = "show" if visibleAccordion == "upcoming" or (not visibleAccordion and volunteer.username == g.current_user.username) else ""%} -
-
-
- - {% if upcomingEvents|length > 0 %} - - - - - - - - - {% for event in upcomingEvents %} - - - - - - - {% if (event.isRsvpRequired) and (event.id in rsvpedEvents) %} - - {% elif (event.isRsvpRequired) and (event.id not in rsvpedEvents) %} - - {% else %} - - {% endif %} - - {% endfor %} - {% else %} -
You have no upcoming events.
- {% endif %} -
ProgramEvent NameEvent DateStart TimeLocationRSVP Status
{{event.program.programName if event.program else '--'}}{{event.name}}{{event.startDate.strftime('%m/%d/%Y')}}{{event.timeStart.strftime('%-I:%M %p')}}{{event.location}}You have RSVP'd for this event.You have not RSVP'd for this event.This event does not require RSVP.
-
-
-
-
- - - -
-

- {% set focus = "open" if visibleAccordion == "history" else "collapsed" %} - -

- {% set show = "show" if visibleAccordion == "history" else "" %} -
-
-
- - {% if participatedEvents|length > 0 %} - - - - - - {% for event in participatedEvents %} - - - - - - {% endfor %} - {% else %} -
You have not participated in any events.
- {% endif %} -
ProgramEvent NameEvent Date
{{event.programName}}{{event.name}}{{event.startDate.strftime('%m/%d/%Y')}}
-
-
-
-
- - - -
-

- {% set focus = "open" if visibleAccordion == "programTable" or (not visibleAccordion and volunteer.username != g.current_user.username) else "collapsed" %} - -

- {% set show = "show" if visibleAccordion == "programTable" or (not visibleAccordion and volunteer.username != g.current_user.username) else "" %} -
-
-
- - - - - - - {% if g.current_user.isCeltsAdmin or g.current_user.isCeltsStudentStaff%} - - {% endif %} - - - - - {% for row in eligibilityTable %} - {% set trainingList = row['trainingList'][volunteer.username] %} - {% set checked = "" %} - {% if row.program in programsInterested %} - {% set checked = "checked" %} - {% endif %} - {% from 'trainingsHoverMacro.html' import trainingsHover %} - - - - - {% if g.current_user.isCeltsAdmin or g.current_user.isCeltsStudentStaff %} - {% if row.isNotBanned %} - - {% set label = "Ban" %} - {% else %} - - {% set label = "Unban" %} - {% endif %} - - {% endif %} - - {% endfor %} - -
ProgramNotifyTrainingEligibility
{{row.program.programName}} - {% set hasCompletedAllTrainings = row.completedTraining %} -    - View - Eligible Banned - {% if g.current_user.isCeltsAdmin %} - - {% endif %} -
- For more information about CELTS opportunities, click here. -
-
-
-
- - - - {% if participatedInLabor or volunteer.isCeltsStudentStaff%} -
-

- {% set focus = "open" if visibleAccordion == "celtsLabor" else "collapsed" %} - -

- {% set show = "show" if visibleAccordion == "celtsLabor" else "" %} -
-
- -
- {% if participatedInLabor %} -
-
CELTS Labor History:
- {% for program, term in participatedInLabor.items() %} -

{{term}}: {{program}}

- {% endfor %} -
- {% endif %} - {% if volunteer.isCeltsStudentStaff %} -
- {% if g.current_user.isCeltsAdmin %} -
Choose programs to manage:
- {% else %} -
{{volunteer.firstName}} {{volunteer.lastName}} is the manager of:
- {% endif %} -
    - {% for program in programs %} - {% set action = "add" %} - {% set checked = "" %} - {% if program.id in permissionPrograms %} - {% set action = "remove" %} - {% set checked = "checked" %} - {% if not g.current_user.isCeltsAdmin%} -
  • {{program.programName}}
  • - {% endif %} - {% endif %} - {% if g.current_user.isCeltsAdmin%} -
  • - - -
  • - {% endif %} - {% endfor %} -
-
- {% endif %} -
-
-
-
- {% endif %} - - - - {% if g.current_user.isCeltsAdmin %} -
-

- {% set focus = "open" if visibleAccordion == "background" else "collapsed" %} - -

- {% set show = "show" if visibleAccordion == "background" else "" %} -
-
-
- - - - - {% if g.current_user.isCeltsAdmin%} - - - - {% endif %} - - - - {% for bgType in backgroundTypes %} - - - {% if g.current_user.isCeltsAdmin %} - - - - {% endif %} - - {%endfor%} - -
DescriptionStatusDate
-
{{bgType.description}}
-
    - {% for bgStatus in allBackgroundHistory[bgType.id] %} -
  • - {{bgStatus.backgroundCheckStatus}}: {{bgStatus.dateCompleted.strftime("%m/%d/%Y")}} - -
  • - {% endfor %} -
-
- -
-
-
-
-
- {% endif %} - - - -
-

- {% set focus = "open" if visibleAccordion == "notes" else "collapsed" %} - -

- {% set show = "show" if visibleAccordion == "notes" else "" %} -
-
- - - - - - - - - - - - {% if g.current_user.isCeltsAdmin %} - {% set userTier = 3 %} - {% elif g.current_user.isCeltsStudentStaff %} - {% set userTier = 2 %} - {% else %} - {% set userTier = 1 %} - {% endif %} - {% set note = namespace(count=0) %} - {% for row in profileNotes %} - {% if userTier >= row.viewTier and (not row.isBonnerNote or g.current_user.isBonnerScholar) %} - - - - - - - - {% set note.count = note.count + 1 %} - {% endif %} - {% endfor %} - -
DateCreatorNoteVisible To
{{row.note.createdOn.strftime('%m/%d/%Y')}}{{row.note.createdBy.firstName+ " "+ row.note.createdBy.lastName}}{{ " (you)" if row.note.createdBy == g.current_user else ""}}{{row.note.noteContent}} - {% set bonner = "Bonner " if row.isBonnerNote else "" %} - {% if row.viewTier == 3 %} - {{bonner}}Admins - {% elif row.viewTier == 2 %} - {{bonner}}Admins/Student Staff - {% else %} - {{ "Bonner Scholar " if row.isBonnerNote else "Everyone"}} - {% endif %} - - {% if (g.current_user == row.note.createdBy) or g.current_user.isCeltsAdmin%} - - {% else %} - - {% endif %} -
- {% if note.count == 0 %} - There are no notes yet - {% endif %} -
-
- -
-
-
- - - -
-

- {% set focus = "open" if visibleAccordion == "dietaryInformation" else "collapsed" %} - -

- {% set show = "show" if visibleAccordion == "dietaryInformation" else "" %} -
-
-
-
-
- - -
-
- -
-
-
-
-
- - - - {% set canViewBonner = (g.current_user.isCeltsAdmin or g.current_user == volunteer) %} - {% if volunteer.isBonnerScholar and canViewBonner %} -
-

- {% set focus = "open" if visibleAccordion == "bonner" else "collapsed" %} - -

- {% set show = "show" if visibleAccordion == "bonner" else "" %} -
-
-
-
- {# Bonner Events #} - {% set address = "Your" if g.current_user == volunteer else volunteer.firstName~"'s" %} -
{{address}} Upcoming Bonner Events
- {% set bonnerEvents = upcomingEvents|selectattr("program.isBonnerScholars")|list %} - {% if bonnerEvents|length %} -
    - {% for event in bonnerEvents %} -
  • -
    {{event.startDate.strftime('%B %d')}} at {{event.timeStart.strftime('%-I:%M%p')|lower}}:
    - -
  • - {% endfor %} -
- {% else %} -

No RSVPs for any upcoming Bonner events.

- {% endif %} -
- - {# Bonner Notes #} -
Notes
- {% set bonnerNotes = profileNotes|selectattr("isBonnerNote")|list %} - {% if bonnerNotes|length %} -
- {% for row in profileNotes|selectattr("isBonnerNote") %} -
{{row.note.noteContent}} -
{{row.note.createdBy.fullName}} {{row.note.createdOn.strftime('%m/%d/%Y')}} - {% endfor %} -
- {% else %} -

No one has left a Bonner note.

- {% endif %} - -
- - {# Bonner Requirements #} -
-
Requirement Progress
-
    - {% for req in bonnerRequirements %} -
  • {{req.name}} - {% if req.completed %} - - {% else %} - - {% endif %} -
  • - {% endfor %} -
-
-
-
-
-
- {% endif %} - -
- - - - - - - - -{% endblock %} +{%set title ="User Profile"%} +{% extends "base.html"%} + +{% block scripts %} + {{super()}} + + +{% endblock %} + +{% block styles %} + {{super()}} + + +{% endblock %} + +{% block app_content %} +
+

{{volunteer.firstName}} {{volunteer.lastName}}

+
+
{{volunteer.bnumber}}
+
{{volunteer.email}}
+
+
+ {% if volunteer.major -%} +
{{volunteer.major}}
+ {% endif %} + {% if volunteer.classLevel -%} +
{{volunteer.classLevel}}
+ {% endif %} +
+
+
+ + Edit +
  +
+
+ +
+
+ +
+
+ + + + +
+

+ {% set focus = "open" if visibleAccordion == "upcoming" or (not visibleAccordion and volunteer.username == g.current_user.username) else "collapsed" %} + +

+ {% set show = "show" if visibleAccordion == "upcoming" or (not visibleAccordion and volunteer.username == g.current_user.username) else ""%} +
+
+
+ + {% if upcomingEvents|length > 0 %} + + + + + + + + + {% for event in upcomingEvents %} + + + + + + + {% if (event.isRsvpRequired) and (event.id in rsvpedEvents) %} + + {% elif (event.isRsvpRequired) and (event.id not in rsvpedEvents) %} + + {% else %} + + {% endif %} + + {% endfor %} + {% else %} +
You have no upcoming events.
+ {% endif %} +
ProgramEvent NameEvent DateStart TimeLocationRSVP Status
{{event.program.programName if event.program else '--'}}{{event.name}}{{event.startDate.strftime('%m/%d/%Y')}}{{event.timeStart.strftime('%-I:%M %p')}}{{event.location}}You have RSVP'd for this event.You have not RSVP'd for this event.This event does not require RSVP.
+
+
+
+
+ + + +
+

+ {% set focus = "open" if visibleAccordion == "history" else "collapsed" %} + +

+ {% set show = "show" if visibleAccordion == "history" else "" %} +
+
+
+ + {% if participatedEvents|length > 0 %} + + + + + + {% for event in participatedEvents %} + + + + + + {% endfor %} + {% else %} +
You have not participated in any events.
+ {% endif %} +
ProgramEvent NameEvent Date
{{event.programName}}{{event.name}}{{event.startDate.strftime('%m/%d/%Y')}}
+
+
+
+
+ + + +
+

+ {% set focus = "open" if visibleAccordion == "programTable" or (not visibleAccordion and volunteer.username != g.current_user.username) else "collapsed" %} + +

+ {% set show = "show" if visibleAccordion == "programTable" or (not visibleAccordion and volunteer.username != g.current_user.username) else "" %} +
+
+
+ + + + + + + {% if g.current_user.isCeltsAdmin or g.current_user.isCeltsStudentStaff%} + + {% endif %} + + + + + {% for row in eligibilityTable %} + {% set trainingList = row['trainingList'][volunteer.username] %} + {% set checked = "" %} + {% if row.program in programsInterested %} + {% set checked = "checked" %} + {% endif %} + {% from 'trainingsHoverMacro.html' import trainingsHover %} + + + + + {% if g.current_user.isCeltsAdmin or g.current_user.isCeltsStudentStaff %} + {% if row.isNotBanned %} + + {% set label = "Ban" %} + {% else %} + + {% set label = "Unban" %} + {% endif %} + + {% endif %} + + {% endfor %} + +
ProgramNotifyTrainingEligibility
{{row.program.programName}} + {% set hasCompletedAllTrainings = row.completedTraining %} +    + View + Eligible Banned + {% if g.current_user.isCeltsAdmin %} + + {% endif %} +
+ For more information about CELTS opportunities, click here. +
+
+
+
+ + + + {% if participatedInLabor or volunteer.isCeltsStudentStaff%} +
+

+ {% set focus = "open" if visibleAccordion == "celtsLabor" else "collapsed" %} + +

+ {% set show = "show" if visibleAccordion == "celtsLabor" else "" %} +
+
+ +
+ {% if participatedInLabor %} +
+
CELTS Labor History:
+ {% for program, term in participatedInLabor.items() %} +

{{term}}: {{program}}

+ {% endfor %} +
+ {% endif %} + {% if volunteer.isCeltsStudentStaff %} +
+ {% if g.current_user.isCeltsAdmin %} +
Choose programs to manage:
+ {% else %} +
{{volunteer.firstName}} {{volunteer.lastName}} is the manager of:
+ {% endif %} +
    + {% for program in programs %} + {% set action = "add" %} + {% set checked = "" %} + {% if program.id in permissionPrograms %} + {% set action = "remove" %} + {% set checked = "checked" %} + {% if not g.current_user.isCeltsAdmin%} +
  • {{program.programName}}
  • + {% endif %} + {% endif %} + {% if g.current_user.isCeltsAdmin%} +
  • + + +
  • + {% endif %} + {% endfor %} +
+
+ {% endif %} +
+
+
+
+ {% endif %} + + + + {% if g.current_user.isCeltsAdmin %} +
+

+ {% set focus = "open" if visibleAccordion == "background" else "collapsed" %} + +

+ {% set show = "show" if visibleAccordion == "background" else "" %} +
+
+
+ + + + + {% if g.current_user.isCeltsAdmin%} + + + + {% endif %} + + + + {% for bgType in backgroundTypes %} + + + {% if g.current_user.isCeltsAdmin %} + + + + {% endif %} + + {%endfor%} + +
DescriptionStatusDate
+
{{bgType.description}}
+
    + {% for bgStatus in allBackgroundHistory[bgType.id] %} +
  • + {{bgStatus.backgroundCheckStatus}}: {{bgStatus.dateCompleted.strftime("%m/%d/%Y")}} + +
  • + {% endfor %} +
+
+ +
+
+
+
+
+ {% endif %} + + + +
+

+ {% set focus = "open" if visibleAccordion == "notes" else "collapsed" %} + +

+ {% set show = "show" if visibleAccordion == "notes" else "" %} +
+
+ + + + + + + + + + + + {% if g.current_user.isCeltsAdmin %} + {% set userTier = 3 %} + {% elif g.current_user.isCeltsStudentStaff %} + {% set userTier = 2 %} + {% else %} + {% set userTier = 1 %} + {% endif %} + {% set note = namespace(count=0) %} + {% for row in profileNotes %} + {% if userTier >= row.viewTier and (not row.isBonnerNote or g.current_user.isBonnerScholar) %} + + + + + + + + {% set note.count = note.count + 1 %} + {% endif %} + {% endfor %} + +
DateCreatorNoteVisible To
{{row.note.createdOn.strftime('%m/%d/%Y')}}{{row.note.createdBy.firstName+ " "+ row.note.createdBy.lastName}}{{ " (you)" if row.note.createdBy == g.current_user else ""}}{{row.note.noteContent}} + {% set bonner = "Bonner " if row.isBonnerNote else "" %} + {% if row.viewTier == 3 %} + {{bonner}}Admins + {% elif row.viewTier == 2 %} + {{bonner}}Admins/Student Staff + {% else %} + {{ "Bonner Scholar " if row.isBonnerNote else "Everyone"}} + {% endif %} + + {% if (g.current_user == row.note.createdBy) or g.current_user.isCeltsAdmin%} + + {% else %} + + {% endif %} +
+ {% if note.count == 0 %} + There are no notes yet + {% endif %} +
+
+ +
+
+
+ + + +
+

+ {% set focus = "open" if visibleAccordion == "dietaryInformation" else "collapsed" %} + +

+ {% set show = "show" if visibleAccordion == "dietaryInformation" else "" %} +
+
+
+
+
+ + +
+
+ +
+
+
+
+
+ + + + {% set canViewBonner = (g.current_user.isCeltsAdmin or g.current_user == volunteer) %} + {% if volunteer.isBonnerScholar and canViewBonner %} +
+

+ {% set focus = "open" if visibleAccordion == "bonner" else "collapsed" %} + +

+ {% set show = "show" if visibleAccordion == "bonner" else "" %} +
+
+
+
+ {# Bonner Events #} + {% set address = "Your" if g.current_user == volunteer else volunteer.firstName~"'s" %} +
{{address}} Upcoming Bonner Events
+ {% set bonnerEvents = upcomingEvents|selectattr("program.isBonnerScholars")|list %} + {% if bonnerEvents|length %} +
    + {% for event in bonnerEvents %} +
  • +
    {{event.startDate.strftime('%B %d')}} at {{event.timeStart.strftime('%-I:%M%p')|lower}}:
    + +
  • + {% endfor %} +
+ {% else %} +

No RSVPs for any upcoming Bonner events.

+ {% endif %} +
+ + {# Bonner Notes #} +
Notes
+ {% set bonnerNotes = profileNotes|selectattr("isBonnerNote")|list %} + {% if bonnerNotes|length %} +
+ {% for row in profileNotes|selectattr("isBonnerNote") %} +
{{row.note.noteContent}} +
{{row.note.createdBy.fullName}} {{row.note.createdOn.strftime('%m/%d/%Y')}} + {% endfor %} +
+ {% else %} +

No one has left a Bonner note.

+ {% endif %} + +
+ + {# Bonner Requirements #} +
+
Requirement Progress
+
    + {% for req in bonnerRequirements %} +
  • {{req.name}} + {% if req.completed %} + + {% else %} + + {% endif %} +
  • + {% endfor %} +
+
+
+
+
+
+ {% endif %} + +
+ + + + + + + + +{% endblock %} From 6f4a093815a1496f05ff5e44f6dc0cf308102b44 Mon Sep 17 00:00:00 2001 From: Karina Agliullova Date: Mon, 11 Mar 2024 12:45:51 -0400 Subject: [PATCH 12/58] Added tests, modififed code to fix consistency issue --- app/static/js/userProfile.js | 7 ++++--- app/templates/main/userProfile.html | 8 +++++--- tests/code/test_transcripts.py | 14 +++++++++++--- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/app/static/js/userProfile.js b/app/static/js/userProfile.js index bbde04400..838d773db 100644 --- a/app/static/js/userProfile.js +++ b/app/static/js/userProfile.js @@ -27,15 +27,16 @@ $(document).ready(function(){ }); $(".removeFromTranscriptCheckbox").each(function() { - var isChecked = $(this).data('isChecked'); // Assuming you pass this data attribute from the backend - $(this).prop("checked", isChecked); + var programID = $(this).data('programId'); // Make sure you have this data attribute + var isChecked = $(this).data('isChecked'); + $("#removeFromTranscriptCheckbox_" + programID).prop("checked", isChecked); }); // Add event listener to save checkbox state on change $('.removeFromTranscriptCheckbox').click(function() { var removeFromTranscript = $(this).is(':checked'); var username = $(this).data('username'); - + var programID = $(this).data('programId'); $.ajax({ type: "POST", diff --git a/app/templates/main/userProfile.html b/app/templates/main/userProfile.html index 1a72f61c4..e8bd4c8ce 100644 --- a/app/templates/main/userProfile.html +++ b/app/templates/main/userProfile.html @@ -449,8 +449,8 @@
Notes
{% if bonnerNotes|length %}
{% for row in profileNotes|selectattr("isBonnerNote") %} -
{{row.note.noteContent}} -
{{row.note.createdBy.fullName}} {{row.note.createdOn.strftime('%m/%d/%Y')}} +
{{row.note.noteContent}}
+
{{row.note.createdBy.fullName}} {{row.note.createdOn.strftime('%m/%d/%Y')}}
{% endfor %}
{% else %} @@ -560,9 +560,11 @@
Reason for ban:
End Date:  
+ {% for program in transcriptData %}
- + + {% endfor %}
diff --git a/tests/code/test_transcripts.py b/tests/code/test_transcripts.py index 92abe4a8f..7c44ee92c 100644 --- a/tests/code/test_transcripts.py +++ b/tests/code/test_transcripts.py @@ -55,7 +55,7 @@ def setup(): endDate = "2021-12-13", recurringId = None, program= 5) - + EventParticipant.create(user = testUser, event = newBonnerEvent, attended = True, @@ -116,7 +116,7 @@ def setup(): endDate = "2021-12-13", recurringId = None, program = 9) - + EventParticipant.create(user = testUser, event = newNonProgramEvent, attended = True, @@ -179,6 +179,15 @@ def testingProgram(): assert programDict assert checkingProgram in [t for t in programDict] +@pytest.mark.integration +def testingProgramTranscriptBannedPrograms(): + + username = "ramsayb2" + transcriptData = getProgramTranscript(username) + bannedProgram = Program.get_by_id(2) + + assert bannedProgram not in transcriptData + @pytest.mark.integration def testingTotalHours(): @@ -206,4 +215,3 @@ def test_getStartYear(): # Course Participant with no Event Participant CourseParticipant.create(course=newCourse, user='namet', hoursEarned=3.0) assert getStartYear('namet') == 2022 - From c43cfd5aaabf7583c2563821fbec9a57f1108920 Mon Sep 17 00:00:00 2001 From: Fleur Gahimbare Date: Mon, 11 Mar 2024 17:54:06 -0400 Subject: [PATCH 13/58] readd code to show/hide checkbox --- app/static/js/userProfile.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/static/js/userProfile.js b/app/static/js/userProfile.js index 88b5d26f5..d2795f24d 100644 --- a/app/static/js/userProfile.js +++ b/app/static/js/userProfile.js @@ -42,7 +42,7 @@ $(document).ready(function(){ } }); }); - + $(".removeFromTranscriptCheckbox").each(function() { var programID = $(this).data('programId'); // Make sure you have this data attribute var isChecked = $(this).data('isChecked'); @@ -119,15 +119,16 @@ $(document).ready(function(){ $("#modalProgramName").text("Program: " + $(this).data("name ")); $("#banModal").modal("toggle"); banNoteDiv.hide(); + $("#removeFromTranscriptDiv").hide(); $("#banNoteTxtArea").val(""); $("#banButton").prop("disabled", true); if( $(this).val()=="Unban"){ banEndDateDiv.hide() banEndDatepicker.val("0001-01-01") //This is a placeholder value for the if statement in line 52 to work properly #PLCHLD1 banNoteDiv.show() + $("#removeFromTranscriptDiv").show(); banNote.text($(this).data("note")) } - }); From 22a3dc062cd6911d872e8be61e40b991ecf8690f Mon Sep 17 00:00:00 2001 From: Karina Agliullova Date: Tue, 19 Mar 2024 14:08:55 -0400 Subject: [PATCH 14/58] Fixing the checkbox --- app/controllers/main/routes.py | 32 ++++++++++++++--------------- app/static/js/userProfile.js | 19 +++++++++++------ app/templates/main/userProfile.html | 22 +++++++++----------- 3 files changed, 39 insertions(+), 34 deletions(-) diff --git a/app/controllers/main/routes.py b/app/controllers/main/routes.py index 25231e3cf..64cf944c6 100644 --- a/app/controllers/main/routes.py +++ b/app/controllers/main/routes.py @@ -3,7 +3,7 @@ from peewee import JOIN from http import cookies from playhouse.shortcuts import model_to_dict -from flask import request, render_template, g, abort, flash, redirect, url_for +from flask import request, render_template, g, abort, flash, redirect, url_for, jsonify from app.controllers.main import main_bp from app import app @@ -55,7 +55,7 @@ def landingPage(): .distinct() .execute()) # Ensure only unique programs are included - return render_template("/main/landingPage.html", + return render_template("/main/landingPage.html", managerProgramDict=managerProgramDict, term=g.current_term, programsWithEventsList=programsWithEventsList) @@ -73,20 +73,20 @@ def events(selectedTerm, activeTab, programID): if selectedTerm: currentTerm = selectedTerm currentTime = datetime.datetime.now() - + listOfTerms = Term.select() participantRSVP = EventRsvp.select(EventRsvp, Event).join(Event).where(EventRsvp.user == g.current_user) rsvpedEventsID = [event.event.id for event in participantRSVP] - term = Term.get_by_id(currentTerm) - + term = Term.get_by_id(currentTerm) + currentEventRsvpAmount = getEventRsvpCountsForTerm(term) studentLedEvents = getStudentLedEvents(term) countUpcomingStudentLedEvents = getUpcomingStudentLedCount(term, currentTime) trainingEvents = getTrainingEvents(term, g.current_user) bonnerEvents = getBonnerEvents(term) otherEvents = getOtherEvents(term) - + managersProgramDict = getManagerProgramDict(g.current_user) return render_template("/events/event_list.html", @@ -146,7 +146,7 @@ def viewUsersProfile(username): ProgramBan.program == program, ProgramBan.endDate > datetime.datetime.now()).execute()) userParticipatedTrainingEvents = getParticipationStatusForTrainings(program, [volunteer], g.current_term) - try: + try: allTrainingsComplete = False not in [attended for event, attended in userParticipatedTrainingEvents[username]] # Did volunteer attend all events except KeyError: allTrainingsComplete = False @@ -201,7 +201,7 @@ def emergencyContactInfo(username): contactInfo=contactInfo, readOnly=readOnly ) - + elif request.method == 'POST': if g.current_user.username != username: abort(403) @@ -210,8 +210,8 @@ def emergencyContactInfo(username): if not rowsUpdated: EmergencyContact.create(user = username, **request.form) createAdminLog(f"{g.current_user} updated {username}'s emergency contact information.") - flash('Emergency contact information saved successfully!', 'success') - + flash('Emergency contact information saved successfully!', 'success') + if request.args.get('action') == 'exit': return redirect (f"/profile/{username}") else: @@ -243,7 +243,7 @@ def insuranceInfo(username): if not rowsUpdated: InsuranceInfo.create(user = username, **request.form) createAdminLog(f"{g.current_user} updated {username}'s emergency contact information.") - flash('Insurance information saved successfully!', 'success') + flash('Insurance information saved successfully!', 'success') if request.args.get('action') == 'exit': return redirect (f"/profile/{username}") @@ -254,7 +254,7 @@ def insuranceInfo(username): def travelForm(username): if not (g.current_user.username == username or g.current_user.isCeltsAdmin): abort(403) - + user = (User.select(User, EmergencyContact, InsuranceInfo) .join(EmergencyContact, JOIN.LEFT_OUTER).switch() .join(InsuranceInfo, JOIN.LEFT_OUTER) @@ -507,12 +507,12 @@ def getDietInfo(): dietaryInfo = request.form user = dietaryInfo["user"] dietInfo = dietaryInfo["dietInfo"] - + if (g.current_user.username == user) or g.current_user.isAdmin: updateDietInfo(user, dietInfo) - userInfo = User.get(User.username == user) + userInfo = User.get(User.username == user) if len(dietInfo) > 0: - createAdminLog(f"Updated {userInfo.fullName}'s dietary restrictions to {dietInfo}.") if dietInfo.strip() else None + createAdminLog(f"Updated {userInfo.fullName}'s dietary restrictions to {dietInfo}.") if dietInfo.strip() else None else: createAdminLog(f"Deleted all {userInfo.fullName}'s dietary restrictions dietary restrictions.") @@ -522,5 +522,5 @@ def getDietInfo(): @main_bp.route('/profile//indicateInterest', methods=['POST']) def indicateMinorInterest(username): toggleMinorInterest(username) - + return "" diff --git a/app/static/js/userProfile.js b/app/static/js/userProfile.js index d2795f24d..3e0403ced 100644 --- a/app/static/js/userProfile.js +++ b/app/static/js/userProfile.js @@ -14,7 +14,7 @@ $(document).ready(function(){ msgToast("Error!", "Failed to save changes!") } }); - }) + }) $("#printButton").on("click", function() { let username = $(this).data('username') @@ -25,7 +25,7 @@ $(document).ready(function(){ $(".notifyInput").click(function updateInterest(){ var programID = $(this).data("programid"); var username = $(this).data('username'); - + var interest = $(this).is(':checked'); var routeUrl = interest ? "addInterest" : "removeInterest"; @@ -55,6 +55,9 @@ $(document).ready(function(){ var username = $(this).data('username'); var programID = $(this).data('programId'); + $("#removeFromTranscriptCheckbox").data("removedFromTranscript", removeFromTranscript); + console.log(removeFromTranscript); + $.ajax({ type: "POST", url: `/profile/${username}/updateTranscript/${programID}`, @@ -101,6 +104,7 @@ $(document).ready(function(){ /* * Ban Functionality */ + var programID; $(".ban").click(function() { var banButton = $("#banButton") var banEndDateDiv = $("#banEndDate") // Div containing the datepicker in the ban modal @@ -126,6 +130,10 @@ $(document).ready(function(){ banEndDateDiv.hide() banEndDatepicker.val("0001-01-01") //This is a placeholder value for the if statement in line 52 to work properly #PLCHLD1 banNoteDiv.show() + var isRemovedFromTranscript = $(this).data("removedFromTranscript"); + console.log(isRemovedFromTranscript); + console.log(banButton); + $("#removeFromTranscriptCheckbox").prop("checked", isRemovedFromTranscript); $("#removeFromTranscriptDiv").show(); banNote.text($(this).data("note")) } @@ -176,9 +184,9 @@ $(document).ready(function(){ $("#noteModal").modal("toggle") }); - $("#addVisibility").click(function() { + $("#addVisibility").click(function() { var bonnerChecked = $("input[name='bonner']:checked").val() - + if (bonnerChecked == 'on') { bonnerNoteOn() } else { @@ -226,7 +234,7 @@ $(document).ready(function(){ * Background Check Functionality */ // Updates the Background check of a volunteer in the database - $(".savebtn").click(function () { + $(".savebtn").click(function () { $(this).prop("disabled", true); let bgCheckType = $(this).data("id") @@ -348,4 +356,3 @@ function updateManagers(el, volunteer_username ){// retrieve the data of the stu } }) } - diff --git a/app/templates/main/userProfile.html b/app/templates/main/userProfile.html index 43e285bbc..a8f6b6d73 100644 --- a/app/templates/main/userProfile.html +++ b/app/templates/main/userProfile.html @@ -16,7 +16,7 @@ {% block app_content %} {% if volunteer.isStudent and not totalSustainedEngagements%}
-

Did you know we have a Civic and Community Engagement Minor? Click +

Did you know we have a Civic and Community Engagement Minor? Click here for more information.

@@ -41,7 +41,7 @@

{{volunteer.firstName}} {{volunteer.lastName}}

{{volunteer.major}}
{% endif %} {% if volunteer.classLevel -%} -
{{volunteer.classLevel}}
+
{{volunteer.classLevel}}
{% endif %}
@@ -202,7 +202,7 @@

{% endif %} {% if g.current_user.isCeltsAdmin %} - + {% endif %} {% endif %} @@ -233,7 +233,7 @@

CELTS Labor History:
{% for program, term in participatedInLabor.items() %} -

{{term}}: {{program}}

+

{{term}}: {{program}}

{% endfor %}
{% endif %} @@ -515,9 +515,9 @@

- -
+ +
{% if g.current_user.isBonnerScholar and volunteer.isBonnerScholar %}
@@ -535,7 +535,7 @@
{% endif %}
- +
@@ -573,12 +573,10 @@
Reason for ban:
End Date:  
- {% for program in transciptData %}
- +
- {% endfor %}
From 33dfc1ad2e2ad75c55076aed64785bf6cf647a04 Mon Sep 17 00:00:00 2001 From: Karina Agliullova Date: Tue, 19 Mar 2024 15:51:08 -0400 Subject: [PATCH 15/58] Fixing the checkbox frontend --- app/static/js/userProfile.js | 9 ++++++--- app/templates/main/userProfile.html | 1 + 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/static/js/userProfile.js b/app/static/js/userProfile.js index 3e0403ced..cb0ea126c 100644 --- a/app/static/js/userProfile.js +++ b/app/static/js/userProfile.js @@ -55,8 +55,11 @@ $(document).ready(function(){ var username = $(this).data('username'); var programID = $(this).data('programId'); - $("#removeFromTranscriptCheckbox").data("removedFromTranscript", removeFromTranscript); - console.log(removeFromTranscript); + var editButton = $(".ban").filter(function(editButton){ + return $(editButton).data("programid") == programID; + }) + + editButton.data("removedFromTranscript", removeFromTranscript); $.ajax({ type: "POST", @@ -106,6 +109,7 @@ $(document).ready(function(){ */ var programID; $(".ban").click(function() { + $("#banModal #programid").val($(this).data("programid")); var banButton = $("#banButton") var banEndDateDiv = $("#banEndDate") // Div containing the datepicker in the ban modal var banEndDatepicker = $("#banEndDatepicker") // Datepicker in the ban modal @@ -132,7 +136,6 @@ $(document).ready(function(){ banNoteDiv.show() var isRemovedFromTranscript = $(this).data("removedFromTranscript"); console.log(isRemovedFromTranscript); - console.log(banButton); $("#removeFromTranscriptCheckbox").prop("checked", isRemovedFromTranscript); $("#removeFromTranscriptDiv").show(); banNote.text($(this).data("note")) diff --git a/app/templates/main/userProfile.html b/app/templates/main/userProfile.html index a8f6b6d73..9fdca08a0 100644 --- a/app/templates/main/userProfile.html +++ b/app/templates/main/userProfile.html @@ -555,6 +555,7 @@