Skip to content

Commit 38b0a59

Browse files
committed
Merged personal/rjs/v4.70@6042:
* Made reporting IPR consistant across document searches, IPR searches, AD document queues, last calls, and agendas. All these points now report on the document(s) being directly queried, and the documents those transitively replace or obsolete. * Brought search results against WGs and document titles into consistency with results from searching for individual documents * Added the IPR count to the link on the documents main page (when not zero) * Built on Henrik's reimplementation of all_related_*, making the *_related_* functions return DocAlias? lists consistently, and added corresponding _relations_ functions to get lists of actual RelatedDocument? objects. * Added getting the DocAlias? with the same name to Document * Added getting related IPR disclosures (as described in the first bullet) to Document * Simplified ipr/related.py * Removed the use of DraftLikeDocAlias? and IETFWG from ipr/search.py. Retooled the various search functions and templates to use DocAlias? and IprDocAlias? directly. * Removed dead code from ipr/search.py * Removed the special handling of WG 2000 from ipr/search.py This fixes bug ietf-tools#1071 - Legacy-Id: 6083
2 parents d647139 + a593fec commit 38b0a59

13 files changed

Lines changed: 140 additions & 269 deletions

ietf/doc/models.py

Lines changed: 43 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -235,51 +235,66 @@ def canonical_name(self):
235235
return "charter-ietf-%s" % self.chartered_group.acronym
236236
return name
237237

238+
def canonical_docalias(self):
239+
return self.docalias_set.get(name=self.name)
240+
238241
def display_name(self):
239242
name = self.canonical_name()
240243
if name.startswith('rfc'):
241244
name = name.upper()
242245
return name
243246

244-
def related_that(self, relationship):
245-
"""Return the documents that are source of relationship targeting self."""
247+
def relations_that(self, relationship):
248+
"""Return the related-document objects that describe a given relationship targeting self."""
246249
if isinstance(relationship, str):
247250
relationship = [ relationship ]
248251
if isinstance(relationship, tuple):
249252
relationship = list(relationship)
250253
if not isinstance(relationship, list):
251254
raise TypeError("Expected a string, tuple or list, received %s" % type(relationship))
252-
return Document.objects.filter(relateddocument__target__document=self, relateddocument__relationship__in=relationship)
255+
return RelatedDocument.objects.filter(target__document=self, relationship__in=relationship).select_related('source')
253256

254-
def related_that_doc(self, relationship):
255-
"""Return the doc aliases that are target of relationship originating from self."""
257+
def all_relations_that(self, relationship, related=None):
258+
if not related:
259+
related = []
260+
rels = self.relations_that(relationship)
261+
for r in rels:
262+
if not r in related:
263+
related += [ r ]
264+
related = r.source.all_relations_that(relationship, related)
265+
return related
266+
267+
def relations_that_doc(self, relationship):
268+
"""Return the related-document objects that describe a given relationship from self to other documents."""
256269
if isinstance(relationship, str):
257270
relationship = [ relationship ]
258271
if isinstance(relationship, tuple):
259272
relationship = list(relationship)
260273
if not isinstance(relationship, list):
261274
raise TypeError("Expected a string, tuple or list, received %s" % type(relationship))
262-
return DocAlias.objects.filter(relateddocument__source=self, relateddocument__relationship__in=relationship)
275+
return RelatedDocument.objects.filter(source=self, relationship__in=relationship).select_related('target__document')
263276

264-
def all_related_that(self, relationship, related=None):
265-
if related is None:
277+
def all_relations_that_doc(self, relationship, related=None):
278+
if not related:
266279
related = []
267-
rel = self.related_that(relationship)
268-
for doc in rel:
269-
if not doc in related:
270-
related += [ doc ]
271-
related = doc.document.all_related_that(relationship, related)
280+
rels = self.relations_that_doc(relationship)
281+
for r in rels:
282+
if not r in related:
283+
related += [ r ]
284+
related = r.target.document.all_relations_that_doc(relationship, related)
272285
return related
273286

