Skip to content

Commit a593fec

Browse files
committed
* 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 Fixes bug 1071 - Legacy-Id: 6042
1 parent 8045425 commit a593fec

13 files changed

Lines changed: 136 additions & 269 deletions

ietf/doc/models.py

Lines changed: 39 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -235,51 +235,64 @@ 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):
246248
if isinstance(relationship, str):
247249
relationship = [ relationship ]
248250
if isinstance(relationship, tuple):
249251
relationship = list(relationship)
250252
if not isinstance(relationship, list):
251253
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)
254+
return RelatedDocument.objects.filter(target__document=self, relationship__in=relationship).select_related('source')
253255

254-
def related_that_doc(self, relationship):
255-
"""Return the doc aliases that are target of relationship originating from self."""
256+
def all_relations_that(self, relationship, related=None):
257+
if not related:
258+
related = []
259+
rels = self.relations_that(relationship)
260+
for r in rels:
261+
if not r in related:
262+
related += [ r ]
263+
related = r.source.all_relations_that(relationship, related)
264+
return related
265+
266+
def relations_that_doc(self, relationship):
256267
if isinstance(relationship, str):
257268
relationship = [ relationship ]
258269
if isinstance(relationship, tuple):
259270
relationship = list(relationship)
260271
if not isinstance(relationship, list):
261272
raise TypeError("Expected a string, tuple or list, received %s" % type(relationship))
262-
return DocAlias.objects.filter(relateddocument__source=self, relateddocument__relationship__in=relationship)
273+
return RelatedDocument.objects.filter(source=self, relationship__in=relationship).select_related('target__document')
263274

264-
def all_related_that(self, relationship, related=None):
265-
if related is None:
275+
def all_relations_that_doc(self, relationship, related=None):
276+
if not related:
266277
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)
278+
rels = self.relations_that_doc(relationship)
279+
for r in rels:
280+
if not r in related:
281+
related += [ r ]
282+
related = r.target.document.all_relations_that_doc(relationship, related)
272283
return related
273284

285+
def related_that(self, relationship):
286+
return list(set([x.source.docalias_set.get(name=x.source.name) for x in self.relations_that(relationship)]))
287+
288+
def all_related_that(self, relationship, related=None):
289+
return list(set([x.source.docalias_set.get(name=x.source.name) for x in self.all_relations_that(relationship)]))
290+
291+
def related_that_doc(self, relationship):
292+
return list(set([x.target for x in self.relations_that_doc(relationship)]))
293+
274294
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
295+
return list(set([x.target for x in self.all_relations_that_doc(relationship)]))
283296

284297
def telechat_date(self, e=None):
285298
if not e:
@@ -352,7 +365,7 @@ def friendly_state(self):
352365
elif state.slug == "repl":
353366
rs = self.related_that("replaces")
354367
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))
368+
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))
356369
else:
357370
return "Replaced"
358371
elif state.slug == "active":
@@ -383,6 +396,10 @@ def ipr(self):
383396
from ietf.ipr.models import IprDocAlias
384397
return IprDocAlias.objects.filter(doc_alias__document=self)
385398

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

387404

388405
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"):
@@ -271,11 +272,11 @@ def document_main(request, name, rev=None):
271272
search_archive = urllib.quote(search_archive, safe="~")
272273

273274
# conflict reviews
274-
conflict_reviews = [d.name for d in doc.related_that("conflrev")]
275+
conflict_reviews = [d.document.name for d in doc.related_that("conflrev")]
275276

276277
status_change_docs = doc.related_that(status_change_relationships)
277-
status_changes = [ rel for rel in status_change_docs if rel.get_state_slug() in ('appr-sent','appr-pend')]
278-
proposed_status_changes = [ rel for rel in status_change_docs if rel.get_state_slug() in ('needshep','adrev','iesgeval','defer','appr-pr')]
278+
status_changes = [ rel.document for rel in status_change_docs if rel.document.get_state_slug() in ('appr-sent','appr-pend')]
279+
proposed_status_changes = [ rel.document for rel in status_change_docs if rel.document.get_state_slug() in ('needshep','adrev','iesgeval','defer','appr-pr')]
279280

280281
# remaining actions
281282
actions = []
@@ -338,9 +339,9 @@ def document_main(request, name, rev=None):
338339
replaces=[d.name for d in doc.related_that_doc("replaces")],
339340
replaced_by=[d.name for d in doc.related_that("replaces")],
340341
updates=[prettify_std_name(d.name) for d in doc.related_that_doc("updates")],
341-
updated_by=[prettify_std_name(d.canonical_name()) for d in doc.related_that("updates")],
342+
updated_by=[prettify_std_name(d.document.canonical_name()) for d in doc.related_that("updates")],
342343
obsoletes=[prettify_std_name(d.name) for d in doc.related_that_doc("obs")],
343-
obsoleted_by=[prettify_std_name(d.canonical_name()) for d in doc.related_that("obs")],
344+
obsoleted_by=[prettify_std_name(d.document.canonical_name()) for d in doc.related_that("obs")],
344345
conflict_reviews=conflict_reviews,
345346
status_changes=status_changes,
346347
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):
@@ -133,9 +134,32 @@ def fill_in_search_attributes(docs):
133134
for d in docs:
134135
d.iprs = []
135136

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

140164
# telechat date, can't do this with above query as we need to get TelechatDocEvents out
141165
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)