Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
b417d7b
got most the way done with editing and viewing functionality
bledsoef Mar 17, 2025
54ec678
cleaned up formatting and working on file handling
bledsoef Mar 19, 2025
c54d7d0
almost done with PR
bledsoef Mar 20, 2025
5896afd
resolved merge conflicts
bledsoef Mar 28, 2025
ebd1578
deleted unnecessary comment
bledsoef Mar 28, 2025
45f2830
Merge branch '1395_NewCCEMinorPortal' of github.com:BCStudentSoftware…
bledsoef Mar 28, 2025
7f8682a
made exiting take you back to the manageproposals tab
bledsoef Mar 28, 2025
ad8cf9c
Added another button for the edit page
Kafui123 Mar 28, 2025
89a81ed
working on tests but they are bugging rn
bledsoef Mar 28, 2025
9cc3bb4
Merge branch '1399_EditFunctionality' of github.com:BCStudentSoftware…
bledsoef Mar 28, 2025
4b22f15
Changes
Kafui123 Mar 29, 2025
2ed4c78
ApprovalFunctionality initial implementations
Kafui123 Mar 29, 2025
4c30034
Changes to the approval functionality tab
Kafui123 Mar 31, 2025
42b3a68
made so many changes its kinda bonkers
bledsoef Apr 3, 2025
fffb76a
Merge branch '1395_NewCCEMinorPortal' of github.com:BCStudentSoftware…
bledsoef Apr 3, 2025
405f93c
Changes
Kafui123 Apr 7, 2025
507add3
Merge branch 'development' into 1399_EditFunctionality
bledsoef Apr 7, 2025
ae093bd
Merge branch '1399_EditFunctionality' of github.com:BCStudentSoftware…
Kafui123 Apr 14, 2025
a3a6130
Changes
Kafui123 Apr 15, 2025
1e757a5
Merge branch '1399_EditFunctionality' of github.com:BCStudentSoftware…
Kafui123 Apr 15, 2025
f71c56b
ApprovalFunctionality Dusted
Kafui123 Apr 16, 2025
cf6c135
Ca
Kafui123 Apr 16, 2025
39e3941
Merge branch 'development' into 1399_EditFunctionality
bledsoef Apr 16, 2025
695eb8b
fixed error
bledsoef Apr 16, 2025
72cf133
fixed bug
bledsoef Apr 16, 2025
8b66da6
Merge branch 'development' into 1399_EditFunctionality
bledsoef Apr 17, 2025
941b1da
fixed conflict
bledsoef Apr 17, 2025
74cd3e1
Merge branch '1399_EditFunctionality' of github.com:BCStudentSoftware…
Kafui123 Apr 18, 2025
9979082
Fixed the failing tests....status was now being passed as part of our…
Kafui123 Apr 18, 2025
0f754bb
added completed status and option
bledsoef Apr 24, 2025
cf49111
fixed merged conflicts
bledsoef Apr 24, 2025
4544ec8
Merge branch '1398_ApprovalFunctionality' of github.com:BCStudentSoft…
bledsoef Apr 24, 2025
793a2dd
made the status timeline
bledsoef Apr 25, 2025
a83ed62
working on macro styling
bledsoef Apr 25, 2025
922b17e
Merge pull request #1436 from BCStudentSoftwareDevTeam/1398_ApprovalF…
bledsoef Apr 25, 2025
2219758
Merge branch 'development' into 1399_EditFunctionality
bledsoef Apr 25, 2025
d32ea22
fixed critical bug
bledsoef Apr 25, 2025
e97e7ca
deleted .copy() THANKS LAWRENCE
bledsoef Apr 25, 2025
7628cce
resolved more comments
bledsoef Apr 28, 2025
c22a3c7
fixed tests
bledsoef Apr 28, 2025
9d22d1d
working on file attachments
bledsoef Apr 28, 2025
700efd6
fixed most of the stuff except for tests
bledsoef Apr 29, 2025
60eaac8
fixed errors
bledsoef Apr 29, 2025
e793306
fixed some bugs
bledsoef Apr 29, 2025
c824d52
made more changes
bledsoef Apr 29, 2025
20f51f6
fixed all bugs, PLEASE LET THIS BE TRUE
bledsoef Apr 29, 2025
3ec0ed5
Fixed failing tests by improving parts of the logic. test_removePropo…
ojmakinde Nov 9, 2025
cff1d8d
All tests fixed. Will now begin improving implementation logic.
ojmakinde Nov 9, 2025
f9baba5
Removed unnecessary exception catching across created files.
ojmakinde Nov 9, 2025
9190876
Improved the functionality of saveFiles() function
ojmakinde Nov 10, 2025
b1acdb1
Fixed tests from saveFiles() improvement
ojmakinde Nov 11, 2025
6be8d15
Fixed the bug where custom hours and custom experience description we…
ojmakinde Nov 11, 2025
35a50a8
Removed hanging logic for creating unnecessary /static dir
ojmakinde Nov 11, 2025
a5d7c82
Merge branch 'development' of github.com:BCStudentSoftwareDevTeam/cel…
ojmakinde Nov 11, 2025
f360568
Merge branch '1399_EditFunctionality' into 1473_AddCompletedStatus
ojmakinde Nov 13, 2025
77811c4
Fixed merge bugs and re-added withdrawProposal() function
ojmakinde Nov 13, 2025
29ae54e
Wrote logic for marking proposals as complete. Need to update the CSS…
ojmakinde Nov 16, 2025
3cb3d35
Corrected the update proposal functionality and wrote tests for new f…
ojmakinde Nov 18, 2025
0918e59
Removed the Notes and Print options from the CCE Minor dropdown
ojmakinde Nov 19, 2025
e4b6306
Implemeneted logic that prevents students from modifying a proposal o…
ojmakinde Nov 19, 2025
3f5af5d
Fixed the getCCEMinorProposals() function and reomved unnecessary ser…
ojmakinde Nov 19, 2025
c5433d5
Added flash message to inform students that proposals can only be edi…
ojmakinde Nov 20, 2025
5662f69
Added isApproved() property and added functionality to approve or una…
ojmakinde Nov 20, 2025
c1d10b7
Fixed the bug with summer experience type not showing up
ojmakinde Nov 21, 2025
185daaf
Merge branch 'development' of github.com:BCStudentSoftwareDevTeam/cel…
ojmakinde Nov 24, 2025
dbf13af
Fixing small JS bugs scattered across files.
ojmakinde Nov 25, 2025
9de8357
Added input check for checkboxes and radios in proposal creation
ojmakinde Nov 26, 2025
8a1d591
Fixed the withdraw modal
ojmakinde Nov 26, 2025
8dcbc77
Fixed form validation and re-implemented native validation, removing …
ojmakinde Dec 10, 2025
58d0f48
Fixing the button for the otherEngagement page.
RueHaile Dec 17, 2025
3e12991
file save error half fix
RueHaile Dec 17, 2025
66e4ec1
Buttons are functional
RueHaile Dec 19, 2025
74ca5c9
css fix
RueHaile Dec 19, 2025
07d62f4
fixing the other events
RueHaile Dec 22, 2025
f195276
setting hrs and week total right
RueHaile Dec 22, 2025
89e6cca
disable the approve if draft
RueHaile Dec 29, 2025
8b5c865
fix the edit view and also the mark as complete.
RueHaile Dec 30, 2025
df14f2e
Merge branch 'development' into 1399_EditFunctionality
bakobagassas Dec 30, 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
121 changes: 100 additions & 21 deletions app/controllers/minor/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,32 @@

