diff --git a/app/controllers/admin_routes/manageDepartments.py b/app/controllers/admin_routes/manageDepartments.py index c202513a..ec9ce2f5 100644 --- a/app/controllers/admin_routes/manageDepartments.py +++ b/app/controllers/admin_routes/manageDepartments.py @@ -17,7 +17,7 @@ from app.logic.manageDepartments import * - +from playhouse.shortcuts import model_to_dict @admin.route('/admin/manageDepartments/', methods=['GET']) def manageDepartments(academicYear = None): @@ -25,7 +25,6 @@ def manageDepartments(academicYear = None): Returns the Manage Departments page, which allows the admin to view all the departments and their allocations. """ - # Checking Admin Rights currentUser = require_login() if not currentUser: # If the current user is not logged in @@ -36,42 +35,25 @@ 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) - - breakHoursByDepartment = {row["department"]: str(row["totalHours"] or 0) for row in getUsedBreakHours(chosenAY)} - - activeDepartments = getActiveDepartmentsWithAllocation(chosenAY) - inactiveDepartments = Department.select().where(Department.isActive == False) + currentAY, nextAY = generateAdjacentYears(g.openTerm.termCode) - allocationStatus = { - department.departmentID: getAllocationStatus(chosenAY, department) - for department in activeDepartments - } + inactiveDepartments = Department.select().where(Department.isActive == False) + allSupervisors = Supervisor.select().order_by(Supervisor.LAST_NAME) - allSupervisors= Supervisor.select().order_by(Supervisor.LAST_NAME) + breakHoursByDepartment = {row["department"]: str(row["totalHours"] or 0) for row in getUsedBreakHours(currentAY)} + + activeDepartmentsAllocations = getActiveDepartmentsAllocations(currentAY,nextAY) return render_template( 'admin/manageDepartments.html', - activeDepartments = activeDepartments, + activeDepartmentsAllocations = activeDepartmentsAllocations, inactiveDepartments = inactiveDepartments, - allSupervisors = allSupervisors, + allSupervisors = allSupervisors, currentAY = currentAY, nextAY = nextAY, - academicYear = chosenAY.termName, breakHoursByDepartment = breakHoursByDepartment, - allocationStatus = allocationStatus ) - @admin.route('/admin/complianceStatus', methods=['POST']) def complianceStatusCheck(): """ diff --git a/app/logic/manageDepartments.py b/app/logic/manageDepartments.py index 4f3fbb9d..6feeec2a 100644 --- a/app/logic/manageDepartments.py +++ b/app/logic/manageDepartments.py @@ -12,17 +12,17 @@ from app.login_manager import require_login +from playhouse.shortcuts import model_to_dict + def generateAdjacentYears(academicYearTermCode=None): """ - Generates the current, the previous, and the following academic years. + Generates the current, and the following academic years. """ - currentYear = g.openTerm.termCode // 100 nextYear = currentYear + 1 - currentAYCode = currentYear * 100 nextAYCode = nextYear * 100 @@ -50,9 +50,6 @@ def generateAdjacentYears(academicYearTermCode=None): # 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. @@ -109,7 +106,7 @@ def getLSFCountSecondaries(currentTerm, department): -def getActiveDepartmentsWithAllocation(term): +def getActiveDepartmentsWithAllocation(term,isFinal = True): """ Returns a list of active departments with allocations for the given term. """ @@ -118,24 +115,61 @@ def getActiveDepartmentsWithAllocation(term): # activeDepartments = Department.select().where(Department.isActive == True) # allAllocations = Allocation.select().where(Allocation.termCode == currentAY) - activeDepartments = (Department + activeDepartments = (Allocation .select(Department, Allocation) - .join(Allocation) + .join(Department) .where( Department.isActive == True, - Allocation.termCode == term.termCode - ) + Allocation.termCode == term.termCode, + Allocation.isFinal == isFinal, + ) ) - for dept in activeDepartments: - dept.totalPrimaries = (dept.allocation.primary_10 + dept.allocation.primary_12 + dept.allocation.primary_15 + dept.allocation.primary_20) - dept.totalSecondaries = (dept.allocation.secondary_5 + dept.allocation.secondary_10) + + for allocation in activeDepartments: + allocation.totalPrimaries = (allocation.primary_10 + allocation.primary_12 + allocation.primary_15 + allocation.primary_20) + allocation.totalSecondaries = (allocation.secondary_5 + allocation.secondary_10) + + if isFinal: # do not need to count them for requested allocations + allocation.lsfCountPrimaries = getLSFCountPrimaries(term, allocation.department) + allocation.lsfCountSecondaries = getLSFCountSecondaries(term, allocation.department) + + return activeDepartments - dept.lsfCountPrimaries = getLSFCountPrimaries(term, dept) - dept.lsfCountSecondaries = getLSFCountSecondaries(term, dept) - return activeDepartments +def getActiveDepartmentsAllocations(term,nextTerm): + """ + This function gets active departments, active allocations for the current AY and future AY. + It returns a dictionary which contains three objects grouped by the departmentID + """ + + activeDepartments = Department.select().where(Department.isActive == True) + + allocations = getActiveDepartmentsWithAllocation(term) + + allocByDeptId = {} + + # index allocations by department ID + for alloc in allocations: + allocByDeptId[alloc.department.departmentID] = alloc + requestedAllocations = getActiveDepartmentsWithAllocation(nextTerm, False) + + # index requested allocations by department ID + reqAllocByDeptId = {} + for alloc in requestedAllocations: + reqAllocByDeptId[alloc.department.departmentID] = alloc + + # build combined dictionary for active departments + activeDepartmentsAllocations = {} + + for dept in activeDepartments: + activeDepartmentsAllocations[dept.departmentID] = { + "department": dept, + "allocation": allocByDeptId.get(dept.departmentID), # None if no allocation + "requestedAllocation": reqAllocByDeptId.get(dept.departmentID), # None if no requested allocation + } + return activeDepartmentsAllocations def getAllocationStatus(term, department): diff --git a/app/templates/admin/manageDepartments.html b/app/templates/admin/manageDepartments.html index 88fcf297..abb4db22 100755 --- a/app/templates/admin/manageDepartments.html +++ b/app/templates/admin/manageDepartments.html @@ -91,69 +91,79 @@

