Coverage for app/controllers/main/routes.py: 23%
389 statements
« prev ^ index » next coverage.py v7.10.2, created at 2026-07-20 21:34 +0000
« prev ^ index » next coverage.py v7.10.2, created at 2026-07-20 21:34 +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))
236 eventParticipant = list(EventParticipant.select(EventParticipant.hoursEarned)
237 .join(Event)
238 .where(EventParticipant.user == volunteer, EventParticipant.event == Event.id,)
239 .order_by(Event.id.asc()))
241 return render_template ("/main/userProfile.html",
242 username=username,
243 programs = programs,
244 programsInterested = programsInterested,
245 upcomingEvents = upcomingEvents,
246 participatedEvents = participatedEvents,
247 rsvpedEvents = rsvpedEvents,
248 permissionPrograms = permissionPrograms,
249 eligibilityTable = eligibilityTable,
250 volunteer = volunteer,
251 backgroundTypes = backgroundTypes,
252 allBackgroundHistory = allBackgroundHistory,
253 currentDateTime = datetime.datetime.now(),
254 profileNotes = profileNotes,
255 bonnerRequirements = bonnerRequirements,
256 managersList = managersList,
257 participatedInLabor = getCeltsLaborHistory(volunteer),
258 totalSustainedEngagements = totalSustainedEngagements,
259 eventParticipant = eventParticipant
260 )
261 abort(403)
263@main_bp.route('/profile/<username>/emergencyContact', methods=['GET', 'POST'])
264def emergencyContactInfo(username):
265 """
266 This loads the Emergency Contact Page
267 """
268 if not (g.current_user.username == username or g.current_user.isCeltsAdmin):
269 abort(403)
271 user = User.get(User.username == username)
273 if request.method == 'GET':
274 readOnly = g.current_user.username != username
275 contactInfo = EmergencyContact.get_or_none(EmergencyContact.user_id == username)
276 return render_template ("/main/emergencyContactInfo.html",
277 username=username,
278 contactInfo=contactInfo,
279 readOnly=readOnly
280 )
282 elif request.method == 'POST':
283 if g.current_user.username != username:
284 abort(403)
286 rowsUpdated = EmergencyContact.update(**request.form).where(EmergencyContact.user == username).execute()
287 if not rowsUpdated:
288 EmergencyContact.create(user = username, **request.form)
290 createActivityLog(f"{g.current_user.fullName} updated {user.fullName}'s emergency contact information.")
291 flash('Emergency contact information saved successfully!', 'success')
293 if request.args.get('action') == 'exit':
294 return redirect (f"/profile/{username}")
295 else:
296 return redirect (f"/profile/{username}/insuranceInfo")
298@main_bp.route('/profile/<username>/insuranceInfo', methods=['GET', 'POST'])
299def insuranceInfo(username):
300 """
301 This loads the Insurance Information Page
302 """
303 if not (g.current_user.username == username or g.current_user.isCeltsAdmin):
304 abort(403)
306 user = User.get(User.username == username)
308 if request.method == 'GET':
309 readOnly = g.current_user.username != username
310 userInsuranceInfo = InsuranceInfo.get_or_none(InsuranceInfo.user == username)
311 return render_template ("/main/insuranceInfo.html",
312 username=username,
313 userInsuranceInfo=userInsuranceInfo,
314 readOnly=readOnly
315 )
317 # Save the form data
318 elif request.method == 'POST':
319 if g.current_user.username != username:
320 abort(403)
322 InsuranceInfo.replace({**request.form, "user": username}).execute()
324 createActivityLog(f"{g.current_user.fullName} updated {user.fullName}'s insurance information.")
325 flash('Insurance information saved successfully!', 'success')
327 if request.args.get('action') == 'exit':
328 return redirect (f"/profile/{username}")
329 else:
330 return redirect (f"/profile/{username}/emergencyContact")
332@main_bp.route('/profile/<username>/travelForm', methods=['GET', 'POST'])
333def travelForm(username):
334 if not (g.current_user.username == username or g.current_user.isCeltsAdmin):
335 abort(403)
337 user = (User.select(User, EmergencyContact, InsuranceInfo)
338 .join(EmergencyContact, JOIN.LEFT_OUTER).switch()
339 .join(InsuranceInfo, JOIN.LEFT_OUTER)
340 .where(User.username == username).limit(1))
341 if not list(user):
342 abort(404)
343 userList = list(user.dicts())[0]
344 userList = [{key: value if value else '' for (key, value) in userList.items()}]
346 return render_template ('/main/travelForm.html',
347 userList = userList
348 )
350@main_bp.route('/event/<eventID>/travelForm', methods=['GET', 'POST'])
351def eventTravelForm(eventID):
352 try:
353 event = Event.get_by_id(eventID)
354 except DoesNotExist as e:
355 print(f"No event found for {eventID}", e)
356 abort(404)
358 if not (g.current_user.isCeltsAdmin):
359 abort(403)
361 if request.method == "POST" and request.form.getlist("username") != []:
362 usernameList = request.form.getlist("username")
363 usernameList = usernameList.copy()
364 userList = []
365 for username in usernameList:
366 user = (User.select(User, EmergencyContact, InsuranceInfo)
367 .join(EmergencyContact, JOIN.LEFT_OUTER).switch()
368 .join(InsuranceInfo, JOIN.LEFT_OUTER)
369 .where(User.username == username).limit(1))
370 if not list(username):
371 abort(404)
372 userData = list(user.dicts())[0]
373 userData = {key: value if value else '' for (key, value) in userData.items()}
374 userList.append(userData)
377 else:
378 return redirect(f"/event/{eventID}/volunteer_details")
381 return render_template ('/main/travelForm.html',
382 usernameList = usernameList,
383 userList = userList,
384 )
386@main_bp.route('/profile/addNote', methods=['POST'])
387def addNote():
388 """
389 This function adds a note to the user's profile.
390 """
391 postData = request.form
392 try:
393 note = addProfileNote(postData["visibility"], postData["bonner"] == "yes", postData["noteTextbox"], postData["username"])
394 flash("Successfully added profile note", "success")
395 return redirect(url_for("main.viewUsersProfile", username=postData["username"]))
396 except Exception as e:
397 print("Error adding note", e)
398 flash("Failed to add profile note", "danger")
399 return "Failed to add profile note", 500
402@main_bp.route('/<username>/deleteNote', methods=['POST'])
403def deleteNote(username):
404 """
405 This function deletes a note from the user's profile.
406 """
407 try:
408 deleteProfileNote(request.form["id"])
409 flash("Successfully deleted profile note", "success")
410 except Exception as e:
411 print("Error deleting note", e)
412 flash("Failed to delete profile note", "danger")
413 return "success"
415# ===========================Ban===============================================
416@main_bp.route('/<username>/ban/<program_id>', methods=['POST'])
417def ban(program_id, username):
418 """
419 This function updates the ban status of a username either when they are banned from a program.
420 program_id: the primary id of the program the student is being banned from
421 username: unique value of a user to correctly identify them
422 """
423 postData = request.form
424 banNote = postData["note"] # This contains the note left about the change
425 banEndDate = postData["endDate"] # Contains the date the ban will no longer be effective
427 try:
428 banUser(program_id, username, banNote, banEndDate, g.current_user)
429 programInfo = Program.get(int(program_id))
430 flash("Successfully banned the volunteer", "success")
431 createActivityLog(f'Banned {username} from {programInfo.programName} until {banEndDate}.')
432 return "Successfully banned the volunteer."
433 except Exception as e:
434 print("Error while updating ban", e)
435 flash("Failed to ban the volunteer", "danger")
436 return "Failed to ban the volunteer", 500
438# ===========================Unban===============================================
439@main_bp.route('/<username>/unban/<program_id>', methods=['POST'])
440def unban(program_id, username):
441 """
442 This function updates the ban status of a username either when they are unbanned from a program.
443 program_id: the primary id of the program the student is being unbanned from
444 username: unique value of a user to correctly identify them
445 """
446 postData = request.form
447 unbanNote = postData["note"] # This contains the note left about the change
448 try:
449 unbanUser(program_id, username, unbanNote, g.current_user)
450 programInfo = Program.get(int(program_id))
451 createActivityLog(f'Unbanned {username} from {programInfo.programName}.')
452 flash("Successfully unbanned the volunteer", "success")
453 return "Successfully unbanned the volunteer"
455 except Exception as e:
456 print("Error while updating Unban", e)
457 flash("Failed to unban the volunteer", "danger")
458 return "Failed to unban the volunteer", 500
461@main_bp.route('/<username>/addInterest/<program_id>', methods=['POST'])
462def addInterest(program_id, username):
463 """
464 This function adds a program to the list of programs a user interested in
465 program_id: the primary id of the program the student is adding interest of
466 username: unique value of a user to correctly identify them
467 """
468 try:
469 success = addUserInterest(program_id, username)
470 if success:
471 flash("Successfully added " + Program.get_by_id(program_id).programName + " as an interest", "success")
472 return ""
473 else:
474 flash("Was unable to remove " + Program.get_by_id(program_id).programName + " as an interest.", "danger")
476 except Exception as e:
477 print(e)
478 return "Error Updating Interest", 500
480@main_bp.route('/<username>/removeInterest/<program_id>', methods=['POST'])
481def removeInterest(program_id, username):
482 """
483 This function removes a program to the list of programs a user interested in
484 program_id: the primary id of the program the student is adding interest of
485 username: unique value of a user to correctly identify them
486 """
487 try:
488 removed = removeUserInterest(program_id, username)
489 if removed:
490 flash("Successfully removed " + Program.get_by_id(program_id).programName + " as an interest.", "success")
491 return ""
492 else:
493 flash("Was unable to remove " + Program.get_by_id(program_id).programName + " as an interest.", "danger")
494 except Exception as e:
495 print(e)
496 return "Error Updating Interest", 500
498@main_bp.route('/rsvpForEvent', methods = ['POST'])
499def volunteerRegister():
500 """
501 This function selects the user ID and event ID and registers the user
502 for the event they have clicked register for.
503 """
504 event = Event.get_by_id(request.form['id'])
505 program = event.program
506 user = g.current_user
508 isEligible = isEligibleForProgram(program, user)
510 personAdded = False
511 if isEligible:
512 personAdded = addPersonToEvent(user, event)
513 if personAdded:
514 flash("Successfully registered for event!","success")
515 else:
516 flash(f"RSVP Failed due to an unknown error.", "danger")
517 else:
518 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 zeroHourEvents = getZeroHourEvents(username)
555 startDate = getStartYear(username)
556 return render_template('main/serviceTranscript.html',
557 allEventTranscript = allEventTranscript,
558 zeroHourEvents = zeroHourEvents,
559 slCourses = slCourses.objects(),
560 totalHours = totalHours,
561 startDate = startDate,
562 userData = user)
564@main_bp.route('/profile/<username>/updateTranscript/<program_id>', methods=['POST'])
565def updateTranscript(username, program_id):
566 # Check user permissions
567 user = User.get_or_none(User.username == username)
568 if user is None:
569 abort(404)
570 if user != g.current_user and not g.current_user.isAdmin:
571 abort(403)
573 # Get the data sent from the client-side JavaScript
574 data = request.json
576 # Retrieve removeFromTranscript value from the request data
577 removeFromTranscript = data.get('removeFromTranscript')
579 # Update the ProgramBan object matching the program_id and username
580 try:
581 bannedProgramForUser = ProgramBan.get((ProgramBan.program == program_id) & (ProgramBan.user == user) & (ProgramBan.unbanNote.is_null()))
582 bannedProgramForUser.removeFromTranscript = removeFromTranscript
583 bannedProgramForUser.save()
584 return jsonify({'status': 'success'})
585 except ProgramBan.DoesNotExist:
586 return jsonify({'status': 'error', 'message': 'ProgramBan not found'})
589@main_bp.route('/searchUser/<query>', methods = ['GET'])
590def searchUser(query):
592 category= request.args.get("category")
594 '''Accepts user input and queries the database returning results that matches user search'''
595 try:
596 query = query.strip()
597 search = query.upper()
598 splitSearch = search.split()
599 searchResults = searchUsers(query,category)
600 return searchResults
601 except Exception as e:
602 print(e)
603 return "Error in searching for user", 500
605@main_bp.route('/contributors',methods = ['GET'])
606def contributors():
607 return render_template("/contributors.html")
609@main_bp.route('/updateDietInformation', methods = ['GET', 'POST'])
610def getDietInfo():
611 dietaryInfo = request.form
612 user = dietaryInfo["user"]
613 dietInfo = dietaryInfo["dietInfo"]
615 if (g.current_user.username == user) or g.current_user.isAdmin:
616 updateDietInfo(user, dietInfo)
617 userInfo = User.get(User.username == user)
618 if len(dietInfo) > 0:
619 createActivityLog(f"Updated {userInfo.fullName}'s dietary restrictions to {dietInfo}.") if dietInfo.strip() else None
620 else:
621 createActivityLog(f"Deleted all {userInfo.fullName}'s dietary restrictions dietary restrictions.")
624 return " "
626@main_bp.route('/profile/<username>/indicateInterest', methods=['POST'])
627def indicateMinorInterest(username):
628 if g.current_user.isCeltsAdmin or g.current_user.username == username:
629 data = request.get_json()
630 isAdding = data.get("isAdding", False)
632 toggleMinorInterest(username, isAdding)
634 else:
635 abort(403)
637 return ""
639@main_bp.route('/profile/<username>/updateMinorDeclaration', methods=["POST"])
640def updateMinorDeclaration(username):
641 if g.current_user.isCeltsAdmin or g.current_user.username == username:
642 declareMinorInterest(username)
643 flash("Candidate minor successfully updated", "success")
644 else:
645 flash("Error updating candidate minor status", "danger")
646 abort(403)
648 tab = request.args.get("tab", "interested")
649 return redirect(url_for('admin.manageMinor', tab=tab))