ietfdata 0.7.0__tar.gz → 0.7.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. {ietfdata-0.7.0 → ietfdata-0.7.2}/PKG-INFO +1 -1
  2. ietfdata-0.7.2/examples/chatlogs.py +54 -0
  3. ietfdata-0.7.2/examples/emails-ietf-community.py +105 -0
  4. ietfdata-0.7.2/examples/rfc-per-year-ietf.py +62 -0
  5. {ietfdata-0.7.0 → ietfdata-0.7.2}/ietfdata/datatracker.py +8 -6
  6. {ietfdata-0.7.0 → ietfdata-0.7.2}/ietfdata/mailarchive2.py +46 -43
  7. {ietfdata-0.7.0 → ietfdata-0.7.2}/ietfdata/rfcindex.py +52 -51
  8. {ietfdata-0.7.0 → ietfdata-0.7.2}/ietfdata.egg-info/PKG-INFO +1 -1
  9. {ietfdata-0.7.0 → ietfdata-0.7.2}/ietfdata.egg-info/SOURCES.txt +3 -0
  10. {ietfdata-0.7.0 → ietfdata-0.7.2}/pyproject.toml +1 -1
  11. {ietfdata-0.7.0 → ietfdata-0.7.2}/setup.py +1 -1
  12. {ietfdata-0.7.0 → ietfdata-0.7.2}/tests/test_datatracker.py +57 -32
  13. {ietfdata-0.7.0 → ietfdata-0.7.2}/tests/test_rfcindex.py +1 -1
  14. {ietfdata-0.7.0 → ietfdata-0.7.2}/LICENSE +0 -0
  15. {ietfdata-0.7.0 → ietfdata-0.7.2}/README.md +0 -0
  16. {ietfdata-0.7.0 → ietfdata-0.7.2}/examples/__init__.py +0 -0
  17. {ietfdata-0.7.0 → ietfdata-0.7.2}/examples/bluesheets.py +0 -0
  18. {ietfdata-0.7.0 → ietfdata-0.7.2}/examples/draft-authors.py +0 -0
  19. {ietfdata-0.7.0 → ietfdata-0.7.2}/examples/draft-references.py +0 -0
  20. {ietfdata-0.7.0 → ietfdata-0.7.2}/examples/draft-submissions-by-date.py +0 -0
  21. {ietfdata-0.7.0 → ietfdata-0.7.2}/examples/draft-submissions.py +0 -0
  22. {ietfdata-0.7.0 → ietfdata-0.7.2}/examples/drafts-for-person.py +0 -0
  23. {ietfdata-0.7.0 → ietfdata-0.7.2}/examples/drafts-for-rfc.py +0 -0
  24. {ietfdata-0.7.0 → ietfdata-0.7.2}/examples/drafts-for-wg.py +0 -0
  25. {ietfdata-0.7.0 → ietfdata-0.7.2}/examples/emails-per-person.py +0 -0
  26. {ietfdata-0.7.0 → ietfdata-0.7.2}/examples/iesg-processing-time.py +0 -0
  27. {ietfdata-0.7.0 → ietfdata-0.7.2}/examples/ietf-leadership.py +0 -0
  28. {ietfdata-0.7.0 → ietfdata-0.7.2}/examples/non-wg-standards-track-rfcs.py +0 -0
  29. {ietfdata-0.7.0 → ietfdata-0.7.2}/examples/org-chart.py +0 -0
  30. {ietfdata-0.7.0 → ietfdata-0.7.2}/examples/person-attendance.py +0 -0
  31. {ietfdata-0.7.0 → ietfdata-0.7.2}/examples/person-emails.py +0 -0
  32. {ietfdata-0.7.0 → ietfdata-0.7.2}/examples/person.py +0 -0
  33. {ietfdata-0.7.0 → ietfdata-0.7.2}/examples/recent-pubreq.py +0 -0
  34. {ietfdata-0.7.0 → ietfdata-0.7.2}/examples/rfc-data.py +0 -0
  35. {ietfdata-0.7.0 → ietfdata-0.7.2}/examples/rfc-per-year.py +0 -0
  36. {ietfdata-0.7.0 → ietfdata-0.7.2}/examples/rfc-streams.py +0 -0
  37. {ietfdata-0.7.0 → ietfdata-0.7.2}/examples/role-names.py +0 -0
  38. {ietfdata-0.7.0 → ietfdata-0.7.2}/ietfdata/__init__.py +0 -0
  39. {ietfdata-0.7.0 → ietfdata-0.7.2}/ietfdata/datatracker_ext.py +0 -0
  40. {ietfdata-0.7.0 → ietfdata-0.7.2}/ietfdata/py.typed +0 -0
  41. {ietfdata-0.7.0 → ietfdata-0.7.2}/ietfdata.egg-info/dependency_links.txt +0 -0
  42. {ietfdata-0.7.0 → ietfdata-0.7.2}/ietfdata.egg-info/requires.txt +0 -0
  43. {ietfdata-0.7.0 → ietfdata-0.7.2}/ietfdata.egg-info/top_level.txt +0 -0
  44. {ietfdata-0.7.0 → ietfdata-0.7.2}/setup.cfg +0 -0
  45. {ietfdata-0.7.0 → ietfdata-0.7.2}/tests/test_datatracker_coverage.py +0 -0
  46. {ietfdata-0.7.0 → ietfdata-0.7.2}/tests/test_mailarchive2.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ietfdata
3
- Version: 0.7.0
3
+ Version: 0.7.2
4
4
  Summary: Access the IETF Data Tracker and RFC Index
5
5
  Home-page: https://github.com/glasgow-ipl/ietfdata
6
6
  Author: Colin Perkins
