Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
064415d
initail commit
qasema Aug 31, 2023
378f998
update student admin
qasema Sep 1, 2023
7d19f29
update student admin
qasema Sep 1, 2023
bd4bf0b
update nav bar
qasema Sep 1, 2023
8600142
new updates
qasema Sep 1, 2023
148cbfb
update student admin role in test data
qasema Sep 1, 2023
86b43a3
Modified conditional logic for Student Admin in different areas.
ojmakinde Sep 6, 2023
ec5bf44
refactor Celts Admins
qasema Sep 6, 2023
d81bc7c
update event list
qasema Sep 6, 2023
1d505db
Merge branch 'development' into studentAdmin
AndersonStettner Sep 7, 2023
227866a
update permoissions
qasema Sep 7, 2023
0bd4a63
update user managment
qasema Sep 8, 2023
2f401f8
remove space
qasema Sep 8, 2023
7554fb3
remove debugging statement
qasema Sep 8, 2023
2d08374
Corrected conditional logic for edit event view
ojmakinde Sep 18, 2023
ffcf2c9
Corrected eventNav functionality for student admins
ojmakinde Sep 19, 2023
379e7ae
Merge branch 'development' into studentAdmin
AndersonStettner Sep 19, 2023
c9cbad0
Created test stubs
ojmakinde Sep 20, 2023
8ff3aa9
Added myself to test data and wrote a unit test for studentAdmin logic
ojmakinde Sep 21, 2023
293bd19
Merge branch 'studentAdmin' of github.com:BCStudentSoftwareDevTeam/ce…
ojmakinde Sep 21, 2023
60c7431
Corrected eligibilty view error
ojmakinde Sep 27, 2023
ad4fca6
Merge branch 'development' into studentAdmin
ojmakinde Oct 23, 2023
4b9c752
Merge branch 'development' into studentAdmin
AndersonStettner Oct 26, 2023
d1a2433
Corrected capitaliztion and string formatting on flash messages
ojmakinde Nov 8, 2023
740f6b8
Corrected capitaliztion and string formatting on flash messages
ojmakinde Nov 8, 2023
d5a2f74
fix conditions
qasema Nov 13, 2023
a340704
merge development
qasema Nov 13, 2023
bd4d1fd
bonnerscholars
qasema Nov 13, 2023
c54bf3b
fix expressions
qasema Nov 13, 2023
e50ca71
fix a bug
qasema Nov 13, 2023
7d90425
resolve merge conflict
qasema Nov 15, 2023
cc64881
Removed Finn Bledsoe as faculty
ojmakinde Dec 4, 2023
9bf62ff
remove condition
qasema Dec 4, 2023
5edb6cd
merge
qasema Dec 4, 2023
329d1d3
Merge branch 'development' into studentAdmin
AndersonStettner Jan 17, 2024
419d2a3
refactored the function to be more logical
azabeli Mar 1, 2024
1e44a8c
Merge branch 'studentAdmin' of github.com:BCStudentSoftwareDevTeam/ce…
azabeli Mar 1, 2024
807d724
resolved conflicts
bledsoef Mar 1, 2024
4600233
Merge branch 'studentAdmin' of github.com:BCStudentSoftwareDevTeam/ce…
bledsoef Mar 1, 2024
86efbc6
updated test data and removed unused routes
bledsoef Mar 11, 2024
1415951
did a little refactoring and added some typing
bledsoef Mar 11, 2024
44d1927
added some more typing and did a little more refactoring
bledsoef Mar 11, 2024
6381d44
changing tests and reformatting logic/controllers to make them more t…
bledsoef Mar 11, 2024
f560f80
rewrote many tests
bledsoef Mar 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 16 additions & 16 deletions app/controllers/admin/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import json
from datetime import datetime
import os
from typing import List

from app import app
from app.models.program import Program
Expand Down Expand Up @@ -44,17 +45,16 @@ def switchUser():

print(f"Switching user from {g.current_user} to",request.form['newuser'])
session['current_user'] = model_to_dict(User.get_by_id(request.form['newuser']))

return redirect(request.referrer)


