Skip to content

Commit 79971e1

Browse files
Allow cancellation of individual sessions of multi-session interim meeting. Fixes ietf-tools#2959. Commit ready for merge.
- Legacy-Id: 18724
1 parent 1b19353 commit 79971e1

14 files changed

Lines changed: 789 additions & 117 deletions

ietf/meeting/ajax.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111
from ietf.meeting.helpers import get_meeting, get_schedule, schedule_permissions, get_person_by_email, get_schedule_by_name
1212
from ietf.meeting.models import TimeSlot, Session, Schedule, Room, Constraint, SchedTimeSessAssignment, ResourceAssociation
1313
from ietf.meeting.views import edit_timeslots, edit_schedule
14-
from ietf.meeting.utils import only_sessions_that_can_meet
15-
from ietf.meeting.utils import add_event_info_to_session_qs
1614

1715
import debug # pyflakes:ignore
1816

@@ -433,11 +431,7 @@ def session_json(request, num, sessionid):
433431
def sessions_json(request, num):
434432
meeting = get_meeting(num)
435433

436-
sessions = add_event_info_to_session_qs(
437-
only_sessions_that_can_meet(meeting.session_set),
438-
requested_time=True,
439-
requested_by=True,
440-
)
434+
sessions = meeting.session_set.that_can_meet().with_requested_time().with_requested_by()
441435