@@ -0,0 +1,54 @@
1
+ # Copyright (C) 2024 University of Glasgow, University of St Andrews
2
+ #
3
+ # Redistribution and use in source and binary forms, with or without
4
+ # modification, are permitted provided that the following conditions
5
+ # are met:
6
+ #
7
+ # 1. Redistributions of source code must retain the above copyright notice,
8
+ # this list of conditions and the following disclaimer.
9
+ #
10
+ # 2. Redistributions in binary form must reproduce the above copyright
11
+ # notice, this list of conditions and the following disclaimer in the
12
+ # documentation and/or other materials provided with the distribution.
13
+ #
14
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
18
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24
+ # POSSIBILITY OF SUCH DAMAGE.
25
+
26
+ import os
27
+ import requests
28
+ import sys
29
+
30
+ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
31
+
32
+ from pathlib import Path
33
+ from ietfdata.datatracker import *
34
+ from ietfdata.rfcindex import *
35
+
36
+ dt = DataTracker()
37
+
38
+ with requests.Session() as session:
39
+ print("Finding chatlogs for ohai WG:")
40
+
41
+ chatlog = dt.document_type_from_slug("chatlog")
42
+ ohai_wg = dt.group_from_acronym("ohai")
43
+
44
+ for doc in dt.documents(doctype = chatlog, group = ohai_wg):
45
+ print(doc)
46
+ print(f" {doc.title}")
47
+ print(f" {doc.url()}")
48
+
49
+ response = session.get(doc.url(), verify=True)
50
+ if response.status_code != 200:
51
+ print(f" {response.status_code}")
52
+
53
+ print("")
54
+
@@ -0,0 +1,105 @@
1
+ # Copyright (C) 2024 University of Glasgow
2
+ #
3
+ # Redistribution and use in source and binary forms, with or without
4
+ # modification, are permitted provided that the following conditions
5
+ # are met:
6
+ #
7
+ # 1. Redistributions of source code must retain the above copyright notice,
8
+ # this list of conditions and the following disclaimer.
9
+ #
10
+ # 2. Redistributions in binary form must reproduce the above copyright
11
+ # notice, this list of conditions and the following disclaimer in the
12
+ # documentation and/or other materials provided with the distribution.
13
+ #
14
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
18
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24
+ # POSSIBILITY OF SUCH DAMAGE.
25
+
26
+ from datetime import datetime
27
+ from email.utils import parseaddr
28
+
29
+ from ietfdata.datatracker import *
30
+ from ietfdata.mailarchive2 import *
31
+
32
+ dt = DataTracker()
33
+ ma = MailArchive()
34
+
35
+ #ma.update()
36
+
37
+ def date_in_range(date):
38
+ if date is None:
39
+ return False
40
+ return date > datetime(2019, 6, 12) and date < datetime(2024, 6, 12)
41
+
42
+ # How many people posted to all the IETF lists?
43
+
44
+ print("Checking lists:")
45
+ mcount_all = 0
46
+ all_addrs = {}
47
+ for ml_name in ma.mailing_list_names():
48
+ if ml_name in ["dmarc-report"]:
49
+ print(f" {ml_name} skipped")
50
+ continue
51
+ print(f" {ml_name}")
52
+ ml = ma.mailing_list(ml_name)
53
+ for msg in ml.messages():
54
+ if date_in_range(msg.date()):
55
+ mcount_all += 1
56
+ addr_hdr = msg.header("from")
57
+ if len(addr_hdr) == 1:
58
+ name, addr = parseaddr(addr_hdr[0])
59
+ if addr in all_addrs:
60
+ all_addrs[addr] += 1
61
+ else:
62
+ all_addrs[addr] = 1
63
+
64
+ count_all = len(all_addrs)
65
+
66
+ print(f"Addresses posting to all lists: {count_all}")
67
+
68
+ # How many people posted to the ietf@ietf.org list?
69
+
70
+ mcount_ietf = 0
71
+ ietf_addrs = {}
72
+ ietf_list = ma.mailing_list("ietf")
73
+ for msg in ietf_list.messages():
74
+ if date_in_range(msg.date()):
75
+ mcount_ietf += 1
76
+ addr_hdr = msg.header("from")
77
+ if len(addr_hdr) == 1:
78
+ name, addr = parseaddr(addr_hdr[0])
79
+ if addr in ietf_addrs:
80
+ ietf_addrs[addr] += 1
81
+ else:
82
+ ietf_addrs[addr] = 1
83
+
84
+ count_ietf = len(ietf_addrs)
85
+ percentage = count_ietf / count_all * 100.0
86
+
87
+ print(f"Addresses posting to ietf@ietf.org: {count_ietf}")
88
+ print(f"{percentage}%")
89
+ print(f"")
90
+
91
+ pm = mcount_ietf / mcount_all * 100.0
92
+
93
+ print(f"Number of messages (all): {mcount_all}")
94
+ print(f"Number of messages (IETF): {mcount_ietf} ({pm}%)")
95
+
96
+ print("")
97
+ c_msg = 0
98
+ c_per = 0
99
+ for addr in sorted(ietf_addrs, key = ietf_addrs.get, reverse=True):
100
+ c_per += 1
101
+ c_msg += ietf_addrs[addr]
102
+ print(c_per, c_msg, c_msg/mcount_ietf * 100, addr, ietf_addrs[addr])
103
+ if c_msg > (mcount_ietf * 0.5):
104
+ break
105
+
@@ -0,0 +1,62 @@
1
+ # Copyright (C) 2020 University of Glasgow
2
+ #
3
+ # Redistribution and use in source and binary forms, with or without
4
+ # modification, are permitted provided that the following conditions
5
+ # are met:
6
+ #
7
+ # 1. Redistributions of source code must retain the above copyright notice,
8
+ # this list of conditions and the following disclaimer.
9
+ #
10
+ # 2. Redistributions in binary form must reproduce the above copyright
11
+ # notice, this list of conditions and the following disclaimer in the
12
+ # documentation and/or other materials provided with the distribution.
13
+ #
14
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
18
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24
+ # POSSIBILITY OF SUCH DAMAGE.
25
+
26
+ import os
27
+ import sys
28
+
29
+ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
30
+
31
+ from pathlib import Path
32
+ from ietfdata.datatracker import *
33
+ from ietfdata.rfcindex import *
34
+
35
+ # =============================================================================
36
+
37
+ ri = RFCIndex()
38
+
39
+ rfcs_per_year = {}
40
+ authors_per_year = {}
41
+
42
+ for rfc in ri.rfcs():
43
+ if rfc.stream == "IETF":
44
+ if rfc.year not in rfcs_per_year:
45
+ rfcs_per_year[rfc.year] = 0
46
+ rfcs_per_year[rfc.year] += 1
47
+
48
+ if rfc.year not in authors_per_year:
49
+ authors_per_year[rfc.year] = []
50
+ for author in rfc.authors:
51
+ if author not in authors_per_year[rfc.year]:
52
+ authors_per_year[rfc.year].append(author)
53
+
54
+ rfcs = 0
55
+ year = 2000
56
+ print("# year rfcs rfc_cumulative authors")
57
+ while year < 2025:
58
+ rfcs += rfcs_per_year[year]
59
+ print(f" {year} {rfcs_per_year[year]:4} {rfcs:14} {len(authors_per_year[year]):7}")
60
+ year += 1
61
+
62
+
@@ -432,7 +432,6 @@ class Document(Resource):
432
432
  rfc_number : Optional[int]
433
433
  rev : str
434
434
  abstract : str
435
- internal_comments : str
436
435
  note : str
437
436
  ad : Optional[PersonURI]
438
437
  shepherd : Optional[EmailURI]
@@ -451,7 +450,7 @@ class Document(Resource):
451
450
  assert self.std_level is None or self.std_level.startswith("/api/v1/name/stdlevelname/")
452
451
 
453
452
  def url(self) -> str:
454
- # See https://trac.tools.ietf.org/tools/ietfdb/browser/trunk/ietf/settings.py and search for DOC_HREFS
453
+ # See https://github.com/ietf-tools/datatracker/blob/main/ietf/settings.py and search for DOC_HREFS
455
454
  if self.type == DocumentTypeURI(uri="/api/v1/name/doctypename/agenda/"):
456
455
  # FIXME: should be "/meeting/{meeting.number}/materials/{doc.name}-{doc.rev}" ???
457
456
  # FIXME: This doesn't work for interim meetings
@@ -493,7 +492,12 @@ class Document(Resource):
493
492
  mtg = self.name.split("-")[1]
494
493
  url = "https://www.ietf.org/proceedings/" + mtg + "/slides/" + self.uploaded_filename