from app.controllers.minor import minor_bp
from app.models.user import User
from app.models.attachmentUpload import AttachmentUpload
from app.models.cceMinorProposal import CCEMinorProposal
from app.models.term import Term
from app.models.attachmentUpload import AttachmentUpload
from app.logic.fileHandler import FileHandler
from app.logic.utils import selectSurroundingTerms, getFilesFromRequest
from app.logic.minor import createOtherEngagementRequest, setCommunityEngagementForUser, getSummerExperience, getEngagementTotal, createSummerExperience, getProgramEngagementHistory, getCourseInformation, getCommunityEngagementByTerm, getCCEMinorProposals, createOtherEngagementRequest, removeProposal, getMinorSpreadsheet
from app.logic.minor import (
changeProposalStatus,
createOtherEngagement,
updateOtherEngagementRequest,
setCommunityEngagementForUser,
getEngagementTotal,
createSummerExperience,
updateSummerExperience,
getProgramEngagementHistory,
getCourseInformation,
getCommunityEngagementByTerm,
getMinorSpreadsheet,
getCCEMinorProposals,
removeProposal
)

@minor_bp.route('/profile/<username>/cceMinor', methods=['GET'])
def viewCceMinor(username):
"""
Load minor management page with community engagements and summer experience
"""
if not (g.current_user.isAdmin):
return abort(403)

