Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions ietf/doc/tests_ballot.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@


import datetime
from unittest import mock

from pyquery import PyQuery

Expand Down Expand Up @@ -716,11 +715,8 @@ def verify_can_see(username, url):
verify_can_see(username, url)

class ApproveBallotTests(TestCase):
@mock.patch('ietf.sync.rfceditor.requests.post', autospec=True)
def test_approve_ballot(self, mock_urlopen):
mock_urlopen.return_value.text = b'OK'
mock_urlopen.return_value.status_code = 200
#
def test_approve_ballot(self):

ad = Person.objects.get(name="Areað Irector")
draft = IndividualDraftFactory(ad=ad, intended_std_level_id='ps')
draft.set_state(State.objects.get(used=True, type="draft-iesg", slug="iesg-eva")) # make sure it's approvable
Expand Down
8 changes: 2 additions & 6 deletions ietf/doc/tests_draft.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import os
import datetime
import io
from unittest import mock

from collections import Counter
from pathlib import Path
Expand Down Expand Up @@ -1549,11 +1548,8 @@ def test_confirm_submission_no_doc_ad(self):


class RequestPublicationTests(TestCase):
@mock.patch('ietf.sync.rfceditor.requests.post', autospec=True)
def test_request_publication(self, mockobj):
mockobj.return_value.text = b'OK'
mockobj.return_value.status_code = 200
#
def test_request_publication(self):

draft = IndividualDraftFactory(stream_id='iab',group__acronym='iab',intended_std_level_id='inf',states=[('draft-stream-iab','approved')])

url = urlreverse('ietf.doc.views_draft.request_publication', kwargs=dict(name=draft.name))
Expand Down
10 changes: 0 additions & 10 deletions ietf/doc/views_ballot.py
Original file line number Diff line number Diff line change
Expand Up @@ -939,16 +939,6 @@ def approve_ballot(request, name):
if ballot_writeup_event.pk == None:
ballot_writeup_event.save()

if new_state.slug == "ann" and new_state.slug != prev_state.slug:
# start by notifying the RFC Editor
import ietf.sync.rfceditor
response, error = ietf.sync.rfceditor.post_approved_draft(settings.RFC_EDITOR_SYNC_NOTIFICATION_URL, doc.name)
if error:
return render(request, 'doc/draft/rfceditor_post_approved_draft_failed.html',
dict(name=doc.name,
response=response,
error=error))

doc.set_state(new_state)
doc.tags.remove(*prev_tags)

Expand Down
9 changes: 0 additions & 9 deletions ietf/doc/views_draft.py
Original file line number Diff line number Diff line change
Expand Up @@ -1276,15 +1276,6 @@ class PublicationForm(forms.Form):
if form.is_valid():
events = []

# start by notifying the RFC Editor
import ietf.sync.rfceditor
response, error = ietf.sync.rfceditor.post_approved_draft(settings.RFC_EDITOR_SYNC_NOTIFICATION_URL, doc.name)
if error:
return render(request, 'doc/draft/rfceditor_post_approved_draft_failed.html',
dict(name=doc.name,
response=response,
error=error))

m.subject = form.cleaned_data["subject"]
m.body = form.cleaned_data["body"]
m.save()
Expand Down
3 changes: 0 additions & 3 deletions ietf/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -893,10 +893,7 @@ def skip_unreadable_post(record):
IANA_SYNC_CHANGES_URL = "https://datatracker.iana.org:4443/data-tracker/changes"
IANA_SYNC_PROTOCOLS_URL = "https://www.iana.org/protocols/"

RFC_EDITOR_SYNC_PASSWORD="secret"
RFC_EDITOR_SYNC_NOTIFICATION_URL = "https://www.rfc-editor.org/parser/parser.php"
RFC_EDITOR_GROUP_NOTIFICATION_EMAIL = "webmaster@rfc-editor.org"
#RFC_EDITOR_GROUP_NOTIFICATION_URL = "https://www.rfc-editor.org/notification/group.php"
RFC_EDITOR_QUEUE_URL = "https://www.rfc-editor.org/queue2.xml"
RFC_EDITOR_INDEX_URL = "https://www.rfc-editor.org/rfc/rfc-index.xml"
RFC_EDITOR_ERRATA_JSON_URL = "https://www.rfc-editor.org/errata.json"
Expand Down
31 changes: 27 additions & 4 deletions ietf/stats/tests.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
# Copyright The IETF Trust 2016-2020, All Rights Reserved
# -*- coding: utf-8 -*-

