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

266 statements  

« prev     ^ index     » next       coverage.py v7.2.5, created at 2023-05-24 14:13 +0000

1from flask import request, render_template, g, abort, flash, redirect, url_for, session 

2import datetime 

3import json 

4from http import cookies 

5 

6from app import app 

7from app.models.program import Program 

8from app.models.event import Event 

9from app.models.backgroundCheck import BackgroundCheck 

10from app.models.backgroundCheckType import BackgroundCheckType 

11from app.models.user import User 

12from app.models.eventParticipant import EventParticipant 

13from app.models.interest import Interest 

14from app.models.programBan import ProgramBan 

15from app.models.programEvent import ProgramEvent 

16from app.models.term import Term 

17from app.models.eventRsvp import EventRsvp 

18from app.models.note import Note 

19from app.models.profileNote import ProfileNote 

20from app.models.programManager import ProgramManager 

21from app.models.courseStatus import CourseStatus 

22from app.models.courseInstructor import CourseInstructor 

23from app.models.certification import Certification 

24 

25from app.controllers.main import main_bp 

26from app.logic.loginManager import logout 

27from app.logic.users import addUserInterest, removeUserInterest, banUser, unbanUser, isEligibleForProgram, getUserBGCheckHistory, addProfileNote, deleteProfileNote, updateDietInfo 

28from app.logic.participants import unattendedRequiredEvents, trainedParticipants, getUserParticipatedTrainingEvents, checkUserRsvp, addPersonToEvent 

29from app.logic.events import * 

30from app.logic.searchUsers import searchUsers 

31from app.logic.transcript import * 

32from app.logic.landingPage import getManagerProgramDict, getActiveEventTab 

33from app.logic.manageSLFaculty import getCourseDict 

34from app.logic.courseManagement import unapprovedCourses, approvedCourses 

35from app.logic.utils import selectSurroundingTerms 

36from app.logic.certification import getCertRequirementsWithCompletion 

37 

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

39def redirectToLogout(): 

40 return redirect(logout()) 

41 

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

43def landingPage(): 

44 managerProgramDict = getManagerProgramDict(g.current_user) 

45 programsWithEvents = list(ProgramEvent.select(ProgramEvent.program).where(ProgramEvent.event.term == g.current_term).join(Event).distinct()) 

46 programsWithEventsList = [program.program.id for program in programsWithEvents] 

47 

48 return render_template("/main/landingPage.html", managerProgramDict = managerProgramDict, 

49 term = g.current_term, 

50 programsWithEventsList = programsWithEventsList) 

51 

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

53def goToEventsList(programID): 

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

55 

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

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

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

59def events(selectedTerm, activeTab, programID): 

60 currentTerm = g.current_term 

61 if selectedTerm: 

62 currentTerm = selectedTerm 

63 currentTime = datetime.datetime.now() 

64 listOfTerms = Term.select() 

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

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

67 term = Term.get_by_id(currentTerm) 

68 studentLedEvents = getStudentLedEvents(term) 

69 trainingEvents = getTrainingEvents(term, g.current_user) 

70 bonnerEvents = getBonnerEvents(term) 

71 otherEvents = getOtherEvents(term) 

72 

73 return render_template("/events/event_list.html", 

74 selectedTerm = term, 

75 studentLedEvents = studentLedEvents, 

76 trainingEvents = trainingEvents, 

77 bonnerEvents = bonnerEvents, 

78 otherEvents = otherEvents, 

79 listOfTerms = listOfTerms, 

80 rsvpedEventsID = rsvpedEventsID, 

81 currentTime = currentTime, 

82 user = g.current_user, 

83 activeTab = activeTab, 

84 programID = int(programID)) 

85 

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

87def viewUsersProfile(username): 

88 """ 

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

90 """ 

91 try: 

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

93 except Exception as e: 

94 if g.current_user.isAdmin: 

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

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

97 else: 

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

99 

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

101 upcomingEvents = getUpcomingEventsForUser(volunteer) 

102 participatedEvents = getParticipatedEventsForUser(volunteer) 

103 programs = Program.select() 

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

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

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

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

108 

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

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

111 

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

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

