diff --git a/app/controllers/admin/routes.py b/app/controllers/admin/routes.py index a7d52183f..22830cb07 100644 --- a/app/controllers/admin/routes.py +++ b/app/controllers/admin/routes.py @@ -20,6 +20,7 @@ from app.models.eventRsvpLog import EventRsvpLog from app.models.attachmentUpload import AttachmentUpload from app.models.bonnerCohort import BonnerCohort +from app.models.eventCohort import EventCohort from app.models.certification import Certification from app.models.user import User from app.models.term import Term @@ -30,7 +31,7 @@ from app.logic.createLogs import createActivityLog from app.logic.certification import getCertRequirements, updateCertRequirements from app.logic.utils import selectSurroundingTerms, getFilesFromRequest, getRedirectTarget, setRedirectTarget -from app.logic.events import attemptSaveMultipleOfferings, cancelEvent, deleteEvent, attemptSaveEvent, preprocessEventData, getRecurringEventsData, deleteEventAndAllFollowing, deleteAllRecurringEvents, getBonnerEvents,addEventView, getEventRsvpCount, copyRsvpToNewEvent, getCountdownToEvent, calculateNewMultipleOfferingId +from app.logic.events import attemptSaveMultipleOfferings, cancelEvent, deleteEvent, attemptSaveEvent, preprocessEventData, getRecurringEventsData, deleteEventAndAllFollowing, deleteAllRecurringEvents, getBonnerEvents,addEventView, getEventRsvpCount, copyRsvpToNewEvent, getCountdownToEvent, calculateNewMultipleOfferingId, inviteCohortsToEvent, updateEventCohorts from app.logic.participants import getParticipationStatusForTrainings, checkUserRsvp from app.logic.minor import getMinorInterest from app.logic.fileHandler import FileHandler @@ -131,11 +132,11 @@ def createEvent(templateid, programid): validationErrorMessage = "Failed to save event." if savedEvents: - rsvpcohorts = request.form.getlist("cohorts[]") - for year in rsvpcohorts: - rsvpForBonnerCohort(int(year), savedEvents[0].id) - addBonnerCohortToRsvpLog(int(year), savedEvents[0].id) - + rsvpCohorts = request.form.getlist("cohorts[]") + if rsvpCohorts: + success, message, invitedCohorts = inviteCohortsToEvent(savedEvents[0], rsvpCohorts) + if not success: + flash(message, 'warning') noun = ((eventData.get('isRecurring') or eventData.get('isMultipleOffering')) and "Events" or "Event") # pluralize flash(f"{noun} successfully created!", 'success') @@ -177,7 +178,14 @@ def createEvent(templateid, programid): requirements, bonnerCohorts = [], [] if eventData['program'] is not None and eventData['program'].isBonnerScholars: requirements = getCertRequirements(Certification.BONNER) - bonnerCohorts = getBonnerCohorts(limit=5) + rawBonnerCohorts = getBonnerCohorts(limit=5) + bonnerCohorts = {} + + for year, cohort in rawBonnerCohorts.items(): + if cohort: + bonnerCohorts[year] = cohort + + return render_template(f"/events/{template.templateFile}", template = template, eventData = eventData, @@ -259,6 +267,10 @@ def eventDisplay(eventId): # Validate given URL try: event = Event.get_by_id(eventId) + invitedCohorts = list(EventCohort.select().where( + EventCohort.event == event + )) + invitedYears = [str(cohort.year) for cohort in invitedCohorts] except DoesNotExist as e: print(f"Unknown event: {eventId}") abort(404) @@ -293,11 +305,8 @@ def eventDisplay(eventId): if savedEvents: - rsvpcohorts = request.form.getlist("cohorts[]") - for year in rsvpcohorts: - rsvpForBonnerCohort(int(year), event.id) - addBonnerCohortToRsvpLog(int(year), event.id) - + rsvpCohorts = request.form.getlist("cohorts[]") + updateEventCohorts(savedEvents[0], rsvpCohorts) flash("Event successfully updated!", "success") return redirect(url_for("admin.eventDisplay", eventId = event.id)) else: @@ -314,7 +323,19 @@ def eventDisplay(eventId): if eventData['program'] and eventData['program'].isBonnerScholars: requirements = getCertRequirements(Certification.BONNER) - bonnerCohorts = getBonnerCohorts(limit=5) + rawBonnerCohorts = getBonnerCohorts(limit=5) + bonnerCohorts = {} + + for year, cohort in rawBonnerCohorts.items(): + if cohort: + bonnerCohorts[year] = cohort + + invitedCohorts = list(EventCohort.select().where( + EventCohort.event_id == eventId, + )) + invitedYears = [str(cohort.year) for cohort in invitedCohorts] + else: + requirements, bonnerCohorts, invitedYears = [], [], [] rule = request.url_rule @@ -322,10 +343,11 @@ def eventDisplay(eventId): if 'edit' in rule.rule: return render_template("events/createEvent.html", eventData = eventData, - futureTerms=futureTerms, + futureTerms = futureTerms, event = event, requirements = requirements, bonnerCohorts = bonnerCohorts, + invitedYears = invitedYears, userHasRSVPed = userHasRSVPed, isProgramManager = isProgramManager, filepaths = filepaths) @@ -350,6 +372,8 @@ def eventDisplay(eventId): currentEventRsvpAmount = getEventRsvpCount(event.id) userParticipatedTrainingEvents = getParticipationStatusForTrainings(eventData['program'], [g.current_user], g.current_term) + + return render_template("events/eventView.html", eventData=eventData, @@ -361,6 +385,7 @@ def eventDisplay(eventId): filepaths=filepaths, image=image, pageViewsCount=pageViewsCount, + invitedYears=invitedYears, eventCountdown=eventCountdown ) diff --git a/app/logic/bonner.py b/app/logic/bonner.py index 1fe11c5bd..dfd64780b 100644 --- a/app/logic/bonner.py +++ b/app/logic/bonner.py @@ -8,6 +8,7 @@ from app.models.bonnerCohort import BonnerCohort from app.models.eventRsvp import EventRsvp from app.models.user import User +from app.models.eventCohort import EventCohort from app.logic.createLogs import createRsvpLog def makeBonnerXls(): @@ -90,4 +91,4 @@ def addBonnerCohortToRsvpLog(year, event): .where(BonnerCohort.year == year)) for bonner in bonnerCohort: fullName = bonner.fullName - createRsvpLog(eventId=event, content=f"Added {fullName} to RSVP list.") \ No newline at end of file + createRsvpLog(eventId=event, content=f"Added {fullName} to RSVP list.") \ No newline at end of file diff --git a/app/logic/events.py b/app/logic/events.py index 8e0ad643c..dd2b34aca 100644 --- a/app/logic/events.py +++ b/app/logic/events.py @@ -17,7 +17,9 @@ from app.models.requirementMatch import RequirementMatch from app.models.certificationRequirement import CertificationRequirement from app.models.eventViews import EventView +from app.models.eventCohort import EventCohort +from app.logic.bonner import rsvpForBonnerCohort, addBonnerCohortToRsvpLog from app.logic.createLogs import createActivityLog, createRsvpLog from app.logic.utils import format24HourTime from app.logic.fileHandler import FileHandler @@ -706,3 +708,66 @@ def copyRsvpToNewEvent(priorEvent, newEvent): numRsvps = len(rsvpInfo) if numRsvps: createRsvpLog(newEvent, f"Copied {numRsvps} Rsvps from {priorEvent['name']} to {newEvent.name}") + + +def inviteCohortsToEvent(event, cohortYears): + """ + Invites cohorts to a newly created event by associating the cohorts directly. + """ + invitedCohorts = [] + try: + for year in cohortYears: + year = int(year) + EventCohort.get_or_create( + event=event, + year=year, + defaults={'invited_at': datetime.now()} + ) + + addBonnerCohortToRsvpLog(year, event.id) + rsvpForBonnerCohort(year, event.id) + invitedCohorts.append(year) + + if invitedCohorts: + cohortList = ', '.join(map(str, invitedCohorts)) + createActivityLog(f"Added Bonner cohorts {cohortList} for newly created event {event.name}") + + return True, "Cohorts successfully added to new event", invitedCohorts + + except Exception as e: + print(f"Error inviting cohorts to new event: {e}") + return False, f"Error adding cohorts to new event: {e}", [] + +def updateEventCohorts(event, cohortYears): + """ + Updates the cohorts for an existing event by adding new ones and removing outdated ones. + """ + invitedCohorts = [] + try: + precedentInvitedCohorts = list(EventCohort.select().where(EventCohort.event == event)) + precedentInvitedYears = [precedentCohort.year for precedentCohort in precedentInvitedCohorts] + + yearsToAdd = [year for year in cohortYears if year not in precedentInvitedYears] + + for year in yearsToAdd: + EventCohort.get_or_create( + event=event, + year=year, + defaults={'invited_at': datetime.now()} + ) + + addBonnerCohortToRsvpLog(year, event.id) + rsvpForBonnerCohort(year, event.id) + invitedCohorts.append(year) + + if yearsToAdd: + cohortList = ', '.join(map(str, invitedCohorts)) + createActivityLog(f"Updated Bonner cohorts for event {event.name}. Added: {yearsToAdd}") + + return True, "Cohorts successfully updated for event", invitedCohorts + + except Exception as e: + print(f"Error updating cohorts for event: {e}") + return False, f"Error updating cohorts for event: {e}", [] + + diff --git a/app/models/eventCohort.py b/app/models/eventCohort.py new file mode 100644 index 000000000..e60a02db7 --- /dev/null +++ b/app/models/eventCohort.py @@ -0,0 +1,13 @@ +from datetime import datetime +from app.models import* +from app.models.event import Event +from app.models.bonnerCohort import BonnerCohort + +class EventCohort(baseModel): + event = ForeignKeyField(Event) + year = IntegerField() + invited_at = DateTimeField(default=datetime.now) + + class Meta: + indexes = ( (('event', 'year'), True), ) + diff --git a/app/static/js/createEvents.js b/app/static/js/createEvents.js index 6381cb6ca..7f4557f15 100644 --- a/app/static/js/createEvents.js +++ b/app/static/js/createEvents.js @@ -332,11 +332,12 @@ function formatDate(originalDate) { */ $(document).ready(function() { //makes sure bonners toggle will stay on between event pages - if (window.location.pathname == '/event/' + $('#newEventID').val() + '/edit') { - if ($("#checkBonners")) { - $("#checkBonners").prop('checked', true); + if (window.location.pathname == '/event/' + $('#newEventID').val() + '/edit') { + if ($("#checkBonners")) { + $("#checkBonners").prop('checked', true); + } } -} + // Initialize datepicker with proper options $.datepicker.setDefaults({ dateFormat: 'yy/mm/dd', // Ensures compatibility across browsers diff --git a/app/templates/events/createEvent.html b/app/templates/events/createEvent.html index 1dbd75c28..6baed091a 100644 --- a/app/templates/events/createEvent.html +++ b/app/templates/events/createEvent.html @@ -235,11 +235,17 @@