495
494
  elif self.type == DocumentTypeURI(uri="/api/v1/name/doctypename/statchg/"):
496
- url = "https://www.ietf.org/sc/" + self.name + "-" + self.rev + ".txt"
495
+ url = "https://www.ietf.org/ietf-ftp/status-changes/" + self.name + "-" + self.rev + ".txt"
496
+ elif self.type == DocumentTypeURI(uri="/api/v1/name/doctypename/chatlog/"):
497
+ mtg = self.name.split("-")[1]
498
+ if mtg == "interim":
499
+ mtg = "-".join(self.name.split("-")[1:-1])
500
+ url = "https://datatracker.ietf.org/meeting/" + mtg + "/materials/" + self.uploaded_filename
497
501
  else:
498
502
  raise NotImplementedError
499
503
  return url
@@ -876,7 +880,6 @@ class Schedule(Resource):
876
880
  visible : bool
877
881
  public : bool
878
882
  badness : Optional[str]
879
- notes : str
880
883
 
881
884
 
882
885
  class Meeting(Resource):
@@ -960,7 +963,6 @@ class SessionAssignment(Resource):
960
963
  schedule : ScheduleURI
961
964
  timeslot : TimeslotURI
962
965
  modified : datetime
963
- notes : str
964
966
  pinned : bool
965
967
  extendedfrom : Optional[str]
966
968
  badness : int
@@ -1622,7 +1624,7 @@ class DataTracker:
1622
1624
  logging.basicConfig(level=os.environ.get("IETFDATA_LOGLEVEL", "INFO"))
1623
1625
  self.log = logging.getLogger("ietfdata")
1624
1626
 
1625
- self.ua = "glasgow-ietfdata/0.7.0" # Update when making a new relaase
1627
+ self.ua = "glasgow-ietfdata/0.7.2" # Update when making a new relaase
1626
1628
  self.base_url = os.environ.get("IETFDATA_DT_URL", "https://datatracker.ietf.org")
1627
1629
  self.get_count = 0
1628
1630
 
@@ -590,49 +590,50 @@ class MailingList:
590
590
  threads = {}
591
591
  seen = {} # type: Dict[str,Envelope]
592
592
  for msg in self.messages():
593
- msg_id = msg.header("message-id")[0]
594
- seen[msg_id] = msg
595
-
596
- self._log.debug(f"{msg.uid():5} {msg.header('message-id')} {msg.header('subject')}")
597
-
598
- parents = msg.in_reply_to()
599
- if len(parents) == 0:
600
- # This is the first message in the thread
601
- if msg.header("message-id")[0] not in threads:
602
- threads[msg.header("message-id")[0]] = [msg]
603
- self._log.debug(" First in thread")
604
- elif parents[0].header("message-id")[0] in seen:
605
- # This is part of a thread we've already seen
606
- self._log.debug(f" {parents[0].header('message-id')} {parents[0].header('subject')}")
607
- self._log.debug(f" Continues known thread")
608
- else:
609
- # This is either a new thread that has been copied to this list
610
- # where the earlier messages in the thread are on another list,
611
- # or this message is part of an existing thread but has arrived
612
- # before its parent.
613
- curr = []
614
- curr.append(msg)
615
- while True:
616
- parents = curr[0].in_reply_to()
617
-
618
- parent_in_this_list = False
619
- for p in parents:
620
- if p.mailing_list() == self.name():
621
- parent_in_this_list = True
622
- if not parent_in_this_list and this_list_only:
623
- self._log.debug(f" {parents[0].header('message-id')} {parents[0].header('subject')}")
624
- self._log.debug(f" Not in this list")
625
- if curr[0].header("message-id")[0] not in threads:
626
- threads[curr[0].header("message-id")[0]] = curr
627
- break
628
-
629
- if len(parents) == 0:
630
- self._log.debug(" First in thread")
631
- if curr[0].header("message-id")[0] not in threads:
632
- threads[curr[0].header("message-id")[0]] = curr
633
- break
634
- curr = parents
635
- self._log.debug(f" {curr[0].header('message-id')} {curr[0].header('subject')}")
593
+ if len(msg.header("message-id")) > 0:
594
+ msg_id = msg.header("message-id")[0]
595
+ seen[msg_id] = msg
596
+
597
+ self._log.debug(f"{msg.uid():5} {msg.header('message-id')} {msg.header('subject')}")
598
+
599
+ parents = msg.in_reply_to()
600
+ if len(parents) == 0:
601
+ # This is the first message in the thread
602
+ if msg.header("message-id")[0] not in threads:
603
+ threads[msg.header("message-id")[0]] = [msg]
604
+ self._log.debug(" First in thread")
605
+ elif parents[0].header("message-id")[0] in seen:
606
+ # This is part of a thread we've already seen
607
+ self._log.debug(f" {parents[0].header('message-id')} {parents[0].header('subject')}")
608
+ self._log.debug(f" Continues known thread")
609
+ else:
610
+ # This is either a new thread that has been copied to this list
611
+ # where the earlier messages in the thread are on another list,
612
+ # or this message is part of an existing thread but has arrived
613
+ # before its parent.
614
+ curr = []
615
+ curr.append(msg)
616
+ while True:
617
+ parents = curr[0].in_reply_to()
618
+
619
+ parent_in_this_list = False
620
+ for p in parents:
621
+ if p.mailing_list() == self.name():
622
+ parent_in_this_list = True
623
+ if not parent_in_this_list and this_list_only:
624
+ self._log.debug(f" {parents[0].header('message-id')} {parents[0].header('subject')}")
625
+ self._log.debug(f" Not in this list")
626
+ if curr[0].header("message-id")[0] not in threads:
627
+ threads[curr[0].header("message-id")[0]] = curr
628
+ break
629
+
630
+ if len(parents) == 0:
631
+ self._log.debug(" First in thread")
632
+ if curr[0].header("message-id")[0] not in threads:
633
+ threads[curr[0].header("message-id")[0]] = curr
634
+ break
635
+ curr = parents
636
+ self._log.debug(f" {curr[0].header('message-id')} {curr[0].header('subject')}")
636
637
  return threads
637
638
 
638
639
 
@@ -759,6 +760,8 @@ class MailArchive:
759
760
  ], unique=False)
760
761
  self._db.metadata.create_index([('message_id', ASCENDING)
761
762
  ], unique=False)
763
+ self._db.metadata.create_index([('in_reply_to', ASCENDING)
764
+ ], unique=False)
762
765
  self._fs = GridFS(self._db)
763
766
  # Create other state:
764
767
  self._mailing_lists = {}
@@ -84,59 +84,59 @@ class RfcEntry:
84
84
  self.formats = []
85
85
 
86
86
  for elem in rfc_element:
87
- if elem.tag == "{http://www.rfc-editor.org/rfc-index}doc-id":
87
+ if elem.tag == "{https://www.rfc-editor.org/rfc-index}doc-id":
88
88
  assert elem.text is not None
89
89
  self.doc_id = DocID(elem.text)
90
- elif elem.tag == "{http://www.rfc-editor.org/rfc-index}title":
90
+ elif elem.tag == "{https://www.rfc-editor.org/rfc-index}title":
91
91
  assert elem.text is not None
