Skip to content

Commit 8b72e3c

Browse files
committed
Merged in [19228] from rjsparks@nostrum.com:
Add BOF Requests to the datatracker. - Legacy-Id: 19237 Note: SVN reference [19228] has been migrated to Git commit 8471710
2 parents cd0c1f3 + 8471710 commit 8b72e3c

50 files changed

Lines changed: 1829 additions & 59 deletions

Some content is hidden

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

ietf/doc/admin.py

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

44

@@ -11,7 +11,8 @@
1111
StateDocEvent, ConsensusDocEvent, BallotType, BallotDocEvent, WriteupDocEvent, LastCallDocEvent,
1212
TelechatDocEvent, BallotPositionDocEvent, ReviewRequestDocEvent, InitialReviewDocEvent,
1313
AddedMessageEvent, SubmissionDocEvent, DeletedEvent, EditedAuthorsDocEvent, DocumentURL,
14-
ReviewAssignmentDocEvent, IanaExpertDocEvent, IRSGBallotDocEvent, DocExtResource, DocumentActionHolder )
14+
ReviewAssignmentDocEvent, IanaExpertDocEvent, IRSGBallotDocEvent, DocExtResource, DocumentActionHolder,
15+
BofreqEditorDocEvent, BofreqResponsibleDocEvent )
1516

1617
from ietf.utils.validators import validate_external_resource_value
1718

@@ -192,6 +193,14 @@ class BallotPositionDocEventAdmin(DocEventAdmin):
192193
class IRSGBallotDocEventAdmin(DocEventAdmin):
193194
raw_id_fields = ["doc", "by"]
194195
admin.site.register(IRSGBallotDocEvent, IRSGBallotDocEventAdmin)
196+
197+
class BofreqEditorDocEventAdmin(DocEventAdmin):
198+
raw_id_fields = ["doc", "by", "editors" ]
199+
admin.site.register(BofreqEditorDocEvent, BofreqEditorDocEventAdmin)
200+
201+
class BofreqResponsibleDocEventAdmin(DocEventAdmin):
202+
raw_id_fields = ["doc", "by", "responsible" ]
203+
admin.site.register(BofreqResponsibleDocEvent, BofreqResponsibleDocEventAdmin)
195204

196205
class DocumentUrlAdmin(admin.ModelAdmin):
197206
list_display = ['id', 'doc', 'tag', 'url', 'desc', ]

ietf/doc/factories.py

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,12 @@
1313

1414
from ietf.doc.models import ( Document, DocEvent, NewRevisionDocEvent, DocAlias, State, DocumentAuthor,
1515
StateDocEvent, BallotPositionDocEvent, BallotDocEvent, BallotType, IRSGBallotDocEvent, TelechatDocEvent,
16-
DocumentActionHolder)
16+
DocumentActionHolder, BofreqEditorDocEvent, BofreqResponsibleDocEvent )
1717
from ietf.group.models import Group
18+
from ietf.person.factories import PersonFactory
19+
from ietf.group.factories import RoleFactory
20+
from ietf.utils.text import xslugify
21+
1822

