@@ -350,6 +350,71 @@ def get_bcp_text_index_entries(): | |||
| 350 | 350 | return entries | |
| 351 | 351 | ||
| 352 | 352 | ||
| 353 | + def get_std_text_index_entries(): | ||
| 354 | + """Returns STD entries for std-index.txt""" | ||
| 355 | + entries = [] | ||
| 356 | + | ||
| 357 | + highest_std_number = ( | ||
| 358 | + Document.objects.filter(type_id="std") | ||
| 359 | + .annotate( | ||
| 360 | + number=Cast( | ||
| 361 | + Substr("name", 4, None), | ||
| 362 | + output_field=models.IntegerField(), | ||
| 363 | + ) | ||
| 364 | + ) | ||
| 365 | + .order_by("-number") | ||
| 366 | + .first() | ||
| 367 | + .number | ||
| 368 | + ) | ||
| 369 | + | ||
| 370 | + for std_number in range(1, highest_std_number + 1): | ||
| 371 | + std_name = f"STD{std_number}" | ||
| 372 | + std = Document.objects.filter(type_id="std", name=f"{std_name.lower()}").first() | ||
| 373 | + | ||
| 374 | + if std and std.contains(): | ||
| 375 | + entry = subseries_text_line( | ||
| 376 | + ( | ||
| 377 | + f"[{std_name}]" | ||
| 378 | + f"{' ' * (SS_TXT_CUE_COL_WIDTH - len(std_name) - 2 - SS_TXT_MARGIN)}" | ||
| 379 | + f"Internet Standard {std_number}," | ||
| 380 | + ), | ||
| 381 | + first=True, | ||
| 382 | + ) | ||
| 383 | + entry += "\n" | ||
| 384 | + entry += subseries_text_line( | ||
| 385 | + f"<{settings.RFC_EDITOR_INFO_BASE_URL}{std_name.lower()}>." | ||
| 386 | + ) | ||
| 387 | + entry += "\n" | ||
| 388 | + entry += subseries_text_line( | ||
| 389 | + "At the time of writing, this STD comprises the following:" | ||
| 390 | + ) | ||
| 391 | + entry += "\n\n" | ||
| 392 | + rfcs = sorted(std.contains(), key=lambda x: x.rfc_number) | ||
| 393 | + for rfc in rfcs: | ||
| 394 | + authors = ", ".join( | ||
| 395 | + author.format_for_titlepage() for author in rfc.rfcauthor_set.all() | ||
| 396 | + ) | ||
| 397 | + entry += subseries_text_line( | ||
| 398 | + ( | ||
| 399 | + f'{authors}, "{rfc.title}", STD¶{std_number}, RFC¶{rfc.rfc_number}, ' | ||
| 400 | + f"DOI¶{rfc.doi}, {rfc.pub_date().strftime('%B %Y')}, " | ||
| 401 | + f"<{settings.RFC_EDITOR_INFO_BASE_URL}rfc{rfc.rfc_number}>." | ||
| 402 | + ) | ||
| 403 | + ).replace("¶", " ") | ||
| 404 | + entry += "\n\n" | ||
| 405 | + else: | ||
| 406 | + entry = subseries_text_line( | ||
| 407 | + ( | ||
| 408 | + f"[{std_name}]" | ||
| 409 | + f"{' ' * (SS_TXT_CUE_COL_WIDTH - len(std_name) - 2 - SS_TXT_MARGIN)}" | ||
| 410 | + f"Internet Standard {std_number} currently contains no RFCs" | ||
| 411 | + ), | ||
| 412 | + first=True, | ||
| 413 | + ) | ||
| 414 | + entries.append(entry) | ||
| 415 | + return entries | ||
| 416 | + | ||
| 417 | + | ||
| 353 | 418 | def add_subseries_xml_index_entries(rfc_index, ss_type, include_all=False): | |
| 354 | 419 | """Add subseries entries for rfc-index.xml""" | |
| 355 | 420 | # subseries docs annotated with numeric number | |
@@ -579,3 +644,18 @@ def create_bcp_txt_index(): | |||
| 579 | 644 | }, | |
| 580 | 645 | ) | |
| 581 | 646 | save_to_red_bucket("bcp-index.txt", index) | |
| 647 | + | ||
| 648 | + | ||
| 649 | + def create_std_txt_index(): | ||
| 650 | + """Create text index of STDs""" | ||
| 651 | + DATE_FMT = "%m/%d/%Y" | ||
| 652 | + created_on = timezone.now().strftime(DATE_FMT) | ||
| 653 | + log("Creating std-index.txt") | ||
| 654 | + index = render_to_string( | ||
| 655 | + "sync/std-index.txt", | ||
| 656 | + { | ||
| 657 | + "created_on": created_on, | ||
| 658 | + "stds": get_std_text_index_entries(), | ||
| 659 | + }, | ||
| 660 | + ) | ||
| 661 | + save_to_red_bucket("std-index.txt", index) | ||
@@ -9,6 +9,7 @@ | |||
| 9 | 9 | ||
| 10 | 10 | from ietf.doc.factories import ( | |
| 11 | 11 | BcpFactory, | |
| 12 | + StdFactory, | ||
| 12 | 13 | IndividualRfcFactory, | |
| 13 | 14 | PublishedRfcDocEventFactory, | |
| 14 | 15 | ) | |
@@ -17,6 +18,7 @@ | |||
| 17 | 18 | create_bcp_txt_index, | |
| 18 | 19 | create_rfc_txt_index, | |
| 19 | 20 | create_rfc_xml_index, | |
| 21 | + create_std_txt_index, | ||
| 20 | 22 | format_rfc_number, | |
| 21 | 23 | get_april1_rfc_numbers, | |
| 22 | 24 | get_publication_std_levels, | |
@@ -78,6 +80,9 @@ def setUp(self): | |||
| 78 | 80 | # Create a BCP with non-April Fools RFC | |
| 79 | 81 | self.bcp = BcpFactory(contains=[self.rfc], name="bcp11") | |
| 80 | 82 | ||
| 83 | + # Create a STD with non-April Fools RFC | ||
| 84 | + self.std = StdFactory(contains=[self.rfc], name="std11") | ||
| 85 | + | ||
| 81 | 86 | # Set up a publication-std-levels.json file to indicate the publication | |
| 82 | 87 | # standard of self.rfc as different from its current value | |
| 83 | 88 | red_bucket.save( | |
@@ -146,7 +151,7 @@ def test_create_rfc_xml_index(self, mock_save): | |||
| 146 | 151 | ||
| 147 | 152 | children = list(index) # elements as list | |
| 148 | 153 | # Should be one rfc-not-issued-entry | |
| 149 | - self.assertEqual(len(children), 14) | ||
| 154 | + self.assertEqual(len(children), 15) | ||
| 150 | 155 | self.assertEqual( | |
| 151 | 156 | [ | |
| 152 | 157 | c.find(f"{ns}doc-id").text | |
@@ -236,7 +241,62 @@ def test_create_bcp_txt_index(self, mock_save): | |||
| 236 | 241 | contents, | |
| 237 | 242 | ) | |
| 238 | 243 | self.assertIn( | |
| 239 | - f'BCP 11, RFC {self.rfc.rfc_number},', | ||
| 244 | + "BCP 11,", | ||
| 245 | + contents, | ||
| 246 | + ) | ||
| 247 | + self.assertIn( | ||
| 248 | + f"RFC {self.rfc.rfc_number},", | ||
| 249 | + contents, | ||
| 250 | + ) | ||
| 251 | + | ||
| 252 | + @override_settings(RFCINDEX_INPUT_PATH="input/") | ||
| 253 | + @mock.patch("ietf.sync.rfcindex.save_to_red_bucket") | ||
| 254 | + def test_create_std_txt_index(self, mock_save): | ||
| 255 | + create_std_txt_index() | ||
| 256 | + self.assertEqual(mock_save.call_count, 1) | ||
| 257 | + self.assertEqual(mock_save.call_args[0][0], "std-index.txt") | ||
| 258 | + contents = mock_save.call_args[0][1] | ||
| 259 | + self.assertTrue(isinstance(contents, str)) | ||
| 260 | + # starts from 1 | ||
| 261 | + self.assertIn( | ||
| 262 | + "[STD1]", | ||
| 263 | + contents, | ||
| 264 | + ) | ||
| 265 | + # fill up to 11 | ||
| 266 | + self.assertIn( | ||
| 267 | + "[STD10]", | ||
| 268 | + contents, | ||
| 269 | + ) | ||
| 270 | + # but not to 12 | ||
| 271 | + self.assertNotIn( | ||
| 272 | + "[STD12]", | ||
| 273 | + contents, | ||
| 274 | + ) | ||
| 275 | + # Test empty STDs | ||
| 276 | + self.assertIn( | ||
| 277 | + "Internet Standard 9 currently contains no RFCs", | ||
| 278 | + contents, | ||
| 279 | + ) | ||
| 280 | + # No zero prefix! | ||
| 281 | + self.assertNotIn( | ||
| 282 | + "[STD0001]", | ||
| 283 | + contents, | ||
| 284 | + ) | ||
| 285 | + # Has STD11 with a RFC | ||
| 286 | + self.assertIn( | ||
| 287 | + "Internet Standard 11,", | ||
| 288 | + contents, | ||
| 289 | + ) | ||
| 290 | + self.assertIn( | ||
| 291 | + f'"{self.rfc.title}"', | ||
| 292 | + contents, | ||
| 293 | + ) | ||
| 294 | + self.assertIn( | ||
| 295 | + "STD 11,", | ||
| 296 | + contents, | ||
| 297 | + ) | ||
| 298 | + self.assertIn( | ||
| 299 | + f"RFC {self.rfc.rfc_number},", | ||
| 240 | 300 | contents, | |
| 241 | 301 | ) | |
| 242 | 302 | ||
@@ -0,0 +1,51 @@ | |||
| 1 | + | ||
| 2 | + | ||
| 3 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 4 | + | ||
| 5 | + STD INDEX | ||
| 6 | + ------------- | ||
| 7 | + | ||
| 8 | + (CREATED ON: {{created_on}}.) | ||
| 9 | + | ||
| 10 | + This file contains citations for all STDs in numeric order. Each | ||
| 11 | + STD represents a single Internet Standard technical specification, | ||
| 12 | + composed of one or more RFCs with Internet Standard status. | ||
| 13 | + | ||
| 14 | + STD citations appear in this format: | ||
| 15 | + | ||
| 16 | + [STD#] Best Current Practice #, | ||
| 17 | + <STD URL>. | ||
| 18 | + At the time of writing, this STD comprises the following: | ||
| 19 | + | ||
| 20 | + Author 1, Author 2, "Title of the RFC", STD #, RFC №, | ||
| 21 | + DOI DOI string, Issue date, | ||
| 22 | + <RFC URL>. | ||
| 23 | + | ||
| 24 | + For example: | ||
| 25 | + | ||
| 26 | + [STD6] Internet Standard 6, | ||
| 27 | + <https://www.rfc-editor.org/info/std6>. | ||
| 28 | + At the time of writing, this STD comprises the following: | ||
| 29 | + | ||
| 30 | + J. Postel, "User Datagram Protocol", STD 6, RFC 768, | ||
| 31 | + DOI 10.17487/RFC0768, August 1980, | ||
| 32 | + <https://www.rfc-editor.org/info/rfc768>. | ||
| 33 | + | ||
| 34 | + Key to fields: | ||
| 35 | + | ||
| 36 | + # is the STD number. | ||
| 37 | + | ||
| 38 | + № is the RFC number. | ||
| 39 | + | ||
| 40 | + STDs and other RFCs may be obtained from https://www.rfc-editor.org. | ||
| 41 | + | ||
| 42 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 43 | + | ||
| 44 | + STD INDEX | ||
| 45 | + --------- | ||
| 46 | + | ||
| 47 | + | ||
| 48 | + | ||
| 49 | + {% for std in stds %}{{std|safe}} | ||
| 50 | + | ||
| 51 | + {% endfor %} | ||
0 commit comments