Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ietf/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ class PersonalInformationExportView(DetailView, JsonExportMixin):

def get(self, request):
person = get_object_or_404(self.model, user=request.user)
expand = ['searchrule', 'documentauthor', 'ad_document_set', 'ad_dochistory_set', 'docevent',
expand = ['searchrule', 'documentauthor', 'rfcauthor', 'ad_document_set', 'ad_dochistory_set', 'docevent',
'ballotpositiondocevent', 'deletedevent', 'email_set', 'groupevent', 'role', 'rolehistory', 'iprdisclosurebase',
'iprevent', 'liaisonstatementevent', 'allowlisted', 'schedule', 'constraint', 'schedulingevent', 'message',
'sendqueue', 'nominee', 'topicfeedbacklastseen', 'alias', 'email', 'apikeys', 'personevent',
Expand Down
7 changes: 5 additions & 2 deletions ietf/community/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,10 @@ def docs_matching_community_list_rule(rule):
return docs.filter(group=rule.group_id)
elif rule.rule_type.startswith("state_"):
return docs
elif rule.rule_type in ["author", "author_rfc"]:
elif rule.rule_type == "author":
return docs.filter(documentauthor__person=rule.person)
elif rule.rule_type == "author_rfc":
return docs.filter(Q(rfcauthor__person=rule.person)|Q(rfcauthor__isnull=True,documentauthor__person=rule.person))
elif rule.rule_type == "ad":
return docs.filter(ad=rule.person)
elif rule.rule_type == "shepherd":
Expand Down Expand Up @@ -122,9 +124,10 @@ def community_list_rules_matching_doc(doc):

# author rules
if doc.type_id == "rfc":
# this will over-return but will be least likely to surprise
rules |= SearchRule.objects.filter(
rule_type="author_rfc",
person__in=list(Person.objects.filter(documentauthor__document=doc)),
person__in=list(Person.objects.filter(Q(documentauthor__document=doc)|Q(rfcauthor__document=doc))),
)
else:
rules |= SearchRule.objects.filter(
Expand Down
8 changes: 7 additions & 1 deletion ietf/doc/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
TelechatDocEvent, BallotPositionDocEvent, ReviewRequestDocEvent, InitialReviewDocEvent,
AddedMessageEvent, SubmissionDocEvent, DeletedEvent, EditedAuthorsDocEvent, DocumentURL,
ReviewAssignmentDocEvent, IanaExpertDocEvent, IRSGBallotDocEvent, DocExtResource, DocumentActionHolder,
BofreqEditorDocEvent, BofreqResponsibleDocEvent, StoredObject )
BofreqEditorDocEvent, BofreqResponsibleDocEvent, StoredObject, RfcAuthor )

from ietf.utils.validators import validate_external_resource_value

Expand Down Expand Up @@ -236,3 +236,9 @@ def is_deleted(self, instance):


admin.site.register(StoredObject, StoredObjectAdmin)

class RfcAuthorAdmin(admin.ModelAdmin):
list_display = ['id', 'document', 'titlepage_name', 'person', 'email', 'affiliation', 'country', 'order']
search_fields = ['document__name', 'titlepage_name', 'person__name', 'email__address', 'affiliation', 'country']
raw_id_fields = ["document", "person", "email"]
admin.site.register(RfcAuthor, RfcAuthorAdmin)
14 changes: 13 additions & 1 deletion ietf/doc/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

from ietf.doc.models import ( Document, DocEvent, NewRevisionDocEvent, State, DocumentAuthor,
StateDocEvent, BallotPositionDocEvent, BallotDocEvent, BallotType, IRSGBallotDocEvent, TelechatDocEvent,
DocumentActionHolder, BofreqEditorDocEvent, BofreqResponsibleDocEvent, DocExtResource )
DocumentActionHolder, BofreqEditorDocEvent, BofreqResponsibleDocEvent, DocExtResource, RfcAuthor )
from ietf.group.models import Group
from ietf.person.factories import PersonFactory
from ietf.group.factories import RoleFactory
Expand Down Expand Up @@ -382,6 +382,18 @@ class Meta:
country = factory.Faker('country')
order = factory.LazyAttribute(lambda o: o.document.documentauthor_set.count() + 1)

class RfcAuthorFactory(factory.django.DjangoModelFactory):
class Meta:
model = RfcAuthor

document = factory.SubFactory(DocumentFactory)
titlepage_name = factory.Faker('name')
person = factory.SubFactory('ietf.person.factories.PersonFactory')
email = factory.LazyAttribute(lambda obj: obj.person.email())
affiliation = factory.Faker('company')
country = factory.Faker('country')
order = factory.LazyAttribute(lambda o: o.document.rfcauthor_set.count() + 1)

class WgDocumentAuthorFactory(DocumentAuthorFactory):
document = factory.SubFactory(WgDraftFactory)

