Skip to content
Open
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
8a085f6
term missed and term added
MImran2002 Mar 21, 2025
c0f13eb
almost there
MImran2002 Mar 24, 2025
3edbffc
The functions are done I just need to work on the test suite
MImran2002 Mar 29, 2025
c043925
remove duplicate certifications, add tooltips to show what terms the …
MImran2002 Mar 29, 2025
b297d18
final push
MImran2002 Apr 3, 2025
7fa9d84
Merge branch 'development' into bonnerCheckmarkProfile
bledsoef Apr 10, 2025
f178417
Merge branch 'development' into bonnerCheckmarkProfile
WackyWeaver Apr 10, 2025
fae60f9
change the tooltips location, add an alternative message if no terms …
MImran2002 Apr 21, 2025
7b2c64f
finding bug
MImran2002 Apr 21, 2025
a2bc75f
done
MImran2002 Apr 21, 2025
2d3f126
Made changes in code structure
stevensonmichel Apr 23, 2025
f22712c
Merge branch 'development' into bonnerCheckmarkProfile
stevensonmichel Apr 23, 2025
b757c69
user current term is refactored as g.current_term and have hidden the…
MImran2002 Apr 24, 2025
5b43afd
Fixed merge conflicts
stevensonmichel Apr 29, 2025
f462772
The current term is updated to false and a new current term is create…
MImran2002 Apr 29, 2025
6585472
merging
MImran2002 Apr 30, 2025
fbc32f6
merge conflicts and probnlem solved
MImran2002 Apr 30, 2025
c0e53f5
Merge branch 'development' of https://github.com/BCStudentSoftwareDev…
MImran2002 Apr 30, 2025
bcdeffc
Fixed logic efficiency with set
stevensonmichel May 1, 2025
51de1cc
Fixed issues from test_certification.py
stevensonmichel May 2, 2025
3082d37
removed print statements
stevensonmichel May 2, 2025
6be5909
Merge branch 'development' into bonnerCheckmarkProfile
RueHaile Sep 15, 2025
41d12fb
Merge branch 'development' of https://github.com/BCStudentSoftwareDev…
MImran2002 Sep 23, 2025
b2a8433
Merge branch 'bonnerCheckmarkProfile' of https://github.com/BCStudent…
MImran2002 Sep 23, 2025
802f172
placing change for tick
MImran2002 Sep 23, 2025
135f65f
userprofile
MImran2002 Sep 27, 2025
61c57f9
stashing
MImran2002 Sep 30, 2025
a3be1e2
remove print statement
MImran2002 Oct 13, 2025
37e6f0e
Merge branch 'development' of https://github.com/BCStudentSoftwareDev…
MImran2002 Nov 6, 2025
12392c9
still working on bonner checkmark profile
MImran2002 Nov 6, 2025
bfce623
worked on test suite, new function created jinja template
MImran2002 Nov 7, 2025
5e5858c
new changes and logic'
MImran2002 Nov 11, 2025
ced4ec8
new changes after Jame's review
MImran2002 Nov 11, 2025
7d3f2b9
minor changes
MImran2002 Nov 12, 2025
6f5caa3
minor changes
MImran2002 Nov 12, 2025
13b2637
final changes hopefully
MImran2002 Nov 12, 2025
f50de7d
changes terms toe vents
MImran2002 Nov 12, 2025
fbc5522
added the view and did some jinja templating
MImran2002 Nov 12, 2025
edf7ea5
added the view and did some jinja templating
MImran2002 Nov 12, 2025
18c6d2b
added the view and did some jinja templating
MImran2002 Nov 12, 2025
b6eba3b
Adjusted structure of termsInTotal() function to make it more readable
ojmakinde Nov 12, 2025
c3de4af
worked on the username and other rawclasslevel possibility
MImran2002 Nov 15, 2025
dffcb2b
Merge branch 'bonnerCheckmarkProfile' of https://github.com/BCStudent…
MImran2002 Nov 15, 2025
1a9b7fa
function created and working
MImran2002 Nov 18, 2025
cd905b5
added docstring
MImran2002 Nov 19, 2025
04dd221
added updated docstring
MImran2002 Nov 20, 2025
146589e
currently working for all except null status
RueHaile Nov 21, 2025
5610de9
Populating correctly for grguated students
RueHaile Nov 21, 2025
d021638
Merge branch 'development' into bonnerCheckmarkProfile
BrianRamsay Nov 24, 2025
e3e4f1f
make sure if the duplicate removing function doesn't remove the once …
MImran2002 Nov 27, 2025
01fbd07
its the print statements that were breaking
MImran2002 Nov 28, 2025
f22d5a2
found a bug that could cause problem with none, null, graduating and …
MImran2002 Dec 10, 2025
fe25769
fixed the bug and remove print statements
MImran2002 Dec 10, 2025
8a8786c
Merge branch 'development' of https://github.com/BCStudentSoftwareDev…
MImran2002 Dec 18, 2025
5b077fc
adjusting what to show when the current term is summer
MImran2002 Dec 18, 2025
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
1 change: 0 additions & 1 deletion app/controllers/admin/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,6 @@ def eventDisplay(eventId):
for year, cohort in rawBonnerCohorts.items():
if cohort:
bonnerCohorts[year] = cohort

