From 7b9ff0ce080877d1d529810f9757c2865ce47720 Mon Sep 17 00:00:00 2001 From: bledsoef Date: Wed, 23 Apr 2025 23:31:07 -0400 Subject: [PATCH 1/2] implemented global graduation student hiding --- app/controllers/admin/graduationManagement.py | 22 ++++++++++--------- app/controllers/admin/userManagement.py | 10 +++++++-- app/controllers/main/routes.py | 5 +++-- app/logic/graduationManagement.py | 11 +++++++++- app/logic/minor.py | 15 ++++++++----- app/logic/searchUsers.py | 11 ++++++++-- app/logic/utils.py | 10 +++++++++ app/models/user.py | 1 + app/static/js/bonnerManagement.js | 2 +- app/static/js/createEvents.js | 2 +- app/static/js/manageVolunteers.js | 2 +- app/static/js/minorAdminPage.js | 2 +- app/static/js/searchStudent.js | 2 +- app/static/js/searchUser.js | 11 ++++++++-- app/static/js/slcManagement.js | 2 +- app/static/js/slcNewProposal.js | 2 +- app/static/js/userManagement.js | 6 ++--- app/static/js/userProfile.js | 21 ++++++++++++++++++ app/templates/main/userProfile.html | 7 +++++- tests/code/test_graduationManagement.py | 16 ++++++++++++-- 20 files changed, 123 insertions(+), 37 deletions(-) diff --git a/app/controllers/admin/graduationManagement.py b/app/controllers/admin/graduationManagement.py index 14dcf9316..c1e819212 100644 --- a/app/controllers/admin/graduationManagement.py +++ b/app/controllers/admin/graduationManagement.py @@ -2,7 +2,7 @@ from app.controllers.admin import admin_bp from app.logic.bonner import getBonnerCohorts -from app.logic.graduationManagement import setGraduatedStatus, getGraduationManagementUsers +from app.logic.graduationManagement import setGraduatedStatus, getGraduationManagementUsers, updateHideGraduatedStudents @admin_bp.route('/admin/graduationManagement', methods=['GET']) @@ -19,18 +19,20 @@ def graduationManagement(): @admin_bp.route('//setGraduationStatus/', methods=['POST']) def setGraduationStatus(username): - """ - This function - username: unique value of a user to correctly identify them - """ if not g.current_user.isAdmin: abort(403) - try: - status = request.form["status"] - setGraduatedStatus(username, status) + status = request.form["status"] + setGraduatedStatus(username, status) + + return "" - except Exception as e: - print(e) +@admin_bp.route("/admin/hideGraduatedStudents/", methods=["POST"]) +def hideGraduatedStudents(username): + if g.current_user.isStudent: + abort(403) + checked = request.form["checked"] + updateHideGraduatedStudents(username, checked) + return "" diff --git a/app/controllers/admin/userManagement.py b/app/controllers/admin/userManagement.py index ad8cc333d..daa2ab164 100644 --- a/app/controllers/admin/userManagement.py +++ b/app/controllers/admin/userManagement.py @@ -90,8 +90,14 @@ def userManagement(): .group_by(Program.id) ) - currentAdmins = list(User.select().where(User.isCeltsAdmin)) - currentStudentStaff = list(User.select().where(User.isCeltsStudentStaff)) + # hide graduated students if the user has indicated it + hideGraduatedStudents = User.get(username=g.current_user).hideGraduatedStudents + hideGraduatedStudentsWhere = True + if hideGraduatedStudents: + hideGraduatedStudentsWhere = (User.hasGraduated == False) + + currentAdmins = list(User.select().where(User.isCeltsAdmin, hideGraduatedStudentsWhere)) + currentStudentStaff = list(User.select().where(User.isCeltsStudentStaff, hideGraduatedStudentsWhere)) if g.current_user.isCeltsAdmin: return render_template('admin/userManagement.html', terms = terms, diff --git a/app/controllers/main/routes.py b/app/controllers/main/routes.py index 8194fa79e..927578de2 100644 --- a/app/controllers/main/routes.py +++ b/app/controllers/main/routes.py @@ -544,14 +544,15 @@ def updateTranscript(username, program_id): @main_bp.route('/searchUser/', methods = ['GET']) def searchUser(query): - category= request.args.get("category") + category = request.args.get("category") + alwaysShowGraduatedStudents = int(request.args.get("alwaysShowGraduatedStudents", 0)) '''Accepts user input and queries the database returning results that matches user search''' try: query = query.strip() search = query.upper() splitSearch = search.split() - searchResults = searchUsers(query,category) + searchResults = searchUsers(query, category, alwaysShowGraduatedStudents) return searchResults except Exception as e: print(e) diff --git a/app/logic/graduationManagement.py b/app/logic/graduationManagement.py index b11487894..5abb9f472 100644 --- a/app/logic/graduationManagement.py +++ b/app/logic/graduationManagement.py @@ -39,4 +39,13 @@ def setGraduatedStatus(username, status): gradStudent.hasGraduated = int(status) gradStudent.save() - \ No newline at end of file + +def updateHideGraduatedStudents(username, checked): + user = User.get(User.username == username) + + # it is necessary we cast this to an int instead of a bool because the + # status is passed as a string and if we cast it to a bool it will always be True + user.hideGraduatedStudents = int(checked) + + user.save() + diff --git a/app/logic/minor.py b/app/logic/minor.py index cb3af106a..1e1911b77 100644 --- a/app/logic/minor.py +++ b/app/logic/minor.py @@ -18,11 +18,11 @@ from app.models.individualRequirement import IndividualRequirement from app.models.certificationRequirement import CertificationRequirement from app.models.cceMinorProposal import CCEMinorProposal +from app.models.attachmentUpload import AttachmentUpload from app.logic.createLogs import createActivityLog from app.logic.fileHandler import FileHandler from app.logic.serviceLearningCourses import deleteCourseObject -from app.models.attachmentUpload import AttachmentUpload - +from app.logic.utils import getHideGraduatedStudentsWhereClause def createSummerExperience(username, formData): """ @@ -74,9 +74,11 @@ def getMinorInterest() -> List[Dict]: """ Get all students that have indicated interest in the CCE minor and return a list of dicts of all interested students """ + hideGraduatedStudentsWhere = getHideGraduatedStudentsWhereClause(g.current_user) + interestedStudents = (User.select(User) .join(IndividualRequirement, JOIN.LEFT_OUTER, on=(User.username == IndividualRequirement.username)) - .where(User.isStudent & User.minorInterest & ~User.declaredMinor & IndividualRequirement.username.is_null(True))) + .where(User.isStudent & User.minorInterest & ~User.declaredMinor & IndividualRequirement.username.is_null(True), hideGraduatedStudentsWhere)) interestedStudentList = [model_to_dict(student) for student in interestedStudents] @@ -88,6 +90,8 @@ def getMinorProgress(): and returns a list of dicts containing the student, how many engagements they have completed, and if they have completed the summer experience. """ + hideGraduatedStudentsWhere = getHideGraduatedStudentsWhereClause(g.current_user) + summerCase = Case(None, [(CCEMinorProposal.proposalType == "Summer Experience", 1)], 0) engagedStudentsWithCount = ( @@ -97,7 +101,7 @@ def getMinorProgress(): .join(IndividualRequirement, on=(User.username == IndividualRequirement.username)) .join(CertificationRequirement, on=(IndividualRequirement.requirement_id == CertificationRequirement.id)) .switch(User).join(CCEMinorProposal, JOIN.LEFT_OUTER, on= (User.username == CCEMinorProposal.student)) - .where(CertificationRequirement.certification_id == Certification.CCE) + .where(CertificationRequirement.certification_id == Certification.CCE, hideGraduatedStudentsWhere) .group_by(User.firstName, User.lastName, User.username) .order_by(SQL("engagementCount").desc()) ) @@ -186,7 +190,8 @@ def getDeclaredMinorStudents(): """ Get a list of the students who have declared minor """ - declaredStudents = User.select().where(User.isStudent & User.minorInterest & User.declaredMinor) + hideGraduatedStudentsWhere = getHideGraduatedStudentsWhereClause(g.current_user) + declaredStudents = User.select().where(User.isStudent & User.minorInterest & User.declaredMinor, hideGraduatedStudentsWhere) interestedStudentList = [model_to_dict(student) for student in declaredStudents] diff --git a/app/logic/searchUsers.py b/app/logic/searchUsers.py index 92ee2c76a..8c56c7426 100644 --- a/app/logic/searchUsers.py +++ b/app/logic/searchUsers.py @@ -1,6 +1,7 @@ from playhouse.shortcuts import model_to_dict +from flask import g from app.models.user import User -def searchUsers(query, category=None): +def searchUsers(query, category=None, alwaysShowGraduatedStudents=False): ''' Search the User table based on the search query and category @@ -11,6 +12,12 @@ def searchUsers(query, category=None): firstName = splitSearch[0] + "%" lastName = " ".join(splitSearch[1:]) +"%" + excludeGraduatedStudentsWhere = True + # this is necessary if they just recently changed this and it hasn't updated yet, maybe? + currentUser = User.get(username=g.current_user) + if (currentUser.hideGraduatedStudents) and (not alwaysShowGraduatedStudents): + excludeGraduatedStudentsWhere = (User.hasGraduated == False) + if len(splitSearch) == 1: # search for query in first OR last name searchWhere = (User.firstName ** firstName | User.lastName ** firstName | User.username ** splitSearch) else: # search for first AND last name @@ -30,6 +37,6 @@ def searchUsers(query, category=None): userWhere = (User.isStudent) # Combine into query - searchResults = User.select().where(searchWhere, userWhere) + searchResults = User.select().where(searchWhere, userWhere, excludeGraduatedStudentsWhere) return { user.username : model_to_dict(user) for user in searchResults } diff --git a/app/logic/utils.py b/app/logic/utils.py index 87e5c3f0e..d4ad934af 100644 --- a/app/logic/utils.py +++ b/app/logic/utils.py @@ -5,6 +5,7 @@ from peewee import DoesNotExist from app.models.term import Term +from app.models.user import User def selectSurroundingTerms(currentTerm, prevTerms=2, summerOnly=False): """ @@ -98,3 +99,12 @@ def setRedirectTarget(target): """ session["redirectTarget"] = target +def getHideGraduatedStudentsWhereClause(username): + + # hide graduated students if the user has indicated it + hideGraduatedStudents = User.get(username=username).hideGraduatedStudents + hideGraduatedStudentsWhere = True + if hideGraduatedStudents: + hideGraduatedStudentsWhere = (User.hasGraduated == False) + + return hideGraduatedStudentsWhere \ No newline at end of file diff --git a/app/models/user.py b/app/models/user.py index 9bb1e5e80..911ef515e 100644 --- a/app/models/user.py +++ b/app/models/user.py @@ -20,6 +20,7 @@ class User(baseModel): minorInterest = BooleanField(default=False) hasGraduated = BooleanField(default=False) declaredMinor = BooleanField(default=False) + hideGraduatedStudents = BooleanField(default=False) # override BaseModel's __init__ so that we can set up an instance attribute for cache def __init__(self,*args, **kwargs): diff --git a/app/static/js/bonnerManagement.js b/app/static/js/bonnerManagement.js index 53d271d57..66952e5a5 100644 --- a/app/static/js/bonnerManagement.js +++ b/app/static/js/bonnerManagement.js @@ -17,7 +17,7 @@ function cohortRequest(year, method, username){ function addSearchCapabilities(inputElement){ $(inputElement).on("input", function(){ let year = $(this).data('year'); - searchUser(this.id, student => cohortRequest(year, "add", student.username), false, null, "student"); + searchUser(this.id, student => cohortRequest(year, "add", student.username), false, false, null, "student"); }); } diff --git a/app/static/js/createEvents.js b/app/static/js/createEvents.js index c81c758fc..2571332c7 100644 --- a/app/static/js/createEvents.js +++ b/app/static/js/createEvents.js @@ -606,7 +606,7 @@ $(document).ready(function() { } $("#eventFacilitator").on('input', function () { - searchUser("eventFacilitator", callback, true, undefined, "instructor"); + searchUser("eventFacilitator", callback, false, true, undefined, "instructor"); }); $("#facilitatorTable").on("click", "#remove", function () { diff --git a/app/static/js/manageVolunteers.js b/app/static/js/manageVolunteers.js index b0f7e7814..540f9a9e1 100644 --- a/app/static/js/manageVolunteers.js +++ b/app/static/js/manageVolunteers.js @@ -127,7 +127,7 @@ $(document).ready(function() { }); $("#addVolunteerInput").on("input", function() { - searchUser("addVolunteerInput", callback, true, "addVolunteerModal"); + searchUser("addVolunteerInput", callback, false, true, "addVolunteerModal"); }); diff --git a/app/static/js/minorAdminPage.js b/app/static/js/minorAdminPage.js index d3e02cb2f..015f3bd6d 100644 --- a/app/static/js/minorAdminPage.js +++ b/app/static/js/minorAdminPage.js @@ -140,6 +140,6 @@ $("#addInterestedStudentsModal").on("shown.bs.modal", function() { }); $("#addStudentInput").on("input", function() { -searchUser("addStudentInput", callback, true, "addInterestedStudentsModal"); +searchUser("addStudentInput", callback, false, true, "addInterestedStudentsModal"); }); diff --git a/app/static/js/searchStudent.js b/app/static/js/searchStudent.js index 93062f6de..b5b70a194 100644 --- a/app/static/js/searchStudent.js +++ b/app/static/js/searchStudent.js @@ -4,7 +4,7 @@ function callback(selected) { } $(document).ready(function() { $("#searchStudentsInput").on("input", function() { - searchUser("searchStudentsInput", callback); + searchUser("searchStudentsInput", callback, true); }); $("#searchIcon").click(function (e) { diff --git a/app/static/js/searchUser.js b/app/static/js/searchUser.js index 5ebf57c70..db91cf860 100644 --- a/app/static/js/searchUser.js +++ b/app/static/js/searchUser.js @@ -1,4 +1,11 @@ -export default function searchUser(inputId, callback, clear=false, parentElementId=null, category = null) +export default function searchUser( + inputId, + callback, + alwaysShowGraduatedStudents=false, + clear=false, + parentElementId=null, + category=null, +) { var query = $(`#${inputId}`).val() let columnDict = {}; @@ -10,7 +17,7 @@ export default function searchUser(inputId, callback, clear=false, parentElement url: `/searchUser/${query}`, type: "GET", dataType: "json", - data:{"category":category}, + data:{"category":category, "alwaysShowGraduatedStudents": alwaysShowGraduatedStudents ? 1 : 0}, success: function(searchResults) { response(Object.entries(searchResults).map( (item) => { return { diff --git a/app/static/js/slcManagement.js b/app/static/js/slcManagement.js index 3a4b0bf7e..59f9be31b 100644 --- a/app/static/js/slcManagement.js +++ b/app/static/js/slcManagement.js @@ -50,7 +50,7 @@ $(document).ready(function() { }); $("#courseInstructor").on('input', function() { - searchUser("courseInstructor", createNewRow, true, null, "instructor"); + searchUser("courseInstructor", createNewRow, false, true, null, "instructor"); setTimeout(function() { $(".ui-autocomplete").css("z-index", 9999); }, 500); diff --git a/app/static/js/slcNewProposal.js b/app/static/js/slcNewProposal.js index e9ee642c4..0da76cd6f 100644 --- a/app/static/js/slcNewProposal.js +++ b/app/static/js/slcNewProposal.js @@ -144,7 +144,7 @@ $(document).ready(function(e) { }) $("#courseInstructor").on('input', function() { - searchUser("courseInstructor", createNewRow, true, null, "instructor"); + searchUser("courseInstructor", createNewRow, false, true, null, "instructor"); }); $("#courseInstructor").popover({ diff --git a/app/static/js/userManagement.js b/app/static/js/userManagement.js index 326ac2ada..1222be422 100644 --- a/app/static/js/userManagement.js +++ b/app/static/js/userManagement.js @@ -29,13 +29,13 @@ function callbackProgramManager(selected, action = 'add') { $(document).ready(function(){ // Admin Management $("#searchCeltsAdminInput").on("input", function(){ - searchUser("searchCeltsAdminInput", callbackAdmin, false, null, "celtsLinkAdmin") + searchUser("searchCeltsAdminInput", callbackAdmin, false, false, null, "celtsLinkAdmin") }); $("#searchCeltsStudentStaffInput").on("input", function(){ - searchUser("searchCeltsStudentStaffInput", callbackStudentStaff, false, null, "student") + searchUser("searchCeltsStudentStaffInput", callbackStudentStaff, false, false, null, "student") }); $("#searchProgramManagersInput").on("input", function() { - searchUser("searchProgramManagersInput", callbackProgramManager, true, "parentManager", "all"); + searchUser("searchProgramManagersInput", callbackProgramManager, false, true, "parentManager", "all"); }); $("#addNewTerm").on("click",function(){ addNewTerm(); diff --git a/app/static/js/userProfile.js b/app/static/js/userProfile.js index e5c459caa..79e59e771 100644 --- a/app/static/js/userProfile.js +++ b/app/static/js/userProfile.js @@ -44,6 +44,27 @@ $(document).ready(function(){ }); }); + $('#hideGraduatedStudents').click(function() { + let checked = $(this).is(":checked") + var username = $(this).data('username'); + $.ajax({ + data: {checked: checked ? 1 : 0}, + method: "POST", + url: `/admin/hideGraduatedStudents/${username}`, + success: function(response) { + if (checked == true) { + msgFlash(`Graduated students hidden on CELTS Link for ${username}.`, "success", 1000) + } else { + msgFlash(`Graduated students shown on CELTS Link for ${username}.`, "success", 1000) + } + }, + error: function(request, status, error) { + console.error("Error hiding graduated students:", error); + msgFlash(`Error hiding graduated students for ${username}.`) + } + }); + }) + $('.onTranscriptCheckbox').click(function() { var onTranscript = $(this).is(':checked'); var username = $(this).data('username'); diff --git a/app/templates/main/userProfile.html b/app/templates/main/userProfile.html index 91831a07f..91928fd59 100644 --- a/app/templates/main/userProfile.html +++ b/app/templates/main/userProfile.html @@ -56,9 +56,14 @@

{{volunteer.firstName}} {{volunteer.lastName}}

-
+ {% if (volunteer == g.current_user) and (not g.current_user.isStudent) %} +
+ + +
+ {% endif %}