Coverage for app/controllers/admin/routes.py: 26%

245 statements  

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

1from flask import request, render_template, url_for, g, Flask, redirect 

2from flask import flash, abort, jsonify, session, send_file 

3from peewee import DoesNotExist, fn, IntegrityError 

4from playhouse.shortcuts import model_to_dict, dict_to_model 

5import json 

6from datetime import datetime, date 

7 

8from app import app 

9from app.models.program import Program 

10from app.models.event import Event 

11from app.models.user import User 

12from app.models.eventTemplate import EventTemplate 

13from app.models.adminLogs import AdminLogs 

14from app.models.attachmentUpload import AttachmentUpload 

15from app.models.bonnerCohort import BonnerCohort 

16from app.models.certification import Certification 

17from app.models.user import User 

18from app.models.eventViews import EventView 

19 

20from app.logic.userManagement import getAllowedPrograms, getAllowedTemplates 

21from app.logic.adminLogs import createLog 

22from app.logic.certification import getCertRequirements, updateCertRequirements 

23from app.logic.volunteers import getEventLengthInHours 

24from app.logic.utils import selectSurroundingTerms, getFilesFromRequest 

25from app.logic.events import deleteEvent, attemptSaveEvent, preprocessEventData, calculateRecurringEventFrequency, deleteEventAndAllFollowing, deleteAllRecurringEvents, getBonnerEvents,addEventView 

26from app.logic.participants import getEventParticipants, getUserParticipatedTrainingEvents, checkUserRsvp, checkUserVolunteer 

27from app.logic.fileHandler import FileHandler 

28from app.logic.bonner import getBonnerCohorts, makeBonnerXls, rsvpForBonnerCohort 

29from app.controllers.admin import admin_bp 

30 

31 

32@admin_bp.route('/switch_user', methods=['POST']) 

33def switchUser(): 

34 if app.env == "production": 

35 print(f"An attempt was made to switch to another user by {g.current_user.username}!") 

36 abort(403) 

37 

38 print(f"Switching user from {g.current_user} to",request.form['newuser']) 

39 session['current_user'] = model_to_dict(User.get_by_id(request.form['newuser'])) 

40 

41 return redirect(request.referrer) 

42 

43 

44@admin_bp.route('/eventTemplates') 

45def templateSelect(): 

46 if g.current_user.isCeltsAdmin or g.current_user.isCeltsStudentStaff: 

47 allprograms = getAllowedPrograms(g.current_user) 

48 visibleTemplates = getAllowedTemplates(g.current_user) 

49 return render_template("/events/template_selector.html", 

50 programs=allprograms, 

51 templates=visibleTemplates) 

52 else: 

53 abort(403) 

54 

55 

56@admin_bp.route('/eventTemplates/<templateid>/create', methods=['GET','POST']) 

57@admin_bp.route('/eventTemplates/<templateid>/<programid>/create', methods=['GET','POST']) 

58def createEvent(templateid, programid=None): 

59 if not (g.current_user.isAdmin or g.current_user.isProgramManagerFor(programid)): 

60 abort(403) 

61 

62 # Validate given URL 

63 program = None 

64 try: 

65 template = EventTemplate.get_by_id(templateid) 

66 if programid: 

67 program = Program.get_by_id(programid) 

68 except DoesNotExist as e: 

69 print("Invalid template or program id:", e) 

70 flash("There was an error with your selection. Please try again or contact Systems Support.", "danger") 

71 return redirect(url_for("admin.program_picker")) 

72 

73 # Get the data from the form or from the template 

74 eventData = template.templateData 

75 

76 if program: 

77 eventData["program"] = program 

78 

79 if request.method == "GET": 

80 eventData['contactName'] = "CELTS Admin" 

81 eventData['contactEmail'] = app.config['celts_admin_contact'] 

82 if program: 

83 eventData['location'] = program.defaultLocation 

84 if program.contactName: 

85 eventData['contactName'] = program.contactName 

86 if program.contactEmail: 

87 eventData['contactEmail'] = program.contactEmail 

88 

89 # Try to save the form 