@admin_bp.route('/eventTemplates')
def templateSelect():
if g.current_user.isCeltsAdmin or g.current_user.isCeltsStudentStaff:
allprograms = getAllowedPrograms(g.current_user)
visibleTemplates = getAllowedTemplates(g.current_user)
def templateSelect() -> str:
if g.current_user.isAdmin:
allowedPrograms: List[Program] = getAllowedPrograms(g.current_user)
visibleTemplates: List[EventTemplate] = getAllowedTemplates(g.current_user)
return render_template("/events/template_selector.html",
programs=allprograms,
programs=allowedPrograms,
celtsSponsoredProgram = Program.get(Program.isOtherCeltsSponsored),
templates=visibleTemplates)
else:
Expand Down Expand Up @@ -143,16 +143,17 @@ def createEvent(templateid, programid):


@admin_bp.route('/event/<eventId>/rsvp', methods=['GET'])
def rsvpLogDisplay(eventId):
event = Event.get_by_id(eventId)
if g.current_user.isCeltsAdmin or (g.current_user.isCeltsStudentStaff and g.current_user.isProgramManagerFor(event.program)):
allLogs = EventRsvpLog.select(EventRsvpLog, User).join(User).where(EventRsvpLog.event_id == eventId).order_by(EventRsvpLog.createdOn.desc())
return render_template("/events/rsvpLog.html",
event = event,
allLogs = allLogs)
else:
def rsvpLogDisplay(eventId: int) -> str:
event: Event = Event.get_by_id(eventId)

canAccessProgram: bool = event.program in getAllowedPrograms(g.current_user)
if not canAccessProgram:
abort(403)

allLogs: List[(EventRsvpLog, User)] = EventRsvpLog.select(EventRsvpLog, User).join(User).where(EventRsvpLog.event_id == eventId).order_by(EventRsvpLog.createdOn.desc())
return render_template("/events/rsvpLog.html",
event = event,
allLogs = allLogs)

@admin_bp.route('/event/<eventId>/view', methods=['GET'])
@admin_bp.route('/event/<eventId>/edit', methods=['GET','POST'])
Expand All @@ -168,8 +169,7 @@ def eventDisplay(eventId):
except DoesNotExist as e:
print(f"Unknown event: {eventId}")
abort(404)

notPermitted = not (g.current_user.isCeltsAdmin or g.current_user.isProgramManagerForEvent(event))
notPermitted = not (event.program in getAllowedPrograms(g.current_user) or (g.current_user.isCeltsStudentAdmin and event.programName != "Bonner Scholars"))
if 'edit' in request.url_rule.rule and notPermitted:
abort(403)

Expand Down
101 changes: 47 additions & 54 deletions app/controllers/admin/userManagement.py
Original file line number Diff line number Diff line change
@@ -1,78 +1,69 @@
from flask import render_template,request, flash, g, abort, redirect, url_for
import re
from typing import Dict, Any, List
from app.controllers.admin import admin_bp
from app.models.user import User
from app.models.program import Program
from app.logic.userManagement import addCeltsAdmin,addCeltsStudentStaff,removeCeltsAdmin,removeCeltsStudentStaff
from app.models.term import Term
from app.logic.userManagement import addCeltsAdmin,addCeltsStudentStaff,addCeltsStudentAdmin ,removeCeltsAdmin,removeCeltsStudentStaff, removeCeltsStudentAdmin
from app.logic.userManagement import changeProgramInfo
from app.logic.utils import selectSurroundingTerms
from app.logic.term import addNextTerm, changeCurrentTerm

@admin_bp.route('/admin/manageUsers', methods = ['POST'])
def manageUsers():
eventData = request.form
user = eventData['user']
method = eventData['method']
username = re.sub("[()]","", (user.split())[-1])
def manageUsers() -> str:
eventData: Dict[str, str] = request.form
user: str = eventData['user']
method: str = eventData['method']
username: str = re.sub("[()]","", (user.split())[-1])

try:
user = User.get_by_id(username)
user: User = User.get_by_id(username)
except Exception as e:
print(e)
flash(username + " is an invalid user.", "danger")
return ("danger", 500)