# Copyright The IETF Trust 2016-2026, All Rights Reserved

import calendar
import json
import datetime

from django.http import Http404
from pyquery import PyQuery

import debug # pyflakes:ignore

from django.test import RequestFactory
from django.urls import reverse as urlreverse
from django.utils import timezone

from ietf.meeting.models import Meeting
from ietf.utils.test_utils import login_testing_unauthorized, TestCase
import ietf.stats.views

Expand Down Expand Up @@ -87,7 +88,29 @@ def test_meeting_stats(self):
self.assertContains(r, "/stats/meeting/124/country")
self.assertContains(r, "/stats/meeting/125/country")
self.assertContains(r, "This page provides a timeline of meeting registrations.")


def test_meeting_stats_for_bad_meeting(self):
self.assertFalse(Meeting.objects.filter(number=676767).exists())
for stats_type in ["affiliation", "country"]:
r = self.client.get(
urlreverse(
"ietf.stats.views.meeting_stats",
kwargs={"meeting_number": 676767, "stats_type": stats_type},
)
)
self.assertEqual(r.status_code, 404)

# We don't have a URL for an interim, but make sure the view will 404 if
# somehow a non-interim gets selected...
interim_num = MeetingFactory(type_id="interim").number
request_factory = RequestFactory()
with self.assertRaises(Http404):
ietf.stats.views.meeting_stats(
request_factory.get(f"/stats/meeting/{interim_num}/{stats_type}"),
meeting_number=interim_num,
stats_type=stats_type,
)

def test_known_country_list(self):
# check redirect
url = urlreverse(ietf.stats.views.known_countries_list)
Expand Down
18 changes: 9 additions & 9 deletions ietf/stats/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from django.contrib.auth.decorators import login_required
from django.core.cache import cache
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.shortcuts import render, get_object_or_404
from django.urls import reverse as urlreverse
from django.db.models import Count

Expand All @@ -27,11 +27,11 @@
from ietf.group.models import Role, Group
from ietf.person.models import Person
from ietf.name.models import ReviewResultName, CountryName, ReviewAssignmentStateName
from ietf.meeting.models import Registration
from ietf.meeting.models import Registration, Meeting
from ietf.ietfauth.utils import has_role
from ietf.utils.response import permission_denied
from ietf.utils.timezone import date_today, DEADLINE_TZINFO
from ietf.meeting.helpers import get_current_ietf_meeting_num, get_ietf_meeting
from ietf.meeting.helpers import get_current_ietf_meeting_num

