Skip to content

Commit bcd37ed

Browse files
Merged from session_purpose_dev
Create dev branch for session purpose work (from revision [19414]) Snapshot of dev work to add session purpose annotation (from revision [19415]) Allow non-WG-like groups to request additional sessions/durations and bypass approval (from revision [19424]) Add 'closed' session purpose, assign purposes for nomcom groups, and update schedule editor to enforce timeslot type and allow blurring sessions by purpose (from revision [19427]) Add management command to set up timeslots/sessions for testing/demoing 'purpose' field (from revision [19430]) Update session purposes and group type -> purpose map to match notes page, change 'session' purpose to 'regular' (from revision [19433]) Redirect edit_schedule urls to edit_meeting_schedule view (from revision [19434]) Allow hiding/blurring sessions and timeslots based on TimeSlotType in the schedule editor (from revision [19438]) Disable session purpose/timeslot type hiding on schedule editor when only 0 or 1 options (from revision [19439]) Improvements to the timeslot and schedule editors (move new toggles to modals, handle overflowing session names, fix timeslot editor scrolling, add buttons to quickly create single timeslot, accept trailing slash on edit URL) (from revision [19449]) Update purpose/types after discussions, add on_agenda Session field, prevent session requests for groups with no allowed purpose, handle addition fields in session request, fix editing session requests, add session edit form/access from schedule editor, eliminate TimeSlotTypeName "private" field, add server-side timeslot type filtering to schedule editor (from revision [19549]) Eliminate the officehours timeslot type, update/renumber migrations, mark offagenda/reserved TimeSlotTypeNames as not used, add a 'none' SessionPurposeName and disallow null, update agenda filter keywords/filter helpers, fix broken tests and general debugging (from revision [19550]) Tear out old meeting schedule editor and related code (from revision [19551]) Fix merge errors in preceding commits (from revision [19556]) - Legacy-Id: 19570 Note: SVN reference [19415] has been migrated to Git commit 1054f90 Note: SVN reference [19424] has been migrated to Git commit 5318081 Note: SVN reference [19427] has been migrated to Git commit 173e438 Note: SVN reference [19430] has been migrated to Git commit 7a2530a Note: SVN reference [19433] has been migrated to Git commit 3be50d6 Note: SVN reference [19434] has been migrated to Git commit 3e3d681 Note: SVN reference [19438] has been migrated to Git commit b6ac3d4 Note: SVN reference [19439] has been migrated to Git commit 446ac7a Note: SVN reference [19449] has been migrated to Git commit 5cbe402 Note: SVN reference [19549] has been migrated to Git commit 3dfce7b Note: SVN reference [19550] has been migrated to Git commit 7b35c09 Note: SVN reference [19551] has been migrated to Git commit d7f2034 Note: SVN reference [19556] has been migrated to Git commit 2b1864f
2 parents 4c1cfea + 2b1864f commit bcd37ed

File tree

94 files changed

+5784
-7135
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

94 files changed

+5784
-7135
lines changed

ietf/doc/templatetags/ietf_filters.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,93 @@ def action_holder_badge(action_holder):
631631
else:
632632
return '' # no alert needed
633633

