Skip to content

Commit

Permalink
Merge branch 'development' into logEmergencyContacts
Browse files Browse the repository at this point in the history
  • Loading branch information
esw0624 committed Jul 17, 2024
2 parents 4046d4f + 70b9e75 commit d10aa89
Show file tree
Hide file tree
Showing 31 changed files with 725 additions and 421 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ If you want to test with actual emails, use an email other than outlook to test
1. Set up two factor authentication on your Gmail (Security Settings)
2. Create an App Password through your Gmail. This 16 character password can only be viewed once, so make sure to save it. (NOTE: You won't have the option to create an app password unless step one is completed)
3. Inside of your secret_config.yaml file set the MAIL_USERNAME and MAIL_DEFAULT_SENDER as your Gmail, set the MAIL_PASSWORD as your new app password as, and set ALWAYS_SEND_MAIL as True. If you want emails to go to their real recipients, remove MAIL_OVERRIDE_ALL from your config or set it to "".
4. For testing purposes, change the email of the student and supervisor to match another email that can receive your test emails (or you can use MAIL_OVERRIDE_ALL to send everything to the address specified.
4. For testing purposes, change the email of the student and supervisor to match another email that can receive your test emails or you can use MAIL_OVERRIDE_ALL to send everything to the address specified.

### SSDT Documentation
This is SSDT Documentation that contains details, references, workflow, system administration, etc. You are welcome to contribute to it and/or review it:
Expand Down
5 changes: 5 additions & 0 deletions app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ def load_currentTerm():
session['current_term'] = model_to_dict(term)
g.current_term = term

import datetime
@app.before_request
def load_currentDateTime():
g.currentDateTime = datetime.datetime.now()

from flask import request
@app.context_processor
def load_visibleAccordion():
Expand Down
22 changes: 19 additions & 3 deletions app/controllers/admin/minor.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,36 @@
from flask import render_template, g, abort
from flask import render_template, g, abort, request, redirect, url_for

from app.models.user import User

from app.controllers.admin import admin_bp

from app.logic.minor import getMinorInterest, getMinorProgress
from app.logic.minor import getMinorInterest, getMinorProgress, toggleMinorInterest

@admin_bp.route('/admin/cceMinor', methods=['GET'])
@admin_bp.route('/admin/cceMinor', methods=['POST','GET'])
def manageMinor():

if not g.current_user.isAdmin:
abort(403)

if request.method == 'POST':
interested_students = request.form.getlist('interestedStudents[]')

for i in interested_students:
user = User.get(username=i)
if not user.minorInterest:
toggleMinorInterest(i)


interestedStudentsList = getMinorInterest()
interestedStudentEmailString = ';'.join([student['email'] for student in interestedStudentsList])
sustainedEngagement = getMinorProgress()


return render_template('/admin/cceMinor.html',
interestedStudentsList = interestedStudentsList,
interestedStudentEmailString = interestedStudentEmailString,
sustainedEngagement = sustainedEngagement,
)



19 changes: 12 additions & 7 deletions app/controllers/admin/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
from app.logic.participants import getParticipationStatusForTrainings, checkUserRsvp
from app.logic.minor import getMinorInterest
from app.logic.fileHandler import FileHandler
from app.logic.bonner import getBonnerCohorts, makeBonnerXls, rsvpForBonnerCohort
from app.logic.bonner import getBonnerCohorts, makeBonnerXls, rsvpForBonnerCohort, addBonnerCohortToRsvpLog
from app.logic.serviceLearningCourses import parseUploadedFile, saveCourseParticipantsToDatabase, unapprovedCourses, approvedCourses, getImportedCourses, getInstructorCourses, editImportedCourses

from app.controllers.admin import admin_bp
Expand Down Expand Up @@ -110,6 +110,8 @@ def createEvent(templateid, programid):
rsvpcohorts = request.form.getlist("cohorts[]")
for year in rsvpcohorts:
rsvpForBonnerCohort(int(year), savedEvents[0].id)
addBonnerCohortToRsvpLog(int(year), savedEvents[0].id)


noun = (eventData['isRecurring'] == 'on' and "Events" or "Event") # pluralize
flash(f"{noun} successfully created!", 'success')
Expand Down Expand Up @@ -149,7 +151,7 @@ def createEvent(templateid, programid):
def rsvpLogDisplay(eventId):
event = Event.get_by_id(eventId)
if g.current_user.isCeltsAdmin or (g.current_user.isCeltsStudentStaff and g.current_user.isProgramManagerFor(event.program)):
allLogs = EventRsvpLog.select(EventRsvpLog, User).join(User).where(EventRsvpLog.event_id == eventId).order_by(EventRsvpLog.createdOn.desc())
allLogs = EventRsvpLog.select(EventRsvpLog, User).join(User, on=(EventRsvpLog.createdBy == User.username)).where(EventRsvpLog.event_id == eventId).order_by(EventRsvpLog.createdOn.desc())
return render_template("/events/rsvpLog.html",
event = event,
allLogs = allLogs)
Expand Down Expand Up @@ -253,6 +255,7 @@ def eventDisplay(eventId):
rsvpcohorts = request.form.getlist("cohorts[]")
for year in rsvpcohorts:
rsvpForBonnerCohort(int(year), event.id)
addBonnerCohortToRsvpLog(int(year), event.id)

flash("Event successfully updated!", "success")
return redirect(url_for("admin.eventDisplay", eventId = event.id))
Expand Down Expand Up @@ -292,7 +295,7 @@ def eventDisplay(eventId):
eventData['timeEnd'] = event.timeEnd.strftime("%-I:%M %p")
eventData['startDate'] = event.startDate.strftime("%m/%d/%Y")
eventCountdown = getCountdownToEvent(event)


# Identify the next event in a recurring series
if event.recurringId:
Expand All @@ -317,7 +320,8 @@ def eventDisplay(eventId):
filepaths=filepaths,
image=image,
pageViewsCount=pageViewsCount,
eventCountdown=eventCountdown)
eventCountdown=eventCountdown
)



