Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
0eb3bb1
Added necessary files for the allocationTable
fritzj2 Jul 30, 2026
5ab2dca
added the individual term for each row in the table
fritzj2 Jul 30, 2026
ce23c09
Bootstrap datatable was implemented
fritzj2 Jul 30, 2026
05216d0
added a download button to the page
fritzj2 Jul 30, 2026
c8f12b1
Added accordians instead of a giant table
fritzj2 Jul 31, 2026
beaba49
structured primaries table
fritzj2 Jul 31, 2026
e981118
Formatted the break table
fritzj2 Jul 31, 2026
fd7c2c6
removed an unecessary card from the HTML
fritzj2 Aug 3, 2026
ddad2d2
added logic using the allocationManager
fritzj2 Aug 3, 2026
eb4e0af
Added break contracts to the table
fritzj2 Aug 3, 2026
9eaf061
fixed function calls, added more data for breaks
fritzj2 Aug 3, 2026
64f4dc4
got the contracts to count in page
fritzj2 Aug 4, 2026
c3e9cc1
Request allocation modification button
fritzj2 Aug 4, 2026
5350883
Created the secondaries table
fritzj2 Aug 4, 2026
f9670d3
table now calls allocations
fritzj2 Aug 4, 2026
23e84d1
fixed table sizing
fritzj2 Aug 4, 2026
54a3f13
added arrows to the accordions in th tables
fritzj2 Aug 4, 2026
cc374dc
fixed formatting of html
fritzj2 Aug 4, 2026
827f390
changed allocation req wording
fritzj2 Aug 4, 2026
5978774
fixed the contract index, the total returns correctly now
fritzj2 Aug 4, 2026
32f8f6b
fixed the sum of break hours
fritzj2 Aug 4, 2026
5110359
added the fall break row
fritzj2 Aug 4, 2026
071232b
moved the header to be centered
fritzj2 Aug 4, 2026
0760c5d
Merge branch 'department-portal-base' into allocation_table
fritzj2 Aug 4, 2026
fa4a35c
fixed format of accordions
fritzj2 Aug 4, 2026
5f44de5
Merge branch 'department-portal-base' of https://github.com/BCStudent…
fritzj2 Aug 5, 2026
764db13
fixed the button layout
fritzj2 Aug 5, 2026
b67164b
Added tests for getBreakContracts
fritzj2 Aug 5, 2026
cdfbae5
made getStudents() less brittle
fritzj2 Aug 5, 2026
2736b90
added notes to getBreakContracts test
fritzj2 Aug 5, 2026
22a1856
added a test for changing terms
fritzj2 Aug 5, 2026
2005c87
created date check in allocationManager
fritzj2 Aug 5, 2026
b9f9cc4
fixed comments for date logic
fritzj2 Aug 6, 2026
96ffe5f
fixed current term logic
fritzj2 Aug 6, 2026
ea29109
shortened js significantly.
fritzj2 Aug 6, 2026
73af911
added a check if no break contracts are found
fritzj2 Aug 6, 2026
76a167b
checks if the user is in a department
fritzj2 Aug 6, 2026
f74fb7d
test_allocationManager accurately reflects changes to logic
fritzj2 Aug 6, 2026
e4b2bc0
fixed test_allocationManager.py name
fritzj2 Aug 7, 2026
8c417f7
contractHours != None -> .is_null(False)
fritzj2 Aug 7, 2026
6299820
removed AddUserToDept route again
fritzj2 Aug 7, 2026
4b2ece2
fixed allocationManager import functions
fritzj2 Aug 7, 2026
1e4d1d4
fixed termCode function calls
fritzj2 Aug 7, 2026
18f1abc
added 404 error to no dept allocation view
fritzj2 Aug 7, 2026
7b0cd5a
added more comments to date contContracts
fritzj2 Aug 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions app/controllers/main_routes/main_routes.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from flask import render_template, request, json, redirect, url_for, send_file, g, flash, jsonify
from peewee import JOIN, DoesNotExist, fn
from functools import reduce
from datetime import datetime, date
import operator

from app.models.department import Department
Expand All @@ -11,6 +12,7 @@
from app.models.formHistory import FormHistory
from app.models.term import Term
from app.models.positionHistory import PositionHistory
from app.models.allocation import Allocation