90 if request.method == "POST": 

91 eventData.update(request.form.copy()) 

92 try: 

93 savedEvents, validationErrorMessage = attemptSaveEvent(eventData, getFilesFromRequest(request)) 

94 

95 except Exception as e: 

96 print("Error saving event:", e) 

97 savedEvents = False 

98 validationErrorMessage = "Unknown Error Saving Event. Please try again" 

99 

100 if savedEvents: 

101 rsvpcohorts = request.form.getlist("cohorts[]") 

102 for year in rsvpcohorts: 

103 rsvpForBonnerCohort(int(year), savedEvents[0].id) 

104 

105 noun = (eventData['isRecurring'] == 'on' and "Events" or "Event") # pluralize 

106 flash(f"{noun} successfully created!", 'success') 

107 

108 if program: 

109 if len(savedEvents) > 1: 

110 createLog(f"Created a recurring event, <a href=\"{url_for('admin.eventDisplay', eventId = savedEvents[0].id)}\">{savedEvents[0].name}</a>, for {program.programName}, with a start date of {datetime.strftime(eventData['startDate'], '%m/%d/%Y')}. The last event in the series will be on {datetime.strftime(savedEvents[-1].startDate, '%m/%d/%Y')}.") 

111 else: 

112 createLog(f"Created <a href=\"{url_for('admin.eventDisplay', eventId = savedEvents[0].id)}\">{savedEvents[0].name}</a> for {program.programName}, with a start date of {datetime.strftime(eventData['startDate'], '%m/%d/%Y')}.") 

113 else: 

114 createLog(f"Created a non-program event, <a href=\"{url_for('admin.eventDisplay', eventId = savedEvents[0].id)}\">{savedEvents[0].name}</a>, with a start date of {datetime.strftime(eventData['startDate'], '%m/%d/%Y')}.") 

115 

116 return redirect(url_for("admin.eventDisplay", eventId = savedEvents[0].id)) 

117 else: 

118 flash(validationErrorMessage, 'warning') 

119 

120 # make sure our data is the same regardless of GET or POST 

121 preprocessEventData(eventData) 

122 isProgramManager = g.current_user.isProgramManagerFor(programid) 

123 

124 futureTerms = selectSurroundingTerms(g.current_term, prevTerms=0) 

125 

126 requirements, bonnerCohorts = [], [] 

127 if 'program' in eventData and eventData['program'].isBonnerScholars: 

128 requirements = getCertRequirements(Certification.BONNER) 

129 bonnerCohorts = getBonnerCohorts(limit=5) 

130 

131 return render_template(f"/admin/{template.templateFile}", 

132 template = template, 

133 eventData = eventData, 

134 futureTerms = futureTerms, 

135 requirements = requirements, 

136 bonnerCohorts = bonnerCohorts, 

137 isProgramManager = isProgramManager) 

138 

139@admin_bp.route('/eventsList/<eventId>/view', methods=['GET']) 

140@admin_bp.route('/eventsList/<eventId>/edit', methods=['GET','POST']) 

141def eventDisplay(eventId): 

142 pageViewsCount = EventView.select().where(EventView.event == eventId).count() 

143 if request.method == 'GET' and request.path == f'/eventsList/{eventId}/view': 

144 viewer = g.current_user 

145 event = Event.get_by_id(eventId) 

146 addEventView(viewer,event) 

147 # Validate given URL 

148 try: 

149 event = Event.get_by_id(eventId) 

150 except DoesNotExist as e: 

151 print(f"Unknown event: {eventId}") 

152 abort(404) 

153 

154 notPermitted = not (g.current_user.isCeltsAdmin or g.current_user.isProgramManagerForEvent(event)) 

155 if 'edit' in request.url_rule.rule and notPermitted: 

156 abort(403) 

157 

158 eventData = model_to_dict(event, recurse=False) 

159 associatedAttachments = AttachmentUpload.select().where(AttachmentUpload.event == event) 

160 

161 if request.method == "POST": # Attempt to save form 

162 eventData = request.form.copy() 

163 try: 