# Color palette for lines
colors = [
Expand Down Expand Up @@ -568,12 +568,12 @@ def meeting_stats(request, meeting_number=None, stats_type='country'):
Returns:
Rendered response for the meeting stats template.
"""

current_meeting = get_current_ietf_meeting_num()
current_meeting_number = get_current_ietf_meeting_num()
if meeting_number is None:
meeting_number = current_meeting

this_meeting = get_ietf_meeting(meeting_number)
meeting_number = current_meeting_number
this_meeting = get_object_or_404(
Meeting.objects.filter(type_id="ietf"), number=meeting_number
)

if stats_type == 'affiliation':
minimum_required = 4
Expand Down Expand Up @@ -616,7 +616,7 @@ def meeting_stats(request, meeting_number=None, stats_type='country'):
if int(meeting_number) > 72: # No registration data before IETF-72
possible_meeting_numbers.append((int(meeting_number)-1, urlreverse(meeting_stats, kwargs={'meeting_number': int(meeting_number)-1, 'stats_type': stats_type})))
possible_meeting_numbers.append((meeting_number, urlreverse(meeting_stats, kwargs={'meeting_number': meeting_number, 'stats_type': stats_type})))
if int(meeting_number) <= int(current_meeting): # Allow current meeting +1
if int(meeting_number) <= int(current_meeting_number): # Allow current meeting +1
possible_meeting_numbers.append((int(meeting_number)+1, urlreverse(meeting_stats, kwargs={'meeting_number': int(meeting_number)+1, 'stats_type': stats_type})))

return render(request, "stats/meeting_stats.html", {
Expand Down
51 changes: 0 additions & 51 deletions ietf/sync/rfceditor.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,15 @@
# -*- coding: utf-8 -*-


import base64
import datetime
import re
import requests

from typing import Iterator, Optional, Union
from urllib.parse import urlencode
from xml.dom import pulldom, Node

from django.conf import settings
from django.db import transaction
from django.db.models import Subquery, OuterRef, F, Q
from django.utils import timezone
from django.utils.encoding import smart_bytes, force_str

import debug # pyflakes:ignore

Expand Down Expand Up @@ -847,50 +842,4 @@ def parse_relation_list(rel_list: list[str]) -> list[Document]:
).update(document=F("subseries_target"))


def post_approved_draft(url, name):
"""Post an approved draft to the RFC Editor so they can retrieve
the data from the Datatracker and start processing it. Returns
response and error (empty string if no error)."""

if settings.SERVER_MODE != "production":
log(f"In production, would have posted RFC-Editor notification of approved I-D '{name}' to '{url}'")
return "", ""

# HTTP basic auth
username = "dtracksync"
password = settings.RFC_EDITOR_SYNC_PASSWORD
headers = {
"Content-type": "application/x-www-form-urlencoded",
"Accept": "text/plain",
"Authorization": "Basic %s" % force_str(base64.encodebytes(smart_bytes("%s:%s" % (username, password)))).replace("\n", ""),
}

log("Posting RFC-Editor notification of approved Internet-Draft '%s' to '%s'" % (name, url))
text = error = ""

try:
r = requests.post(
url,
headers=headers,
data=smart_bytes(urlencode({ 'draft': name })),
timeout=settings.DEFAULT_REQUESTS_TIMEOUT,
)

log("RFC-Editor notification result for Internet-Draft '%s': %s:'%s'" % (name, r.status_code, r.text))

if r.status_code != 200:
raise RuntimeError("Status code is not 200 OK (it's %s)." % r.status_code)

if force_str(r.text) != "OK":
raise RuntimeError('Response is not "OK" (it\'s "%s").' % r.text)

except Exception as e:
# catch everything so we don't leak exceptions, convert them
# into string instead
msg = "Exception on RFC-Editor notification for Internet-Draft '%s': %s: %s" % (name, type(e), str(e))
log(msg)
if settings.SERVER_MODE == 'test':
debug.say(msg)
error = str(e)

return text, error
24 changes: 0 additions & 24 deletions ietf/sync/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -795,30 +795,6 @@ def test_update_draft_auth48_url(self):
auth48_docurl = draft.documenturl_set.filter(tag_id='auth48').first()
self.assertIsNone(auth48_docurl)

def test_post_approved_draft_in_production_only(self):
self.requests_mock.post("https://rfceditor.example.com/", status_code=200, text="OK")

# be careful playing with SERVER_MODE!
with override_settings(SERVER_MODE="test"):
self.assertEqual(
rfceditor.post_approved_draft("https://rfceditor.example.com/", "some-draft"),
("", "")
)
self.assertFalse(self.requests_mock.called)
with override_settings(SERVER_MODE="development"):
self.assertEqual(
rfceditor.post_approved_draft("https://rfceditor.example.com/", "some-draft"),
("", "")
)
self.assertFalse(self.requests_mock.called)
with override_settings(SERVER_MODE="production"):
self.assertEqual(
rfceditor.post_approved_draft("https://rfceditor.example.com/", "some-draft"),
("", "")
)
self.assertTrue(self.requests_mock.called)


class DiscrepanciesTests(TestCase):
def test_discrepancies(self):

Expand Down
26 changes: 0 additions & 26 deletions ietf/templates/doc/draft/rfceditor_post_approved_draft_failed.html

This file was deleted.

9 changes: 6 additions & 3 deletions ietf/utils/searchindex.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,10 @@ def typesense_doc_from_rfc(rfc: Document) -> DocumentSchema:
)
subseries = subseries[0] if len(subseries) > 0 else None
obsoleted_by = rfc.related_that("obs")
is_obsoleted = len(obsoleted_by) > 0
updated_by = rfc.related_that("updates")
is_updated = len(updated_by) > 0
is_historic = rfc.std_level.slug == "hist"

stored_txt = (
StoredObject.objects.exclude_deleted()
Expand Down Expand Up @@ -134,9 +137,9 @@ def typesense_doc_from_rfc(rfc: Document) -> DocumentSchema:
for rfc_author in rfc.rfcauthor_set.all()
],
"flags": {
"hiddenDefault": False,
"obsoleted": len(obsoleted_by) > 0,
"updated": len(updated_by) > 0,
"hiddenDefault": is_obsoleted or is_historic,
"obsoleted": is_obsoleted,
"updated": is_updated,
},
"obsoletedBy": [str(doc.rfc_number) for doc in obsoleted_by],
"updatedBy": [str(doc.rfc_number) for doc in updated_by],
Expand Down
Loading
Loading