92
92
  self.title = elem.text
93
- elif elem.tag == "{http://www.rfc-editor.org/rfc-index}doi":
93
+ elif elem.tag == "{https://www.rfc-editor.org/rfc-index}doi":
94
94
  assert elem.text is not None
95
95
  self.doi = elem.text
96
- elif elem.tag == "{http://www.rfc-editor.org/rfc-index}stream":
96
+ elif elem.tag == "{https://www.rfc-editor.org/rfc-index}stream":
97
97
  assert elem.text is not None
98
98
  self.stream = elem.text
99
- elif elem.tag == "{http://www.rfc-editor.org/rfc-index}wg_acronym":
99
+ elif elem.tag == "{https://www.rfc-editor.org/rfc-index}wg_acronym":
100
100
  self.wg = elem.text
101
- elif elem.tag == "{http://www.rfc-editor.org/rfc-index}area":
101
+ elif elem.tag == "{https://www.rfc-editor.org/rfc-index}area":
102
102
  self.area = elem.text
103
- elif elem.tag == "{http://www.rfc-editor.org/rfc-index}current-status":
103
+ elif elem.tag == "{https://www.rfc-editor.org/rfc-index}current-status":
104
104
  assert elem.text is not None
105
105
  self.curr_status = elem.text
106
- elif elem.tag == "{http://www.rfc-editor.org/rfc-index}publication-status":
106
+ elif elem.tag == "{https://www.rfc-editor.org/rfc-index}publication-status":
107
107
  assert elem.text is not None
108
108
  self.publ_status = elem.text
109
- elif elem.tag == "{http://www.rfc-editor.org/rfc-index}author":
109
+ elif elem.tag == "{https://www.rfc-editor.org/rfc-index}author":
110
110
  for inner in elem:
111
- if inner.tag == "{http://www.rfc-editor.org/rfc-index}name":
111
+ if inner.tag == "{https://www.rfc-editor.org/rfc-index}name":
112
112
  assert inner.text is not None
113
113
  self.authors.append(inner.text)
114
- elif inner.tag == "{http://www.rfc-editor.org/rfc-index}title":
114
+ elif inner.tag == "{https://www.rfc-editor.org/rfc-index}title":
115
115
  # Ignore <title>...</title> within <author>...</author> tags
116
116
  # (this is normally just "Editor", which isn't useful)
117
117
  pass
118
118
  else:
119
119
  raise NotImplementedError
120
- elif elem.tag == "{http://www.rfc-editor.org/rfc-index}date":
120
+ elif elem.tag == "{https://www.rfc-editor.org/rfc-index}date":
121
121
  for inner in elem:
122
122
  assert inner.text is not None
123
- if inner.tag == "{http://www.rfc-editor.org/rfc-index}day":
123
+ if inner.tag == "{https://www.rfc-editor.org/rfc-index}day":
124
124
  # <day>...</day> is only included for 1 April RFCs
125
125
  self.day = int(inner.text)
126
- elif inner.tag == "{http://www.rfc-editor.org/rfc-index}month":
126
+ elif inner.tag == "{https://www.rfc-editor.org/rfc-index}month":
127
127
  self.month = inner.text
128
- elif inner.tag == "{http://www.rfc-editor.org/rfc-index}year":
128
+ elif inner.tag == "{https://www.rfc-editor.org/rfc-index}year":
129
129
  self.year = int(inner.text)
130
130
  else:
131
131
  raise NotImplementedError
132
- elif elem.tag == "{http://www.rfc-editor.org/rfc-index}format":
132
+ elif elem.tag == "{https://www.rfc-editor.org/rfc-index}format":
133
133
  for inner in elem:
134
134
  assert inner.text is not None
135
- if inner.tag == "{http://www.rfc-editor.org/rfc-index}file-format":
135
+ if inner.tag == "{https://www.rfc-editor.org/rfc-index}file-format":
136
136
  self.formats.append(inner.text)
137
137
  else:
138
138
  raise NotImplementedError
139
- elif elem.tag == "{http://www.rfc-editor.org/rfc-index}draft":
139
+ elif elem.tag == "{https://www.rfc-editor.org/rfc-index}draft":
140
140
  if elem.text == "rfc4049bis":
141
141
  # RFC 6019 is RFC 4049 republished as a Proposed Standard RF.
142
142
  # with virtually no change. It was never published as a draft,
@@ -147,63 +147,63 @@ class RfcEntry:
147
147
  self.draft = None
148
148
  else:
149
149
  self.draft = elem.text
150
- elif elem.tag == "{http://www.rfc-editor.org/rfc-index}keywords":
150
+ elif elem.tag == "{https://www.rfc-editor.org/rfc-index}keywords":
151
151
  for inner in elem:
152
- if inner.tag == "{http://www.rfc-editor.org/rfc-index}kw":
152
+ if inner.tag == "{https://www.rfc-editor.org/rfc-index}kw":
153
153
  # Omit empty <kw></kw>
154
154
  if inner.text is not None:
155
155
  self.keywords.append(inner.text)
156
156
  else:
157
157
  raise NotImplementedError
158
- elif elem.tag == "{http://www.rfc-editor.org/rfc-index}updates":
158
+ elif elem.tag == "{https://www.rfc-editor.org/rfc-index}updates":
159
159
  for inner in elem:
160
160
  assert inner.text is not None
161
- if inner.tag == "{http://www.rfc-editor.org/rfc-index}doc-id":
161
+ if inner.tag == "{https://www.rfc-editor.org/rfc-index}doc-id":
162
162
  self.updates.append(DocID(inner.text))
163
163
  else:
164
164
  raise NotImplementedError
165
- elif elem.tag == "{http://www.rfc-editor.org/rfc-index}updated-by":
165
+ elif elem.tag == "{https://www.rfc-editor.org/rfc-index}updated-by":
166
166
  for inner in elem:
167
167
  assert inner.text is not None
168
- if inner.tag == "{http://www.rfc-editor.org/rfc-index}doc-id":
168
+ if inner.tag == "{https://www.rfc-editor.org/rfc-index}doc-id":
169
169
  self.updated_by.append(DocID(inner.text))
170
170
  else:
171
171
  raise NotImplementedError
172
- elif elem.tag == "{http://www.rfc-editor.org/rfc-index}obsoletes":
172
+ elif elem.tag == "{https://www.rfc-editor.org/rfc-index}obsoletes":
173
173
  for inner in elem:
174
174
  assert inner.text is not None
175
- if inner.tag == "{http://www.rfc-editor.org/rfc-index}doc-id":
175
+ if inner.tag == "{https://www.rfc-editor.org/rfc-index}doc-id":
176
176
  self.obsoletes.append(DocID(inner.text))
177
177
  else:
178
178
  raise NotImplementedError
179
- elif elem.tag == "{http://www.rfc-editor.org/rfc-index}obsoleted-by":
179
+ elif elem.tag == "{https://www.rfc-editor.org/rfc-index}obsoleted-by":
180
180
  for inner in elem:
181
181
  assert inner.text is not None