sustainedEngagementByTerm = getCommunityEngagementByTerm(username)

Expand All @@ -29,7 +42,7 @@ def viewCceMinor(username):
activeTab=activeTab)

@minor_bp.route('/cceMinor/<username>/otherEngagement', methods=['GET', 'POST'])
def requestOtherEngagement(username):
def createOtherEngagementRequest(username):
"""
Load minor management page with community engagements and summer experience
"""
Expand All @@ -39,22 +52,68 @@ def requestOtherEngagement(username):

# once we submit the form for creation
if request.method == "POST":
createdProposal = createOtherEngagementRequest(username, request.form)
attachment = request.files.get("attachmentObject")
if attachment:
addFile = FileHandler(getFilesFromRequest(request), proposalId=createdProposal.id)
addFile.saveFiles()
createOtherEngagement(username, request)

return redirect(url_for('minor.viewCceMinor', username=username))

return render_template("minor/requestOtherEngagement.html",
editable = True,
user = User.get_by_id(username),
selectableTerms = selectSurroundingTerms(g.current_term),
allTerms = getSummerExperience(username))
postRoute = f"/cceMinor/{username}/otherEngagement", # when form is submitted, what POST route is it being submitted to.
attachmentFilePath = "",
attachmentFileName = "",
proposal = None)

@minor_bp.route('/cceMinor/viewOtherEngagement/<proposalID>', methods=['GET'])
@minor_bp.route('/cceMinor/viewSummerExperience/<proposalID>', methods=['GET'])
@minor_bp.route('/cceMinor/editOtherEngagement/<proposalID>', methods=['GET', 'POST'])
@minor_bp.route('/cceMinor/editSummerExperience/<proposalID>', methods=['GET', 'POST'])
def editOrViewProposal(proposalID: int):
proposal = CCEMinorProposal.get_by_id(int(proposalID))
if not (g.current_user.isAdmin or g.current_user.username == proposal.student.username):
return abort(403)

editProposal = 'view' not in request.path

# if proposal is approved, only admins can edit, but not if the admin is the student
if proposal.isApproved and editProposal:
if g.current_user.username == proposal.student or not g.current_user.isAdmin:
return abort(403)

attachmentObject = AttachmentUpload.get_or_none(proposal=proposalID)
attachmentFilePath = ""
attachmentFileName = ""

if attachmentObject:
fileHandler = FileHandler(proposalId=proposalID)
attachmentFilePath = fileHandler.getFileFullPath(attachmentObject.fileName).lstrip("app/") # we need to remove app/ from the url because it prevents it from displaying
attachmentFileName = attachmentObject.fileName

