Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
9bd90f6
added changes from previous branch to avoid all files being commited
conwelld Jul 21, 2026
56d5a29
fixed some pr comments
conwelld Jul 21, 2026
faf1957
fixed some pr comments for css styling
conwelld Jul 21, 2026
1204269
fixed useless code
conwelld Jul 21, 2026
0f166c2
fixed department code
conwelld Jul 21, 2026
6f5850a
We have change the hard coded part with variable able to be flexible …
Jul 22, 2026
813f398
Temporary allocation logic will be replaced by the official shared se…
Jul 22, 2026
7584ffa
fix the review comment from Minran, all of them
DanielRukwasha Jul 27, 2026
e9a4415
Merge branch 'department-portal-base' into new-allocation-card, resol…
DanielRukwasha Jul 28, 2026
20b424c
Fix import after allocationUtilization rename to getAllocation, add i…
DanielRukwasha Jul 29, 2026
ee0e066
Address PR review comments: consolidate getAllocation return dict, ex…
DanielRukwasha Jul 30, 2026
6abd704
Merge branch 'department-portal-base' into new-allocation-card, resol…
DanielRukwasha Jul 30, 2026
8618b68
Adopt approval-status filtering from UsedAllocFunction branch in getA…
DanielRukwasha Jul 30, 2026
a0ba238
Polish the allocation card: dynamic Fall/Spring term label, "X of Y" …
munsakad Aug 3, 2026
86bee45
Merge branch 'department-portal-base' into new-allocation-card
munsakad Aug 3, 2026
45b13e0
Fix duplicate main.managePositions endpoint left over from the depart…
munsakad Aug 3, 2026
74bdd3a
Rework allocation rows to the "N hr: X contracts (out of Y allocation…
munsakad Aug 3, 2026
a7ca474
Address allocation card review: camelCase naming, stacked layout, con…
munsakad Aug 4, 2026
c7ef1ec
Merge branch 'department-portal-base' of https://github.com/BCStudent…
munsakad Aug 5, 2026
25d9a9b
Reuse allocationManager.py in getAllocation.py
munsakad Aug 5, 2026
f5640b3
Fix missing LaborReleaseForm import in demo_data.py
munsakad Aug 5, 2026
4c17019
Fix allocation card showing zeros for departments with only a draft a…
munsakad Aug 5, 2026
f3b43c6
removed unneccesary comments
munsakad Aug 5, 2026
6ae3931
removed department information in main routes
munsakad Aug 5, 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
21 changes: 21 additions & 0 deletions app/controllers/main_routes/main_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from app.models.laborStatusForm import LaborStatusForm
from app.models.formHistory import FormHistory
from app.models.term import Term
from app.models.allocation import Allocation
from app.models.positionHistory import PositionHistory

from app.controllers.admin_routes.allPendingForms import checkAdjustment
Expand All @@ -20,6 +21,7 @@
from app.login_manager import require_login, logout
from app.logic.getTableData import getDatatableData
from app.logic.banner import Banner
from app.logic.getAllocation import getDepartmentAllocationSummary
from app.logic.getSupervisors import getSupervisors
from app.logic.getPositions import getActivePositions

Expand Down Expand Up @@ -71,17 +73,36 @@ def departmentPortal(org=None,account=None):

supervisors, laborCoordinators = getSupervisors(dept)

allocationSummary = getDepartmentAllocationSummary(dept)
recentTerm = allocationSummary["term"]

if recentTerm:
try:
allocation = Allocation.select(Allocation, Term).join(Term).where(Allocation.department == dept, Allocation.termCode == recentTerm.termCode).get()
except DoesNotExist:
allocation = None
else:
allocation = None

positionsList, posURL = getActivePositions(dept)

return render_template('main/departmentPortal.html',
departments = departments,
department = dept,
allocation = allocation,
allocated = allocationSummary["allocated"],

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.

I believe I have comment on this before if we are rendering allocationsummary with multiple variables we can just send the allocationsummary to jinja and use {{ allocatedsummary.allocated }} to access the elements inside while in the macros like {% macro allocationRow(hours, used, allocationsummary) -%}
you just use allocationsummary instead

used = allocationSummary["used"],
term = recentTerm,
currentSemester = allocationSummary["currentSemester"],
usedPositions = allocationSummary["usedPositions"],
breakHours = allocationSummary["breakHours"],
supervisors = supervisors,
laborCoordinators=laborCoordinators,
currentUser=currentUser,
positions = positionsList,
posURL = posURL)