182
- if inner.tag == "{http://www.rfc-editor.org/rfc-index}doc-id":
182
+ if inner.tag == "{https://www.rfc-editor.org/rfc-index}doc-id":
183
183
  self.obsoleted_by.append(DocID(inner.text))
184
184
  else:
185
185
  raise NotImplementedError
186
- elif elem.tag == "{http://www.rfc-editor.org/rfc-index}is-also":
186
+ elif elem.tag == "{https://www.rfc-editor.org/rfc-index}is-also":
187
187
  for inner in elem:
188
188
  assert inner.text is not None
189
- if inner.tag == "{http://www.rfc-editor.org/rfc-index}doc-id":
189
+ if inner.tag == "{https://www.rfc-editor.org/rfc-index}doc-id":
190
190
  self.is_also.append(DocID(inner.text))
191
191
  else:
192
192
  raise NotImplementedError
193
- elif elem.tag == "{http://www.rfc-editor.org/rfc-index}see-also":
193
+ elif elem.tag == "{https://www.rfc-editor.org/rfc-index}see-also":
194
194
  for inner in elem:
195
195
  assert inner.text is not None
196
- if inner.tag == "{http://www.rfc-editor.org/rfc-index}doc-id":
196
+ if inner.tag == "{https://www.rfc-editor.org/rfc-index}doc-id":
197
197
  self.see_also.append(DocID(inner.text))
198
198
  else:
199
199
  raise NotImplementedError
200
- elif elem.tag == "{http://www.rfc-editor.org/rfc-index}errata-url":
200
+ elif elem.tag == "{https://www.rfc-editor.org/rfc-index}errata-url":
201
201
  self.errata_url = elem.text
202
- elif elem.tag == "{http://www.rfc-editor.org/rfc-index}abstract":
202
+ elif elem.tag == "{https://www.rfc-editor.org/rfc-index}abstract":
203
203
  # The <abstract>...</abstract> contains formatted XML, most
204
204
  # typically a sequence of <p>...</p> tags.
205
205
  self.abstract = elem
206
- elif elem.tag == "{http://www.rfc-editor.org/rfc-index}page-count":
206
+ elif elem.tag == "{https://www.rfc-editor.org/rfc-index}page-count":
207
207
  assert elem.text is not None
208
208
  self.page_count = int(elem.text)
209
209
  else:
@@ -327,7 +327,7 @@ class RfcNotIssuedEntry:
327
327
 
328
328
  def __init__(self, rfc_not_issued_element: ET.Element) -> None:
329
329
  for elem in rfc_not_issued_element:
330
- if elem.tag == "{http://www.rfc-editor.org/rfc-index}doc-id":
330
+ if elem.tag == "{https://www.rfc-editor.org/rfc-index}doc-id":
331
331
  assert elem.text is not None
332
332
  self.doc_id = DocID(elem.text)
333
333
  else:
@@ -354,13 +354,13 @@ class BcpEntry:
354
354
  self.is_also = []
355
355
 
356
356
  for elem in bcp_element:
357
- if elem.tag == "{http://www.rfc-editor.org/rfc-index}doc-id":
357
+ if elem.tag == "{https://www.rfc-editor.org/rfc-index}doc-id":
358
358
  assert elem.text is not None
359
359
  self.doc_id = DocID(elem.text)
360
- elif elem.tag == "{http://www.rfc-editor.org/rfc-index}is-also":
360
+ elif elem.tag == "{https://www.rfc-editor.org/rfc-index}is-also":
361
361
  for inner in elem:
362
362
  assert inner.text is not None
363
- if inner.tag == "{http://www.rfc-editor.org/rfc-index}doc-id":
363
+ if inner.tag == "{https://www.rfc-editor.org/rfc-index}doc-id":
364
364
  self.is_also.append(DocID(inner.text))
365
365
  else:
366
366
  raise NotImplementedError
@@ -391,14 +391,14 @@ class StdEntry:
391
391
 
392
392
  for elem in std_element:
393
393
  assert elem.text is not None
394
- if elem.tag == "{http://www.rfc-editor.org/rfc-index}doc-id":
394
+ if elem.tag == "{https://www.rfc-editor.org/rfc-index}doc-id":
395
395
  self.doc_id = DocID(elem.text)
396
- elif elem.tag == "{http://www.rfc-editor.org/rfc-index}title":
396
+ elif elem.tag == "{https://www.rfc-editor.org/rfc-index}title":
397
397
  self.title = elem.text
398
- elif elem.tag == "{http://www.rfc-editor.org/rfc-index}is-also":
398
+ elif elem.tag == "{https://www.rfc-editor.org/rfc-index}is-also":
399
399
  for inner in elem:
400
400
  assert inner.text is not None
401
- if inner.tag == "{http://www.rfc-editor.org/rfc-index}doc-id":
401
+ if inner.tag == "{https://www.rfc-editor.org/rfc-index}doc-id":
402
402
  self.is_also.append(DocID(inner.text))
403
403
  else:
404
404
  raise NotImplementedError
@@ -428,12 +428,12 @@ class FyiEntry:
428
428
 
429
429
  for elem in fyi_element:
430
430
  assert elem.text is not None
431
- if elem.tag == "{http://www.rfc-editor.org/rfc-index}doc-id":
431
+ if elem.tag == "{https://www.rfc-editor.org/rfc-index}doc-id":
432
432
  self.doc_id = DocID(elem.text)
433
- elif elem.tag == "{http://www.rfc-editor.org/rfc-index}is-also":
433
+ elif elem.tag == "{https://www.rfc-editor.org/rfc-index}is-also":
434
434
  for inner in elem:
435
435
  assert inner.text is not None
436
- if inner.tag == "{http://www.rfc-editor.org/rfc-index}doc-id":
436
+ if inner.tag == "{https://www.rfc-editor.org/rfc-index}doc-id":
437
437
  self.is_also.append(DocID(inner.text))
438
438
  else:
439
439
  raise NotImplementedError
@@ -515,22 +515,23 @@ class RFCIndex:
515
515
  raise RuntimeError
516
516
 
517
517
  for doc in ET.fromstring(xml):
518
- if doc.tag == "{http://www.rfc-editor.org/rfc-index}rfc-entry":
518
+ if doc.tag == "{https://www.rfc-editor.org/rfc-index}rfc-entry":
519
519
  rfc = RfcEntry(doc)
520
520
  self._rfc[rfc.doc_id] = rfc
521
- elif doc.tag == "{http://www.rfc-editor.org/rfc-index}rfc-not-issued-entry":
521
+ elif doc.tag == "{https://www.rfc-editor.org/rfc-index}rfc-not-issued-entry":
522
522
  rne = RfcNotIssuedEntry(doc)
523
523
  self._rfc_not_issued[rne.doc_id] = rne
524
- elif doc.tag == "{http://www.rfc-editor.org/rfc-index}bcp-entry":
524
+ elif doc.tag == "{https://www.rfc-editor.org/rfc-index}bcp-entry":
525
525
  bcp = BcpEntry(doc)
526
526
  self._bcp[bcp.doc_id] = bcp
527
- elif doc.tag == "{http://www.rfc-editor.org/rfc-index}std-entry":
527
+ elif doc.tag == "{https://www.rfc-editor.org/rfc-index}std-entry":
528
528
  std = StdEntry(doc)