if request.method == "GET":
selectedTerm = Term.get_by_id(proposal.term)
flash("Once approved, a proposal can only be edited by an admin.", 'warning')
return render_template("minor/requestOtherEngagement.html" if 'OtherEngagement' in request.path else "minor/summerExperience.html",
editable = editProposal,
selectedTerm = selectedTerm,
contentAreas = proposal.contentAreas.split(", ") if proposal.contentAreas else [],
selectableTerms = selectSurroundingTerms(g.current_term, summerOnly=False if 'OtherEngagement' else True),
user = User.get_by_id(proposal.student),
postRoute = f"/cceMinor/editSummerExperience/{proposal.id}" if "SummerExperience" in request.path else f"/cceMinor/editOtherEngagement/{proposal.id}",
proposal = proposal,
attachmentFilePath = attachmentFilePath,
attachmentFileName = attachmentFileName
)

if "OtherEngagement" in request.path:
updateOtherEngagementRequest(proposalID, request)
else:
updateSummerExperience(proposalID, request.form)

return redirect(url_for('minor.viewCceMinor', username=proposal.student))

@minor_bp.route('/cceMinor/<username>/summerExperience', methods=['GET', 'POST'])
def requestSummerExperience(username):
def createSummerExperienceRequest(username):
"""
Load minor management page with community engagements and summer experience
"""
Expand All @@ -69,7 +128,8 @@ def requestSummerExperience(username):
summerTerms = selectSurroundingTerms(g.current_term, summerOnly=True)

return render_template("minor/summerExperience.html",
summerTerms = summerTerms,
selectableTerms = summerTerms,
contentAreas = [],
user = User.get_by_id(username),
)

Expand All @@ -86,19 +146,38 @@ def getEngagementInformation(username, type, id, term):
return information


@minor_bp.route('/cceMinor/withdraw/<username>/<proposalID>', methods = ['POST'])
def withdrawProposal(username, proposalID):
@minor_bp.route('/cceMinor/<action>/<username>/<proposalId>', methods=['POST'])
def updateProposal(action, username, proposalId):
try:
if g.current_user.isAdmin or g.current_user.isFaculty or g.current_user == username:
removeProposal(proposalID)
flash("Experience successfully withdrawn", 'success')
if not (g.current_user.isAdmin or g.current_user.isFaculty or g.current_user.username == username):
flash("Unauthorized to perform this action", "warning")
return ""

actionMap = {
"withdraw": ("Withdrawn", "Proposal successfully withdrawn"),
"complete": ("Completed", "Proposal successfully completed"),
"approve": ("Approved", "Proposal approved"),
"unapprove": ("Submitted", "Proposal unapproved"),
}

if action not in actionMap:
flash("Invalid action", "warning")
return ""

newStatus, message = actionMap[action]

if action == "withdraw":
removeProposal(proposalId)
else:
flash("Unauthorized to perform this action", 'warning')
changeProposalStatus(proposalId, newStatus)
flash(message, "success")

except Exception as e:
print(e)
flash("Withdrawal Unsuccessful", 'warning')
flash("Proposal status could not be changed", "warning")

return ""


@minor_bp.route('/cceMinor/getMinorSpreadsheet', methods=['GET'])
def returnMinorSpreadsheet():
Expand Down
2 changes: 1 addition & 1 deletion app/logic/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ def attemptSaveEvent(eventData, attachmentFiles = None, renewedEvent = False):
if attachmentFiles:
for event in events:
addFile = FileHandler(attachmentFiles, eventId=event.id)
addFile.saveFiles(saveOriginalFile=events[0])
addFile.saveFiles(parentEvent=events[0])
return events, ""


