Coverage for app/controllers/minor/routes.py: 30%

98 statements  

« prev     ^ index     » next       coverage.py v7.10.2, created at 2025-12-22 18:34 +0000

1from flask import g, render_template, request, abort, flash, redirect, url_for 

2from peewee import DoesNotExist 

3 

4from app.controllers.minor import minor_bp 

5from app.models.user import User 

6from app.models.cceMinorProposal import CCEMinorProposal 

7from app.models.term import Term 

8from app.models.attachmentUpload import AttachmentUpload 

9from app.logic.fileHandler import FileHandler 

10from app.logic.utils import selectSurroundingTerms, getFilesFromRequest 

11from app.logic.minor import ( 

12 changeProposalStatus, 

13 createOtherEngagement, 

14 updateOtherEngagementRequest, 

15 setCommunityEngagementForUser, 

16 getEngagementTotal, 

17 createSummerExperience, 

18 updateSummerExperience, 

19 getProgramEngagementHistory, 

20 getCourseInformation, 

21 getCommunityEngagementByTerm, 

22 getMinorSpreadsheet, 

23 getCCEMinorProposals, 

24 removeProposal 

25) 

26 

27@minor_bp.route('/profile/<username>/cceMinor', methods=['GET']) 

28def viewCceMinor(username): 

29 """ 

30 Load minor management page with community engagements and summer experience 

31 """ 

32 

33 sustainedEngagementByTerm = getCommunityEngagementByTerm(username) 

34 

35 activeTab = request.args.get("tab", "sustainedCommunityEngagements") 

36 

37 return render_template("minor/profile.html", 

38 user = User.get_by_id(username), 

39 proposalList = getCCEMinorProposals(username), 

40 sustainedEngagementByTerm = sustainedEngagementByTerm, 

41 totalSustainedEngagements = getEngagementTotal(sustainedEngagementByTerm), 

42 activeTab=activeTab) 

43 

44@minor_bp.route('/cceMinor/<username>/otherEngagement', methods=['GET', 'POST']) 

45def createOtherEngagementRequest(username): 

46 """ 

47 Load minor management page with community engagements and summer experience 

48 """ 

49 if not (g.current_user.isAdmin or g.current_user.username == username): 

50 return abort(403) 

51 

52 

53 # once we submit the form for creation 

54 if request.method == "POST": 

55 createOtherEngagement(username, request) 

56 

57 return redirect(url_for('minor.viewCceMinor', username=username)) 

58 

59 return render_template("minor/requestOtherEngagement.html", 

60 editable = True, 

61 user = User.get_by_id(username), 

62 selectableTerms = selectSurroundingTerms(g.current_term), 

63 postRoute = f"/cceMinor/{username}/otherEngagement", # when form is submitted, what POST route is it being submitted to. 

64 attachmentFilePath = "", 

65 attachmentFileName = "", 

66 proposal = None) 

67 

68@minor_bp.route('/cceMinor/viewOtherEngagement/<proposalID>', methods=['GET']) 

69@minor_bp.route('/cceMinor/viewSummerExperience/<proposalID>', methods=['GET']) 

70@minor_bp.route('/cceMinor/editOtherEngagement/<proposalID>', methods=['GET', 'POST']) 

71@minor_bp.route('/cceMinor/editSummerExperience/<proposalID>', methods=['GET', 'POST']) 

72def editOrViewProposal(proposalID: int): 

73 proposal = CCEMinorProposal.get_by_id(int(proposalID)) 

74 if not (g.current_user.isAdmin or g.current_user.username == proposal.student.username): 

75 return abort(403) 

76 

77 editProposal = 'view' not in request.path 

78 

79 # if proposal is approved, only admins can edit, but not if the admin is the student 

80 if proposal.isApproved and editProposal: 

81 if g.current_user.username == proposal.student or not g.current_user.isAdmin: 

82 return abort(403) 

83 

84 attachmentObject = AttachmentUpload.get_or_none(proposal=proposalID) 

85 attachmentFilePath = "" 

86 attachmentFileName = "" 

87 

88 if attachmentObject: 

89 fileHandler = FileHandler(proposalId=proposalID) 

90 attachmentFilePath = fileHandler.getFileFullPath(attachmentObject.fileName).lstrip("app/") # we need to remove app/ from the url because it prevents it from displaying 

91 attachmentFileName = attachmentObject.fileName 

92 

93 if request.method == "GET": 

94 selectedTerm = Term.get_by_id(proposal.term) 

95 flash("Once approved, a proposal can only be edited by an admin.", 'warning') 