invitedCohorts = list(EventCohort.select().where(
EventCohort.event_id == eventId,
))
Expand Down
4 changes: 1 addition & 3 deletions app/controllers/main/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
from app.logic.utils import selectSurroundingTerms
from app.logic.celtsLabor import getCeltsLaborHistory
from app.logic.createLogs import createRsvpLog, createActivityLog
from app.logic.certification import getCertRequirementsWithCompletion
from app.logic.certification import getCertRequirementsWithCompletion, termsMissed
from app.logic.landingPage import getManagerProgramDict, getActiveEventTab
from app.logic.minor import toggleMinorInterest, declareMinorInterest, getCommunityEngagementByTerm, getEngagementTotal
from app.logic.participants import unattendedRequiredEvents, trainedParticipants, getParticipationStatusForTrainings, checkUserRsvp, addPersonToEvent
Expand Down Expand Up @@ -218,9 +218,7 @@ def viewUsersProfile(username):
"onTranscript": onTranscript}),

profileNotes = ProfileNote.select().where(ProfileNote.user == volunteer)

bonnerRequirements = getCertRequirementsWithCompletion(certification=Certification.BONNER, username=volunteer)

managersProgramDict = getManagerProgramDict(g.current_user)
managersList = [id[1] for id in managersProgramDict.items()]
totalSustainedEngagements = getEngagementTotal(getCommunityEngagementByTerm(volunteer))
Expand Down
104 changes: 86 additions & 18 deletions app/logic/certification.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,68 @@
from peewee import JOIN, DoesNotExist, Case

from flask import g
from app.models.event import Event
from app.models.term import Term
from app.models.certification import Certification
from app.models.certificationRequirement import CertificationRequirement
from app.models.requirementMatch import RequirementMatch
from app.models.eventParticipant import EventParticipant
from app.models.user import User

def termsAttended(certification=None, username=None):
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.

We should handle if username is not provided in a different way. Currently, we'll get an error, as len(attendance) will break in some fashion, as attendance won't have been defined if there is no username.

We should probably check for a missing username and exit the function right at the top if it is not provided.

Since we are handling that case, we should also have a test case when the expected parameters are not given, and make sure that the function fails gracefully (or fails the way we would expect)

Copy link
Copy Markdown
Contributor

@BrianRamsay BrianRamsay Nov 13, 2025

Choose a reason for hiding this comment

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

Doesn't appear to be resolved. If username is None, attendance will be undefined when we try to use it on line 21.

(username if conditional check has been removed-Imran)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

username conditional check has been removed.

'''
Retrieve terms attended by a user for certification and filter them based on frequency of a term
'''
attendedTerms = []
if username:
attendance = (RequirementMatch.select()
.join(EventParticipant, JOIN.LEFT_OUTER, on=(RequirementMatch.event == EventParticipant.event))
.where(RequirementMatch.requirement_id == certification)
.where(EventParticipant.user == username))
for termRecord in range(len(attendance)):
attendedTerms.append(attendance[termRecord].event.term.description)
return attendedTerms

def termsMissed(certification=None, username=None):
'''
Calculate how many certification-eligible terms a student has missed based on their class level
and attendance record.

Logic:
- Each class level is expected to participate in 2 terms per year.
- If the user is currently in their final spring term (e.g., Spring of senior year),
they are expected to have completed all terms: missedTerms = (level + 1) * 2.
- Otherwise, assume they’ve had one fewer term to attend: missedTerms = ((level + 1) * 2) - 1.
- If the user's classification is None, assume just 1 expected term.
- Subtract the number of terms the student has attended from the expected total to get the missed count.
'''
classLevel = ["Freshman", "Sophomore", "Junior", "Senior"]
currentTerm = g.current_term
currentDescription = currentTerm.description

# looking into a scenario where the current term is summer so that we can reassigned the current term variable to the next term
if currentTerm.isSummer == True:
currentDescription = f'Fall {currentTerm.year}'
currentTerm = Term.select(Term).where(Term.description == currentDescription).get()
else:
currentDescription = currentTerm.description

