diff --git a/app/controllers/main/routes.py b/app/controllers/main/routes.py index 51e1b77b3..df9c6797f 100644 --- a/app/controllers/main/routes.py +++ b/app/controllers/main/routes.py @@ -57,7 +57,7 @@ def landingPage(): # Limit returned list to events in the future futureEvents = [p for p in programsWithEventsList if not p.event.isPastEnd] - return render_template("/main/landingPage.html", + return render_template("/main/landingPage.html", managerProgramDict=managerProgramDict, term=g.current_term, programsWithEventsList = futureEvents) @@ -77,20 +77,20 @@ def events(selectedTerm, activeTab, programID): if selectedTerm: currentTerm = selectedTerm currentTime = datetime.datetime.now() - + listOfTerms = Term.select() participantRSVP = EventRsvp.select(EventRsvp, Event).join(Event).where(EventRsvp.user == g.current_user) rsvpedEventsID = [event.event.id for event in participantRSVP] - term: Term = Term.get_by_id(currentTerm) - + term: Term = Term.get_by_id(currentTerm) + currentEventRsvpAmount = getEventRsvpCountsForTerm(term) studentLedEvents = getStudentLedEvents(term) countUpcomingStudentLedEvents = getUpcomingStudentLedCount(term, currentTime) trainingEvents = getTrainingEvents(term, g.current_user) bonnerEvents = getBonnerEvents(term) otherEvents = getOtherEvents(term) - + managersProgramDict = getManagerProgramDict(g.current_user) # Fetch toggle state from session @@ -189,7 +189,7 @@ def viewUsersProfile(username): ProgramBan.program == program, ProgramBan.endDate > datetime.datetime.now()).execute()) userParticipatedTrainingEvents = getParticipationStatusForTrainings(program, [volunteer], g.current_term) - try: + try: allTrainingsComplete = False not in [attended for event, attended in userParticipatedTrainingEvents[username]] # Did volunteer attend all events except KeyError: allTrainingsComplete = False @@ -245,7 +245,7 @@ def emergencyContactInfo(username): contactInfo=contactInfo, readOnly=readOnly ) - + elif request.method == 'POST': if g.current_user.username != username: abort(403) @@ -253,6 +253,7 @@ def emergencyContactInfo(username): rowsUpdated = EmergencyContact.update(**request.form).where(EmergencyContact.user == username).execute() if not rowsUpdated: EmergencyContact.create(user = username, **request.form) + createActivityLog(f"{g.current_user.fullName} updated {user.fullName}'s emergency contact information.") flash('Emergency contact information saved successfully!', 'success') @@ -288,6 +289,7 @@ def insuranceInfo(username): rowsUpdated = InsuranceInfo.update(**request.form).where(InsuranceInfo.user == username).execute() if not rowsUpdated: InsuranceInfo.create(user = username, **request.form) + createActivityLog(f"{g.current_user.fullName} updated {user.fullName}'s insurance information.") flash('Insurance information saved successfully!', 'success') @@ -300,7 +302,7 @@ def insuranceInfo(username): def travelForm(username): if not (g.current_user.username == username or g.current_user.isCeltsAdmin): abort(403) - + user = (User.select(User, EmergencyContact, InsuranceInfo) .join(EmergencyContact, JOIN.LEFT_OUTER).switch() .join(InsuranceInfo, JOIN.LEFT_OUTER) @@ -354,8 +356,9 @@ def ban(program_id, username): postData = request.form banNote = postData["note"] # This contains the note left about the change banEndDate = postData["endDate"] # Contains the date the ban will no longer be effective + toRemoveFromTranscript = postData["removeFromTranscript"] =='true' try: - banUser(program_id, username, banNote, banEndDate, g.current_user) + banUser(program_id, username, banNote, banEndDate, toRemoveFromTranscript, g.current_user) programInfo = Program.get(int(program_id)) flash("Successfully banned the volunteer", "success") createActivityLog(f'Banned {username} from {programInfo.programName} until {banEndDate}.') @@ -495,6 +498,47 @@ def serviceTranscript(username): startDate = startDate, userData = user) +@main_bp.route('/profile//removeFromTranscript/', methods=['GET']) +def isRemovedFromTranscript(username, program_id): + user = User.get_or_none(User.username == username) + if user is None: + abort(404) + + try: + bannedProgramsForUser = ProgramBan.get((ProgramBan.program == program_id) & (ProgramBan.user == user)) + # If the user is banned, check if it's marked for removal from transcript + if bannedProgramsForUser.removeFromTranscript: + return jsonify({'removedFromTranscript': True}) + else: + return jsonify({'removedFromTranscript': False}) + except ProgramBan.DoesNotExist: + return jsonify({'status': 'error', 'message': 'ProgramBan not found'}) + +@main_bp.route('/profile//updateTranscript/', methods=['POST']) +def updateTranscript(username, program_id): + # Check user permissions + user = User.get_or_none(User.username == username) + if user is None: + abort(404) + if user != g.current_user and not g.current_user.isAdmin: + abort(403) + + # Get the data sent from the client-side JavaScript + data = request.json + + # Retrieve removeFromTranscript value from the request data + removeFromTranscript = data.get('removeFromTranscript') + + # Update the ProgramBan object matching the program_id and username + try: + bannedProgramsForUser = ProgramBan.get((ProgramBan.program == program_id) & (ProgramBan.user == user)) + bannedProgramsForUser.removeFromTranscript = removeFromTranscript + bannedProgramsForUser.save() + return jsonify({'status': 'success'}) + except ProgramBan.DoesNotExist: + return jsonify({'status': 'error', 'message': 'ProgramBan not found'}) + + @main_bp.route('/searchUser/', methods = ['GET']) def searchUser(query): @@ -520,10 +564,10 @@ def getDietInfo(): dietaryInfo = request.form user = dietaryInfo["user"] dietInfo = dietaryInfo["dietInfo"] - + if (g.current_user.username == user) or g.current_user.isAdmin: updateDietInfo(user, dietInfo) - userInfo = User.get(User.username == user) + userInfo = User.get(User.username == user) if len(dietInfo) > 0: createActivityLog(f"Updated {userInfo.fullName}'s dietary restrictions to {dietInfo}.") if dietInfo.strip() else None else: diff --git a/app/logic/transcript.py b/app/logic/transcript.py index 7dfb75cf8..b8c6d3be4 100644 --- a/app/logic/transcript.py +++ b/app/logic/transcript.py @@ -1,8 +1,10 @@ from peewee import fn +from collections import defaultdict from app.models.course import Course from app.models.courseParticipant import CourseParticipant from app.models.program import Program +from app.models.programBan import ProgramBan from app.models.courseInstructor import CourseInstructor from app.models.user import User from app.models.term import Term @@ -12,23 +14,30 @@ def getProgramTranscript(username): """ Returns a Program query object containing all the programs for - current user. + the current user. """ # Add up hours earned in a term for each program they've participated in - + EventData = (Event.select(Event, fn.SUM(EventParticipant.hoursEarned).alias("hoursEarned")) .join(EventParticipant) .where(EventParticipant.user == username) .group_by(Event.program, Event.term) .order_by(Event.term) .having(fn.SUM(EventParticipant.hoursEarned > 0))) - transcriptData = {} + + # Fetch all ProgramBan objects for the user + bannedProgramsForParticipant = ProgramBan.select().where(ProgramBan.user == username) + + # Create a set of program IDs to remove from transcript + programsToRemoveFromTranscript = {bannedProgram.program_id for bannedProgram in bannedProgramsForParticipant if bannedProgram.removeFromTranscript} + transcriptData = defaultdict(list) + + # Iterate through EventData and populate transcriptData for event in EventData: - if event.program in transcriptData: + if event.program.id not in programsToRemoveFromTranscript: # Check if program is not in programs to be removed from transcript transcriptData[event.program].append([event.term.description, event.hoursEarned]) - else: - transcriptData[event.program] = [[event.term.description, event.hoursEarned]] - return transcriptData + + return dict(transcriptData) def getSlCourseTranscript(username): """ diff --git a/app/logic/users.py b/app/logic/users.py index 5982e535c..01035cc63 100644 --- a/app/logic/users.py +++ b/app/logic/users.py @@ -5,6 +5,7 @@ from app.models.note import Note from app.models.user import User from app.models.profileNote import ProfileNote +from app.models.programBan import ProgramBan from app.models.backgroundCheck import BackgroundCheck from app.models.backgroundCheckType import BackgroundCheckType from app.logic.volunteers import addUserBackgroundCheck @@ -65,7 +66,7 @@ def isBannedFromEvent(username, eventId): user = User.get(User.username == username) return not isEligibleForProgram(program, user) -def banUser(program_id, username, note, banEndDate, creator): +def banUser(program_id, username, note, banEndDate, toRemoveFromTranscript, creator): """ This function creates an entry in the note table and programBan table in order to ban the selected user. @@ -76,6 +77,7 @@ def banUser(program_id, username, note, banEndDate, creator): banEndDate: date when the ban will end creator: the admin or person with authority who created the ban """ + noteForDb = Note.create(createdBy = creator, createdOn = datetime.datetime.now(), noteContent = note, @@ -85,7 +87,8 @@ def banUser(program_id, username, note, banEndDate, creator): ProgramBan.create(program = program_id, user = username, endDate = banEndDate, - banNote = noteForDb) + banNote = noteForDb, + removeFromTranscript = toRemoveFromTranscript) def unbanUser(program_id, username, note, creator): """ @@ -103,7 +106,8 @@ def unbanUser(program_id, username, note, creator): isPrivate = 0, noteType = "unban") (ProgramBan.update(endDate = datetime.datetime.now(), - unbanNote = noteForDb) + unbanNote = noteForDb, + removeFromTranscript = 0) .where(ProgramBan.program == program_id, ProgramBan.user == username, ProgramBan.endDate > datetime.datetime.now())).execute() diff --git a/app/models/programBan.py b/app/models/programBan.py index 45a58d064..f94c6100d 100644 --- a/app/models/programBan.py +++ b/app/models/programBan.py @@ -9,3 +9,6 @@ class ProgramBan(baseModel): endDate = DateField(null=True) banNote = ForeignKeyField(Note, null=False) unbanNote = ForeignKeyField(Note, null=True) + removeFromTranscript = BooleanField(default=False) + + diff --git a/app/static/js/userProfile.js b/app/static/js/userProfile.js index 5e34eb594..bd04679f6 100644 --- a/app/static/js/userProfile.js +++ b/app/static/js/userProfile.js @@ -14,7 +14,7 @@ $(document).ready(function(){ msgToast("Error!", "Failed to save changes!") } }); - }) + }) $("#printButton").on("click", function() { let username = $(this).data('username') @@ -25,7 +25,7 @@ $(document).ready(function(){ $(".notifyInput").click(function updateInterest(){ var programID = $(this).data("programid"); var username = $(this).data('username'); - + var interest = $(this).is(':checked'); var routeUrl = interest ? "addInterest" : "removeInterest"; @@ -43,6 +43,26 @@ $(document).ready(function(){ }); }); + $('.removeFromTranscriptCheckbox').click(function() { + var removeFromTranscript = $(this).is(':checked'); + var username = $(this).data('username'); + + + $.ajax({ + type: "POST", + url: `/profile/${username}/updateTranscript/${programID}`, + contentType: "application/json", + data: JSON.stringify({ username: username, removeFromTranscript: removeFromTranscript, programID: programID }), + success: function(response) { + + }, + error: function(error) { + console.error("An error occurred:", error); + } + }); + }); + + function changeAction(e){ let profileAction = $(this).val() let username = $(this).data('username') @@ -74,33 +94,51 @@ $(document).ready(function(){ /* * Ban Functionality */ - $(".ban").click(function() { + + + var programID; + $(".banEdit").click(function() { + $.ajax({ + url: `/profile/${$(this).data("username")}/removeFromTranscript/${$(this).data("programid")}`, + type: "GET", + success: function(response) { + // Check if the program is marked for removal from transcript + $('#removeFromTranscriptCheckbox').prop('checked', response.removedFromTranscript) + }, + error: function(error, status) { + console.log(error, status); + } + }); + var banButton = $("#banButton") var banEndDateDiv = $("#banEndDate") // Div containing the datepicker in the ban modal var banEndDatepicker = $("#banEndDatepicker") // Datepicker in the ban modal var banNoteDiv = $("#banNoteDiv") // Div containing the note displaying why the user was banned previously - //Should only diplay when the modal is going to unban a user + banNoteDiv.hide(); //Should only diplay when the modal is going to unban a user var banNote = $("#banNote") + var banValue = $(this).val() - banButton.text($(this).val() + " Volunteer"); - banButton.data("programID", $(this).data("programid")) - banButton.data("username", $("#notifyInput").data("username")) - banButton.data("banOrUnban", $(this).val()); + banButton.text(banValue + " Volunteer"); + programID = $(this).data("programid"); // Assign value to programID variable + banButton.data("programID", programID) + banButton.data("username", $(".banEdit").data("username")) + banButton.data("banOrUnban", banValue); banEndDateDiv.show(); banEndDatepicker.val("") - $(".modal-title").text($(this).val() + " Volunteer"); + $(".modal-title-ban").text(banValue + " Volunteer"); $("#modalProgramName").text("Program: " + $(this).data("name ")); $("#banModal").modal("toggle"); - banNoteDiv.hide(); + // $("#removeFromTranscriptDiv").hide(); $("#banNoteTxtArea").val(""); $("#banButton").prop("disabled", true); - if( $(this).val()=="Unban"){ + if(banValue == "Unban"){ banEndDateDiv.hide() banEndDatepicker.val("0001-01-01") //This is a placeholder value for the if statement in line 52 to work properly #PLCHLD1 banNoteDiv.show() + // $("#removeFromTranscriptDiv").show(); banNote.text($(this).data("note")) } - + else {$('#removeFromTranscriptCheckbox').prop('checked', true);} }); @@ -114,11 +152,14 @@ $(document).ready(function(){ var username = $(this).data("username") //Expected to be the unique username of a user in the database var route = ($(this).data("banOrUnban")).toLowerCase() //Expected to be "ban" or "unban" var program = $(this).data("programID") //Expected to be a program's primary ID + var removeFromTranscriptState = $("#removeFromTranscriptCheckbox").is(':checked'); + $.ajax({ method: "POST", url: "/" + username + "/" + route + "/" + program, data: {"note": $("#banNoteTxtArea").val(), - "endDate":$("#banEndDatepicker").val() //Expected to be a date in this format YYYY-MM-DD + "endDate":$("#banEndDatepicker").val(), //Expected to be a date in this format YYYY-MM-DD + "removeFromTranscript": removeFromTranscriptState }, success: function(response) { reloadWithAccordion("programTable") @@ -148,9 +189,9 @@ $(document).ready(function(){ $("#noteModal").modal("toggle") }); - $("#addVisibility").click(function() { + $("#addVisibility").click(function() { var bonnerChecked = $("input[name='bonner']:checked").val() - + if (bonnerChecked == 'on') { bonnerNoteOn() } else { @@ -198,7 +239,7 @@ $(document).ready(function(){ * Background Check Functionality */ // Updates the Background check of a volunteer in the database - $(".savebtn").click(function () { + $(".savebtn").click(function () { $(this).prop("disabled", true); let bgCheckType = $(this).data("id") @@ -320,4 +361,3 @@ function updateManagers(el, volunteer_username ){// retrieve the data of the stu } }) } - diff --git a/app/templates/main/serviceTranscript.html b/app/templates/main/serviceTranscript.html index 2534bc09a..31e9d8f15 100644 --- a/app/templates/main/serviceTranscript.html +++ b/app/templates/main/serviceTranscript.html @@ -75,3 +75,5 @@
No Volunteer Record
{% endblock %} + + diff --git a/app/templates/main/userProfile.html b/app/templates/main/userProfile.html index 7ba6f103d..6cec373b5 100644 --- a/app/templates/main/userProfile.html +++ b/app/templates/main/userProfile.html @@ -45,7 +45,7 @@

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

{{volunteer.major}}
{% endif %} {% if volunteer.classLevel -%} -
{{volunteer.classLevel}}
+
{{volunteer.classLevel}}
{% endif %} {% if volunteer.cpoNumber %}
@@ -193,6 +193,7 @@

Training {% if g.current_user.isCeltsAdmin or g.current_user.isCeltsStudentStaff%} Eligibility + {% endif %} @@ -215,17 +216,18 @@

{% if g.current_user.isCeltsAdmin or g.current_user.isCeltsStudentStaff %} {% if row.isNotBanned %} - Eligible + Eligible {% set label = "Ban" %} {% else %} - Banned + Banned {% set label = "Unban" %} {% endif %} - + {% if g.current_user.isCeltsAdmin %} - + {% endif %} + {% endif %} {% endfor %} @@ -254,7 +256,7 @@

CELTS Labor History:
{% for program, term in participatedInLabor.items() %} -

{{term}}: {{program}}

+

{{term}}: {{program}}

{% endfor %}
{% endif %} @@ -536,9 +538,9 @@

- -
+ +
{% if g.current_user.isBonnerScholar and volunteer.isBonnerScholar %}
@@ -556,7 +558,7 @@
{% endif %}
- +
@@ -580,7 +582,7 @@