Skip to content

Commit 1eb4636

Browse files
committed
Provide a per-document status file in the format expected by idnits2. Rework the rfc status blob and obsoleted file to manage the long generation times. Commit ready for merge.
- Legacy-Id: 19265
1 parent d283b96 commit 1eb4636

9 files changed

Lines changed: 188 additions & 46 deletions

File tree

bin/hourly

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ mv $TMPFILE9 $DERIVED/1id-index.txt
8585
mv $TMPFILEA $DERIVED/1id-abstracts.txt
8686
mv $TMPFILEB $DERIVED/all_id2.txt
8787

88+
$DTDIR/ietf/manage.py generate_idnits2_rfc_status
89+
$DTDIR/ietf/manage.py generate_idnits2_rfcs_obsoleted
8890

8991
CHARTER=/a/www/ietf-ftp/charter
9092
wget -q https://datatracker.ietf.org/wg/1wg-charters-by-acronym.txt -O $CHARTER/1wg-charters-by-acronym.txt
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Copyright The IETF Trust 2021 All Rights Reserved
2+
3+
import os
4+
5+
from django.conf import settings
6+
from django.core.management.base import BaseCommand
7+
8+
from ietf.doc.utils import generate_idnits2_rfc_status
9+
from ietf.utils.log import log
10+
11+
class Command(BaseCommand):
12+
help = ('Generate the rfc_status blob used by idnits2')
13+
14+
def handle(self, *args, **options):
15+
filename=os.path.join(settings.DERIVED_DIR,'idnits2-rfc-status')
16+
blob = generate_idnits2_rfc_status()
17+
try:
18+
bytes = blob.encode('utf-8')
19+
with open(filename,'wb') as f:
20+
f.write(bytes)
21+
except Exception as e:
22+
log('failed to write idnits2-rfc-status: '+str(e))
23+
raise e
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Copyright The IETF Trust 2021 All Rights Reserved
2+
3+
import os
4+
5+
from django.conf import settings
6+
from django.core.management.base import BaseCommand
7+
8+
from ietf.doc.utils import generate_idnits2_rfcs_obsoleted
9+
from ietf.utils.log import log
10+
11+
class Command(BaseCommand):
12+
help = ('Generate the rfcs-obsoleted file used by idnits2')
13+
14+
def handle(self, *args, **options):
15+
filename=os.path.join(settings.DERIVED_DIR,'idnits2-rfcs-obsoleted')
16+
blob = generate_idnits2_rfcs_obsoleted()
17+
try:
18+
bytes = blob.encode('utf-8')
19+
with open(filename,'wb') as f:
20+
f.write(bytes)
21+
except Exception as e:
22+
log('failed to write idnits2-rfcs-obsoleted: '+str(e))
23+
raise e

ietf/doc/tests.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2380,20 +2380,58 @@ def test_markdown_and_text(self):
23802380

23812381
class Idnits2SupportTests(TestCase):
23822382

2383+
def setUp(self):
2384+
self.derived_dir = self.tempdir('derived')
2385+
self.saved_derived_dir = settings.DERIVED_DIR
2386+
settings.DERIVED_DIR = self.derived_dir
2387+
2388+
def tearDown(self):
2389+
settings.DERIVED_DIR = self.saved_derived_dir
2390+
shutil.rmtree(self.derived_dir)
2391+
23832392
def test_obsoleted(self):
23842393
rfc = WgRfcFactory(alias2__name='rfc1001')
23852394
WgRfcFactory(alias2__name='rfc1003',relations=[('obs',rfc)])
23862395
rfc = WgRfcFactory(alias2__name='rfc1005')
23872396
WgRfcFactory(alias2__name='rfc1007',relations=[('obs',rfc)])
23882397

2398+
url = urlreverse('ietf.doc.views_doc.idnits2_rfcs_obsoleted')
2399+
r = self.client.get(url)
2400+
self.assertEqual(r.status_code, 404)
2401+
call_command('generate_idnits2_rfcs_obsoleted')
23892402
url = urlreverse('ietf.doc.views_doc.idnits2_rfcs_obsoleted')
23902403
r = self.client.get(url)
23912404
self.assertEqual(r.status_code, 200)
23922405
self.assertEqual(r.content, b'1001 1003\n1005 1007\n')
23932406

23942407
def test_rfc_status(self):
2408+
for slug in ('bcp', 'ds', 'exp', 'hist', 'inf', 'std', 'ps', 'unkn'):
2409+
WgRfcFactory(std_level_id=slug)
23952410
url = urlreverse('ietf.doc.views_doc.idnits2_rfc_status')
23962411
r = self.client.get(url)
2412+
self.assertEqual(r.status_code,404)
2413+
call_command('generate_idnits2_rfc_status')
2414+
r = self.client.get(url)
23972415
self.assertEqual(r.status_code,200)
23982416
blob = unicontent(r).replace('\n','')
23992417
self.assertEqual(blob[6312-1],'O')
2418+
2419+
def test_idnits2_state(self):
2420+
rfc = WgRfcFactory()
2421+
url = urlreverse('ietf.doc.views_doc.idnits2_state', kwargs=dict(name=rfc.canonical_name()))
2422+
r = self.client.get(url)
2423+
self.assertEqual(r.status_code, 200)
2424+
self.assertContains(r,'rfcnum')
2425+
2426+
draft = WgDraftFactory()
2427+
url = urlreverse('ietf.doc.views_doc.idnits2_state', kwargs=dict(name=draft.canonical_name()))
2428+
r = self.client.get(url)
2429+
self.assertEqual(r.status_code, 200)
2430+
self.assertNotContains(r,'rfcnum')
2431+
self.assertContains(r,'Unknown')
2432+
2433+
draft = WgDraftFactory(intended_std_level_id='ps')
2434+
url = urlreverse('ietf.doc.views_doc.idnits2_state', kwargs=dict(name=draft.canonical_name()))
2435+
r = self.client.get(url)
2436+
self.assertEqual(r.status_code, 200)
2437+
self.assertContains(r,'Proposed')

ietf/doc/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080

8181
url(r'^%(name)s(?:/%(rev)s)?/$' % settings.URL_REGEXPS, views_doc.document_main),
8282
url(r'^%(name)s(?:/%(rev)s)?/bibtex/$' % settings.URL_REGEXPS, views_doc.document_bibtex),
83+
url(r'^%(name)s(?:/%(rev)s)?/idnits2-state/$' % settings.URL_REGEXPS, views_doc.idnits2_state),
8384
url(r'^bibxml3/reference.I-D.%(name)s(?:-%(rev)s)?.xml$' % settings.URL_REGEXPS, views_doc.document_bibxml_ref),
8485
url(r'^bibxml3/%(name)s(?:-%(rev)s)?.xml$' % settings.URL_REGEXPS, views_doc.document_bibxml),
8586
url(r'^%(name)s/history/$' % settings.URL_REGEXPS, views_doc.document_history),

ietf/doc/utils.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,19 @@
99
import math
1010
import os
1111
import re
12+
import textwrap
1213

1314
from collections import defaultdict
1415
from urllib.parse import quote
1516

1617
from django.conf import settings
1718
from django.contrib import messages
1819
from django.forms import ValidationError
20+
from django.template.loader import render_to_string
1921
from django.utils.html import escape
2022
from django.urls import reverse as urlreverse
2123

24+
2225
import debug # pyflakes:ignore
2326
from ietf.community.models import CommunityList
2427
from ietf.community.utils import docs_tracked_by_community_list
@@ -1232,3 +1235,54 @@ def update_doc_extresources(doc, new_resources, by):
12321235
e.save()
12331236
doc.save_with_history([e])
12341237
return True
1238+
1239+
def generate_idnits2_rfc_status():
1240+
1241+
blob=['N']*10000
1242+
1243+
symbols={
1244+
'ps': 'P',
1245+
'inf': 'I',
1246+
'exp': 'E',
1247+
'ds': 'D',
1248+
'hist': 'H',
1249+
'std': 'S',
1250+
'bcp': 'B',
1251+
'unkn': 'U',
1252+
}
1253+
1254+
rfcs = Document.objects.filter(type_id='draft',states__slug='rfc',states__type='draft')
1255+
for rfc in rfcs:
1256+
offset = int(rfc.rfcnum)-1
1257+
blob[offset] = symbols[rfc.std_level_id]
1258+
if rfc.related_that('obs'):
1259+
blob[offset] = 'O'
1260+
1261+
# Workarounds for unusual states in the datatracker
1262+
1263+
# Document.get(docalias='rfc6312').rfcnum == 6342
1264+
# 6312 was published with the wrong rfc number in it
1265+
# weird workaround in the datatracker - there are two
1266+
# DocAliases starting with rfc - the canonical name code
1267+
# searches for the lexically highest alias starting with rfc
1268+
# which is getting lucky.
1269+
blob[6312 - 1] = 'O'
1270+
1271+
# RFC200 is an old RFC List by Number
1272+
blob[200 -1] = 'O'
1273+
1274+
# End Workarounds
1275+
1276+
blob = re.sub('N*$','',''.join(blob))
1277+
blob = textwrap.fill(blob, width=64)
1278+
1279+
return blob
1280+
1281+
def generate_idnits2_rfcs_obsoleted():
1282+
obsdict = defaultdict(list)
1283+
for r in RelatedDocument.objects.filter(relationship_id='obs'):
1284+
obsdict[int(r.target.document.rfc_number())].append(int(r.source.rfc_number()))
1285+
for k in obsdict:
1286+
obsdict[k] = sorted(obsdict[k])
1287+
return render_to_string('doc/idnits2-rfcs-obsoleted.txt', context={'obsitems':sorted(obsdict.items())})
1288+

ietf/doc/views_doc.py

Lines changed: 40 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,7 @@
4141
import os
4242
import re
4343
import markdown
44-
import textwrap
4544

46-
from collections import defaultdict
4745
from urllib.parse import quote
4846

4947
from django.http import HttpResponse, Http404
@@ -52,14 +50,13 @@
5250
from django.urls import reverse as urlreverse
5351
from django.conf import settings
5452
from django import forms
55-
from django.views.decorators.cache import cache_page
5653

5754

5855
import debug # pyflakes:ignore
5956

6057
from ietf.doc.models import ( Document, DocAlias, DocHistory, DocEvent, BallotDocEvent, BallotType,
6158
ConsensusDocEvent, NewRevisionDocEvent, TelechatDocEvent, WriteupDocEvent, IanaExpertDocEvent,
62-
IESG_BALLOT_ACTIVE_STATES, STATUSCHANGE_RELATIONS, DocumentActionHolder, DocumentAuthor, RelatedDocument)
59+
IESG_BALLOT_ACTIVE_STATES, STATUSCHANGE_RELATIONS, DocumentActionHolder, DocumentAuthor)
6360
from ietf.doc.utils import (add_links_in_new_revision_events, augment_events_with_revision,
6461
can_adopt_draft, can_unadopt_draft, get_chartering_type, get_tags_for_stream_id,
6562
needed_ballot_positions, nice_consensus, prettify_std_name, update_telechat, has_same_ballot,
@@ -83,6 +80,7 @@
8380
from ietf.review.utils import can_request_review_of_doc, review_assignments_to_list_for_docs
8481
from ietf.review.utils import no_review_from_teams_on_doc
8582
from ietf.utils import markup_txt, log
83+
from ietf.utils.draft import Draft
8684
from ietf.utils.response import permission_denied
8785
from ietf.utils.text import maybe_split
8886

@@ -1719,57 +1717,53 @@ def all_presentations(request, name):
17191717
'past' : past+recent,
17201718
})
17211719

1722-
@cache_page ( 60 * 60, cache="slowpages" )
1723-
def idnits2_rfcs_obsoleted(request):
1724-
1725-
obsdict = defaultdict(list)
1726-
for r in RelatedDocument.objects.filter(relationship_id='obs'):
1727-
obsdict[int(r.target.document.rfc_number())].append(int(r.source.rfc_number()))
17281720

1729-
for k in obsdict:
1730-
obsdict[k] = sorted(obsdict[k])
1721+
def idnits2_rfcs_obsoleted(request):
1722+
filename = os.path.join(settings.DERIVED_DIR,'idnits2-rfcs-obsoleted')
1723+
try:
1724+
with open(filename,'rb') as f:
1725+
blob = f.read()
1726+
return HttpResponse(blob,content_type='text/plain;charset=utf-8')
1727+
except Exception as e:
1728+
log.log('Failed to read idnits2-rfcs-obsoleted:'+str(e))
1729+
raise Http404
17311730

1732-
return render(request, 'doc/idnits2-rfcs-obsoleted.txt', context={'obsitems':sorted(obsdict.items())},content_type='text/plain;charset=utf-8')
17331731