from app.controllers.admin_routes.allPendingForms import checkAdjustment
from app.controllers.main_routes import main_bp
Expand All @@ -22,6 +24,7 @@
from app.logic.banner import Banner
from app.logic.getSupervisors import getSupervisors
from app.logic.getPositions import getActivePositions
from app.logic.allocationManager import getBreakContracts, getContractedAllocations, getTotalAllocations


@main_bp.route('/logout', methods=['GET'])
Expand Down Expand Up @@ -82,6 +85,55 @@ def departmentPortal(org=None,account=None):
positions = positionsList,
posURL = posURL)

@main_bp.route('/department/<org>/<account>/allocations', methods=['GET'])
def allocationTable(org=None, account=None):
currentUser = g.currentUser
try:
dept = Department.get(Department.ORG == org, Department.ACCOUNT == account)
except (NameError, DoesNotExist):
Comment thread
fritzj2 marked this conversation as resolved.
return render_template('errors/404.html'), 404

if not currentUser.isLaborAdmin:
allowedDepartmentIds = [d.departmentID for d in getDepartmentsForSupervisor(currentUser)]
if dept.departmentID not in allowedDepartmentIds:
return render_template('errors/403.html'), 403


currentDate = date.today()
if currentDate.month <= 6:
# If it is the spring semester, then the term code is 1 year behind. e.g. 2025-2026 term code is 202500. Thus the - 100 in the spring term.
# The (year * 100) turns the year into an AY term code, 2025 -> 202500. The + 12/11 turns it into a fall or spring term.
springTerm = Term.select().where(Term.termCode == currentDate.year * 100 + 12 - 100).get()
currentAY = Term.select().where(Term.termCode == currentDate.year * 100 - 100).get()
fallTerm = Term.select().where(Term.termCode == currentDate.year * 100 + 11 - 100).get()

else:
fallTerm = Term.select().where(Term.termCode == currentDate.year * 100 + 11).get()
currentAY = Term.select().where(Term.termCode == currentDate.year * 100).get()
springTerm = Term.select().where(Term.termCode == currentDate.year * 100 + 12).get()

allocationDict = getTotalAllocations(currentAY.termCode, dept)
fallContracts = getContractedAllocations(fallTerm.termCode, dept)
springContracts = getContractedAllocations(springTerm.termCode, dept)

breakContracts = {
"total": 0,
"thanksgiving":getBreakContracts(currentAY.termCode + 1, dept),
"winter": getBreakContracts(currentAY.termCode + 2, dept),
"spring": getBreakContracts(currentAY.termCode + 3, dept),
"fall":getBreakContracts(currentAY.termCode + 4, dept),
"summer": getBreakContracts(currentAY.termCode + 13, dept)
}
breakContracts["total"] = sum(breakContracts.values())
return render_template('main/allocationTable.html',
department = dept,
currentAY = currentAY,
allocations = allocationDict,
fallContracts = fallContracts,
springContracts = springContracts,
breakContracts = breakContracts)


