Skip to content

Commit 45ed2c5

Browse files
committed
Add support in the new meeting schedule editor for making a tombstone
session when rescheduling a session after the schedule is made the official meeting schedule. Show both cancelled and rescheduled sessions as tombstones in the new meeting schedule editor. Add support for showing rescheduled tombstones in the meeting agenda views. Adjust the Secretariat session tool so that it's not possible to (re)cancel cancelled or rescheduled tombstones. - Legacy-Id: 18108
1 parent 4678f0b commit 45ed2c5

14 files changed

Lines changed: 320 additions & 126 deletions

File tree

ietf/meeting/helpers.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,10 +212,15 @@ def preprocess_assignments_for_agenda(assignments_queryset, meeting, extra_prefe
212212
parents = Group.objects.filter(pk__in=parent_id_set)
213213
parent_replacements = find_history_replacements_active_at(parents, meeting_time)
214214

215+
timeslot_by_session_pk = {a.session_id: a.timeslot for a in assignments}
216+
215217
for a in assignments:
216218
if a.session and a.session.historic_group and a.session.historic_group.parent_id:
217219
a.session.historic_group.historic_parent = parent_replacements.get(a.session.historic_group.parent_id)
218220

221+
if a.session.current_status == 'resched':
222+
a.session.rescheduled_to = timeslot_by_session_pk.get(a.session.tombstone_for_id)
223+
219224
for d in a.session.prefetched_active_materials:
220225
# make sure these are precomputed with the meeting instead
221226
# of having to look it up
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Copyright The IETF Trust 2020, All Rights Reserved
2+
3+
from django.db import migrations, models
4+
import django.db.models.deletion
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
('meeting', '0028_auto_20200501_0139'),
11+
]
12+
13+
operations = [
14+
migrations.AddField(
15+
model_name='session',
16+
name='tombstone_for',
17+
field=models.ForeignKey(blank=True, help_text='This session is the tombstone for a session that was rescheduled', null=True, on_delete=django.db.models.deletion.CASCADE, to='meeting.Session'),
18+
),
19+
]

ietf/meeting/models.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -923,6 +923,8 @@ class Session(models.Model):
923923
modified = models.DateTimeField(auto_now=True)
924924
remote_instructions = models.CharField(blank=True,max_length=1024)
925925

926+
tombstone_for = models.ForeignKey('Session', blank=True, null=True, help_text="This session is the tombstone for a session that was rescheduled", on_delete=models.CASCADE)
927+
926928
materials = models.ManyToManyField(Document, through=SessionPresentation, blank=True)
927929
resources = models.ManyToManyField(ResourceAssociation, blank=True)
928930

ietf/meeting/tests_js.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@
1616

1717
from ietf.doc.factories import DocumentFactory
1818
from ietf.group import colors
19+
from ietf.person.models import Person
1920
from ietf.meeting.factories import SessionFactory
2021
from ietf.meeting.test_data import make_meeting_test_data
2122
from ietf.meeting.models import Schedule, SchedTimeSessAssignment, Session, Room, TimeSlot, Constraint, ConstraintName
23+
from ietf.meeting.models import SchedulingEvent, SessionStatusName
2224
from ietf.utils.test_runner import IetfLiveServerTestCase
2325
from ietf.utils.pipe import pipe
2426
from ietf import settings
@@ -107,6 +109,12 @@ def test_edit_meeting_schedule(self):
107109

108110
s2b = Session.objects.create(meeting=meeting, group=s2.group, attendees=10, requested_duration=datetime.timedelta(minutes=60), type_id='regular')
109111

