Coverage for app/controllers/main/routes.py: 23%

367 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2025-05-02 15:35 +0000

1import json 

2import datetime 

3from peewee import JOIN 

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 

7 

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 

28 

29from app.logic.events import getUpcomingEventsForUser, getParticipatedEventsForUser, getTrainingEvents, getEventRsvpCountsForTerm, getUpcomingStudentLedCount, getStudentLedEvents, getBonnerEvents, getOtherEvents, getEngagementEvents 

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, termsMissed 

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 

41 

42@main_bp.route('/logout', methods=['GET']) 

43def redirectToLogout(): 

44 return redirect(logout()) 

45 

46@main_bp.route('/', methods=['GET']) 

47def landingPage(): 

48 

49 managerProgramDict = getManagerProgramDict(g.current_user) 

50 # Optimize the query to fetch programs with non-canceled, non-past events in the current term 

51 

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] 

59 

60 return render_template("/main/landingPage.html", 

61 managerProgramDict=managerProgramDict, 

62 term=g.current_term, 

63 programsWithEventsList = futureEvents) 

64 

65 

66 

67 

68@main_bp.route('/goToEventsList/<programID>', methods=['GET']) 

69def goToEventsList(programID): 

70 return {"activeTab": getActiveEventTab(programID)} 

71 

72@main_bp.route('/eventsList/<selectedTerm>', methods=['GET'], defaults={'activeTab': "studentLedEvents", 'programID': 0}) 

73@main_bp.route('/eventsList/<selectedTerm>/<activeTab>', methods=['GET'], defaults={'programID': 0}) 

74@main_bp.route('/eventsList/<selectedTerm>/<activeTab>/<programID>', methods=['GET']) 

75def events(selectedTerm, activeTab, programID): 

76 currentTerm = g.current_term 

77 if selectedTerm: 

78 currentTerm = selectedTerm 

79 

80 currentTime = datetime.datetime.now() 

81 listOfTerms = Term.select().order_by(Term.termOrder) 

82 participantRSVP = EventRsvp.select(EventRsvp, Event).join(Event).where(EventRsvp.user == g.current_user) 

83 rsvpedEventsID = [event.event.id for event in participantRSVP] 

84 

85 term: Term = Term.get_by_id(currentTerm) 

86 

87 currentEventRsvpAmount = getEventRsvpCountsForTerm(term) 

88 studentLedEvents = getStudentLedEvents(term) 

89 countUpcomingStudentLedEvents = getUpcomingStudentLedCount(term, currentTime) 

90 trainingEvents = getTrainingEvents(term, g.current_user) 

91 engagementEvents = getEngagementEvents(term) 

92 bonnerEvents = getBonnerEvents(term) 

93 otherEvents = getOtherEvents(term) 

94 

95 managersProgramDict = getManagerProgramDict(g.current_user) 

96 

97 # Fetch toggle state from session  

98 toggleState = request.args.get('toggleState', 'unchecked') 

99 

100 # compile all student led events into one list 

101 studentEvents = [] 

102 for studentEvent in studentLedEvents.values(): 

103 studentEvents += studentEvent # add all contents of studentEvent to the studentEvents list 

104 

105 # Get the count of all term events for each category to display in the event list page. 

106 studentLedEventsCount: int = len(studentEvents) 

107 trainingEventsCount: int = len(trainingEvents) 

108 engagementEventsCount: int = len(engagementEvents) 

109 bonnerEventsCount: int = len(bonnerEvents) 

110 otherEventsCount: int = len(otherEvents) 

111 

112 # gets only upcoming events to display in indicators 

113 if (toggleState == 'unchecked'): 

114 studentLedEventsCount: int = sum(list(countUpcomingStudentLedEvents.values())) 

115 for event in trainingEvents: 

116 if event.isPastEnd: 

117 trainingEventsCount -= 1 

118 for event in engagementEvents: 

119 if event.isPastEnd: 

120 engagementEventsCount -= 1 

121 for event in bonnerEvents: 

122 if event.isPastEnd: 

123 bonnerEventsCount -= 1 

124 for event in otherEvents: 

125 if event.isPastEnd: 

126 otherEventsCount -= 1 

