Coverage for app/logic/users.py: 94%

112 statements  

« prev     ^ index     » next       coverage.py v7.10.2, created at 2026-09-04 14:23 +0000

1from app.models.eventParticipant import EventParticipant 

2from app.models.program import Program 

3from app.models.term import Term 

4from app.models.user import User 

5from app.models.event import Event 

6from app.models.programBan import ProgramBan 

7from app.models.interest import Interest 

8from app.models.note import Note 

9from app.models.user import User 

10from app.models.profileNote import ProfileNote 

11from app.models.programBan import ProgramBan 

12from app.models.backgroundCheck import BackgroundCheck 

13from app.models.backgroundCheckType import BackgroundCheckType 

14from app.logic.volunteers import addUserBackgroundCheck 

15import datetime 

16from peewee import JOIN, DoesNotExist, fn 

17from dateutil import parser 

18from flask import g 

19from playhouse.shortcuts import model_to_dict 

20 

21def isEligibleForProgram(program, user): 

22 """ 

23 Verifies if a given user is eligible for a program by checking if they are: 

24 1. Banned from a program. 

25 2. Missed All Volunteer Training (volunteers) or All CELTS training (labor) 

26 3. Missed Program-specific training 

27 4. Not signed the handbook (or expired) 

28 5. Background Check Submitted (does not matter if it passed or not) 

29 

30 :param program: accepts a Program object or a valid programid 

31 :param user: accepts a User object or userid 

32 :return: True if the user is not banned and meets the requirements, and False otherwise 

33 """ 

34 now = datetime.datetime.now() 

35 try: 

36 user = User.get_by_id(user) 

37 except DoesNotExist: 

38 raise DoesNotExist 

39 # Banned? 

40 if (ProgramBan.select().where(ProgramBan.user == user, ProgramBan.program == program, ProgramBan.endDate > now, ProgramBan.unbanNote == None).exists()): 

41 return False 

42 # Missed trainings? 

43 if user not in trainedParticipants(program, g.current_term): 

44 return False 

45 # Missing signature? 

46 if not user.signatureTerm: 

47 return False 

48 # Old signature? 

49 elif not user.signatureTerm.academicYear == g.current_term.academicYear: 

50 return False 

51 # Background check submitted? 

52 if not (User.select(User.username, BackgroundCheck.dateCompleted).join(BackgroundCheck).distinct()): 

53 return False 

54 

55 return True 

56 

57def addUserInterest(program_id, username): 

58 """ 

59 This function is used to add an interest to . 

60 Parameters: 

61 program_id: id of the program the user is interested in 

62 username: username of the user showing interest 

63 """ 

64 Interest.get_or_create(program = program_id, user = username) 

65 return True 

66 

67def removeUserInterest(program_id, username): 

68 """ 

69 This function is used to add or remove interest from the interest table. 

70 Parameters: 

71 program_id: id of the program the user is interested in 

72 username: username of the user showing disinterest 

73 

74 """ 

75 interestToDelete = Interest.get_or_none(Interest.program == program_id, Interest.user == username) 

76 if interestToDelete: 

77 interestToDelete.delete_instance() 

78 return True 

79 

80def getUserInterest(username): 

81 """ 

82 This function is used to retrieve a user's interests. 

83 Parameters:  

84 username: username of the user showing interest 

85 """ 

86 return Interest.select().where(Interest.user == username) 

87 

88def getProgramInterest(program): 

89 """ 

90 This function is used to retrieve a programs's interested users. 

91 Parameters: 

92 program: Program object 

93 """ 

94 return User.select().join(Interest).where(Interest.program == program) 

95 

96def getBannedUsers(program): 

97 """ 

98 This function returns users banned from a program. 

99 """ 

100 return ProgramBan.select().where(ProgramBan.program == program, ProgramBan.unbanNote == None) 

101 

102def isBannedFromEvent(username, eventId): 

103 """ 

104 This function returns whether the user is banned from the program associated with an event. 

105 """ 

106 program = Event.get_by_id(eventId).program 

107 user = User.get(User.username == username) 

108 isBanned = (ProgramBan.select() 

109 .join(User) 

110 .switch(ProgramBan) 

111 .join(Program) 

112 .where(ProgramBan.user == user, 

113 ProgramBan.program == program, 

114 ProgramBan.endDate > datetime.datetime.now(), 

115 ProgramBan.unbanNote.is_null()).exists() 

116 ) 

117 return isBanned 

118 

119def trainedParticipants(programID, targetTerm): 

120 """ 

121 This function tracks the users who have attended every Prerequisite 

122 event and adds them to a list that will not flag them when tracking hours. 

123 Returns a list of user objects who've completed all training events. 

124 """ 

125 

126 # Reset program eligibility each term for all other trainings 

127 isRelevantAllVolunteer = (Event.isAllVolunteerTraining | Event.isCeltsTraining) & (Event.term.academicYear == targetTerm.academicYear) 

128 isRelevantProgramTraining = (Event.program == programID) & (Event.term == targetTerm) & (Event.isTraining) 

129 allTrainings = (Event.select() 

130 .join(Term) 

131 .where(isRelevantAllVolunteer | isRelevantProgramTraining, 

132 Event.isCanceled == False)) 