529
529
  self._std[std.doc_id] = std
530
- elif doc.tag == "{http://www.rfc-editor.org/rfc-index}fyi-entry":
530
+ elif doc.tag == "{https://www.rfc-editor.org/rfc-index}fyi-entry":
531
531
  fyi = FyiEntry(doc)
532
532
  self._fyi[fyi.doc_id] = fyi
533
533
  else:
534
+ print(f"Unexpected doc.tag: {doc.tag}")
534
535
  raise NotImplementedError
535
536
 
536
537
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ietfdata
3
- Version: 0.7.0
3
+ Version: 0.7.2
4
4
  Summary: Access the IETF Data Tracker and RFC Index
5
5
  Home-page: https://github.com/glasgow-ipl/ietfdata
6
6
  Author: Colin Perkins
@@ -4,6 +4,7 @@ pyproject.toml
4
4
  setup.py
5
5
  examples/__init__.py
6
6
  examples/bluesheets.py
7
+ examples/chatlogs.py
7
8
  examples/draft-authors.py
8
9
  examples/draft-references.py
9
10
  examples/draft-submissions-by-date.py
@@ -11,6 +12,7 @@ examples/draft-submissions.py
11
12
  examples/drafts-for-person.py
12
13
  examples/drafts-for-rfc.py
13
14
  examples/drafts-for-wg.py
15
+ examples/emails-ietf-community.py
14
16
  examples/emails-per-person.py
15
17
  examples/iesg-processing-time.py
16
18
  examples/ietf-leadership.py
@@ -21,6 +23,7 @@ examples/person-emails.py
21
23
  examples/person.py
22
24
  examples/recent-pubreq.py
23
25
  examples/rfc-data.py
26
+ examples/rfc-per-year-ietf.py
24
27
  examples/rfc-per-year.py
25
28
  examples/rfc-streams.py
26
29
  examples/role-names.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ietfdata"
7
- version = "0.7.0"
7
+ version = "0.7.2"
8
8
  authors = [
9
9
  {name = "Colin Perkins", email = "csp@csperkins.org"}
10
10
  ]
@@ -5,7 +5,7 @@ with open("README.md", "r") as fh:
5
5
 
