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

101 statements  

« prev     ^ index     » next       coverage.py v7.10.2, created at 2026-03-08 07:10 +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 sustainedEngagementByTerm = getCommunityEngagementByTerm(username) 

33 

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

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

36 user = User.get_by_id(username), 

37 proposalList = getCCEMinorProposals(username), 

38 sustainedEngagementByTerm = sustainedEngagementByTerm, 

39 totalSustainedEngagements = getEngagementTotal(sustainedEngagementByTerm), 

40 activeTab=activeTab) 

41 

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

43def createOtherEngagementRequest(username): 

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

45 return abort(403) 

46 

47 # once we submit the form for creation 

48 if request.method == "POST": 

49 createOtherEngagement(username, request) 

50 flash("Proposal successfully created.", "success") 

51 return redirect(url_for('minor.viewCceMinor', username=username, tab="manageProposals")) 

52 

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

54 editable = True, 

55 user = User.get_by_id(username), 

56 selectableTerms = selectSurroundingTerms(g.current_term), 

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

58 attachmentFilePath = "", 

59 attachmentFileName = "", 

60 proposal = None) 

61 

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

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

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

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

66def editOrViewProposal(proposalID: int): 

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

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

69 return abort(403) 

70 

71 editProposal = 'view' not in request.path 

72 

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

74 if proposal.isApproved and editProposal: 

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

76 return abort(403) 

77 

78 attachmentObject = AttachmentUpload.get_or_none(proposal=proposalID) 

79 attachmentFilePath = "" 

80 attachmentFileName = "" 

81 

82 if attachmentObject: 

83 fileHandler = FileHandler(proposalId=proposalID) 

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

85 attachmentFileName = attachmentObject.fileName 

86 

87 if request.method == "GET": 

88 selectedTerm = Term.get_by_id(proposal.term) 

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

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

91 editable = editProposal, 

92 selectedTerm = selectedTerm, 

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

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

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

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

97 proposal = proposal, 

98 attachmentFilePath = attachmentFilePath, 

99 attachmentFileName = attachmentFileName 

100 ) 

101 

102 if "OtherEngagement" in request.path: 

103 updateOtherEngagementRequest(proposalID, request) 

104 else: 

105 updateSummerExperience(proposalID, request.form) 

106 

107 flash("Proposal updated", "success") 

108 return redirect(url_for('minor.viewCceMinor', username=proposal.student, tab='manageProposals')) 

109 

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

111def createSummerExperienceRequest(username): 

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

113 return abort(403) 

114 

115 # once we submit the form for creation 

116 if request.method == "POST": 

117 createSummerExperience(username, request.form) 

118 flash("Proposal successfully created.", "success") 

119 return redirect(url_for('minor.viewCceMinor', username=username, tab="manageProposals")) 

120 

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

122 

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

124 selectableTerms = summerTerms, 

125 contentAreas = [], 

126 user = User.get_by_id(username), 

127 ) 

128 

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

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

131 if type == "program": 

132 information = getProgramEngagementHistory(id, username, term) 

133 else: 

134 information = getCourseInformation(id) 

135 

136 return information 

137 

138 

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

140def updateProposal(action, username, proposalId): 

141 try: 

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

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

144 return "" 

145 

146 actionMap = { 

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

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

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

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

151 } 

152 

153 if action not in actionMap: 

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

155 return "" 

156 

157 newStatus, message = actionMap[action] 

158 

159 if action == "withdraw": 

160 removeProposal(proposalId) 

161 else: 

162 changeProposalStatus(proposalId, newStatus) 

163 flash(message, "success") 

164 

165 except Exception as e: 

166 print(e) 

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

168 

169 return "" 

170 

171 

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

173def returnMinorSpreadsheet(): 

174 """ 

175 Returns a spreadsheet containing users and related spreadsheet information. 

176 """ 

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

178 

179 return minorSpreadsheet 

180 

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

182def modifyCommunityEngagement(username): 

183 """ 

184 Saving a term participation/activities for sustained community engagement 

185 """ 

186 if not g.current_user.isCeltsAdmin: 

187 abort(403) 

188 

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

190 try: 

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

192 except DoesNotExist: 

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

194 

195 return "" 

196