133 

134 fullyTrainedUsers = (User.select() 

135 .join(EventParticipant) 

136 .where(EventParticipant.event.in_(allTrainings)) 

137 .group_by(EventParticipant.user) 

138 .having(fn.Count(EventParticipant.user) == len(allTrainings)).order_by(User.username)) 

139 return list(fullyTrainedUsers) 

140 

141def banUser(program_id, username, note, banEndDate, creator): 

142 """ 

143 This function creates an entry in the note table and programBan table in order 

144 to ban the selected user. 

145 Parameters: 

146 program_id: primary id of the program the user has been banned from 

147 username: username of the user to be banned 

148 note: note left about the ban, expected to be a reason why the change is needed 

149 banEndDate: date when the ban will end 

150 creator: the admin or person with authority who created the ban 

151 """ 

152 

153 noteForDb = Note.create(createdBy = creator, 

154 createdOn = datetime.datetime.now(), 

155 noteContent = note, 

156 isPrivate = 0, 

157 noteType = "ban") 

158 

159 ProgramBan.create(program = program_id, 

160 user = username, 

161 endDate = banEndDate, 

162 banNote = noteForDb) 

163 

164def unbanUser(program_id, username, note, creator): 

165 """ 

166 This function creates an entry in the note table and programBan table in order 

167 to unban the selected user. 

168 Parameters: 

169 program_id: primary id of the program the user has been unbanned from 

170 username: username of the user to be unbanned 

171 note: note left about the ban, expected to be a reason why the change is needed 

172 creator: the admin or person with authority who removed the ban 

173 """ 

174 noteForDb = Note.create(createdBy = creator, 

175 createdOn = datetime.datetime.now(), 

176 noteContent = note, 

177 isPrivate = 0, 

178 noteType = "unban") 

179 (ProgramBan.update(endDate = datetime.datetime.now(), 

180 unbanNote = noteForDb, 

181 removeFromTranscript = 0) 

182 .where(ProgramBan.program == program_id, 

183 ProgramBan.user == username, 

184 ProgramBan.endDate > datetime.datetime.now())).execute() 

185 

186def getUserBGCheckHistory(username): 

187 """ 

188 Get a users background check history 

189 """ 

190 bgHistory = {'CAN': [], 'FBI': [], 'SHS': [], 'BSL': [],'DDC':[]} 

191 

192 allBackgroundChecks = (BackgroundCheck.select(BackgroundCheck, BackgroundCheckType) 

193 .join(BackgroundCheckType) 

194 .where(BackgroundCheck.user == username, BackgroundCheck.deletionDate == None) 

195 .order_by(BackgroundCheck.dateCompleted.desc())) 

196 for row in allBackgroundChecks: 

197 bgHistory[row.type_id].append(row) 

198 return bgHistory 

199def getProfileNoteData(formData, includeUsername=False, includeId=False): 

200 noteData = { 

201 "visibility": int(formData.get("visibility", 1)), 

202 "bonner": formData.get("bonner") == "yes", 

203 "cceMinor": formData.get("cceMinor") == "yes", 

204 "noteTextbox": formData.get("noteTextbox", "").strip(), 

205 } 

206 if not noteData["noteTextbox"]: 

207 raise ValueError("Note cannot be empty") 

208 if includeUsername: 

209 noteData["username"] = formData.get("username") 

210 if not noteData["username"]: 

211 raise ValueError("Missing username") 

212 if includeId: 

213 noteData["profileNoteID"] = formData.get("id") 

214 if not noteData["profileNoteID"]: 

215 raise ValueError("Missing profile note ID") 

216 return noteData 

217 

218def addProfileNote(visibility, bonner, cceMinor, noteTextbox, username): 

219 user = User.get(User.username == username) 

220 visibility = int(visibility) 

221 if bonner: 

222 visibility = 1 

223 

224 noteForDb = Note.create( createdBy=g.current_user, createdOn=datetime.datetime.now(), noteContent=noteTextbox, noteType="profile" ) 

225 profileNote = ProfileNote.create( user=user, note=noteForDb, isBonnerNote=bonner, isCCEMinorNote=cceMinor, viewTier=visibility, ) 

226 return profileNote 

227 

228def updateProfileNote( profileNoteID, visibility, bonner, cceMinor, noteTextbox): 

229 profileNote = ProfileNote.get_by_id(profileNoteID) 

230 visibility = int(visibility) 

231 if bonner: 

232 visibility = 1 

233 note = profileNote.note 

234 note.noteContent = noteTextbox 

235 note.save() 

236 profileNote.viewTier = visibility 

237 profileNote.isBonnerNote = bonner 

238 profileNote.isCCEMinorNote = cceMinor 

239 profileNote.save() 

240 return profileNote 

241 

242def deleteProfileNote(noteId): 

243 return ProfileNote.delete().where(ProfileNote.id == noteId).execute() 

244 

245def updateDietInfo(username, dietContent): 

246 """ 

247 Creates or update a user's diet information 

248 """ 

249 

250 User.update(dietRestriction = dietContent).where(User.username == username).execute() 

251 

252 return ""