Coverage for app/controllers/main/routes.py: 23%
387 statements
« prev ^ index » next coverage.py v7.10.2, created at 2026-03-25 20:13 +0000
« prev ^ index » next coverage.py v7.10.2, created at 2026-03-25 20:13 +0000
1import json
2import datetime
3from peewee import JOIN, DoesNotExist
4from http import cookies
5from playhouse.shortcuts import model_to_dict
6from flask import request, render_template, jsonify, g, abort, flash, redirect, url_for, make_response, session, request
8from app.controllers.main import main_bp
9from app import app
10from app.models.term import Term
11from app.models.user import User
12from app.models.note import Note
13from app.models.event import Event
14from app.models.program import Program
15from app.models.interest import Interest
16from app.models.eventRsvp import EventRsvp
17from app.models.celtsLabor import CeltsLabor
18from app.models.programBan import ProgramBan
19from app.models.profileNote import ProfileNote
20from app.models.insuranceInfo import InsuranceInfo
21from app.models.certification import Certification
22from app.models.programManager import ProgramManager
23from app.models.backgroundCheck import BackgroundCheck
24from app.models.emergencyContact import EmergencyContact
25from app.models.eventParticipant import EventParticipant
26from app.models.courseInstructor import CourseInstructor
27from app.models.backgroundCheckType import BackgroundCheckType
29from app.logic.events import getUpcomingEventsForUser, getParticipatedEventsForUser, getTrainingEvents, getEventRsvpCountsForTerm, getUpcomingVolunteerOpportunitiesCount, getVolunteerOpportunities, getBonnerEvents, getCeltsLabor, getEngagementEvents, getPastVolunteerOpportunitiesCount
30from app.logic.transcript import *
31from app.logic.loginManager import logout
32from app.logic.searchUsers import searchUsers
33from app.logic.utils import selectSurroundingTerms
34from app.logic.celtsLabor import getCeltsLaborHistory
35from app.logic.createLogs import createRsvpLog, createActivityLog
36from app.logic.certification import getCertRequirementsWithCompletion
37from app.logic.landingPage import getManagerProgramDict, getActiveEventTab
38from app.logic.minor import toggleMinorInterest, declareMinorInterest, getCommunityEngagementByTerm, getEngagementTotal
39from app.logic.participants import unattendedRequiredEvents, trainedParticipants, getParticipationStatusForTrainings, checkUserRsvp, addPersonToEvent
40from app.logic.users import addUserInterest, removeUserInterest, banUser, unbanUser, isEligibleForProgram, getUserBGCheckHistory, addProfileNote, deleteProfileNote, updateDietInfo
42@main_bp.route('/logout', methods=['GET'])
43def redirectToLogout():
44 return redirect(logout())
46@main_bp.route('/', methods=['GET'])
47def landingPage():
49 managerProgramDict = getManagerProgramDict(g.current_user)
50 # Optimize the query to fetch programs with non-canceled, non-past events in the current term
52 programsWithEventsList = list(Program.select(Program, Event)
53 .join(Event)
54 .where((Event.term == g.current_term) & (Event.isCanceled == False))
55 .distinct()
56 .execute()) # Ensure only unique programs are included
57 # Limit returned list to events in the future
58 futureEvents = [p for p in programsWithEventsList if not p.event.isPastEnd]
60 return render_template("/main/landingPage.html",
61 managerProgramDict=managerProgramDict,
62 term=g.current_term,
63 programsWithEventsList = futureEvents)
68@main_bp.route('/goToEventsList/<programID>', methods=['GET'])
69def goToEventsList(programID):
70 return {"activeTab": getActiveEventTab(programID)}
72@main_bp.route('/eventsList/<selectedTerm>', methods=['GET'], defaults={'activeTab': "volunteerOpportunities", 'programID': 0})
73@main_bp.route('/eventsList/<selectedTerm>/', methods=['GET'], defaults={'activeTab': "volunteerOpportunities", 'programID': 0})
74@main_bp.route('/eventsList/<selectedTerm>/<activeTab>', methods=['GET'], defaults={'programID': 0})
75@main_bp.route('/eventsList/<selectedTerm>/<activeTab>/<programID>', methods=['GET'])
76def events(selectedTerm, activeTab, programID):
78 currentTime = datetime.datetime.now()
79 listOfTerms = Term.select().order_by(Term.termOrder)
80 participantRSVP = EventRsvp.select(EventRsvp, Event).join(Event).where(EventRsvp.user == g.current_user)
81 rsvpedEventsID = [event.event.id for event in participantRSVP]
83 term = g.current_term
84 if selectedTerm:
85 term = selectedTerm
87 # Make sure we have a Term object
88 term = Term.get_or_none(Term.id == term)
89 if term is None:
90 term = Term.get(Term.isCurrentTerm == True)
92 currentEventRsvpAmount = getEventRsvpCountsForTerm(term)
93 volunteerOpportunities = getVolunteerOpportunities(term)
94 countUpcomingVolunteerOpportunities = getUpcomingVolunteerOpportunitiesCount(term, currentTime)
95 countPastVolunteerOpportunities = getPastVolunteerOpportunitiesCount(term, currentTime)
96 trainingEvents = getTrainingEvents(term, g.current_user)
97 engagementEvents = getEngagementEvents(term)
98 bonnerEvents = getBonnerEvents(term)
99 celtsLabor = getCeltsLabor(term)
101 managersProgramDict = getManagerProgramDict(g.current_user)
103 # Fetch toggle state from session
104 toggleState = request.args.get('toggleState', 'unchecked')
106 # compile all volunteer opportunitiesevents into one list
107 studentEvents = []
108 for studentEvent in volunteerOpportunities.values():
109 studentEvents += studentEvent # add all contents of studentEvent to the studentEvents list
111 # Get the count of all term events for each category to display in the event list page.
112 volunteerOpportunitiesCount: int = len(studentEvents)
113 countUpcomingVolunteerOpportunitiesCount: int = len(countUpcomingVolunteerOpportunities)
114 countPastVolunteerOpportunitiesCount: int = len(countPastVolunteerOpportunities)
115 trainingEventsCount: int = len(trainingEvents)
116 engagementEventsCount: int = len(engagementEvents)
117 bonnerEventsCount: int = len(bonnerEvents)
118 celtsLaborCount: int = len(celtsLabor)
120 # gets only upcoming events to display in indicators
121 if (toggleState == 'unchecked'):
122 for event in trainingEvents:
123 if event.isPastEnd:
124 trainingEventsCount -= 1
125 for event in engagementEvents:
126 if event.isPastEnd:
127 engagementEventsCount -= 1
128 for event in bonnerEvents:
129 if event.isPastEnd:
130 bonnerEventsCount -= 1
131 for event in celtsLabor:
132 if event.isPastEnd:
133 celtsLaborCount -= 1
135 # Handle ajax request for Event category header number notifiers and toggle
136 if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
137 return jsonify({
138 "volunteerOpportunitiesCount": volunteerOpportunitiesCount,
139 "countPastVolunteerOpportunitiesCount": countPastVolunteerOpportunitiesCount,
140 "countUpcomingVolunteerOpportunitiesCount": countUpcomingVolunteerOpportunitiesCount,
141 "trainingEventsCount": trainingEventsCount,
142 "engagementEventsCount": engagementEventsCount,
143 "bonnerEventsCount": bonnerEventsCount,
144 "celtsLaborCount": celtsLaborCount,
145 "toggleStatus": toggleState
146 })
147 return render_template("/events/eventList.html",
148 selectedTerm = term,
149 volunteerOpportunities = volunteerOpportunities,
150 trainingEvents = trainingEvents,
151 engagementEvents = engagementEvents,
152 bonnerEvents = bonnerEvents,
153 celtsLabor = celtsLabor,
154 listOfTerms = listOfTerms,
155 rsvpedEventsID = rsvpedEventsID,
156 currentEventRsvpAmount = currentEventRsvpAmount,
157 currentTime = currentTime,
158 user = g.current_user,
159 activeTab = activeTab,
160 programID = int(programID),
161 managersProgramDict = managersProgramDict,
162 countUpcomingVolunteerOpportunities = countUpcomingVolunteerOpportunities,
163 countPastVolunteerOpportunities = countPastVolunteerOpportunities,
164 toggleState = toggleState,
165 )
167@main_bp.route('/profile/<username>', methods=['GET'])
168def viewUsersProfile(username):
169 """
170 This function displays the information of a volunteer to the user
171 """
172 try:
173 volunteer = User.get(User.username == username)
174 except Exception as e:
175 if g.current_user.isAdmin:
176 flash(f"{username} does not exist! ", category='danger')
177 return redirect(url_for('admin.studentSearchPage'))
178 else:
179 abort(403) # Error 403 if non admin/student-staff user trys to access via url
181 if (g.current_user == volunteer) or g.current_user.isAdmin:
182 upcomingEvents = getUpcomingEventsForUser(volunteer)
183 participatedEvents = getParticipatedEventsForUser(volunteer)
184 programs = Program.select()
185 if not g.current_user.isBonnerScholar and not g.current_user.isAdmin:
186 programs = programs.where(Program.isBonnerScholars == False)
187 interests = Interest.select(Interest, Program).join(Program).where(Interest.user == volunteer)
188 programsInterested = [interest.program for interest in interests]
190 rsvpedEventsList = EventRsvp.select(EventRsvp, Event).join(Event).where(EventRsvp.user == volunteer)
191 rsvpedEvents = [event.event.id for event in rsvpedEventsList]
193 programManagerPrograms = ProgramManager.select(ProgramManager, Program).join(Program).where(ProgramManager.user == volunteer)
194 permissionPrograms = [entry.program.id for entry in programManagerPrograms]
196 allBackgroundHistory = getUserBGCheckHistory(volunteer)
197 backgroundTypes = list(BackgroundCheckType.select())
201 eligibilityTable = []
203 for program in programs:
204 banNotes = list(ProgramBan.select(ProgramBan, Note)
205 .join(Note, on=(ProgramBan.banNote == Note.id))
206 .where(ProgramBan.user == volunteer,
207 ProgramBan.program == program,
208 ProgramBan.endDate > datetime.datetime.now()).execute())
209 onTranscriptQuery = list(ProgramBan.select(ProgramBan)
210 .where(ProgramBan.user == volunteer,
211 ProgramBan.program == program,
212 ProgramBan.unbanNote.is_null(),
213 ProgramBan.removeFromTranscript == 0))
215 onTranscript = True if len(onTranscriptQuery) > 0 else False
216 userParticipatedTrainingEvents = getParticipationStatusForTrainings(program, [volunteer], g.current_term)
217 try:
218 allTrainingsComplete = False not in [attended for event, attended in userParticipatedTrainingEvents[username]] # Did volunteer attend all events
219 except KeyError:
220 allTrainingsComplete = False
221 noteForDict = banNotes[-1].banNote.noteContent if banNotes else ""
222 eligibilityTable.append({"program": program,
223 "completedTraining": allTrainingsComplete,
224 "trainingList": userParticipatedTrainingEvents,
225 "isNotBanned": (not banNotes),
226 "banNote": noteForDict,
227 "onTranscript": onTranscript}),
229 profileNotes = ProfileNote.select().where(ProfileNote.user == volunteer)
231 bonnerRequirements = getCertRequirementsWithCompletion(certification=Certification.BONNER, username=volunteer)
233 managersProgramDict = getManagerProgramDict(g.current_user)
234 managersList = [id[1] for id in managersProgramDict.items()]
235 totalSustainedEngagements = getEngagementTotal(getCommunityEngagementByTerm(volunteer))
237 return render_template ("/main/userProfile.html",
238 username=username,
239 programs = programs,
240 programsInterested = programsInterested,
241 upcomingEvents = upcomingEvents,
242 participatedEvents = participatedEvents,
243 rsvpedEvents = rsvpedEvents,
244 permissionPrograms = permissionPrograms,
245 eligibilityTable = eligibilityTable,
246 volunteer = volunteer,
247 backgroundTypes = backgroundTypes,
248 allBackgroundHistory = allBackgroundHistory,
249 currentDateTime = datetime.datetime.now(),
250 profileNotes = profileNotes,
251 bonnerRequirements = bonnerRequirements,
252 managersList = managersList,
253 participatedInLabor = getCeltsLaborHistory(volunteer),
254 totalSustainedEngagements = totalSustainedEngagements,
255 )
256 abort(403)
258@main_bp.route('/profile/<username>/emergencyContact', methods=['GET', 'POST'])
259def emergencyContactInfo(username):
260 """
261 This loads the Emergency Contact Page
262 """
263 if not (g.current_user.username == username or g.current_user.isCeltsAdmin):
264 abort(403)
266 user = User.get(User.username == username)
268 if request.method == 'GET':
269 readOnly = g.current_user.username != username
270 contactInfo = EmergencyContact.get_or_none(EmergencyContact.user_id == username)
271 return render_template ("/main/emergencyContactInfo.html",
272 username=username,
273 contactInfo=contactInfo,
274 readOnly=readOnly
275 )
277 elif request.method == 'POST':
278 if g.current_user.username != username:
279 abort(403)
281 rowsUpdated = EmergencyContact.update(**request.form).where(EmergencyContact.user == username).execute()
282 if not rowsUpdated:
283 EmergencyContact.create(user = username, **request.form)
285 createActivityLog(f"{g.current_user.fullName} updated {user.fullName}'s emergency contact information.")
286 flash('Emergency contact information saved successfully!', 'success')
288 if request.args.get('action') == 'exit':
289 return redirect (f"/profile/{username}")
290 else:
291 return redirect (f"/profile/{username}/insuranceInfo")
293@main_bp.route('/profile/<username>/insuranceInfo', methods=['GET', 'POST'])
294def insuranceInfo(username):
295 """
296 This loads the Insurance Information Page
297 """
298 if not (g.current_user.username == username or g.current_user.isCeltsAdmin):
299 abort(403)
301 user = User.get(User.username == username)
303 if request.method == 'GET':
304 readOnly = g.current_user.username != username
305 userInsuranceInfo = InsuranceInfo.get_or_none(InsuranceInfo.user == username)
306 return render_template ("/main/insuranceInfo.html",
307 username=username,
308 userInsuranceInfo=userInsuranceInfo,
309 readOnly=readOnly
310 )
312 # Save the form data
313 elif request.method == 'POST':
314 if g.current_user.username != username:
315 abort(403)
317 InsuranceInfo.replace({**request.form, "user": username}).execute()
319 createActivityLog(f"{g.current_user.fullName} updated {user.fullName}'s insurance information.")
320 flash('Insurance information saved successfully!', 'success')
322 if request.args.get('action') == 'exit':
323 return redirect (f"/profile/{username}")
324 else:
325 return redirect (f"/profile/{username}/emergencyContact")
327@main_bp.route('/profile/<username>/travelForm', methods=['GET', 'POST'])
328def travelForm(username):
329 if not (g.current_user.username == username or g.current_user.isCeltsAdmin):
330 abort(403)
332 user = (User.select(User, EmergencyContact, InsuranceInfo)
333 .join(EmergencyContact, JOIN.LEFT_OUTER).switch()
334 .join(InsuranceInfo, JOIN.LEFT_OUTER)
335 .where(User.username == username).limit(1))
336 if not list(user):
337 abort(404)
338 userList = list(user.dicts())[0]
339 userList = [{key: value if value else '' for (key, value) in userList.items()}]
341 return render_template ('/main/travelForm.html',
342 userList = userList
343 )
345@main_bp.route('/event/<eventID>/travelForm', methods=['GET', 'POST'])
346def eventTravelForm(eventID):
347 try:
348 event = Event.get_by_id(eventID)
349 except DoesNotExist as e:
350 print(f"No event found for {eventID}", e)
351 abort(404)
353 if not (g.current_user.isCeltsAdmin):
354 abort(403)
356 if request.method == "POST" and request.form.getlist("username") != []:
357 usernameList = request.form.getlist("username")
358 usernameList = usernameList.copy()
359 userList = []
360 for username in usernameList:
361 user = (User.select(User, EmergencyContact, InsuranceInfo)
362 .join(EmergencyContact, JOIN.LEFT_OUTER).switch()
363 .join(InsuranceInfo, JOIN.LEFT_OUTER)
364 .where(User.username == username).limit(1))
365 if not list(username):
366 abort(404)
367 userData = list(user.dicts())[0]
368 userData = {key: value if value else '' for (key, value) in userData.items()}
369 userList.append(userData)
372 else:
373 return redirect(f"/event/{eventID}/volunteer_details")
376 return render_template ('/main/travelForm.html',
377 usernameList = usernameList,
378 userList = userList,
379 )
381@main_bp.route('/profile/addNote', methods=['POST'])
382def addNote():
383 """
384 This function adds a note to the user's profile.
385 """
386 postData = request.form
387 try:
388 note = addProfileNote(postData["visibility"], postData["bonner"] == "yes", postData["noteTextbox"], postData["username"])
389 flash("Successfully added profile note", "success")
390 return redirect(url_for("main.viewUsersProfile", username=postData["username"]))
391 except Exception as e:
392 print("Error adding note", e)
393 flash("Failed to add profile note", "danger")
394 return "Failed to add profile note", 500
397@main_bp.route('/<username>/deleteNote', methods=['POST'])
398def deleteNote(username):
399 """
400 This function deletes a note from the user's profile.
401 """
402 try:
403 deleteProfileNote(request.form["id"])
404 flash("Successfully deleted profile note", "success")
405 except Exception as e:
406 print("Error deleting note", e)
407 flash("Failed to delete profile note", "danger")
408 return "success"
410# ===========================Ban===============================================
411@main_bp.route('/<username>/ban/<program_id>', methods=['POST'])
412def ban(program_id, username):
413 """
414 This function updates the ban status of a username either when they are banned from a program.
415 program_id: the primary id of the program the student is being banned from
416 username: unique value of a user to correctly identify them
417 """
418 postData = request.form
419 banNote = postData["note"] # This contains the note left about the change
420 banEndDate = postData["endDate"] # Contains the date the ban will no longer be effective
422 try:
423 banUser(program_id, username, banNote, banEndDate, g.current_user)
424 programInfo = Program.get(int(program_id))
425 flash("Successfully banned the volunteer", "success")
426 createActivityLog(f'Banned {username} from {programInfo.programName} until {banEndDate}.')
427 return "Successfully banned the volunteer."
428 except Exception as e:
429 print("Error while updating ban", e)
430 flash("Failed to ban the volunteer", "danger")
431 return "Failed to ban the volunteer", 500
433# ===========================Unban===============================================
434@main_bp.route('/<username>/unban/<program_id>', methods=['POST'])
435def unban(program_id, username):
436 """
437 This function updates the ban status of a username either when they are unbanned from a program.
438 program_id: the primary id of the program the student is being unbanned from
439 username: unique value of a user to correctly identify them
440 """
441 postData = request.form
442 unbanNote = postData["note"] # This contains the note left about the change
443 try:
444 unbanUser(program_id, username, unbanNote, g.current_user)
445 programInfo = Program.get(int(program_id))
446 createActivityLog(f'Unbanned {username} from {programInfo.programName}.')
447 flash("Successfully unbanned the volunteer", "success")
448 return "Successfully unbanned the volunteer"
450 except Exception as e:
451 print("Error while updating Unban", e)
452 flash("Failed to unban the volunteer", "danger")
453 return "Failed to unban the volunteer", 500
456@main_bp.route('/<username>/addInterest/<program_id>', methods=['POST'])
457def addInterest(program_id, username):
458 """
459 This function adds a program to the list of programs a user interested in
460 program_id: the primary id of the program the student is adding interest of
461 username: unique value of a user to correctly identify them
462 """
463 try:
464 success = addUserInterest(program_id, username)
465 if success:
466 flash("Successfully added " + Program.get_by_id(program_id).programName + " as an interest", "success")
467 return ""
468 else:
469 flash("Was unable to remove " + Program.get_by_id(program_id).programName + " as an interest.", "danger")
471 except Exception as e:
472 print(e)
473 return "Error Updating Interest", 500
475@main_bp.route('/<username>/removeInterest/<program_id>', methods=['POST'])
476def removeInterest(program_id, username):
477 """
478 This function removes a program to the list of programs a user interested in
479 program_id: the primary id of the program the student is adding interest of
480 username: unique value of a user to correctly identify them
481 """
482 try:
483 removed = removeUserInterest(program_id, username)
484 if removed:
485 flash("Successfully removed " + Program.get_by_id(program_id).programName + " as an interest.", "success")
486 return ""
487 else:
488 flash("Was unable to remove " + Program.get_by_id(program_id).programName + " as an interest.", "danger")
489 except Exception as e:
490 print(e)
491 return "Error Updating Interest", 500
493@main_bp.route('/rsvpForEvent', methods = ['POST'])
494def volunteerRegister():
495 """
496 This function selects the user ID and event ID and registers the user
497 for the event they have clicked register for.
498 """
499 event = Event.get_by_id(request.form['id'])
500 program = event.program
501 user = g.current_user
503 isEligible = isEligibleForProgram(program, user)
505 personAdded = False
506 if isEligible:
507 personAdded = addPersonToEvent(user, event)
508 if personAdded:
509 flash("Successfully registered for event!","success")
510 else:
511 flash(f"RSVP Failed due to an unknown error.", "danger")
512 else:
513 flash(f"Cannot RSVP. Contact CELTS administrators: {app.config['celts_admin_contact']}.", "danger")
515 if 'from' in request.form:
516 if request.form['from'] == 'ajax':
517 return ''
518 return redirect(url_for("admin.eventDisplay", eventId=event.id))
520@main_bp.route('/rsvpRemove', methods = ['POST'])
521def RemoveRSVP():
522 """
523 This function deletes the user ID and event ID from database when RemoveRSVP is clicked
524 """
525 eventData = request.form
526 event = Event.get_by_id(eventData['id'])
528 currentRsvpParticipant = EventRsvp.get(EventRsvp.user == g.current_user, EventRsvp.event == event)
529 logBody = "withdrew from the waitlist" if currentRsvpParticipant.rsvpWaitlist else "un-RSVP'd"
530 currentRsvpParticipant.delete_instance()
531 createRsvpLog(event.id, f"{g.current_user.fullName} {logBody}.")
532 flash("Successfully unregistered for event!", "success")
533 if 'from' in eventData:
534 if eventData['from'] == 'ajax':
535 return ''
536 return redirect(url_for("admin.eventDisplay", eventId=event.id))
538@main_bp.route('/profile/<username>/serviceTranscript', methods = ['GET'])
539def serviceTranscript(username):
540 user = User.get_or_none(User.username == username)
541 if user is None:
542 abort(404)
543 if user != g.current_user and not g.current_user.isAdmin:
544 abort(403)
546 slCourses = getSlCourseTranscript(username)
547 totalHours = getTotalHours(username)
548 allEventTranscript = getProgramTranscript(username)
549 startDate = getStartYear(username)
550 return render_template('main/serviceTranscript.html',
551 allEventTranscript = allEventTranscript,
552 slCourses = slCourses.objects(),
553 totalHours = totalHours,
554 startDate = startDate,
555 userData = user)
557@main_bp.route('/profile/<username>/updateTranscript/<program_id>', methods=['POST'])
558def updateTranscript(username, program_id):
559 # Check user permissions
560 user = User.get_or_none(User.username == username)
561 if user is None:
562 abort(404)
563 if user != g.current_user and not g.current_user.isAdmin:
564 abort(403)
566 # Get the data sent from the client-side JavaScript
567 data = request.json
569 # Retrieve removeFromTranscript value from the request data
570 removeFromTranscript = data.get('removeFromTranscript')
572 # Update the ProgramBan object matching the program_id and username
573 try:
574 bannedProgramForUser = ProgramBan.get((ProgramBan.program == program_id) & (ProgramBan.user == user) & (ProgramBan.unbanNote.is_null()))
575 bannedProgramForUser.removeFromTranscript = removeFromTranscript
576 bannedProgramForUser.save()
577 return jsonify({'status': 'success'})
578 except ProgramBan.DoesNotExist:
579 return jsonify({'status': 'error', 'message': 'ProgramBan not found'})
582@main_bp.route('/searchUser/<query>', methods = ['GET'])
583def searchUser(query):
585 category= request.args.get("category")
587 '''Accepts user input and queries the database returning results that matches user search'''
588 try:
589 query = query.strip()
590 search = query.upper()
591 splitSearch = search.split()
592 searchResults = searchUsers(query,category)
593 return searchResults
594 except Exception as e:
595 print(e)
596 return "Error in searching for user", 500
598@main_bp.route('/contributors',methods = ['GET'])
599def contributors():
600 return render_template("/contributors.html")
602@main_bp.route('/updateDietInformation', methods = ['GET', 'POST'])
603def getDietInfo():
604 dietaryInfo = request.form
605 user = dietaryInfo["user"]
606 dietInfo = dietaryInfo["dietInfo"]
608 if (g.current_user.username == user) or g.current_user.isAdmin:
609 updateDietInfo(user, dietInfo)
610 userInfo = User.get(User.username == user)
611 if len(dietInfo) > 0:
612 createActivityLog(f"Updated {userInfo.fullName}'s dietary restrictions to {dietInfo}.") if dietInfo.strip() else None
613 else:
614 createActivityLog(f"Deleted all {userInfo.fullName}'s dietary restrictions dietary restrictions.")
617 return " "
619@main_bp.route('/profile/<username>/indicateInterest', methods=['POST'])
620def indicateMinorInterest(username):
621 if g.current_user.isCeltsAdmin or g.current_user.username == username:
622 data = request.get_json()
623 isAdding = data.get("isAdding", False)
625 toggleMinorInterest(username, isAdding)
627 else:
628 abort(403)
630 return ""
632@main_bp.route('/profile/<username>/updateMinorDeclaration', methods=["POST"])
633def updateMinorDeclaration(username):
634 if g.current_user.isCeltsAdmin or g.current_user.username == username:
635 declareMinorInterest(username)
636 flash("Candidate minor successfully updated", "success")
637 else:
638 flash("Error updating candidate minor status", "danger")
639 abort(403)
641 tab = request.args.get("tab", "interested")
642 return redirect(url_for('admin.manageMinor', tab=tab))