Expand Down Expand Up @@ -588,7 +592,8 @@ def saveRequirements(certid):

@admin_bp.route("/displayEventFile", methods=["POST"])
def displayEventFile():
fileData= request.form
eventfile=FileHandler(eventId=fileData["id"])
eventfile.changeDisplay(fileData['id'])
fileData = request.form
eventfile = FileHandler(eventId=fileData["id"])
isChecked = fileData.get('checked') == 'true'
eventfile.changeDisplay(fileData['id'], isChecked)
return ""
6 changes: 5 additions & 1 deletion app/controllers/main/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,10 @@ def getDietInfo():

@main_bp.route('/profile/<username>/indicateInterest', methods=['POST'])
def indicateMinorInterest(username):
toggleMinorInterest(username)
if g.current_user.isCeltsAdmin or g.current_user.username == username:
toggleMinorInterest(username)

else:
abort(403)

return ""
18 changes: 16 additions & 2 deletions app/logic/bonner.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
from collections import defaultdict
from datetime import date
from peewee import IntegrityError, SQL
from peewee import IntegrityError, SQL, fn

import xlsxwriter

from app import app
from app.models.bonnerCohort import BonnerCohort
from app.models.eventRsvp import EventRsvp
from app.models.user import User
from app.logic.createLogs import createRsvpLog

