Coverage for app/logic/participants.py: 90%
103 statements
« prev ^ index » next coverage.py v7.2.7, created at 2024-03-11 22:25 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2024-03-11 22:25 +0000
1from flask import g
2from peewee import fn, JOIN
3from datetime import date
4from typing import List
6from app.models.user import User
7from app.models.event import Event
8from app.models.term import Term
9from app.models.eventRsvp import EventRsvp
10from app.models.program import Program
11from app.models.eventParticipant import EventParticipant
12from app.logic.users import isEligibleForProgram
13from app.logic.volunteers import getEventLengthInHours
14from app.logic.events import getEventRsvpCountsForTerm
15from app.logic.createLogs import createRsvpLog
17def trainedParticipants(programID: int, targetTerm: Term) -> List[User]:
18 """
19 This function tracks the users who have attended every Prerequisite
20 event and adds them to a list that will not flag them when tracking hours.
21 Returns a list of user objects who've completed all training events.
22 """
24 # Reset program eligibility each term for all other trainings
25 isRelevantAllVolunteer: bool = (Event.isAllVolunteerTraining) & (Event.term.academicYear == targetTerm.academicYear)
26 isRelevantProgramTraining: bool = (Event.program == programID) & (Event.term == targetTerm) & (Event.isTraining)
27 allTrainings: List[Event] = (Event.select()
28 .join(Term)
29 .where(isRelevantAllVolunteer | isRelevantProgramTraining,
30 Event.isCanceled == False))
32 fullyTrainedUsers: List[User] = (User.select()
33 .join(EventParticipant)
34 .where(EventParticipant.event.in_(allTrainings))
35 .group_by(EventParticipant.user)
36 .having(fn.Count(EventParticipant.user) == len(allTrainings)).order_by(User.username))
38 return list(fullyTrainedUsers)
40def addBnumberAsParticipant(bnumber, eventId):
41 """Accepts scan input and signs in the user. If user exists or is already
42 signed in will return user and login status"""
43 try:
44 kioskUser = User.get(User.bnumber == bnumber)
45 except Exception as e:
46 print(e)
47 return None, "does not exist"
49 event = Event.get_by_id(eventId)
50 if not isEligibleForProgram(event.program, kioskUser):
51 userStatus = "banned"
53 elif checkUserVolunteer(kioskUser, event):
54 userStatus = "already signed in"
56 else:
57 userStatus = "success"
58 # We are not using addPersonToEvent to do this because
59 # that function checks if the event is in the past, but
60 # someone could start signing people up via the kiosk
61 # before an event has started
62 totalHours = getEventLengthInHours(event.timeStart, event.timeEnd, event.startDate)
63 EventParticipant.create (user=kioskUser, event=event, hoursEarned=totalHours)
65 return kioskUser, userStatus
67def checkUserRsvp(user, event):
68 return EventRsvp.select().where(EventRsvp.user==user, EventRsvp.event == event).exists()
70def checkUserVolunteer(user, event):
71 return EventParticipant.select().where(EventParticipant.user == user, EventParticipant.event == event).exists()
73def addPersonToEvent(user, event):
74 """
75 Add a user to an event.
76 If the event is in the past, add the user as a volunteer (EventParticipant) including hours worked.
77 If the event is in the future, rsvp for the user (EventRsvp)
79 Returns True if the operation was successful, false otherwise
80 """
81 try:
82 volunteerExists = checkUserVolunteer(user, event)
83 rsvpExists = checkUserRsvp(user, event)
84 if event.isPast:
85 if not volunteerExists:
86 # We duplicate these two lines in addBnumberAsParticipant
87 eventHours = getEventLengthInHours(event.timeStart, event.timeEnd, event.startDate)
88 EventParticipant.create(user = user, event = event, hoursEarned = eventHours)
89 else:
90 if not rsvpExists:
91 currentRsvp = getEventRsvpCountsForTerm(event.term)
92 waitlist = currentRsvp[event.id] >= event.rsvpLimit if event.rsvpLimit is not None else 0
93 EventRsvp.create(user = user, event = event, rsvpWaitlist = waitlist)
95 targetList = "the waitlist" if waitlist else "the RSVP list"
96 if g.current_user.username == user.username:
97 createRsvpLog(event.id, f"{user.fullName} joined {targetList}.")
98 else:
99 createRsvpLog(event.id, f"Added {user.fullName} to {targetList}.")
101 if volunteerExists or rsvpExists:
102 return "already in"
103 except Exception as e:
104 print(e)
105 return False
107 return True
109def unattendedRequiredEvents(program, user):
111 # Check for events that are prerequisite for program
112 requiredEvents = (Event.select(Event)
113 .where(Event.isTraining == True, Event.program == program))
115 if requiredEvents:
116 attendedRequiredEventsList = []
117 for event in requiredEvents:
118 attendedRequirement = (EventParticipant.select().where(EventParticipant.user == user, EventParticipant.event == event))
119 if not attendedRequirement:
120 attendedRequiredEventsList.append(event.name)
121 if attendedRequiredEventsList is not None:
122 return attendedRequiredEventsList
123 else:
124 return []
127def getEventParticipants(event):
128 eventParticipants = (EventParticipant.select(EventParticipant, User)
129 .join(User)
130 .where(EventParticipant.event == event))
132 return [p for p in eventParticipants]
134def getParticipationStatusForTrainings(program, userList, term):
135 """
136 This function returns a dictionary of all trainings for a program and
137 whether the current user participated in them.
139 :returns: trainings for program and if the user participated
140 """
141 isRelevantAllVolunteer = (Event.isAllVolunteerTraining) & (Event.term.academicYear == term.academicYear)
142 isRelevantProgramTraining = (Event.program == program) & (Event.term == term) & (Event.isTraining)
143 programTrainings = (Event.select(Event, Term, EventParticipant, EventRsvp)
144 .join(EventParticipant, JOIN.LEFT_OUTER).switch()
145 .join(EventRsvp, JOIN.LEFT_OUTER).switch()
146 .join(Term)
147 .where(isRelevantAllVolunteer | isRelevantProgramTraining, (Event.isCanceled != True)).order_by(Event.startDate))
149 # Create a dictionary where the keys are trainings and values are a list of those who attended
150 trainingData = {}
151 for training in programTrainings:
152 try:
153 if training.isPast:
154 trainingData[training] = trainingData.get(training, []) + [training.eventparticipant.user_id]
155 else: # The training has yet to happen
156 trainingData[training] = trainingData.get(training, []) + [training.eventrsvp.user_id]
157 except AttributeError:
158 trainingData[training] = trainingData.get(training, [])
159 # Create a dictionary binding usernames to tuples. The tuples consist of the training (event object) and whether or not they attended it (bool)
160 userParticipationStatus = {}
161 for user in userList:
162 for training, attendeeList in trainingData.items():
163 userParticipationStatus[user.username] = userParticipationStatus.get(user.username, []) + [(training, user.username in attendeeList)]
165 return userParticipationStatus
167def sortParticipantsByStatus(event):
168 """
169 Takes in an event object, queries all participants, and then filters those
170 participants by their attendee status.
172 return: a list of participants who didn't attend, a list of participants who are waitlisted,
173 a list of participants who attended, and a list of all participants who have some status for the
174 event.
175 """
176 eventParticipants = getEventParticipants(event)
178 # get all RSVPs for event and filter out those that did not attend into separate list
179 eventRsvpData = list(EventRsvp.select(EventRsvp, User).join(User).where(EventRsvp.event==event).order_by(EventRsvp.rsvpTime))
180 eventNonAttendedData = [rsvp for rsvp in eventRsvpData if rsvp.user not in eventParticipants]
182 if event.isPast:
183 eventVolunteerData = eventParticipants
185 # if the event date has passed disregard the waitlist
186 eventWaitlistData = []
187 else:
188 # if rsvp is required for the event, grab all volunteers that are in the waitlist
189 eventWaitlistData = [volunteer for volunteer in (eventParticipants + eventRsvpData) if volunteer.rsvpWaitlist and event.isRsvpRequired]
191 # put the rest of the users that are not on the waitlist into the volunteer data
192 eventVolunteerData = [volunteer for volunteer in eventNonAttendedData if volunteer not in eventWaitlistData]
193 eventNonAttendedData = []
195 return eventNonAttendedData, eventWaitlistData, eventVolunteerData, eventParticipants