Skip to content
Open
Show file tree
Hide file tree
Changes from all 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 @@ -323,7 +323,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
2 changes: 0 additions & 2 deletions app/controllers/main/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,9 +221,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
157 changes: 132 additions & 25 deletions app/logic/certification.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,95 @@
from peewee import JOIN, DoesNotExist, Case

from peewee import JOIN, fn, 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
import math
def termsAttended(certification, username):
'''
Retrieve terms attended by a user for certification and filter them based on frequency of a term
'''
attendedTerms = []
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)):
if not attendance[termRecord].event.term.isSummer:
attendedTerms.append(attendance[termRecord].event.term.description)
totalTerms = termsInTotal(username)
attendedTerms = {term for term in attendedTerms if term in totalTerms}
return attendedTerms


def termsInTotal(username):
'''
The function returns all non-summer academic terms a student should have, based on their class level where it finds
the start term and populate from it with Fall-start alignment and special handling for NULL/Non-degree class level
'''
currentTerm = g.current_term
currentDesc = currentTerm.description
user = User.select().where(User.username == username).get()
if currentTerm.isSummer and user.rawClassLevel == "Freshman":
currentDesc = f"Fall {currentTerm.year}"
elif currentTerm.isSummer:
currentDesc = f"Spring {currentTerm.year}"
classLevel = ["Freshman", "Sophomore", "Junior", "Senior"]
totalTerms = []
for level, name in enumerate(classLevel):
if user.rawClassLevel == name:
totalTermsCount = (level + 1) * 2
if currentDesc.startswith("Fall"):
totalTermsCount -= 1
if currentDesc.startswith("Spring"):
startYear = currentTerm.year - level - 1
else:
startYear = currentTerm.year - level
for k in range(totalTermsCount):
if k % 2 == 0:
season = "Fall"
year = startYear + (k // 2)
else:
season = "Spring"
year = startYear + (k // 2) + 1
totalTerms.append(f"{season} {year}")
break

if user.rawClassLevel is None or user.rawClassLevel in ["NULL", "Graduating", "Non-Degree"]:
totalTermsCount = 8
currentYear = currentTerm.year
currentSeason = "Fall" if "Fall" in currentDesc else "Spring"
for a in range(totalTermsCount):
totalTerms.append(f"{currentSeason} {currentYear}")
if currentSeason == "Fall":
currentSeason = "Spring"
else:
currentSeason = "Fall"
currentYear -= 1
list.reverse(totalTerms)
return totalTerms

def termsMissed(certification, username):
'''
Calculate how many certification-eligible terms a student has missed based on their class level
and attendance record.
'''
totalTerms = termsInTotal(username)
attendedTerms = termsAttended(certification, username)
missedTerms = [term for term in totalTerms if term not in 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)
return getCertRequirements(certification, username, reqCheck=True)

def getCertRequirements(certification=None, username=None):
def getCertRequirements(certification=None, username=None, reqCheck=False):
"""
Return the requirements for all certifications, or for one if requested.

Expand All @@ -28,7 +105,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 @@ -37,32 +113,63 @@ def getCertRequirements(certification=None, username=None):
.select(Certification, CertificationRequirement, completedCase.alias("completed"))
.join(CertificationRequirement, JOIN.LEFT_OUTER, attr="requirement")
.join(RequirementMatch, JOIN.LEFT_OUTER)
.join(EventParticipant, JOIN.LEFT_OUTER, on=(RequirementMatch.event == EventParticipant.event))
.where(EventParticipant.user.is_null(True) | (EventParticipant.user == username))
.join(EventParticipant, JOIN.LEFT_OUTER, on=(RequirementMatch.event == EventParticipant.event) & (EventParticipant.user == username))
.order_by(Certification.id, CertificationRequirement.order.asc(nulls="LAST")))

# 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]

certs = {}
# this is to get the calculation when it comes to events with term, twice, annual as their frequency
cert.requirement.attendedTerms = len(termsAttended(cert.requirement.id, username))
cert.requirement.attendedDescriptions = termsAttended(cert.requirement.id, username)
if cert.requirement.frequency == "term":
cert.requirement.missedTerms = len(termsMissed(cert.requirement.id, username))
cert.requirement.missedDescriptions = termsMissed(cert.requirement.id, username)
cert.requirement.totalTerms = len(termsInTotal(username))
elif cert.requirement.frequency == "annual":
totalTerms = len(termsInTotal(username))
cert.requirement.attendedAnnual = len(termsAttended(cert.requirement.id, username))
cert.requirement.totalAnnual = int(math.floor(totalTerms/2+0.5)) if totalTerms % 2 == 1 else totalTerms/2
elif cert.requirement.frequency == "once" and cert.requirement.completed:
term_record = (RequirementMatch
.select(RequirementMatch, Event, Term)
.join(Event)
.join(Term)
.where(RequirementMatch.requirement == cert.requirement.id)
.order_by(Term.year.desc()) # latest term first
.first()
)
cert.requirement.attendedTerm = term_record.event.term.description
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:
req = certificationList[certificationIndex]
if req not in validCertification:
validCertification.add(req)
uniqueCertification.append(req)
# Override incomplete requirement when a completed 'once' requirement is found when removing duplicates
elif reqCheck and req.frequency == "once" and req.completed:
for i in range(len(uniqueCertification)):
if uniqueCertification[i].id == req.id and not uniqueCertification[i].completed:
uniqueCertification[i] = req
validCertification.add(req)
certificationIndex += 1
certificationList = uniqueCertification
return certificationList
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
2 changes: 1 addition & 1 deletion app/logic/volunteerSpreadsheet.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ def getRetentionRate(academicYear):

def termParticipation(term):
base = getBaseQuery(term.academicYear)

participationQuery = (base.select(Event.program, EventParticipant.user_id.alias('participant'), Program.programName.alias("programName"))
.where(Event.term == term)
.order_by(EventParticipant.user))
Expand Down
42 changes: 28 additions & 14 deletions app/static/js/userProfile.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
$(document).ready(function(){

$("#checkDietRestriction").on("change", function() {
$("#checkDietRestriction").on("change", function() {
let norestrict = $(this).is(':checked');
if (norestrict) {
$("#dietContainer").hide();
Expand Down Expand Up @@ -35,7 +35,6 @@ $(document).ready(function(){
});
})

$("#phoneInput").inputmask('(999)-999-9999');
$(".notifyInput").click(function updateInterest(){
var programID = $(this).data("programid");
var username = $(this).data('username');
Expand Down Expand Up @@ -216,6 +215,8 @@ $(document).ready(function(){
}
});
});
});


$(".deleteNoteButton").click(function() {
let username = $(this).data('username')
Expand Down Expand Up @@ -307,17 +308,27 @@ $(document).ready(function(){
})
});

// Popover functionality
var requiredTraining = $(".trainingPopover");
requiredTraining.popover({
trigger: "hover",
sanitize: false,
html: true,
content: function() {
return $(this).attr('data-content');
}
$(function () {
$('.trainingPopover').each(function () {
new bootstrap.Popover(this, {
trigger: 'hover focus',
html: true,
sanitize: false,
placement: 'right',
});
});
});

$(function () {
$('.bonnerCheckmark').each(function () {
new bootstrap.Popover(this, {
trigger: 'hover focus',
html: true,
sanitize: false,
placement: 'right',
});
});
});

setupPhoneNumber("#updatePhone", "#phoneInput")

// Dietary Restrictions
Expand Down Expand Up @@ -355,8 +366,10 @@ $(document).ready(function(){
typingTimer = setTimeout(saveDiet, saveInterval);
});
});

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

// Update program manager status
function updateManagers(el, volunteerUsername ) {
Expand Down Expand Up @@ -387,3 +400,4 @@ function updateManagers(el, volunteerUsername ) {
}
})
}

1 change: 1 addition & 0 deletions app/templates/admin/bonnerManagement.html
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ <h3 class="accordion-header" id="headingThree">
<td>
<select class="flex-column form-select empty frequency-select" name="frequency_{{req.id}}">
<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
Loading