def makeBonnerXls():
"""
Expand Down Expand Up @@ -72,8 +73,21 @@ def getBonnerCohorts(limit=None, currentYear=date.today().year):

return cohorts



def rsvpForBonnerCohort(year, event):
"""
Adds an EventRsvp record to the given event for each user in the given Bonner year.
"""
EventRsvp.insert_from(BonnerCohort.select(BonnerCohort.user, event, SQL('NOW()')).where(BonnerCohort.year == year),[EventRsvp.user, EventRsvp.event, EventRsvp.rsvpTime]).on_conflict(action='IGNORE').execute()
EventRsvp.insert_from(BonnerCohort.select(BonnerCohort.user, event, SQL('NOW()'))
.where(BonnerCohort.year == year),
[EventRsvp.user, EventRsvp.event, EventRsvp.rsvpTime]).on_conflict(action='IGNORE').execute()

def addBonnerCohortToRsvpLog(year, event):
""" This method adds the table information in the RSVP Log page"""
bonnerCohort = list(BonnerCohort.select(fn.CONCAT(User.firstName, ' ', User.lastName).alias("fullName"))
.join(User, on=(User.username == BonnerCohort.user))
.where(BonnerCohort.year == year))
for bonner in bonnerCohort:
fullName = bonner.fullName
createRsvpLog(eventId=event, content=f"Added {fullName} to RSVP list.")
73 changes: 25 additions & 48 deletions app/logic/fileHandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,101 +4,78 @@
from app.models.attachmentUpload import AttachmentUpload

class FileHandler:
def __init__(self,files=None, courseId=None, eventId=None):
self.files=files
def __init__(self, files=None, courseId=None, eventId=None):
self.files = files
self.path = app.config['files']['base_path']
self.courseId = courseId
self.eventId = eventId
if courseId:
self.path = os.path.join(self.path, app.config['files']['course_attachment_path'], str(courseId))
elif eventId:
# eventID is not included in the path, because it is now a part of the attachment filename.
self.path = os.path.join(self.path, app.config['files']['event_attachment_path'])

def makeDirectory(self):
# This creates a directory.
# Created to remove duplicates when an event is recurring.
try:
extraDir = str(self.eventId) if self.eventId else ""
os.makedirs(os.path.join(self.path, extraDir))
# Error 17 Occurs when we try to create a directory that already exists
except OSError as e:
if e.errno != 17:
print(f'Fail to create directory: {e}')
raise e

def getFileFullPath(self, newfilename = ''):
"""
This creates the directory/path for the object from the "Choose File" input in the create event and edit event.
:returns: directory path for attachment
"""

# Added the eventID of the first recurring event to track the file path for subsequent recurring events.

def getFileFullPath(self, newfilename=''):
try:
# tries to create the full path of the files location and passes if
# the directories already exist or there is no attachment
filePath=(os.path.join(self.path, newfilename))
except AttributeError: # will pass if there is no attachment to save
filePath = (os.path.join(self.path, newfilename))
except AttributeError:
pass
except FileExistsError:
pass

return filePath

def saveFiles(self, saveOriginalFile=None):
""" Saves the attachment in the app/static/files/eventattachments/ or courseattachements/ directory """
try:
for file in self.files:
saveFileToFilesystem = None
if self.eventId:
attachmentName = str(saveOriginalFile.id) + "/" + file.filename

# isFileInEvent checks if the attachment exists in the database under that eventId and filename.
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)

# Only save the file if our event is on its own, or the first of a recurring series
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)
AttachmentUpload.create(course=self.courseId, fileName=file.filename)
saveFileToFilesystem = file.filename
else:
else:
saveFileToFilesystem = file.filename

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

except AttributeError: # will pass if there is no attachment to save
file.save(self.getFileFullPath(newfilename=saveFileToFilesystem))
except AttributeError:
pass

def retrievePath(self,files):
pathDict={}
def retrievePath(self, files):
pathDict = {}
for file in files:
pathDict[file.fileName] = ((self.path+"/"+ file.fileName)[3:], file)

pathDict[file.fileName] = ((self.path + "/" + file.fileName)[3:], file)
return pathDict

def deleteFile(self, fileId):
"""
Deletes attachmant from the app/static/files/eventattachments/ or courseattachments/ directory
"""
file = AttachmentUpload.get_by_id(fileId)
file.delete_instance()

# checks if there are other instances with the same filename in the AttachmentUpload table
if not AttachmentUpload.select().where(AttachmentUpload.fileName == file.fileName).exists():
path = os.path.join(self.path, file.fileName)
os.remove(path)
def changeDisplay(self, fileId):

def changeDisplay(self, fileId, isDisplayed):
file = AttachmentUpload.get_by_id(fileId)
AttachmentUpload.update(isDisplayed=False).where(AttachmentUpload.event == file.event, AttachmentUpload.isDisplayed==True).execute()
AttachmentUpload.update(isDisplayed=True).where(AttachmentUpload.id == fileId).execute()
return ""

# Uncheck all other checkboxes for the same event
AttachmentUpload.update(isDisplayed=False).where(AttachmentUpload.event == file.event).execute()

# Check the selected checkbox
AttachmentUpload.update(isDisplayed=isDisplayed).where(AttachmentUpload.id == fileId).execute()
return ""
18 changes: 17 additions & 1 deletion app/logic/serviceLearningCourses.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,28 @@ def getSLProposalInfoForUser(user: User) -> Dict[int, Dict[str, Any]]:

courseDict[course.id] = {"id":course.id,
"creator":f"{course.createdBy.firstName} {course.createdBy.lastName}",
"name":course.courseName if course.courseName else course.courseAbbreviation,
"name": course.courseName,
"abbr": course.courseAbbreviation,
"courseDisplayName": createCourseDisplayName(course.courseName, course.courseAbbreviation),
"faculty": faculty,
"term": course.term,
"status": course.status.status}
return courseDict

def createCourseDisplayName(name, abbreviation):
'''
This function combines course name and numbers with conditions
inputs: course name, course abbreviation
'''
if name and abbreviation:
return f"{abbreviation} - {name}"
elif not name and not abbreviation:
return ''
elif not name:
return abbreviation
elif not abbreviation:
return name

def saveCourseParticipantsToDatabase(cpPreview: Dict[str, Dict[str, Dict[str, List[Dict[str, Any]]]]]) -> None:
for term, terminfo in cpPreview.items():
termObj: Term = Term.get_or_none(description = term) or addPastTerm(term)
Expand Down
8 changes: 8 additions & 0 deletions app/static/css/base.css
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,12 @@ select.empty {

.rsvp-btn{
padding-top: 4em;
}
.required::after {
content: " *";
color: red;
font-size: 1.3em; /* Adjust the font size as needed */
font-weight: bolder; /* make it bold */
vertical-align: middle; /* Align vertically with the text */
line-height: 1; /* Match the line height of the parent text */
}
6 changes: 5 additions & 1 deletion app/static/css/eventList.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
.accordion-button::after {
margin-left: 1.5rem;
}
}

.icon {
margin-right: 20px; /* Adjust the value as needed */
}
19 changes: 0 additions & 19 deletions app/static/css/slcNewProposal.css
Original file line number Diff line number Diff line change
Expand Up @@ -28,24 +28,5 @@
background-color: #0d6efd;
}

@media (max-width: 1500px){
.btn{
height:60px;
}
.step{
width: 13px;
height:13px;
}
}

@media (max-width: 1100px){
.btn{
height:80px;
}
.step{
width: 10px;
height:10px;
}
}


Loading

0 comments on commit d10aa89

Please sign in to comment.