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