Skip to content

Commit 8ab7a30

Browse files
committed
Merged in [18724] from jennifer@painless-security.com:
Allow cancellation of individual sessions of multi-session interim meeting. Fixes ietf-tools#2959. - Legacy-Id: 18757 Note: SVN reference [18724] has been migrated to Git commit 79971e1
2 parents 73b771d + 79971e1 commit 8ab7a30

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
@@ -468,7 +468,8 @@ def get_announcement_initial(meeting, is_change=False):
468468
type = 'BOF'
469469

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

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

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

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

9581025
unique_constraints_dict = None
9591026

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

0 commit comments

Comments
 (0)