Skip to content

Commit b143dd4

Browse files
committed
Merged in ^/branch/dash/automatic-scheduler@17395, which adds groundwor for
upcoming automatic scheduling assistance: . Added a management command to create a dummy IETF 999 meeting. . Added display of new constraints and joint sessions to agenda builder interface. . The new timerange, time_relation and wg_adjacent constraints, along with the joint_with_groups option, are now reflected in the special requests field. This allows them to be taken into account while scheduling sessions. . Clarified the wording in the session request form regarding conflicts with BOFs. . Added support for structured entry and storage of joint sessions in meetings: - Also adds additional tests for the SessionForm - Fixes a javascript error in session requests for non-WG groups, that could cause incorrect form behaviour. - Expands the tests added in [17289] a bit. . Added support for the timerange, wg_adjacent and time_relation constraints. This adds three new constraints to the database and relevant UIs: - timerange: "This WG can't meet during these timeframes" - wg_adjacent: "Schedule adjacent to another WG (directly following, no breaks, same room)" - time_relation: schedule the two sessions of one WG on subsequent days or with at least one day seperation - Legacy-Id: 17605 Note: SVN reference [17289] has been migrated to Git commit a227813
2 parents 6f4372f + 56d73e4 commit b143dd4

18 files changed

Lines changed: 5480 additions & 43 deletions

File tree

ietf/meeting/management/commands/create_dummy_meeting.py

Lines changed: 4651 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Copyright The IETF Trust 2020, All Rights Reserved
2+
# -*- coding: utf-8 -*-
3+
# Generated by Django 1.11.27 on 2020-02-11 04:47
4+
from __future__ import unicode_literals
5+
6+
from django.db import migrations, models
7+
8+
9+
def forward(apps, schema_editor):
10+
ConstraintName = apps.get_model("name", "ConstraintName")
11+
ConstraintName.objects.create(slug="timerange", desc="", penalty=100000,
12+
name="Can't meet within timerange")
13+
ConstraintName.objects.create(slug="time_relation", desc="", penalty=1000,
14+
name="Preference for time between sessions")
15+
ConstraintName.objects.create(slug="wg_adjacent", desc="", penalty=10000,
16+
name="Request for adjacent scheduling with another WG")
17+
18+
19+
def reverse(apps, schema_editor):
20+
ConstraintName = apps.get_model("name", "ConstraintName")
21+
ConstraintName.objects.filter(slug__in=["timerange", "time_relation", "wg_adjacent"]).delete()
22+
23+
24+
class Migration(migrations.Migration):
25+
26+
dependencies = [
27+
('name', '0010_timerangename'),
28+
('meeting', '0026_cancel_107_sessions'),
29+
]
30+
31+
operations = [
32+
migrations.RemoveField(
33+
model_name='constraint',
34+
name='day',
35+
),
36+
migrations.AddField(
37+
model_name='constraint',
38+
name='time_relation',
39+
field=models.CharField(blank=True, choices=[('subsequent-days', 'Schedule the sessions on subsequent days'), ('one-day-seperation', 'Leave at least one free day in between the two sessions')], max_length=200),
40+
),
41+
migrations.AddField(
42+
model_name='constraint',
43+
name='timeranges',
44+
field=models.ManyToManyField(to='name.TimerangeName'),
45+
),
46+
migrations.AddField(
47+
model_name='session',
48+
name='joint_with_groups',
49+
field=models.ManyToManyField(related_name='sessions_joint_in', to='group.Group'),
50+
),
51+
migrations.RunPython(forward, reverse),
52+
]