634+
@register.filter
635+
def is_regular_agenda_item(assignment):
636+
"""Is this agenda item a regular session item?
637+
638+
A regular item appears as a sub-entry in a timeslot within the agenda
639+
640+
>>> from collections import namedtuple # use to build mock objects
641+
>>> mock_timeslot = namedtuple('t2', ['slug'])
642+
>>> mock_assignment = namedtuple('t1', ['slot_type']) # slot_type must be a callable
643+
>>> factory = lambda t: mock_assignment(slot_type=lambda: mock_timeslot(slug=t))
644+
>>> is_regular_agenda_item(factory('regular'))
645+
True
646+
647+
>>> any(is_regular_agenda_item(factory(t)) for t in ['plenary', 'break', 'reg', 'other', 'officehours'])
648+
False
649+
"""
650+
return assignment.slot_type().slug == 'regular'
651+
652+
@register.filter
653+
def is_plenary_agenda_item(assignment):
654+
"""Is this agenda item a regular session item?
655+
656+
A regular item appears as a sub-entry in a timeslot within the agenda
657+
658+
>>> from collections import namedtuple # use to build mock objects
659+
>>> mock_timeslot = namedtuple('t2', ['slug'])
660+
>>> mock_assignment = namedtuple('t1', ['slot_type']) # slot_type must be a callable
661+
>>> factory = lambda t: mock_assignment(slot_type=lambda: mock_timeslot(slug=t))
662+
>>> is_plenary_agenda_item(factory('plenary'))
663+
True
664+
665+
>>> any(is_plenary_agenda_item(factory(t)) for t in ['regular', 'break', 'reg', 'other', 'officehours'])
666+
False
667+
"""
668+
return assignment.slot_type().slug == 'plenary'
669+
670+
@register.filter
671+
def is_special_agenda_item(assignment):
672+
"""Is this agenda item a special item?
673+
674+
Special items appear as top-level agenda entries with their own timeslot information.
675+
676+
>>> from collections import namedtuple # use to build mock objects
677+
>>> mock_timeslot = namedtuple('t2', ['slug'])
678+
>>> mock_assignment = namedtuple('t1', ['slot_type']) # slot_type must be a callable
679+
>>> factory = lambda t: mock_assignment(slot_type=lambda: mock_timeslot(slug=t))
680+
>>> all(is_special_agenda_item(factory(t)) for t in ['break', 'reg', 'other', 'officehours'])
681+
True
682+
683+
>>> any(is_special_agenda_item(factory(t)) for t in ['regular', 'plenary'])
684+
False
685+
"""
686+
return assignment.slot_type().slug in [
687+
'break',
688+
'reg',
689+
'other',
690+
'officehours',
691+
]
692+
693+
@register.filter
694+
def should_show_agenda_session_buttons(assignment):
695+
"""Should this agenda item show the session buttons (jabber link, etc)?
696+
697+
In IETF-111 and earlier, office hours sessions were designated by a name ending
698+
with ' office hours' and belonged to the IESG or some other group. This led to
699+
incorrect session buttons being displayed. Suppress session buttons for
700+
when name ends with 'office hours' in the pre-111 meetings.
701+
>>> from collections import namedtuple # use to build mock objects
702+
>>> mock_meeting = namedtuple('t3', ['number'])
703+
>>> mock_session = namedtuple('t2', ['name'])
704+
>>> mock_assignment = namedtuple('t1', ['meeting', 'session']) # meeting must be a callable
705+
>>> factory = lambda num, name: mock_assignment(session=mock_session(name), meeting=lambda: mock_meeting(num))
706+
>>> test_cases = [('105', 'acme office hours'), ('111', 'acme office hours')]
707+
>>> any(should_show_agenda_session_buttons(factory(*tc)) for tc in test_cases)
708+
False
709+
>>> test_cases = [('interim-2020-acme-112', 'acme'), ('112', 'acme'), ('150', 'acme'), ('105', 'acme'),]
710+
>>> test_cases.extend([('111', 'acme'), ('interim-2020-acme-112', 'acme office hours')])
711+
>>> test_cases.extend([('112', 'acme office hours'), ('150', 'acme office hours')])
712+
>>> all(should_show_agenda_session_buttons(factory(*tc)) for tc in test_cases)
713+
True
714+
"""
715+
num = assignment.meeting().number
716+
if num.isdigit() and int(num) <= settings.MEETING_LEGACY_OFFICE_HOURS_END:
717+
return not assignment.session.name.lower().endswith(' office hours')
718+
else:
719+
return True
720+
634721

635722
@register.simple_tag
636723
def absurl(viewname, **kwargs):

ietf/doc/tests.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
from ietf.group.models import Group
4141
from ietf.group.factories import GroupFactory, RoleFactory
4242
from ietf.ipr.factories import HolderIprDisclosureFactory
43-
from ietf.meeting.models import Meeting, Session, SessionPresentation, SchedulingEvent
43+
from ietf.meeting.models import Meeting, SessionPresentation, SchedulingEvent
4444
from ietf.meeting.factories import ( MeetingFactory, SessionFactory, SessionPresentationFactory,
4545
ProceedingsMaterialFactory )
4646

