Coverage for app/logic/users.py: 94%
112 statements
« prev ^ index » next coverage.py v7.10.2, created at 2026-09-04 14:24 +0000
« prev ^ index » next coverage.py v7.10.2, created at 2026-09-04 14:24 +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
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)
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
55 return True
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
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
74 """
75 interestToDelete = Interest.get_or_none(Interest.program == program_id, Interest.user == username)
76 if interestToDelete:
77 interestToDelete.delete_instance()
78 return True
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)
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)
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)
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
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 """
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))
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)
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 """
153 noteForDb = Note.create(createdBy = creator,
154 createdOn = datetime.datetime.now(),
155 noteContent = note,
156 isPrivate = 0,
157 noteType = "ban")
159 ProgramBan.create(program = program_id,
160 user = username,
161 endDate = banEndDate,
162 banNote = noteForDb)
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()
186def getUserBGCheckHistory(username):
187 """
188 Get a users background check history
189 """
190 bgHistory = {'CAN': [], 'FBI': [], 'SHS': [], 'BSL': [],'DDC':[]}
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
218def addProfileNote(visibility, bonner, cceMinor, noteTextbox, username):
219 user = User.get(User.username == username)
220 visibility = int(visibility)
221 if bonner:
222 visibility = 1
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
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
242def deleteProfileNote(noteId):
243 return ProfileNote.delete().where(ProfileNote.id == noteId).execute()
245def updateDietInfo(username, dietContent):
246 """
247 Creates or update a user's diet information
248 """
250 User.update(dietRestriction = dietContent).where(User.username == username).execute()
252 return ""