ietf/meeting/models.py

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
from ietf.doc.models import Document
2929
from ietf.group.models import Group
3030
from ietf.group.utils import can_manage_materials
31-
from ietf.name.models import MeetingTypeName, TimeSlotTypeName, SessionStatusName, ConstraintName, RoomResourceName, ImportantDateName
31+
from ietf.name.models import MeetingTypeName, TimeSlotTypeName, SessionStatusName, ConstraintName, RoomResourceName, ImportantDateName, TimerangeName
3232
from ietf.person.models import Person
3333
from ietf.utils.decorators import memoize
3434
from ietf.utils.storage import NoLocationMigrationFileSystemStorage
@@ -815,27 +815,42 @@ def slug(self):
815815
class Constraint(models.Model):
816816
"""
817817
Specifies a constraint on the scheduling.
818-
One type (name=conflic?) of constraint is between source WG and target WG,
819-
e.g. some kind of conflict.
820-
Another type (name=bethere) of constraint is between source WG and
821-
availability of a particular Person, usually an AD.
822-
A third type (name=avoidday) of constraint is between source WG and
823-
a particular day of the week, specified in day.
818+
Available types are:
819+
- conflict/conflic2/conflic3: a conflict between source and target WG/session,
820+
with varying priority. The first is used for a chair conflict, the second for
821+
technology overlap, third for key person conflict
822+
- bethere: a constraint between source WG and a particular person
823+
- timerange: can not meet during these times
824+
- time_relation: preference for a time difference between sessions
825+
- wg_adjacent: request for source WG to be adjacent (directly before or after,
826+
no breaks, same room) the target WG
824827
"""
828+
TIME_RELATION_CHOICES = (
829+
('subsequent-days', 'Schedule the sessions on subsequent days'),
830+
('one-day-seperation', 'Leave at least one free day in between the two sessions'),
831+
)
825832
meeting = ForeignKey(Meeting)
826833
source = ForeignKey(Group, related_name="constraint_source_set")
827834
target = ForeignKey(Group, related_name="constraint_target_set", null=True)
828835
person = ForeignKey(Person, null=True, blank=True)
829-
day = models.DateTimeField(null=True, blank=True)
830836
name = ForeignKey(ConstraintName)
837+
time_relation = models.CharField(max_length=200, choices=TIME_RELATION_CHOICES, blank=True)
838+
timeranges = models.ManyToManyField(TimerangeName)
831839

832840
active_status = None
833841

834842
def __str__(self):
835843
return u"%s %s target=%s person=%s" % (self.source, self.name.name.lower(), self.target, self.person)
836844

837845
def brief_display(self):
838-
if self.target and self.person:
846+
if self.name.slug == "wg_adjacent":
847+
return "Adjacent with %s" % self.target.acronym
848+
elif self.name.slug == "time_relation":
849+
return self.get_time_relation_display()
850+
elif self.name.slug == "timerange":
851+
timeranges_str = ", ".join([t.desc for t in self.timeranges.all()])
852+
return "Can't meet %s" % timeranges_str
853+
elif self.target and self.person:
839854
return "%s ; %s" % (self.target.acronym, self.person)
840855
elif self.target and not self.person:
841856
return "%s " % (self.target.acronym)
@@ -857,6 +872,13 @@ def json_dict(self, host_scheme):
857872
if self.target is not None:
858873
ct1['target_href'] = urljoin(host_scheme, self.target.json_url())
859874
ct1['meeting_href'] = urljoin(host_scheme, self.meeting.json_url())
875+
if self.time_relation:
876+
ct1['time_relation'] = self.time_relation
877+
ct1['time_relation_display'] = self.get_time_relation_display()
878+
if self.timeranges.count():
879+
ct1['timeranges_cant_meet'] = [t.slug for t in self.timeranges.all()]
880+
timeranges_str = ", ".join([t.desc for t in self.timeranges.all()])
881+
ct1['timeranges_display'] = "Can't meet %s" % timeranges_str
860882
return ct1
861883

862884

@@ -890,6 +912,7 @@ class Session(models.Model):
890912
short = models.CharField(blank=True, max_length=32, help_text="Short version of 'name' above, for use in filenames.")
891913
type = ForeignKey(TimeSlotTypeName)
892914
group = ForeignKey(Group) # The group type historically determined the session type. BOFs also need to be added as a group. Note that not all meeting requests have a natural group to associate with.
915+
joint_with_groups = models.ManyToManyField(Group, related_name='sessions_joint_in')
893916
attendees = models.IntegerField(null=True, blank=True)
894917
agenda_note = models.CharField(blank=True, max_length=255)
895918
requested_duration = models.DurationField(default=datetime.timedelta(0))
@@ -1013,6 +1036,9 @@ def can_manage_materials(self, user):
10131036