if method == "addCeltsAdmin":
if user.isStudent and not user.isCeltsStudentStaff:
flash(user.firstName + " " + user.lastName + " cannot be added as a CELTS-Link admin", 'danger')
else:
if user.isCeltsAdmin:
flash(user.firstName + " " + user.lastName + " is already a CELTS-Link Admin", 'danger')
else:
addCeltsAdmin(user)
flash(user.firstName + " " + user.lastName + " has been added as a CELTS-Link Admin", 'success')
try:
addCeltsAdmin(user)
flash(f"{user.fullName} has been added as CELTS Admin.", 'success')
except Exception as errorMessage:
flash(str(errorMessage), 'danger')

elif method == "addCeltsStudentStaff":
if not user.isStudent:
flash(username + " cannot be added as CELTS Student Staff", 'danger')
else:
if user.isCeltsStudentStaff:
flash(user.firstName + " " + user.lastName + " is already a CELTS Student Staff", 'danger')
else:
addCeltsStudentStaff(user)
flash(user.firstName + " " + user.lastName + " has been added as a CELTS Student Staff", 'success')
try:
addCeltsStudentStaff(user)
flash(f"{user.fullName} has been added as CELTS Student Staff.", 'success')
except Exception as errorMessage:
flash(str(errorMessage), "danger")

elif method == "addCeltsStudentAdmin":
try:
addCeltsStudentAdmin(user)
flash(f"{user.fullName} has been added as CELTS Student Admin.", 'success')
except Exception as errorMessage:
flash(str(errorMessage), "danger")

elif method == "removeCeltsAdmin":
removeCeltsAdmin(user)
flash(user.firstName + " " + user.lastName + " is no longer a CELTS Admin ", 'success')
flash(f"{user.fullName} is no longer a CELTS Admin.", 'success')

elif method == "removeCeltsStudentStaff":
removeCeltsStudentStaff(user)
flash(user.firstName + " " + user.lastName + " is no longer a CELTS Student Staff", 'success')
return ("success")
flash(f"{user.fullName} is no longer a CELTS Student Staff.", 'success')

@admin_bp.route('/addProgramManagers', methods=['POST'])
def addProgramManagers():
eventData = request.form
try:
return addProgramManager(eventData['username'],int(eventData['programID']))
except Exception as e:
print(e)
flash('Error while trying to add a manager.','warning')
abort(500,"'Error while trying to add a manager.'")
elif method == "removeCeltsStudentAdmin":
removeCeltsStudentAdmin(user)
flash(f"{user.fullName} is no longer a CELTS Student Admin.", 'success')

@admin_bp.route('/removeProgramManagers', methods=['POST'])
def removeProgramManagers():
eventData = request.form
try:
return removeProgramManager(eventData['username'],int(eventData['programID']))
except Exception as e:
print(e)
flash('Error while removing a manager.','warning')
abort(500,"Error while trying to remove a manager.")
return ("success")