Expand Down
69 changes: 0 additions & 69 deletions ietf/doc/management/commands/reset_rfc_authors.py

This file was deleted.

72 changes: 0 additions & 72 deletions ietf/doc/management/commands/tests.py

This file was deleted.

82 changes: 82 additions & 0 deletions ietf/doc/migrations/0027_rfcauthor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Copyright The IETF Trust 2025, All Rights Reserved

from django.db import migrations, models
import django.db.models.deletion
import ietf.utils.models


class Migration(migrations.Migration):

dependencies = [
("person", "0004_alter_person_photo_alter_person_photo_thumb"),
("doc", "0026_change_wg_state_descriptions"),
]

operations = [
migrations.CreateModel(
name="RfcAuthor",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("titlepage_name", models.CharField(max_length=128)),
("is_editor", models.BooleanField(default=False)),
(
"affiliation",
models.CharField(
blank=True,
help_text="Organization/company used by author for submission",
max_length=100,
),
),
(
"country",
models.CharField(
blank=True,
help_text="Country used by author for submission",
max_length=255,
),
),
("order", models.IntegerField(default=1)),
(
"document",
ietf.utils.models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to="doc.document"
),
),
(
"email",
ietf.utils.models.ForeignKey(
blank=True,
help_text="Email address used by author for submission",
null=True,
on_delete=django.db.models.deletion.PROTECT,
to="person.email",
),
),
(
"person",
ietf.utils.models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.PROTECT,
to="person.person",
),
),
],
options={
"ordering": ["document", "order"],
"indexes": [
models.Index(
fields=["document", "order"],
name="doc_rfcauth_documen_6b5dc4_idx",
)
],
},
),
]
64 changes: 64 additions & 0 deletions ietf/doc/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# -*- coding: utf-8 -*-


from collections import namedtuple
import datetime
import logging
import os
Expand Down Expand Up @@ -407,7 +408,33 @@ def friendly_state(self):
else:
return state.name

def author_names(self):
names = []
if self.type_id=="rfc" and self.rfcauthor_set.exists():
for author in self.rfcauthor_set.all():
if author.person:
names.append(author.person.name)
elif author.titlepage_name != "":
names.append(author.titlepage_name)
else:
names = [author.person.name for author in self.documentauthor_set.all()]
return names

def author_persons_or_names(self):
Author = namedtuple("Author", "person titlepage_name")
persons_or_names = []
if self.type_id=="rfc" and self.rfcauthor_set.exists():
for author in self.rfcauthor_set.all():
persons_or_names.append(Author(person=author.person, titlepage_name=author.titlepage_name))
else:
for author in self.documentauthor_set.all():
persons_or_names.append(Author(person=author.person, titlepage_name=""))
return persons_or_names


def author_list(self):
if self.type_id == "rfc":
raise NotImplementedError
best_addresses = []
for author in self.documentauthor_set.all():
if author.email:
Expand All @@ -418,6 +445,8 @@ def author_list(self):
return ", ".join(best_addresses)

def authors(self):
if self.type_id == "rfc":
raise NotImplementedError
return [ a.person for a in self.documentauthor_set.all() ]

# This, and several other ballot related functions here, assume that there is only one active ballot for a document at any point in time.
Expand Down Expand Up @@ -845,6 +874,41 @@ def is_approved_downref(self):

return False

class RfcAuthor(models.Model):
"""Captures the authors of an RFC as represented on the RFC title page.

This deviates from DocumentAuthor in that it does not get moved into the DocHistory
hierarchy as documents are saved. It will attempt to preserve email, country, and affiliation
from the DocumentAuthor objects associated with the draft leading to this RFC (which
may be wrong if the author moves or changes affiliation while the document is in the
queue).

It does not, at this time, attempt to capture the authors from anything _but_ the title
page. The datatracker may know more about such authors based on information from the draft
leading to the RFC, and future work may take that into account.

Once doc.rfcauthor_set.exists() for a doc of type `rfc`, doc.documentauthor_set should be
ignored.
"""

document = ForeignKey("Document", on_delete=models.CASCADE)
titlepage_name = models.CharField(max_length=128)
is_editor = models.BooleanField(default=False)
person = ForeignKey(Person, null=True, on_delete=models.PROTECT)
email = ForeignKey(Email, help_text="Email address used by author for submission", blank=True, null=True, on_delete=models.PROTECT)
affiliation = models.CharField(max_length=100, blank=True, help_text="Organization/company used by author for submission")
country = models.CharField(max_length=255, blank=True, help_text="Country used by author for submission")
order = models.IntegerField(default=1)

def __str__(self):
return u"%s %s (%s)" % (self.document.name, self.person, self.order)

class Meta:
ordering=["document", "order"]
indexes=[
models.Index(fields=["document", "order"])
]

class DocumentAuthorInfo(models.Model):
person = ForeignKey(Person)
# email should only be null for some historic documents
Expand Down
Loading
Loading