442436
sess1_dict = [ x.json_dict(request.build_absolute_uri('/')) for x in sessions ]
443437
return HttpResponse(json.dumps(sess1_dict, sort_keys=True, indent=2),

ietf/meeting/helpers.py

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,8 @@ def get_announcement_initial(meeting, is_change=False):
467467
type = 'BOF'
468468

469469
assignments = SchedTimeSessAssignment.objects.filter(
470-
schedule__in=[meeting.schedule, meeting.schedule.base if meeting.schedule else None]
470+
schedule__in=[meeting.schedule, meeting.schedule.base if meeting.schedule else None],
471+
session__in=meeting.session_set.not_canceled()
471472
).order_by('timeslot__time')
472473

473474
initial['subject'] = '{name} ({acronym}) {type} {desc} Meeting: {date}{change}'.format(
@@ -614,7 +615,7 @@ def send_interim_announcement_request(meeting):
614615
context,
615616
cc_list)
616617

617-
def send_interim_cancellation_notice(meeting):
618+
def send_interim_meeting_cancellation_notice(meeting):
618619
"""Sends an email that a scheduled interim meeting has been cancelled."""
619620
session = meeting.session_set.first()
620621
group = session.group
@@ -627,9 +628,39 @@ def send_interim_cancellation_notice(meeting):
627628
date=meeting.date.strftime('%Y-%m-%d'))
628629
start_time = session.official_timeslotassignment().timeslot.time
629630
end_time = start_time + session.requested_duration
630-
from ietf.meeting.utils import add_event_info_to_session_qs
631-
is_multi_day = add_event_info_to_session_qs(meeting.session_set.all()).filter(current_status='sched').count() > 1
632-
template = 'meeting/interim_cancellation_notice.txt'
631+
is_multi_day = session.meeting.session_set.with_current_status().filter(current_status='sched').count() > 1
632+
template = 'meeting/interim_meeting_cancellation_notice.txt'
633+
context = locals()
634+
send_mail(None,
635+
to_email,
636+
from_email,
637+
subject,
638+
template,
639+
context,
640+
cc=cc_list)
641+
642+
643+
def send_interim_session_cancellation_notice(session):
644+
"""Sends an email that one session of a scheduled interim meeting has been cancelled."""
645+
group = session.group
646+
start_time = session.official_timeslotassignment().timeslot.time
647+
end_time = start_time + session.requested_duration
648+
(to_email, cc_list) = gather_address_lists('interim_cancelled',group=group)
649+
from_email = settings.INTERIM_ANNOUNCE_FROM_EMAIL_PROGRAM if group.type_id=='program' else settings.INTERIM_ANNOUNCE_FROM_EMAIL_DEFAULT
650+
651+
if session.name:
652+
description = '"%s" session' % session.name
653+
else:
654+
description = 'interim meeting session'
655+
656+
subject = '{group} ({acronym}) {type} {description} cancelled (was {date})'.format(
657+
group=group.name,
658+
acronym=group.acronym,
659+
type=group.type.slug.upper(),
660+
description=description,
661+
date=start_time.date().strftime('%Y-%m-%d'))
662+
is_multi_day = session.meeting.session_set.with_current_status().filter(current_status='sched').count() > 1
663+
template = 'meeting/interim_session_cancellation_notice.txt'
633664
context = locals()
634665
send_mail(None,
635666
to_email,

ietf/meeting/models.py

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717

1818
from django.core.validators import MinValueValidator, RegexValidator
1919
from django.db import models
20-
from django.db.models import Max
20+
from django.db.models import Max, Subquery, OuterRef, TextField, Value
21+
from django.db.models.functions import Coalesce
2122
from django.conf import settings
2223
# mostly used by json_dict()
2324
#from django.template.defaultfilters import slugify, date as date_format, time as time_format
@@ -924,12 +925,78 @@ def __str__(self):
924925
constraint_cache_uses = 0
925926
constraint_cache_initials = 0
926927

928+
class SessionQuerySet(models.QuerySet):
929+
def with_current_status(self):
930+
"""Annotate session with its current status
931+
932+
Adds current_status, containing the text representation of the status.
933+
"""
934+
return self.annotate(
935+
# coalesce with '' to avoid nulls which give funny
936+
# results, e.g. .exclude(current_status='canceled') also
937+
# skips rows with null in them
938+
current_status=Coalesce(
939+
Subquery(
940+
SchedulingEvent.objects.filter(
941+
session=OuterRef('pk')
942+
).order_by(
943+
'-time', '-id'
944+
).values('status')[:1]),
945+
Value(''),
946+
output_field=TextField()),
947+
)
948+
949+
def with_requested_by(self):
950+
"""Annotate session with requested_by field
951+
952+
Adds requested_by field - pk of the Person who made the request
953+
"""
954+
return self.annotate(
955+
requested_by=Subquery(
956+
SchedulingEvent.objects.filter(
957+
session=OuterRef('pk')
958+
).order_by(
959+
'time', 'id'
960+
).values('by')[:1]),
961+
)
962+
963+
def with_requested_time(self):
964+
"""Annotate session with requested_time field"""
965+
return self.annotate(
966+
requested_time=Subquery(
967+
SchedulingEvent.objects.filter(
968+
session=OuterRef('pk')
969+
).order_by(
970+
'time', 'id'
971+
).values('time')[:1]),
972+
)
973+
974+
def not_canceled(self):
975+
"""Queryset containing all sessions not canceled
976+
977+
Results annotated with current_status
978+
"""
979+
return self.with_current_status().exclude(current_status__in=Session.CANCELED_STATUSES)
980+
981+
def that_can_meet(self):
982+
"""Queryset containing sessions that can meet
983+
984+
Results annotated with current_status
985+
"""
986+
return self.with_current_status().exclude(
987+
current_status__in=['notmeet', 'disappr', 'deleted', 'apprw']
988+
).filter(
989+
type__slug='regular'
990+
)
991+
992+
927993
class Session(models.Model):
928994
"""Session records that a group should have a session on the
929995
meeting (time and location is stored in a TimeSlot) - if multiple
930996
timeslots are needed, multiple sessions will have to be created.
931997
Training sessions and similar are modeled by filling in a
932998
responsible group (e.g. Edu team) and filling in the name."""
999+
objects = SessionQuerySet.as_manager() # sets default query manager
9331000
meeting = ForeignKey(Meeting)
9341001
name = models.CharField(blank=True, max_length=255, help_text="Name of session, in case the session has a purpose rather than just being a group meeting.")
9351002
short = models.CharField(blank=True, max_length=32, help_text="Short version of 'name' above, for use in filenames.")
@@ -951,6 +1018,8 @@ class Session(models.Model):
9511018

9521019
unique_constraints_dict = None
9531020

1021+
CANCELED_STATUSES = ['canceled', 'canceledpa']
1022+
9541023
# Should work on how materials are captured so that deleted things are no longer associated with the session
9551024
# (We can keep the information about something being added to and removed from a session in the document's history)
9561025
def get_material(self, material_type, only_one):

0 commit comments

Comments
 (0)