for level in range(4):
user = User.select().where(User.username == username).get()
if user.rawClassLevel == classLevel[level] and currentDescription == f'Spring {currentTerm.year}':
missedTerms = (level + 1) * 2
elif user.rawClassLevel == classLevel[level]:
missedTerms = ((level + 1) * 2) - 1
elif str(user.rawClassLevel) == "None":
missedTerms = 1

attendedTerms = termsAttended(certification, username)
missedTerms = missedTerms - len(attendedTerms)

return missedTerms

def getCertRequirementsWithCompletion(*, certification, username):
"""
Function to differentiate between simple requirements and requirements completion checking.
See: `getCertRequirements`
Differentiate between simple requirements and requirements completion checking.
"""
return getCertRequirements(certification, username)

Expand All @@ -28,7 +82,6 @@ def getCertRequirements(certification=None, username=None):
reqList = (Certification.select(Certification, CertificationRequirement)
.join(CertificationRequirement, JOIN.LEFT_OUTER, attr="requirement")
.order_by(Certification.id, CertificationRequirement.order.asc(nulls="LAST")))

if certification:
if username:
# I don't know how to add something to a select, so we have to recreate the whole query :(
Expand All @@ -43,26 +96,41 @@ def getCertRequirements(certification=None, username=None):

# we have to add the is not null check so that `cert.requirement` always exists
reqList = reqList.where(Certification.id == certification, CertificationRequirement.id.is_null(False))
reqList = reqList.distinct()

certs = []
certificationList = []
for cert in reqList:
if username:
cert.requirement.completed = bool(cert.__dict__['completed'])
certs.append(cert.requirement)
return certs

#return [cert.requirement for cert in reqList]
# this is to get the calculation when it comes to events with term as their frequency
if cert.requirement.frequency == "term":
cert.requirement.missedTerms = termsMissed(cert.requirement.id, username)
cert.requirement.attendedTerms = len(termsAttended(cert.requirement.id, username))
cert.requirement.attendedDescriptions = termsAttended(cert.requirement.id, username)
certificationList.append(cert.requirement)

# the .distinct() doesn't work efficiently, so we have to manually go through the list and removed duplicates that exist
validCertification = set()
certificationIndex = 0
uniqueCertification = []

for cert in certificationList:
if certificationList[certificationIndex] not in validCertification:
validCertification.add(certificationList[certificationIndex])
uniqueCertification.append(certificationList[certificationIndex])

certificationIndex += 1

certificationList = uniqueCertification

return certificationList

certs = {}
certificationDict = {}
for cert in reqList:
if cert.id not in certs.keys():
certs[cert.id] = {"data": cert, "requirements": []}

if cert.id not in certificationDict.keys():
certificationDict[cert.id] = {"data": cert, "requirements": []}
if getattr(cert, 'requirement', None):
certs[cert.id]["requirements"].append(cert.requirement)

return certs
certificationDict[cert.id]["requirements"].append(cert.requirement)
return certificationDict