96 return render_template("minor/requestOtherEngagement.html" if 'OtherEngagement' in request.path else "minor/summerExperience.html", 

97 editable = editProposal, 

98 selectedTerm = selectedTerm, 

99 contentAreas = proposal.contentAreas.split(", ") if proposal.contentAreas else [], 

100 selectableTerms = selectSurroundingTerms(g.current_term, summerOnly=False if 'OtherEngagement' else True), 

101 user = User.get_by_id(proposal.student), 

102 postRoute = f"/cceMinor/editSummerExperience/{proposal.id}" if "SummerExperience" in request.path else f"/cceMinor/editOtherEngagement/{proposal.id}", 

103 proposal = proposal, 

104 attachmentFilePath = attachmentFilePath, 

105 attachmentFileName = attachmentFileName 

106 ) 

107 

108 if "OtherEngagement" in request.path: 

109 updateOtherEngagementRequest(proposalID, request) 

110 else: 

111 updateSummerExperience(proposalID, request.form) 

112 

113 return redirect(url_for('minor.viewCceMinor', username=proposal.student)) 

114 

115@minor_bp.route('/cceMinor/<username>/summerExperience', methods=['GET', 'POST']) 

116def createSummerExperienceRequest(username): 

117 """ 

118 Load minor management page with community engagements and summer experience 

119 """ 

120 if not (g.current_user.isAdmin or g.current_user.username == username): 

121 return abort(403) 

122 

123 # once we submit the form for creation 

124 if request.method == "POST": 

125 createSummerExperience(username, request.form) 

126 return redirect(url_for('minor.viewCceMinor', username=username)) 

127 

128 summerTerms = selectSurroundingTerms(g.current_term, summerOnly=True) 

129 

130 return render_template("minor/summerExperience.html", 

131 selectableTerms = summerTerms, 

132 contentAreas = [], 

133 user = User.get_by_id(username), 

134 ) 

135 

136@minor_bp.route('/cceMinor/<username>/getEngagementInformation/<type>/<term>/<id>', methods=['GET']) 

137def getEngagementInformation(username, type, id, term): 

138 """ 

139 For a particular engagement activity (program or course), get the participation history or course information respectively. 

140 """ 

141 if type == "program": 

142 information = getProgramEngagementHistory(id, username, term) 

143 else: 

144 information = getCourseInformation(id) 

145 

146 return information 

147 

148 

149@minor_bp.route('/cceMinor/<action>/<username>/<proposalId>', methods=['POST']) 

150def updateProposal(action, username, proposalId): 

151 try: 

152 if not (g.current_user.isAdmin or g.current_user.isFaculty or g.current_user.username == username): 

153 flash("Unauthorized to perform this action", "warning") 

154 return "" 

155 

156 actionMap = { 

157 "withdraw": ("Withdrawn", "Proposal successfully withdrawn"), 

158 "complete": ("Completed", "Proposal successfully completed"), 

159 "approve": ("Approved", "Proposal approved"), 

160 "unapprove": ("Submitted", "Proposal unapproved"), 

161 } 

162 

163 if action not in actionMap: 

164 flash("Invalid action", "warning") 

165 return "" 

166 

167 newStatus, message = actionMap[action] 

168 

169 if action == "withdraw": 

170 removeProposal(proposalId) 

171 else: 

172 changeProposalStatus(proposalId, newStatus) 

173 flash(message, "success") 

174 

175 except Exception as e: 

176 print(e) 

177 flash("Proposal status could not be changed", "warning") 

178 

179 return "" 

180 

181 

182@minor_bp.route('/cceMinor/getMinorSpreadsheet', methods=['GET']) 

183def returnMinorSpreadsheet(): 

184 """ 

185 Returns a spreadsheet containing users and related spreadsheet information. 

186 """ 

187 minorSpreadsheet = getMinorSpreadsheet() # can we get for any term or only curr term? 

188 

189 return minorSpreadsheet 

190 

191@minor_bp.route('/cceMinor/<username>/modifyCommunityEngagement', methods=['PUT','DELETE']) 

192def modifyCommunityEngagement(username): 

193 """ 

194 Saving a term participation/activities for sustained community engagement 

195 """ 

196 if not g.current_user.isCeltsAdmin: 

197 abort(403) 

198 

199 action = 'add' if request.method == 'PUT' else 'remove' 

200 try: 

201 setCommunityEngagementForUser(action, request.form, g.current_user) 

202 except DoesNotExist: 

203 return "There are already 4 Sustained Community Engagement records." 

204 

205 return "" 

206