1923
def draft_name_generator(type_id,group,n):
2024
return '%s-%s-%s-%s%d'%(
@@ -379,3 +383,59 @@ class Meta:
379383

380384
class WgDocumentAuthorFactory(DocumentAuthorFactory):
381385
document = factory.SubFactory(WgDraftFactory)
386+
387+
class BofreqEditorDocEventFactory(DocEventFactory):
388+
class Meta:
389+
model = BofreqEditorDocEvent
390+
391+
type = "changed_editors"
392+
doc = factory.SubFactory('ietf.doc.factories.BofreqFactory')
393+
394+
395+
@factory.post_generation
396+
def editors(obj, create, extracted, **kwargs):
397+
if not create:
398+
return
399+
if extracted:
400+
obj.editors.set(extracted)
401+
else:
402+
obj.editors.set(PersonFactory.create_batch(3))
403+
obj.desc = f'Changed editors to {", ".join(obj.editors.values_list("name",flat=True)) or "(None)"}'
404+
405+
class BofreqResponsibleDocEventFactory(DocEventFactory):
406+
class Meta:
407+
model = BofreqResponsibleDocEvent
408+
409+
type = "changed_responsible"
410+
doc = factory.SubFactory('ietf.doc.factories.BofreqFactory')
411+
412+
413+
@factory.post_generation
414+
def responsible(obj, create, extracted, **kwargs):
415+
if not create:
416+
return
417+
if extracted:
418+
obj.responsible.set(extracted)
419+
else:
420+
ad = RoleFactory(group__type_id='area',name_id='ad').person
421+
obj.responsible.set([ad])
422+
obj.desc = f'Changed responsible leadership to {", ".join(obj.responsible.values_list("name",flat=True)) or "(None)"}'
423+
424+
class BofreqFactory(BaseDocumentFactory):
425+
type_id = 'bofreq'
426+
title = factory.Faker('sentence')
427+
name = factory.LazyAttribute(lambda o: 'bofreq-%s'%(xslugify(o.title)))
428+
429+
bofreqeditordocevent = factory.RelatedFactory('ietf.doc.factories.BofreqEditorDocEventFactory','doc')
430+
bofreqresponsibledocevent = factory.RelatedFactory('ietf.doc.factories.BofreqResponsibleDocEventFactory','doc')
431+
432+
@factory.post_generation
433+
def states(obj, create, extracted, **kwargs):
434+
if not create:
435+
return
436+
if extracted:
437+
for (state_type_id,state_slug) in extracted:
438+
obj.set_state(State.objects.get(type_id=state_type_id,slug=state_slug))
439+
else:
440+
obj.set_state(State.objects.get(type_id='bofreq',slug='proposed'))
441+

ietf/doc/mails.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from ietf.ipr.utils import iprs_from_docs, related_docs
2121
from ietf.doc.models import WriteupDocEvent, LastCallDocEvent, DocAlias, ConsensusDocEvent
2222
from ietf.doc.utils import needed_ballot_positions
23+
from ietf.doc.utils_bofreq import bofreq_editors, bofreq_responsible
2324
from ietf.group.models import Role
2425
from ietf.doc.models import Document
2526
from ietf.mailtrigger.utils import gather_address_lists
@@ -689,3 +690,43 @@ def send_external_resource_change_request(request, doc, submitter_info, requeste
689690
doc_url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url(),
690691
),
691692
cc=list(cc),)
693+
694+
def email_bofreq_title_changed(request, bofreq):
695+
addrs = gather_address_lists('bofreq_title_changed', doc=bofreq)
696+
697+
send_mail(request, addrs.to, settings.DEFAULT_FROM_EMAIL,
698+
f'BOF Request title changed : {bofreq.name}',
699+
'doc/mail/bofreq_title_changed.txt',
700+
dict(bofreq=bofreq, request=request),
701+
cc=addrs.cc)
702+
703+
def plain_names(persons):
704+
return [p.plain_name() for p in persons]
705+
706+
def email_bofreq_editors_changed(request, bofreq, previous_editors):
707+
editors = bofreq_editors(bofreq)
708+
addrs = gather_address_lists('bofreq_editors_changed', doc=bofreq, previous_editors=previous_editors)
709+
710+
send_mail(request, addrs.to, settings.DEFAULT_FROM_EMAIL,
711+
f'BOF Request editors changed : {bofreq.name}',
712+
'doc/mail/bofreq_editors_changed.txt',
713+
dict(bofreq=bofreq, request=request, editors=plain_names(editors), previous_editors=plain_names(previous_editors)),
714+
cc=addrs.cc)
715+
716+
def email_bofreq_responsible_changed(request, bofreq, previous_responsible):
717+
responsible = bofreq_responsible(bofreq)
718+
addrs = gather_address_lists('bofreq_responsible_changed', doc=bofreq, previous_responsible=previous_responsible)
719+
720+
send_mail(request, addrs.to, settings.DEFAULT_FROM_EMAIL,
721+
f'BOF Request responsible leadership changed : {bofreq.name}',
722+
'doc/mail/bofreq_responsible_changed.txt',
723+
dict(bofreq=bofreq, request=request, responsible=plain_names(responsible), previous_responsible=plain_names(previous_responsible)),
724+
cc=addrs.cc)
725+
726+
def email_bofreq_new_revision(request, bofreq):
727+
addrs = gather_address_lists('bofreq_new_revision', doc=bofreq)
728+
send_mail(request, addrs.to, settings.DEFAULT_FROM_EMAIL,
729+
f'New BOF request revision uploaded: {bofreq.name}-{bofreq.rev}',
730+
'doc/mail/bofreq_new_revision.txt',
731+
dict(bofreq=bofreq, request=request ),
732+
cc=addrs.cc)
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Copyright The IETF Trust 2021 All Rights Reserved
2+
3+
# Generated by Django 2.2.23 on 2021-05-21 13:29
4+
5+
from django.db import migrations
6+
7+
def forward(apps, schema_editor):
8+
StateType = apps.get_model('doc', 'StateType')
9+
State = apps.get_model('doc', 'State')
10+
11+
StateType.objects.create(slug='bofreq', label='BOF Request State')
12+
proposed = State.objects.create(type_id='bofreq', slug='proposed', name='Proposed', used=True, desc='The BOF request is proposed', order=0)
13+
approved = State.objects.create(type_id='bofreq', slug='approved', name='Approved', used=True, desc='The BOF request is approved', order=1)
14+
declined = State.objects.create(type_id='bofreq', slug='declined', name='Declined', used=True, desc='The BOF request is declined', order=2)
15+
replaced = State.objects.create(type_id='bofreq', slug='replaced', name='Replaced', used=True, desc='The BOF request is proposed', order=3)
16+
abandoned = State.objects.create(type_id='bofreq', slug='abandoned', name='Abandoned', used=True, desc='The BOF request is abandoned', order=4)
17+
18+
proposed.next_states.set([approved,declined,replaced,abandoned])
19+
20+
def reverse(apps, schema_editor):
21+
StateType = apps.get_model('doc', 'StateType')
22+
State = apps.get_model('doc', 'State')
23+
State.objects.filter(type_id='bofreq').delete()
24+
StateType.objects.filter(slug='bofreq').delete()
25+
26+
27+
class Migration(migrations.Migration):
28+
29+
dependencies = [
30+
('doc', '0041_add_documentactionholder'),
31+
('name', '0027_add_bofrequest'),
32+
]
33+
34+
operations = [
35+
migrations.RunPython(forward, reverse)
36+
]
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Generated by Django 2.2.24 on 2021-07-06 13:34
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+
('person', '0019_auto_20210604_1443'),
11+
('doc', '0042_bofreq_states'),
12+
]
13+
14+
operations = [
15+
migrations.AlterField(
16+
model_name='docevent',
17+
name='type',
18+
field=models.CharField(choices=[('new_revision', 'Added new revision'), ('new_submission', 'Uploaded new revision'), ('changed_document', 'Changed document metadata'), ('added_comment', 'Added comment'), ('added_message', 'Added message'), ('edited_authors', 'Edited the documents author list'), ('deleted', 'Deleted document'), ('changed_state', 'Changed state'), ('changed_stream', 'Changed document stream'), ('expired_document', 'Expired document'), ('extended_expiry', 'Extended expiry of document'), ('requested_resurrect', 'Requested resurrect'), ('completed_resurrect', 'Completed resurrect'), ('changed_consensus', 'Changed consensus'), ('published_rfc', 'Published RFC'), ('added_suggested_replaces', 'Added suggested replacement relationships'), ('reviewed_suggested_replaces', 'Reviewed suggested replacement relationships'), ('changed_action_holders', 'Changed action holders for document'), ('changed_group', 'Changed group'), ('changed_protocol_writeup', 'Changed protocol writeup'), ('changed_charter_milestone', 'Changed charter milestone'), ('initial_review', 'Set initial review time'), ('changed_review_announcement', 'Changed WG Review text'), ('changed_action_announcement', 'Changed WG Action text'), ('started_iesg_process', 'Started IESG process on document'), ('created_ballot', 'Created ballot'), ('closed_ballot', 'Closed ballot'), ('sent_ballot_announcement', 'Sent ballot announcement'), ('changed_ballot_position', 'Changed ballot position'), ('changed_ballot_approval_text', 'Changed ballot approval text'), ('changed_ballot_writeup_text', 'Changed ballot writeup text'), ('changed_rfc_editor_note_text', 'Changed RFC Editor Note text'), ('changed_last_call_text', 'Changed last call text'), ('requested_last_call', 'Requested last call'), ('sent_last_call', 'Sent last call'), ('scheduled_for_telechat', 'Scheduled for telechat'), ('iesg_approved', 'IESG approved document (no problem)'), ('iesg_disapproved', 'IESG disapproved document (do not publish)'), ('approved_in_minute', 'Approved in minute'), ('iana_review', 'IANA review comment'), ('rfc_in_iana_registry', 'RFC is in IANA registry'), ('rfc_editor_received_announcement', 'Announcement was received by RFC Editor'), ('requested_publication', 'Publication at RFC Editor requested'), ('sync_from_rfc_editor', 'Received updated information from RFC Editor'), ('requested_review', 'Requested review'), ('assigned_review_request', 'Assigned review request'), ('closed_review_request', 'Closed review request'), ('closed_review_assignment', 'Closed review assignment'), ('downref_approved', 'Downref approved'), ('posted_related_ipr', 'Posted related IPR'), ('removed_related_ipr', 'Removed related IPR'), ('changed_editors', 'Changed BOF Request editors')], max_length=50),
19+
),
20+
migrations.CreateModel(
21+
name='BofreqResponsibleDocEvent',
22+
fields=[
23+
('docevent_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='doc.DocEvent')),
24+
('responsible', models.ManyToManyField(blank=True, to='person.Person')),
25+
],
26+
bases=('doc.docevent',),
27+
),
28+
migrations.CreateModel(
29+
name='BofreqEditorDocEvent',
30+
fields=[
31+
('docevent_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='doc.DocEvent')),
32+
('editors', models.ManyToManyField(blank=True, to='person.Person')),
33+
],
34+
bases=('doc.docevent',),
35+
),
36+
]

ietf/doc/models.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,8 @@ def get_file_path(self):
142142
self._cached_file_path = settings.CONFLICT_REVIEW_PATH
143143
elif self.type_id == "statchg":
144144
self._cached_file_path = settings.STATUS_CHANGE_PATH
145+
elif self.type_id == "bofreq":
146+
self._cached_file_path = settings.BOFREQ_PATH
145147
else:
146148
self._cached_file_path = settings.DOCUMENT_PATH_PATTERN.format(doc=self)
147149
return self._cached_file_path
@@ -163,6 +165,8 @@ def get_base_name(self):
163165
elif self.type_id == 'review':
164166
# TODO: This will be wrong if a review is updated on the same day it was created (or updated more than once on the same day)
165167
self._cached_base_name = "%s.txt" % self.name
168+
elif self.type_id == 'bofreq':
169+
self._cached_base_name = "%s-%s.md" % (self.name, self.rev)
166170
else:
167171
if self.rev:
168172
self._cached_base_name = "%s-%s.txt" % (self.canonical_name(), self.rev)
@@ -1145,6 +1149,9 @@ class DocReminder(models.Model):
11451149
# IPR events
11461150
("posted_related_ipr", "Posted related IPR"),
11471151
("removed_related_ipr", "Removed related IPR"),
1152+
1153+
# Bofreq Editor events
1154+
("changed_editors", "Changed BOF Request editors")
11481155
]
11491156

11501157
class DocEvent(models.Model):
@@ -1340,3 +1347,11 @@ class EditedAuthorsDocEvent(DocEvent):
13401347
Example 'basis' values might be from ['manually adjusted','recomputed by parsing document', etc.]
13411348
"""
13421349
basis = models.CharField(help_text="What is the source or reasoning for the changes to the author list",max_length=255)
1350+
1351+
class BofreqEditorDocEvent(DocEvent):
1352+
""" Capture the proponents of a BOF Request."""
1353+
editors = models.ManyToManyField('person.Person', blank=True)
1354+
1355+
class BofreqResponsibleDocEvent(DocEvent):
1356+
""" Capture the responsible leadership (IAB and IESG members) for a BOF Request """
1357+
responsible = models.ManyToManyField('person.Person', blank=True)

ietf/doc/resources.py

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
InitialReviewDocEvent, DocHistoryAuthor, BallotDocEvent, RelatedDocument,
1818
RelatedDocHistory, BallotPositionDocEvent, AddedMessageEvent, SubmissionDocEvent,
1919
ReviewRequestDocEvent, ReviewAssignmentDocEvent, EditedAuthorsDocEvent, DocumentURL,
20-
IanaExpertDocEvent, IRSGBallotDocEvent, DocExtResource, DocumentActionHolder )
20+
IanaExpertDocEvent, IRSGBallotDocEvent, DocExtResource, DocumentActionHolder,
21+
BofreqEditorDocEvent,BofreqResponsibleDocEvent)
2122

2223
from ietf.name.resources import BallotPositionNameResource, DocTypeNameResource
2324
class BallotTypeResource(ModelResource):
@@ -806,3 +807,55 @@ class Meta:
806807
"person": ALL_WITH_RELATIONS,
807808
}
808809
api.doc.register(DocumentActionHolderResource())
810+
811+
812+
from ietf.person.resources import PersonResource
813+
class BofreqEditorDocEventResource(ModelResource):
814+
by = ToOneField(PersonResource, 'by')
815+
doc = ToOneField(DocumentResource, 'doc')
816+
docevent_ptr = ToOneField(DocEventResource, 'docevent_ptr')
817+
editors = ToManyField(PersonResource, 'editors', null=True)
818+
class Meta:
819+
queryset = BofreqEditorDocEvent.objects.all()
820+
serializer = api.Serializer()
821+
cache = SimpleCache()
822+
#resource_name = 'bofreqeditordocevent'
823+
ordering = ['docevent_ptr', ]
824+
filtering = {
825+
"id": ALL,
826+
"time": ALL,
827+
"type": ALL,
828+
"rev": ALL,
829+
"desc": ALL,
830+
"by": ALL_WITH_RELATIONS,
831+
"doc": ALL_WITH_RELATIONS,
832+
"docevent_ptr": ALL_WITH_RELATIONS,
833+
"editors": ALL_WITH_RELATIONS,
834+
}
835+
api.doc.register(BofreqEditorDocEventResource())
836+
837+
838+
from ietf.person.resources import PersonResource
839+
class BofreqResponsibleDocEventResource(ModelResource):
840+
by = ToOneField(PersonResource, 'by')
841+
doc = ToOneField(DocumentResource, 'doc')
842+
docevent_ptr = ToOneField(DocEventResource, 'docevent_ptr')
843+
responsible = ToManyField(PersonResource, 'responsible', null=True)
844+
class Meta:
845+
queryset = BofreqResponsibleDocEvent.objects.all()
846+
serializer = api.Serializer()
847+
cache = SimpleCache()
848+
#resource_name = 'bofreqresponsibledocevent'
849+
ordering = ['docevent_ptr', ]
850+
filtering = {
851+
"id": ALL,
852+
"time": ALL,
853+
"type": ALL,
854+
"rev": ALL,
855+
"desc": ALL,
856+
"by": ALL_WITH_RELATIONS,
857+
"doc": ALL_WITH_RELATIONS,
858+
"docevent_ptr": ALL_WITH_RELATIONS,
859+
"responsible": ALL_WITH_RELATIONS,
860+
}
861+
api.doc.register(BofreqResponsibleDocEventResource())

0 commit comments

Comments
 (0)