Coverage for app/logic/utils.py: 54%

63 statements  

« prev     ^ index     » next       coverage.py v7.10.2, created at 2026-08-06 22:03 +0000

1from datetime import datetime 

2import collections.abc as collections 

3 

4from flask import session 

5from peewee import DoesNotExist 

6 

7from app.models.term import Term 

8 

9def selectSurroundingTerms(currentTerm, prevTerms=2, summerOnly=False): 

10 """ 

11 Returns a list of term objects around the provided Term object for the current term. 

12 Chooses the previous terms according to the prevTerms parameter (defaulting to 2), 

13 and then chooses terms for the next two years after the current term. 

14 

15 To get only the current and future terms, pass prevTerms=0. 

16 """ 

17 # Find the starting term 

18 startOrder = currentTerm.termOrder 

19 while(prevTerms > 0): 

20 orderPieces = startOrder.split('-') 

21 if(orderPieces[1] == "1"): 

22 orderPieces[0] = int(orderPieces[0]) - 1 

23 orderPieces[1] = 3 

24 else: 

25 orderPieces[1] = int(orderPieces[1]) - 1 

26 startOrder = f"{orderPieces[0]}-{orderPieces[1]}" 

27 

28 prevTerms -= 1 

29 

30 surroundingTerms = (Term.select() 

31 .where(Term.termOrder >= startOrder, Term.year <= currentTerm.year + 2) 

32 .order_by(Term.termOrder)) 

33 

34 if summerOnly: 

35 surroundingTerms = surroundingTerms.where(Term.isSummer) 

36 

37 return surroundingTerms 

38 

39def selectAllSummerTerms(currentTerm, student): 

40 """ 

41 Select the summer terms during which a CCE Minor student could be enrolled. 

42 

43 The user record does not store an admission date, so the admission academic 

44 year is inferred from the student's current class level. The range never 

45 extends beyond the student's inferred final school year; students marked as 

46 ``Graduating`` are treated as fifth-year/fall-graduating students. 

47 """ 

48 classYears = { 

49 "Freshman": 1, 

50 "Sophomore": 2, 

51 "Junior": 3, 

52 "Senior": 4, 

53 "Graduating": 5, 

54 } 

55 

56 if student.hasGraduated or student.rawClassLevel not in classYears: 

57 return [] 

58 

59 classYear = classYears[student.rawClassLevel] 

60 # A Spring/Summer term belongs to the academic year that began the prior fall. 

61 academicYearStart = (currentTerm.year if currentTerm.description.startswith("Fall") 

62 else currentTerm.year - 1) 

63 inferredAdmissionYear = academicYearStart - (classYear - 1) 

64 

65 firstSummer = inferredAdmissionYear + 1 

66 lastSummer = inferredAdmissionYear + max(4, classYear) - 1 

67 if currentTerm.description.startswith("Summer"): 

68 lastSummer = max(lastSummer, currentTerm.year) 

69 

70 return (Term.select() 

71 .where(Term.isSummer, 

72 Term.year >= firstSummer, 

73 Term.year <= lastSummer) 

74 .order_by(Term.termOrder)) 

75 

76def getStartofCurrentAcademicYear(currentTerm): 

77 if ("Summer" in currentTerm.description) or ("Spring" in currentTerm.description): 

78 fallTerm = Term.select().where(Term.year==currentTerm.year-1, Term.description == f"Fall {currentTerm.year-1}").get() 

79 return fallTerm 

80 return currentTerm 

81 

82def format24HourTime(unformattedTime): 

83 """ 

84 Turns a time string or datetime object into a string with a time in 24 hour format 

85 unformattedTime: expects a string with format HH:mm AM/PM or HH:mm OR a datetime object 

86 returns: a string in 24 hour format HH:mm 

87 """ 

88 if type(unformattedTime) == str: 

89 try: 

90 formattedTime = datetime.strptime(unformattedTime, "%I:%M %p").strftime("%H:%M") # Converts string to datetime then back to string and formats correctly 

91 return formattedTime 

92 except ValueError: 

93 # calling strptime here to explicitly raise an exception if it wasn't properly in 24 hour format 

94 formattedTime = datetime.strptime(unformattedTime, "%H:%M") 

95 return unformattedTime 

96 else: 

97 formattedTime = unformattedTime.strftime("%H:%M") 

98 return formattedTime 

99 

100def getUsernameFromEmail(email): 

101 return email.split("@")[0] 

102 

103def getFilesFromRequest(request): 

104 attachmentFiles = request.files.getlist("attachmentObject") 

105 fileDoesNotExist = attachmentFiles[0].content_type == "application/octet-stream" 

106 if fileDoesNotExist: 

107 attachmentFiles = None 

108 

109 return attachmentFiles 

110 

111def getRedirectTarget(popTarget=False): 

112 """ 

113 This function returns a string with the URL or route to a page in the Application 

114 saved with setRedirectTarget() and is able to pop the value from the session 

115 to make it an empty value 

116 popTarget: expects a bool value to determine whether or not to reset 

117 redirectTarget to an emtpy value 

118 return: a string with the URL or route to a page in the application that was 

119 saved in setRedirectTarget() 

120 """ 

121 if "redirectTarget" not in session: 

122 return '' 

123 

124 target = session["redirectTarget"] 

125 if popTarget: 

126 session.pop("redirectTarget") 

127 return target 

128 

129def setRedirectTarget(target): 

130 """ 

131 This function saves the target URL in the session for future redirection 

132 to said page 

133 target: expects a string that is a URL or a route to a page in the application 

134 return: None 

135 """ 

136 session["redirectTarget"] = target