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

389 statements  

« prev     ^ index     » next       coverage.py v7.10.2, created at 2025-12-17 17:33 +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 

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, getUpcomingVolunteerOpportunitiesCount, getVolunteerOpportunities, getBonnerEvents, getCeltsLabor, 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 

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': "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): 

77 

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] 

82 

83 term = g.current_term 

84 if selectedTerm: 

85 term = selectedTerm 

86 

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) 

91 

92 currentEventRsvpAmount = getEventRsvpCountsForTerm(term) 

93 volunteerOpportunities = getVolunteerOpportunities(term) 

94 countUpcomingVolunteerOpportunities = getUpcomingVolunteerOpportunitiesCount(term, currentTime) 

95 trainingEvents = getTrainingEvents(term, g.current_user) 

96 engagementEvents = getEngagementEvents(term) 

97 bonnerEvents = getBonnerEvents(term) 

98 celtsLabor = getCeltsLabor(term) 

99 

100 managersProgramDict = getManagerProgramDict(g.current_user) 

101 

102 # Fetch toggle state from session  

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

104 

105 # compile all volunteer opportunitiesevents into one list 

106 studentEvents = [] 

107 for studentEvent in volunteerOpportunities.values(): 

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

109 

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

111 volunteerOpportunitiesCount: int = len(studentEvents) 

112 trainingEventsCount: int = len(trainingEvents) 

113 engagementEventsCount: int = len(engagementEvents) 

114 bonnerEventsCount: int = len(bonnerEvents) 

115 celtsLaborCount: int = len(celtsLabor) 

116 

117 # gets only upcoming events to display in indicators 

118 if (toggleState == 'unchecked'): 

119 for event in trainingEvents: 

120 if event.isPastEnd: 

121 trainingEventsCount -= 1 

122 for event in engagementEvents: 

123 if event.isPastEnd: 

124 engagementEventsCount -= 1 

125 for event in bonnerEvents: 

126 if event.isPastEnd: 

127 bonnerEventsCount -= 1 

128 for event in celtsLabor: 

129 if event.isPastEnd: 

130 celtsLaborCount -= 1 

131 

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

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

134 return jsonify({ 

135 "volunteerOpportunitiesCount": volunteerOpportunitiesCount, 

136 "trainingEventsCount": trainingEventsCount, 

137 "engagementEventsCount": engagementEventsCount, 

138 "bonnerEventsCount": bonnerEventsCount, 

139 "celtsLaborCount": celtsLaborCount, 

140 "toggleStatus": toggleState 

141 }) 

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

143 selectedTerm = term, 

144 volunteerOpportunities = volunteerOpportunities, 

145 trainingEvents = trainingEvents, 

146 engagementEvents = engagementEvents, 

147 bonnerEvents = bonnerEvents, 

148 celtsLabor = celtsLabor, 

149 listOfTerms = listOfTerms, 

150 rsvpedEventsID = rsvpedEventsID, 

151 currentEventRsvpAmount = currentEventRsvpAmount, 

152 currentTime = currentTime, 

153 user = g.current_user, 

154 activeTab = activeTab, 

155 programID = int(programID), 

156 managersProgramDict = managersProgramDict, 

157 countUpcomingVolunteerOpportunities = countUpcomingVolunteerOpportunities, 

158 toggleState = toggleState, 

159 ) 

160 

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

162def viewUsersProfile(username): 

163 """ 

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

165 """ 

166 try: 

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

168 except Exception as e: 

169 if g.current_user.isAdmin: 

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

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

172 else: 

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

174 

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

176 upcomingEvents = getUpcomingEventsForUser(volunteer) 

177 participatedEvents = getParticipatedEventsForUser(volunteer) 

178 programs = Program.select() 

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

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

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

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

183 

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

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

186 

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

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

189 

190 allBackgroundHistory = getUserBGCheckHistory(volunteer) 

191 backgroundTypes = list(BackgroundCheckType.select()) 

192 

193 

194 

195 eligibilityTable = [] 

196 

197 for program in programs: 

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

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

200 .where(ProgramBan.user == volunteer, 

201 ProgramBan.program == program, 

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

203 onTranscriptQuery = list(ProgramBan.select(ProgramBan) 

204 .where(ProgramBan.user == volunteer, 

205 ProgramBan.program == program, 

206 ProgramBan.unbanNote.is_null(), 

207 ProgramBan.removeFromTranscript == 0)) 

208 

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

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

211 try: 

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

213 except KeyError: 

214 allTrainingsComplete = False 

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

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

217 "completedTraining": allTrainingsComplete, 

218 "trainingList": userParticipatedTrainingEvents, 

219 "isNotBanned": (not banNotes), 

220 "banNote": noteForDict, 

221 "onTranscript": onTranscript}), 