@main_bp.route('/supervisorPortal/download', methods=['POST'])
def downloadSupervisorPortalResults():
'''
Expand Down
42 changes: 14 additions & 28 deletions app/logic/allocationManager.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,36 +59,22 @@ def countContracts(jobType: str, weeklyContractHours: int, termCode: int, dept:

def getContractedAllocations(termCode: int, dept: int):
'''
This function returns a dictionary with a breakdown of all types of contracts
This function returns a dictionary with a breakdown of all types of contracts
for the given department and term in the form of a dictionary.
'''
academicYearCode = int(str(termCode)[:4] + "00")
allocationObject = getAllocation(termCode, dept)
breakAllocation = FormHistory.select(
LaborStatusForm.department,
LaborStatusForm.termCode,
fn.SUM(LaborStatusForm.contractHours).alias('total_hours')
).join(
LaborStatusForm,
on=(FormHistory.formID == LaborStatusForm.laborStatusFormID),
).join(
Term,
on = (LaborStatusForm.termCode == Term.termCode )
).where(
(FormHistory.historyType == "Labor Status Form") &
(FormHistory.status == "Approved") &
(LaborStatusForm.termCode.in_([termCode,academicYearCode]))
).group_by(
LaborStatusForm.department,
LaborStatusForm.termCode).dicts()

breakSum = {"total_hours": 0}
if dept:
for row in breakAllocation:
if row["department"] == dept:
breakSum = row
break

breakHoursTotal = (
FormHistory.select(fn.SUM(LaborStatusForm.contractHours))
.join(LaborStatusForm, on=(FormHistory.formID == LaborStatusForm.laborStatusFormID))
.where(
FormHistory.historyType == "Labor Status Form",
FormHistory.status == "Approved",
LaborStatusForm.termCode.in_([termCode, academicYearCode]),
LaborStatusForm.department == dept,
)
.scalar()
) or 0

# dictionary definition:
usedPositions = {
"used_10": countContracts("Primary", "10", termCode, dept),
Expand All @@ -100,7 +86,7 @@ def getContractedAllocations(termCode: int, dept: int):
"used_primaries": 0,
"used_secondaries": 0,
"used_total": 0, # all contracts with weekly hours, i.e. primaries + secondaries (not break contracts)
"break_hours": breakSum["total_hours"] # all break hours contracted (but not necessarily worked)
"break_hours": breakHoursTotal # all break hours contracted (but not necessarily worked)
}
usedPositions["used_primaries"] = sum(list(usedPositions.values())[:4])
usedPositions["used_secondaries"] = sum(list(usedPositions.values())[4:6])
Expand Down
71 changes: 71 additions & 0 deletions app/logic/getAllocation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
from datetime import date

from app.logic.allocationManager import getContractedAllocations
from app.models.allocation import Allocation
Comment thread
DanielRukwasha marked this conversation as resolved.
from app.models.term import Term
Comment thread
DanielRukwasha marked this conversation as resolved.


def getCurrentSemesterLabel(term):
"""Return the Fall/Spring label (e.g. "Fall 2025") for the AY term's
current semester, picking the season from today's month."""
if not term:
return None
academicYear = int(str(term.termCode)[:4])
if date.today().month >= 8:
return f"Fall {academicYear}"
return f"Spring {academicYear + 1}"


def getDepartmentAllocationSummary(department):
"""Return allocation-utilization values for a department's most recent term."""
result = {
"term": None,
"currentSemester": None,
"allocated": 0,
"used": 0,
"usedPositions": {
"used10": 0,
"used12": 0,
"used15": 0,
"used20": 0,
"usedSecondary5": 0,
"usedSecondary10": 0,
},
"breakHours": 0,
}
Comment thread
munsakad marked this conversation as resolved.

departmentAllocations = list(
Allocation.select(Allocation, Term).join(Term).where(Allocation.department == department)
)
if not departmentAllocations:
return result

recentTerm = Term.order_by_term([a.termCode for a in departmentAllocations], reverse=True)[0]
termCode = recentTerm.termCode
result["term"] = recentTerm
result["currentSemester"] = getCurrentSemesterLabel(recentTerm)

# "allocated" is summed directly from the rows already fetched above rather
# than through allocationManager's getTotalAllocations, since that only
# looks at the *final* Allocation row for a term - a department whose most
# recent term is still a draft (isFinal=False, no final row yet) would
# otherwise show 0 allocated instead of its draft numbers.
recentTermAllocations = [a for a in departmentAllocations if a.termCode_id == termCode]
result["allocated"] = sum(
a.primary_10 + a.primary_12 + a.primary_15 + a.primary_20 + a.secondary_5 + a.secondary_10
for a in recentTermAllocations
)

contractedAllocations = getContractedAllocations(termCode, department.departmentID)
result["used"] = contractedAllocations["used_total"]
result["usedPositions"] = {
"used10": contractedAllocations["used_10"],
"used12": contractedAllocations["used_12"],
"used15": contractedAllocations["used_15"],
"used20": contractedAllocations["used_20"],
"usedSecondary5": contractedAllocations["used_5_sec"],
"usedSecondary10": contractedAllocations["used_10_sec"],
}
result["breakHours"] = contractedAllocations["break_hours"]

return result
70 changes: 70 additions & 0 deletions app/static/css/departmentPortal.css
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,64 @@
font-size: 3rem;
color:#6e6e6e;
}
.bi-clock {
Comment thread
munsakad marked this conversation as resolved.
border: 1px solid #c0c0c0;
border-radius: 8px;
padding: 3px 3.5px 1.5px 3.5px;
font-size: 3rem;
color:#6e6e6e;
}
.bi-info-circle {
padding: 3px 3.5px 1.5px 3.5px;
font-size: 1.5rem;
vertical-align: middle;
color:#6e6e6e;
}
.allocation-table-wrapper {
overflow-x: auto;
margin: 10px 0;
}
.allocation-summary {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-items: baseline;
gap: 0 1rem;
}
.allocation-summary h4 {
margin: 10px 0;
}
.allocation-columns {
display: flex;

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.

Image make sure the secondary hours are on the top and not evenly space

flex-wrap: wrap;
gap: 0 2rem;
}
.allocation-table {
/* wraps onto its own line when the card is too narrow, instead of shrinking */
flex: 1 1 180px;
border-collapse: collapse;
font-size: 1.2em;
}
.allocation-table th,
.allocation-table td {
text-align: left;
white-space: nowrap;
padding: 4px 10px 4px 0;
line-height: 1.3;
}
.allocation-table th {
font-weight: 700;
padding-top: 10px;
}

.bi-people-fill { /* Bootstrap Icon for Members Card */
border: 1px solid #c0c0c0;
border-radius: 8px;
padding: 3px 3.5px 1.5px 3.5px;
font-size: 3rem;
color:#6e6e6e;
}

.card-group {
gap: 1rem;
}
Expand All @@ -52,3 +110,15 @@
flex-direction: column;
}
}

/* Narrow card: stack Secondary below Primary, and the position count below the
term, rather than shrinking the text to keep them side by side. */
@media (min-width: 1200px) and (max-width: 1450px), (max-width: 480px) {
.allocation-summary {
flex-direction: column;
gap: 0;
}
.allocation-columns .allocation-table {
flex-basis: 100%;
}
}
4 changes: 4 additions & 0 deletions app/static/js/departmentPortal.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@ $(document).ready(function() {
window.location = `/department/${deptData.org}/${deptData.account}`;
});
});

$(function () {
$('[data-toggle="tooltip"]').tooltip()
})
48 changes: 45 additions & 3 deletions app/templates/main/departmentPortal.html
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,52 @@ <h2 class="text-center">{% if department %} {{department.DEPT_NAME}} Portal {% e
{% if department %}
<div class="card-row g-3">
<div class="col-12 col-lg-4 ">
<section class="card" aria-labelledby="current_allocation-title">
<p> Insert Allocations Card Here </p>
<section class="card" aria-labelledby="allocationTitle">
<div class="card-body">
<div class="media">
<div class="media-left media-middle">
<span aria-hidden="true" style="color: #424242; font-size: 24px;">
<i class="bi bi-clock"></i>
</span>
</div>
<div class="media-body media-middle">
<h3 id="allocationTitle">Current Allocations</h3>
</div>
{% macro allocationRow(hours, used, allocated) -%}
<tr><td>{{ hours }} hr: {{ used }} contract{{ 's' if used != 1 else '' }}<br>(out of {{ allocated }} allocation{{ 's' if allocated != 1 else '' }})</td></tr>
{%- endmacro %}
<div class="allocation-table-wrapper">
<div class="allocation-summary">
<h4>{{ currentSemester if currentSemester else "No term data" }} <i class="bi bi-info-circle" data-toggle="tooltip" data-placement="top" title="All values are shown as Contracted/Allocated"></i></h4>
<h4>{{used}} contracted of {{allocated or 0}} allocated Positions</h4>
</div>
<div class="allocation-columns">
<table class="allocation-table">
<thead>
<tr><th>Primary</th></tr>
</thead>
<tbody>
{{ allocationRow(10, usedPositions.used10, allocation.primary_10) }}
{{ allocationRow(12, usedPositions.used12, allocation.primary_12) }}
{{ allocationRow(15, usedPositions.used15, allocation.primary_15) }}
{{ allocationRow(20, usedPositions.used20, allocation.primary_20) }}
</tbody>
</table>
<table class="allocation-table">
<thead>
<tr><th>Secondary</th></tr>
</thead>
<tbody>
{{ allocationRow(5, usedPositions.usedSecondary5, allocation.secondary_5) }}
{{ allocationRow(10, usedPositions.usedSecondary10, allocation.secondary_10) }}
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="card-footer text-center">
<a href="/department/{{ department.ORG }}/{{ department.ACCOUNT }}/REPLACEME" class="btn btn-primary">View Allocations<span class="glyphicon glyphicon-chevron-right"></span></a>
<a href="#" class="btn btn-primary">View Allocations<span class="glyphicon glyphicon-chevron-right"></span></a>
</div>
</section>
</div>
Expand Down
Loading