287+
def related_that(self, relationship):
288+
return list(set([x.source.docalias_set.get(name=x.source.name) for x in self.relations_that(relationship)]))
289+
290+
def all_related_that(self, relationship, related=None):
291+
return list(set([x.source.docalias_set.get(name=x.source.name) for x in self.all_relations_that(relationship)]))
292+
293+
def related_that_doc(self, relationship):
294+
return list(set([x.target for x in self.relations_that_doc(relationship)]))
295+
274296
def all_related_that_doc(self, relationship, related=None):
275-
if related is None:
276-
related = []
277-
rel = self.related_that_doc(relationship)
278-
for alias in rel:
279-
if not alias in related:
280-
related += [ alias ]
281-
related = alias.document.all_related_that_doc(relationship, related)
282-
return related
297+
return list(set([x.target for x in self.all_relations_that_doc(relationship)]))
283298

284299
def telechat_date(self, e=None):
285300
if not e:
@@ -352,7 +367,7 @@ def friendly_state(self):
352367
elif state.slug == "repl":
353368
rs = self.related_that("replaces")
354369
if rs:
355-
return mark_safe("Replaced by " + ", ".join("<a href=\"%s\">%s</a>" % (urlreverse('doc_view', kwargs=dict(name=name)), name) for name in rs))
370+
return mark_safe("Replaced by " + ", ".join("<a href=\"%s\">%s</a>" % (urlreverse('doc_view', kwargs=dict(name=alias.document)), alias.document) for alias in rs))
356371
else:
357372
return "Replaced"
358373
elif state.slug == "active":
@@ -383,6 +398,12 @@ def ipr(self):
383398
from ietf.ipr.models import IprDocAlias
384399
return IprDocAlias.objects.filter(doc_alias__document=self)
385400

401+
def related_ipr(self):
402+
"""Returns the IPR disclosures against this document and those documents this
403+
document directly or indirectly obsoletes or replaces
404+
"""
405+
from ietf.ipr.models import IprDocAlias
406+
return IprDocAlias.objects.filter(doc_alias__in=list(self.docalias_set.all())+self.all_related_that_doc(['obs','replaces']))
386407

387408

388409
class RelatedDocHistory(models.Model):

ietf/doc/views_doc.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
from ietf.ietfauth.utils import *
5050
from ietf.doc.views_status_change import RELATION_SLUGS as status_change_relationships
5151
from ietf.wgcharter.utils import historic_milestones_for_charter
52+
from ietf.ipr.models import IprDocAlias
5253

5354
def render_document_top(request, doc, tab, name):
5455
tabs = []
@@ -92,7 +93,7 @@ def document_main(request, name, rev=None):
9293

9394
group = doc.group
9495
if doc.type_id == 'conflrev':
95-
conflictdoc = doc.related_that_doc('conflrev').get().document
96+
conflictdoc = doc.related_that_doc('conflrev')[0].document
9697

9798
revisions = []
9899
for h in doc.history_set.order_by("time", "id"):
@@ -278,11 +279,11 @@ def document_main(request, name, rev=None):
278279
search_archive = urllib.quote(search_archive, safe="~")
279280

280281
# conflict reviews
281-
conflict_reviews = [d.name for d in doc.related_that("conflrev")]
282+
conflict_reviews = [d.document.name for d in doc.related_that("conflrev")]
282283

283284
status_change_docs = doc.related_that(status_change_relationships)
284-
status_changes = [ rel for rel in status_change_docs if rel.get_state_slug() in ('appr-sent','appr-pend')]
285-
proposed_status_changes = [ rel for rel in status_change_docs if rel.get_state_slug() in ('needshep','adrev','iesgeval','defer','appr-pr')]
285+
status_changes = [ rel.document for rel in status_change_docs if rel.document.get_state_slug() in ('appr-sent','appr-pend')]
286+
proposed_status_changes = [ rel.document for rel in status_change_docs if rel.document.get_state_slug() in ('needshep','adrev','iesgeval','defer','appr-pr')]
286287

287288
# remaining actions
288289
actions = []
@@ -345,9 +346,9 @@ def document_main(request, name, rev=None):
345346
replaces=[d.name for d in doc.related_that_doc("replaces")],
346347
replaced_by=[d.name for d in doc.related_that("replaces")],
347348
updates=[prettify_std_name(d.name) for d in doc.related_that_doc("updates")],
348-
updated_by=[prettify_std_name(d.canonical_name()) for d in doc.related_that("updates")],
349+
updated_by=[prettify_std_name(d.document.canonical_name()) for d in doc.related_that("updates")],
349350
obsoletes=[prettify_std_name(d.name) for d in doc.related_that_doc("obs")],
350-
obsoleted_by=[prettify_std_name(d.canonical_name()) for d in doc.related_that("obs")],
351+
obsoleted_by=[prettify_std_name(d.document.canonical_name()) for d in doc.related_that("obs")],
351352
conflict_reviews=conflict_reviews,
352353
status_changes=status_changes,
353354
proposed_status_changes=proposed_status_changes,