@@ -1465,12 +1465,12 @@ def test_document_material(self):
14651465
)
14661466
doc.set_state(State.objects.get(type="slides", slug="active"))
14671467

1468-
session = Session.objects.create(
1468+
session = SessionFactory(
14691469
name = "session-72-mars-1",
14701470
meeting = Meeting.objects.get(number='72'),
14711471
group = Group.objects.get(acronym='mars'),
14721472
modified = datetime.datetime.now(),
1473-
type_id = 'regular',
1473+
add_to_schedule=False,
14741474
)
14751475
SchedulingEvent.objects.create(
14761476
session=session,

ietf/doc/tests_material.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
from ietf.doc.models import Document, State, DocAlias, NewRevisionDocEvent
1919
from ietf.group.factories import RoleFactory
2020
from ietf.group.models import Group
21-
from ietf.meeting.factories import MeetingFactory
22-
from ietf.meeting.models import Meeting, Session, SessionPresentation, SchedulingEvent
21+
from ietf.meeting.factories import MeetingFactory, SessionFactory
22+
from ietf.meeting.models import Meeting, SessionPresentation, SchedulingEvent
2323
from ietf.name.models import SessionStatusName
2424
from ietf.person.models import Person
2525
from ietf.utils.test_utils import TestCase, login_testing_unauthorized
@@ -151,12 +151,11 @@ def test_edit_title(self):
151151
def test_revise(self):
152152
doc = self.create_slides()
153153

154-
session = Session.objects.create(
154+
session = SessionFactory(
155155
name = "session-42-mars-1",
156156
meeting = Meeting.objects.get(number='42'),
157157
group = Group.objects.get(acronym='mars'),
158158
modified = datetime.datetime.now(),
159-
type_id='regular',
160159
)
161160
SchedulingEvent.objects.create(
162161
session=session,

ietf/group/admin.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ class GroupFeaturesAdmin(admin.ModelAdmin):
188188
'customize_workflow',
189189
'is_schedulable',
190190
'show_on_agenda',
191+
'agenda_filter_type',
191192
'req_subm_approval',
192193
'agenda_type',
193194
'material_types',
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Copyright The IETF Trust 2021 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+
('name', '0033_populate_agendafiltertypename'),
11+
('group', '0048_has_session_materials'),
12+
]
13+
14+
operations = [
15+
migrations.AddField(
16+
model_name='groupfeatures',
17+
name='agenda_filter_type',
18+
field=models.ForeignKey(default='none', on_delete=django.db.models.deletion.PROTECT, to='name.AgendaFilterTypeName'),
19+
),
20+
]
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Copyright The IETF Trust 2021 All Rights Reserved
2+
3+
from django.db import migrations
4+
5+
6+
def forward(apps, schema_editor):
7+
GroupFeatures = apps.get_model('group', 'GroupFeatures')
8+
9+
# map AgendaFilterTypeName slug to group types - unlisted get 'none'
10+
filter_types = dict(
11+
# list previously hard coded in agenda view, plus 'review'
12+
normal={'wg', 'ag', 'rg', 'rag', 'iab', 'program', 'review'},
13+
heading={'area', 'ietf', 'irtf'},
14+
special={'team', 'adhoc'},
15+
)
16+
17+
for ft, group_types in filter_types.items():
18+
for gf in GroupFeatures.objects.filter(type__slug__in=group_types):
19+
gf.agenda_filter_type_id = ft
20+
gf.save()
21+
22+
23+
def reverse(apps, schema_editor):
24+
pass # nothing to do, model will be deleted anyway
25+
26+
27+
class Migration(migrations.Migration):
28+
29+
dependencies = [
30+
('group', '0049_groupfeatures_agenda_filter_type'),
31+
]
32+
33+
operations = [
34+
migrations.RunPython(forward, reverse),
35+
]
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Copyright The IETF Trust 2021 All Rights Reserved
2+
3+
# Generated by Django 2.2.24 on 2021-09-26 11:29
4+
5+
from django.db import migrations
6+
import ietf.group.models
7+
import ietf.name.models
8+
import jsonfield.fields
9+
10+
11+
class Migration(migrations.Migration):
12+
13+
dependencies = [
14+
('group', '0050_populate_groupfeatures_agenda_filter_type'),
15+
('name', '0034_sessionpurposename'),
16+
]
17+
18+
operations = [
19+
migrations.AddField(
20+
model_name='groupfeatures',
21+
name='session_purposes',
22+
field=jsonfield.fields.JSONField(default=[], help_text='Allowed session purposes for this group type', max_length=256, validators=[ietf.group.models.JSONForeignKeyListValidator(ietf.name.models.SessionPurposeName)]),
23+
),
24+
]
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Copyright The IETF Trust 2021 All Rights Reserved
2+
3+
# Generated by Django 2.2.24 on 2021-09-26 11:29
4+
5+
from django.db import migrations
6+
7+
8+
default_purposes = dict(
9+
adhoc=['presentation'],
10+
adm=['closed_meeting', 'officehours'],
11+
ag=['regular'],
12+
area=['regular'],
13+
dir=['open_meeting', 'presentation', 'regular', 'social', 'tutorial'],
14+
iab=['closed_meeting', 'regular'],
15+
iabasg=['closed_meeting', 'officehours', 'open_meeting'],
16+
iana=['officehours'],
17+
iesg=['closed_meeting', 'open_meeting'],
18+
ietf=['admin', 'plenary', 'presentation', 'social'],
19+
irtf=[],
20+
ise=['officehours'],
21+
isoc=['officehours', 'open_meeting', 'presentation'],
22+
nomcom=['closed_meeting', 'officehours'],
23+
program=['regular', 'tutorial'],
24+
rag=['regular'],
25+
review=['open_meeting', 'social'],
26+
rfcedtyp=['officehours'],
27+
rg=['regular'],
28+
team=['coding', 'presentation', 'social', 'tutorial'],
29+
wg=['regular'],
30+
)
31+
32+
33+
def forward(apps, schema_editor):
34+
GroupFeatures = apps.get_model('group', 'GroupFeatures')
35+
SessionPurposeName = apps.get_model('name', 'SessionPurposeName')
36+
37+
# verify that we're not about to use an invalid purpose
38+
for purposes in default_purposes.values():
39+
for purpose in purposes:
40+
SessionPurposeName.objects.get(pk=purpose) # throws an exception unless exists
41+
42+
for type_, purposes in default_purposes.items():
43+
GroupFeatures.objects.filter(
44+
type=type_
45+
).update(
46+
session_purposes=purposes
47+
)
48+
49+
def reverse(apps, schema_editor):
50+
GroupFeatures = apps.get_model('group', 'GroupFeatures')
51+
GroupFeatures.objects.update(session_purposes=[]) # clear back out to default
52+
53+
54+
class Migration(migrations.Migration):
55+
56+
dependencies = [
57+
('group', '0051_groupfeatures_session_purposes'),
58+
('name', '0035_populate_sessionpurposename'),
59+
60+
]
61+
62+
operations = [
63+
migrations.RunPython(forward, reverse),
64+
]

ietf/group/models.py

Lines changed: 10 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,23 @@
88
import os
99
import re
1010

11-
from urllib.parse import urljoin
12-
1311
from django.conf import settings
1412
from django.core.validators import RegexValidator
1513
from django.db import models
16-
from django.db.models.deletion import CASCADE
14+
from django.db.models.deletion import CASCADE, PROTECT
1715
from django.dispatch import receiver
1816

19-
#from simple_history.models import HistoricalRecords
20-
2117
import debug # pyflakes:ignore
2218

2319
from ietf.group.colors import fg_group_colors, bg_group_colors
24-
from ietf.name.models import GroupStateName, GroupTypeName, DocTagName, GroupMilestoneStateName, RoleName, AgendaTypeName, ExtResourceName
20+
from ietf.name.models import (GroupStateName, GroupTypeName, DocTagName, GroupMilestoneStateName, RoleName,
21+
AgendaTypeName, AgendaFilterTypeName, ExtResourceName, SessionPurposeName)
2522
from ietf.person.models import Email, Person
2623
from ietf.utils.db import IETFJSONField
2724
from ietf.utils.mail import formataddr, send_mail_text
2825
from ietf.utils import log
2926
from ietf.utils.models import ForeignKey, OneToOneField
27+
from ietf.utils.validators import JSONForeignKeyListValidator
3028

3129

3230
class GroupInfo(models.Model):
@@ -167,30 +165,6 @@ def fg_color(self):
167165
def bg_color(self):
168166
return bg_group_colors[self.upcase_acronym]
169167

170-
def json_url(self):
171-
return "/group/%s.json" % (self.acronym,)
172-
173-
def json_dict(self, host_scheme):
174-
group1= dict()
175-
group1['href'] = urljoin(host_scheme, self.json_url())
176-
group1['acronym'] = self.acronym
177-
group1['name'] = self.name
178-
group1['state'] = self.state.slug
179-
group1['type'] = self.type.slug
180-
if self.parent is not None:
181-
group1['parent_href'] = urljoin(host_scheme, self.parent.json_url())
182-
# uncomment when people URL handle is created
183-
try:
184-
if self.ad_role() is not None:
185-
group1['ad_href'] = urljoin(host_scheme, self.ad_role().person.json_url())
186-
except Person.DoesNotExist:
187-
pass
188-
group1['list_email'] = self.list_email
189-
group1['list_subscribe'] = self.list_subscribe
190-
group1['list_archive'] = self.list_archive
191-
group1['comments'] = self.comments
192-
return group1
193-
194168
def liaison_approvers(self):
195169
'''Returns roles that have liaison statement approval authority for group'''
196170

@@ -248,6 +222,7 @@ def get_description(self):
248222
code='invalid',
249223
)
250224

225+
251226
class GroupFeatures(models.Model):
252227
type = OneToOneField(GroupTypeName, primary_key=True, null=False, related_name='features')
253228
#history = HistoricalRecords()
@@ -275,6 +250,7 @@ class GroupFeatures(models.Model):
275250
customize_workflow = models.BooleanField("Workflow", default=False)
276251
is_schedulable = models.BooleanField("Schedulable",default=False)
277252
show_on_agenda = models.BooleanField("On Agenda", default=False)
253+
agenda_filter_type = models.ForeignKey(AgendaFilterTypeName, default='none', on_delete=PROTECT)
278254
req_subm_approval = models.BooleanField("Subm. Approval", default=False)
279255
#
280256
agenda_type = models.ForeignKey(AgendaTypeName, null=True, default="ietf", on_delete=CASCADE)
@@ -288,7 +264,10 @@ class GroupFeatures(models.Model):
288264
groupman_authroles = IETFJSONField(max_length=128, accepted_empty_values=[[], {}], blank=False, default=["Secretariat",])
289265
matman_roles = IETFJSONField(max_length=128, accepted_empty_values=[[], {}], blank=False, default=["ad","chair","delegate","secr"])
290266
role_order = IETFJSONField(max_length=128, accepted_empty_values=[[], {}], blank=False, default=["chair","secr","member"],
291-
help_text="The order in which roles are shown, for instance on photo pages. Enter valid JSON.")
267+
help_text="The order in which roles are shown, for instance on photo pages. Enter valid JSON.")
268+
session_purposes = jsonfield.JSONField(max_length=256, blank=False, default=[],
269+
help_text="Allowed session purposes for this group type",
270+
validators=[JSONForeignKeyListValidator(SessionPurposeName)])
292271

293272

294273
class GroupHistory(GroupInfo):

ietf/group/urls.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@
5454
group_urls = [
5555
url(r'^$', views.active_groups),
5656
url(r'^groupmenu.json', views.group_menu_data, None, 'ietf.group.views.group_menu_data'),
57-
url(r'^%(acronym)s.json$' % settings.URL_REGEXPS, views.group_json),
5857
url(r'^chartering/$', views.chartering_groups),
5958
url(r'^chartering/create/(?P<group_type>(wg|rg))/$', views.edit, {'action': "charter"}),
6059
url(r'^concluded/$', views.concluded_groups),

0 commit comments

Comments
 (0)