127 

128 # Handle ajax request for Event category header number notifiers and toggle 

129 if request.headers.get('X-Requested-With') == 'XMLHttpRequest': 

130 return jsonify({ 

131 "studentLedEventsCount": studentLedEventsCount, 

132 "trainingEventsCount": trainingEventsCount, 

133 "engagementEventsCount": engagementEventsCount, 

134 "bonnerEventsCount": bonnerEventsCount, 

135 "otherEventsCount": otherEventsCount, 

136 "toggleStatus": toggleState 

137 }) 

138 

139 return render_template("/events/eventList.html", 

140 selectedTerm = term, 

141 studentLedEvents = studentLedEvents, 

142 trainingEvents = trainingEvents, 

143 engagementEvents = engagementEvents, 

144 bonnerEvents = bonnerEvents, 

145 otherEvents = otherEvents, 

146 listOfTerms = listOfTerms, 

147 rsvpedEventsID = rsvpedEventsID, 

148 currentEventRsvpAmount = currentEventRsvpAmount, 

149 currentTime = currentTime, 

150 user = g.current_user, 

151 activeTab = activeTab, 

152 programID = int(programID), 

153 managersProgramDict = managersProgramDict, 

154 countUpcomingStudentLedEvents = countUpcomingStudentLedEvents, 

155 toggleState = toggleState, 

156 ) 

157 

158@main_bp.route('/profile/<username>', methods=['GET']) 

159def viewUsersProfile(username): 

160 """ 

161 This function displays the information of a volunteer to the user 

162 """ 

163 try: 

164 volunteer = User.get(User.username == username) 

165 except Exception as e: 

166 if g.current_user.isAdmin: 

167 flash(f"{username} does not exist! ", category='danger') 

168 return redirect(url_for('admin.studentSearchPage')) 

169 else: 

170 abort(403) # Error 403 if non admin/student-staff user trys to access via url 

171 

172 if (g.current_user == volunteer) or g.current_user.isAdmin: 

173 upcomingEvents = getUpcomingEventsForUser(volunteer) 

174 participatedEvents = getParticipatedEventsForUser(volunteer) 

175 programs = Program.select() 

176 if not g.current_user.isBonnerScholar and not g.current_user.isAdmin: 

177 programs = programs.where(Program.isBonnerScholars == False) 

178 interests = Interest.select(Interest, Program).join(Program).where(Interest.user == volunteer) 

179 programsInterested = [interest.program for interest in interests] 

180 

181 rsvpedEventsList = EventRsvp.select(EventRsvp, Event).join(Event).where(EventRsvp.user == volunteer) 

182 rsvpedEvents = [event.event.id for event in rsvpedEventsList] 

183 

184 programManagerPrograms = ProgramManager.select(ProgramManager, Program).join(Program).where(ProgramManager.user == volunteer) 

185 permissionPrograms = [entry.program.id for entry in programManagerPrograms] 

186 

187 allBackgroundHistory = getUserBGCheckHistory(volunteer) 

188 backgroundTypes = list(BackgroundCheckType.select()) 

189 

190 

191 

192 eligibilityTable = [] 

193 

194 for program in programs: 

195 banNotes = list(ProgramBan.select(ProgramBan, Note) 

196 .join(Note, on=(ProgramBan.banNote == Note.id)) 

197 .where(ProgramBan.user == volunteer, 

198 ProgramBan.program == program, 

199 ProgramBan.endDate > datetime.datetime.now()).execute()) 

200 onTranscriptQuery = list(ProgramBan.select(ProgramBan) 

201 .where(ProgramBan.user == volunteer, 

202 ProgramBan.program == program, 

203 ProgramBan.unbanNote.is_null(), 

204 ProgramBan.removeFromTranscript == 0)) 

205 

206 onTranscript = True if len(onTranscriptQuery) > 0 else False 

207 userParticipatedTrainingEvents = getParticipationStatusForTrainings(program, [volunteer], g.current_term) 

208 try: 

209 allTrainingsComplete = False not in [attended for event, attended in userParticipatedTrainingEvents[username]] # Did volunteer attend all events 

210 except KeyError: 

211 allTrainingsComplete = False 