164 savedEvents, validationErrorMessage = attemptSaveEvent(eventData, getFilesFromRequest(request)) 

165 

166 except Exception as e: 

167 print("Error saving event:", e) 

168 savedEvents = False 

169 validationErrorMessage = "Unknown Error Saving Event. Please try again" 

170 

171 

172 if savedEvents: 

173 rsvpcohorts = request.form.getlist("cohorts[]") 

174 for year in rsvpcohorts: 

175 rsvpForBonnerCohort(int(year), event.id) 

176 

177 flash("Event successfully updated!", "success") 

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

179 else: 

180 flash(validationErrorMessage, 'warning') 

181 

182 # make sure our data is the same regardless of GET and POST 

183 preprocessEventData(eventData) 

184 eventData['program'] = event.singleProgram 

185 futureTerms = selectSurroundingTerms(g.current_term) 

186 userHasRSVPed = checkUserRsvp(g.current_user, event) 

187 isPastEvent = event.isPast 

188 filepaths = FileHandler(eventId=event.id).retrievePath(associatedAttachments) 

189 isProgramManager = g.current_user.isProgramManagerFor(eventData['program']) 

190 

191 requirements, bonnerCohorts = [], [] 

192 if eventData['program'] and eventData['program'].isBonnerScholars: 

193 requirements = getCertRequirements(Certification.BONNER) 

194 bonnerCohorts = getBonnerCohorts(limit=5) 

195 

196 rule = request.url_rule 

197 # Event Edit 

198 if 'edit' in rule.rule: 

199 return render_template("admin/createEvent.html", 

200 eventData = eventData, 

201 futureTerms=futureTerms, 

202 isPastEvent = isPastEvent, 

203 requirements = requirements, 

204 bonnerCohorts = bonnerCohorts, 

205 userHasRSVPed = userHasRSVPed, 

206 isProgramManager = isProgramManager, 

207 filepaths = filepaths) 

208 # Event View 

209 else: 

210 # get text representations of dates 

211 eventData['timeStart'] = event.timeStart.strftime("%-I:%M %p") 

212 eventData['timeEnd'] = event.timeEnd.strftime("%-I:%M %p") 

213 eventData["startDate"] = event.startDate.strftime("%m/%d/%Y") 

214 

215 # Identify the next event in a recurring series 

216 if event.recurringId: 

217 eventSeriesList = list(Event.select().where(Event.recurringId == event.recurringId).order_by(Event.startDate)) 

218 eventIndex = eventSeriesList.index(event) 

219 if len(eventSeriesList) != (eventIndex + 1): 

220 eventData["nextRecurringEvent"] = eventSeriesList[eventIndex + 1] 

221 

222 UserParticipatedTrainingEvents = getUserParticipatedTrainingEvents(eventData['program'], g.current_user, g.current_term) 

223 return render_template("eventView.html", 

224 eventData = eventData, 

225 isPastEvent = isPastEvent, 

226 userHasRSVPed = userHasRSVPed, 

227 programTrainings = UserParticipatedTrainingEvents, 

228 isProgramManager = isProgramManager, 

229 filepaths = filepaths, 

230 pageViewsCount= pageViewsCount) 

231 

232@admin_bp.route('/event/<eventId>/delete', methods=['POST']) 

233def deleteRoute(eventId): 

234 try: 

235 deleteEvent(eventId) 

236 flash("Event successfully deleted.", "success") 

237 return redirect(url_for("main.events", selectedTerm=g.current_term)) 

238 

239 except Exception as e: 

240 print('Error while canceling event:', e) 

241 return "", 500 

242@admin_bp.route('/event/<eventId>/deleteEventAndAllFollowing', methods=['POST']) 

243def deleteEventAndAllFollowingRoute(eventId): 

244 try: 

245 deleteEventAndAllFollowing(eventId) 

246 flash("Events successfully deleted.", "success") 

247 return redirect(url_for("main.events", selectedTerm=g.current_term)) 

248 

249 except Exception as e: 

250 print('Error while canceling event:', e) 

251 return "", 500 