114 

115 allBackgroundHistory = getUserBGCheckHistory(volunteer) 

116 backgroundTypes = list(BackgroundCheckType.select()) 

117 

118 eligibilityTable = [] 

119 for program in programs: 

120 notes = list(ProgramBan.select(ProgramBan, Note) 

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

122 .where(ProgramBan.user == volunteer, 

123 ProgramBan.program == program, 

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

125 

126 UserParticipatedTrainingEvents = getUserParticipatedTrainingEvents(program, g.current_user, g.current_term) 

127 allTrainingsComplete = not len([event for event in UserParticipatedTrainingEvents.values() if event != True]) 

128 noteForDict = notes[-1].banNote.noteContent if notes else "" 

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

130 "completedTraining": allTrainingsComplete, 

131 "trainingList": UserParticipatedTrainingEvents, 

132 "isNotBanned": True if not notes else False, 

133 "banNote": noteForDict}) 

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

135 userDietQuery = User.select().where(User.username == username) 

136 userDiet = [note.dietRestriction for note in userDietQuery] 

137 

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

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

140 programs = programs, 

141 programsInterested = programsInterested, 

142 upcomingEvents = upcomingEvents, 

143 participatedEvents = participatedEvents, 

144 rsvpedEvents = rsvpedEvents, 

145 permissionPrograms = permissionPrograms, 

146 eligibilityTable = eligibilityTable, 

147 volunteer = volunteer, 

148 backgroundTypes = backgroundTypes, 

149 allBackgroundHistory = allBackgroundHistory, 

150 currentDateTime = datetime.datetime.now(), 

151 profileNotes = profileNotes, 

152 bonnerRequirements = bonnerRequirements, 

153 userDiet = userDiet 

154 ) 

155 abort(403) 

156 

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

158def addNote(): 

159 """ 

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

161 """ 

162 postData = request.form 

163 try: 

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

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

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

167 except Exception as e: 

168 print("Error adding note", e) 

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

170 return "Failed to add profile note", 500 

171 

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

173def deleteNote(username): 

174 """ 

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

176 """ 

177 try: 

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

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

180 except Exception as e: 

181 print("Error deleting note", e) 

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

183 return "success" 

184 

185# ===========================Ban=============================================== 

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

187def ban(program_id, username): 

188 """ 

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

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

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

192 """ 

193 postData = request.form 

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

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

196 try: 

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

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

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

200 createLog(f'Banned {username} from {programInfo.programName} until {banEndDate}.') 

201 return "Successfully banned the volunteer." 

202 except Exception as e: 

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

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

205 return "Failed to ban the volunteer", 500 

206 

207# ===========================Unban=============================================== 

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

209def unban(program_id, username): 

210 """ 

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

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

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

214 """ 

215 postData = request.form 

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

217 try: 

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

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

220 createLog(f'Unbanned {username} from {programInfo.programName}.') 

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

222 return "Successfully unbanned the volunteer" 

223 

224 except Exception as e: 

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

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

227 return "Failed to unban the volunteer", 500 

228 

229 

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

231def addInterest(program_id, username): 

232 """ 

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

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

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

236 """ 

237 try: 

238 success = addUserInterest(program_id, username) 

239 if success: 

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

241 return "" 

242 else: 

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

244 

245 except Exception as e: 

246 print(e) 

247 return "Error Updating Interest", 500 

248 

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

250def removeInterest(program_id, username): 

251 """ 

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

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

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

255 """ 

256 try: 

257 removed = removeUserInterest(program_id, username) 

258 if removed: 

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

260 return "" 

261 else: 

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

263 except Exception as e: 

264 print(e) 

265 return "Error Updating Interest", 500 

266 

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

268def volunteerRegister(): 

269 """ 

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

271 for the event they have clicked register for. 

272 """ 

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

274 program = event.singleProgram 

275 user = g.current_user 

276 

277 isAdded = checkUserRsvp(user, event) 

278 isEligible = isEligibleForProgram(program, user) 

279 listOfRequirements = unattendedRequiredEvents(program, user) 

280 

281 personAdded = False 

282 if isEligible: 

283 personAdded = addPersonToEvent(user, event) 

