Skip to content

Commit 7bf5cb1

Browse files
committed
Merged in [19103] from jennifer@painless-security.com:
Add ConstraintNames for chair, tech, and key participant conflicts. Replace temporary UI workaround with proper conflict type handling. Fixes ietf-tools#3083. - Legacy-Id: 19123 Note: SVN reference [19103] has been migrated to Git commit 66b9c41
2 parents 5501d9e + 66b9c41 commit 7bf5cb1

17 files changed

Lines changed: 758 additions & 446 deletions

ietf/meeting/management/commands/generate_schedule.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -583,7 +583,7 @@ def __init__(self, stdout, meeting, session_db, business_constraint_costs, verbo
583583
self.last_cost = None
584584

585585
for constraint_db in constraints_db:
586-
if constraint_db.name.slug in ['conflict', 'conflic2', 'conflic3']:
586+
if constraint_db.name.is_group_conflict:
587587
self.conflict_groups[constraint_db.target.acronym] += constraint_db.name.penalty
588588
elif constraint_db.name.slug == 'bethere':
589589
self.conflict_people.add(constraint_db.person.pk)
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Generated by Django 2.2.20 on 2021-05-13 07:51
2+
3+
from django.db import migrations
4+
5+
6+
replacement_slugs = (
7+
('conflict', 'chair_conflict'),
8+
('conflic2', 'tech_overlap'),
9+
('conflic3', 'key_participant'),
10+
)
11+
12+
13+
def affected(constraint_qs):
14+
"""Filter constraints, keeping only those to be updated"""
15+
# The constraints were renamed in the UI in commit 16699 on 2019-09-03.
16+
# This was between meetings 105 and 106. Assuming this migration and
17+
# the new conflict types are in place before meeting 111, these
18+
# are the meetings for which the UI disagreed with the constraint
19+
# type actually created.
20+
affected_meetings = ['106', '107', '108', '109', '110']
21+
return constraint_qs.filter(meeting__number__in=affected_meetings)
22+
23+
24+
def forward(apps, schema_editor):
25+
Constraint = apps.get_model('meeting', 'Constraint')
26+
affected_constraints = affected(Constraint.objects.all())
27+
for old, new in replacement_slugs:
28+
affected_constraints.filter(name_id=old).update(name_id=new)
29+
30+
31+
def reverse(apps, schema_editor):
32+
Constraint = apps.get_model('meeting', 'Constraint')
33+
affected_constraints = affected(Constraint.objects.all())
34+
for old, new in replacement_slugs:
35+
affected_constraints.filter(name_id=new).update(name_id=old)
36+
37+
38+
class Migration(migrations.Migration):
39+
40+
dependencies = [
41+
('meeting', '0040_auto_20210130_1027'),
42+
('name', '0026_add_conflict_constraintnames'),
43+
]
44+
45+
operations = [
46+
migrations.RunPython(forward, reverse),
47+
]

ietf/meeting/models.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,26 @@ def get_number(self):
207207
else:
208208
return None
209209

210+
@property
211+
def session_constraintnames(self):
212+
"""Gets a list of the constraint names that should be used for this meeting
213+
214+
Anticipated that this will soon become a many-to-many relationship with ConstraintName
215+
(see issue #2770). Making this a @property allows use of the .all(), .filter(), etc,
216+
so that other code should not need changes when this is replaced.
217+
"""
218+
try:
219+
mtg_num = int(self.number)
220+
except ValueError:
221+
mtg_num = None # should not come up, but this method should not fail
222+
if mtg_num is None or mtg_num >= 106:
223+
# These meetings used the old 'conflic?' constraint types labeled as though
224+
# they were the new types.
225+
slugs = ('chair_conflict', 'tech_overlap', 'key_participant')
226+
else:
227+
slugs = ('conflict', 'conflic2', 'conflic3')
228+
return ConstraintName.objects.filter(slug__in=slugs)
229+
210230
def json_url(self):
211231
return "/meeting/%s/json" % (self.number, )
212232

ietf/name/fixtures/names.json

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5999,6 +5999,7 @@
59995999
"fields": {
60006000
"desc": "",
60016001
"editor_label": "<i class=\"fa fa-user-o\"></i>{count}",
6002+
"is_group_conflict": false,
60026003
"name": "Person must be present",
60036004
"order": 4,
60046005
"penalty": 10000,
@@ -6007,10 +6008,24 @@
60076008
"model": "name.constraintname",
60086009
"pk": "bethere"
60096010
},
6011+
{
6012+
"fields": {
6013+
"desc": "Indicates other WGs the chairs also lead or will be active participants in",
6014+
"editor_label": "<i class=\"fa fa-gavel\"></i>",
6015+
"is_group_conflict": true,
6016+
"name": "Chair conflict",
6017+
"order": 8,
6018+
"penalty": 100000,
6019+
"used": true
6020+
},
6021+
"model": "name.constraintname",
6022+
"pk": "chair_conflict"
6023+
},
60106024
{
60116025
"fields": {
60126026
"desc": "",
60136027
"editor_label": "<span class=\"encircled\">2</span>",
6028+
"is_group_conflict": true,
60146029
"name": "Conflicts with (secondary)",
60156030
"order": 2,
60166031
"penalty": 10000,
@@ -6023,6 +6038,7 @@
60236038
"fields": {
60246039
"desc": "",
60256040
"editor_label": "<span class=\"encircled\">3</span>",
6041+
"is_group_conflict": true,
60266042
"name": "Conflicts with (tertiary)",
60276043
"order": 3,
60286044
"penalty": 100000,
@@ -6035,6 +6051,7 @@
60356051
"fields": {
60366052
"desc": "",
60376053
"editor_label": "<span class=\"encircled\">1</span>",
6054+
"is_group_conflict": true,
60386055
"name": "Conflicts with",
60396056
"order": 1,
60406057
"penalty": 100000,
@@ -6043,10 +6060,37 @@
60436060
"model": "name.constraintname",
60446061
"pk": "conflict"
60456062
},
6063+
{
6064+
"fields": {
6065+
"desc": "Indicates WGs with which key participants (presenter, secretary, etc.) may overlap",
6066+
"editor_label": "<i class=\"fa fa-key\"></i>",
6067+
"is_group_conflict": true,
6068+
"name": "Key participant conflict",
6069+
"order": 10,
6070+
"penalty": 100000,
6071+
"used": true
6072+
},
6073+
"model": "name.constraintname",
6074+
"pk": "key_participant"
6075+
},
6076+
{
6077+
"fields": {
6078+
"desc": "Indicates WGs with a related technology or a closely related charter",
6079+
"editor_label": "<i class=\"fa fa-rocket\"></i>",
6080+
"is_group_conflict": true,
6081+
"name": "Technology overlap",
6082+
"order": 9,
6083+
"penalty": 10000,
6084+
"used": true
6085+
},
6086+
"model": "name.constraintname",
6087+
"pk": "tech_overlap"
6088+
},
60466089
{
60476090
"fields": {
60486091
"desc": "",
60496092
"editor_label": "&Delta;",
6093+
"is_group_conflict": false,
60506094
"name": "Preference for time between sessions",
60516095
"order": 6,
60526096
"penalty": 1000,
@@ -6059,6 +6103,7 @@
60596103
"fields": {
60606104
"desc": "",
60616105
"editor_label": "<i class=\"fa fa-calendar-o\"></i>",
6106+
"is_group_conflict": false,
60626107
"name": "Can't meet within timerange",
60636108
"order": 5,
60646109
"penalty": 1000000,
@@ -6071,6 +6116,7 @@
60716116
"fields": {
60726117
"desc": "",
60736118
"editor_label": "<i class=\"fa fa-step-forward\"></i>",
6119+
"is_group_conflict": false,
60746120
"name": "Request for adjacent scheduling with another WG",
60756121
"order": 7,
60766122
"penalty": 1000,
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 2.2.20 on 2021-05-19 09:45
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
dependencies = [
8+
('name', '0023_change_stream_descriptions'),
9+
]
10+
11+
operations = [
12+
migrations.AddField(
13+
model_name='constraintname',
14+
name='is_group_conflict',
15+
field=models.BooleanField(default=False,
16+
help_text='Does this constraint capture a conflict between groups?'),
17+
),
18+
]
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Generated by Django 2.2.20 on 2021-05-19 09:55
2+
3+
from django.db import migrations
4+
5+
6+
def forward(apps, schema_editor):
7+
"""Set is_group_conflict for ConstraintNames that need it to be True"""
8+
ConstraintName = apps.get_model('name', 'ConstraintName')
9+
ConstraintName.objects.filter(
10+
slug__in=['conflict', 'conflic2', 'conflic3']
11+
).update(is_group_conflict=True)
12+
13+
14+
def reverse(apps, schema_editor):
15+
pass # nothing to be done
16+
17+
18+
class Migration(migrations.Migration):
19+
dependencies = [
20+
('name', '0024_constraintname_is_group_conflict'),
21+
]
22+
23+
operations = [
24+
migrations.RunPython(forward, reverse),
25+
]
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Generated by Django 2.2.20 on 2021-05-05 10:05
2+
3+
from collections import namedtuple
4+
from django.db import migrations
5+
from django.db.models import Max
6+
7+
8+
# Simple type for representing constraint name data that will be
9+
# modified.
10+
ConstraintInfo = namedtuple(
11+
'ConstraintInfo',
12+
['replaces', 'slug', 'name', 'desc', 'editor_label'],
13+
)
14+
15+
constraint_names_to_add = [
16+
ConstraintInfo(
17+
replaces='conflict',
18+
slug='chair_conflict',
19+
name='Chair conflict',
20+
desc='Indicates other WGs the chairs also lead or will be active participants in',
21+
editor_label='<i class="fa fa-gavel"></i>',
22+
),
23+
ConstraintInfo(
24+
replaces='conflic2',
25+
slug='tech_overlap',
26+
name='Technology overlap',
27+
desc='Indicates WGs with a related technology or a closely related charter',
28+
editor_label='<i class="fa fa-rocket"></i>',
29+
),
30+
ConstraintInfo(
31+
replaces='conflic3',
32+
slug='key_participant',
33+
name='Key participant conflict',
34+
desc='Indicates WGs with which key participants (presenter, secretary, etc.) may overlap',
35+
editor_label='<i class="fa fa-key"></i>',
36+
)
37+
]
38+
39+
40+
def forward(apps, schema_editor):
41+
ConstraintName = apps.get_model('name', 'ConstraintName')
42+
max_order = ConstraintName.objects.all().aggregate(Max('order'))['order__max']
43+
44+
for index, new_constraint in enumerate(constraint_names_to_add):
45+
# hack_constraint is the constraint type relabeled by the hack fix in #2754
46+
hack_constraint = ConstraintName.objects.get(slug=new_constraint.replaces)
47+
ConstraintName.objects.create(
48+
slug=new_constraint.slug,
49+
name=new_constraint.name,
50+
desc=new_constraint.desc,
51+
used=hack_constraint.used,
52+
order=max_order + index + 1,
53+
penalty=hack_constraint.penalty,
54+
editor_label=new_constraint.editor_label,
55+
is_group_conflict=True,
56+
)
57+
58+
59+
def reverse(apps, schema_editor):
60+
ConstraintName = apps.get_model('name', 'ConstraintName')
61+
for new_constraint in constraint_names_to_add:
62+
ConstraintName.objects.filter(slug=new_constraint.slug).delete()
63+
64+
class Migration(migrations.Migration):
65+
dependencies = [
66+
('name', '0025_set_constraintname_is_group_conflict'),
67+
# Reversing this migration requires that the 'day' field be removed from
68+
# the Constraint model, so we indirectly depend on the migration that
69+
# removed it.
70+
('meeting', '0027_add_constraint_options_and_joint_groups'),
71+
]
72+
73+
operations = [
74+
migrations.RunPython(forward, reverse),
75+
]

ietf/name/models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ class ConstraintName(NameModel):
7171
"""conflict, conflic2, conflic3, bethere, timerange, time_relation, wg_adjacent"""
7272
penalty = models.IntegerField(default=0, help_text="The penalty for violating this kind of constraint; for instance 10 (small penalty) or 10000 (large penalty)")
7373
editor_label = models.CharField(max_length=64, blank=True, help_text="Very short label for producing warnings inline in the sessions in the schedule editor.")
74+
is_group_conflict = models.BooleanField(default=False, help_text="Does this constraint capture a conflict between groups?")
7475
class TimerangeName(NameModel):
7576
"""(monday|tuesday|wednesday|thursday|friday)-(morning|afternoon-early|afternoon-late)"""
7677
class LiaisonStatementPurposeName(NameModel):

0 commit comments

Comments
 (0)