212 noteForDict = banNotes[-1].banNote.noteContent if banNotes else "" 

213 eligibilityTable.append({"program": program, 

214 "completedTraining": allTrainingsComplete, 

215 "trainingList": userParticipatedTrainingEvents, 

216 "isNotBanned": (not banNotes), 

217 "banNote": noteForDict, 

218 "onTranscript": onTranscript}), 

219 

220 profileNotes = ProfileNote.select().where(ProfileNote.user == volunteer) 

221 bonnerRequirements = getCertRequirementsWithCompletion(certification=Certification.BONNER, username=volunteer) 

222 managersProgramDict = getManagerProgramDict(g.current_user) 

223 managersList = [id[1] for id in managersProgramDict.items()] 

224 totalSustainedEngagements = getEngagementTotal(getCommunityEngagementByTerm(volunteer)) 

225 

226 return render_template ("/main/userProfile.html", 

227 programs = programs, 

228 programsInterested = programsInterested, 

229 upcomingEvents = upcomingEvents, 

230 participatedEvents = participatedEvents, 

231 rsvpedEvents = rsvpedEvents, 

232 permissionPrograms = permissionPrograms, 

233 eligibilityTable = eligibilityTable, 

234 volunteer = volunteer, 

235 backgroundTypes = backgroundTypes, 

236 allBackgroundHistory = allBackgroundHistory, 

237 currentDateTime = datetime.datetime.now(), 

238 profileNotes = profileNotes, 

239 bonnerRequirements = bonnerRequirements, 

240 managersList = managersList, 

241 participatedInLabor = getCeltsLaborHistory(volunteer), 

242 totalSustainedEngagements = totalSustainedEngagements, 

243 ) 

244 abort(403) 

245 

246@main_bp.route('/profile/<username>/emergencyContact', methods=['GET', 'POST']) 

247def emergencyContactInfo(username): 

248 """ 

249 This loads the Emergency Contact Page 

250 """ 

251 if not (g.current_user.username == username or g.current_user.isCeltsAdmin): 

252 abort(403) 

253 

254 user = User.get(User.username == username) 

255 

256 if request.method == 'GET': 

257 readOnly = g.current_user.username != username 

258 contactInfo = EmergencyContact.get_or_none(EmergencyContact.user_id == username) 

259 return render_template ("/main/emergencyContactInfo.html", 

260 username=username, 

261 contactInfo=contactInfo, 

262 readOnly=readOnly 

263 ) 

264 

265 elif request.method == 'POST': 

266 if g.current_user.username != username: 

267 abort(403) 

268 

269 rowsUpdated = EmergencyContact.update(**request.form).where(EmergencyContact.user == username).execute() 

270 if not rowsUpdated: 

271 EmergencyContact.create(user = username, **request.form) 

272 

273 createActivityLog(f"{g.current_user.fullName} updated {user.fullName}'s emergency contact information.") 

274 flash('Emergency contact information saved successfully!', 'success') 

275 

276 if request.args.get('action') == 'exit': 

277 return redirect (f"/profile/{username}") 

278 else: 

279 return redirect (f"/profile/{username}/insuranceInfo") 

280 

281@main_bp.route('/profile/<username>/insuranceInfo', methods=['GET', 'POST']) 

282def insuranceInfo(username): 

283 """ 

284 This loads the Insurance Information Page 

285 """ 

286 if not (g.current_user.username == username or g.current_user.isCeltsAdmin): 

287 abort(403) 

288 

289 user = User.get(User.username == username) 

290 

291 if request.method == 'GET': 

292 readOnly = g.current_user.username != username 

293 userInsuranceInfo = InsuranceInfo.get_or_none(InsuranceInfo.user == username) 

294 return render_template ("/main/insuranceInfo.html", 

295 username=username, 

296 userInsuranceInfo=userInsuranceInfo, 

297 readOnly=readOnly 

298 ) 

299 

300 # Save the form data 

301 elif request.method == 'POST': 

302 if g.current_user.username != username: 

303 abort(403) 

304 

305 rowsUpdated = InsuranceInfo.update(**request.form).where(InsuranceInfo.user == username).execute() 

