Create DocEvent when related IprDisclosure is posted or removed. Fixes #2811. Commit ready for merge.

- Legacy-Id: 17284
This commit is contained in:
Jennifer Richards 2020-02-07 20:41:13 +00:00
parent cd77c76372
commit b02b42da81
5 changed files with 155 additions and 3 deletions

View file

@ -0,0 +1,21 @@
# Copyright The IETF Trust 2020, All Rights Reserved
# -*- coding: utf-8 -*-
# Generated by Django 1.11.27 on 2020-01-17 11:54
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('doc', '0028_irsgballotdocevent'),
]
operations = [
migrations.AlterField(
model_name='docevent',
name='type',
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_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')], max_length=50),
),
]

View file

@ -1031,6 +1031,10 @@ EVENT_TYPES = [
# downref
("downref_approved", "Downref approved"),
# IPR events
("posted_related_ipr", "Posted related IPR"),
("removed_related_ipr", "Removed related IPR"),
]
@python_2_unicode_compatible

View file

@ -0,0 +1,68 @@
# Copyright The IETF Trust 2020, All Rights Reserved
# -*- coding: utf-8 -*-
# Generated by Django 1.11.27 on 2020-01-17 12:32
from __future__ import unicode_literals
from django.db import migrations
def create_or_delete_ipr_doc_events(apps, delete=False):
"""Create or delete DocEvents for IprEvents
Mostly duplicates IprEvent.create_doc_events(). This is necessary
because model methods, including custom save() methods, are not
available to migrations.
"""
IprEvent = apps.get_model('ipr', 'IprEvent')
DocEvent = apps.get_model('doc', 'DocEvent')
# Map from self.type_id to DocEvent.EVENT_TYPES for types that
# should be logged as DocEvents
event_type_map = {
'posted': 'posted_related_ipr',
'removed': 'removed_related_ipr',
}
for ipr_event in IprEvent.objects.filter(type_id__in=event_type_map):
related_docs = set() # related docs, no duplicates
for alias in ipr_event.disclosure.docs.all():
related_docs.update(alias.docs.all())
for doc in related_docs:
kwargs = dict(
type=event_type_map[ipr_event.type_id],
time=ipr_event.time,
by=ipr_event.by,
doc=doc,
rev='',
desc='%s related IPR disclosure: <b>%s</b>' % (ipr_event.type.name,
ipr_event.disclosure.title),
)
events = DocEvent.objects.filter(**kwargs) # get existing events
if delete:
events.delete()
elif len(events) == 0:
DocEvent.objects.create(**kwargs) # create if did not exist
def forward(apps, schema_editor):
"""Create a DocEvent for each 'posted' or 'removed' IprEvent"""
create_or_delete_ipr_doc_events(apps, delete=False)
def reverse(apps, schema_editor):
"""Delete DocEvents that would be created by the forward migration
This removes data, but only data that can be regenerated by running
the forward migration.
"""
create_or_delete_ipr_doc_events(apps, delete=True)
class Migration(migrations.Migration):
dependencies = [
('ipr', '0006_document_primary_key_cleanup'),
# Ensure the DocEvent types we need exist
('doc', '0029_add_ipr_event_types'),
]
operations = [
migrations.RunPython(forward, reverse),
]

View file

@ -1,4 +1,4 @@
# Copyright The IETF Trust 2007-2019, All Rights Reserved
# Copyright The IETF Trust 2007-2020, All Rights Reserved
# -*- coding: utf-8 -*-
@ -11,7 +11,7 @@ from django.db import models
from django.urls import reverse
from django.utils.encoding import python_2_unicode_compatible
from ietf.doc.models import DocAlias
from ietf.doc.models import DocAlias, DocEvent
from ietf.name.models import DocRelationshipName,IprDisclosureStateName,IprLicenseTypeName,IprEventTypeName
from ietf.person.models import Person
from ietf.message.models import Message
@ -214,6 +214,12 @@ class IprEvent(models.Model):
def __str__(self):
return "%s %s by %s at %s" % (self.disclosure.title, self.type.name.lower(), self.by.plain_name(), self.time)
def save(self, *args, **kwargs):
created = not self.pk
super(IprEvent, self).save(*args, **kwargs)
if created:
self.create_doc_events()
def response_past_due(self):
"""Returns true if it's beyond the response_due date and no response has been
received"""
@ -223,6 +229,29 @@ class IprEvent(models.Model):
else:
return False
def create_doc_events(self):
"""Create DocEvents for documents affected by an IprEvent"""
# Map from self.type_id to DocEvent.EVENT_TYPES for types that
# should be logged as DocEvents
event_type_map = {
'posted': 'posted_related_ipr',
'removed': 'removed_related_ipr',
}
if self.type_id in event_type_map:
related_docs = set() # related docs, no duplicates
for alias in self.disclosure.docs.all():
related_docs.update(alias.docs.all())
for doc in related_docs:
DocEvent.objects.create(
type=event_type_map[self.type_id],
time=self.time,
by=self.by,
doc=doc,
rev='',
desc='%s related IPR disclosure <b>%s</b>' % (self.type.name,
self.disclosure.title),
)
class Meta:
ordering = ['-time', '-id']

View file

@ -1,4 +1,4 @@
# Copyright The IETF Trust 2009-2019, All Rights Reserved
# Copyright The IETF Trust 2009-2020, All Rights Reserved
# -*- coding: utf-8 -*-
@ -681,3 +681,33 @@ Subject: test
self.assertEqual(response.status_code,302)
disclosure = HolderIprDisclosure.objects.get(pk=disclosure.pk)
self.assertEqual(disclosure.compliant,False)
def test_docevent_creation(self):
"""Test that IprEvent creation triggers DocEvent creation"""
doc = DocumentFactory()
ipr = HolderIprDisclosureFactory(docs=[doc])
# Document starts with no ipr-related events
self.assertEqual(0, doc.docevent_set.filter(type='posted_related_ipr').count(),
'New Document already has a "posted_related_ipr" DocEvent')
self.assertEqual(0, doc.docevent_set.filter(type='removed_related_ipr').count(),
'New Document already has a "removed_related_ipr" DocEvent')
# A 'posted' IprEvent must create a corresponding DocEvent
IprEventFactory(type_id='posted', disclosure=ipr)
self.assertEqual(1, doc.docevent_set.filter(type='posted_related_ipr').count(),
'Creating "posted" IprEvent did not create a "posted_related_ipr" DocEvent')
self.assertEqual(0, doc.docevent_set.filter(type='removed_related_ipr').count(),
'Creating "posted" IprEvent created a "removed_related_ipr" DocEvent')
# A 'removed' IprEvent must create a corresponding DocEvent
IprEventFactory(type_id='removed', disclosure=ipr)
self.assertEqual(1, doc.docevent_set.filter(type='posted_related_ipr').count(),
'Creating "removed" IprEvent created a "posted_related_ipr" DocEvent')
self.assertEqual(1, doc.docevent_set.filter(type='removed_related_ipr').count(),
'Creating "removed" IprEvent did not create a "removed_related_ipr" DocEvent')
# The DocEvent descriptions must refer to the IprEvents
posted_docevent = doc.docevent_set.filter(type='posted_related_ipr').first()
self.assertIn(ipr.title, posted_docevent.desc,
'IprDisclosure title does not appear in DocEvent desc when posted')
removed_docevent = doc.docevent_set.filter(type='removed_related_ipr').first()
self.assertIn(ipr.title, removed_docevent.desc,
'IprDisclosure title does not appear in DocEvent desc when removed')