222 

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

224 

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

226 

227 managersProgramDict = getManagerProgramDict(g.current_user) 

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

229 totalSustainedEngagements = getEngagementTotal(getCommunityEngagementByTerm(volunteer)) 

230 

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

232 username=username, 

233 programs = programs, 

234 programsInterested = programsInterested, 

235 upcomingEvents = upcomingEvents, 

236 participatedEvents = participatedEvents, 

237 rsvpedEvents = rsvpedEvents, 

238 permissionPrograms = permissionPrograms, 

239 eligibilityTable = eligibilityTable, 

240 volunteer = volunteer, 

241 backgroundTypes = backgroundTypes, 

242 allBackgroundHistory = allBackgroundHistory, 

243 currentDateTime = datetime.datetime.now(), 

244 profileNotes = profileNotes, 

245 bonnerRequirements = bonnerRequirements, 

246 managersList = managersList, 

247 participatedInLabor = getCeltsLaborHistory(volunteer), 

248 totalSustainedEngagements = totalSustainedEngagements, 

249 ) 

250 abort(403) 

251 

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

253def emergencyContactInfo(username): 

254 """ 

255 This loads the Emergency Contact Page 

256 """ 

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

258 abort(403) 

259 

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

261 

262 if request.method == 'GET': 

263 readOnly = g.current_user.username != username 

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

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

266 username=username, 

267 contactInfo=contactInfo, 

268 readOnly=readOnly 

269 ) 

270 

271 elif request.method == 'POST': 

272 if g.current_user.username != username: 

273 abort(403) 

274 

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

276 if not rowsUpdated: 

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

278 

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

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

281 

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

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

284 else: 

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

286 

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

288def insuranceInfo(username): 

289 """ 

290 This loads the Insurance Information Page 

291 """ 

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

293 abort(403) 

294 

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

296 

297 if request.method == 'GET': 

298 readOnly = g.current_user.username != username 

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

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

301 username=username, 

302 userInsuranceInfo=userInsuranceInfo, 

303 readOnly=readOnly 

304 ) 

305 

306 # Save the form data 

307 elif request.method == 'POST': 

308 if g.current_user.username != username: 

309 abort(403) 

310 

311 InsuranceInfo.replace({**request.form, "user": username}).execute() 

312 

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

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

315 

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

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

318 else: 

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

320 

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

322def travelForm(username): 

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

324 abort(403) 

325 

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

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

328 .join(InsuranceInfo, JOIN.LEFT_OUTER) 

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

330 if not list(user): 

331 abort(404) 

332 userList = list(user.dicts())[0] 

333 userList = [{key: value if value else '' for (key, value) in userList.items()}] 

334 

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

336 userList = userList 

337 ) 

338 

339@main_bp.route('/event/<eventID>/travelForm', methods=['GET', 'POST']) 

340def eventTravelForm(eventID): 

341 try: 

342 event = Event.get_by_id(eventID) 

343 except DoesNotExist as e: 

344 print(f"No event found for {eventID}", e) 

345 abort(404) 

346 

347 if not (g.current_user.isCeltsAdmin): 

348 abort(403) 

349 

350 if request.method == "POST" and request.form.getlist("username") != []: 

351 usernameList = request.form.getlist("username") 

352 usernameList = usernameList.copy() 

353 userList = [] 

354 for username in usernameList: 

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

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

357 .join(InsuranceInfo, JOIN.LEFT_OUTER) 

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

359 if not list(username): 

360 abort(404) 

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

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

363 userList.append(userData) 

364 

365 

366 else: 

367 return redirect(f"/event/{eventID}/volunteer_details") 

368 

369 

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

371 usernameList = usernameList, 

372 userList = userList, 

373 ) 

374 

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

376def addNote(): 

377 """ 

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

379 """ 

380 postData = request.form 

381 try: 

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

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

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

385 except Exception as e: 

386 print("Error adding note", e) 

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

388 return "Failed to add profile note", 500 

389 

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

391def deleteNote(username): 

392 """ 

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

394 """ 

395 try: 

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

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

398 except Exception as e: 

399 print("Error deleting note", e) 

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

401 return "success" 

402 

403# ===========================Ban=============================================== 

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

405def ban(program_id, username): 