@admin_bp.route('/admin/updateProgramInfo/<programID>', methods=['POST'])
def updateProgramInfo(programID):
def updateProgramInfo(programID: int) -> Any:
"""Grabs info and then outputs it to logic function"""
programInfo = request.form # grabs user inputs
if g.current_user.isCeltsAdmin:
programInfo: Dict[str, str] = request.form # grabs user inputs
if g.current_user.isCeltsAdmin or g.current_user.isCeltsStudentAdmin:
try:
changeProgramInfo(programInfo["programName"], #calls logic function to add data to database
programInfo["contactEmail"],
Expand All @@ -89,17 +80,19 @@ def updateProgramInfo(programID):
abort(403)

@admin_bp.route('/admin', methods = ['GET'])
def userManagement():
terms = selectSurroundingTerms(g.current_term)
current_programs = Program.select()
currentAdmins = list(User.select().where(User.isCeltsAdmin))
currentStudentStaff = list(User.select().where(User.isCeltsStudentStaff))
if g.current_user.isCeltsAdmin:
def userManagement() -> str:
terms: List[Term] = selectSurroundingTerms(g.current_term)
currentPrograms: List[Program] = list(Program.select())
currentAdmins: List[User] = list(User.select().where(User.isCeltsAdmin))
currentStudentStaff: List[User] = list(User.select().where(User.isCeltsStudentStaff))
currentStudentAdmin: List[User] = list(User.select().where(User.isCeltsStudentAdmin))
if g.current_user.isCeltsAdmin or g.current_user.isCeltsStudentAdmin:
return render_template('admin/userManagement.html',
terms = terms,
programs = list(current_programs),
programs = currentPrograms,
currentAdmins = currentAdmins,
currentStudentStaff = currentStudentStaff,
currentStudentAdmin = currentStudentAdmin,
)
abort(403)

Expand Down
93 changes: 49 additions & 44 deletions app/controllers/admin/volunteers.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
from flask import request, render_template, redirect, url_for, flash, abort, g, json, jsonify
from peewee import DoesNotExist, JOIN
from playhouse.shortcuts import model_to_dict
from typing import List

from app.controllers.admin import admin_bp
from app.models.event import Event
from app.models.program import Program
from app.models.user import User
from app.models.eventParticipant import EventParticipant
from app.models.emergencyContact import EmergencyContact

from app.logic.userManagement import getAllowedPrograms
from app.logic.searchUsers import searchUsers
from app.logic.volunteers import updateEventParticipants, getEventLengthInHours, addUserBackgroundCheck, setProgramManager
from app.logic.participants import trainedParticipants, addPersonToEvent, getParticipationStatusForTrainings, sortParticipantsByStatus
Expand All @@ -18,13 +22,13 @@


@admin_bp.route('/searchVolunteers/<query>', methods = ['GET'])
def getVolunteers(query):
def getVolunteers(query: str) -> str:
'''Accepts user input and queries the database returning results that matches user search'''

return json.dumps(searchUsers(query))

@admin_bp.route('/event/<eventID>/manage_volunteers', methods=['GET', 'POST'])
def manageVolunteersPage(eventID):
def manageVolunteersPage(eventID) -> str:
"""
Controller that handles POST and GET requests regarding the Manage Volunteers page.

Expand All @@ -36,14 +40,14 @@ def manageVolunteersPage(eventID):
renders the manageVolunteers.html template.
"""
try:
event = Event.get_by_id(eventID)
event: Event = Event.get_by_id(eventID)
except DoesNotExist as e:
print(f"No event found for {eventID}", e)
abort(404)

# ------------ POST request ------------
if request.method == "POST":
volunteerUpdated = updateEventParticipants(request.form)
volunteerUpdated: bool = updateEventParticipants(request.form)

# error handling depending on the boolean returned from updateEventParticipants
if volunteerUpdated:
Expand All @@ -53,54 +57,55 @@ def manageVolunteersPage(eventID):
return redirect(url_for("admin.manageVolunteersPage", eventID=eventID))

# ------------ GET request ------------
elif request.method == "GET":
if not (g.current_user.isCeltsAdmin or (g.current_user.isCeltsStudentStaff and g.current_user.isProgramManagerForEvent(event))):
abort(403)

# ------- Grab the different lists of participants -------
trainedParticipantsForProgramAndTerm = trainedParticipants(event.program, event.term)

bannedUsersForProgram = [bannedUser.user for bannedUser in getBannedUsers(event.program)]

eventNonAttendedData, eventWaitlistData, eventVolunteerData, eventParticipants = sortParticipantsByStatus(event)

allRelevantUsers = [participant.user for participant in (eventParticipants + eventNonAttendedData + eventWaitlistData)]

# ----------- Get miscellaneous data -----------

participationStatusForTrainings = getParticipationStatusForTrainings(event.program, allRelevantUsers, event.term)

eventLengthInHours = getEventLengthInHours(event.timeStart, event.timeEnd, event.startDate)

recurringVolunteers = getPreviousRecurringEventData(event.recurringId)

currentRsvpAmount = getEventRsvpCount(event.id)

# ----------- Render template with all of the data ------------
return render_template("/events/manageVolunteers.html",
eventVolunteerData = eventVolunteerData,
eventNonAttendedData = eventNonAttendedData,
eventWaitlistData = eventWaitlistData,
eventLength = eventLengthInHours,
event = event,
recurringVolunteers = recurringVolunteers,
bannedUsersForProgram = bannedUsersForProgram,
trainedParticipantsForProgramAndTerm = trainedParticipantsForProgramAndTerm,
participationStatusForTrainings = participationStatusForTrainings,
currentRsvpAmount = currentRsvpAmount)
canAccessProgram: bool = event.program in getAllowedPrograms(g.current_user)
if not canAccessProgram:
abort(403)

# ------- Grab the different lists of participants -------
trainedParticipantsForProgramAndTerm: List[User] = trainedParticipants(event.program, event.term)

bannedUsersForProgram: List[User] = [bannedUser.user for bannedUser in getBannedUsers(event.program)]

eventNonAttendedData, eventWaitlistData, eventVolunteerData, eventParticipants = sortParticipantsByStatus(event)

allRelevantUsers = [participant.user for participant in (eventParticipants + eventNonAttendedData + eventWaitlistData)]

# ----------- Get miscellaneous data -----------

participationStatusForTrainings = getParticipationStatusForTrainings(event.program, allRelevantUsers, event.term)

eventLengthInHours: float = getEventLengthInHours(event.timeStart, event.timeEnd, event.startDate)

recurringVolunteers = getPreviousRecurringEventData(event.recurringId)

currentRsvpAmount: int = getEventRsvpCount(event.id)

# ----------- Render template with all of the data ------------

return render_template("/events/manageVolunteers.html",
eventVolunteerData = eventVolunteerData,
eventNonAttendedData = eventNonAttendedData,
eventWaitlistData = eventWaitlistData,
eventLength = eventLengthInHours,
event = event,
recurringVolunteers = recurringVolunteers,
bannedUsersForProgram = bannedUsersForProgram,
trainedParticipantsForProgramAndTerm = trainedParticipantsForProgramAndTerm,
participationStatusForTrainings = participationStatusForTrainings,
currentRsvpAmount = currentRsvpAmount)

@admin_bp.route('/event/<eventID>/volunteer_details', methods=['GET'])
def volunteerDetailsPage(eventID):
def volunteerDetailsPage(eventID) -> str:
try:
event = Event.get_by_id(eventID)
event: Event = Event.get_by_id(eventID)
except DoesNotExist as e:
print(f"No event found for {eventID}", e)
abort(404)

if not (g.current_user.isCeltsAdmin or (g.current_user.isCeltsStudentStaff and g.current_user.isProgramManagerForEvent(event))):
canAccessProgram: bool = event.program in getAllowedPrograms(g.current_user)
if not (canAccessProgram):
abort(403)

eventRsvpData = list(EventRsvp.select(EmergencyContact, EventRsvp)
eventRsvpData: List = list(EventRsvp.select(EmergencyContact, EventRsvp)
.join(EmergencyContact, JOIN.LEFT_OUTER, on=(EmergencyContact.user==EventRsvp.user))
.where(EventRsvp.event==event))
eventParticipantData = list(EventParticipant.select(EmergencyContact, EventParticipant)
Expand Down
12 changes: 7 additions & 5 deletions app/logic/participants.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from flask import g
from peewee import fn, JOIN
from datetime import date
from typing import List

from app.models.user import User
from app.models.event import Event
from app.models.term import Term
Expand All @@ -12,22 +14,22 @@
from app.logic.events import getEventRsvpCountsForTerm
from app.logic.createLogs import createRsvpLog

def trainedParticipants(programID, targetTerm):
def trainedParticipants(programID: int, targetTerm: Term) -> List[User]:
"""
This function tracks the users who have attended every Prerequisite
event and adds them to a list that will not flag them when tracking hours.
Returns a list of user objects who've completed all training events.
"""

# Reset program eligibility each term for all other trainings
isRelevantAllVolunteer = (Event.isAllVolunteerTraining) & (Event.term.academicYear == targetTerm.academicYear)
isRelevantProgramTraining = (Event.program == programID) & (Event.term == targetTerm) & (Event.isTraining)
allTrainings = (Event.select()
isRelevantAllVolunteer: bool = (Event.isAllVolunteerTraining) & (Event.term.academicYear == targetTerm.academicYear)
isRelevantProgramTraining: bool = (Event.program == programID) & (Event.term == targetTerm) & (Event.isTraining)
allTrainings: List[Event] = (Event.select()
.join(Term)
.where(isRelevantAllVolunteer | isRelevantProgramTraining,
Event.isCanceled == False))

fullyTrainedUsers = (User.select()
fullyTrainedUsers: List[User] = (User.select()
.join(EventParticipant)
.where(EventParticipant.event.in_(allTrainings))
.group_by(EventParticipant.user)
Expand Down
Loading