306 if not rowsUpdated: 

307 InsuranceInfo.create(user = username, **request.form) 

308 

309 createActivityLog(f"{g.current_user.fullName} updated {user.fullName}'s insurance information.") 

310 flash('Insurance information saved successfully!', 'success') 

311 

312 if request.args.get('action') == 'exit': 

313 return redirect (f"/profile/{username}") 

314 else: 

315 return redirect (f"/profile/{username}/emergencyContact") 

316 

317@main_bp.route('/profile/<username>/travelForm', methods=['GET', 'POST']) 

318def travelForm(username): 

319 if not (g.current_user.username == username or g.current_user.isCeltsAdmin): 

320 abort(403) 

321 

322 user = (User.select(User, EmergencyContact, InsuranceInfo) 

323 .join(EmergencyContact, JOIN.LEFT_OUTER).switch() 

324 .join(InsuranceInfo, JOIN.LEFT_OUTER) 

325 .where(User.username == username).limit(1)) 

326 if not list(user): 

327 abort(404) 

328 userData = list(user.dicts())[0] 

329 userData = {key: value if value else '' for (key, value) in userData.items()} 

330 

331 return render_template ('/main/travelForm.html', 

332 userData = userData 

333 ) 

334 

335 

336@main_bp.route('/profile/addNote', methods=['POST']) 

337def addNote(): 

338 """ 

339 This function adds a note to the user's profile. 

340 """ 

341 postData = request.form 

342 try: 

343 note = addProfileNote(postData["visibility"], postData["bonner"] == "yes", postData["noteTextbox"], postData["username"]) 

344 flash("Successfully added profile note", "success") 

345 return redirect(url_for("main.viewUsersProfile", username=postData["username"])) 

346 except Exception as e: 

347 print("Error adding note", e) 

348 flash("Failed to add profile note", "danger") 

349 return "Failed to add profile note", 500 

350 

351@main_bp.route('/<username>/deleteNote', methods=['POST']) 

352def deleteNote(username): 

353 """ 

354 This function deletes a note from the user's profile. 

355 """ 

356 try: 

357 deleteProfileNote(request.form["id"]) 

358 flash("Successfully deleted profile note", "success") 

359 except Exception as e: 

360 print("Error deleting note", e) 

361 flash("Failed to delete profile note", "danger") 

362 return "success" 

363 

364# ===========================Ban=============================================== 

365@main_bp.route('/<username>/ban/<program_id>', methods=['POST']) 

366def ban(program_id, username): 

367 """ 

368 This function updates the ban status of a username either when they are banned from a program. 

369 program_id: the primary id of the program the student is being banned from 

370 username: unique value of a user to correctly identify them 

371 """ 

372 postData = request.form 

373 banNote = postData["note"] # This contains the note left about the change 

374 banEndDate = postData["endDate"] # Contains the date the ban will no longer be effective 

375 

376 try: 

377 banUser(program_id, username, banNote, banEndDate, g.current_user) 

378 programInfo = Program.get(int(program_id)) 

379 flash("Successfully banned the volunteer", "success") 

380 createActivityLog(f'Banned {username} from {programInfo.programName} until {banEndDate}.') 

381 return "Successfully banned the volunteer." 

382 except Exception as e: 

383 print("Error while updating ban", e) 

384 flash("Failed to ban the volunteer", "danger") 

385 return "Failed to ban the volunteer", 500 

386 

387# ===========================Unban=============================================== 

388@main_bp.route('/<username>/unban/<program_id>', methods=['POST']) 

389def unban(program_id, username): 

390 """ 

391 This function updates the ban status of a username either when they are unbanned from a program. 

392 program_id: the primary id of the program the student is being unbanned from 

393 username: unique value of a user to correctly identify them 

394 """ 

395 postData = request.form 

396 unbanNote = postData["note"] # This contains the note left about the change 

397 try: 

398 unbanUser(program_id, username, unbanNote, g.current_user) 

399 programInfo = Program.get(int(program_id)) 

400 createActivityLog(f'Unbanned {username} from {programInfo.programName}.') 

401 flash("Successfully unbanned the volunteer", "success") 

402 return "Successfully unbanned the volunteer" 