112+
SchedulingEvent.objects.create(
113+
session=s2b,
114+
status=SessionStatusName.objects.get(slug='appr'),
115+
by=Person.objects.get(name='(System)'),
116+
)
117+
110118
Constraint.objects.create(
111119
meeting=meeting,
112120
source=s1.group,
@@ -226,7 +234,7 @@ def test_edit_meeting_schedule(self):
226234
self.assertTrue(s1_element.is_displayed())
227235

228236
# hide timeslots
229-
self.driver.find_element_by_css_selector(".timeslot-group-toggles button".format(s1.group.parent.acronym)).click()
237+
self.driver.find_element_by_css_selector(".timeslot-group-toggles button").click()
230238
self.assertTrue(self.driver.find_element_by_css_selector("#timeslot-group-toggles-modal").is_displayed())
231239
self.driver.find_element_by_css_selector("#timeslot-group-toggles-modal [value=\"{}\"]".format("ts-group-{}-{}".format(slot2.time.strftime("%Y%m%d-%H%M"), int(slot2.duration.total_seconds() / 60)))).click()
232240
self.driver.find_element_by_css_selector("#timeslot-group-toggles-modal [data-dismiss=\"modal\"]").click()

ietf/meeting/tests_views.py

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,10 +1049,14 @@ def test_edit_meeting_schedule(self):
10491049
self.assertEqual(r.status_code, 403)
10501050

10511051
# turn us into owner
1052-
meeting.schedule.owner = Person.objects.get(user__username="secretary")
1053-
meeting.schedule.save()
1052+
schedule = meeting.schedule
1053+
schedule.owner = Person.objects.get(user__username="secretary")
1054+
schedule.save()
1055+
1056+
meeting.schedule = None
1057+
meeting.save()
10541058

1055-
url = urlreverse("ietf.meeting.views.edit_meeting_schedule", kwargs=dict(num=meeting.number, owner=meeting.schedule.owner_email(), name=meeting.schedule.name))
1059+
url = urlreverse("ietf.meeting.views.edit_meeting_schedule", kwargs=dict(num=meeting.number, owner=schedule.owner_email(), name=schedule.name))
10561060
r = self.client.get(url)
10571061
q = PyQuery(r.content)
10581062
self.assertTrue(not q("em:contains(\"You can't edit this schedule\")"))
@@ -1065,25 +1069,52 @@ def test_edit_meeting_schedule(self):
10651069
'timeslot': timeslots[0].pk,
10661070
'session': s1.pk,
10671071
})
1068-
self.assertEqual(r.content, b"OK")
1069-
self.assertEqual(SchedTimeSessAssignment.objects.get(schedule=meeting.schedule, session=s1).timeslot, timeslots[0])
1072+
self.assertEqual(json.loads(r.content)['success'], True)
1073+
self.assertEqual(SchedTimeSessAssignment.objects.get(schedule=schedule, session=s1).timeslot, timeslots[0])
10701074

1071-
# move assignment
1075+
# move assignment on unofficial schedule
10721076
r = self.client.post(url, {
10731077
'action': 'assign',
10741078
'timeslot': timeslots[1].pk,
10751079
'session': s1.pk,
10761080
})
1077-
self.assertEqual(r.content, b"OK")
1078-
self.assertEqual(SchedTimeSessAssignment.objects.get(schedule=meeting.schedule, session=s1).timeslot, timeslots[1])
1081+
self.assertEqual(json.loads(r.content)['success'], True)
1082+
self.assertEqual(SchedTimeSessAssignment.objects.get(schedule=schedule, session=s1).timeslot, timeslots[1])
1083+
1084+
# move assignment on official schedule, leaving tombstone
1085+
meeting.schedule = schedule
1086+
meeting.save()
1087+
SchedulingEvent.objects.create(
1088+
session=s1,
1089+
status=SessionStatusName.objects.get(slug='sched'),
1090+
by=Person.objects.get(name='(System)')
1091+
)
1092+
r = self.client.post(url, {
1093+
'action': 'assign',
1094+
'timeslot': timeslots[0].pk,
1095+
'session': s1.pk,
1096+
})
1097+
json_content = json.loads(r.content)
1098+
self.assertEqual(json_content['success'], True)
1099+
self.assertEqual(SchedTimeSessAssignment.objects.get(schedule=schedule, session=s1).timeslot, timeslots[0])
1100+
1101+
sessions_for_group = Session.objects.filter(group=s1.group, meeting=meeting)
1102+
self.assertEqual(len(sessions_for_group), 2)
1103+
s_tombstone = [s for s in sessions_for_group if s != s1][0]
1104+
self.assertEqual(s_tombstone.tombstone_for, s1)
1105+
tombstone_event = SchedulingEvent.objects.get(session=s_tombstone)
1106+
self.assertEqual(tombstone_event.status_id, 'resched')
1107+
1108+
self.assertEqual(SchedTimeSessAssignment.objects.get(schedule=schedule, session=s_tombstone).timeslot, timeslots[1])
1109+
self.assertTrue(PyQuery(json_content['tombstone'])("#session{}.tombstone".format(s_tombstone.pk)).html())
10791110

10801111
# unassign
10811112
r = self.client.post(url, {
10821113
'action': 'unassign',
10831114
'session': s1.pk,
10841115
})
1085-
self.assertEqual(r.content, b"OK")
1086-
self.assertEqual(list(SchedTimeSessAssignment.objects.filter(schedule=meeting.schedule, session=s1)), [])
1116+
self.assertEqual(json.loads(r.content)['success'], True)
1117+
self.assertEqual(list(SchedTimeSessAssignment.objects.filter(schedule=schedule, session=s1)), [])
10871118

10881119

10891120
def test_copy_meeting_schedule(self):

0 commit comments

Comments
 (0)