6
6
  setuptools.setup(
7
7
  name="ietfdata",
8
- version="0.7.0",
8
+ version="0.7.2",
9
9
  author="Colin Perkins",
10
10
  author_email="csp@csperkins.org",
11
11
  description="Access the IETF Data Tracker and RFC Index",
@@ -511,7 +511,6 @@ class TestDatatracker(unittest.TestCase):
511
511
  self.assertEqual(d.notify, "")
512
512
  self.assertEqual(d.type, DocumentTypeURI(uri="/api/v1/name/doctypename/agenda/"))
513
513
  self.assertEqual(d.rev, "2")
514
- self.assertEqual(d.internal_comments, "")
515
514
  self.assertEqual(d.id, 218)
516
515
  self.assertEqual(d.std_level, None)
517
516
  self.assertEqual(d.ad, None)
@@ -540,7 +539,6 @@ class TestDatatracker(unittest.TestCase):
540
539
  def test_document_bluesheets(self) -> None:
541
540
  d = self.dt.document(DocumentURI(uri="/api/v1/doc/document/bluesheets-95-xrblock-01/"))
542
541
  if d is not None:
543
- self.assertEqual(d.internal_comments, "")
544
542
  self.assertEqual(d.id, 68163)
545
543
  self.assertEqual(d.name, "bluesheets-95-xrblock-01")
546
544
  self.assertEqual(d.notify, "")
@@ -577,7 +575,6 @@ class TestDatatracker(unittest.TestCase):
577
575
  def test_document_charter(self) -> None:
578
576
  d = self.dt.document(DocumentURI(uri="/api/v1/doc/document/charter-ietf-vgmib/"))
579
577
  if d is not None:
580
- self.assertEqual(d.internal_comments, "")
581
578
  self.assertEqual(d.id, 1)
582
579
  self.assertEqual(d.name, "charter-ietf-vgmib")
583
580
  self.assertEqual(d.notify, "")
@@ -611,10 +608,45 @@ class TestDatatracker(unittest.TestCase):
611
608
  self.fail("Cannot find document")
612
609
 
613
610
 
611
+ def test_document_chatlog(self) -> None:
612
+ d = self.dt.document(DocumentURI(uri="/api/v1/doc/document/chatlog-114-ohai-202207261330/"))
613
+ if d is not None:
614
+ self.assertEqual(d.id, 110925)
615
+ self.assertEqual(d.name, "chatlog-114-ohai-202207261330")
616
+ self.assertEqual(d.notify, "")
617
+ self.assertEqual(d.rev, "00")
618
+ self.assertEqual(d.external_url, "")
619
+ self.assertEqual(d.expires, None)
620
+ self.assertEqual(d.type, DocumentTypeURI(uri="/api/v1/name/doctypename/chatlog/"))
621
+ self.assertEqual(d.group, GroupURI(uri="/api/v1/group/group/2312/"))
622
+ self.assertEqual(d.resource_uri, DocumentURI(uri="/api/v1/doc/document/chatlog-114-ohai-202207261330/"))
623
+ self.assertEqual(d.title, "Chat Log IETF114: ohai: Tue 13:30")
624
+ self.assertEqual(d.abstract, "")
625
+ self.assertEqual(d.uploaded_filename, "chatlog-114-ohai-202207261330-00.json")
626
+ self.assertEqual(d.rfc, None)
627
+ self.assertEqual(d.shepherd, None)
628
+ self.assertEqual(d.submissions, [])
629
+ self.assertEqual(d.intended_std_level, None)
630
+ self.assertEqual(d.ad, None)
631
+ self.assertEqual(d.note, "")
632
+ self.assertEqual(d.words, None)
633
+ self.assertEqual(d.tags, [])
634
+ self.assertEqual(d.time, datetime.fromisoformat("2022-10-18T15:21:12+0000"))
635
+ self.assertEqual(d.pages, None)
636
+ self.assertEqual(d.stream, None)
637
+ self.assertEqual(d.std_level, None)
638
+ self.assertEqual(d.states, [DocumentStateURI(uri="/api/v1/doc/state/165/")])
639
+
640
+ url = d.url()
641
+ self.assertEqual(url, "https://datatracker.ietf.org/meeting/114/materials/chatlog-114-ohai-202207261330-00.json")
642
+ self.assertEqual(self.dt.session.get(url).status_code, 200)
643
+ else:
644
+ self.fail("Cannot find document")
645
+
646
+
614
647
  def test_document_conflrev(self) -> None:
615
648
  d = self.dt.document(DocumentURI(uri="/api/v1/doc/document/conflict-review-kiyomoto-kcipher2/"))
616
649
  if d is not None:
617
- self.assertEqual(d.internal_comments, "")
618
650
  self.assertEqual(d.id, 17898)
619
651
  self.assertEqual(d.name, "conflict-review-kiyomoto-kcipher2")
620
652
  self.assertEqual(d.notify, "\"Nevil Brownlee\" <rfc-ise@rfc-editor.org>, draft-kiyomoto-kcipher2@tools.ietf.org")
@@ -651,7 +683,6 @@ class TestDatatracker(unittest.TestCase):
651
683
  def test_document_draft(self) -> None:
652
684
  d = self.dt.document(DocumentURI(uri="/api/v1/doc/document/draft-ietf-avt-rtp-new/"))
653
685
  if d is not None:
654
- self.assertEqual(d.internal_comments, "")
655
686
  self.assertEqual(d.id, 19971)
656
687
  self.assertEqual(d.name, "draft-ietf-avt-rtp-new")
657
688
  self.assertEqual(d.notify, "magnus.westerlund@ericsson.com, csp@csperkins.org")
@@ -688,7 +719,6 @@ class TestDatatracker(unittest.TestCase):
688
719
  def test_document_liaison(self) -> None:
689
720
  d = self.dt.document(DocumentURI(uri="/api/v1/doc/document/liaison-2012-05-31-3gpp-mmusic-on-rtcp-bandwidth-negotiation-attachment-1/"))
690
721
  if d is not None:
691
- self.assertEqual(d.internal_comments, "")
692
722
  self.assertEqual(d.id, 46457)
693
723
  self.assertEqual(d.name, "liaison-2012-05-31-3gpp-mmusic-on-rtcp-bandwidth-negotiation-attachment-1")
694
724
  self.assertEqual(d.notify, "")
@@ -725,7 +755,6 @@ class TestDatatracker(unittest.TestCase):
725
755
  def test_document_liai_att(self) -> None:
726
756
  d = self.dt.document(DocumentURI(uri="/api/v1/doc/document/liaison-2004-08-23-itu-t-ietf-liaison-statement-to-ietf-and-itu-t-study-groups-countering-spam-pdf-version-attachment-1/"))
727
757
  if d is not None:
728
- self.assertEqual(d.internal_comments, "")
729
758
  self.assertEqual(d.id, 43519)
730
759
  self.assertEqual(d.name, "liaison-2004-08-23-itu-t-ietf-liaison-statement-to-ietf-and-itu-t-study-groups-countering-spam-pdf-version-attachment-1")
731
760
  self.assertEqual(d.notify, "")
@@ -762,7 +791,6 @@ class TestDatatracker(unittest.TestCase):
762
791
  def test_document_minutes(self) -> None:
763
792
  d = self.dt.document(DocumentURI(uri="/api/v1/doc/document/minutes-89-cfrg/"))
764
793
  if d is not None:
765
- self.assertEqual(d.internal_comments, "")
766
794
  self.assertEqual(d.id, 272)
767
795
  self.assertEqual(d.name, "minutes-89-cfrg")
768
796
  self.assertEqual(d.notify, "")
@@ -799,7 +827,6 @@ class TestDatatracker(unittest.TestCase):
799
827
  def test_document_recording(self) -> None:
800
828
  d = self.dt.document(DocumentURI(uri="/api/v1/doc/document/recording-94-taps-1/"))
801
829
  if d is not None:
802
- self.assertEqual(d.internal_comments, "")
803
830
  self.assertEqual(d.id, 49624)
804
831
  self.assertEqual(d.name, "recording-94-taps-1")
805
832
  self.assertEqual(d.notify, "")
@@ -837,7 +864,6 @@ class TestDatatracker(unittest.TestCase):
837
864
  def test_document_review(self) -> None:
838
865
  d = self.dt.document(DocumentURI(uri="/api/v1/doc/document/review-bchv-rfc6890bis-04-genart-lc-kyzivat-2017-02-28/"))
839
866
  if d is not None:
840
- self.assertEqual(d.internal_comments, "")
841
867
  self.assertEqual(d.id, 69082)
842
868
  self.assertEqual(d.name, "review-bchv-rfc6890bis-04-genart-lc-kyzivat-2017-02-28")
843
869
  self.assertEqual(d.notify, "")
@@ -879,7 +905,6 @@ class TestDatatracker(unittest.TestCase):
879
905
  def test_document_slides(self) -> None:
880
906
  d = self.dt.document(DocumentURI(uri="/api/v1/doc/document/slides-65-l2vpn-4/"))
881
907
  if d is not None:
882
- self.assertEqual(d.internal_comments, "")
883
908
  self.assertEqual(d.id, 736)
884
909
  self.assertEqual(d.name, "slides-65-l2vpn-4")
885
910
  self.assertEqual(d.notify, "")
@@ -916,7 +941,6 @@ class TestDatatracker(unittest.TestCase):
916
941
  def test_document_statchg(self) -> None:
917
942
  d = self.dt.document(DocumentURI(uri="/api/v1/doc/document/status-change-rfc3044-rfc3187-orig-urn-regs-to-historic/"))
918
943
  if d is not None:
919
- self.assertEqual(d.internal_comments, "")
920
944
  self.assertEqual(d.id, 78306)
921
945
  self.assertEqual(d.name, "status-change-rfc3044-rfc3187-orig-urn-regs-to-historic")
922
946
  self.assertEqual(d.notify, "")
@@ -944,7 +968,7 @@ class TestDatatracker(unittest.TestCase):
944
968
  self.assertEqual(d.states, [DocumentStateURI(uri="/api/v1/doc/state/127/")])
945
969
 
946
970
  url = d.url()
947
- self.assertEqual(url, "https://www.ietf.org/sc/status-change-rfc3044-rfc3187-orig-urn-regs-to-historic-00.txt")
971
+ self.assertEqual(url, "https://www.ietf.org/ietf-ftp/status-changes/status-change-rfc3044-rfc3187-orig-urn-regs-to-historic-00.txt")
948
972
  self.assertEqual(self.dt.session.get(url).status_code, 200)
949
973
  else:
950
974
  self.fail("Cannot find document")
@@ -1731,19 +1755,20 @@ class TestDatatracker(unittest.TestCase):
1731
1755
 
1732
1756
  def test_group_roles_email(self) -> None:
1733
1757
  group_roles = list(self.dt.group_roles(email="csp@csperkins.org"))
1734
- self.assertEqual(len(group_roles), 12)
1758
+ self.assertEqual(len(group_roles), 13)
1735
1759
  self.assertEqual(group_roles[0].id, 1076) # SAFE BoF chair
1736
1760
  self.assertEqual(group_roles[1].id, 3998) # TSV DIR reviewer
1737
1761
  self.assertEqual(group_roles[2].id, 8464) # IRSG chair
1738
1762
  self.assertEqual(group_roles[3].id, 8465) # IRTF Open Meeting chair
1739
1763
  self.assertEqual(group_roles[4].id, 8466) # IRTF chair
1740
1764
  self.assertEqual(group_roles[5].id, 9355) # RMCAT chair
1741
- self.assertEqual(group_roles[6].id, 10200) # IAB EDM programme member
1742
- self.assertEqual(group_roles[7].id, 11103) # TSV ART reviewer
1743
- self.assertEqual(group_roles[8].id, 11680) # IRTF ANRW chair
1744
- self.assertEqual(group_roles[9].id, 12875) # RSAB member
1745
- self.assertEqual(group_roles[10].id, 12915) # IAB-ISOC Policy Coordination
1746
- self.assertEqual(group_roles[11].id, 13098) # IAB E-Impact workshop
1765
+ self.assertEqual(group_roles[6].id, 11103) # TSV ART reviewer
1766
+ self.assertEqual(group_roles[7].id, 11680) # IRTF ANRW chair
1767
+ self.assertEqual(group_roles[8].id, 12875) # RSAB member
1768
+ self.assertEqual(group_roles[9].id, 12915) # IAB-ISOC Policy Coordination
1769
+ self.assertEqual(group_roles[10].id, 13098) # IAB E-Impact workshop
1770
+ self.assertEqual(group_roles[11].id, 13698) # IAB
1771
+ self.assertEqual(group_roles[12].id, 13726) #
1747
1772
 
1748
1773
 
1749
1774
  def test_group_roles_group(self) -> None:
@@ -1770,19 +1795,20 @@ class TestDatatracker(unittest.TestCase):
1770
1795
 
1771
1796
  def test_group_roles_person(self) -> None:
1772
1797
  group_roles = list(self.dt.group_roles(person=self.dt.person(PersonURI(uri="/api/v1/person/person/20209/"))))
1773
- self.assertEqual(len(group_roles), 12)
1798
+ self.assertEqual(len(group_roles), 13)
1774
1799
  self.assertEqual(group_roles[0].id, 1076) # SAFE BoF chair
1775
1800
  self.assertEqual(group_roles[1].id, 3998) # TSV DIR reviewer
1776
1801
  self.assertEqual(group_roles[2].id, 8464) # IRSG chair
1777
1802
  self.assertEqual(group_roles[3].id, 8465) # IRTF Open Meeting chair
1778
1803
  self.assertEqual(group_roles[4].id, 8466) # IRTF chair
1779
1804
  self.assertEqual(group_roles[5].id, 9355) # RMCAT chair
1780
- self.assertEqual(group_roles[6].id, 10200) # IAB EDM programme member
1781
- self.assertEqual(group_roles[7].id, 11103) # TSV ART reviewer
1782
- self.assertEqual(group_roles[8].id, 11680) # IRTF ANRW chair
1783
- self.assertEqual(group_roles[9].id, 12875) # RSAB member
1784
- self.assertEqual(group_roles[10].id, 12915) # IAB-ISOC Policy Coordination
1785
- self.assertEqual(group_roles[11].id, 13098) # IAB E-Impact workshop
1805
+ self.assertEqual(group_roles[6].id, 11103) # TSV ART reviewer
1806
+ self.assertEqual(group_roles[7].id, 11680) # IRTF ANRW chair
1807
+ self.assertEqual(group_roles[8].id, 12875) # RSAB member
1808
+ self.assertEqual(group_roles[9].id, 12915) # IAB-ISOC Policy Coordination
1809
+ self.assertEqual(group_roles[10].id, 13098) # IAB E-Impact workshop
1810
+ self.assertEqual(group_roles[11].id, 13698) # IAB
1811
+ self.assertEqual(group_roles[12].id, 13726) #
1786
1812
 
1787
1813
 
1788
1814
  def test_group_milestone_history(self) -> None:
@@ -1888,7 +1914,7 @@ class TestDatatracker(unittest.TestCase):
1888
1914
 
1889
1915
  def test_group_role_histories_email(self) -> None:
1890
1916
  group_role_histories = list(self.dt.group_role_histories(email="csp@csperkins.org"))
1891
- self.assertEqual(len(group_role_histories), 84)
1917
+ self.assertEqual(len(group_role_histories), 91)
1892
1918
 
1893
1919
 
1894
1920
  def test_group_role_histories_group(self) -> None:
@@ -1904,7 +1930,7 @@ class TestDatatracker(unittest.TestCase):
1904
1930
 
1905
1931
  def test_group_role_histories_person(self) -> None:
1906
1932
  group_role_histories = list(self.dt.group_role_histories(person=self.dt.person(PersonURI(uri="/api/v1/person/person/20209/"))))
1907
- self.assertEqual(len(group_role_histories), 84)
1933
+ self.assertEqual(len(group_role_histories), 91)
1908
1934
 
1909
1935
 
1910
1936
  def test_group_state_change_event(self) -> None:
@@ -2080,7 +2106,6 @@ class TestDatatracker(unittest.TestCase):
2080
2106
  self.assertEqual(assignment.pinned, False)
2081
2107
  self.assertEqual(assignment.resource_uri, SessionAssignmentURI(uri="/api/v1/meeting/schedtimesessassignment/61212/"))
2082
2108
  self.assertEqual(assignment.badness, 0)
2083
- self.assertEqual(assignment.notes, "")
2084
2109
  else:
2085
2110
  self.fail("cannot find meeting session assignment")
2086
2111
 
@@ -2283,7 +2308,6 @@ class TestDatatracker(unittest.TestCase):
2283
2308
  self.assertEqual(schedule.visible, True)
2284
2309
  self.assertEqual(schedule.public, True)
2285
2310
  self.assertEqual(schedule.badness, None)
2286
- self.assertEqual(schedule.notes, "")
2287
2311
  else:
2288
2312
  self.fail("cannot find meeting schedule")
2289
2313
 
@@ -3746,7 +3770,7 @@ class TestDatatracker(unittest.TestCase):
3746
3770
 
3747
3771
  def test_announcements_from_name(self) -> None:
3748
3772
  announcements_from = list(self.dt.announcements_from(name=self.dt.role_name(RoleNameURI(uri="/api/v1/name/rolename/chair/"))))
3749
- self.assertEqual(len(announcements_from), 10)
3773
+ self.assertEqual(len(announcements_from), 11)
3750
3774
  self.assertEqual(announcements_from[0].id, 1)
3751
3775
  self.assertEqual(announcements_from[1].id, 2)
3752
3776
  self.assertEqual(announcements_from[2].id, 3)
@@ -3757,6 +3781,7 @@ class TestDatatracker(unittest.TestCase):
3757
3781
  self.assertEqual(announcements_from[7].id, 16)
3758
3782
  self.assertEqual(announcements_from[8].id, 24)
3759
3783
  self.assertEqual(announcements_from[9].id, 26)
3784
+ self.assertEqual(announcements_from[10].id, 30)
3760
3785
 
3761
3786
 
3762
3787
  #def test_message(self) -> None:
@@ -65,7 +65,7 @@ class TestRFCIndex(unittest.TestCase):
65
65
  self.assertEqual(rfc.obsoleted_by, [])
66
66
  self.assertEqual(rfc.is_also, ["STD0064"])
67
67
  self.assertEqual(rfc.see_also, [])
68
- self.assertEqual(rfc.errata_url, "http://www.rfc-editor.org/errata_search.php?rfc=3550")
68
+ self.assertEqual(rfc.errata_url, "https://www.rfc-editor.org/errata/rfc3550")
69
69
  self.assertEqual(rfc.charset(), "utf-8")
70
70
  self.assertEqual(rfc.content_url("ASCII"), "https://www.rfc-editor.org/rfc/rfc3550.txt")
71
71
  self.assertEqual(rfc.content_url("PS"), "https://www.rfc-editor.org/rfc/rfc3550.ps")
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes