Merged in [19658] from jennifer@painless-security.com:
Return rev used to find doc when heuristics modify the input. Share heuristics between rfcdiff and html views. Fixes #3437.
- Legacy-Id: 19659
Note: SVN reference [19658] has been migrated to Git commit 38469966e2
This commit is contained in:
commit
0996842651
|
@ -87,6 +87,19 @@ class BaseDocumentFactory(factory.django.DjangoModelFactory):
|
||||||
continue
|
continue
|
||||||
obj.relateddocument_set.create(relationship_id=rel_id, target=docalias)
|
obj.relateddocument_set.create(relationship_id=rel_id, target=docalias)
|
||||||
|
|
||||||
|
@factory.post_generation
|
||||||
|
def create_revisions(obj, create, extracted, **kwargs): # pylint: disable=no-self-argument
|
||||||
|
"""Create additional revisions of the document
|
||||||
|
|
||||||
|
Argument should be an iterable of revisions. Remember that range() is exclusive on the end
|
||||||
|
index, so range(1, 10) stops at 9.
|
||||||
|
"""
|
||||||
|
if create and extracted:
|
||||||
|
for rev in extracted:
|
||||||
|
e = NewRevisionDocEventFactory(doc=obj, rev=f'{rev:02d}')
|
||||||
|
obj.rev = f'{rev:02d}'
|
||||||
|
obj.save_with_history([e])
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _after_postgeneration(cls, obj, create, results=None):
|
def _after_postgeneration(cls, obj, create, results=None):
|
||||||
"""Save again the instance if creating and at least one hook ran."""
|
"""Save again the instance if creating and at least one hook ran."""
|
||||||
|
|
|
@ -2494,6 +2494,7 @@ class RfcdiffSupportTests(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
self.target_view = 'ietf.doc.views_doc.rfcdiff_latest_json'
|
self.target_view = 'ietf.doc.views_doc.rfcdiff_latest_json'
|
||||||
|
self._last_rfc_num = 8000
|
||||||
|
|
||||||
def getJson(self, view_args):
|
def getJson(self, view_args):
|
||||||
url = urlreverse(self.target_view, kwargs=view_args)
|
url = urlreverse(self.target_view, kwargs=view_args)
|
||||||
|
@ -2501,79 +2502,85 @@ class RfcdiffSupportTests(TestCase):
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
return r.json()
|
return r.json()
|
||||||
|
|
||||||
def test_draft(self):
|
def next_rfc_number(self):
|
||||||
draft = IndividualDraftFactory(name='draft-somebody-did-something',rev='00')
|
self._last_rfc_num += 1
|
||||||
for r in range(0,13):
|
return self._last_rfc_num
|
||||||
e = NewRevisionDocEventFactory(doc=draft,rev=f'{r:02d}')
|
|
||||||
draft.rev = f'{r:02d}'
|
def do_draft_test(self, name):
|
||||||
draft.save_with_history([e])
|
draft = IndividualDraftFactory(name=name, rev='00', create_revisions=range(0,13))
|
||||||
draft = reload_db_objects(draft)
|
draft = reload_db_objects(draft)
|
||||||
|
|
||||||
received = self.getJson(dict(name=draft.name))
|
received = self.getJson(dict(name=draft.name))
|
||||||
self.assertEqual(received, dict(
|
self.assertEqual(
|
||||||
name=draft.name,
|
received,
|
||||||
rev=draft.rev,
|
dict(
|
||||||
content_url=draft.get_href(),
|
name=draft.name,
|
||||||
previous=f'{draft.name}-{(int(draft.rev)-1):02d}'
|
rev=draft.rev,
|
||||||
))
|
content_url=draft.get_href(),
|
||||||
|
previous=f'{draft.name}-{(int(draft.rev)-1):02d}'
|
||||||
|
),
|
||||||
|
'Incorrect JSON when draft revision not specified',
|
||||||
|
)
|
||||||
|
|
||||||
received = self.getJson(dict(name=draft.name, rev=draft.rev))
|
received = self.getJson(dict(name=draft.name, rev=draft.rev))
|
||||||
self.assertEqual(received, dict(
|
self.assertEqual(
|
||||||
name=draft.name,
|
received,
|
||||||
rev=draft.rev,
|
dict(
|
||||||
content_url=draft.get_href(),
|
name=draft.name,
|
||||||
previous=f'{draft.name}-{(int(draft.rev)-1):02d}'
|
rev=draft.rev,
|
||||||
))
|
content_url=draft.get_href(),
|
||||||
|
previous=f'{draft.name}-{(int(draft.rev)-1):02d}'
|
||||||
|
),
|
||||||
|
'Incorrect JSON when latest revision specified',
|
||||||
|
)
|
||||||
|
|
||||||
received = self.getJson(dict(name=draft.name, rev='10'))
|
received = self.getJson(dict(name=draft.name, rev='10'))
|
||||||
self.assertEqual(received, dict(
|
self.assertEqual(
|
||||||
name=draft.name,
|
received,
|
||||||
rev='10',
|
dict(
|
||||||
content_url=draft.history_set.get(rev='10').get_href(),
|
name=draft.name,
|
||||||
previous=f'{draft.name}-09'
|
rev='10',
|
||||||
))
|
content_url=draft.history_set.get(rev='10').get_href(),
|
||||||
|
previous=f'{draft.name}-09'
|
||||||
|
),
|
||||||
|
'Incorrect JSON when historical revision specified',
|
||||||
|
)
|
||||||
|
|
||||||
received = self.getJson(dict(name=draft.name, rev='00'))
|
received = self.getJson(dict(name=draft.name, rev='00'))
|
||||||
self.assertNotIn('previous', received)
|
self.assertNotIn('previous', received, 'Rev 00 has no previous name when not replacing a draft')
|
||||||
|
|
||||||
replaced = IndividualDraftFactory()
|
replaced = IndividualDraftFactory()
|
||||||
RelatedDocument.objects.create(relationship_id='replaces',source=draft,target=replaced.docalias.first())
|
RelatedDocument.objects.create(relationship_id='replaces',source=draft,target=replaced.docalias.first())
|
||||||
received = self.getJson(dict(name=draft.name, rev='00'))
|
received = self.getJson(dict(name=draft.name, rev='00'))
|
||||||
self.assertEqual(received['previous'], f'{replaced.name}-{replaced.rev}')
|
self.assertEqual(received['previous'], f'{replaced.name}-{replaced.rev}',
|
||||||
|
'Rev 00 has a previous name when replacing a draft')
|
||||||
|
|
||||||
|
def test_draft(self):
|
||||||
|
# test with typical, straightforward names
|
||||||
|
self.do_draft_test(name='draft-somebody-did-a-thing')
|
||||||
|
# try with different potentially problematic names
|
||||||
|
self.do_draft_test(name='draft-someone-did-something-01-02')
|
||||||
|
self.do_draft_test(name='draft-someone-did-something-else-02')
|
||||||
|
self.do_draft_test(name='draft-someone-did-something-02-weird-01')
|
||||||
|
|
||||||
def test_draft_with_broken_history(self):
|
def do_draft_with_broken_history_test(self, name):
|
||||||
draft = IndividualDraftFactory(rev='10')
|
draft = IndividualDraftFactory(name=name, rev='10')
|
||||||
received = self.getJson(dict(name=draft.name,rev='09'))
|
received = self.getJson(dict(name=draft.name,rev='09'))
|
||||||
self.assertEqual(received['rev'],'09')
|
self.assertEqual(received['rev'],'09')
|
||||||
self.assertEqual(received['previous'], f'{draft.name}-08')
|
self.assertEqual(received['previous'], f'{draft.name}-08')
|
||||||
self.assertTrue('warning' in received)
|
self.assertTrue('warning' in received)
|
||||||
|
|
||||||
|
def test_draft_with_broken_history(self):
|
||||||
|
# test with typical, straightforward names
|
||||||
|
self.do_draft_with_broken_history_test(name='draft-somebody-did-something')
|
||||||
|
# try with different potentially problematic names
|
||||||
|
self.do_draft_with_broken_history_test(name='draft-someone-did-something-01-02')
|
||||||
|
self.do_draft_with_broken_history_test(name='draft-someone-did-something-else-02')
|
||||||
|
self.do_draft_with_broken_history_test(name='draft-someone-did-something-02-weird-03')
|
||||||
|
|
||||||
def test_draftname_with_numeric_suffix(self):
|
def do_rfc_test(self, draft_name):
|
||||||
draft = IndividualDraftFactory(name='draft-someone-did-something-01-02',rev='00')
|
draft = WgDraftFactory(name=draft_name, create_revisions=range(0,2))
|
||||||
for r in range(0,4):
|
draft.docalias.create(name=f'rfc{self.next_rfc_number():04}')
|
||||||
e = NewRevisionDocEventFactory(doc=draft,rev=f'{r:02d}')
|
|
||||||
draft.rev = f'{r:02d}'
|
|
||||||
draft.save_with_history([e])
|
|
||||||
|
|
||||||
received = self.getJson(dict(name=draft.name))
|
|
||||||
self.assertEqual(received['rev'],'03')
|
|
||||||
self.assertIn('01-02-03',received['content_url'])
|
|
||||||
self.assertIn('01-02-02',received['previous'])
|
|
||||||
|
|
||||||
received = self.getJson(dict(name=draft.name,rev='02'))
|
|
||||||
self.assertEqual(received['rev'],'02')
|
|
||||||
self.assertIn('01-02-02',received['content_url'])
|
|
||||||
|
|
||||||
def test_rfc(self):
|
|
||||||
draft = WgDraftFactory()
|
|
||||||
for r in range(0,2):
|
|
||||||
e = NewRevisionDocEventFactory(doc=draft,rev=f'{r:02d}')
|
|
||||||
draft.rev = f'{r:02d}'
|
|
||||||
draft.save_with_history([e])
|
|
||||||
|
|
||||||
draft.docalias.create(name='rfc8000')
|
|
||||||
draft.set_state(State.objects.get(type_id='draft',slug='rfc'))
|
draft.set_state(State.objects.get(type_id='draft',slug='rfc'))
|
||||||
draft.set_state(State.objects.get(type_id='draft-iesg', slug='pub'))
|
draft.set_state(State.objects.get(type_id='draft-iesg', slug='pub'))
|
||||||
draft = reload_db_objects(draft)
|
draft = reload_db_objects(draft)
|
||||||
|
@ -2581,26 +2588,47 @@ class RfcdiffSupportTests(TestCase):
|
||||||
|
|
||||||
number = rfc.rfc_number()
|
number = rfc.rfc_number()
|
||||||
received = self.getJson(dict(name=number))
|
received = self.getJson(dict(name=number))
|
||||||
self.assertEqual(received, dict(
|
self.assertEqual(
|
||||||
content_url=rfc.get_href(),
|
received,
|
||||||
name=rfc.canonical_name(),
|
dict(
|
||||||
previous=f'{draft.name}-{draft.rev}',
|
content_url=rfc.get_href(),
|
||||||
))
|
name=rfc.canonical_name(),
|
||||||
|
previous=f'{draft.name}-{draft.rev}',
|
||||||
|
),
|
||||||
|
'Can look up an RFC by number',
|
||||||
|
)
|
||||||
|
|
||||||
num_received = received
|
num_received = received
|
||||||
received = self.getJson(dict(name=rfc.canonical_name()))
|
received = self.getJson(dict(name=rfc.canonical_name()))
|
||||||
self.assertEqual(num_received, received)
|
self.assertEqual(num_received, received, 'RFC by canonical name gives same result as by number')
|
||||||
|
|
||||||
received = self.getJson(dict(name=f'RfC {number}'))
|
received = self.getJson(dict(name=f'RfC {number}'))
|
||||||
self.assertEqual(num_received, received)
|
self.assertEqual(num_received, received, 'RFC with unusual spacing/caps gives same result as by number')
|
||||||
|
|
||||||
|
received = self.getJson(dict(name=draft.name))
|
||||||
|
self.assertEqual(num_received, received, 'RFC by draft name and no rev gives same result as by number')
|
||||||
|
|
||||||
|
received = self.getJson(dict(name=draft.name, rev='01'))
|
||||||
|
self.assertEqual(
|
||||||
|
received,
|
||||||
|
dict(
|
||||||
|
content_url=draft.history_set.get(rev='01').get_href(),
|
||||||
|
name=draft.name,
|
||||||
|
rev='01',
|
||||||
|
previous=f'{draft.name}-00',
|
||||||
|
),
|
||||||
|
'RFC by draft name with rev should give draft name, not canonical name'
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_rfc(self):
|
||||||
|
# simple draft name
|
||||||
|
self.do_rfc_test(draft_name='draft-test-ar-ef-see')
|
||||||
|
# tricky draft names
|
||||||
|
self.do_rfc_test(draft_name='draft-whatever-02')
|
||||||
|
self.do_rfc_test(draft_name='draft-test-me-03-04')
|
||||||
|
|
||||||
def test_rfc_with_tombstone(self):
|
def test_rfc_with_tombstone(self):
|
||||||
draft = WgDraftFactory()
|
draft = WgDraftFactory(create_revisions=range(0,2))
|
||||||
for r in range(0,2):
|
|
||||||
e = NewRevisionDocEventFactory(doc=draft,rev=f'{r:02d}')
|
|
||||||
draft.rev = f'{r:02d}'
|
|
||||||
draft.save_with_history([e])
|
|
||||||
|
|
||||||
draft.docalias.create(name='rfc3261') # See views_doc.HAS_TOMBSTONE
|
draft.docalias.create(name='rfc3261') # See views_doc.HAS_TOMBSTONE
|
||||||
draft.set_state(State.objects.get(type_id='draft',slug='rfc'))
|
draft.set_state(State.objects.get(type_id='draft',slug='rfc'))
|
||||||
draft.set_state(State.objects.get(type_id='draft-iesg', slug='pub'))
|
draft.set_state(State.objects.get(type_id='draft-iesg', slug='pub'))
|
||||||
|
@ -2611,4 +2639,46 @@ class RfcdiffSupportTests(TestCase):
|
||||||
received = self.getJson(dict(name=rfc.canonical_name()))
|
received = self.getJson(dict(name=rfc.canonical_name()))
|
||||||
self.assertTrue(received['previous'].endswith('00'))
|
self.assertTrue(received['previous'].endswith('00'))
|
||||||
|
|
||||||
|
def do_rfc_with_broken_history_test(self, draft_name):
|
||||||
|
draft = WgDraftFactory(rev='10', name=draft_name)
|
||||||
|
draft.docalias.create(name=f'rfc{self.next_rfc_number():04}')
|
||||||
|
draft.set_state(State.objects.get(type_id='draft',slug='rfc'))
|
||||||
|
draft.set_state(State.objects.get(type_id='draft-iesg', slug='pub'))
|
||||||
|
draft = reload_db_objects(draft)
|
||||||
|
rfc = draft
|
||||||
|
|
||||||
|
received = self.getJson(dict(name=draft.name))
|
||||||
|
self.assertEqual(
|
||||||
|
received,
|
||||||
|
dict(
|
||||||
|
content_url=rfc.get_href(),
|
||||||
|
name=rfc.canonical_name(),
|
||||||
|
previous=f'{draft.name}-10',
|
||||||
|
),
|
||||||
|
'RFC by draft name without rev should return canonical RFC name and no rev',
|
||||||
|
)
|
||||||
|
|
||||||
|
received = self.getJson(dict(name=draft.name, rev='10'))
|
||||||
|
self.assertEqual(received['name'], draft.name, 'RFC by draft name with rev should return draft name')
|
||||||
|
self.assertEqual(received['rev'], '10', 'Requested rev should be returned')
|
||||||
|
self.assertEqual(received['previous'], f'{draft.name}-09', 'Previous rev is one less than requested')
|
||||||
|
self.assertIn(f'{draft.name}-10', received['content_url'], 'Returned URL should include requested rev')
|
||||||
|
self.assertNotIn('warning', received, 'No warning when we have the rev requested')
|
||||||
|
|
||||||
|
received = self.getJson(dict(name=f'{draft.name}-09'))
|
||||||
|
self.assertEqual(received['name'], draft.name, 'RFC by draft name with rev should return draft name')
|
||||||
|
self.assertEqual(received['rev'], '09', 'Requested rev should be returned')
|
||||||
|
self.assertEqual(received['previous'], f'{draft.name}-08', 'Previous rev is one less than requested')
|
||||||
|
self.assertIn(f'{draft.name}-09', received['content_url'], 'Returned URL should include requested rev')
|
||||||
|
self.assertEqual(
|
||||||
|
received['warning'],
|
||||||
|
'History for this version not found - these results are speculation',
|
||||||
|
'Warning should be issued when requested rev is not found'
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_rfc_with_broken_history(self):
|
||||||
|
# simple draft name
|
||||||
|
self.do_rfc_with_broken_history_test(draft_name='draft-some-draft')
|
||||||
|
# tricky draft names
|
||||||
|
self.do_rfc_with_broken_history_test(draft_name='draft-gizmo-01')
|
||||||
|
self.do_rfc_with_broken_history_test(draft_name='draft-oh-boy-what-a-draft-02-03')
|
||||||
|
|
|
@ -8,9 +8,9 @@ from ietf.name.models import DocTagName
|
||||||
from ietf.person.factories import PersonFactory
|
from ietf.person.factories import PersonFactory
|
||||||
from ietf.utils.test_utils import TestCase
|
from ietf.utils.test_utils import TestCase
|
||||||
from ietf.person.models import Person
|
from ietf.person.models import Person
|
||||||
from ietf.doc.factories import DocumentFactory
|
from ietf.doc.factories import DocumentFactory, WgRfcFactory
|
||||||
from ietf.doc.models import State, DocumentActionHolder, DocumentAuthor
|
from ietf.doc.models import State, DocumentActionHolder, DocumentAuthor, Document
|
||||||
from ietf.doc.utils import update_action_holders, add_state_change_event, update_documentauthors
|
from ietf.doc.utils import update_action_holders, add_state_change_event, update_documentauthors, fuzzy_find_documents
|
||||||
|
|
||||||
|
|
||||||
class ActionHoldersTests(TestCase):
|
class ActionHoldersTests(TestCase):
|
||||||
|
@ -239,4 +239,49 @@ class MiscTests(TestCase):
|
||||||
self.assertIn('cleared country (was "USA")', events[0].desc)
|
self.assertIn('cleared country (was "USA")', events[0].desc)
|
||||||
docauth = doc.documentauthor_set.first()
|
docauth = doc.documentauthor_set.first()
|
||||||
self.assertEqual(docauth.affiliation, '')
|
self.assertEqual(docauth.affiliation, '')
|
||||||
self.assertEqual(docauth.country, '')
|
self.assertEqual(docauth.country, '')
|
||||||
|
|
||||||
|
def do_fuzzy_find_documents_rfc_test(self, name):
|
||||||
|
rfc = WgRfcFactory(name=name, create_revisions=(0, 1, 2))
|
||||||
|
rfc = Document.objects.get(pk=rfc.pk) # clear out any cached values
|
||||||
|
|
||||||
|
# by canonical name
|
||||||
|
found = fuzzy_find_documents(rfc.canonical_name(), None)
|
||||||
|
self.assertCountEqual(found.documents, [rfc])
|
||||||
|
self.assertEqual(found.matched_rev, None)
|
||||||
|
self.assertEqual(found.matched_name, rfc.canonical_name())
|
||||||
|
|
||||||
|
# by draft name, no rev
|
||||||
|
found = fuzzy_find_documents(rfc.name, None)
|
||||||
|
self.assertCountEqual(found.documents, [rfc])
|
||||||
|
self.assertEqual(found.matched_rev, None)
|
||||||
|
self.assertEqual(found.matched_name, rfc.name)
|
||||||
|
|
||||||
|
# by draft name, latest rev
|
||||||
|
found = fuzzy_find_documents(rfc.name, '02')
|
||||||
|
self.assertCountEqual(found.documents, [rfc])
|
||||||
|
self.assertEqual(found.matched_rev, '02')
|
||||||
|
self.assertEqual(found.matched_name, rfc.name)
|
||||||
|
|
||||||
|
# by draft name, earlier rev
|
||||||
|
found = fuzzy_find_documents(rfc.name, '01')
|
||||||
|
self.assertCountEqual(found.documents, [rfc])
|
||||||
|
self.assertEqual(found.matched_rev, '01')
|
||||||
|
self.assertEqual(found.matched_name, rfc.name)
|
||||||
|
|
||||||
|
# wrong name or revision
|
||||||
|
found = fuzzy_find_documents(rfc.name + '-incorrect')
|
||||||
|
self.assertCountEqual(found.documents, [], 'Should not find document that does not match')
|
||||||
|
found = fuzzy_find_documents(rfc.name + '-incorrect', '02')
|
||||||
|
self.assertCountEqual(found.documents, [], 'Still should not find document, even with a version')
|
||||||
|
found = fuzzy_find_documents(rfc.name, '22')
|
||||||
|
self.assertCountEqual(found.documents, [rfc],
|
||||||
|
'Should find document even if rev does not exist')
|
||||||
|
|
||||||
|
|
||||||
|
def test_fuzzy_find_documents(self):
|
||||||
|
# Should add additional tests/test cases for other document types/name formats
|
||||||
|
self.do_fuzzy_find_documents_rfc_test('draft-normal-name')
|
||||||
|
self.do_fuzzy_find_documents_rfc_test('draft-name-with-number-01')
|
||||||
|
self.do_fuzzy_find_documents_rfc_test('draft-name-that-has-two-02-04')
|
||||||
|
self.do_fuzzy_find_documents_rfc_test('draft-wild-01-numbers-0312')
|
||||||
|
|
|
@ -11,7 +11,7 @@ import os
|
||||||
import re
|
import re
|
||||||
import textwrap
|
import textwrap
|
||||||
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict, namedtuple
|
||||||
from urllib.parse import quote
|
from urllib.parse import quote
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
@ -1286,3 +1286,39 @@ def generate_idnits2_rfcs_obsoleted():
|
||||||
obsdict[k] = sorted(obsdict[k])
|
obsdict[k] = sorted(obsdict[k])
|
||||||
return render_to_string('doc/idnits2-rfcs-obsoleted.txt', context={'obsitems':sorted(obsdict.items())})
|
return render_to_string('doc/idnits2-rfcs-obsoleted.txt', context={'obsitems':sorted(obsdict.items())})
|
||||||
|
|
||||||
|
|
||||||
|
def fuzzy_find_documents(name, rev=None):
|
||||||
|
"""Find a document based on name/rev
|
||||||
|
|
||||||
|
Applies heuristics, assuming the inputs were joined by a '-' that may have been misplaced.
|
||||||
|
If returned documents queryset is empty, matched_rev and and matched_name are meaningless.
|
||||||
|
The rev input is not validated - it is used to find possible names if the name input does
|
||||||
|
not match anything, but matched_rev may not correspond to an actual version of the found
|
||||||
|
document.
|
||||||
|
"""
|
||||||
|
# Handle special case name formats
|
||||||
|
if name.startswith('rfc0'):
|
||||||
|
name = "rfc" + name[3:].lstrip('0')
|
||||||
|
if name.startswith('review-') and re.search(r'-\d\d\d\d-\d\d$', name):
|
||||||
|
name = "%s-%s" % (name, rev)
|
||||||
|
rev = None
|
||||||
|
if rev and not name.startswith('charter-') and re.search('[0-9]{1,2}-[0-9]{2}', rev):
|
||||||
|
name = "%s-%s" % (name, rev[:-3])
|
||||||
|
rev = rev[-2:]
|
||||||
|
if re.match("^[0-9]+$", name):
|
||||||
|
name = f'rfc{name}'
|
||||||
|
if re.match("^[Rr][Ff][Cc] [0-9]+$",name):
|
||||||
|
name = f'rfc{name[4:]}'
|
||||||
|
|
||||||
|
# see if we can find a document using this name
|
||||||
|
docs = Document.objects.filter(docalias__name=name, type_id='draft')
|
||||||
|
if rev and not docs.exists():
|
||||||
|
# No document found, see if the name/rev split has been misidentified.
|
||||||
|
# Handles some special cases, like draft-ietf-tsvwg-ieee-802-11.
|
||||||
|
name = '%s-%s' % (name, rev)
|
||||||
|
docs = Document.objects.filter(docalias__name=name, type_id='draft')
|
||||||
|
if docs.exists():
|
||||||
|
rev = None # found a doc by name with rev = None, so update that
|
||||||
|
|
||||||
|
FoundDocuments = namedtuple('FoundDocuments', 'documents matched_name matched_rev')
|
||||||
|
return FoundDocuments(docs, name, rev)
|
||||||
|
|
|
@ -64,7 +64,7 @@ from ietf.doc.utils import (add_links_in_new_revision_events, augment_events_wit
|
||||||
get_initial_notify, make_notify_changed_event, make_rev_history, default_consensus,
|
get_initial_notify, make_notify_changed_event, make_rev_history, default_consensus,
|
||||||
add_events_message_info, get_unicode_document_content, build_doc_meta_block,
|
add_events_message_info, get_unicode_document_content, build_doc_meta_block,
|
||||||
augment_docs_and_user_with_user_info, irsg_needed_ballot_positions, add_action_holder_change_event,
|
augment_docs_and_user_with_user_info, irsg_needed_ballot_positions, add_action_holder_change_event,
|
||||||
build_doc_supermeta_block, build_file_urls, update_documentauthors)
|
build_doc_supermeta_block, build_file_urls, update_documentauthors, fuzzy_find_documents)
|
||||||
from ietf.doc.utils_bofreq import bofreq_editors, bofreq_responsible
|
from ietf.doc.utils_bofreq import bofreq_editors, bofreq_responsible
|
||||||
from ietf.group.models import Role, Group
|
from ietf.group.models import Role, Group
|
||||||
from ietf.group.utils import can_manage_all_groups_of_type, can_manage_materials, group_features_role_filter
|
from ietf.group.utils import can_manage_all_groups_of_type, can_manage_materials, group_features_role_filter
|
||||||
|
@ -721,40 +721,26 @@ def document_main(request, name, rev=None):
|
||||||
|
|
||||||
|
|
||||||
def document_html(request, name, rev=None):
|
def document_html(request, name, rev=None):
|
||||||
if name.startswith('rfc0'):
|
found = fuzzy_find_documents(name, rev)
|
||||||
name = "rfc" + name[3:].lstrip('0')
|
num_found = found.documents.count()
|
||||||
if name.startswith('review-') and re.search(r'-\d\d\d\d-\d\d$', name):
|
if num_found == 0:
|
||||||
name = "%s-%s" % (name, rev)
|
|
||||||
if rev and not name.startswith('charter-') and re.search('[0-9]{1,2}-[0-9]{2}', rev):
|
|
||||||
name = "%s-%s" % (name, rev[:-3])
|
|
||||||
rev = rev[-2:]
|
|
||||||
if re.match("^[0-9]+$", name):
|
|
||||||
return redirect('ietf.doc.views_doc.document_html',name=f'rfc{name}')
|
|
||||||
if re.match("^[Rr][Ff][Cc] [0-9]+$",name):
|
|
||||||
return redirect('ietf.doc.views_doc.document_html',name=f'rfc{name[4:]}')
|
|
||||||
docs = Document.objects.filter(docalias__name=name)
|
|
||||||
if rev and not docs.exists():
|
|
||||||
# handle some special cases, like draft-ietf-tsvwg-ieee-802-11
|
|
||||||
name = '%s-%s' % (name, rev)
|
|
||||||
rev=None
|
|
||||||
docs = Document.objects.filter(docalias__name=name)
|
|
||||||
if not docs.exists():
|
|
||||||
raise Http404("Document not found: %s" % name)
|
raise Http404("Document not found: %s" % name)
|
||||||
if docs.count() > 1:
|
if num_found > 1:
|
||||||
raise Http404("Multiple documents matched: %s" % name)
|
raise Http404("Multiple documents matched: %s" % name)
|
||||||
|
|
||||||
doc = docs.get()
|
if found.matched_name.startswith('rfc') and name != found.matched_name:
|
||||||
|
return redirect('ietf.doc.views_doc.document_html', name=found.matched_name)
|
||||||
|
|
||||||
|
doc = found.documents.get()
|
||||||
if not os.path.exists(doc.get_file_name()):
|
if not os.path.exists(doc.get_file_name()):
|
||||||
raise Http404("File not found: %s" % doc.get_file_name())
|
raise Http404("File not found: %s" % doc.get_file_name())
|
||||||
|
|
||||||
if not rev and not name.startswith('rfc'):
|
if found.matched_rev or found.matched_name.startswith('rfc'):
|
||||||
|
rev = found.matched_rev
|
||||||
|
else:
|
||||||
rev = doc.rev
|
rev = doc.rev
|
||||||
if rev:
|
if rev:
|
||||||
docs = DocHistory.objects.filter(doc=doc, rev=rev)
|
doc = doc.history_set.filter(rev=rev).first() or doc.fake_history_obj(rev)
|
||||||
if docs.exists():
|
|
||||||
doc = docs.first()
|
|
||||||
else:
|
|
||||||
doc = doc.fake_history_obj(rev)
|
|
||||||
if doc.type_id in ['draft',]:
|
if doc.type_id in ['draft',]:
|
||||||
doc.supermeta = build_doc_supermeta_block(doc)
|
doc.supermeta = build_doc_supermeta_block(doc)
|
||||||
doc.meta = build_doc_meta_block(doc, settings.HTMLIZER_URL_PREFIX)
|
doc.meta = build_doc_meta_block(doc, settings.HTMLIZER_URL_PREFIX)
|
||||||
|
@ -1794,44 +1780,30 @@ def idnits2_state(request, name, rev=None):
|
||||||
return render(request, 'doc/idnits2-state.txt', context={'doc':doc}, content_type='text/plain;charset=utf-8')
|
return render(request, 'doc/idnits2-state.txt', context={'doc':doc}, content_type='text/plain;charset=utf-8')
|
||||||
|
|
||||||
def find_doc_for_rfcdiff(name, rev):
|
def find_doc_for_rfcdiff(name, rev):
|
||||||
if name.startswith('rfc0'):
|
"""rfcdiff lookup heuristics
|
||||||
name = "rfc" + name[3:].lstrip('0')
|
|
||||||
if name.startswith('review-') and re.search(r'-\d\d\d\d-\d\d$', name):
|
|
||||||
name = "%s-%s" % (name, rev)
|
|
||||||
if rev and not name.startswith('charter-') and re.search('[0-9]{1,2}-[0-9]{2}', rev):
|
|
||||||
name = "%s-%s" % (name, rev[:-3])
|
|
||||||
rev = rev[-2:]
|
|
||||||
if re.match("^[0-9]+$", name):
|
|
||||||
name = f'rfc{name}'
|
|
||||||
if re.match("^[Rr][Ff][Cc] [0-9]+$",name):
|
|
||||||
name = f'rfc{name[4:]}'
|
|
||||||
|
|
||||||
docs = Document.objects.filter(docalias__name=name, type_id='draft')
|
|
||||||
if rev and not docs.exists():
|
|
||||||
# handle some special cases, like draft-ietf-tsvwg-ieee-802-11
|
|
||||||
name = '%s-%s' % (name, rev)
|
|
||||||
rev=None
|
|
||||||
docs = Document.objects.filter(docalias__name=name, type_id='draft')
|
|
||||||
|
|
||||||
|
Returns a tuple with:
|
||||||
|
[0] - condition string
|
||||||
|
[1] - document found (or None)
|
||||||
|
[2] - historic version
|
||||||
|
[3] - revision actually found (may differ from :rev: input)
|
||||||
|
"""
|
||||||
|
found = fuzzy_find_documents(name, rev)
|
||||||
condition = 'no such document'
|
condition = 'no such document'
|
||||||
if not docs.exists() or docs.count() > 1:
|
if found.documents.count() != 1:
|
||||||
return (condition, None, None)
|
return (condition, None, None, rev)
|
||||||
doc = docs.get()
|
doc = found.documents.get()
|
||||||
if not rev or (rev and doc.rev==rev):
|
if found.matched_rev is None or doc.rev == found.matched_rev:
|
||||||
condition = 'current version'
|
condition = 'current version'
|
||||||
return condition, doc, None
|
return (condition, doc, None, found.matched_rev)
|
||||||
else:
|
else:
|
||||||
candidate = None
|
candidate = doc.history_set.filter(rev=found.matched_rev).order_by("-time").first()
|
||||||
for h in doc.history_set.order_by("-time"):
|
|
||||||
if rev == h.rev:
|
|
||||||
candidate = h
|
|
||||||
break
|
|
||||||
if candidate:
|
if candidate:
|
||||||
condition = 'historic version'
|
condition = 'historic version'
|
||||||
return condition, doc, candidate
|
return (condition, doc, candidate, found.matched_rev)
|
||||||
else:
|
else:
|
||||||
condition = 'version dochistory not found'
|
condition = 'version dochistory not found'
|
||||||
return condition, doc, None
|
return (condition, doc, None, found.matched_rev)
|
||||||
|
|
||||||
# This is a proof of concept of a service that would redirect to the current revision
|
# This is a proof of concept of a service that would redirect to the current revision
|
||||||
# def rfcdiff_latest(request, name, rev=None):
|
# def rfcdiff_latest(request, name, rev=None):
|
||||||
|
@ -1886,17 +1858,20 @@ HAS_TOMBSTONE = [
|
||||||
|
|
||||||
def rfcdiff_latest_json(request, name, rev=None):
|
def rfcdiff_latest_json(request, name, rev=None):
|
||||||
response = dict()
|
response = dict()
|
||||||
condition, document, history = find_doc_for_rfcdiff(name, rev)
|
condition, document, history, found_rev = find_doc_for_rfcdiff(name, rev)
|
||||||
|
|
||||||
if condition == 'no such document':
|
if condition == 'no such document':
|
||||||
raise Http404
|
raise Http404
|
||||||
elif condition in ('historic version', 'current version'):
|
elif condition in ('historic version', 'current version'):
|
||||||
doc = history if history else document
|
doc = history if history else document
|
||||||
if not rev and doc.is_rfc():
|
if not found_rev and doc.is_rfc():
|
||||||
response['content_url'] = doc.get_href()
|
response['content_url'] = doc.get_href()
|
||||||
response['name']=doc.canonical_name()
|
response['name']=doc.canonical_name()
|
||||||
if doc.name != doc.canonical_name():
|
if doc.name != doc.canonical_name():
|
||||||
prev_rev = doc.rev
|
prev_rev = doc.rev
|
||||||
|
# not sure what to do if non-numeric values come back, so at least log it
|
||||||
|
log.assertion('doc.rfc_number().isdigit()')
|
||||||
|
log.assertion('doc.rev.isdigit()')
|
||||||
if int(doc.rfc_number()) in HAS_TOMBSTONE and prev_rev != '00':
|
if int(doc.rfc_number()) in HAS_TOMBSTONE and prev_rev != '00':
|
||||||
prev_rev = f'{(int(doc.rev)-1):02d}'
|
prev_rev = f'{(int(doc.rev)-1):02d}'
|
||||||
response['previous'] = f'{doc.name}-{prev_rev}'
|
response['previous'] = f'{doc.name}-{prev_rev}'
|
||||||
|
@ -1915,16 +1890,20 @@ def rfcdiff_latest_json(request, name, rev=None):
|
||||||
if match and match.group(2):
|
if match and match.group(2):
|
||||||
response['previous'] = f'rfc{match.group(2)}'
|
response['previous'] = f'rfc{match.group(2)}'
|
||||||
else:
|
else:
|
||||||
|
# not sure what to do if non-numeric values come back, so at least log it
|
||||||
|
log.assertion('doc.rev.isdigit()')
|
||||||
response['previous'] = f'{doc.name}-{(int(doc.rev)-1):02d}'
|
response['previous'] = f'{doc.name}-{(int(doc.rev)-1):02d}'
|
||||||
elif condition == 'version dochistory not found':
|
elif condition == 'version dochistory not found':
|
||||||
response['warning'] = 'History for this version not found - these results are speculation'
|
response['warning'] = 'History for this version not found - these results are speculation'
|
||||||
response['name'] = document.name
|
response['name'] = document.name
|
||||||
response['rev'] = rev
|
response['rev'] = found_rev
|
||||||
document.rev = rev
|
document.rev = found_rev
|
||||||
document.is_rfc = lambda: False
|
document.is_rfc = lambda: False
|
||||||
response['content_url'] = document.get_href()
|
response['content_url'] = document.get_href()
|
||||||
if int(rev)>0:
|
# not sure what to do if non-numeric values come back, so at least log it
|
||||||
response['previous'] = f'{document.name}-{(int(rev)-1):02d}'
|
log.assertion('found_rev.isdigit()')
|
||||||
|
if int(found_rev) > 0:
|
||||||
|
response['previous'] = f'{document.name}-{(int(found_rev)-1):02d}'
|
||||||
else:
|
else:
|
||||||
match = re.search("-(rfc)?([0-9][0-9][0-9]+)bis(-.*)?$", name)
|
match = re.search("-(rfc)?([0-9][0-9][0-9]+)bis(-.*)?$", name)
|
||||||
if match and match.group(2):
|
if match and match.group(2):
|
||||||
|
|
Loading…
Reference in a new issue