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
« 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
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
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
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)
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']))
41 return redirect(request.referrer)
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)
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)
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"))
73 # Get the data from the form or from the template
74 eventData = template.templateData
76 if program:
77 eventData["program"] = program
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
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))
95 except Exception as e:
96 print("Error saving event:", e)
97 savedEvents = False
98 validationErrorMessage = "Unknown Error Saving Event. Please try again"
100 if savedEvents:
101 rsvpcohorts = request.form.getlist("cohorts[]")
102 for year in rsvpcohorts:
103 rsvpForBonnerCohort(int(year), savedEvents[0].id)
105 noun = (eventData['isRecurring'] == 'on' and "Events" or "Event") # pluralize
106 flash(f"{noun} successfully created!", 'success')
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')}.")
116 return redirect(url_for("admin.eventDisplay", eventId = savedEvents[0].id))
117 else:
118 flash(validationErrorMessage, 'warning')
120 # make sure our data is the same regardless of GET or POST
121 preprocessEventData(eventData)
122 isProgramManager = g.current_user.isProgramManagerFor(programid)
124 futureTerms = selectSurroundingTerms(g.current_term, prevTerms=0)
126 requirements, bonnerCohorts = [], []
127 if 'program' in eventData and eventData['program'].isBonnerScholars:
128 requirements = getCertRequirements(Certification.BONNER)
129 bonnerCohorts = getBonnerCohorts(limit=5)
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)
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)
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)
158 eventData = model_to_dict(event, recurse=False)
159 associatedAttachments = AttachmentUpload.select().where(AttachmentUpload.event == event)
161 if request.method == "POST": # Attempt to save form
162 eventData = request.form.copy()
163 try:
164 savedEvents, validationErrorMessage = attemptSaveEvent(eventData, getFilesFromRequest(request))
166 except Exception as e:
167 print("Error saving event:", e)
168 savedEvents = False
169 validationErrorMessage = "Unknown Error Saving Event. Please try again"
172 if savedEvents:
173 rsvpcohorts = request.form.getlist("cohorts[]")
174 for year in rsvpcohorts:
175 rsvpForBonnerCohort(int(year), event.id)
177 flash("Event successfully updated!", "success")
178 return redirect(url_for("admin.eventDisplay", eventId = event.id))
179 else:
180 flash(validationErrorMessage, 'warning')
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'])
191 requirements, bonnerCohorts = [], []
192 if eventData['program'] and eventData['program'].isBonnerScholars:
193 requirements = getCertRequirements(Certification.BONNER)
194 bonnerCohorts = getBonnerCohorts(limit=5)
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")
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]
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)
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))
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))
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))
259 except Exception as e:
260 print('Error while canceling event:', e)
261 return "", 500
263@admin_bp.route('/makeRecurringEvents', methods=['POST'])
264def addRecurringEvents():
265 recurringEvents = calculateRecurringEventFrequency(preprocessEventData(request.form.copy()))
266 return json.dumps(recurringEvents, default=str)
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))
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)
282@admin_bp.route('/addParticipants', methods = ['GET'])
283def addParticipants():
284 '''Renders the page, will be removed once merged with full page'''
286 return render_template('addParticipants.html',
287 title="Add Participants")
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)
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 ""
305@admin_bp.route("/manageBonner")
306def manageBonner():
307 if not g.current_user.isCeltsAdmin:
308 abort(403)
310 return render_template("/admin/bonnerManagement.html",
311 cohorts=getBonnerCohorts(),
312 events=getBonnerEvents(g.current_term),
313 requirements = getCertRequirements(certification=Certification.BONNER))
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)
320 try:
321 user = User.get_by_id(username)
322 except:
323 abort(500)
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)
336 return ""
338@admin_bp.route("/bonnerxls")
339def bonnerxls():
340 if not g.current_user.isCeltsAdmin:
341 abort(403)
343 newfile = makeBonnerXls()
344 return send_file(open(newfile, 'rb'), download_name='BonnerStudents.xlsx', as_attachment=True)
346@admin_bp.route("/saveRequirements/<certid>", methods=["POST"])
347def saveRequirements(certid):
348 if not g.current_user.isCeltsAdmin:
349 abort(403)
351 newRequirements = updateCertRequirements(certid, request.get_json())
353 return jsonify([requirement.id for requirement in newRequirements])