1734-
@cache_page ( 60 * 60, cache="slowpages" )
17351732
def idnits2_rfc_status(request):
1733+
filename = os.path.join(settings.DERIVED_DIR,'idnits2-rfc-status')
1734+
try:
1735+
with open(filename,'rb') as f:
1736+
blob = f.read()
1737+
return HttpResponse(blob,content_type='text/plain;charset=utf-8')
1738+
except Exception as e:
1739+
log.log('Failed to read idnits2-rfc-status:'+str(e))
1740+
raise Http404
17361741

1737-
blob=['N']*10000
1738-
1739-
symbols={
1740-
'ps': 'P',
1741-
'inf': 'I',
1742-
'exp': 'E',
1743-
'ds': 'D',
1744-
'hist': 'H',
1745-
'std': 'S',
1746-
'bcp': 'B',
1747-
'unkn': 'U',
1748-
}
1749-
1750-
rfcs = Document.objects.filter(type_id='draft',states__slug='rfc',states__type='draft')
1751-
for rfc in rfcs:
1752-
offset = int(rfc.rfcnum)-1
1753-
blob[offset] = symbols[rfc.std_level_id]
1754-
if rfc.related_that('obs'):
1755-
blob[offset] = 'O'
17561742

1757-
# Workarounds for unusual states in the datatracker
1743+
def idnits2_state(request, name, rev=None):
1744+
doc = get_object_or_404(Document, docalias__name=name)
1745+
if doc.type_id!='draft':
1746+
raise Http404
1747+
zero_revision = NewRevisionDocEvent.objects.filter(doc=doc,rev='00').first()
1748+
if zero_revision:
1749+
doc.created = zero_revision.time
1750+
else:
1751+
doc.created = doc.docevent_set.order_by('-time').first().time
1752+
if doc.std_level:
1753+
doc.deststatus = doc.std_level.name
1754+
elif doc.intended_std_level:
1755+
doc.deststatus = doc.intended_std_level.name
1756+
else:
1757+
text = doc.text()
1758+
if text:
1759+
parsed_draft = Draft(text=doc.text(), source=name, name_from_source=False)
1760+
doc.deststatus = parsed_draft.get_status()
1761+
else:
1762+
doc.deststatus="Unknown"
1763+
return render(request, 'doc/idnits2-state.txt', context={'doc':doc}, content_type='text/plain;charset=utf-8')
17581764

1759-
# Document.get(docalias='rfc6312').rfcnum == 6342
1760-
# 6312 was published with the wrong rfc number in it
1761-
# weird workaround in the datatracker - there are two
1762-
# DocAliases starting with rfc - the canonical name code
1763-
# searches for the lexically highest alias starting with rfc
1764-
# which is getting lucky.
1765-
blob[6312 - 1] = 'O'
17661765

1767-
# RFC200 is an old RFC List by Number
1768-
blob[200 -1] = 'O'
17691766

1770-
# End Workarounds
17711767

1772-
blob = re.sub('N*$','',''.join(blob))
17731768

1774-
return HttpResponse(textwrap.fill(blob, width=64),content_type='text/plain;charset=utf-8')
17751769

ietf/settings.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -682,6 +682,7 @@ def skip_unreadable_post(record):
682682
# write anything to this directory -- its content is maintained by ghostlinkd:
683683
INTERNET_ALL_DRAFTS_ARCHIVE_DIR = '/a/ietfdata/doc/draft/archive'
684684
MEETING_RECORDINGS_DIR = '/a/www/audio'
685+
DERIVED_DIR = '/a/ietfdata/derived'
685686

686687
DOCUMENT_FORMAT_WHITELIST = ["txt", "ps", "pdf", "xml", "html", ]
687688

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{% load ietf_filters %}{% filter linebreaks_lf %}{% comment %}
2+
{% endcomment %}Doc-created: {{doc.created|date:"Y-m-d"}};datatracker
3+
Doc-deststatus: {{doc.deststatus}};datatracker
4+
Doc-rev: {{doc.rev}};datatracker{% if doc.rfcnum %}
5+
Doc-rfcnum: {{doc.rfcnum}};datatracker{%endif%}
6+
{% endfilter %}

0 commit comments

Comments
 (0)