Create DocEvent when related IprDisclosure is posted or removed. Fixes #2811. Commit ready for merge.
- Legacy-Id: 17284
This commit is contained in:
parent
cd77c76372
commit
b02b42da81
21
ietf/doc/migrations/0029_add_ipr_event_types.py
Normal file
21
ietf/doc/migrations/0029_add_ipr_event_types.py
Normal 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),
|
||||
),
|
||||
]
|
|
@ -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
|
||||
|
|
68
ietf/ipr/migrations/0007_create_ipr_doc_events.py
Normal file
68
ietf/ipr/migrations/0007_create_ipr_doc_events.py
Normal 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),
|
||||
]
|
|
@ -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']
|
||||
|
||||
|
|
|
@ -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')
|
||||
|
Loading…
Reference in a new issue