ietf/idrfc/mails.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ def generate_last_call_announcementREDESIGN(request, doc):
147147

148148
doc.filled_title = textwrap.fill(doc.title, width=70, subsequent_indent=" " * 3)
149149

150-
iprs, _ = iprs_from_docs(related_docs(DocAlias.objects.get(name=doc.canonical_name()),[]))
150+
iprs, _ = iprs_from_docs(related_docs(DocAlias.objects.get(name=doc.canonical_name())))
151151
if iprs:
152152
ipr_links = [ urlreverse("ietf.ipr.views.show", kwargs=dict(ipr_id=i.ipr_id)) for i in iprs]
153153
ipr_links = [ settings.IDTRACKER_BASE_URL+url if not url.startswith("http") else url for url in ipr_links ]

ietf/idrfc/views_search.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
from ietf.person.models import *
4545
from ietf.group.models import *
4646
from ietf.ipr.models import IprDocAlias
47+
from ietf.ipr.search import related_docs
4748
from ietf.idindex.index import active_drafts_index_by_group
4849

4950
class SearchForm(forms.Form):
@@ -135,9 +136,32 @@ def fill_in_search_attributes(docs):
135136
for d in docs:
136137
d.iprs = []
137138

139+
# Consider reworking the following block using all_relations_that_doc? That might simplify the final assembly
140+
# down to the code at "if a not in docs_dict"...
141+
142+
rel_docs = []
143+
rel_id_camefrom = {}
144+
for d in docs:
145+
if isinstance(d,DocAlias):
146+
d = d.document
147+
rel_this_doc = d.all_related_that_doc(['replaces','obs'])
148+
for rel in rel_this_doc:
149+
rel_id_camefrom.setdefault(rel.document.pk,[]).append(d.pk)
150+
rel_docs += [x.document for x in rel_this_doc]
151+
138152
ipr_docaliases = IprDocAlias.objects.filter(doc_alias__document__in=doc_ids).select_related('doc_alias')
139153
for a in ipr_docaliases:
140154
docs_dict[a.doc_alias.document_id].iprs.append(a)
155+
156+
rel_docs_dict = dict((d.pk, d) for d in rel_docs)
157+
rel_doc_ids = rel_docs_dict.keys()
158+
159+
rel_ipr_docaliases = IprDocAlias.objects.filter(doc_alias__document__in=rel_doc_ids).select_related('doc_alias')
160+
for a in rel_ipr_docaliases:
161+
if a.doc_alias.document_id in rel_id_camefrom:
162+
for k in rel_id_camefrom[a.doc_alias.document_id]:
163+
if a not in docs_dict[k].iprs:
164+
docs_dict[k].iprs.append(a)
141165

142166
# telechat date, can't do this with above query as we need to get TelechatDocEvents out
143167
seen = set()

ietf/ipr/related.py

Lines changed: 12 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -1,137 +1,15 @@
11
# Copyright The IETF Trust 2007, All Rights Reserved
22