Manage Departments

Department - Status - Current Allocations
({{ academicYear }}) + Current Allocations
({{ currentAY.termName }}) Requested Allocations
({{ nextAY.termName }}) Actions - {% for department in activeDepartments %} + {% for entry in activeDepartmentsAllocations.values() %} - {{department.DEPT_NAME}}
({{department.ORG}}, - {{department.ACCOUNT}}) + {{entry.department.DEPT_NAME}}
({{entry.department.ORG}}, + {{entry.department.ACCOUNT}}) - - + + {% if entry.allocation %} Primary: - {{department.lsfCountPrimaries}} of {{department.totalPrimaries}} + {{entry.allocation.lsfCountPrimaries}} of {{entry.allocation.totalPrimaries}}
+ Secondary: - {{department.lsfCountSecondaries}} of {{department.totalSecondaries}} + {{entry.allocation.lsfCountSecondaries}} of {{entry.allocation.totalSecondaries}}
- Break: {{ breakHoursByDepartment.get(department.departmentID, 0) }} of {{ - department.allocation.breakHours }} hours + Break: {{ breakHoursByDepartment.get(entry.department.departmentID, 0) }} of + {{entry.allocation.breakHours }} hours + {% else %} + No current allocation + {% endif %} + + {% if entry.allocation %} Primary: - {{department.lsfCountPrimaries}} of {{department.totalPrimaries}} + {{entry.requestedAllocation.totalPrimaries}}
Secondary: - {{department.lsfCountSecondaries}} of {{department.totalSecondaries}} + {{entry.requestedAllocation.totalSecondaries}}
- Break: {{ breakHoursByDepartment.get(department.departmentID, 0) }} of {{ department.allocation.breakHours }} hours + Break: {{ entry.requestedAllocation.breakHours }} hours + {% else %} + No current allocation + {% endif %} - - + - {% endfor %} + {%endfor%} diff --git a/database/demo_data.py b/database/demo_data.py index b7a59297..13128f7c 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": 0, + }, ] Term.insert_many(terms).on_conflict_replace().execute() @@ -1105,10 +1115,12 @@ ############################ # Allocation Dummy Data: ########################### + +# Active Allocations for 2025 allocations = [ { "termCode": 202500, - "department": 3, + "department": 1, "isFinal": False, "approvedOn": None, "approvedBy": None, @@ -1138,7 +1150,7 @@ }, { "termCode": 202500, - "department": 1, + "department": 3, "isFinal": True, "approvedOn": None, "approvedBy": None, @@ -1154,7 +1166,7 @@ { "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", @@ -1181,7 +1193,160 @@ "secondary_10": 1, "breakHours": 900, }, - + + # Active Allocations for 2026 + { + "termCode": 202600, + "department": 5, + "isFinal": True, + "approvedOn": None, + "approvedBy": None, + "justification": "Due to rapid department growth, we need to hire more students to help with the increased workload", + "primary_10": 8, + "primary_12": 10, + "primary_15": 7, + "primary_20": 4, + "secondary_5": 5, + "secondary_10": 1, + "breakHours": 900, + }, + { + "termCode": 202600, + "department": 4, + "isFinal": True, + "approvedOn": None, + "approvedBy": None, + "justification": "Due to rapid department growth, we need to hire more students to help with the increased workload", + "primary_10": 8, + "primary_12": 10, + "primary_15": 7, + "primary_20": 4, + "secondary_5": 5, + "secondary_10": 1, + "breakHours": 900, + }, + { + "termCode": 202600, + "department": 3, + "isFinal": True, + "approvedOn": None, + "approvedBy": None, + "justification": "Due to rapid department growth, we need to hire more students to help with the increased workload", + "primary_10": 8, + "primary_12": 10, + "primary_15": 7, + "primary_20": 4, + "secondary_5": 5, + "secondary_10": 1, + "breakHours": 900, + }, + { + "termCode": 202600, + "department": 2, + "isFinal": True, + "approvedOn": None, + "approvedBy": None, + "justification": "Due to rapid department growth, we need to hire more students to help with the increased workload", + "primary_10": 8, + "primary_12": 10, + "primary_15": 7, + "primary_20": 4, + "secondary_5": 5, + "secondary_10": 1, + "breakHours": 900, + }, + { + "termCode": 202600, + "department": 1, + "isFinal": True, + "approvedOn": None, + "approvedBy": None, + "justification": "Due to rapid department growth, we need to hire more students to help with the increased workload", + "primary_10": 8, + "primary_12": 10, + "primary_15": 7, + "primary_20": 4, + "secondary_5": 5, + "secondary_10": 1, + "breakHours": 900, + }, + + # Requested Allocations for 2026-2027 (isFinal = False) + { + "termCode": 202600, + "department": 5, + "isFinal": False, + "approvedOn": None, + "approvedBy": None, + "justification": "Due to rapid department growth, we need to hire more students to help with the increased workload", + "primary_10": 1, + "primary_12": 22, + "primary_15": 3, + "primary_20": 4, + "secondary_5": 1, + "secondary_10": 1, + "breakHours": 89, + }, + { + "termCode": 202600, + "department": 4, + "isFinal": False, + "approvedOn": None, + "approvedBy": None, + "justification": "Due to rapid department growth, we need to hire more students to help with the increased workload", + "primary_10": 11, + "primary_12": 2, + "primary_15": 3, + "primary_20": 4, + "secondary_5": 1, + "secondary_10": 1, + "breakHours": 293, + }, + { + "termCode": 202600, + "department": 3, + "isFinal": False, + "approvedOn": None, + "approvedBy": None, + "justification": "Due to rapid department growth, we need to hire more students to help with the increased workload", + "primary_10": 1, + "primary_12": 23, + "primary_15": 3, + "primary_20": 4, + "secondary_5": 1, + "secondary_10": 1, + "breakHours": 999, + }, + { + "termCode": 202600, + "department": 2, + "isFinal": False, + "approvedOn": None, + "approvedBy": None, + "justification": "Due to rapid department growth, we need to hire more students to help with the increased workload", + "primary_10": 10, + "primary_12": 2, + "primary_15": 3, + "primary_20": 13, + "secondary_5": 1, + "secondary_10": 19, + "breakHours": 1000, + }, + { + "termCode": 202600, + "department": 1, + "isFinal": False, + "approvedOn": None, + "approvedBy": None, + "justification": "Due to rapid department growth, we need to hire more students to help with the increased workload", + "primary_10": 1, + "primary_12": 2, + "primary_15": 3, + "primary_20": 4, + "secondary_5": 1, + "secondary_10": 1, + "breakHours": 100, + }, ] Allocation.insert_many(allocations).on_conflict_replace().execute()