284 if personAdded and listOfRequirements: 

285 reqListToString = ', '.join(listOfRequirements) 

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

287 elif personAdded: 

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

289 else: 

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

291 else: 

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

293 

294 

295 if 'from' in request.form: 

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

297 return '' 

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

299 

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

301def RemoveRSVP(): 

302 """ 

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

304 """ 

305 eventData = request.form 

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

307 

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

309 currentRsvpParticipant.delete_instance() 

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

311 if 'from' in eventData: 

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

313 return '' 

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

315 

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

317def serviceTranscript(username): 

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

319 if user is None: 

320 abort(404) 

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

322 abort(403) 

323 

324 slCourses = getSlCourseTranscript(username) 

325 totalHours = getTotalHours(username) 

326 allEventTranscript = getAllEventTranscript(username) 

327 startDate = getStartYear(username) 

328 

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

330 allEventTranscript = allEventTranscript, 

331 slCourses = slCourses.objects(), 

332 totalHours = totalHours, 

333 startDate = startDate, 

334 userData = user) 

335 

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

337def searchUser(query): 

338 

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

340 

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

342 try: 

343 query = query.strip() 

344 search = query.upper() 

345 splitSearch = search.split() 

346 searchResults = searchUsers(query,category) 

347 return searchResults 

348 except Exception as e: 

349 print(e) 

350 return "Error in searching for user", 500 

351 

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

353def contributors(): 

354 return render_template("/contributors.html") 

355 

356@main_bp.route('/proposalReview/', methods = ['GET', 'POST']) 

357def reviewProposal(): 

358 """ 

359 this function gets the submitted course id and returns the its data to the review proposal modal 

360 """ 

361 courseID=request.form 

362 course=Course.get_by_id(courseID["course_id"]) 

363 instructors_data=course.courseInstructors 

364 return render_template('/main/reviewproposal.html', 

365 course=course, 

366 instructors_data=instructors_data) 

367 

368@main_bp.route('/manageServiceLearning', methods = ['GET', 'POST']) 

369@main_bp.route('/manageServiceLearning/<term>', methods = ['GET', 'POST']) 

370def getAllCourseInstructors(term=None): 

371 """ 

372 This function selects all the Instructors Name and the previous courses 

373 """ 

374 if g.current_user.isCeltsAdmin: 

375 setRedirectTarget("/manageServiceLearning") 

376 courseDict = getCourseDict() 

377 

378 term = Term.get_or_none(Term.id == term) or g.current_term 

379 

380 unapproved = unapprovedCourses(term) 

381 approved = approvedCourses(term) 

382 terms = selectSurroundingTerms(g.current_term) 

383 

384 return render_template('/main/manageServiceLearningFaculty.html', 

385 courseInstructors = courseDict, 

386 unapprovedCourses = unapproved, 

387 approvedCourses = approved, 

388 terms = terms, 

389 term = term, 

390 CourseStatus = CourseStatus) 

391 else: 

392 abort(403) 

393 

394def getRedirectTarget(popTarget=False): 

395 """ 

396 This function returns a string with the URL or route to a page in the Application 

397 saved with setRedirectTarget() and is able to pop the value from the session 

398 to make it an empty value 

399 popTarget: expects a bool value to determine whether or not to reset 

400 redirectTarget to an emtpy value 

401 return: a string with the URL or route to a page in the application that was 

402 saved in setRedirectTarget() 

403 """ 

404 if "redirectTarget" not in session: 

405 return '' 

406 

407 target = session["redirectTarget"] 

408 if popTarget: 

409 session.pop("redirectTarget") 

410 return target 

411 

412def setRedirectTarget(target): 

413 """ 

414 This function saves the target URL in the session for future redirection 

415 to said page 

416 target: expects a string that is a URL or a route to a page in the application 

417 return: None 

418 """ 

419 session["redirectTarget"] = target 

420 

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

422def getDietInfo(): 

423 dietaryInfo = request.form 

424 user = dietaryInfo["user"] 

425 dietInfo = dietaryInfo["dietInfo"] 

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

427 updateDietInfo(user, dietInfo) 

428 

429 return " "