2021-07-19 13:00:59 +00:00
import logging
from django import forms
from django . conf import settings
from django . contrib import messages
from django . db . models import Count , Q
from django . shortcuts import get_object_or_404 , redirect
from django . urls import reverse
from django . utils . safestring import mark_safe
from django . views . generic import DetailView , ListView , TemplateView
from django . views . generic . edit import CreateView , DeleteView , FormView , UpdateView
2021-07-19 13:06:10 +00:00
from camps . mixins import CampViewMixin
2021-07-19 13:00:59 +00:00
from program . autoscheduler import AutoScheduler
2021-08-07 13:31:11 +00:00
from program . email import add_event_scheduled_email
2021-07-19 13:00:59 +00:00
from program . mixins import AvailabilityMatrixViewMixin
from program . models import (
Event ,
EventLocation ,
EventProposal ,
EventSession ,
EventSlot ,
EventType ,
Speaker ,
SpeakerProposal ,
)
from program . utils import save_speaker_availability
from . . forms import (
AutoScheduleApplyForm ,
AutoScheduleValidateForm ,
EventScheduleForm ,
SpeakerForm ,
)
2021-07-19 13:06:10 +00:00
from . . mixins import ContentTeamPermissionMixin
2021-07-19 13:00:59 +00:00
logger = logging . getLogger ( " bornhack. %s " % __name__ )
#######################################
# MANAGE SPEAKER/EVENT PROPOSAL VIEWS
class PendingProposalsView ( CampViewMixin , ContentTeamPermissionMixin , ListView ) :
2021-07-19 13:06:10 +00:00
""" This convenience view shows a list of pending proposals """
2021-07-19 13:00:59 +00:00
model = SpeakerProposal
template_name = " pending_proposals.html "
context_object_name = " speaker_proposal_list "
def get_queryset ( self , * * kwargs ) :
qs = super ( ) . get_queryset ( * * kwargs ) . filter ( proposal_status = " pending " )
qs = qs . prefetch_related ( " user " , " urls " , " speaker " )
return qs
def get_context_data ( self , * * kwargs ) :
context = super ( ) . get_context_data ( * * kwargs )
context [ " event_proposal_list " ] = self . camp . event_proposals . filter (
proposal_status = EventProposal . PROPOSAL_PENDING
) . prefetch_related ( " event_type " , " track " , " speakers " , " tags " , " user " , " event " )
return context
class ProposalApproveBaseView ( CampViewMixin , ContentTeamPermissionMixin , UpdateView ) :
"""
Shared logic between SpeakerProposalApproveView and EventProposalApproveView
"""
fields = [ " reason " ]
def form_valid ( self , form ) :
"""
We have two submit buttons in this form , Approve and Reject
"""
if " approve " in form . data :
# approve button was pressed
form . instance . mark_as_approved ( self . request )
elif " reject " in form . data :
# reject button was pressed
form . instance . mark_as_rejected ( self . request )
else :
messages . error ( self . request , " Unknown submit action " )
return redirect (
reverse (
" backoffice:pending_proposals " , kwargs = { " camp_slug " : self . camp . slug }
)
)
class SpeakerProposalListView ( CampViewMixin , ContentTeamPermissionMixin , ListView ) :
2021-07-19 13:06:10 +00:00
""" This view permits Content Team members to list SpeakerProposals """
2021-07-19 13:00:59 +00:00
model = SpeakerProposal
template_name = " speaker_proposal_list.html "
context_object_name = " speaker_proposal_list "
def get_queryset ( self , * * kwargs ) :
qs = super ( ) . get_queryset ( * * kwargs )
qs = qs . prefetch_related ( " user " , " urls " , " speaker " )
return qs
class SpeakerProposalDetailView (
AvailabilityMatrixViewMixin ,
ContentTeamPermissionMixin ,
DetailView ,
) :
2021-07-19 13:06:10 +00:00
""" This view permits Content Team members to see SpeakerProposal details """
2021-07-19 13:00:59 +00:00
model = SpeakerProposal
template_name = " speaker_proposal_detail_backoffice.html "
context_object_name = " speaker_proposal "
def get_queryset ( self , * args , * * kwargs ) :
qs = super ( ) . get_queryset ( * args , * * kwargs )
qs = qs . prefetch_related ( " user " , " urls " )
return qs
class SpeakerProposalApproveRejectView ( ProposalApproveBaseView ) :
2021-07-19 13:06:10 +00:00
""" This view allows ContentTeam members to approve/reject SpeakerProposals """
2021-07-19 13:00:59 +00:00
model = SpeakerProposal
template_name = " speaker_proposal_approve_reject.html "
context_object_name = " speaker_proposal "
class EventProposalListView ( CampViewMixin , ContentTeamPermissionMixin , ListView ) :
2021-07-19 13:06:10 +00:00
""" This view permits Content Team members to list EventProposals """
2021-07-19 13:00:59 +00:00
model = EventProposal
template_name = " event_proposal_list.html "
context_object_name = " event_proposal_list "
def get_queryset ( self , * args , * * kwargs ) :
qs = super ( ) . get_queryset ( * args , * * kwargs )
qs = qs . prefetch_related (
" user " ,
" urls " ,
" event " ,
" event_type " ,
" speakers__event_proposals " ,
" track " ,
" tags " ,
)
return qs
class EventProposalDetailView ( CampViewMixin , ContentTeamPermissionMixin , DetailView ) :
2021-07-19 13:06:10 +00:00
""" This view permits Content Team members to see EventProposal details """
2021-07-19 13:00:59 +00:00
model = EventProposal
template_name = " event_proposal_detail_backoffice.html "
context_object_name = " event_proposal "
class EventProposalApproveRejectView ( ProposalApproveBaseView ) :
2021-07-19 13:06:10 +00:00
""" This view allows ContentTeam members to approve/reject EventProposals """
2021-07-19 13:00:59 +00:00
model = EventProposal
template_name = " event_proposal_approve_reject.html "
context_object_name = " event_proposal "
################################
# MANAGE SPEAKER VIEWS
class SpeakerListView ( CampViewMixin , ContentTeamPermissionMixin , ListView ) :
2021-07-19 13:06:10 +00:00
""" This view is used by the Content Team to see Speaker objects. """
2021-07-19 13:00:59 +00:00
model = Speaker
template_name = " speaker_list_backoffice.html "
def get_queryset ( self , * args , * * kwargs ) :
qs = super ( ) . get_queryset ( * args , * * kwargs )
qs = qs . prefetch_related (
" proposal__user " ,
" events__event_slots " ,
" events__event_type " ,
" event_conflicts " ,
)
return qs
class SpeakerDetailView (
AvailabilityMatrixViewMixin , ContentTeamPermissionMixin , DetailView
) :
2021-07-19 13:06:10 +00:00
""" This view is used by the Content Team to see details for Speaker objects """
2021-07-19 13:00:59 +00:00
model = Speaker
template_name = " speaker_detail_backoffice.html "
def get_queryset ( self , * args , * * kwargs ) :
qs = super ( ) . get_queryset ( * args , * * kwargs )
qs = qs . prefetch_related (
" event_conflicts " , " events__event_slots " , " events__event_type "
)
return qs
class SpeakerUpdateView (
AvailabilityMatrixViewMixin , ContentTeamPermissionMixin , UpdateView
) :
2021-07-19 13:06:10 +00:00
""" This view is used by the Content Team to update Speaker objects """
2021-07-19 13:00:59 +00:00
model = Speaker
template_name = " speaker_update.html "
form_class = SpeakerForm
def get_form_kwargs ( self ) :
2021-07-19 13:06:10 +00:00
""" Set camp for the form """
2021-07-19 13:00:59 +00:00
kwargs = super ( ) . get_form_kwargs ( )
kwargs . update ( { " camp " : self . camp } )
return kwargs
def form_valid ( self , form ) :
2021-07-19 13:06:10 +00:00
""" Save object and availability """
2021-07-19 13:00:59 +00:00
speaker = form . save ( )
save_speaker_availability ( form , obj = speaker )
messages . success ( self . request , " Speaker has been updated " )
return redirect (
reverse (
" backoffice:speaker_detail " ,
kwargs = { " camp_slug " : self . camp . slug , " slug " : self . get_object ( ) . slug } ,
)
)
class SpeakerDeleteView ( CampViewMixin , ContentTeamPermissionMixin , DeleteView ) :
2021-07-19 13:06:10 +00:00
""" This view is used by the Content Team to delete Speaker objects """
2021-07-19 13:00:59 +00:00
model = Speaker
template_name = " speaker_delete.html "
def delete ( self , * args , * * kwargs ) :
speaker = self . get_object ( )
# delete related objects first
speaker . availabilities . all ( ) . delete ( )
speaker . urls . all ( ) . delete ( )
return super ( ) . delete ( * args , * * kwargs )
def get_success_url ( self ) :
messages . success (
self . request , f " Speaker ' { self . get_object ( ) . name } ' has been deleted "
)
return reverse ( " backoffice:speaker_list " , kwargs = { " camp_slug " : self . camp . slug } )
################################
# MANAGE EVENTTYPE VIEWS
class EventTypeListView ( CampViewMixin , ContentTeamPermissionMixin , ListView ) :
2021-07-19 13:06:10 +00:00
""" This view is used by the Content Team to list EventTypes """
2021-07-19 13:00:59 +00:00
model = EventType
template_name = " event_type_list.html "
context_object_name = " event_type_list "
def get_queryset ( self , * args , * * kwargs ) :
qs = super ( ) . get_queryset ( * args , * * kwargs )
qs = qs . annotate (
# only count events for the current camp
event_count = Count (
" events " , distinct = True , filter = Q ( events__track__camp = self . camp )
) ,
# only count EventSessions for the current camp
event_sessions_count = Count (
" event_sessions " ,
distinct = True ,
filter = Q ( event_sessions__camp = self . camp ) ,
) ,
# only count EventSlots for the current camp
event_slots_count = Count (
" event_sessions__event_slots " ,
distinct = True ,
filter = Q ( event_sessions__camp = self . camp ) ,
) ,
)
return qs
class EventTypeDetailView ( CampViewMixin , ContentTeamPermissionMixin , DetailView ) :
2021-07-19 13:06:10 +00:00
""" This view is used by the Content Team to see details for EventTypes """
2021-07-19 13:00:59 +00:00
model = EventType
template_name = " event_type_detail.html "
context_object_name = " event_type "
def get_context_data ( self , * args , * * kwargs ) :
context = super ( ) . get_context_data ( * args , * * kwargs )
context [ " event_sessions " ] = self . camp . event_sessions . filter (
event_type = self . get_object ( )
) . prefetch_related ( " event_location " , " event_slots " )
context [ " events " ] = self . camp . events . filter (
event_type = self . get_object ( )
) . prefetch_related (
" speakers " , " event_slots__event_session__event_location " , " event_type "
)
return context
################################
# MANAGE EVENTLOCATION VIEWS
class EventLocationListView ( CampViewMixin , ContentTeamPermissionMixin , ListView ) :
2021-07-19 13:06:10 +00:00
""" This view is used by the Content Team to list EventLocation objects. """
2021-07-19 13:00:59 +00:00
model = EventLocation
template_name = " event_location_list.html "
context_object_name = " event_location_list "
def get_queryset ( self , * args , * * kwargs ) :
qs = super ( ) . get_queryset ( * args , * * kwargs )
qs = qs . prefetch_related ( " event_sessions__event_slots " , " conflicts " )
return qs
class EventLocationDetailView ( CampViewMixin , ContentTeamPermissionMixin , DetailView ) :
2021-07-19 13:06:10 +00:00
""" This view is used by the Content Team to see details for EventLocation objects """
2021-07-19 13:00:59 +00:00
model = EventLocation
template_name = " event_location_detail.html "
context_object_name = " event_location "
def get_queryset ( self , * args , * * kwargs ) :
qs = super ( ) . get_queryset ( * args , * * kwargs )
qs = qs . prefetch_related (
" conflicts " , " event_sessions__event_slots " , " event_sessions__event_type "
)
return qs
class EventLocationCreateView ( CampViewMixin , ContentTeamPermissionMixin , CreateView ) :
2021-07-19 13:06:10 +00:00
""" This view is used by the Content Team to create EventLocation objects """
2021-07-19 13:00:59 +00:00
model = EventLocation
fields = [ " name " , " icon " , " capacity " , " conflicts " ]
template_name = " event_location_form.html "
def get_form ( self , * args , * * kwargs ) :
form = super ( ) . get_form ( * args , * * kwargs )
form . fields [ " conflicts " ] . queryset = self . camp . event_locations . all ( )
return form
def form_valid ( self , form ) :
location = form . save ( commit = False )
location . camp = self . camp
location . save ( )
form . save_m2m ( )
messages . success (
self . request , f " EventLocation { location . name } has been created "
)
return redirect (
reverse (
" backoffice:event_location_detail " ,
kwargs = { " camp_slug " : self . camp . slug , " slug " : location . slug } ,
)
)
class EventLocationUpdateView ( CampViewMixin , ContentTeamPermissionMixin , UpdateView ) :
2021-07-19 13:06:10 +00:00
""" This view is used by the Content Team to update EventLocation objects """
2021-07-19 13:00:59 +00:00
model = EventLocation
fields = [ " name " , " icon " , " capacity " , " conflicts " ]
template_name = " event_location_form.html "
def get_form ( self , * args , * * kwargs ) :
form = super ( ) . get_form ( * args , * * kwargs )
form . fields [ " conflicts " ] . queryset = self . camp . event_locations . exclude (
pk = self . get_object ( ) . pk
)
return form
def get_success_url ( self ) :
messages . success (
self . request , f " EventLocation { self . get_object ( ) . name } has been updated "
)
return reverse (
" backoffice:event_location_detail " ,
kwargs = { " camp_slug " : self . camp . slug , " slug " : self . get_object ( ) . slug } ,
)
class EventLocationDeleteView ( CampViewMixin , ContentTeamPermissionMixin , DeleteView ) :
2021-07-19 13:06:10 +00:00
""" This view is used by the Content Team to delete EventLocation objects """
2021-07-19 13:00:59 +00:00
model = EventLocation
template_name = " event_location_delete.html "
context_object_name = " event_location "
def delete ( self , * args , * * kwargs ) :
slotsdeleted , slotdetails = self . get_object ( ) . event_slots . all ( ) . delete ( )
sessionsdeleted , sessiondetails = (
self . get_object ( ) . event_sessions . all ( ) . delete ( )
)
return super ( ) . delete ( * args , * * kwargs )
def get_success_url ( self ) :
messages . success (
self . request , f " EventLocation ' { self . get_object ( ) . name } ' has been deleted. "
)
return reverse (
" backoffice:event_location_list " , kwargs = { " camp_slug " : self . camp . slug }
)
################################
# MANAGE EVENT VIEWS
class EventListView ( CampViewMixin , ContentTeamPermissionMixin , ListView ) :
2021-07-19 13:06:10 +00:00
""" This view is used by the Content Team to see Event objects. """
2021-07-19 13:00:59 +00:00
model = Event
template_name = " event_list_backoffice.html "
def get_queryset ( self , * args , * * kwargs ) :
qs = super ( ) . get_queryset ( * args , * * kwargs )
qs = qs . prefetch_related (
" speakers__events " ,
" event_type " ,
" event_slots__event_session__event_location " ,
" tags " ,
)
return qs
class EventDetailView ( CampViewMixin , ContentTeamPermissionMixin , DetailView ) :
2021-07-19 13:06:10 +00:00
""" This view is used by the Content Team to see details for Event objects """
2021-07-19 13:00:59 +00:00
model = Event
template_name = " event_detail_backoffice.html "
class EventUpdateView ( CampViewMixin , ContentTeamPermissionMixin , UpdateView ) :
2021-07-19 13:06:10 +00:00
""" This view is used by the Content Team to update Event objects """
2021-07-19 13:00:59 +00:00
model = Event
fields = [
" title " ,
" abstract " ,
" video_recording " ,
" duration_minutes " ,
" demand " ,
" tags " ,
]
template_name = " event_update.html "
def get_success_url ( self ) :
messages . success ( self . request , " Event has been updated " )
return reverse (
" backoffice:event_detail " ,
kwargs = { " camp_slug " : self . camp . slug , " slug " : self . get_object ( ) . slug } ,
)
class EventDeleteView ( CampViewMixin , ContentTeamPermissionMixin , DeleteView ) :
2021-07-19 13:06:10 +00:00
""" This view is used by the Content Team to delete Event objects """
2021-07-19 13:00:59 +00:00
model = Event
template_name = " event_delete.html "
def delete ( self , * args , * * kwargs ) :
self . get_object ( ) . urls . all ( ) . delete ( )
return super ( ) . delete ( * args , * * kwargs )
def get_success_url ( self ) :
messages . success (
self . request ,
f " Event ' { self . get_object ( ) . title } ' has been deleted! " ,
)
return reverse ( " backoffice:event_list " , kwargs = { " camp_slug " : self . camp . slug } )
class EventScheduleView ( CampViewMixin , ContentTeamPermissionMixin , FormView ) :
""" This view is used by the Content Team to manually schedule Events.
It shows a table with radioselect buttons for the available slots for the
EventType of the Event """
form_class = EventScheduleForm
template_name = " event_schedule.html "
def setup ( self , * args , * * kwargs ) :
super ( ) . setup ( * args , * * kwargs )
self . event = get_object_or_404 (
Event , track__camp = self . camp , slug = kwargs [ " slug " ]
)
def get_form ( self , * args , * * kwargs ) :
form = super ( ) . get_form ( * args , * * kwargs )
self . slots = [ ]
slotindex = 0
# loop over sessions, get free slots
for session in self . camp . event_sessions . filter (
event_type = self . event . event_type ,
event_duration_minutes__gte = self . event . duration_minutes ,
) :
for slot in session . get_available_slots ( ) :
# loop over speakers to see if they are all available
for speaker in self . event . speakers . all ( ) :
if not speaker . is_available ( slot . when ) :
# this speaker is not available, skip this slot
break
else :
# all speakers are available for this slot
self . slots . append ( { " index " : slotindex , " slot " : slot } )
slotindex + = 1
# add the slot choicefield
form . fields [ " slot " ] = forms . ChoiceField (
widget = forms . RadioSelect ,
choices = [ ( s [ " index " ] , s [ " index " ] ) for s in self . slots ] ,
)
return form
def get_context_data ( self , * args , * * kwargs ) :
"""
Add event to context
"""
context = super ( ) . get_context_data ( * args , * * kwargs )
context [ " event " ] = self . event
context [ " event_slots " ] = self . slots
return context
def form_valid ( self , form ) :
"""
Set needed values , save slot and return
"""
slot = self . slots [ int ( form . cleaned_data [ " slot " ] ) ] [ " slot " ]
slot . event = self . event
slot . autoscheduled = False
slot . save ( )
messages . success (
self . request ,
f " { self . event . title } has been scheduled to begin at { slot . when . lower } at location { slot . event_location . name } successfully! " ,
)
2021-08-07 13:31:11 +00:00
add_event_scheduled_email ( slot )
2021-07-19 13:00:59 +00:00
return redirect (
reverse (
" backoffice:event_detail " ,
kwargs = { " camp_slug " : self . camp . slug , " slug " : self . event . slug } ,
)
)
################################
# MANAGE EVENTSESSION VIEWS
class EventSessionCreateTypeSelectView (
CampViewMixin , ContentTeamPermissionMixin , ListView
) :
"""
This view is shown first when creating a new EventSession
"""
model = EventType
template_name = " event_session_create_type_select.html "
context_object_name = " event_type_list "
class EventSessionCreateLocationSelectView (
CampViewMixin , ContentTeamPermissionMixin , ListView
) :
"""
This view is shown second when creating a new EventSession
"""
model = EventLocation
template_name = " event_session_create_location_select.html "
context_object_name = " event_location_list "
def setup ( self , * args , * * kwargs ) :
super ( ) . setup ( * args , * * kwargs )
self . event_type = get_object_or_404 ( EventType , slug = kwargs [ " event_type_slug " ] )
def get_context_data ( self , * args , * * kwargs ) :
"""
Add event_type to context
"""
context = super ( ) . get_context_data ( * args , * * kwargs )
context [ " event_type " ] = self . event_type
return context
class EventSessionFormViewMixin :
"""
A mixin with the stuff shared between EventSession { Create | Update } View
"""
def get_form ( self , * args , * * kwargs ) :
"""
The default range widgets are a bit shit because they eat the help_text and
have no indication of which field is for what . So we add a nice placeholder .
We also limit the event_location dropdown to only the current camps locations .
"""
form = super ( ) . get_form ( * args , * * kwargs )
form . fields [ " when " ] . widget . widgets [ 0 ] . attrs = {
" placeholder " : f " Start Date and Time (YYYY-MM-DD HH:MM). Time zone is { settings . TIME_ZONE } . " ,
}
form . fields [ " when " ] . widget . widgets [ 1 ] . attrs = {
" placeholder " : f " End Date and Time (YYYY-MM-DD HH:MM). Time zone is { settings . TIME_ZONE } . " ,
}
if hasattr ( form . fields , " event_location " ) :
form . fields [ " event_location " ] . queryset = EventLocation . objects . filter (
camp = self . camp
)
return form
def get_context_data ( self , * args , * * kwargs ) :
"""
Add event_type and location and existing sessions to context
"""
context = super ( ) . get_context_data ( * args , * * kwargs )
if not hasattr ( self , " event_type " ) :
self . event_type = self . get_object ( ) . event_type
context [ " event_type " ] = self . event_type
if not hasattr ( self , " event_location " ) :
self . event_location = self . get_object ( ) . event_location
context [ " event_location " ] = self . event_location
context [ " sessions " ] = self . event_type . event_sessions . filter ( camp = self . camp )
return context
class EventSessionCreateView (
CampViewMixin , ContentTeamPermissionMixin , EventSessionFormViewMixin , CreateView
) :
"""
This view is used by the Content Team to create EventSession objects
"""
model = EventSession
fields = [ " description " , " when " , " event_duration_minutes " ]
template_name = " event_session_form.html "
def setup ( self , * args , * * kwargs ) :
super ( ) . setup ( * args , * * kwargs )
self . event_type = get_object_or_404 ( EventType , slug = kwargs [ " event_type_slug " ] )
self . event_location = get_object_or_404 (
EventLocation , camp = self . camp , slug = kwargs [ " event_location_slug " ]
)
def form_valid ( self , form ) :
"""
Set camp and event_type , check for overlaps and save
"""
session = form . save ( commit = False )
session . event_type = self . event_type
session . event_location = self . event_location
session . camp = self . camp
session . save ( )
messages . success ( self . request , f " { session } has been created successfully! " )
return redirect (
reverse (
" backoffice:event_session_list " , kwargs = { " camp_slug " : self . camp . slug }
)
)
class EventSessionListView ( CampViewMixin , ContentTeamPermissionMixin , ListView ) :
"""
This view is used by the Content Team to see EventSession objects .
"""
model = EventSession
template_name = " event_session_list.html "
context_object_name = " event_session_list "
def get_queryset ( self , * args , * * kwargs ) :
qs = super ( ) . get_queryset ( * args , * * kwargs )
qs = qs . prefetch_related ( " event_type " , " event_location " , " event_slots " )
return qs
class EventSessionDetailView ( CampViewMixin , ContentTeamPermissionMixin , DetailView ) :
"""
This view is used by the Content Team to see details for EventSession objects
"""
model = EventSession
template_name = " event_session_detail.html "
context_object_name = " session "
class EventSessionUpdateView (
CampViewMixin , ContentTeamPermissionMixin , EventSessionFormViewMixin , UpdateView
) :
"""
This view is used by the Content Team to update EventSession objects
"""
model = EventSession
fields = [ " when " , " description " , " event_duration_minutes " ]
template_name = " event_session_form.html "
def form_valid ( self , form ) :
"""
Just save , we have a post_save signal which takes care of fixing EventSlots
"""
session = form . save ( )
messages . success ( self . request , f " { session } has been updated successfully! " )
return redirect (
reverse (
" backoffice:event_session_list " , kwargs = { " camp_slug " : self . camp . slug }
)
)
class EventSessionDeleteView ( CampViewMixin , ContentTeamPermissionMixin , DeleteView ) :
"""
This view is used by the Content Team to delete EventSession objects
"""
model = EventSession
template_name = " event_session_delete.html "
context_object_name = " session "
def get ( self , * args , * * kwargs ) :
2021-07-19 13:06:10 +00:00
""" Show a warning if we have something scheduled in this EventSession """
2021-07-19 13:00:59 +00:00
if self . get_object ( ) . event_slots . filter ( event__isnull = False ) . exists ( ) :
messages . warning (
self . request ,
" NOTE: One or more EventSlots in this EventSession has an Event scheduled. Make sure you are deleting the correct session! " ,
)
return super ( ) . get ( * args , * * kwargs )
def delete ( self , * args , * * kwargs ) :
session = self . get_object ( )
session . event_slots . all ( ) . delete ( )
return super ( ) . delete ( * args , * * kwargs )
def get_success_url ( self ) :
messages . success (
self . request ,
" EventSession and related EventSlots was deleted successfully! " ,
)
return reverse (
" backoffice:event_session_list " , kwargs = { " camp_slug " : self . camp . slug }
)
################################
# MANAGE EVENTSLOT VIEWS
class EventSlotListView ( CampViewMixin , ContentTeamPermissionMixin , ListView ) :
2021-07-19 13:06:10 +00:00
""" This view is used by the Content Team to see EventSlot objects. """
2021-07-19 13:00:59 +00:00
model = EventSlot
template_name = " event_slot_list.html "
context_object_name = " event_slot_list "
def get_queryset ( self , * args , * * kwargs ) :
qs = super ( ) . get_queryset ( * args , * * kwargs )
qs = qs . prefetch_related (
" event__speakers " ,
" event_session__event_location " ,
" event_session__event_type " ,
)
return qs
class EventSlotDetailView ( CampViewMixin , ContentTeamPermissionMixin , DetailView ) :
2021-07-19 13:06:10 +00:00
""" This view is used by the Content Team to see details for EventSlot objects """
2021-07-19 13:00:59 +00:00
model = EventSlot
template_name = " event_slot_detail.html "
context_object_name = " event_slot "
class EventSlotUnscheduleView ( CampViewMixin , ContentTeamPermissionMixin , UpdateView ) :
2021-07-19 13:06:10 +00:00
""" This view is used by the Content Team to remove an Event from the schedule/EventSlot """
2021-07-19 13:00:59 +00:00
model = EventSlot
template_name = " event_slot_unschedule.html "
fields = [ ]
context_object_name = " event_slot "
def form_valid ( self , form ) :
event_slot = self . get_object ( )
event = event_slot . event
event_slot . unschedule ( )
messages . success (
self . request ,
f " The Event ' { event . title } ' has been removed from the slot { event_slot } " ,
)
return redirect (
reverse (
" backoffice:event_detail " ,
kwargs = { " camp_slug " : self . camp . slug , " slug " : event . slug } ,
)
)
################################
# AUTOSCHEDULER VIEWS
class AutoScheduleManageView ( CampViewMixin , ContentTeamPermissionMixin , TemplateView ) :
2021-07-19 13:06:10 +00:00
""" Just an index type view with links to the various actions """
2021-07-19 13:00:59 +00:00
template_name = " autoschedule_index.html "
class AutoScheduleCrashCourseView (
CampViewMixin , ContentTeamPermissionMixin , TemplateView
) :
2021-07-19 13:06:10 +00:00
""" A short crash course on the autoscheduler """
2021-07-19 13:00:59 +00:00
template_name = " autoschedule_crash_course.html "
class AutoScheduleValidateView ( CampViewMixin , ContentTeamPermissionMixin , FormView ) :
""" This view is used to validate schedules. It uses the AutoScheduler and can
either validate the currently applied schedule or a new similar schedule , or a
brand new schedule """
template_name = " autoschedule_validate.html "
form_class = AutoScheduleValidateForm
def form_valid ( self , form ) :
# initialise AutoScheduler
scheduler = AutoScheduler ( camp = self . camp )
# get autoschedule
if form . cleaned_data [ " schedule " ] == " current " :
autoschedule = scheduler . build_current_autoschedule ( )
message = f " The currently scheduled Events form a valid schedule! AutoScheduler has { len ( scheduler . autoslots ) } Slots based on { scheduler . event_sessions . count ( ) } EventSessions for { scheduler . event_types . count ( ) } EventTypes. { scheduler . events . count ( ) } Events in the schedule. "
elif form . cleaned_data [ " schedule " ] == " similar " :
original_autoschedule = scheduler . build_current_autoschedule ( )
autoschedule , diff = scheduler . calculate_similar_autoschedule (
original_autoschedule
)
message = f " The new similar schedule is valid! AutoScheduler has { len ( scheduler . autoslots ) } Slots based on { scheduler . event_sessions . count ( ) } EventSessions for { scheduler . event_types . count ( ) } EventTypes. Differences to the current schedule: { len ( diff [ ' event_diffs ' ] ) } Event diffs and { len ( diff [ ' slot_diffs ' ] ) } Slot diffs. "
elif form . cleaned_data [ " schedule " ] == " new " :
autoschedule = scheduler . calculate_autoschedule ( )
message = f " The new schedule is valid! AutoScheduler has { len ( scheduler . autoslots ) } Slots based on { scheduler . event_sessions . count ( ) } EventSessions for { scheduler . event_types . count ( ) } EventTypes. { scheduler . events . count ( ) } Events in the schedule. "
# check validity
valid , violations = scheduler . is_valid ( autoschedule , return_violations = True )
if valid :
messages . success ( self . request , message )
else :
messages . error ( self . request , " Schedule is NOT valid! " )
message = " Schedule violations:<br> "
for v in violations :
message + = v + " <br> "
messages . error ( self . request , mark_safe ( message ) )
return redirect (
reverse (
" backoffice:autoschedule_validate " , kwargs = { " camp_slug " : self . camp . slug }
)
)
class AutoScheduleDiffView ( CampViewMixin , ContentTeamPermissionMixin , TemplateView ) :
template_name = " autoschedule_diff.html "
def get_context_data ( self , * * kwargs ) :
context = super ( ) . get_context_data ( * * kwargs )
scheduler = AutoScheduler ( camp = self . camp )
autoschedule , diff = scheduler . calculate_similar_autoschedule ( )
context [ " diff " ] = diff
context [ " scheduler " ] = scheduler
return context
class AutoScheduleApplyView ( CampViewMixin , ContentTeamPermissionMixin , FormView ) :
""" This view is used by the Content Team to apply a new schedules by unscheduling
all autoscheduled Events , and scheduling all Event / Slot combinations in the schedule .
TODO : see comment in program . autoscheduler . AutoScheduler . apply ( ) method .
"""
template_name = " autoschedule_apply.html "
form_class = AutoScheduleApplyForm
def form_valid ( self , form ) :
# initialise AutoScheduler
scheduler = AutoScheduler ( camp = self . camp )
# get autoschedule
if form . cleaned_data [ " schedule " ] == " similar " :
autoschedule , diff = scheduler . calculate_similar_autoschedule ( )
elif form . cleaned_data [ " schedule " ] == " new " :
autoschedule = scheduler . calculate_autoschedule ( )
# check validity
valid , violations = scheduler . is_valid ( autoschedule , return_violations = True )
if valid :
# schedule is valid, apply it
deleted , created = scheduler . apply ( autoschedule )
messages . success (
self . request ,
f " Schedule has been applied! { deleted } Events removed from schedule, { created } new Events scheduled. Differences to the previous schedule: { len ( diff [ ' event_diffs ' ] ) } Event diffs and { len ( diff [ ' slot_diffs ' ] ) } Slot diffs. " ,
)
else :
messages . error ( self . request , " Schedule is NOT valid, cannot apply! " )
return redirect (
reverse (
" backoffice:autoschedule_apply " , kwargs = { " camp_slug " : self . camp . slug }
)
)
class AutoScheduleDebugEventSlotUnavailabilityView (
CampViewMixin , ContentTeamPermissionMixin , TemplateView
) :
template_name = " autoschedule_debug_slots.html "
def get_context_data ( self , * * kwargs ) :
scheduler = AutoScheduler ( camp = self . camp )
context = {
" scheduler " : scheduler ,
}
return context
class AutoScheduleDebugEventConflictsView (
CampViewMixin , ContentTeamPermissionMixin , TemplateView
) :
template_name = " autoschedule_debug_events.html "
def get_context_data ( self , * * kwargs ) :
scheduler = AutoScheduler ( camp = self . camp )
context = {
" scheduler " : scheduler ,
}
return context