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
« 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
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)
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 """
33 sustainedEngagementByTerm = getCommunityEngagementByTerm(username)
35 activeTab = request.args.get("tab", "sustainedCommunityEngagements")
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)
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)
53 # once we submit the form for creation
54 if request.method == "POST":
55 createOtherEngagement(username, request)
57 return redirect(url_for('minor.viewCceMinor', username=username))
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)
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)
77 editProposal = 'view' not in request.path
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)
84 attachmentObject = AttachmentUpload.get_or_none(proposal=proposalID)
85 attachmentFilePath = ""
86 attachmentFileName = ""
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
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 )
108 if "OtherEngagement" in request.path:
109 updateOtherEngagementRequest(proposalID, request)
110 else:
111 updateSummerExperience(proposalID, request.form)
113 return redirect(url_for('minor.viewCceMinor', username=proposal.student))
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)
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))
128 summerTerms = selectSurroundingTerms(g.current_term, summerOnly=True)
130 return render_template("minor/summerExperience.html",
131 selectableTerms = summerTerms,
132 contentAreas = [],
133 user = User.get_by_id(username),
134 )
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)
146 return information
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 ""
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 }
163 if action not in actionMap:
164 flash("Invalid action", "warning")
165 return ""
167 newStatus, message = actionMap[action]
169 if action == "withdraw":
170 removeProposal(proposalId)
171 else:
172 changeProposalStatus(proposalId, newStatus)
173 flash(message, "success")
175 except Exception as e:
176 print(e)
177 flash("Proposal status could not be changed", "warning")
179 return ""
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?
189 return minorSpreadsheet
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)
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."
205 return ""