Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Selectively remove service transcript entry for banned programs #1176

Open
wants to merge 58 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
a1c0e85
push branch
gahimbaref Feb 5, 2024
3c4eb34
add checkbox in modal
gahimbaref Feb 5, 2024
6c848b4
Added functionality for removing banned users from transcript, still …
Karina-Agliullova Feb 7, 2024
fa020dd
add function to handle checkbox
gahimbaref Feb 9, 2024
d6ee173
fix conflicts
gahimbaref Feb 9, 2024
4b1d9ee
Fixed merge conflict
Karina-Agliullova Feb 13, 2024
793e87b
add remove from transcript field
gahimbaref Feb 19, 2024
5394bdb
merge development
gahimbaref Feb 19, 2024
3205cf7
Fixed logic
Karina-Agliullova Feb 21, 2024
f17f91b
fix checkbox functionality, only show on banned modals
gahimbaref Feb 23, 2024
f9506eb
remove print
gahimbaref Feb 23, 2024
e9248e4
Fixing merge conflicts
Karina-Agliullova Mar 5, 2024
c69653f
Fixing merge conflicts on transcript.py
Karina-Agliullova Mar 5, 2024
b5e9366
Made changes to keep checkbox consistent
Karina-Agliullova Mar 6, 2024
6f4a093
Added tests, modififed code to fix consistency issue
Karina-Agliullova Mar 11, 2024
6177f68
fix merge conflicts
gahimbaref Mar 11, 2024
c43cfd5
readd code to show/hide checkbox
gahimbaref Mar 11, 2024
22a3dc0
Fixing the checkbox
Karina-Agliullova Mar 19, 2024
33dfc1a
Fixing the checkbox frontend
Karina-Agliullova Mar 19, 2024
3237498
fix merge conflicts
gahimbaref Mar 21, 2024
a6ad471
merge development
gahimbaref Apr 2, 2024
463c029
fix merge conflicts
gahimbaref Apr 2, 2024
413c8cf
reset to working version
gahimbaref Apr 2, 2024
8d2b452
fix the way programs appear
gahimbaref Apr 2, 2024
a886752
merge development
gahimbaref Apr 8, 2024
d337fe5
pushed some sample code
bledsoef Apr 8, 2024
001562d
fix checkbox status issue
gahimbaref Apr 9, 2024
652b51b
merge development
gahimbaref Apr 9, 2024
158fa2f
Removed redundant code
Karina-Agliullova Apr 9, 2024
464921b
try to fix unnecessary line changes
gahimbaref Apr 9, 2024
31ec70e
update branch
gahimbaref Apr 9, 2024
49a9261
Merge branch 'development' into serviceTEntry658
AndersonStettner Apr 10, 2024
ca7b7cf
rename function
gahimbaref Apr 15, 2024
9b04efb
use camelcase
gahimbaref Apr 15, 2024
6a3d3e2
fix capitalization of a variable
gahimbaref Apr 15, 2024
d968653
variable name fix
gahimbaref Apr 15, 2024
8fa706e
Merge branch 'development' into serviceTEntry658
bledsoef Apr 17, 2024
4693c03
Fixed the comments
Karina-Agliullova Apr 19, 2024
2d32488
Fixed comments for userProfile.js
Karina-Agliullova Apr 22, 2024
02069b6
use defaultdict for efficiency
gahimbaref Apr 22, 2024
bf06345
Merge branch 'development' into serviceTEntry658
AndersonStettner May 8, 2024
f0c8e29
Merge branch 'development' into serviceTEntry658
bledsoef Aug 25, 2024
5b4d738
removed logging statements, chatgpt esc comments, and converted line …
bledsoef Aug 25, 2024
fb42817
resolved final PR comment
bledsoef Aug 25, 2024
cc96799
fixed bug where program doesnt appear on transcript after unbanning
bledsoef Aug 25, 2024
4e3695d
Merge branch 'development' into serviceTEntry658
bledsoef Aug 27, 2024
d8323d2
Merge branch 'development' into serviceTEntry658
bledsoef Aug 30, 2024
4711ada
Merge branch 'development' of https://github.com/BCStudentSoftwareDev…
ojmakinde Sep 13, 2024
512146a
Fixed boolean error in updating Program Ban
ojmakinde Sep 13, 2024
29c825f
Merge branch 'development' into serviceTEntry658
bledsoef Oct 11, 2024
1f9a4e4
added a new column to user profile
bledsoef Oct 14, 2024
b1e0a7d
Merge branch 'development' into serviceTEntry658
bledsoef Oct 17, 2024
d0e1dfa
???
ojmakinde Oct 21, 2024
750a8df
Anna, deleted the headings for edit and transcript and made the check…
vungc Oct 21, 2024
d67199f
Anna, changed edit into pencil icon and moved it next to eligibility …
vungc Oct 22, 2024
ca94830
Anna, checkbox in ban modal is checked by default. Still need to make…
vungc Oct 22, 2024
6530720
Merge branch 'development' of https://github.com/BCStudentSoftwareDev…
vungc Nov 5, 2024
f0fde03
Anna, removeFromTranscript status can now be saved into the table as …
vungc Nov 6, 2024
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
66 changes: 55 additions & 11 deletions app/controllers/main/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -245,14 +245,15 @@ def emergencyContactInfo(username):
contactInfo=contactInfo,
readOnly=readOnly
)

elif request.method == 'POST':
if g.current_user.username != username:
abort(403)

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')

Expand Down Expand Up @@ -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')

Expand All @@ -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)
Expand Down Expand Up @@ -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}.')
Expand Down Expand Up @@ -495,6 +498,47 @@ def serviceTranscript(username):
startDate = startDate,
userData = user)

@main_bp.route('/profile/<username>/removeFromTranscript/<program_id>', 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/<username>/updateTranscript/<program_id>', 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/<query>', methods = ['GET'])
def searchUser(query):

Expand All @@ -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:
Expand Down
23 changes: 16 additions & 7 deletions app/logic/transcript.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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):
"""
Expand Down
10 changes: 7 additions & 3 deletions app/logic/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand All @@ -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,
Expand All @@ -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):
"""
Expand All @@ -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()
Expand Down
3 changes: 3 additions & 0 deletions app/models/programBan.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)


74 changes: 57 additions & 17 deletions app/static/js/userProfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ $(document).ready(function(){
msgToast("Error!", "Failed to save changes!")
}
});
})
})

$("#printButton").on("click", function() {
let username = $(this).data('username')
Expand All @@ -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";
Expand All @@ -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')
Expand Down Expand Up @@ -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);}
});


Expand All @@ -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")
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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")

Expand Down Expand Up @@ -320,4 +361,3 @@ function updateManagers(el, volunteer_username ){// retrieve the data of the stu
}
})
}

Loading
Loading