def updateCertRequirements(certId, newRequirements):
"""
Expand Down
12 changes: 10 additions & 2 deletions app/static/js/userProfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ $(document).ready(function(){
});
})

var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl)
})

$("#printButton").on("click", function() {
let username = $(this).data('username')
printDocument(`/profile/${username}/travelForm`)
Expand Down Expand Up @@ -215,7 +220,7 @@ $(document).ready(function(){
reloadWithAccordion(target)
}
});
});
});

$(".deleteNoteButton").click(function() {
let username = $(this).data('username')
Expand Down Expand Up @@ -326,7 +331,10 @@ $(document).ready(function(){
},
})
});

const bonnerStudent = $("#bonnerStudent").data('username')
if (bonnerStudent === "False"){
$("#bonnerStudent").prop("hidden", true)
}
});

function updateManagers(el, volunteerUsername ){// retrieve the data of the student staff and program id if the boxes are checked or not
Expand Down
3 changes: 2 additions & 1 deletion app/templates/admin/bonnerManagement.html
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ <h3 class="accordion-header" id="headingOne">
{% set show = "active" if subTab|int == year else "" %}
{% set aria = "true" if subTab|int == year else "false" %}
<button class="nav-link {{show}}" id="v-pills-{{year}}-tab" data-bs-toggle="pill" data-bs-target="#v-pills-{{year}}" type="button" role="tab" data-year="{{year}}" aria-controls="v-pills-{{year}}" aria-selected="{{aria}}">{{year}} - {{year + 1}}</button>
{% endfor %}
{% endfor %}
</div>
<div class="tab-content me-2 ms-2" id="v-pills-tabContent">
{% for year, users in cohorts.items() %}
Expand Down Expand Up @@ -150,6 +150,7 @@ <h3 class="accordion-header" id="headingThree">
<select class="flex-column form-select empty frequency-select" name="frequency_{{req.id}}">
<option value="" {{ "selected" if not req.frequency else "" }}>Frequency</option>
<option value="once" {{ "selected" if req.frequency == "once" else "" }}>Once</option>
<option value="twice" {{ "selected" if req.frequency == "twice" else "" }}>Twice</option>
<option value="annual" {{ "selected" if req.frequency == "annual" else "" }}>Annual</option>
<option value="term" {{ "selected" if req.frequency == "term" else "" }}>Every Term</option>
</select>
Expand Down
45 changes: 33 additions & 12 deletions app/templates/main/userProfile.html
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ <h1>{{volunteer.firstName}} {{volunteer.lastName}}</h1>
{% if volunteer.major -%}
<div>{{volunteer.major}}</div>
{% endif %}
<div>{{volunteer.processedClassLevel}}</div>
{% if volunteer.classLevel %}
<div>{{volunteer.classLevel}} </div>
{% endif %}
{% if volunteer.cpoNumber %}
<div>
{{volunteer.cpoNumber}}
Expand Down Expand Up @@ -459,14 +461,15 @@ <h3 class="accordion-header" id="headingSeven">
<!-- ################# Bonner Scholar ################ -->
{% set canViewBonner = (g.current_user.isCeltsAdmin or g.current_user == volunteer) %}
{% if volunteer.isBonnerScholar and canViewBonner %}
<div class="accordion-item">
<h3 class="accordion-header" id="headingEight">
{% set focus = "open" if visibleAccordion == "bonner" else "collapsed" %}
<button class="accordion-button {{focus}}" type="button" data-bs-toggle="collapse" data-bs-target="#collapseEight" aria-expanded="false" aria-controls="collapseEight">Bonner Program</button>
</h3>
{% set show = "show" if visibleAccordion == "bonner" else "" %}
<div id="collapseEight" class="accordion-collapse collapse {{show}}" aria-labelledby="headingEight" data-bs-parent="#userProfile">
<div class="accordion-body container">
<div id="bonnerStudent" data-username="{{volunteer.isStudent}}">
<div class="accordion-item">
<h3 class="accordion-header" id="headingEight">
{% set focus = "open" if visibleAccordion == "bonner" else "collapsed" %}
<button class="accordion-button {{focus}}" type="button" data-bs-toggle="collapse" data-bs-target="#collapseEight" aria-expanded="false" aria-controls="collapseEight">Bonner Program</button>
</h3>
{% set show = "show" if visibleAccordion == "bonner" else "" %}
<div id="collapseEight" class="accordion-collapse collapse {{show}}" aria-labelledby="headingEight" data-bs-parent="#userProfile">
<div class="accordion-body container">
<div class="row">
<div class="col-md-6">
{# Bonner Events #}
Expand Down Expand Up @@ -508,11 +511,28 @@ <h5>Notes</h5>
<h5>Requirement Progress</h5>
<ul>
{% for req in bonnerRequirements %}
<li>{{req.name}}
{% if req.completed %}
<li>
<span {% if req.frequency == "term"%}
data-bs-toggle="tooltip"
data-bs-placement="top"
title="{% if req.attendedTerms %}You have attended these terms: {{ req.attendedDescriptions | join(', ') }}{%else%} You haven't attended any terms {% endif %}"
{% endif %}>
{{req.name}}
</span>
{% if req.frequency == "term"%}
{% for a in range(req.missedTerms) %}
<span class="bi bi-x fs-3"></span>
{% endfor %}
{% for i in range(req.attendedTerms) %}
<span class="bi bi-check fs-3 text-success"></span>
{% endfor %}
{% elif req.completed %}
<span class="bi bi-check fs-3 text-success"></span>
{% else %}
{% elif req.completed == False and req.frequency == "once" %}
<span class="bi bi-x fs-3"></span>
{% elif req.completed == False and req.frequency == "twice" %}
<span class="bi bi-x fs-3"></span>
<span class="bi bi-x fs-3"></span>
{% endif %}
</li>
{% endfor %}
Expand All @@ -522,6 +542,7 @@ <h5>Requirement Progress</h5>
</div>
</div>
</div>
</div>
{% endif %}
<!-- ################# Bonner Scholar ################ -->
</div>
Expand Down
Loading