- *To submit the form, you must either specify the number of extra break hours or fill in one of the fields in the Requested Positions section.
+ *After approving the request, you will not be able to approve it again over the next academic year ({{nextAY.termName.split(" ")[1]}}).
-
+
diff --git a/database/demo_data.py b/database/demo_data.py
index b7a59297..afcf9672 100644
--- a/database/demo_data.py
+++ b/database/demo_data.py
@@ -637,6 +637,16 @@
"adjustmentCutOff": f"2025-09-01",
"isBreak": 1,
},
+ {
+ "termCode": f"202600",
+ "termName": f"AY 2026-2027",
+ "termStart": f"2026-08-01",
+ "termEnd": f"2027-05-01",
+ "termState": 0,
+ "primaryCutOff": f"2026-09-01",
+ "adjustmentCutOff": f"2026-09-01",
+ "isBreak": 1,
+ },
]
Term.insert_many(terms).on_conflict_replace().execute()
@@ -1108,26 +1118,41 @@
allocations = [
{
"termCode": 202500,
- "department": 3,
- "isFinal": False,
+ "department": 1,
+ "isFinal": True,
"approvedOn": None,
"approvedBy": None,
- "justification": "Downscaling due to decrease in student enrollment caused by current economic conditions",
- "primary_10": 2,
- "primary_12": 2,
- "primary_15": 1,
- "primary_20": 0,
- "secondary_5": 1,
+ "justification": "We are hiring more students to help with the increased workload in the department",
+ "primary_10": 5,
+ "primary_12": 6,
+ "primary_15": 4,
+ "primary_20": 1,
+ "secondary_5": 7,
"secondary_10": 0,
- "breakHours": 260,
+ "breakHours": 550,
},
{
- "termCode": 202500,
+ "termCode": 202600,
+ "department": 1,
+ "isFinal": False,
+ "approvedOn": None,
+ "approvedBy": None,
+ "justification": "We need even more students to help with the increased workload in the department",
+ "primary_10": 8,
+ "primary_12": 12,
+ "primary_15": 5,
+ "primary_20": 2,
+ "secondary_5": 8,
+ "secondary_10": 1,
+ "breakHours": 560,
+ },
+ {
+ "termCode": 202500,
"department": 2,
"isFinal": True,
"approvedOn": None,
"approvedBy": None,
- "justification": "Increase in student enrollment due to exodous from CS department",
+ "justification": "Increase in student enrollment due to an exodus from the CS department",
"primary_10": 4,
"primary_12": 2,
"primary_15": 7,
@@ -1137,24 +1162,68 @@
"breakHours": 750,
},
{
+ "termCode": 202600,
+ "department": 2,
+ "isFinal": False,
+ "approvedOn": None,
+ "approvedBy": None,
+ "justification": "We need more students than last year",
+ "primary_10": 5,
+ "primary_12": 3,
+ "primary_15": 8,
+ "primary_20": 5,
+ "secondary_5": 3,
+ "secondary_10": 0,
+ "breakHours": 900,
+ },
+ {
"termCode": 202500,
- "department": 1,
+ "department": 3,
"isFinal": True,
"approvedOn": None,
"approvedBy": None,
- "justification": "We are hiring more students to help with the increased workload in the department",
+ "justification": "Downscaling due to decrease in student enrollment caused by current economic conditions",
+ "primary_10": 2,
+ "primary_12": 2,
+ "primary_15": 1,
+ "primary_20": 0,
+ "secondary_5": 1,
+ "secondary_10": 0,
+ "breakHours": 260,
+ },
+ {
+ "termCode": 202600,
+ "department": 3,
+ "isFinal": False,
+ "approvedOn": None,
+ "approvedBy": None,
+ "justification": "Having more students, as economic conditions seem to improve",
"primary_10": 5,
- "primary_12": 6,
- "primary_15": 4,
- "primary_20": 1,
- "secondary_5": 7,
+ "primary_12": 3,
+ "primary_15": 3,
+ "primary_20": 0,
+ "secondary_5": 2,
"secondary_10": 0,
- "breakHours": 550,
+ "breakHours": 360,
+ },
+ {
+ "termCode": 202600,
+ "department": 3,
+ "isFinal": True,
+ "approvedOn": None,
+ "approvedBy": None,
+ "primary_10": 2,
+ "primary_12": 2,
+ "primary_15": 1,
+ "primary_20": 0,
+ "secondary_5": 1,
+ "secondary_10": 0,
+ "breakHours": 260,
},
{
"termCode": 202500,
"department": 4,
- "isFinal": False,
+ "isFinal": True,
"approvedOn": None,
"approvedBy": None,
"justification": "Downscaling the number of students in the department due to budget cuts",
@@ -1167,6 +1236,21 @@
"breakHours": 300,
},
{
+ "termCode": 202600,
+ "department": 4,
+ "isFinal": False,
+ "approvedOn": None,
+ "approvedBy": None,
+ "justification": "Having more students! Yay!!!",
+ "primary_10": 11,
+ "primary_12": 7,
+ "primary_15": 0,
+ "primary_20": 0,
+ "secondary_5": 6,
+ "secondary_10": 0,
+ "breakHours": 301,
+ },
+ {
"termCode": 202500,
"department": 5,
"isFinal": True,
@@ -1181,7 +1265,21 @@
"secondary_10": 1,
"breakHours": 900,
},
-
+ {
+ "termCode": 202600,
+ "department": 5,
+ "isFinal": False,
+ "approvedOn": None,
+ "approvedBy": None,
+ "justification": "Due to rapid department growth, we need to hire even more students to help with the increased workload",
+ "primary_10": 9,
+ "primary_12": 11,
+ "primary_15": 9,
+ "primary_20": 12,
+ "secondary_5": 3,
+ "secondary_10": 9,
+ "breakHours": 1200,
+ },
]
Allocation.insert_many(allocations).on_conflict_replace().execute()
From fc218630c4d913fe88af8c43a33344c6c8b1e250 Mon Sep 17 00:00:00 2001
From: kurasova
Date: Mon, 3 Aug 2026 13:43:09 -0400
Subject: [PATCH 03/22] Add popovers for the Allocation Review page
---
app/static/css/allocationReview.css | 12 ++++++++----
app/static/js/allocationReview.js | 8 +++++++-
app/templates/admin/allocationReview.html | 12 ++++++------
3 files changed, 21 insertions(+), 11 deletions(-)
diff --git a/app/static/css/allocationReview.css b/app/static/css/allocationReview.css
index 184357c3..5de5ca06 100644
--- a/app/static/css/allocationReview.css
+++ b/app/static/css/allocationReview.css
@@ -1,10 +1,10 @@
-@media(min-width:970px) and (max-width:1240px) {
+@media(min-width:970px) and (max-width:1340px) {
.container {
width: 80%;
}
}
-@media(min-width:1240px) and (max-width:1800px) {
+@media(min-width:1340px) and (max-width:1800px) {
.container {
width: 55%;
}
@@ -48,8 +48,12 @@
margin-bottom: -10px;
}
-.noBorders {
- border: none !important;
+.currentAndAllocated {
+ transition-duration: 150ms;
+}
+
+.currentAndAllocated:hover {
+ color: grey;
}
diff --git a/app/static/js/allocationReview.js b/app/static/js/allocationReview.js
index 49c14f10..d661f5a3 100644
--- a/app/static/js/allocationReview.js
+++ b/app/static/js/allocationReview.js
@@ -1,6 +1,12 @@
$(document).ready( function(){
+
+ $('[data-toggle="popover"]').popover();
+
// not allowing users to type anything in a numeric spinner
- $("input[type='number'].numericSpinner").keypress(function (evt) {
+ $("input[type='number'].breakHoursNumericSpinner").keypress(function (evt) {
+ evt.preventDefault();
+ });
+ $("input[type='number'].positionNumericSpinner").keypress(function (evt) {
evt.preventDefault();
});
});
\ No newline at end of file
diff --git a/app/templates/admin/allocationReview.html b/app/templates/admin/allocationReview.html
index d5697569..a1bea09a 100644
--- a/app/templates/admin/allocationReview.html
+++ b/app/templates/admin/allocationReview.html
@@ -38,7 +38,7 @@
-
+
{% endblock %}
\ No newline at end of file
From 37e6fbb468883a5caebf2ce4643bcc8cfb5b8dbe Mon Sep 17 00:00:00 2001
From: kurasova
Date: Tue, 4 Aug 2026 10:07:16 -0400
Subject: [PATCH 06/22] THe approvedBy and approvedOn are now saved with the
submission of an allocation review + admins can no longer review allocations
that has not been requested
---
.../admin_routes/manageDepartments.py | 22 +++++++++++++++----
app/templates/admin/allocationReview.html | 2 +-
database/demo_data.py | 17 +-------------
3 files changed, 20 insertions(+), 21 deletions(-)
diff --git a/app/controllers/admin_routes/manageDepartments.py b/app/controllers/admin_routes/manageDepartments.py
index dcc05b79..94d4fb2c 100644
--- a/app/controllers/admin_routes/manageDepartments.py
+++ b/app/controllers/admin_routes/manageDepartments.py
@@ -124,10 +124,18 @@ def allocationReview(org=None, account=None):
currentAY, _, nextAY = generateAdjacentYears()
- requestedAlloc = Allocation.get(Allocation.termCode == nextAY.termCode, Allocation.department == dept, Allocation.isFinal == False)
+ # getting the current allocation
currentAlloc = Allocation.get(Allocation.termCode == currentAY.termCode, Allocation.department == dept, Allocation.isFinal == True)
+ # checking if the department has requested any allocation review
+ requestedAlloc = Allocation.get_or_none(Allocation.termCode == nextAY.termCode, Allocation.department == dept, Allocation.isFinal == False)
+ isRequested = bool(requestedAlloc)
+ if not isRequested:
+ flash(f"The {dept.DEPT_NAME} department has not requested an allocation review yet.", "danger")
+ return redirect('/admin/manageDepartments/')
+
+
# checking if the allocation has already been approved
isApproved = bool(Allocation.get_or_none(Allocation.termCode == nextAY.termCode, Allocation.department == dept, Allocation.isFinal == True))
if isApproved:
@@ -159,9 +167,17 @@ def approveAllocationReview():
)
+
+ # getting the name of the user who approves the request
+ supervisorID = require_login().supervisor
+
+
+ # saving the newly approved allocation
newApprovedAlloc = Allocation.create(termCode = nextAY.termCode,
department = request.form.get("requester", type=int, default=None),
isFinal = True,
+ approvedBy = supervisorID,
+ approvedOn = date.today(),
primary_10 = request.form.get("primary_10", type=int, default=currentAlloc.primary_10),
primary_12 = request.form.get("primary_12", type=int, default=currentAlloc.primary_12),
primary_15 = request.form.get("primary_15", type=int, default=currentAlloc.primary_15),
@@ -169,9 +185,7 @@ def approveAllocationReview():
secondary_5 = request.form.get("secondary_5", type=int, default=currentAlloc.secondary_5),
secondary_10 = request.form.get("secondary_10", type=int, default=currentAlloc.secondary_10),
breakHours = request.form.get("breakHours", type=int, default=currentAlloc.breakHours)
- )
-
-
+ )
newApprovedAlloc.save()
diff --git a/app/templates/admin/allocationReview.html b/app/templates/admin/allocationReview.html
index e2334cd7..70caa344 100644
--- a/app/templates/admin/allocationReview.html
+++ b/app/templates/admin/allocationReview.html
@@ -111,7 +111,7 @@
- *After approving the request, you will not be able to approve it again over the next academic year ({{nextAY.termName.split(" ")[1]}}).
+ Once this request is approved, the {{department.DEPT_NAME}} department can no longer submit any new allocation requests for the year of {{nextAY.termName.split(" ")[1]}}.
- Once this request is approved, the {{department.DEPT_NAME}} department can no longer submit any new allocation requests for the year of {{nextAY.termName.split(" ")[1]}}.
+ Once this request is approved, the {{department.DEPT_NAME}} department can no longer submit any new allocation requests for {{nextAY.termName.split(" ")[1]}}.
From f3833f74ed99960329d938c6bc83e03b4960679b Mon Sep 17 00:00:00 2001
From: kurasova
Date: Tue, 4 Aug 2026 16:49:00 -0400
Subject: [PATCH 09/22] Cleaned up the code
---
app/controllers/admin_routes/manageDepartments.py | 10 ++++++----
app/static/css/allocationRequest.css | 5 ++++-
app/static/css/allocationReview.css | 10 +---------
app/templates/admin/allocationReview.html | 4 ++--
app/templates/main/allocationRequest.html | 6 +++---
5 files changed, 16 insertions(+), 19 deletions(-)
diff --git a/app/controllers/admin_routes/manageDepartments.py b/app/controllers/admin_routes/manageDepartments.py
index e910f0f7..c7563d44 100644
--- a/app/controllers/admin_routes/manageDepartments.py
+++ b/app/controllers/admin_routes/manageDepartments.py
@@ -201,7 +201,7 @@ def approveAllocationReview():
@admin.route('/department///allocations/request', methods=['GET'])
-def managePositions(org, account):
+def allocationRequest(org, account):
try:
dept = Department.get(Department.ORG == org, Department.ACCOUNT == account)
except DoesNotExist:
@@ -222,7 +222,7 @@ def managePositions(org, account):
# checking if the allocation has already been approved
isApproved = bool(Allocation.get_or_none(Allocation.termCode == nextAY.termCode, Allocation.department == dept, Allocation.isFinal == True))
- if isApproved:
+ if isApproved: # if the approved allocation exists (in other words, if it is not None)
flash(f"The allocation for the {nextAY.termName} academic year has already been approved; therefore, you can no longer resubmit it.", "danger")
return redirect('/admin/manageDepartments/')
@@ -256,6 +256,8 @@ def submitAllocationRequest():
# getting the name of the user who approves the request
supervisorID = require_login().supervisor
+
+ # the list of the fields updated after submitting the allocation request
updatedFields = {
"termCode": nextAY.termCode,
"department": request.form.get("submitter", type=int, default=None),
@@ -277,9 +279,9 @@ def submitAllocationRequest():
isFinal=False,
defaults={**updatedFields})
- if not wasCreated:
+ if not wasCreated: # if the allocation has already existed (it is being resubmitted/updated)
for key, value in updatedFields.items():
- setattr(requestedAlloc, key, value)
+ setattr(requestedAlloc, key, value) # updating all the fields based on updatedFields values
requestedAlloc.save()
diff --git a/app/static/css/allocationRequest.css b/app/static/css/allocationRequest.css
index c34f1f89..622dc831 100644
--- a/app/static/css/allocationRequest.css
+++ b/app/static/css/allocationRequest.css
@@ -52,11 +52,14 @@
margin-top: 30px;
}
-.unresizeable {
+#justificationTextField {
resize: none;
+ width: 100%;
+ margin-bottom: 40px;
}
#allocationReviewNote {
max-width:70%;
+ margin-top: -5px;
color: grey;
}
\ No newline at end of file
diff --git a/app/static/css/allocationReview.css b/app/static/css/allocationReview.css
index 5de5ca06..5c366124 100644
--- a/app/static/css/allocationReview.css
+++ b/app/static/css/allocationReview.css
@@ -56,20 +56,12 @@
color: grey;
}
-
-/* #allocationJustification {
- margin-top: 30px;
-}
-
-.unresizeable {
- resize: none;
-} */
-
#allocationReviewFooter{
margin-top: 30px;
}
#allocationReviewNote {
max-width:70%;
+ margin-top: -5px;
color: grey;
}
\ No newline at end of file
diff --git a/app/templates/admin/allocationReview.html b/app/templates/admin/allocationReview.html
index b1556117..e1a70b13 100644
--- a/app/templates/admin/allocationReview.html
+++ b/app/templates/admin/allocationReview.html
@@ -101,8 +101,8 @@
-
-
+
+
Once this request is approved, the {{department.DEPT_NAME}} department can no longer submit any new allocation requests for {{nextAY.termName.split(" ")[1]}}.
+
This allocation request for {{nextAY.termName.split(" ")[1]}} can be updated by resubmission. However, once the Labor Office approves it, you can no longer change it.
From 73b3ac492b7aed967a6adfcb2114802abf827d3e Mon Sep 17 00:00:00 2001
From: kurasova
Date: Wed, 5 Aug 2026 13:55:21 -0400
Subject: [PATCH 10/22] Reformatted the code for the Allocation Review and
Allocation Request pages
---
.../admin_routes/manageDepartments.py | 155 ++++--------------
.../main_routes/departmentPortal.py | 55 ++++++-
app/logic/allocationManager.py | 14 +-
app/logic/allocationRequest.py | 45 +++++
4 files changed, 144 insertions(+), 125 deletions(-)
create mode 100644 app/logic/allocationRequest.py
diff --git a/app/controllers/admin_routes/manageDepartments.py b/app/controllers/admin_routes/manageDepartments.py
index c7563d44..40b5c314 100644
--- a/app/controllers/admin_routes/manageDepartments.py
+++ b/app/controllers/admin_routes/manageDepartments.py
@@ -16,6 +16,11 @@
from app.models.laborStatusForm import *
from app.logic.manageDepartments import *
+from app.logic.allocationManager import approvedAllocationExists, requestedAllocationExists
+
+
+
+### MANAGE DEPARTMENTS PAGE ###################################################################################
@@ -93,6 +98,10 @@ def complianceStatusCheck():
+### ALLOCATION REVIEW PAGE ####################################################################################
+
+
+
@admin.route('/admin/manageDepartments///allocationReview', methods=['GET'])
def allocationReview(org=None, account=None):
"""
@@ -101,7 +110,7 @@ def allocationReview(org=None, account=None):
"""
- # Retrieving the departments based on the org and account numbers
+ # getting the name of the currently chosen department (based on the org and account numbers)
try:
dept = Department.get(Department.ORG == org, Department.ACCOUNT == account)
except (NameError, DoesNotExist):
@@ -120,34 +129,32 @@ def allocationReview(org=None, account=None):
# Retrieving the next year
- # DON'T DELETE THE UNDERSCORES
+ # DON'T DELETE THE UNDERSCORE
currentAY, _, nextAY = generateAdjacentYears()
-
- # getting the current allocation
- currentAlloc = Allocation.get(Allocation.termCode == currentAY.termCode, Allocation.department == dept, Allocation.isFinal == True)
+ # checking if the allocation has already been approved
+ if approvedAllocationExists(nextAY.termCode, dept):
+ flash("You cannot reapprove an allocation request.", "danger")
+ return redirect('/admin/manageDepartments/')
+
# checking if the department has requested any allocation review
- requestedAlloc = Allocation.get_or_none(Allocation.termCode == nextAY.termCode, Allocation.department == dept, Allocation.isFinal == False)
- isRequested = bool(requestedAlloc)
- if not isRequested:
+ if not requestedAllocationExists(nextAY.termCode, dept):
flash(f"The {dept.DEPT_NAME} department has not requested an allocation review yet.", "danger")
return redirect('/admin/manageDepartments/')
- # checking if the allocation has already been approved
- isApproved = bool(Allocation.get_or_none(Allocation.termCode == nextAY.termCode, Allocation.department == dept, Allocation.isFinal == True))
- if isApproved:
- flash("You cannot reapprove an allocation request.", "danger")
- return redirect('/admin/manageDepartments/')
+ # getting the current and the requested allocations
+ currentAlloc = Allocation.get(Allocation.termCode == currentAY.termCode, Allocation.department == dept, Allocation.isFinal == True)
+ requestedAlloc = Allocation.get(Allocation.termCode == nextAY.termCode, Allocation.department == dept, Allocation.isFinal == False)
return render_template('admin/allocationReview.html',
department = dept,
nextAY = nextAY,
- requestedAlloc = requestedAlloc,
- currentAlloc = currentAlloc
+ currentAlloc = currentAlloc,
+ requestedAlloc = requestedAlloc
)
@@ -156,26 +163,26 @@ def allocationReview(org=None, account=None):
def approveAllocationReview():
# Retrieving the next year
- # DON'T DELETE THE UNDERSCORES
+ # DON'T DELETE THE UNDERSCORE
currentAY, _, nextAY = generateAdjacentYears()
+ # getting the name of the user who approves the request
+ approverID = require_login().supervisor
- currentAlloc = Allocation.get(
- Allocation.termCode == currentAY.termCode,
- Allocation.department == request.form.get("requester", type=int, default=None),
- Allocation.isFinal == True
- )
+ # getting the name of the requesting department
+ requester = request.form.get("requester", type=int, default=None)
- # getting the name of the user who approves the request
- supervisorID = require_login().supervisor
+
+ # getting the current allocation (for default values)
+ currentAlloc = Allocation.get(Allocation.termCode == currentAY.termCode, Allocation.department == requester, Allocation.isFinal == True)
# saving the newly approved allocation
newApprovedAlloc = Allocation.create(termCode = nextAY.termCode,
- department = request.form.get("requester", type=int, default=None),
+ department = requester,
isFinal = True,
- approvedBy = supervisorID,
+ approvedBy = approverID,
approvedOn = date.today(),
primary_10 = request.form.get("primary_10", type=int, default=currentAlloc.primary_10),
primary_12 = request.form.get("primary_12", type=int, default=currentAlloc.primary_12),
@@ -189,101 +196,3 @@ def approveAllocationReview():
return redirect("/admin/manageDepartments")
-
-
-
-
-
-
-#######################################################################################################################
-### ALL THE CODE BELOW SHOULD BE MOVED TO departmentPortal.py EVENTUALLY #############################################
-#####################################################################################################################
-
-
-@admin.route('/department///allocations/request', methods=['GET'])
-def allocationRequest(org, account):
- try:
- dept = Department.get(Department.ORG == org, Department.ACCOUNT == account)
- except DoesNotExist:
- return render_template('errors/404.html'), 404
-
- if not g.currentUser.isLaborAdmin:
- if not SupervisorDepartment.select().where(
- (SupervisorDepartment.supervisor == g.currentUser.supervisor) &
- (SupervisorDepartment.department == dept.departmentID)
- ).exists():
- return render_template('errors/403.html'), 403
-
-
- # Retrieving the next year
- # DON'T DELETE THE UNDERSCORES
- currentAY, _, nextAY = generateAdjacentYears()
-
-
- # checking if the allocation has already been approved
- isApproved = bool(Allocation.get_or_none(Allocation.termCode == nextAY.termCode, Allocation.department == dept, Allocation.isFinal == True))
- if isApproved: # if the approved allocation exists (in other words, if it is not None)
- flash(f"The allocation for the {nextAY.termName} academic year has already been approved; therefore, you can no longer resubmit it.", "danger")
- return redirect('/admin/manageDepartments/')
-
-
- # getting the current allocation
- currentAlloc = Allocation.get(Allocation.termCode == currentAY.termCode, Allocation.department == dept, Allocation.isFinal == True)
-
-
- return render_template('main/allocationRequest.html',
- department = dept,
- nextAY = nextAY,
- currentAlloc = currentAlloc
- )
-
-
-@admin.route('/allocationRequest/submit', methods=['POST'])
-def submitAllocationRequest():
-
- # Retrieving the next year
- # DON'T DELETE THE UNDERSCORES
- currentAY, _, nextAY = generateAdjacentYears()
-
-
- currentAlloc = Allocation.get(
- Allocation.termCode == currentAY.termCode,
- Allocation.department == request.form.get("submitter", type=int, default=None),
- Allocation.isFinal == True
- )
-
-
- # getting the name of the user who approves the request
- supervisorID = require_login().supervisor
-
-
- # the list of the fields updated after submitting the allocation request
- updatedFields = {
- "termCode": nextAY.termCode,
- "department": request.form.get("submitter", type=int, default=None),
- "isFinal": False,
- "justification": request.form.get("justification", default=""),
- "primary_10": request.form.get("primary_10", type=int, default=currentAlloc.primary_10),
- "primary_12": request.form.get("primary_12", type=int, default=currentAlloc.primary_12),
- "primary_15": request.form.get("primary_15", type=int, default=currentAlloc.primary_15),
- "primary_20": request.form.get("primary_20", type=int, default=currentAlloc.primary_20),
- "secondary_5": request.form.get("secondary_5", type=int, default=currentAlloc.secondary_5),
- "secondary_10": request.form.get("secondary_10", type=int, default=currentAlloc.secondary_10),
- "breakHours": request.form.get("breakHours", type=int, default=currentAlloc.breakHours)
- }
-
-
- # saving the newly approved allocation
- requestedAlloc, wasCreated = Allocation.get_or_create(termCode=nextAY.termCode,
- department=request.form.get("submitter", type=int, default=None),
- isFinal=False,
- defaults={**updatedFields})
-
- if not wasCreated: # if the allocation has already existed (it is being resubmitted/updated)
- for key, value in updatedFields.items():
- setattr(requestedAlloc, key, value) # updating all the fields based on updatedFields values
-
- requestedAlloc.save()
-
-
- return redirect("/admin/manageDepartments")
\ No newline at end of file
diff --git a/app/controllers/main_routes/departmentPortal.py b/app/controllers/main_routes/departmentPortal.py
index b0c33f5e..1618e7ff 100644
--- a/app/controllers/main_routes/departmentPortal.py
+++ b/app/controllers/main_routes/departmentPortal.py
@@ -1,9 +1,62 @@
-from flask import render_template, g
+from flask import render_template, g, request, redirect
+from app.login_manager import require_login
from app.controllers.main_routes import main_bp
from app.logic.getPositions import getPositions
from peewee import DoesNotExist
from app.models.department import Department
+from app.models.allocation import Allocation
from app.models.supervisorDepartment import SupervisorDepartment
+from app.logic.allocationRequest import belongsToDepartment, getOrUpdateRequestedAllocation
+from app.logic.allocationManager import approvedAllocationExists
+
+
+@main_bp.route('/department///allocations/request', methods=['GET'])
+def allocationRequest(org, account):
+
+ # an import statement (it was put here to avoid circular imports)
+ from app.logic.manageDepartments import generateAdjacentYears
+
+
+ # getting the name of the currently chosen department (based on the org and account numbers)
+ try:
+ dept = Department.get(Department.ORG == org, Department.ACCOUNT == account)
+ except DoesNotExist:
+ return render_template('errors/404.html'), 404
+
+
+ # cheching if the user can visit this page
+ if not g.currentUser.isLaborAdmin:
+ if not belongsToDepartment(dept):
+ return render_template('errors/403.html'), 403
+
+
+ # Retrieving the next year
+ # DON'T DELETE THE UNDERSCORES
+ currentAY, _, nextAY = generateAdjacentYears()
+
+
+ # checking if the allocation has already been approved (in other words, if an approved allocation exists)
+ if approvedAllocationExists(nextAY.termCode, dept):
+ flash(f"The allocation for the {nextAY.termName.split(" ")[1]} academic year has already been approved; therefore, you can no longer resubmit it.", "danger")
+ return redirect('/admin/manageDepartments/')
+
+
+ # getting the current approved allocation
+ currentAlloc = Allocation.get(Allocation.termCode == currentAY.termCode, Allocation.department == dept, Allocation.isFinal == True)
+
+
+ return render_template('main/allocationRequest.html',
+ department = dept,
+ nextAY = nextAY,
+ currentAlloc = currentAlloc
+ )
+
+
+@main_bp.route('/allocationRequest/submit', methods=['POST'])
+def submitAllocationRequest():
+ getOrUpdateRequestedAllocation()
+ return redirect("/admin/manageDepartments")
+
@main_bp.route('/department///positions', methods=['GET'])
def managePositions(org, account):
diff --git a/app/logic/allocationManager.py b/app/logic/allocationManager.py
index f1c58cbc..4890aec2 100644
--- a/app/logic/allocationManager.py
+++ b/app/logic/allocationManager.py
@@ -105,4 +105,16 @@ def getContractedAllocations(termCode: int, dept: int):
usedPositions["used_primaries"] = sum(list(usedPositions.values())[:4])
usedPositions["used_secondaries"] = sum(list(usedPositions.values())[4:6])
usedPositions["used_total"] = sum(list(usedPositions.values())[:6])
- return usedPositions
\ No newline at end of file
+ return usedPositions
+
+def approvedAllocationExists(termCode: int, dept: int):
+ """
+ Checks if there is an approved allocation.
+ """
+ return bool(Allocation.get_or_none(Allocation.termCode == termCode, Allocation.department == dept, Allocation.isFinal == True))
+
+def requestedAllocationExists(termCode: int, dept: int):
+ """
+ Checks if there is a requested allocation.
+ """
+ return bool(Allocation.get_or_none(Allocation.termCode == termCode, Allocation.department == dept, Allocation.isFinal == False))
\ No newline at end of file
diff --git a/app/logic/allocationRequest.py b/app/logic/allocationRequest.py
new file mode 100644
index 00000000..5fb08153
--- /dev/null
+++ b/app/logic/allocationRequest.py
@@ -0,0 +1,45 @@
+from flask import request, g
+from app.models.allocation import Allocation
+from app.logic.allocationManager import *
+
+def belongsToDepartment(department):
+ """
+ Checks whether the current user is a supervisor who belongs to a certain department.
+ """
+ return SupervisorDepartment.select().where((SupervisorDepartment.supervisor == g.currentUser.supervisor) & (SupervisorDepartment.department == department.departmentID)).exists()
+
+
+def getOrUpdateRequestedAllocation():
+ """
+ Gets or updates the requested allocation (used for the Allocation Request page specificially).
+ """
+ currentAY = (g.openTerm.termCode // 100) * 100 # current academic year
+ nextAY = currentAY + 100 # upcoming (next) academic year
+
+ requester = request.form.get("submitter", type=int, default=None) # the requesting department
+
+ currentAlloc = getAllocation(g.openTerm.termCode, requester, True) # the current allocation
+
+ # the list of the fields updated after submitting the allocation request
+ updatedFields = {
+ "termCode": nextAY,
+ "department": request.form.get("submitter", type=int, default=None),
+ "isFinal": False,
+ "justification": request.form.get("justification", default=""),
+ "primary_10": request.form.get("primary_10", type=int, default=currentAlloc["primary_10"]),
+ "primary_12": request.form.get("primary_12", type=int, default=currentAlloc["primary_12"]),
+ "primary_15": request.form.get("primary_15", type=int, default=currentAlloc["primary_15"]),
+ "primary_20": request.form.get("primary_20", type=int, default=currentAlloc["primary_20"]),
+ "secondary_5": request.form.get("secondary_5", type=int, default=currentAlloc["secondary_5"]),
+ "secondary_10": request.form.get("secondary_10", type=int, default=currentAlloc["secondary_10"]),
+ "breakHours": request.form.get("breakHours", type=int, default=currentAlloc["breakHours"])
+ }
+
+ # saving the newly approved allocation
+ requestedAlloc, wasCreated = Allocation.get_or_create(termCode=nextAY, department=requester, isFinal=False, defaults={**updatedFields})
+
+ if not wasCreated: # if the allocation has already existed (it is being resubmitted/updated)
+ for key, value in updatedFields.items():
+ setattr(requestedAlloc, key, value) # updating all the fields based on updatedFields values
+
+ requestedAlloc.save()
\ No newline at end of file
From 5585ef0629b3cc2f6e194d10f39362cf41a4c910 Mon Sep 17 00:00:00 2001
From: kurasova
Date: Wed, 5 Aug 2026 14:19:56 -0400
Subject: [PATCH 11/22] Updated some of the logic
---
.../admin_routes/manageDepartments.py | 6 +++---
.../main_routes/departmentPortal.py | 13 ++++++++-----
app/logic/allocationManager.py | 18 ++++++------------
app/logic/allocationRequest.py | 6 ------
4 files changed, 17 insertions(+), 26 deletions(-)
diff --git a/app/controllers/admin_routes/manageDepartments.py b/app/controllers/admin_routes/manageDepartments.py
index 40b5c314..bb9edf9a 100644
--- a/app/controllers/admin_routes/manageDepartments.py
+++ b/app/controllers/admin_routes/manageDepartments.py
@@ -16,7 +16,7 @@
from app.models.laborStatusForm import *
from app.logic.manageDepartments import *
-from app.logic.allocationManager import approvedAllocationExists, requestedAllocationExists
+from app.logic.allocationManager import allocationExists
@@ -134,13 +134,13 @@ def allocationReview(org=None, account=None):
# checking if the allocation has already been approved
- if approvedAllocationExists(nextAY.termCode, dept):
+ if allocationExists(nextAY.termCode, dept, isFinal=True):
flash("You cannot reapprove an allocation request.", "danger")
return redirect('/admin/manageDepartments/')
# checking if the department has requested any allocation review
- if not requestedAllocationExists(nextAY.termCode, dept):
+ if not allocationExists(nextAY.termCode, dept, isFinal=False):
flash(f"The {dept.DEPT_NAME} department has not requested an allocation review yet.", "danger")
return redirect('/admin/manageDepartments/')
diff --git a/app/controllers/main_routes/departmentPortal.py b/app/controllers/main_routes/departmentPortal.py
index 1618e7ff..405a68eb 100644
--- a/app/controllers/main_routes/departmentPortal.py
+++ b/app/controllers/main_routes/departmentPortal.py
@@ -1,4 +1,4 @@
-from flask import render_template, g, request, redirect
+from flask import render_template, g, request, redirect, flash
from app.login_manager import require_login
from app.controllers.main_routes import main_bp
from app.logic.getPositions import getPositions
@@ -6,8 +6,8 @@
from app.models.department import Department
from app.models.allocation import Allocation
from app.models.supervisorDepartment import SupervisorDepartment
-from app.logic.allocationRequest import belongsToDepartment, getOrUpdateRequestedAllocation
-from app.logic.allocationManager import approvedAllocationExists
+from app.logic.allocationRequest import getOrUpdateRequestedAllocation
+from app.logic.allocationManager import allocationExists
@main_bp.route('/department///allocations/request', methods=['GET'])
@@ -26,7 +26,10 @@ def allocationRequest(org, account):
# cheching if the user can visit this page
if not g.currentUser.isLaborAdmin:
- if not belongsToDepartment(dept):
+ if not SupervisorDepartment.select().where(
+ (SupervisorDepartment.supervisor == g.currentUser.supervisor) &
+ (SupervisorDepartment.department == dept.departmentID)
+ ).exists():
return render_template('errors/403.html'), 403
@@ -36,7 +39,7 @@ def allocationRequest(org, account):
# checking if the allocation has already been approved (in other words, if an approved allocation exists)
- if approvedAllocationExists(nextAY.termCode, dept):
+ if allocationExists(nextAY.termCode, dept, isFinal=True):
flash(f"The allocation for the {nextAY.termName.split(" ")[1]} academic year has already been approved; therefore, you can no longer resubmit it.", "danger")
return redirect('/admin/manageDepartments/')
diff --git a/app/logic/allocationManager.py b/app/logic/allocationManager.py
index 4890aec2..16d70753 100644
--- a/app/logic/allocationManager.py
+++ b/app/logic/allocationManager.py
@@ -9,15 +9,15 @@
def getAllocation(termCode: int, dept: int, isFinal = True):
'''
- This function returns a peewee object containing the selected allocation for given
+ This function returns a dictionary containing the selected allocation for given
department and term. If you want the pending allocation, pass in False for isFinal.
'''
academicYearCode = int(str(termCode)[:4] + "00")
- allocationObject = Allocation.select().where(
+ allocationDict = Allocation.select().where(
Allocation.termCode.in_([termCode,academicYearCode]),
Allocation.department == dept,
Allocation.isFinal == isFinal).dicts().get()
- return allocationObject
+ return allocationDict
def getTotalAllocations(termCode: int, dept: int):
@@ -107,14 +107,8 @@ def getContractedAllocations(termCode: int, dept: int):
usedPositions["used_total"] = sum(list(usedPositions.values())[:6])
return usedPositions
-def approvedAllocationExists(termCode: int, dept: int):
- """
- Checks if there is an approved allocation.
- """
- return bool(Allocation.get_or_none(Allocation.termCode == termCode, Allocation.department == dept, Allocation.isFinal == True))
-
-def requestedAllocationExists(termCode: int, dept: int):
+def allocationExists(termCode: int, dept: int, isFinal: bool):
"""
- Checks if there is a requested allocation.
+ Checks if there is an allocation that matches certain criteria.
"""
- return bool(Allocation.get_or_none(Allocation.termCode == termCode, Allocation.department == dept, Allocation.isFinal == False))
\ No newline at end of file
+ return bool(Allocation.get_or_none(Allocation.termCode == termCode, Allocation.department == dept, Allocation.isFinal == isFinal))
\ No newline at end of file
diff --git a/app/logic/allocationRequest.py b/app/logic/allocationRequest.py
index 5fb08153..ad15001c 100644
--- a/app/logic/allocationRequest.py
+++ b/app/logic/allocationRequest.py
@@ -2,12 +2,6 @@
from app.models.allocation import Allocation
from app.logic.allocationManager import *
-def belongsToDepartment(department):
- """
- Checks whether the current user is a supervisor who belongs to a certain department.
- """
- return SupervisorDepartment.select().where((SupervisorDepartment.supervisor == g.currentUser.supervisor) & (SupervisorDepartment.department == department.departmentID)).exists()
-
def getOrUpdateRequestedAllocation():
"""
From e4c78956dcaf7128a3e3ffdf83875910df2457d8 Mon Sep 17 00:00:00 2001
From: kurasova
Date: Wed, 5 Aug 2026 16:03:56 -0400
Subject: [PATCH 12/22] Added tests for the newly created logic functions
---
tests/code/test_allocationManger.py | 45 ++++++++++++-
tests/code/test_allocationRequest.py | 96 ++++++++++++++++++++++++++++
2 files changed, 140 insertions(+), 1 deletion(-)
create mode 100644 tests/code/test_allocationRequest.py
diff --git a/tests/code/test_allocationManger.py b/tests/code/test_allocationManger.py
index ea7da678..6d15b58e 100644
--- a/tests/code/test_allocationManger.py
+++ b/tests/code/test_allocationManger.py
@@ -204,4 +204,47 @@ def test_getContractedAllocations(testLaborStatusForm, testTerm, testDepartment,
assert contractedAllocation['used_secondaries'] == 0
assert contractedAllocation['used_total'] == 1
- assert contractedAllocation['break_hours'] == 500
\ No newline at end of file
+ assert contractedAllocation['break_hours'] == 500
+
+@pytest.mark.integration
+def test_allocationExists(testTerm, testDepartment, testAllocation, testPendingAllocation):
+
+ assert allocationExists(testTerm.termCode, testDepartment.departmentID, isFinal=False) == True
+ assert allocationExists(testTerm.termCode, testDepartment.departmentID, isFinal=True) == True
+
+ assert allocationExists(testTerm.termCode + 100, testDepartment.departmentID, isFinal=False) == False
+ assert allocationExists(testTerm.termCode + 100, testDepartment.departmentID, isFinal=True) == False
+
+ assert allocationExists(testTerm.termCode, 456, isFinal=False) == False
+ assert allocationExists(testTerm.termCode, 456, isFinal=True) == False
+
+ testAllocation.delete_instance()
+
+ assert allocationExists(testTerm.termCode, testDepartment.departmentID, isFinal=False) == True
+ assert allocationExists(testTerm.termCode, testDepartment.departmentID, isFinal=True) == False
+
+ testPendingAllocation.delete_instance()
+
+ assert allocationExists(testTerm.termCode, testDepartment.departmentID, isFinal=False) == False
+ assert allocationExists(testTerm.termCode, testDepartment.departmentID, isFinal=True) == False
+
+ with mainDB.atomic() as transaction:
+ allocation = Allocation.create(
+ termCode = testTerm.termCode,
+ department = testDepartment.departmentID,
+ isFinal = True,
+ approvedOn = None,
+ approvedBy = None,
+ justification = "brovich",
+ primary_10 = 22,
+ primary_12 = 6,
+ primary_15 = 7,
+ primary_20 = 12,
+ secondary_5 = 45,
+ secondary_10 = 22,
+ breakHours = 894)
+
+ assert allocationExists(testTerm.termCode, testDepartment.departmentID, isFinal=False) == False
+ assert allocationExists(testTerm.termCode, testDepartment.departmentID, isFinal=True) == True
+
+ transaction.rollback()
diff --git a/tests/code/test_allocationRequest.py b/tests/code/test_allocationRequest.py
new file mode 100644
index 00000000..0323ddd7
--- /dev/null
+++ b/tests/code/test_allocationRequest.py
@@ -0,0 +1,96 @@
+import pytest
+
+from flask import request, g
+from werkzeug.datastructures import ImmutableMultiDict
+
+from app import app
+from app.models.allocation import Allocation
+from app.models import mainDB
+from app.models.term import Term
+
+from app.logic.allocationRequest import *
+
+
+@pytest.fixture
+def client():
+ app.config['TESTING'] = True
+ with app.test_client() as client:
+ yield client
+
+
+@pytest.mark.integration
+def test_getOrUpdateRequestedAllocation(client):
+ with app.test_request_context('/allocationRequest/submit', method='POST', data={
+ 'submitter': "2",
+ 'breakHours': "750",
+ 'primary_10': "4",
+ 'primary_12': "13",
+ 'primary_15': "7",
+ 'primary_20': "5",
+ 'secondary_5': "2",
+ 'secondary_10': "0",
+ 'breakHours': "100",
+ 'justification': ""
+ }):
+ with mainDB.atomic() as transaction:
+ g.openTerm, _ = Term.get_or_create(
+ termCode=200200,
+ defaults={"termName": "AY 2002-2003", "isAcademicYear": True}
+ )
+
+ currentAlloc = Allocation.create(
+ termCode=200200,
+ department=2,
+ isFinal=True,
+ justification="",
+ primary_10=12,
+ primary_12=3,
+ primary_15=4,
+ primary_20=5,
+ secondary_5=1,
+ secondary_10=3,
+ breakHours=399
+ )
+
+ nextYear = Term.create(termCode=200300)
+
+ getOrUpdateRequestedAllocation()
+
+ allocation = Allocation.get(Allocation.termCode == g.openTerm.termCode + 100, Allocation.department == request.form.get("submitter", type=int, default=None))
+
+ assert isinstance(allocation.termCode, Term)
+ assert isinstance(allocation.termCode.termCode, int)
+ assert allocation.termCode.termCode == 200300
+
+ assert isinstance(allocation.department, Department)
+ assert isinstance(allocation.department.departmentID, int)
+ assert allocation.department.departmentID == 2
+
+ assert isinstance(allocation.isFinal, bool)
+ assert allocation.isFinal == False
+
+ assert isinstance(allocation.justification, str)
+ assert allocation.justification == ""
+
+ assert isinstance(allocation.primary_10, int)
+ assert allocation.primary_10 == 4
+
+ assert isinstance(allocation.primary_12, int)
+ assert allocation.primary_12 == 13
+
+ assert isinstance(allocation.primary_15, int)
+ assert allocation.primary_15 == 7
+
+ assert isinstance(allocation.primary_20, int)
+ assert allocation.primary_20 == 5
+
+ assert isinstance(allocation.secondary_5, int)
+ assert allocation.secondary_5 == 2
+
+ assert isinstance(allocation.secondary_10, int)
+ assert allocation.secondary_10 == 0
+
+ assert isinstance(allocation.breakHours, int)
+ assert allocation.breakHours == 100
+
+ transaction.rollback()
\ No newline at end of file
From 582a393253f211293d556c396cc31024df5b0d0b Mon Sep 17 00:00:00 2001
From: kurasova
Date: Wed, 5 Aug 2026 16:12:30 -0400
Subject: [PATCH 13/22] Made the getOrUpdateRequestedAllocation function not
depend on the current allocation
---
app/logic/allocationRequest.py | 16 +++++++---------
tests/code/test_allocationRequest.py | 14 --------------
2 files changed, 7 insertions(+), 23 deletions(-)
diff --git a/app/logic/allocationRequest.py b/app/logic/allocationRequest.py
index ad15001c..20feb025 100644
--- a/app/logic/allocationRequest.py
+++ b/app/logic/allocationRequest.py
@@ -12,21 +12,19 @@ def getOrUpdateRequestedAllocation():
requester = request.form.get("submitter", type=int, default=None) # the requesting department
- currentAlloc = getAllocation(g.openTerm.termCode, requester, True) # the current allocation
-
# the list of the fields updated after submitting the allocation request
updatedFields = {
"termCode": nextAY,
"department": request.form.get("submitter", type=int, default=None),
"isFinal": False,
"justification": request.form.get("justification", default=""),
- "primary_10": request.form.get("primary_10", type=int, default=currentAlloc["primary_10"]),
- "primary_12": request.form.get("primary_12", type=int, default=currentAlloc["primary_12"]),
- "primary_15": request.form.get("primary_15", type=int, default=currentAlloc["primary_15"]),
- "primary_20": request.form.get("primary_20", type=int, default=currentAlloc["primary_20"]),
- "secondary_5": request.form.get("secondary_5", type=int, default=currentAlloc["secondary_5"]),
- "secondary_10": request.form.get("secondary_10", type=int, default=currentAlloc["secondary_10"]),
- "breakHours": request.form.get("breakHours", type=int, default=currentAlloc["breakHours"])
+ "primary_10": request.form.get("primary_10", type=int, default=None),
+ "primary_12": request.form.get("primary_12", type=int, default=None),
+ "primary_15": request.form.get("primary_15", type=int, default=None),
+ "primary_20": request.form.get("primary_20", type=int, default=None),
+ "secondary_5": request.form.get("secondary_5", type=int, default=None),
+ "secondary_10": request.form.get("secondary_10", type=int, default=None),
+ "breakHours": request.form.get("breakHours", type=int, default=None)
}
# saving the newly approved allocation
diff --git a/tests/code/test_allocationRequest.py b/tests/code/test_allocationRequest.py
index 0323ddd7..f06c4557 100644
--- a/tests/code/test_allocationRequest.py
+++ b/tests/code/test_allocationRequest.py
@@ -38,20 +38,6 @@ def test_getOrUpdateRequestedAllocation(client):
defaults={"termName": "AY 2002-2003", "isAcademicYear": True}
)
- currentAlloc = Allocation.create(
- termCode=200200,
- department=2,
- isFinal=True,
- justification="",
- primary_10=12,
- primary_12=3,
- primary_15=4,
- primary_20=5,
- secondary_5=1,
- secondary_10=3,
- breakHours=399
- )
-
nextYear = Term.create(termCode=200300)
getOrUpdateRequestedAllocation()
From d8a66359d4aff3422e5ad7c5ea4f0232e6825934 Mon Sep 17 00:00:00 2001
From: kurasova
Date: Wed, 5 Aug 2026 16:47:44 -0400
Subject: [PATCH 14/22] Added a new getCurrentAndNextYear() function + tests
for it
---
.../admin_routes/manageDepartments.py | 15 ++----
.../main_routes/departmentPortal.py | 9 ++--
app/logic/academicYearManager.py | 26 ++++++++++
app/logic/allocationRequest.py | 4 +-
tests/code/test_academicYearManager.py | 50 +++++++++++++++++++
5 files changed, 86 insertions(+), 18 deletions(-)
create mode 100644 app/logic/academicYearManager.py
create mode 100644 tests/code/test_academicYearManager.py
diff --git a/app/controllers/admin_routes/manageDepartments.py b/app/controllers/admin_routes/manageDepartments.py
index bb9edf9a..ef10f6da 100644
--- a/app/controllers/admin_routes/manageDepartments.py
+++ b/app/controllers/admin_routes/manageDepartments.py
@@ -17,6 +17,7 @@
from app.logic.manageDepartments import *
from app.logic.allocationManager import allocationExists
+from app.logic.academicYearManager import getCurrentAndNextAY
@@ -128,9 +129,8 @@ def allocationReview(org=None, account=None):
return render_template('errors/403.html'), 403
- # Retrieving the next year
- # DON'T DELETE THE UNDERSCORE
- currentAY, _, nextAY = generateAdjacentYears()
+ # Retrieving the current and following academic years
+ currentAY, nextAY = getCurrentAndNextAY()
# checking if the allocation has already been approved
@@ -162,22 +162,18 @@ def allocationReview(org=None, account=None):
@admin.route('/admin/allocationReview/approve', methods=['POST'])
def approveAllocationReview():
- # Retrieving the next year
- # DON'T DELETE THE UNDERSCORE
- currentAY, _, nextAY = generateAdjacentYears()
+ # Retrieving the current and following academic years
+ currentAY, nextAY = getCurrentAndNextAY()
# getting the name of the user who approves the request
approverID = require_login().supervisor
-
# getting the name of the requesting department
requester = request.form.get("requester", type=int, default=None)
-
# getting the current allocation (for default values)
currentAlloc = Allocation.get(Allocation.termCode == currentAY.termCode, Allocation.department == requester, Allocation.isFinal == True)
-
# saving the newly approved allocation
newApprovedAlloc = Allocation.create(termCode = nextAY.termCode,
department = requester,
@@ -194,5 +190,4 @@ def approveAllocationReview():
)
newApprovedAlloc.save()
-
return redirect("/admin/manageDepartments")
diff --git a/app/controllers/main_routes/departmentPortal.py b/app/controllers/main_routes/departmentPortal.py
index 405a68eb..6146c674 100644
--- a/app/controllers/main_routes/departmentPortal.py
+++ b/app/controllers/main_routes/departmentPortal.py
@@ -8,15 +8,12 @@
from app.models.supervisorDepartment import SupervisorDepartment
from app.logic.allocationRequest import getOrUpdateRequestedAllocation
from app.logic.allocationManager import allocationExists
+from app.logic.academicYearManager import getCurrentAndNextAY
@main_bp.route('/department///allocations/request', methods=['GET'])
def allocationRequest(org, account):
- # an import statement (it was put here to avoid circular imports)
- from app.logic.manageDepartments import generateAdjacentYears
-
-
# getting the name of the currently chosen department (based on the org and account numbers)
try:
dept = Department.get(Department.ORG == org, Department.ACCOUNT == account)
@@ -33,9 +30,9 @@ def allocationRequest(org, account):
return render_template('errors/403.html'), 403
- # Retrieving the next year
+ # Retrieving the current and following academic years
# DON'T DELETE THE UNDERSCORES
- currentAY, _, nextAY = generateAdjacentYears()
+ currentAY, nextAY = getCurrentAndNextAY()
# checking if the allocation has already been approved (in other words, if an approved allocation exists)
diff --git a/app/logic/academicYearManager.py b/app/logic/academicYearManager.py
new file mode 100644
index 00000000..8953f487
--- /dev/null
+++ b/app/logic/academicYearManager.py
@@ -0,0 +1,26 @@
+from flask import g
+from app.models.term import *
+
+def getCurrentAndNextAY():
+ """
+ Returns two Term peewee objects: one is the current academic year,
+ and the other is the next academic year.
+ """
+
+ currentYear = g.openTerm.termCode // 100
+ nextYear = currentYear + 1
+
+ currentAYCode = currentYear * 100
+ nextAYCode = nextYear * 100
+
+ currentAY, _ = Term.get_or_create(
+ termCode=currentAYCode,
+ defaults={"termName": "AY {}-{}".format(currentYear, currentYear + 1), "isAcademicYear": True}
+ )
+
+ nextAY, _ = Term.get_or_create(
+ termCode=nextAYCode,
+ defaults={"termName": "AY {}-{}".format(nextYear, nextYear + 1), "isAcademicYear": True}
+ )
+
+ return (currentAY, nextAY)
\ No newline at end of file
diff --git a/app/logic/allocationRequest.py b/app/logic/allocationRequest.py
index 20feb025..6315a627 100644
--- a/app/logic/allocationRequest.py
+++ b/app/logic/allocationRequest.py
@@ -1,14 +1,14 @@
from flask import request, g
from app.models.allocation import Allocation
from app.logic.allocationManager import *
+from app.logic.academicYearManager import getCurrentAndNextAY
def getOrUpdateRequestedAllocation():
"""
Gets or updates the requested allocation (used for the Allocation Request page specificially).
"""
- currentAY = (g.openTerm.termCode // 100) * 100 # current academic year
- nextAY = currentAY + 100 # upcoming (next) academic year
+ currentAY, nextAY = getCurrentAndNextAY()
requester = request.form.get("submitter", type=int, default=None) # the requesting department
diff --git a/tests/code/test_academicYearManager.py b/tests/code/test_academicYearManager.py
new file mode 100644
index 00000000..3cd08744
--- /dev/null
+++ b/tests/code/test_academicYearManager.py
@@ -0,0 +1,50 @@
+import pytest
+
+from flask import g
+from app.models.term import *
+
+from app.logic.academicYearManager import *
+
+@pytest.mark.integration
+def test_getCurrentAndNextAY():
+ with app.app_context():
+ g.openTerm, _ = Term.get_or_create(
+ termCode = 196700,
+ defaults={"termName": "AY 1967-1968", "isAcademicYear": True}
+ )
+
+ currentYear, nextYear = getCurrentAndNextAY()
+
+ assert currentYear.termCode == 196700
+ assert currentYear.termName == "AY 1967-1968"
+
+ assert nextYear.termCode == 196800
+ assert nextYear.termName == "AY 1968-1969"
+
+
+ g.openTerm, _ = Term.get_or_create(
+ termCode = 210212,
+ defaults={"termName": "AY 2102-2103", "isAcademicYear": True}
+ )
+
+ currentYear, nextYear = getCurrentAndNextAY()
+
+ assert currentYear.termCode == 210200
+ assert currentYear.termName == "AY 2102-2103"
+
+ assert nextYear.termCode == 210300
+ assert nextYear.termName == "AY 2103-2104"
+
+ # Testing data types
+ assert isinstance(currentYear.termCode, int)
+ assert isinstance(nextYear.termCode, int)
+
+ assert isinstance(currentYear.termName, str)
+ assert isinstance(nextYear.termName, str)
+
+ # Testing whether termName is formatted correctly
+ assert currentYear.termName.split(" ")[0] == "AY"
+ assert nextYear.termName.split(" ")[0] == "AY"
+
+ assert currentYear.termName.split(" ")[1] == "2102-2103"
+ assert nextYear.termName.split(" ")[1] == "2103-2104"
\ No newline at end of file
From 731ab491370d7cccbfbf38077cef2fd74a19a1c0 Mon Sep 17 00:00:00 2001
From: kurasova
Date: Thu, 6 Aug 2026 11:26:44 -0400
Subject: [PATCH 15/22] Updated the demo data and the getCurrentAndNextAY
function to use g.currentYear instead of g.openTerm
---
app/__init__.py | 47 ++++++++++++++-----
.../main_routes/departmentPortal.py | 3 +-
app/logic/academicYearManager.py | 5 +-
database/demo_data.py | 30 ++++++++----
tests/code/test_academicYearManager.py | 12 +----
5 files changed, 61 insertions(+), 36 deletions(-)
diff --git a/app/__init__.py b/app/__init__.py
index 11c90a0a..a131ae4f 100755
--- a/app/__init__.py
+++ b/app/__init__.py
@@ -1,9 +1,9 @@
import os
-
-from flask import Flask
-from flask_restful import Api
+from datetime import date
+from flask import Flask, g, request, session
from flask_bootstrap import Bootstrap
-from playhouse.shortcuts import model_to_dict, dict_to_model
+from flask_restful import Api
+from playhouse.shortcuts import dict_to_model, model_to_dict
app = Flask(__name__)
@@ -55,17 +55,27 @@ def new_execute(*args, **kwargs):
from app.controllers.api_routes.routes import initializeApiRoutes
initializeApiRoutes(api)
-from flask import g
from app.models.user import User
-from app.login_manager import require_login
+from app.login_manager import getUsernameFromEnv, require_login
@app.before_request
def load_user():
- try:
- g.currentUser = dict_to_model(User, session['currentUser'])
+ requestUsername = getUsernameFromEnv(request.environ)
+ try:
+ cachedUser = session['currentUser']
+
+ if cachedUser.get('username') == requestUsername:
+ g.currentUser = dict_to_model(User, cachedUser)
+ return
+
+ session.pop('currentUser', None)
+ session.pop('username', None)
+
except Exception as e:
- user = require_login()
- session['currentUser'] = model_to_dict(user)
- g.currentUser = user
+ pass
+
+ user = require_login()
+ session['currentUser'] = model_to_dict(user)
+ g.currentUser = user
from app.models.term import Term
from app.login_manager import getOpenTerm
@@ -79,6 +89,20 @@ def load_openTerm():
session['openTerm'] = model_to_dict(term)
g.openTerm = term
+def getCurrentYear():
+ today = date.today()
+ year = today.year
+
+ if today.month < 7:
+ return year - 1, year
+
+ return year, year + 1
+
+@app.before_request
+def load_currentYear():
+ g.currentYear = getCurrentYear()
+
+
@app.context_processor
def inject_environment():
return dict(env=app.config['ENV'])
@@ -87,4 +111,3 @@ def inject_environment():
def queryCount():
if session:
session['querycount'] = 0
-
diff --git a/app/controllers/main_routes/departmentPortal.py b/app/controllers/main_routes/departmentPortal.py
index 6146c674..0088d1be 100644
--- a/app/controllers/main_routes/departmentPortal.py
+++ b/app/controllers/main_routes/departmentPortal.py
@@ -30,8 +30,7 @@ def allocationRequest(org, account):
return render_template('errors/403.html'), 403
- # Retrieving the current and following academic years
- # DON'T DELETE THE UNDERSCORES
+ # Retrieving the current and following academic years
currentAY, nextAY = getCurrentAndNextAY()
diff --git a/app/logic/academicYearManager.py b/app/logic/academicYearManager.py
index 8953f487..f66efd15 100644
--- a/app/logic/academicYearManager.py
+++ b/app/logic/academicYearManager.py
@@ -4,10 +4,11 @@
def getCurrentAndNextAY():
"""
Returns two Term peewee objects: one is the current academic year,
- and the other is the next academic year.
+ and the other is the next academic year (note that a new academic year
+ begins from the start of July).
"""
- currentYear = g.openTerm.termCode // 100
+ currentYear = g.currentYear[0]
nextYear = currentYear + 1
currentAYCode = currentYear * 100
diff --git a/database/demo_data.py b/database/demo_data.py
index 201f2ee4..979f49ce 100644
--- a/database/demo_data.py
+++ b/database/demo_data.py
@@ -647,6 +647,16 @@
"adjustmentCutOff": f"2026-09-01",
"isBreak": 1,
},
+ {
+ "termCode": f"202700",
+ "termName": f"AY 2027-2028",
+ "termStart": f"2027-08-01",
+ "termEnd": f"2028-05-01",
+ "termState": 0,
+ "primaryCutOff": f"2027-09-01",
+ "adjustmentCutOff": f"2027-09-01",
+ "isBreak": 1,
+ },
]
Term.insert_many(terms).on_conflict_replace().execute()
@@ -1117,7 +1127,7 @@
###########################
allocations = [
{
- "termCode": 202500,
+ "termCode": 202600,
"department": 1,
"isFinal": True,
"approvedOn": None,
@@ -1132,7 +1142,7 @@
"breakHours": 550,
},
{
- "termCode": 202600,
+ "termCode": 202700,
"department": 1,
"isFinal": False,
"approvedOn": None,
@@ -1147,7 +1157,7 @@
"breakHours": 560,
},
{
- "termCode": 202500,
+ "termCode": 202600,
"department": 2,
"isFinal": True,
"approvedOn": None,
@@ -1162,7 +1172,7 @@
"breakHours": 750,
},
{
- "termCode": 202600,
+ "termCode": 202700,
"department": 2,
"isFinal": False,
"approvedOn": None,
@@ -1177,7 +1187,7 @@
"breakHours": 900,
},
{
- "termCode": 202500,
+ "termCode": 202600,
"department": 3,
"isFinal": True,
"approvedOn": None,
@@ -1192,7 +1202,7 @@
"breakHours": 260,
},
{
- "termCode": 202600,
+ "termCode": 202700,
"department": 3,
"isFinal": False,
"approvedOn": None,
@@ -1207,7 +1217,7 @@
"breakHours": 360,
},
{
- "termCode": 202600,
+ "termCode": 202700,
"department": 3,
"isFinal": True,
"approvedOn": None,
@@ -1221,7 +1231,7 @@
"breakHours": 260,
},
{
- "termCode": 202500,
+ "termCode": 202600,
"department": 4,
"isFinal": True,
"approvedOn": None,
@@ -1236,7 +1246,7 @@
"breakHours": 300,
},
{
- "termCode": 202500,
+ "termCode": 202600,
"department": 5,
"isFinal": True,
"approvedOn": None,
@@ -1251,7 +1261,7 @@
"breakHours": 900,
},
{
- "termCode": 202600,
+ "termCode": 202700,
"department": 5,
"isFinal": False,
"approvedOn": None,
diff --git a/tests/code/test_academicYearManager.py b/tests/code/test_academicYearManager.py
index 3cd08744..830c4a4e 100644
--- a/tests/code/test_academicYearManager.py
+++ b/tests/code/test_academicYearManager.py
@@ -8,11 +8,7 @@
@pytest.mark.integration
def test_getCurrentAndNextAY():
with app.app_context():
- g.openTerm, _ = Term.get_or_create(
- termCode = 196700,
- defaults={"termName": "AY 1967-1968", "isAcademicYear": True}
- )
-
+ g.currentYear = (1967, 1968)
currentYear, nextYear = getCurrentAndNextAY()
assert currentYear.termCode == 196700
@@ -22,11 +18,7 @@ def test_getCurrentAndNextAY():
assert nextYear.termName == "AY 1968-1969"
- g.openTerm, _ = Term.get_or_create(
- termCode = 210212,
- defaults={"termName": "AY 2102-2103", "isAcademicYear": True}
- )
-
+ g.currentYear = (2102, 2103)
currentYear, nextYear = getCurrentAndNextAY()
assert currentYear.termCode == 210200
From 5a24ad29faf8abe85bd50b106a0fde74a163798f Mon Sep 17 00:00:00 2001
From: kurasova
Date: Thu, 6 Aug 2026 16:12:01 -0400
Subject: [PATCH 16/22] Fixed some backend issues + Allowed inactive
departments to submit allocation requests
---
.../admin_routes/manageDepartments.py | 27 ++++++------------
.../main_routes/departmentPortal.py | 4 +--
app/templates/admin/allocationReview.html | 14 +++++-----
app/templates/main/allocationRequest.html | 28 +++++++++----------
4 files changed, 31 insertions(+), 42 deletions(-)
diff --git a/app/controllers/admin_routes/manageDepartments.py b/app/controllers/admin_routes/manageDepartments.py
index ef10f6da..921c2855 100644
--- a/app/controllers/admin_routes/manageDepartments.py
+++ b/app/controllers/admin_routes/manageDepartments.py
@@ -21,10 +21,6 @@
-### MANAGE DEPARTMENTS PAGE ###################################################################################
-
-
-
@admin.route('/admin/manageDepartments/', methods=['GET'])
@admin.route('/admin/manageDepartments/', methods=['GET'])
def manageDepartments(academicYear = None):
@@ -99,10 +95,6 @@ def complianceStatusCheck():
-### ALLOCATION REVIEW PAGE ####################################################################################
-
-
-
@admin.route('/admin/manageDepartments///allocationReview', methods=['GET'])
def allocationReview(org=None, account=None):
"""
@@ -146,7 +138,7 @@ def allocationReview(org=None, account=None):
# getting the current and the requested allocations
- currentAlloc = Allocation.get(Allocation.termCode == currentAY.termCode, Allocation.department == dept, Allocation.isFinal == True)
+ currentAlloc = Allocation.get_or_none(Allocation.termCode == currentAY.termCode, Allocation.department == dept, Allocation.isFinal == True)
requestedAlloc = Allocation.get(Allocation.termCode == nextAY.termCode, Allocation.department == dept, Allocation.isFinal == False)
@@ -171,22 +163,19 @@ def approveAllocationReview():
# getting the name of the requesting department
requester = request.form.get("requester", type=int, default=None)
- # getting the current allocation (for default values)
- currentAlloc = Allocation.get(Allocation.termCode == currentAY.termCode, Allocation.department == requester, Allocation.isFinal == True)
-
# saving the newly approved allocation
newApprovedAlloc = Allocation.create(termCode = nextAY.termCode,
department = requester,
isFinal = True,
approvedBy = approverID,
approvedOn = date.today(),
- primary_10 = request.form.get("primary_10", type=int, default=currentAlloc.primary_10),
- primary_12 = request.form.get("primary_12", type=int, default=currentAlloc.primary_12),
- primary_15 = request.form.get("primary_15", type=int, default=currentAlloc.primary_15),
- primary_20 = request.form.get("primary_20", type=int, default=currentAlloc.primary_20),
- secondary_5 = request.form.get("secondary_5", type=int, default=currentAlloc.secondary_5),
- secondary_10 = request.form.get("secondary_10", type=int, default=currentAlloc.secondary_10),
- breakHours = request.form.get("breakHours", type=int, default=currentAlloc.breakHours)
+ primary_10 = request.form.get("primary_10", type=int, default=None),
+ primary_12 = request.form.get("primary_12", type=int, default=None),
+ primary_15 = request.form.get("primary_15", type=int, default=None),
+ primary_20 = request.form.get("primary_20", type=int, default=None),
+ secondary_5 = request.form.get("secondary_5", type=int, default=None),
+ secondary_10 = request.form.get("secondary_10", type=int, default=None),
+ breakHours = request.form.get("breakHours", type=int, default=None)
)
newApprovedAlloc.save()
diff --git a/app/controllers/main_routes/departmentPortal.py b/app/controllers/main_routes/departmentPortal.py
index 0088d1be..7d7547cf 100644
--- a/app/controllers/main_routes/departmentPortal.py
+++ b/app/controllers/main_routes/departmentPortal.py
@@ -36,12 +36,12 @@ def allocationRequest(org, account):
# checking if the allocation has already been approved (in other words, if an approved allocation exists)
if allocationExists(nextAY.termCode, dept, isFinal=True):
- flash(f"The allocation for the {nextAY.termName.split(" ")[1]} academic year has already been approved; therefore, you can no longer resubmit it.", "danger")
+ flash(f"The allocation for the {nextAY.termName.split(' ')[1]} academic year has already been approved; therefore, you can no longer resubmit it.", "danger")
return redirect('/admin/manageDepartments/')
# getting the current approved allocation
- currentAlloc = Allocation.get(Allocation.termCode == currentAY.termCode, Allocation.department == dept, Allocation.isFinal == True)
+ currentAlloc = Allocation.get_or_none(Allocation.termCode == currentAY.termCode, Allocation.department == dept, Allocation.isFinal == True)
return render_template('main/allocationRequest.html',
diff --git a/app/templates/admin/allocationReview.html b/app/templates/admin/allocationReview.html
index e1a70b13..a0805878 100644
--- a/app/templates/admin/allocationReview.html
+++ b/app/templates/admin/allocationReview.html
@@ -40,7 +40,7 @@
-
- Once this request is approved, the {{department.DEPT_NAME}} department can no longer submit any new allocation requests for {{nextAY.termName.split(" ")[1]}}.
-
-
-
-
-
-
-
+
+
+ Once this request is approved, the {{department.DEPT_NAME}} department can no longer submit any new allocation requests for {{nextAY.termName.split(" ")[1]}}.
+
+
Submit an allocation request to the Labor Department
@@ -42,7 +42,7 @@
-
+
@@ -101,20 +101,17 @@
-
-
-
-
- This allocation request for {{nextAY.termName.split(" ")[1]}} can be updated by resubmission. However, once the Labor Office approves it, you can no longer change it.
-
-
-
-
-
-
-
+
+
+ This allocation request for {{nextAY.termName.split(" ")[1]}} can be updated by resubmission. However, once the Labor Office approves it, you can no longer change it.
+
+
+
+
+
+
{% endblock %}
\ No newline at end of file
From dd23662398019e81cdde603e5462a9193c417209 Mon Sep 17 00:00:00 2001
From: kurasova
Date: Fri, 7 Aug 2026 09:57:45 -0400
Subject: [PATCH 18/22] Allowed labor office students to access the Allocation
Review page
---
app/controllers/admin_routes/manageDepartments.py | 6 +++---
app/models/allocation.py | 4 ++--
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/app/controllers/admin_routes/manageDepartments.py b/app/controllers/admin_routes/manageDepartments.py
index 921c2855..8d65cd3a 100644
--- a/app/controllers/admin_routes/manageDepartments.py
+++ b/app/controllers/admin_routes/manageDepartments.py
@@ -114,7 +114,7 @@ def allocationReview(org=None, account=None):
currentUser = require_login()
if not currentUser: # If the current user is not logged in
return render_template('errors/403.html')
- if not currentUser.isLaborAdmin:
+ if not (currentUser.isLaborAdmin or currentUser.isLaborDepartmentStudent()):
if currentUser.student:
return redirect('/laborHistory/' + currentUser.student.ID)
elif currentUser.supervisor:
@@ -157,8 +157,8 @@ def approveAllocationReview():
# Retrieving the current and following academic years
currentAY, nextAY = getCurrentAndNextAY()
- # getting the name of the user who approves the request
- approverID = require_login().supervisor
+ # getting the ID of the user who approves the request
+ approverID = require_login().userID
# getting the name of the requesting department
requester = request.form.get("requester", type=int, default=None)
diff --git a/app/models/allocation.py b/app/models/allocation.py
index 02c53aae..79d6426b 100644
--- a/app/models/allocation.py
+++ b/app/models/allocation.py
@@ -1,6 +1,6 @@
from app.models import *
from app.models.department import Department
-from app.models.supervisor import Supervisor
+from app.models.user import User
from app.models.term import Term
class Allocation(baseModel):
@@ -8,7 +8,7 @@ class Allocation(baseModel):
department = ForeignKeyField(Department)
isFinal = BooleanField(default=False)
approvedOn = DateField(null=True)
- approvedBy = ForeignKeyField(Supervisor, null=True)
+ approvedBy = ForeignKeyField(User, null=True)
justification = TextField(default="", null=False)
primary_10 = IntegerField()
primary_12 = IntegerField()
From 3727d18c78e787246b4383005c20ebaec67d9a32 Mon Sep 17 00:00:00 2001
From: kurasova
Date: Fri, 7 Aug 2026 09:58:50 -0400
Subject: [PATCH 19/22] Allowed labor office students to access the Allocation
Request page
---
app/controllers/main_routes/departmentPortal.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/controllers/main_routes/departmentPortal.py b/app/controllers/main_routes/departmentPortal.py
index 7d7547cf..ab801645 100644
--- a/app/controllers/main_routes/departmentPortal.py
+++ b/app/controllers/main_routes/departmentPortal.py
@@ -22,7 +22,7 @@ def allocationRequest(org, account):
# cheching if the user can visit this page
- if not g.currentUser.isLaborAdmin:
+ if not (currentUser.isLaborAdmin or currentUser.isLaborDepartmentStudent()):
if not SupervisorDepartment.select().where(
(SupervisorDepartment.supervisor == g.currentUser.supervisor) &
(SupervisorDepartment.department == dept.departmentID)
From 0e217b9e79b32f5b6135dba7d7a35f843018539e Mon Sep 17 00:00:00 2001
From: kurasova
Date: Fri, 7 Aug 2026 10:04:47 -0400
Subject: [PATCH 20/22] Allowed labor office students to access other pages for
admins
---
app/controllers/admin_routes/manageDepartments.py | 4 ++--
app/controllers/main_routes/departmentPortal.py | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/app/controllers/admin_routes/manageDepartments.py b/app/controllers/admin_routes/manageDepartments.py
index 8d65cd3a..613c5705 100644
--- a/app/controllers/admin_routes/manageDepartments.py
+++ b/app/controllers/admin_routes/manageDepartments.py
@@ -33,7 +33,7 @@ def manageDepartments(academicYear = None):
currentUser = require_login()
if not currentUser: # If the current user is not logged in
return render_template('errors/403.html')
- if not currentUser.isLaborAdmin:
+ if not (currentUser.isLaborAdmin or currentUser.isLaborDepartmentStudent):
if currentUser.student:
return redirect('/laborHistory/' + currentUser.student.ID)
elif currentUser.supervisor:
@@ -114,7 +114,7 @@ def allocationReview(org=None, account=None):
currentUser = require_login()
if not currentUser: # If the current user is not logged in
return render_template('errors/403.html')
- if not (currentUser.isLaborAdmin or currentUser.isLaborDepartmentStudent()):
+ if not (currentUser.isLaborAdmin or currentUser.isLaborDepartmentStudent):
if currentUser.student:
return redirect('/laborHistory/' + currentUser.student.ID)
elif currentUser.supervisor:
diff --git a/app/controllers/main_routes/departmentPortal.py b/app/controllers/main_routes/departmentPortal.py
index ab801645..19a114b7 100644
--- a/app/controllers/main_routes/departmentPortal.py
+++ b/app/controllers/main_routes/departmentPortal.py
@@ -22,7 +22,7 @@ def allocationRequest(org, account):
# cheching if the user can visit this page
- if not (currentUser.isLaborAdmin or currentUser.isLaborDepartmentStudent()):
+ if not (g.currentUser.isLaborAdmin or g.currentUser.isLaborDepartmentStudent):
if not SupervisorDepartment.select().where(
(SupervisorDepartment.supervisor == g.currentUser.supervisor) &
(SupervisorDepartment.department == dept.departmentID)
@@ -64,7 +64,7 @@ def managePositions(org, account):
except DoesNotExist:
return render_template('errors/404.html'), 404
- if not g.currentUser.isLaborAdmin:
+ if not (g.currentUser.isLaborAdmin or g.currentUser.isLaborDepartmentStudent):
if not SupervisorDepartment.select().where(
(SupervisorDepartment.supervisor == g.currentUser.supervisor) &
(SupervisorDepartment.department == dept.departmentID)
From 3e83e9d6b79810ca246867650d64b604d61928c4 Mon Sep 17 00:00:00 2001
From: kurasova
Date: Fri, 7 Aug 2026 11:08:43 -0400
Subject: [PATCH 21/22] Moved from the generateAdjacentYears() function to
getCurrentAndNextAY
---
.../admin_routes/manageDepartments.py | 14 +--
app/logic/manageDepartments.py | 80 +--------------
tests/code/test_manageDepartments.py | 98 +------------------
3 files changed, 5 insertions(+), 187 deletions(-)
diff --git a/app/controllers/admin_routes/manageDepartments.py b/app/controllers/admin_routes/manageDepartments.py
index bae2be8d..5c5cc3e3 100644
--- a/app/controllers/admin_routes/manageDepartments.py
+++ b/app/controllers/admin_routes/manageDepartments.py
@@ -22,7 +22,7 @@
@admin.route('/admin/manageDepartments/', methods=['GET'])
-def manageDepartments(academicYear = None):
+def manageDepartments():
"""
Returns the Manage Departments page, which allows the admin to view all the departments
and their allocations.
@@ -38,16 +38,8 @@ def manageDepartments(academicYear = None):
elif currentUser.supervisor:
return render_template('errors/403.html'), 403
-
- # The condition below may be deleted if the routing to the Manage Departments page is changed.
- if academicYear == None:
- academicYear = g.openTerm.termCode
- else:
- academicYear = int(academicYear)
-
-
- currentAY, nextAY = generateAdjacentYears(academicYear)
- chosenAY = Term.get(Term.termCode == academicYear)
+ currentAY, nextAY = getCurrentAndNextAY()
+ chosenAY = Term.get(Term.termCode == currentAY.termCode)
breakHoursByDepartment = {row["department"]: str(row["totalHours"] or 0) for row in getUsedBreakHours(chosenAY)}
diff --git a/app/logic/manageDepartments.py b/app/logic/manageDepartments.py
index 4f3fbb9d..9f823c78 100644
--- a/app/logic/manageDepartments.py
+++ b/app/logic/manageDepartments.py
@@ -14,45 +14,6 @@
-def generateAdjacentYears(academicYearTermCode=None):
- """
- Generates the current, the previous, and the following academic years.
- """
-
- currentYear = g.openTerm.termCode // 100
- nextYear = currentYear + 1
-
-
- currentAYCode = currentYear * 100
- nextAYCode = nextYear * 100
-
- # Admins cannot view allocations for the years that are beyond the current, the previous, or the following academic year
- if academicYearTermCode not in (None, currentAYCode, nextAYCode):
- abort(400)
-
-
- currentAY, _ = Term.get_or_create(
- termCode=currentAYCode,
- defaults={"termName": "AY {}-{}".format(currentYear, currentYear + 1), "isAcademicYear": True}
- )
-
- nextAY, _ = Term.get_or_create(
- termCode=nextAYCode,
- defaults={"termName": "AY {}-{}".format(nextYear, nextYear + 1), "isAcademicYear": True}
- )
-
- return (currentAY, nextAY)
-
-
-
-
-####################################################################################################################################
-# Everything below this line will eventually be deleted
-
-
-
-
-
def getUsedBreakHours(term):
"""
Returns the total number of break hours used by each department for a given term.
@@ -146,43 +107,4 @@ def getAllocationStatus(term, department):
(Allocation.termCode == term) &
(Allocation.department == department)
)
- return allocation.isFinal
-
-
-
-
-
-
-# THE FUNCTIONS BELOW ARE NO LONGER USED IN THE CODE (BECAUSE WE CAN ONLY CHOOSE AN ACADEMIC YEAR IN THE CODE).
-# IF SOMETHING CHANGES,YOU CAN USE THE CODE BELOW
-
-# # USED IN THE generateTermsForAdjacentYears() FUNCTION
-# def generateTerms(termCode):
-# """
-# Generates all the terms in an academic year.
-# """
-
-# # Truncating term codes to hundreds. That's how we get the academic year.
-# academicYearCode = (termCode // 100)
-
-# return createTerms(academicYearCode)
-
-
-
-# def generateTermsForAdjacentYears(academicYear):
-# """
-# Generates all the terms for the current, the previous, and the future academic years.
-# """
-
-# previousAYCode = g.openTerm.termCode - 100
-# currentAYCode = g.openTerm.termCode
-# nextATCode = g.openTerm.termCode + 100
-
-# if (academicYear != previousAYCode) and (academicYear != currentAYCode) and (academicYear != nextATCode):
-# abort(400)
-
-# PreviousAYTerms = generateTerms(previousAYCode)
-# CurrentAYTerms = generateTerms(currentAYCode)
-# NextAYTerms = generateTerms(nextATCode)
-
-# return (PreviousAYTerms, CurrentAYTerms, NextAYTerms)
\ No newline at end of file
+ return allocation.isFinal
\ No newline at end of file
diff --git a/tests/code/test_manageDepartments.py b/tests/code/test_manageDepartments.py
index 77cb2ee3..fc165a82 100644
--- a/tests/code/test_manageDepartments.py
+++ b/tests/code/test_manageDepartments.py
@@ -14,100 +14,4 @@
# The following test file is for testing the manageDepartments logic file and its associated functions and queries.
-# It is designed to ensure that the manageDepartments functionality works as expected and returns the correct data.
-
-
-@pytest.mark.integration
-def test_generateAdjacentYears():
- with app.app_context():
- with mainDB.atomic() as transaction:
-
- ################ THE FIRST TEST ################
- ################ TESTING WHETHER THE generateAdjacentYear() FUNCTION WORKS AT ALL
- g.openTerm, _ = Term.get_or_create(
- termCode = 202500,
- defaults={"termName": "AY 2025-2026", "isAcademicYear": True}
- )
-
- #
- currentYear, previousYear, followingYear = generateAdjacentYears(202500)
-
- assert currentYear.termCode == 202500
- assert currentYear.termName == "AY 2025-2026"
-
- assert previousYear.termCode == 202400
- assert previousYear.termName == "AY 2024-2025"
-
- assert followingYear.termCode == 202600
- assert followingYear.termName == "AY 2026-2027"
-
-
- ################ THE SECOND TEST ################
- ######### TESTING VARIOUS EDGE CASES ############
- with pytest.raises(BadRequest):
- generateAdjacentYears(202300)
- transaction.rollback()
-
- with pytest.raises(BadRequest):
- generateAdjacentYears(202200)
- transaction.rollback()
-
- with pytest.raises(BadRequest):
- generateAdjacentYears(2025)
- transaction.rollback()
-
- with pytest.raises(BadRequest):
- generateAdjacentYears(True)
- transaction.rollback()
-
- with pytest.raises(BadRequest):
- generateAdjacentYears(False)
- transaction.rollback()
-
- with pytest.raises(BadRequest):
- generateAdjacentYears("SELECT lsf DELETE *")
- transaction.rollback()
-
-
- ################ THE THIRD TEST ################
- ############# MISCELLANEOUS TESTS #############
- g.openTerm, _ = Term.get_or_create(
- termCode = 198200,
- defaults={"termName": "AY 1982-1983", "isAcademicYear": True}
- )
-
- # Testing different years
- currentYear, previousYear, followingYear = generateAdjacentYears(198200)
-
- assert currentYear.termCode == 198200
- assert currentYear.termName == "AY 1982-1983"
-
- assert previousYear.termCode == 198100
- assert previousYear.termName == "AY 1981-1982"
-
- assert followingYear.termCode == 198300
- assert followingYear.termName == "AY 1983-1984"
-
- # Testing data types
- assert isinstance(currentYear.termCode, int)
- assert isinstance(previousYear.termCode, int)
- assert isinstance(followingYear.termCode, int)
-
- # Testing whether currentYear.termName is formatted correctly
- assert currentYear.termName.split(" ")[0] == "AY"
- assert previousYear.termName.split(" ")[0] == "AY"
- assert followingYear.termName.split(" ")[0] == "AY"
-
- assert currentYear.termName.split(" ")[1] == "1982-1983"
- assert previousYear.termName.split(" ")[1] == "1981-1982"
- assert followingYear.termName.split(" ")[1] == "1983-1984"
-
-
- # Testing the generateAdjacentYears() function without any parameters
- currentYear, previousYear, followingYear = generateAdjacentYears()
-
- assert currentYear.termCode == 198200
- assert previousYear.termCode == 198100
- assert followingYear.termCode == 198300
-
- transaction.rollback()
\ No newline at end of file
+# It is designed to ensure that the manageDepartments functionality works as expected and returns the correct data.
\ No newline at end of file
From 14b9365173fb10c3b3b669e275f46a32accaf06d Mon Sep 17 00:00:00 2001
From: kurasova
Date: Fri, 7 Aug 2026 15:13:47 -0400
Subject: [PATCH 22/22] Reflected on the changes Imran had suggested
---
app/controllers/admin_routes/manageDepartments.py | 8 ++++----
app/controllers/main_routes/departmentPortal.py | 9 +++++----
app/logic/allocationRequest.py | 2 +-
app/templates/main/allocationRequest.html | 2 +-
4 files changed, 11 insertions(+), 10 deletions(-)
diff --git a/app/controllers/admin_routes/manageDepartments.py b/app/controllers/admin_routes/manageDepartments.py
index 5c5cc3e3..ec1aef0a 100644
--- a/app/controllers/admin_routes/manageDepartments.py
+++ b/app/controllers/admin_routes/manageDepartments.py
@@ -32,7 +32,7 @@ def manageDepartments():
currentUser = require_login()
if not currentUser: # If the current user is not logged in
return render_template('errors/403.html')
- if not (currentUser.isLaborAdmin or currentUser.isLaborDepartmentStudent):
+ if not currentUser.isLaborAdmin:
if currentUser.student:
return redirect('/laborHistory/' + currentUser.student.ID)
elif currentUser.supervisor:
@@ -104,7 +104,7 @@ def allocationReview(org=None, account=None):
currentUser = require_login()
if not currentUser: # If the current user is not logged in
return render_template('errors/403.html')
- if not (currentUser.isLaborAdmin or currentUser.isLaborDepartmentStudent):
+ if not currentUser.isLaborAdmin:
if currentUser.student:
return redirect('/laborHistory/' + currentUser.student.ID)
elif currentUser.supervisor:
@@ -117,13 +117,13 @@ def allocationReview(org=None, account=None):
# checking if the allocation has already been approved
if allocationExists(nextAY.termCode, dept, isFinal=True):
- flash("You cannot reapprove an allocation request.", "danger")
+ flash("You cannot reapprove an allocation request.", "info")
return redirect('/admin/manageDepartments/')
# checking if the department has requested any allocation review
if not allocationExists(nextAY.termCode, dept, isFinal=False):
- flash(f"The {dept.DEPT_NAME} department has not requested an allocation review yet.", "danger")
+ flash(f"The {dept.DEPT_NAME} department has not requested an allocation review yet.", "info")
return redirect('/admin/manageDepartments/')
diff --git a/app/controllers/main_routes/departmentPortal.py b/app/controllers/main_routes/departmentPortal.py
index 19a114b7..2673a874 100644
--- a/app/controllers/main_routes/departmentPortal.py
+++ b/app/controllers/main_routes/departmentPortal.py
@@ -22,7 +22,7 @@ def allocationRequest(org, account):
# cheching if the user can visit this page
- if not (g.currentUser.isLaborAdmin or g.currentUser.isLaborDepartmentStudent):
+ if not g.currentUser.isLaborAdmin:
if not SupervisorDepartment.select().where(
(SupervisorDepartment.supervisor == g.currentUser.supervisor) &
(SupervisorDepartment.department == dept.departmentID)
@@ -36,7 +36,7 @@ def allocationRequest(org, account):
# checking if the allocation has already been approved (in other words, if an approved allocation exists)
if allocationExists(nextAY.termCode, dept, isFinal=True):
- flash(f"The allocation for the {nextAY.termName.split(' ')[1]} academic year has already been approved; therefore, you can no longer resubmit it.", "danger")
+ flash(f"The allocation for the {nextAY.termName.split(' ')[1]} academic year has already been approved; therefore, you can no longer resubmit it.", "info")
return redirect('/admin/manageDepartments/')
@@ -54,7 +54,8 @@ def allocationRequest(org, account):
@main_bp.route('/allocationRequest/submit', methods=['POST'])
def submitAllocationRequest():
getOrUpdateRequestedAllocation()
- return redirect("/admin/manageDepartments")
+ submitter = Department.get(Department.departmentID == request.form.get("submitter", type=int, default=None))
+ return redirect(f"/department/{submitter.ORG}/{submitter.ACCOUNT}")
@main_bp.route('/department///positions', methods=['GET'])
@@ -64,7 +65,7 @@ def managePositions(org, account):
except DoesNotExist:
return render_template('errors/404.html'), 404
- if not (g.currentUser.isLaborAdmin or g.currentUser.isLaborDepartmentStudent):
+ if not g.currentUser.isLaborAdmin:
if not SupervisorDepartment.select().where(
(SupervisorDepartment.supervisor == g.currentUser.supervisor) &
(SupervisorDepartment.department == dept.departmentID)
diff --git a/app/logic/allocationRequest.py b/app/logic/allocationRequest.py
index 6315a627..141d2698 100644
--- a/app/logic/allocationRequest.py
+++ b/app/logic/allocationRequest.py
@@ -15,7 +15,7 @@ def getOrUpdateRequestedAllocation():
# the list of the fields updated after submitting the allocation request
updatedFields = {
"termCode": nextAY,
- "department": request.form.get("submitter", type=int, default=None),
+ "department": requester,
"isFinal": False,
"justification": request.form.get("justification", default=""),
"primary_10": request.form.get("primary_10", type=int, default=None),
diff --git a/app/templates/main/allocationRequest.html b/app/templates/main/allocationRequest.html
index 15ae67bc..48f63ae6 100644
--- a/app/templates/main/allocationRequest.html
+++ b/app/templates/main/allocationRequest.html
@@ -108,7 +108,7 @@