10141037
def is_material_submission_cutoff(self):
10151038
return datetime.date.today() > self.meeting.get_submission_correction_date()
1039+
1040+
def joint_with_groups_acronyms(self):
1041+
return [group.acronym for group in self.joint_with_groups.all()]
10161042

10171043
def __str__(self):
10181044
if self.meeting.type_id == "interim":
@@ -1108,6 +1134,7 @@ def json_dict(self, host_scheme):
11081134
sess1['bof'] = str(self.group.is_bof())
11091135
sess1['agenda_note'] = self.agenda_note
11101136
sess1['attendees'] = str(self.attendees)
1137+
sess1['joint_with_groups'] = self.joint_with_groups_acronyms()
11111138

11121139
# fish out scheduling information - eventually, we should pick
11131140
# this out in the caller instead

ietf/meeting/tests_api.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright The IETF Trust 2013-2019, All Rights Reserved
1+
# Copyright The IETF Trust 2013-2020, All Rights Reserved
22
# -*- coding: utf-8 -*-
33

44

@@ -12,6 +12,7 @@
1212

1313
import debug # pyflakes:ignore
1414

15+
from ietf.name.models import TimerangeName
1516
from ietf.group.models import Group
1617
from ietf.meeting.models import Schedule, TimeSlot, Session, SchedTimeSessAssignment, Meeting, Constraint
1718
from ietf.meeting.test_data import make_meeting_test_data
@@ -119,10 +120,23 @@ def test_constraints_json(self):
119120
person=Person.objects.get(user__username="ad"),
120121
name_id="bethere")
121122

123+
c_adjacent = Constraint.objects.create(meeting=meeting, source=session.group,
124+
target=Group.objects.get(acronym="irg"),
125+
name_id="wg_adjacent")
126+
127+
c_time_relation = Constraint.objects.create(meeting=meeting, source=session.group,
128+
time_relation='subsequent-days',
129+
name_id="time_relation")
130+
131+
c_timerange = Constraint.objects.create(meeting=meeting, source=session.group,
132+
name_id="timerange")
133+
c_timerange.timeranges.set(TimerangeName.objects.filter(slug__startswith='monday'))
134+
122135
r = self.client.get(urlreverse("ietf.meeting.ajax.session_constraints", kwargs=dict(num=meeting.number, sessionid=session.pk)))
123136
self.assertEqual(r.status_code, 200)
124137
constraints = r.json()
125-
self.assertEqual(set([c_ames.pk, c_person.pk]), set(c["constraint_id"] for c in constraints))
138+
expected_keys = set([c_ames.pk, c_person.pk, c_adjacent.pk, c_time_relation.pk, c_timerange.pk])
139+
self.assertEqual(expected_keys, set(c["constraint_id"] for c in constraints))
126140

127141
def test_meeting_json(self):
128142
meeting = make_meeting_test_data()

ietf/name/admin.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright The IETF Trust 2016-2019, All Rights Reserved
1+
# Copyright The IETF Trust 2010-2020, All Rights Reserved
22
from django.contrib import admin
33

44
from ietf.name.models import (
@@ -10,7 +10,7 @@
1010
LiaisonStatementState, LiaisonStatementTagName, MeetingTypeName, NomineePositionStateName,
1111
ReviewRequestStateName, ReviewResultName, ReviewTypeName, RoleName, RoomResourceName,
1212
SessionStatusName, StdLevelName, StreamName, TimeSlotTypeName, TopicAudienceName,
13-
DocUrlTagName, ReviewAssignmentStateName, ReviewerQueuePolicyName)
13+
DocUrlTagName, ReviewAssignmentStateName, ReviewerQueuePolicyName, TimerangeName)
1414

1515
from ietf.stats.models import CountryAlias
1616

@@ -79,5 +79,6 @@ class ImportantDateNameAdmin(NameAdmin):
7979
admin.site.register(StdLevelName, NameAdmin)
8080
admin.site.register(StreamName, NameAdmin)
8181
admin.site.register(TimeSlotTypeName, NameAdmin)
82+
admin.site.register(TimerangeName, NameAdmin)
8283
admin.site.register(TopicAudienceName, NameAdmin)
8384
admin.site.register(DocUrlTagName, NameAdmin)

0 commit comments

Comments
 (0)