Expand Down
96 changes: 49 additions & 47 deletions app/logic/fileHandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,14 @@ def __init__(self, files=None, courseId=None, eventId=None, programId=None, prop
elif programId:
self.path = os.path.join(self.path, app.config['files']['program_attachment_path'])
elif proposalId:
self.path = os.path.join(self.path, app.config['files']['proposal_attachment_path'])
self.path = os.path.join(self.path, app.config['files']['proposal_attachment_path'], str(proposalId))

def makeDirectory(self):
try:
extraDir = str(self.eventId) if self.eventId else ""
extraDir = ""
if self.eventId:
extraDir = str(self.eventId)

os.makedirs(os.path.join(self.path, extraDir))
except OSError as e:
if e.errno != 17:
Expand All @@ -46,57 +49,56 @@ def getFileFullPath(self, newfilename=''):

return filePath

def saveFiles(self, saveOriginalFile=None):
try:
for file in self.files:
saveFileToFilesystem = None
def saveFiles(self, parentEvent=None):
"""
Saves attachments for different types and creates DB record for stored attachment
"""
for file in self.files:
saveFileToFilesystem = None

if self.eventId:
attachmentName = str(parentEvent.id) + "/" + file.filename
isFileInEvent = AttachmentUpload.select().where(AttachmentUpload.event_id == self.eventId,
AttachmentUpload.fileName == attachmentName).exists()
if not isFileInEvent:
AttachmentUpload.create(event=self.eventId, fileName=attachmentName)
if parentEvent and parentEvent.id == self.eventId:
saveFileToFilesystem = attachmentName

if self.eventId:
attachmentName = str(saveOriginalFile.id) + "/" + file.filename
isFileInEvent = AttachmentUpload.select().where(AttachmentUpload.event_id == self.eventId,
AttachmentUpload.fileName == attachmentName).exists()
if not isFileInEvent:
AttachmentUpload.create(event=self.eventId, fileName=attachmentName)
if saveOriginalFile and saveOriginalFile.id == self.eventId:
saveFileToFilesystem = attachmentName
elif self.courseId:
isFileInCourse = AttachmentUpload.select().where(AttachmentUpload.course == self.courseId, AttachmentUpload.fileName == file.filename).exists()
if not isFileInCourse:
AttachmentUpload.create(course=self.courseId, fileName=file.filename)
saveFileToFilesystem = file.filename
elif self.programId:
elif self.courseId or self.proposalId:
recordId = self.courseId if self.courseId else self.proposalId
fieldName = 'course' if self.courseId else 'proposal'

fileExists = AttachmentUpload.select().where(
getattr(AttachmentUpload, fieldName) == recordId,
AttachmentUpload.fileName == file.filename
).exists()

if not fileExists:
create_data = {fieldName: recordId, 'fileName': file.filename}
AttachmentUpload.create(**create_data)
saveFileToFilesystem = file.filename

# remove the existing file
deleteFileObject = AttachmentUpload.get_or_none(program=self.programId)
if deleteFileObject:
self.deleteFile(deleteFileObject.id)
elif self.programId:

# add the new file
fileType = file.filename.split('.')[-1]
fileName = f"{self.programId}.{fileType}"
AttachmentUpload.create(program=self.programId, fileName=fileName)
currentProgramID = fileName
saveFileToFilesystem = currentProgramID

elif self.proposalId:
fileType = file.filename.split('.')[-1]
fileName = f"{self.proposalId}.{fileType}"
isFileInProposal = AttachmentUpload.select().where(AttachmentUpload.proposal == self.proposalId,
AttachmentUpload.fileName == fileName).exists()
if not isFileInProposal:
# add the new file
AttachmentUpload.create(proposal=self.proposalId, fileName=fileName)
saveFileToFilesystem = fileName
# remove the existing file
deleteFileObject = AttachmentUpload.get_or_none(program=self.programId)
if deleteFileObject:
self.deleteFile(deleteFileObject.id)

else:
saveFileToFilesystem = file.filename
# add the new file
fileType = file.filename.split('.')[-1]
fileName = f"{self.programId}.{fileType}"
AttachmentUpload.create(program=self.programId, fileName=fileName)
currentProgramID = fileName
saveFileToFilesystem = currentProgramID

if saveFileToFilesystem:
self.makeDirectory()
file.save(self.getFileFullPath(newfilename=saveFileToFilesystem))
else:
saveFileToFilesystem = file.filename

except AttributeError as e:
print(e)
if saveFileToFilesystem:
self.makeDirectory()
file.save(self.getFileFullPath(newfilename=saveFileToFilesystem))

def retrievePath(self, files):
pathDict = {}
Expand Down
Loading