403 

404 except Exception as e: 

405 print("Error while updating Unban", e) 

406 flash("Failed to unban the volunteer", "danger") 

407 return "Failed to unban the volunteer", 500 

408 

409 

410@main_bp.route('/<username>/addInterest/<program_id>', methods=['POST']) 

411def addInterest(program_id, username): 

412 """ 

413 This function adds a program to the list of programs a user interested in 

414 program_id: the primary id of the program the student is adding interest of 

415 username: unique value of a user to correctly identify them 

416 """ 

417 try: 

418 success = addUserInterest(program_id, username) 

419 if success: 

420 flash("Successfully added " + Program.get_by_id(program_id).programName + " as an interest", "success") 

421 return "" 

422 else: 

423 flash("Was unable to remove " + Program.get_by_id(program_id).programName + " as an interest.", "danger") 

424 

425 except Exception as e: 

426 print(e) 

427 return "Error Updating Interest", 500 

428 

429@main_bp.route('/<username>/removeInterest/<program_id>', methods=['POST']) 

430def removeInterest(program_id, username): 

431 """ 

432 This function removes a program to the list of programs a user interested in 

433 program_id: the primary id of the program the student is adding interest of 

434 username: unique value of a user to correctly identify them 

435 """ 

436 try: 

437 removed = removeUserInterest(program_id, username) 

438 if removed: 

439 flash("Successfully removed " + Program.get_by_id(program_id).programName + " as an interest.", "success") 

440 return "" 

441 else: 

442 flash("Was unable to remove " + Program.get_by_id(program_id).programName + " as an interest.", "danger") 

443 except Exception as e: 

444 print(e) 

445 return "Error Updating Interest", 500 

446 

447@main_bp.route('/rsvpForEvent', methods = ['POST']) 

448def volunteerRegister(): 

449 """ 

450 This function selects the user ID and event ID and registers the user 

451 for the event they have clicked register for. 

452 """ 

453 event = Event.get_by_id(request.form['id']) 

454 program = event.program 

455 user = g.current_user 

456 

457 isAdded = checkUserRsvp(user, event) 

458 isEligible = isEligibleForProgram(program, user) 

459 listOfRequirements = unattendedRequiredEvents(program, user) 

460 

461 personAdded = False 

462 if isEligible: 

463 personAdded = addPersonToEvent(user, event) 

464 if personAdded and listOfRequirements: 

465 reqListToString = ', '.join(listOfRequirements) 

466 flash(f"{user.firstName} {user.lastName} successfully registered. However, the following training may be required: {reqListToString}.", "success") 

467 elif personAdded: 

468 flash("Successfully registered for event!","success") 

469 else: 

470 flash(f"RSVP Failed due to an unknown error.", "danger") 

471 else: 

472 flash(f"Cannot RSVP. Contact CELTS administrators: {app.config['celts_admin_contact']}.", "danger") 

473 

474 

475 if 'from' in request.form: 

476 if request.form['from'] == 'ajax': 

477 return '' 

478 return redirect(url_for("admin.eventDisplay", eventId=event.id)) 

479 

480@main_bp.route('/rsvpRemove', methods = ['POST']) 

481def RemoveRSVP(): 

482 """ 

483 This function deletes the user ID and event ID from database when RemoveRSVP is clicked 

484 """ 

485 eventData = request.form 

486 event = Event.get_by_id(eventData['id']) 

487 

488 currentRsvpParticipant = EventRsvp.get(EventRsvp.user == g.current_user, EventRsvp.event == event) 

489 logBody = "withdrew from the waitlist" if currentRsvpParticipant.rsvpWaitlist else "un-RSVP'd" 

490 currentRsvpParticipant.delete_instance() 

491 createRsvpLog(event.id, f"{g.current_user.fullName} {logBody}.") 

492 flash("Successfully unregistered for event!", "success") 

493 if 'from' in eventData: 

494 if eventData['from'] == 'ajax': 

495 return '' 

496 return redirect(url_for("admin.eventDisplay", eventId=event.id)) 

497 

498@main_bp.route('/profile/<username>/serviceTranscript', methods = ['GET']) 

499def serviceTranscript(username): 

