Coverage for app/logic/participants.py: 90%

102 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2024-04-04 20:38 +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 

14 

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 """ 

21 

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)) 

29 

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)) 

35 

36 return list(fullyTrainedUsers) 

37 

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" 

46 

47 event = Event.get_by_id(eventId) 

48 if not isEligibleForProgram(event.program, kioskUser): 

49 userStatus = "banned" 

50 

51 elif checkUserVolunteer(kioskUser, event): 

52 userStatus = "already signed in" 

53 

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) 

62 

63 return kioskUser, userStatus 

64 

65def checkUserRsvp(user, event): 

66 return EventRsvp.select().where(EventRsvp.user==user, EventRsvp.event == event).exists() 

67 

68def checkUserVolunteer(user, event): 

69 return EventParticipant.select().where(EventParticipant.user == user, EventParticipant.event == event).exists() 

70 

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) 

76 

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.isPast: 

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) 

92 

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}.") 

98 

99 if volunteerExists or rsvpExists: 

100 return "already in" 

101 except Exception as e: 

102 print(e) 

103 return False 

104 

105 return True 

106 

107def unattendedRequiredEvents(program, user): 

108 

109 # Check for events that are prerequisite for program 

110 requiredEvents = (Event.select(Event) 

111 .where(Event.isTraining == True, Event.program == program)) 

112 

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 [] 

123 

124 

125def getEventParticipants(event): 

126 eventParticipants = (EventParticipant.select(EventParticipant, User) 

127 .join(User) 

128 .where(EventParticipant.event == event)) 

129 

130 return [p for p in eventParticipants] 

131 

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. 

136 

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)) 

146 

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.isPast: 

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)] 

162 

163 return userParticipationStatus 

164 

165def sortParticipantsByStatus(event): 

166 """ 

167 Takes in an event object, queries all participants, and then filters those 

168 participants by their attendee status. 

169 

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) 

175 

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] 

179 

180 if event.isPast: 

181 eventVolunteerData = eventParticipants 

182 

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] 

188 

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 = [] 

192 

193 return eventNonAttendedData, eventWaitlistData, eventVolunteerData, eventParticipants