Coverage for app/controllers/main/routes.py: 23%
392 statements
« prev ^ index » next coverage.py v7.10.2, created at 2025-12-31 18:06 +0000
« prev ^ index » next coverage.py v7.10.2, created at 2025-12-31 18:06 +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
396@main_bp.route('/<username>/deleteNote', methods=['POST'])
397def deleteNote(username):
398 """
399 This function deletes a note from the user's profile.
400 """
401 try:
402 deleteProfileNote(request.form["id"])
403 flash("Successfully deleted profile note", "success")
404 except Exception as e:
405 print("Error deleting note", e)
406 flash("Failed to delete profile note", "danger")
407 return "success"
409# ===========================Ban===============================================
410@main_bp.route('/<username>/ban/<program_id>', methods=['POST'])
411def ban(program_id, username):
412 """
413 This function updates the ban status of a username either when they are banned from a program.
414 program_id: the primary id of the program the student is being banned from
415 username: unique value of a user to correctly identify them
416 """
417 postData = request.form
418 banNote = postData["note"] # This contains the note left about the change
419 banEndDate = postData["endDate"] # Contains the date the ban will no longer be effective
421 try:
422 banUser(program_id, username, banNote, banEndDate, g.current_user)
423 programInfo = Program.get(int(program_id))
424 flash("Successfully banned the volunteer", "success")
425 createActivityLog(f'Banned {username} from {programInfo.programName} until {banEndDate}.')
426 return "Successfully banned the volunteer."
427 except Exception as e:
428 print("Error while updating ban", e)
429 flash("Failed to ban the volunteer", "danger")
430 return "Failed to ban the volunteer", 500
432# ===========================Unban===============================================
433@main_bp.route('/<username>/unban/<program_id>', methods=['POST'])
434def unban(program_id, username):
435 """
436 This function updates the ban status of a username either when they are unbanned from a program.
437 program_id: the primary id of the program the student is being unbanned from
438 username: unique value of a user to correctly identify them
439 """
440 postData = request.form
441 unbanNote = postData["note"] # This contains the note left about the change
442 try:
443 unbanUser(program_id, username, unbanNote, g.current_user)
444 programInfo = Program.get(int(program_id))
445 createActivityLog(f'Unbanned {username} from {programInfo.programName}.')
446 flash("Successfully unbanned the volunteer", "success")
447 return "Successfully unbanned the volunteer"
449 except Exception as e:
450 print("Error while updating Unban", e)
451 flash("Failed to unban the volunteer", "danger")
452 return "Failed to unban the volunteer", 500
455@main_bp.route('/<username>/addInterest/<program_id>', methods=['POST'])
456def addInterest(program_id, username):
457 """
458 This function adds a program to the list of programs a user interested in
459 program_id: the primary id of the program the student is adding interest of
460 username: unique value of a user to correctly identify them
461 """
462 try:
463 success = addUserInterest(program_id, username)
464 if success:
465 flash("Successfully added " + Program.get_by_id(program_id).programName + " as an interest", "success")
466 return ""
467 else:
468 flash("Was unable to remove " + Program.get_by_id(program_id).programName + " as an interest.", "danger")
470 except Exception as e:
471 print(e)
472 return "Error Updating Interest", 500
474@main_bp.route('/<username>/removeInterest/<program_id>', methods=['POST'])
475def removeInterest(program_id, username):
476 """
477 This function removes a program to the list of programs a user interested in
478 program_id: the primary id of the program the student is adding interest of
479 username: unique value of a user to correctly identify them
480 """
481 try:
482 removed = removeUserInterest(program_id, username)
483 if removed:
484 flash("Successfully removed " + Program.get_by_id(program_id).programName + " as an interest.", "success")
485 return ""
486 else:
487 flash("Was unable to remove " + Program.get_by_id(program_id).programName + " as an interest.", "danger")
488 except Exception as e:
489 print(e)
490 return "Error Updating Interest", 500
492@main_bp.route('/rsvpForEvent', methods = ['POST'])
493def volunteerRegister():
494 """
495 This function selects the user ID and event ID and registers the user
496 for the event they have clicked register for.
497 """
498 event = Event.get_by_id(request.form['id'])
499 program = event.program
500 user = g.current_user
502 isAdded = checkUserRsvp(user, event)
503 isEligible = isEligibleForProgram(program, user)
504 listOfRequirements = unattendedRequiredEvents(program, user)
506 personAdded = False
507 if isEligible:
508 personAdded = addPersonToEvent(user, event)
509 if personAdded and listOfRequirements:
510 reqListToString = ', '.join(listOfRequirements)
511 flash(f"{user.firstName} {user.lastName} successfully registered. However, the following training may be required: {reqListToString}.", "success")
512 elif personAdded:
513 flash("Successfully registered for event!","success")
514 else:
515 flash(f"RSVP Failed due to an unknown error.", "danger")
516 else:
517 flash(f"Cannot RSVP. Contact CELTS administrators: {app.config['celts_admin_contact']}.", "danger")
520 if 'from' in request.form:
521 if request.form['from'] == 'ajax':
522 return ''
523 return redirect(url_for("admin.eventDisplay", eventId=event.id))
525@main_bp.route('/rsvpRemove', methods = ['POST'])
526def RemoveRSVP():
527 """
528 This function deletes the user ID and event ID from database when RemoveRSVP is clicked
529 """
530 eventData = request.form
531 event = Event.get_by_id(eventData['id'])
533 currentRsvpParticipant = EventRsvp.get(EventRsvp.user == g.current_user, EventRsvp.event == event)
534 logBody = "withdrew from the waitlist" if currentRsvpParticipant.rsvpWaitlist else "un-RSVP'd"
535 currentRsvpParticipant.delete_instance()
536 createRsvpLog(event.id, f"{g.current_user.fullName} {logBody}.")
537 flash("Successfully unregistered for event!", "success")
538 if 'from' in eventData:
539 if eventData['from'] == 'ajax':
540 return ''
541 return redirect(url_for("admin.eventDisplay", eventId=event.id))
543@main_bp.route('/profile/<username>/serviceTranscript', methods = ['GET'])
544def serviceTranscript(username):
545 user = User.get_or_none(User.username == username)
546 if user is None:
547 abort(404)
548 if user != g.current_user and not g.current_user.isAdmin:
549 abort(403)
551 slCourses = getSlCourseTranscript(username)
552 totalHours = getTotalHours(username)
553 allEventTranscript = getProgramTranscript(username)
554 startDate = getStartYear(username)
555 return render_template('main/serviceTranscript.html',
556 allEventTranscript = allEventTranscript,
557 slCourses = slCourses.objects(),
558 totalHours = totalHours,
559 startDate = startDate,
560 userData = user)
562@main_bp.route('/profile/<username>/updateTranscript/<program_id>', methods=['POST'])
563def updateTranscript(username, program_id):
564 # Check user permissions
565 user = User.get_or_none(User.username == username)
566 if user is None:
567 abort(404)
568 if user != g.current_user and not g.current_user.isAdmin:
569 abort(403)
571 # Get the data sent from the client-side JavaScript
572 data = request.json
574 # Retrieve removeFromTranscript value from the request data
575 removeFromTranscript = data.get('removeFromTranscript')
577 # Update the ProgramBan object matching the program_id and username
578 try:
579 bannedProgramForUser = ProgramBan.get((ProgramBan.program == program_id) & (ProgramBan.user == user) & (ProgramBan.unbanNote.is_null()))
580 bannedProgramForUser.removeFromTranscript = removeFromTranscript
581 bannedProgramForUser.save()
582 return jsonify({'status': 'success'})
583 except ProgramBan.DoesNotExist:
584 return jsonify({'status': 'error', 'message': 'ProgramBan not found'})
587@main_bp.route('/searchUser/<query>', methods = ['GET'])
588def searchUser(query):
590 category= request.args.get("category")
592 '''Accepts user input and queries the database returning results that matches user search'''
593 try:
594 query = query.strip()
595 search = query.upper()
596 splitSearch = search.split()
597 searchResults = searchUsers(query,category)
598 return searchResults
599 except Exception as e:
600 print(e)
601 return "Error in searching for user", 500
603@main_bp.route('/contributors',methods = ['GET'])
604def contributors():
605 return render_template("/contributors.html")
607@main_bp.route('/updateDietInformation', methods = ['GET', 'POST'])
608def getDietInfo():
609 dietaryInfo = request.form
610 user = dietaryInfo["user"]
611 dietInfo = dietaryInfo["dietInfo"]
613 if (g.current_user.username == user) or g.current_user.isAdmin:
614 updateDietInfo(user, dietInfo)
615 userInfo = User.get(User.username == user)
616 if len(dietInfo) > 0:
617 createActivityLog(f"Updated {userInfo.fullName}'s dietary restrictions to {dietInfo}.") if dietInfo.strip() else None
618 else:
619 createActivityLog(f"Deleted all {userInfo.fullName}'s dietary restrictions dietary restrictions.")
622 return " "
624@main_bp.route('/profile/<username>/indicateInterest', methods=['POST'])
625def indicateMinorInterest(username):
626 if g.current_user.isCeltsAdmin or g.current_user.username == username:
627 data = request.get_json()
628 isAdding = data.get("isAdding", False)
630 toggleMinorInterest(username, isAdding)
632 else:
633 abort(403)
635 return ""
637@main_bp.route('/profile/<username>/updateMinorDeclaration', methods=["POST"])
638def updateMinorDeclaration(username):
639 if g.current_user.isCeltsAdmin or g.current_user.username == username:
640 declareMinorInterest(username)
641 flash("Candidate minor successfully updated", "success")
642 else:
643 flash("Error updating candidate minor status", "danger")
644 abort(403)
646 tab = request.args.get("tab", "interested")
647 return redirect(url_for('admin.manageMinor', tab=tab))