500 user = User.get_or_none(User.username == username) 

501 if user is None: 

502 abort(404) 

503 if user != g.current_user and not g.current_user.isAdmin: 

504 abort(403) 

505 

506 slCourses = getSlCourseTranscript(username) 

507 totalHours = getTotalHours(username) 

508 allEventTranscript = getProgramTranscript(username) 

509 startDate = getStartYear(username) 

510 return render_template('main/serviceTranscript.html', 

511 allEventTranscript = allEventTranscript, 

512 slCourses = slCourses.objects(), 

513 totalHours = totalHours, 

514 startDate = startDate, 

515 userData = user) 

516 

517@main_bp.route('/profile/<username>/updateTranscript/<program_id>', methods=['POST']) 

518def updateTranscript(username, program_id): 

519 # Check user permissions 

520 user = User.get_or_none(User.username == username) 

521 if user is None: 

522 abort(404) 

523 if user != g.current_user and not g.current_user.isAdmin: 

524 abort(403) 

525 

526 # Get the data sent from the client-side JavaScript 

527 data = request.json 

528 

529 # Retrieve removeFromTranscript value from the request data 

530 removeFromTranscript = data.get('removeFromTranscript') 

531 

532 # Update the ProgramBan object matching the program_id and username 

533 try: 

534 bannedProgramForUser = ProgramBan.get((ProgramBan.program == program_id) & (ProgramBan.user == user) & (ProgramBan.unbanNote.is_null())) 

535 bannedProgramForUser.removeFromTranscript = removeFromTranscript 

536 bannedProgramForUser.save() 

537 return jsonify({'status': 'success'}) 

538 except ProgramBan.DoesNotExist: 

539 return jsonify({'status': 'error', 'message': 'ProgramBan not found'}) 

540 

541 

542@main_bp.route('/searchUser/<query>', methods = ['GET']) 

543def searchUser(query): 

544 

545 category= request.args.get("category") 

546 

547 '''Accepts user input and queries the database returning results that matches user search''' 

548 try: 

549 query = query.strip() 

550 search = query.upper() 

551 splitSearch = search.split() 

552 searchResults = searchUsers(query,category) 

553 return searchResults 

554 except Exception as e: 

555 print(e) 

556 return "Error in searching for user", 500 

557 

558@main_bp.route('/contributors',methods = ['GET']) 

559def contributors(): 

560 return render_template("/contributors.html") 

561 

562@main_bp.route('/updateDietInformation', methods = ['GET', 'POST']) 

563def getDietInfo(): 

564 dietaryInfo = request.form 

565 user = dietaryInfo["user"] 

566 dietInfo = dietaryInfo["dietInfo"] 

567 

568 if (g.current_user.username == user) or g.current_user.isAdmin: 

569 updateDietInfo(user, dietInfo) 

570 userInfo = User.get(User.username == user) 

571 if len(dietInfo) > 0: 

572 createActivityLog(f"Updated {userInfo.fullName}'s dietary restrictions to {dietInfo}.") if dietInfo.strip() else None 

573 else: 

574 createActivityLog(f"Deleted all {userInfo.fullName}'s dietary restrictions dietary restrictions.") 

575 

576 

577 return " " 

578 

579@main_bp.route('/profile/<username>/indicateInterest', methods=['POST']) 

580def indicateMinorInterest(username): 

581 if g.current_user.isCeltsAdmin or g.current_user.username == username: 

582 data = request.get_json() 

583 isAdding = data.get("isAdding", False) 

584 

585 toggleMinorInterest(username, isAdding) 

586 

587 else: 

588 abort(403) 

589 

590 return "" 

591 

592@main_bp.route('/profile/<username>/updateMinorDeclaration', methods=["POST"]) 

593def updateMinorDeclaration(username): 

594 if g.current_user.isCeltsAdmin or g.current_user.username == username: 

595 declareMinorInterest(username) 

596 flash("Candidate minor successfully updated", "success") 

597 else: 

598 flash("Error updating candidate minor status", "danger") 

599 abort(403) 

600 

601 tab = request.args.get("tab", "interested") 

602 return redirect(url_for('admin.manageMinor', tab=tab)) 

603