252@admin_bp.route('/event/<eventId>/deleteAllRecurring', methods=['POST']) 

253def deleteAllRecurringEventsRoute(eventId): 

254 try: 

255 deleteAllRecurringEvents(eventId) 

256 flash("Events successfully deleted.", "success") 

257 return redirect(url_for("main.events", selectedTerm=g.current_term)) 

258 

259 except Exception as e: 

260 print('Error while canceling event:', e) 

261 return "", 500 

262 

263@admin_bp.route('/makeRecurringEvents', methods=['POST']) 

264def addRecurringEvents(): 

265 recurringEvents = calculateRecurringEventFrequency(preprocessEventData(request.form.copy())) 

266 return json.dumps(recurringEvents, default=str) 

267 

268 

269@admin_bp.route('/userProfile', methods=['POST']) 

270def userProfile(): 

271 volunteerName= request.form.copy() 

272 username = volunteerName['searchStudentsInput'].strip("()") 

273 user=username.split('(')[-1] 

274 return redirect(url_for('main.viewUsersProfile', username=user)) 

275 

276@admin_bp.route('/search_student', methods=['GET']) 

277def studentSearchPage(): 

278 if g.current_user.isAdmin: 

279 return render_template("/admin/searchStudentPage.html") 

280 abort(403) 

281 

282@admin_bp.route('/addParticipants', methods = ['GET']) 

283def addParticipants(): 

284 '''Renders the page, will be removed once merged with full page''' 

285 

286 return render_template('addParticipants.html', 

287 title="Add Participants") 

288 

289@admin_bp.route('/adminLogs', methods = ['GET', 'POST']) 

290def adminLogs(): 

291 if g.current_user.isCeltsAdmin: 

292 allLogs = AdminLogs.select(AdminLogs, User).join(User).order_by(AdminLogs.createdOn.desc()) 

293 return render_template("/admin/adminLogs.html", 

294 allLogs = allLogs) 

295 else: 

296 abort(403) 

297 

298@admin_bp.route("/deleteEventFile", methods=["POST"]) 

299def deleteEventFile(): 

300 fileData= request.form 

301 eventfile=FileHandler(eventId=fileData["eventId"]) 

302 eventfile.deleteFile(fileData["fileId"]) 

303 return "" 

304 

305@admin_bp.route("/manageBonner") 

306def manageBonner(): 

307 if not g.current_user.isCeltsAdmin: 

308 abort(403) 

309 

310 return render_template("/admin/bonnerManagement.html", 

311 cohorts=getBonnerCohorts(), 

312 events=getBonnerEvents(g.current_term), 

313 requirements = getCertRequirements(certification=Certification.BONNER)) 

314 

315@admin_bp.route("/bonner/<year>/<method>/<username>", methods=["POST"]) 

316def updatecohort(year, method, username): 

317 if not g.current_user.isCeltsAdmin: 

318 abort(403) 

319 

320 try: 

321 user = User.get_by_id(username) 

322 except: 

323 abort(500) 

324 

325 if method == "add": 

326 try: 

327 BonnerCohort.create(year=year, user=user) 

328 except IntegrityError as e: 

329 # if they already exist, ignore the error 

330 pass 

331 elif method == "remove": 

332 BonnerCohort.delete().where(BonnerCohort.user == user, BonnerCohort.year == year).execute() 

333 else: 

334 abort(500) 

335 

336 return "" 

337 

338@admin_bp.route("/bonnerxls") 

339def bonnerxls(): 

340 if not g.current_user.isCeltsAdmin: 

341 abort(403) 

342 

343 newfile = makeBonnerXls() 

344 return send_file(open(newfile, 'rb'), download_name='BonnerStudents.xlsx', as_attachment=True) 

345 

346@admin_bp.route("/saveRequirements/<certid>", methods=["POST"]) 

347def saveRequirements(certid): 

348 if not g.current_user.isCeltsAdmin: 

349 abort(403) 

350 

351 newRequirements = updateCertRequirements(certid, request.get_json()) 

352 

353 return jsonify([requirement.id for requirement in newRequirements])