@main_bp.route('/supervisorPortal/download', methods=['POST'])
def downloadSupervisorPortalResults():
'''
Expand Down
42 changes: 40 additions & 2 deletions app/logic/allocationManager.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,36 @@ def getTotalAllocations(termCode: int, dept: int):
"totalAllocations": (allocationObject["primary_10"] + allocationObject["primary_12"] + allocationObject["primary_15"] + allocationObject["primary_20"] + allocationObject["secondary_5"] + allocationObject["secondary_10"] )}
return allocationDict

def countContracts(jobType: str, weeklyContractHours: int, termCode: int, dept: int):
def countContracts(jobType: str, weeklyContractHours: int, termCode: int, dept: int, AYtermCode: int = None):
'''
This function counts the number of positions of a given type in a given department.
For example, countContracts('secondary', 5, 202511, 1) returns the number of secondary
5-hour positions in the CS department for the 2025 Fall term.
'''
academicYearCode = int(str(termCode)[:4] + "00")

# This sets the date condition to determine whether the form is within the boundaries of the term
# Fall only contracts end before spring, spring contracts start after fall.
fallMonths = ["07","08","09","10","11","12"]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we clean up this date filtering?

This currently compares extracted month values to string values like "07" and "08". It may be safer to compare against actual dates or the selected term’s termStart / termEnd instead of only checking months. Month-only logic can get confusing for contracts that cross years, cover the full academic year, or overlap summer.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As interesting a concept as it sounds, the impact on code performance here is almost negligible. In the end, the check for the year-long positions checks the end-of-year dates anyway. e.g. (lsf.startDate >= AY.startDate) & lsf.startDate <= AY.endDate).

  • In the end, it requires a comparison that changes the implementation to check the same thing. It still checks if it pushes into the end of the year regardless.
  • To change this implementation to be more strict, it requires us to always pass in a fall and spring term into the code, which we may not always need. This implementation is also the longest of all the options and the hardest to read.
  • The edge case that this fixes, however, is a termDate change. Where the end of a Fall term ends in January, instead of December.

The end code requires many comparisons, which makes the already hard-to-read code even harder to read and debug, for very little benefit.

springMonths = ["01","02","03","04","05","06"]
if str(termCode).endswith("11"):
dateCondition = (
(LaborStatusForm.endDate.month.in_(fallMonths)) |
(LaborStatusForm.startDate.month.in_(fallMonths) & # Reused check for year-long positions
LaborStatusForm.endDate.month.in_(springMonths))
)
elif str(termCode).endswith("12"):
dateCondition = (
(LaborStatusForm.startDate.month.in_(springMonths)) |
(LaborStatusForm.startDate.month.in_(fallMonths) &
LaborStatusForm.endDate.month.in_(springMonths))
)
else:
dateCondition = (
(LaborStatusForm.startDate.month.in_(fallMonths) &
LaborStatusForm.endDate.month.in_(springMonths))
)

lsfCountPositions = FormHistory.select(
).join(LaborStatusForm
).join(Department
Expand All @@ -54,6 +77,7 @@ def countContracts(jobType: str, weeklyContractHours: int, termCode: int, dept:
LaborStatusForm.jobType == jobType, # 'primary' or 'secondary'
LaborStatusForm.weeklyHours == weeklyContractHours, # 5, 10, 12, 15, or 20
Department.departmentID == dept,
dateCondition,
).count()
return lsfCountPositions

Expand Down Expand Up @@ -105,4 +129,18 @@ 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
return usedPositions

def getBreakContracts(termCode, dept):
break_allocation = FormHistory.select(fn.SUM(LaborStatusForm.contractHours)
).join(LaborStatusForm
).where(
FormHistory.historyType == "Labor Status Form",
FormHistory.status.in_(["Approved", "Pending", "Pre-Student Approval"]),
LaborStatusForm.termCode == termCode,
LaborStatusForm.department == dept,
LaborStatusForm.contractHours.is_null(False)).scalar()
if break_allocation != None:
return break_allocation
else:
return 0
63 changes: 63 additions & 0 deletions app/static/css/allocationTable.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
.grid-container {
display: grid;
grid-template-columns: 1fr 1fr;
}
.btn-success{
align-self: center;
justify-self: end;
}
.card-header {
background-color: #efebeb;
margin-bottom: 7px;
height: 6rem;
}
.mb-0, .collapsed {
outline-color: none;
color: black;
font-size: 18px;
font-weight: 525;
}
.collapse{
padding-left: 5% ;
padding-right: 5%;
}
.accordion{
padding-left: 5%;
padding-right: 5%;
}
.table-striped {
table-layout:fixed;
width:100%;
}
.table-striped tbody tr:nth-of-type(odd) {
background-color: #f2f2f2;
}
.btn-info{
justify-self:flex-end;
align-self: center;
margin-left: 4px
}
.accordion-arrow {
display: inline-block;
transition: transform 0.2s ease-in-out;
}
.btn.collapsed .accordion-arrow {
transform: rotate(-90deg);
}
.btn:not(.collapsed) .accordion-arrow {
transform: rotate(0deg);
}
.button-container {
display:flex;
justify-content:end;
}
.btn-block{
align-items: center;
align-self: center;
text-align: center;
justify-content: center;
height: 100%
}
.termDisplay{
margin-right:auto;
}
18 changes: 18 additions & 0 deletions app/static/js/allocationTable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
$(document).ready( function(){
function initTable(selector) {
return $(selector).DataTable({
pageLength: 25,
info: false,
lengthChange: false,
searching: false,
paging: false,
order: []
});
}

const fallTermPrimaries = initTable('#fallTermPrimaries');
const fallTermSecondaries = initTable('#fallTermSecondaries');
const springTermPrimaries = initTable('#springTermPrimaries');
const springTermSecondaries = initTable('#springTermSecondaries');
const breakTable = initTable('#breakTable');
});
Loading