406 """ 

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

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

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

410 """ 

411 postData = request.form 

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

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

414 

415 try: 

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

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

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

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

420 return "Successfully banned the volunteer." 

421 except Exception as e: 

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

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

424 return "Failed to ban the volunteer", 500 

425 

426# ===========================Unban=============================================== 

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

428def unban(program_id, username): 

429 """ 

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

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

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

433 """ 

434 postData = request.form 

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

436 try: 

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

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

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

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

441 return "Successfully unbanned the volunteer" 

442 

443 except Exception as e: 

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

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

446 return "Failed to unban the volunteer", 500 

447 

448 

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

450def addInterest(program_id, username): 

451 """ 

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

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

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

455 """ 

456 try: 

457 success = addUserInterest(program_id, username) 

458 if success: 

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

460 return "" 

461 else: 

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

463 

464 except Exception as e: 

465 print(e) 

466 return "Error Updating Interest", 500 

467 

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

469def removeInterest(program_id, username): 

470 """ 

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

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

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

474 """ 

475 try: 

476 removed = removeUserInterest(program_id, username) 

477 if removed: 

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

479 return "" 

480 else: 

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

482 except Exception as e: 

483 print(e) 

484 return "Error Updating Interest", 500 

485 

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

487def volunteerRegister(): 

488 """ 

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

490 for the event they have clicked register for. 

491 """ 

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

493 program = event.program 

494 user = g.current_user 

495 

496 isAdded = checkUserRsvp(user, event) 

497 isEligible = isEligibleForProgram(program, user) 

498 listOfRequirements = unattendedRequiredEvents(program, user) 

499 

500 personAdded = False 

501 if isEligible: 

502 personAdded = addPersonToEvent(user, event) 

503 if personAdded and listOfRequirements: 

504 reqListToString = ', '.join(listOfRequirements) 

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

506 elif personAdded: 

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

508 else: 

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

510 else: 

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

512 

513 

514 if 'from' in request.form: 

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

516 return '' 

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

518 

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

520def RemoveRSVP(): 

521 """ 

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

523 """ 

524 eventData = request.form 

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

526 

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

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

529 currentRsvpParticipant.delete_instance() 

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

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

532 if 'from' in eventData: 

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

534 return '' 

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

536 

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

538def serviceTranscript(username): 

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

540 if user is None: 

541 abort(404) 

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

543 abort(403) 

544 

545 slCourses = getSlCourseTranscript(username) 

546 totalHours = getTotalHours(username) 

547 allEventTranscript = getProgramTranscript(username) 

548 startDate = getStartYear(username) 

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

550 allEventTranscript = allEventTranscript, 

551 slCourses = slCourses.objects(), 

552 totalHours = totalHours, 

553 startDate = startDate, 

554 userData = user) 

555 

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

557def updateTranscript(username, program_id): 

558 # Check user permissions 

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

560 if user is None: 

561 abort(404) 

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

563 abort(403) 

564 

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

566 data = request.json 

567 

568 # Retrieve removeFromTranscript value from the request data 

569 removeFromTranscript = data.get('removeFromTranscript') 

570 

571 # Update the ProgramBan object matching the program_id and username 

572 try: 

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

574 bannedProgramForUser.removeFromTranscript = removeFromTranscript 

575 bannedProgramForUser.save() 

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

577 except ProgramBan.DoesNotExist: 

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

579 

580 

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

582def searchUser(query): 

583 

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

585 

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

587 try: 

588 query = query.strip() 

589 search = query.upper() 

590 splitSearch = search.split() 

591 searchResults = searchUsers(query,category) 

592 return searchResults 

593 except Exception as e: 

594 print(e) 

595 return "Error in searching for user", 500 

596 

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

598def contributors(): 

599 return render_template("/contributors.html") 

600 

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

602def getDietInfo(): 

603 dietaryInfo = request.form 

604 user = dietaryInfo["user"] 

605 dietInfo = dietaryInfo["dietInfo"] 

606 

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

608 updateDietInfo(user, dietInfo) 

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

610 if len(dietInfo) > 0: 

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

612 else: 

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

614 

615 

616 return " " 

617 

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

619def indicateMinorInterest(username): 

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

621 data = request.get_json() 

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

623 

624 toggleMinorInterest(username, isAdding) 

625 

626 else: 

627 abort(403) 

628 

629 return "" 

630 

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

632def updateMinorDeclaration(username): 

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

634 declareMinorInterest(username) 

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

636 else: 

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

638 abort(403) 

639 

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

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

642