3-
from django.conf import settings
4-
from django.db.models import Q
5-
from ietf.idtracker.models import InternetDraft, Rfc
6-
7-
inverse = {
8-
'updates': 'is_updated_by',
9-
'is_updated_by': 'updates',
10-
'obsoletes': 'is_obsoleted_by',
11-
'is_obsoleted_by': 'obsoletes',
12-
'replaces': 'is_replaced_by',
13-
'is_replaced_by': 'replaces',
14-
'is_rfc_of': 'is_draft_of',
15-
'is_draft_of': 'is_rfc_of',
16-
}
17-
18-
display_relation = {
19-
'updates': 'that updated',
20-
'is_updated_by': 'that was updated by',
21-
'obsoletes': 'that obsoleted',
22-
'is_obsoleted_by': 'that was obsoleted by',
23-
'replaces': 'that replaced',
24-
'is_replaced_by': 'that was replaced by',
25-
'is_rfc_of': 'which came from',
26-
'is_draft_of': 'that was published as',
27-
}
28-
29-
def set_related(obj, rel, target):
30-
#print obj, rel, target
31-
# remember only the first relationship we find.
32-
if not hasattr(obj, "related"):
33-
obj.related = target
34-
obj.relation = display_relation[rel]
35-
return obj
36-
37-
def set_relation(first, rel, second):
38-
set_related(first, rel, second)
39-
set_related(second, inverse[rel], first)
40-
41-
def related_docs(doc, found = []):
42-
"""Get a list of document related to the given document.
43-
"""
44-
#print "\nrelated_docs(%s, %s)" % (doc, found)
45-
found.append(doc)
46-
if isinstance(doc, Rfc):
47-
try:
48-
item = InternetDraft.objects.get(rfc_number=doc.rfc_number)
49-
if not item in found:
50-
set_relation(doc, 'is_rfc_of', item)
51-
found = related_docs(item, found)
52-
except InternetDraft.DoesNotExist:
53-
pass
54-
for entry in doc.updated_or_obsoleted_by.all():
55-
item = entry.rfc
56-
if not item in found:
57-
action = inverse[entry.action.lower()]
58-
set_relation(doc, action, item)
59-
found = related_docs(item, found)
60-
for entry in doc.updates_or_obsoletes.all():
61-
item = entry.rfc_acted_on
62-
if not item in found:
63-
action = entry.action.lower()
64-
set_relation(doc, action, item)
65-
found = related_docs(item, found)
66-
if isinstance(doc, InternetDraft):
67-
if doc.replaced_by_id:
68-
item = doc.replaced_by
69-
if not item in found:
70-
set_relation(doc, 'is_replaced_by', item)
71-
found = related_docs(item, found)
72-
for item in doc.replaces_set.all():
73-
if not item in found:
74-
set_relation(doc, 'replaces', item)
75-
found = related_docs(item, found)
76-
if doc.rfc_number:
77-
item = Rfc.objects.get(rfc_number=doc.rfc_number)
78-
if not item in found:
79-
set_relation(doc, 'is_draft_of', item)
80-
found = related_docs(item, found)
81-
return found
82-
83-
def related_docsREDESIGN(alias, _):
84-
"""Get related document aliases to given alias through depth-first search."""
85-
from ietf.doc.models import RelatedDocument
86-
from ietf.doc.proxy import DraftLikeDocAlias
87-
88-
mapping = dict(
89-
updates='that updated',
90-
obs='that obsoleted',
91-
replaces='that replaced',
92-
)
93-
inverse_mapping = dict(
94-
updates='that was updated by',
95-
obs='that was obsoleted by',
96-
replaces='that was replaced by',
97-
)
3+
def related_docs(alias):
4+
results = list(alias.document.docalias_set.all())
985

99-
res = [ alias ]
100-
remaining = [ alias ]
101-
while remaining:
102-
a = remaining.pop()
103-
related = RelatedDocument.objects.filter(relationship__in=mapping.keys()).filter(Q(source=a.document) | Q(target=a))
104-
for r in related:
105-
if r.source == a.document:
106-
found = DraftLikeDocAlias.objects.filter(pk=r.target_id)
107-
inverse = True
108-
else:
109-
found = DraftLikeDocAlias.objects.filter(document=r.source)
110-
inverse = False
111-
112-
for x in found:
113-
if not x in res:
114-
x.related = a
115-
x.relation = (inverse_mapping if inverse else mapping)[r.relationship_id]
116-
res.append(x)
117-
remaining.append(x)
118-
119-
# there's one more source of relatedness, a draft can have been published
120-
aliases = DraftLikeDocAlias.objects.filter(document=a.document).exclude(pk__in=[x.pk for x in res])
121-
for oa in aliases:
122-
rel = None
123-
if a.name.startswith("rfc") and oa.name.startswith("draft"):
124-
rel = "that was published as"
125-
elif a.name.startswith("draft") and oa.name.startswith("rfc"):
126-
rel = "which came from"
127-
128-
if rel:
129-
oa.related = a
130-
oa.relation = rel
131-
res.append(oa)
132-
remaining.append(oa)
133-
134-
return res
135-
136-
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
137-
related_docs = related_docsREDESIGN
6+
rels = alias.document.all_relations_that_doc(['replaces','obs'])
7+
8+
for rel in rels:
9+
rel_aliases = list(rel.target.document.docalias_set.all())
10+
11+
for x in rel_aliases:
12+
x.related = rel
13+
x.relation = rel.relationship.revname
14+
results += rel_aliases
15+
return list(set(results))

0 commit comments

Comments
 (0)