From 85d2cdddc20d7c0b0abaae273810dcc115bc3ca3 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Mon, 17 Sep 2012 15:57:48 +0000 Subject: [PATCH] Add POST interface for IANA/RFC Editor for triggering updates, add discrepancies report for seeing differences between the Datatracker/RFC Editor/IANA, add tests of IANA/RFC Editor integration, add script for weekly discrepancies emails - Legacy-Id: 4851 --- ietf/bin/email-sync-discrepancies | 35 ++ ietf/sync/discrepancies.py | 37 ++ ietf/sync/mails.py | 21 + ietf/sync/tests.py | 401 +++++++++++++++++++ ietf/sync/urls.py | 8 + ietf/sync/views.py | 79 ++++ ietf/templates/sync/discrepancies.html | 37 ++ ietf/templates/sync/discrepancies_report.txt | 18 + ietf/templates/sync/update.html | 27 ++ ietf/urls.py | 1 + ietf/utils/test_data.py | 52 +++ 11 files changed, 716 insertions(+) create mode 100755 ietf/bin/email-sync-discrepancies create mode 100644 ietf/sync/discrepancies.py create mode 100644 ietf/sync/mails.py create mode 100644 ietf/sync/tests.py create mode 100644 ietf/sync/urls.py create mode 100644 ietf/sync/views.py create mode 100644 ietf/templates/sync/discrepancies.html create mode 100644 ietf/templates/sync/discrepancies_report.txt create mode 100644 ietf/templates/sync/update.html diff --git a/ietf/bin/email-sync-discrepancies b/ietf/bin/email-sync-discrepancies new file mode 100755 index 000000000..8f01fcbcf --- /dev/null +++ b/ietf/bin/email-sync-discrepancies @@ -0,0 +1,35 @@ +#!/usr/bin/env python + +import sys, os +import syslog + +# boilerplate +basedir = os.path.abspath(os.path.join(os.path.dirname(__file__), "../..")) +sys.path = [ basedir ] + sys.path + +from ietf import settings +from django.core import management +management.setup_environ(settings) + + +from optparse import OptionParser + +parser = OptionParser() +parser.add_option("-t", "--to", dest="to", + help="Email address to send report to", metavar="EMAIL") + +options, args = parser.parse_args() + + +syslog.openlog(os.path.basename(__file__), syslog.LOG_PID, syslog.LOG_LOCAL0) + +from ietf.sync.mails import email_discrepancies + +receivers = ["iesg-secretary@ietf.org"] + +if options.to: + receivers = [options.to] + +email_discrepancies(receivers) + +syslog.syslog("Emailed sync discrepancies to %s" % receivers) diff --git a/ietf/sync/discrepancies.py b/ietf/sync/discrepancies.py new file mode 100644 index 000000000..9c0db606d --- /dev/null +++ b/ietf/sync/discrepancies.py @@ -0,0 +1,37 @@ +from ietf.doc.models import * + +def find_discrepancies(): + res = [] + + title = "Drafts that have been sent to the RFC Editor but do not have an RFC Editor state" + + docs = Document.objects.filter(states__in=list(State.objects.filter(type="draft-iesg", slug__in=("ann", "rfcqueue")))).exclude(states__in=list(State.objects.filter(type="draft-rfceditor"))) + + res.append((title, docs)) + + title = "Drafts that have the IANA Action state \"In Progress\" but do not have a \"IANA\" RFC-Editor state/tag" + + docs = Document.objects.filter(states__in=list(State.objects.filter(type="draft-iana-action", slug__in=("inprog",)))).exclude(tags="iana").exclude(states__in=list(State.objects.filter(type="draft-rfceditor", slug="iana"))) + + res.append((title, docs)) + + title = "Drafts that have the IANA Action state \"Waiting on RFC Editor\" or \"RFC-Ed-Ack\" but are in the RFC Editor state \"IANA\"/tagged with \"IANA\"" + + docs = Document.objects.filter(states__in=list(State.objects.filter(type="draft-iana-action", slug__in=("waitrfc", "rfcedack")))).filter(models.Q(tags="iana") | models.Q(states__in=list(State.objects.filter(type="draft-rfceditor", slug="iana")))) + + res.append((title, docs)) + + title = "Drafts that have a state other than \"RFC Ed Queue\", \"RFC Published\" or \"Sent to the RFC Editor\" and have an RFC Editor or IANA Action state" + + docs = Document.objects.exclude(states__in=list(State.objects.filter(type="draft-iesg", slug__in=("rfcqueue", "pub"))) + list(State.objects.filter(type__in=("draft-stream-iab", "draft-stream-ise", "draft-stream-irtf"), slug="rfc-edit"))).filter(states__in=list(State.objects.filter(type__in=("draft-iana-action", "draft-rfceditor")))) + + res.append((title, docs)) + + for _, docs in res: + for d in docs: + d.iesg_state = d.get_state("draft-iesg") + d.rfc_state = d.get_state("draft-rfceditor") + d.iana_action_state = d.get_state("draft-iana-action") + + return res + diff --git a/ietf/sync/mails.py b/ietf/sync/mails.py new file mode 100644 index 000000000..480fd1b40 --- /dev/null +++ b/ietf/sync/mails.py @@ -0,0 +1,21 @@ +from django.core.urlresolvers import reverse as urlreverse +from django.conf import settings + +from ietf.utils.mail import send_mail + +from ietf.sync.discrepancies import find_discrepancies + +def email_discrepancies(receivers): + sections = find_discrepancies() + + send_mail(None, + receivers, + None, + "Datatracker Sync Discrepancies Report", + "sync/discrepancies_report.txt", + dict(sections=sections, + url=settings.IDTRACKER_BASE_URL + urlreverse("ietf.sync.views.discrepancies"), + base_url=settings.IDTRACKER_BASE_URL, + )) + + diff --git a/ietf/sync/tests.py b/ietf/sync/tests.py new file mode 100644 index 000000000..8aa97da85 --- /dev/null +++ b/ietf/sync/tests.py @@ -0,0 +1,401 @@ +import unittest, re, json, datetime, StringIO +import django.test +from django.conf import settings +from django.core.urlresolvers import reverse as urlreverse + +from ietf.utils.mail import outbox +from ietf.utils.test_data import make_test_data +from ietf.utils.test_utils import login_testing_unauthorized + +from ietf.doc.models import * +from ietf.person.models import * + +from ietf.sync import iana, rfceditor + +from pyquery import PyQuery + +class IANASyncTestCase(django.test.TestCase): + fixtures = ['names'] + + def test_protocol_page_sync(self): + draft = make_test_data() + DocAlias.objects.create(name="rfc1234", document=draft) + DocEvent.objects.create(doc=draft, type="published_rfc", by=Person.objects.get(name="(System)")) + + rfc_names = iana.parse_protocol_page('RFC 1234') + self.assertEqual(len(rfc_names), 1) + self.assertEqual(rfc_names[0], "rfc1234") + + iana.update_rfc_log_from_protocol_page(rfc_names, datetime.datetime.now() - datetime.timedelta(days=1)) + self.assertEqual(DocEvent.objects.filter(doc=draft, type="rfc_in_iana_registry").count(), 1) + + # make sure it doesn't create duplicates + iana.update_rfc_log_from_protocol_page(rfc_names, datetime.datetime.now() - datetime.timedelta(days=1)) + self.assertEqual(DocEvent.objects.filter(doc=draft, type="rfc_in_iana_registry").count(), 1) + + def test_changes_sync(self): + draft = make_test_data() + + data = json.dumps({ + "changes": [ + { + "time": "2011-10-09 12:00:01", + "doc": draft.name, + "state": "IANA Not OK", + "type": "iana_review", + }, + { + "time": "2011-10-09 12:00:00", + "doc": draft.name, + "state": "Waiting on RFC-Editor", + "type": "iana_state", + }, + { + "time": "2011-10-09 11:00:00", + "doc": draft.name, + "state": "In Progress", + "type": "iana_state", + } + ] + }) + + changes = iana.parse_changes_json(data) + # check sorting + self.assertEqual(changes[0]["time"], "2011-10-09 11:00:00") + + mailbox_before = len(outbox) + added_events, warnings = iana.update_history_with_changes(changes) + + self.assertEqual(len(added_events), 3) + self.assertEqual(len(warnings), 0) + self.assertEqual(draft.get_state_slug("draft-iana-review"), "not-ok") + self.assertEqual(draft.get_state_slug("draft-iana-action"), "waitrfc") + e = draft.latest_event(StateDocEvent, type="changed_state", state_type="draft-iana-action") + self.assertEqual(e.desc, "IANA Action state changed to Waiting on RFC Editor from In Progress") + self.assertEqual(e.time, datetime.datetime(2011, 10, 9, 5, 0)) # check timezone handling + self.assertEqual(len(outbox), mailbox_before + 3 * 2) + + # make sure it doesn't create duplicates + added_events, warnings = iana.update_history_with_changes(changes) + self.assertEqual(len(added_events), 0) + self.assertEqual(len(warnings), 0) + + def test_changes_sync_errors(self): + draft = make_test_data() + + # missing "type" + data = json.dumps({ + "changes": [ + { + "time": "2011-10-09 12:00:01", + "doc": draft.name, + "state": "IANA Not OK", + }, + ] + }) + + self.assertRaises(Exception, iana.parse_changes_json, data) + + # error response + data = json.dumps({ + "error": "I am in error." + }) + + self.assertRaises(Exception, iana.parse_changes_json, data) + + # missing document from database + data = json.dumps({ + "changes": [ + { + "time": "2011-10-09 12:00:01", + "doc": "draft-this-does-not-exist", + "state": "IANA Not OK", + "type": "iana_review", + }, + ] + }) + + changes = iana.parse_changes_json(data) + added_events, warnings = iana.update_history_with_changes(changes) + self.assertEqual(len(added_events), 0) + self.assertEqual(len(warnings), 1) + + def test_iana_review_mail(self): + draft = make_test_data() + + msg = """From: "%(person)s via RT" +Date: Thu, 10 May 2012 12:00:00 +0000 +Subject: [IANA #12345] Last Call: <%(draft)s-%(rev)s.txt> (Long text) to Informational RFC +(BEGIN IANA LAST CALL COMMENTS) + +IESG: + +IANA has reviewed %(draft)s-%(rev)s, which is=20 +currently in Last Call, and has the following comments: + +IANA understands that, upon approval of this document, there are no=20 +IANA Actions that need completion. + +Thanks, + +%(person)s +IANA Fake Test Person +ICANN + +(END IANA LAST CALL COMMENTS) +""" + + msg = msg % dict(person=Person.objects.get(user__username="iana").name, + draft=draft.name, + rev=draft.rev) + + doc_name, review_time, by, comment = iana.parse_review_email(msg) + + self.assertEqual(doc_name, draft.name) + self.assertEqual(review_time, datetime.datetime(2012, 5, 10, 5, 0, 0)) + self.assertEqual(by, Person.objects.get(user__username="iana")) + self.assertTrue("there are no IANA Actions" in comment.replace("\n", "")) + + iana.add_review_comment(doc_name, review_time, by, comment) + + e = draft.latest_event(type="iana_review") + self.assertTrue(e) + self.assertEqual(e.desc, comment) + self.assertEqual(e.by, by) + + # make sure it doesn't create duplicates + iana.add_review_comment(doc_name, review_time, by, comment) + self.assertEqual(DocEvent.objects.filter(doc=draft, type="iana_review").count(), 1) + + +class RFCSyncTestCase(django.test.TestCase): + fixtures = ['names'] + + def test_rfc_index(self): + doc = make_test_data() + doc.set_state(State.objects.get(type="draft-iesg", slug="rfcqueue")) + # it's a bit strange to have this set when draft-iesg is set + # too, but for testing purposes ... + doc.set_state(State.objects.get(type="draft-stream-ise", slug="rfc-edit")) + + updated_doc = Document.objects.create(name="draft-ietf-something") + DocAlias.objects.create(name=updated_doc.name, document=updated_doc) + DocAlias.objects.create(name="rfc123", document=updated_doc) + + today = datetime.date.today() + + t = ''' + + + BCP0001 + + RFC1234 + RFC2345 + + + + FYI0001 + + RFC1234 + + + + STD0001 + Test + + RFC1234 + + + + RFC1234 + A Testing RFC + + A. Irector + + + %(month)s + %(year)s + + + ASCII + 12345 + 42 + + + test + +

This is some interesting text.

+ %(name)s-%(rev)s + + RFC123 + + + BCP0001 + + PROPOSED STANDARD + PROPOSED STANDARD + IETF + %(area)s + %(group)s + http://www.rfc-editor.org/errata_search.php?rfc=1234 +
+
''' % dict(year=today.strftime("%Y"), + month=today.strftime("%B"), + name=doc.name, + rev=doc.rev, + area=doc.group.parent.acronym, + group=doc.group.acronym) + + data = rfceditor.parse_index(StringIO.StringIO(t)) + self.assertEqual(len(data), 1) + + rfc_number, title, authors, rfc_published_date, current_status, updates, updated_by, obsoletes, obsoleted_by, also, draft, has_errata, stream, wg, file_formats, pages, abstract = data[0] + + # currently, we only check what we actually use + self.assertEqual(rfc_number, 1234) + self.assertEqual(title, "A Testing RFC") + self.assertEqual(rfc_published_date.year, today.year) + self.assertEqual(rfc_published_date.month, today.month) + self.assertEqual(current_status, "Proposed Standard") + self.assertEqual(updates, ["RFC123"]) + self.assertEqual(set(also), set(["BCP0001", "FYI0001", "STD0001"])) + self.assertEqual(draft, doc.name) + self.assertEqual(wg, doc.group.acronym) + self.assertEqual(has_errata, True) + self.assertEqual(stream, "IETF") + self.assertEqual(pages, "42") + self.assertEqual(abstract, "This is some interesting text.") + + + mailbox_before = len(outbox) + + changed = rfceditor.update_docs_from_rfc_index(data, today - datetime.timedelta(days=30)) + + doc = Document.objects.get(name=doc.name) + + self.assertEqual(doc.docevent_set.all()[0].type, "published_rfc") + self.assertEqual(doc.docevent_set.all()[0].time.date(), today) + self.assertTrue("errata" in doc.tags.all().values_list("slug", flat=True)) + self.assertTrue(DocAlias.objects.filter(name="rfc1234", document=doc)) + self.assertTrue(DocAlias.objects.filter(name="bcp0001", document=doc)) + self.assertTrue(DocAlias.objects.filter(name="fyi0001", document=doc)) + self.assertTrue(DocAlias.objects.filter(name="std0001", document=doc)) + self.assertTrue(RelatedDocument.objects.filter(source=doc, target__name="rfc123", relationship="updates")) + self.assertEqual(doc.title, "A Testing RFC") + self.assertEqual(doc.abstract, "This is some interesting text.") + self.assertEqual(doc.get_state_slug(), "rfc") + self.assertEqual(doc.get_state_slug("draft-iesg"), "pub") + self.assertEqual(doc.get_state_slug("draft-stream-ise"), "pub") + self.assertEqual(doc.std_level_id, "ps") + self.assertEqual(doc.pages, 42) + + # make sure we can apply it again with no changes + changed = rfceditor.update_docs_from_rfc_index(data, today - datetime.timedelta(days=30)) + self.assertEquals(len(changed), 0) + + + def test_rfc_queue(self): + draft = make_test_data() + + draft.set_state(State.objects.get(type="draft-iesg", slug="ann")) + + t = ''' +
+ +%(name)s-%(rev)s.txt +2010-09-08 +EDIT*R*A(1G) +http://www.rfc-editor.org/auth48/rfc1234 + +%(ref)s +IN-QUEUE + +A. Author + +%(title)s + +10000000 +%(group)s + +
+
''' % dict(name=draft.name, + rev=draft.rev, + title=draft.title, + group=draft.group.name, + ref="draft-ietf-test") + + drafts, warnings = rfceditor.parse_queue(StringIO.StringIO(t)) + self.assertEqual(len(drafts), 1) + self.assertEqual(len(warnings), 0) + + draft_name, date_received, state, tags, missref_generation, stream, auth48, cluster, refs = drafts[0] + + # currently, we only check what we actually use + self.assertEqual(draft_name, draft.name) + self.assertEqual(state, "EDIT") + self.assertEqual(set(tags), set(["iana", "ref"])) + self.assertEqual(auth48, "http://www.rfc-editor.org/auth48/rfc1234") + + + mailbox_before = len(outbox) + + changed, warnings = rfceditor.update_drafts_from_queue(drafts) + self.assertEqual(len(changed), 1) + self.assertEqual(len(warnings), 0) + + self.assertEqual(draft.get_state_slug("draft-rfceditor"), "edit") + self.assertEqual(set(draft.tags.all()), set(DocTagName.objects.filter(slug__in=("iana", "ref")))) + self.assertEqual(draft.docevent_set.all()[0].type, "changed_state") + self.assertEqual(draft.docevent_set.all()[1].type, "rfc_editor_received_announcement") + + self.assertEqual(len(outbox), mailbox_before + 1) + self.assertTrue("RFC Editor queue" in outbox[-1]["Subject"]) + + # make sure we can apply it again with no changes + changed, warnings = rfceditor.update_drafts_from_queue(drafts) + self.assertEquals(len(changed), 0) + self.assertEquals(len(warnings), 0) + +class DiscrepanciesTestCase(django.test.TestCase): + fixtures = ['names'] + + def test_discrepancies(self): + make_test_data() + + # draft approved but no RFC Editor state + doc = Document.objects.create(name="draft-ietf-test1", type_id="draft") + doc.set_state(State.objects.get(type="draft-iesg", slug="ann")) + + r = self.client.get(urlreverse("ietf.sync.views.discrepancies")) + self.assertTrue(doc.name in r.content) + + # draft with IANA state "In Progress" but RFC Editor state not IANA + doc = Document.objects.create(name="draft-ietf-test2", type_id="draft") + doc.set_state(State.objects.get(type="draft-iesg", slug="rfcqueue")) + doc.set_state(State.objects.get(type="draft-iana-action", slug="inprog")) + doc.set_state(State.objects.get(type="draft-rfceditor", slug="auth")) + + r = self.client.get(urlreverse("ietf.sync.views.discrepancies")) + self.assertTrue(doc.name in r.content) + + # draft with IANA state "Waiting on RFC Editor" or "RFC-Ed-Ack" + # but RFC Editor state is IANA + doc = Document.objects.create(name="draft-ietf-test3", type_id="draft") + doc.set_state(State.objects.get(type="draft-iesg", slug="rfcqueue")) + doc.set_state(State.objects.get(type="draft-iana-action", slug="waitrfc")) + doc.set_state(State.objects.get(type="draft-rfceditor", slug="iana")) + + r = self.client.get(urlreverse("ietf.sync.views.discrepancies")) + self.assertTrue(doc.name in r.content) + + # draft with state other than "RFC Ed Queue" or "RFC Published" + # that are in RFC Editor or IANA queues + doc = Document.objects.create(name="draft-ietf-test4", type_id="draft") + doc.set_state(State.objects.get(type="draft-iesg", slug="ann")) + doc.set_state(State.objects.get(type="draft-rfceditor", slug="auth")) + + r = self.client.get(urlreverse("ietf.sync.views.discrepancies")) + self.assertTrue(doc.name in r.content) diff --git a/ietf/sync/urls.py b/ietf/sync/urls.py new file mode 100644 index 000000000..f99cc76c1 --- /dev/null +++ b/ietf/sync/urls.py @@ -0,0 +1,8 @@ +from django.conf.urls.defaults import patterns, url + +urlpatterns = patterns('', + url(r'^discrepancies/$', 'ietf.sync.views.discrepancies'), + url(r'^iana/update/$', 'ietf.sync.views.update_iana'), + url(r'^rfc-editor/update/$', 'ietf.sync.views.update_rfc_editor'), +) + diff --git a/ietf/sync/views.py b/ietf/sync/views.py new file mode 100644 index 000000000..c6b9c7fc1 --- /dev/null +++ b/ietf/sync/views.py @@ -0,0 +1,79 @@ +import subprocess, os + +from django.http import HttpResponse +from django.shortcuts import render_to_response +from django.template import RequestContext +from django.template.loader import render_to_string +from django import forms +from django.db.models import Q + +from ietf.ietfauth.decorators import role_required +from ietf.doc.models import * +from ietf.sync import iana, rfceditor +from ietf.sync.discrepancies import find_discrepancies + +SYNC_BIN_PATH = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../bin")) + +#@role_required('Secretariat', 'IANA', 'RFC Editor') +def discrepancies(request): + sections = find_discrepancies() + + return render_to_response("sync/discrepancies.html", + dict(sections=sections), + context_instance=RequestContext(request)) + + +class UpdateIanaForm(forms.Form): + protocols_page = forms.BooleanField(initial=False, required=False, help_text="For when a reference to an RFC has been added to the IANA protocols page" % iana.PROTOCOLS_URL) + changes = forms.BooleanField(initial=False, required=False, help_text="For new changes at the changes JSON dump" % iana.CHANGES_URL) + +def update_iana(request): + if request.method == 'POST': + form = UpdateIanaForm(request.POST) + if form.is_valid(): + failed = False + if form.cleaned_data["protocols_page"]: + failed = failed or subprocess.call(["python", os.path.join(SYNC_BIN_PATH, "iana-protocols-updates")]) + if form.cleaned_data["changes"]: + failed = failed or subprocess.call(["python", os.path.join(SYNC_BIN_PATH, "iana-changes-updates")]) + + if failed: + return HttpResponse("FAIL") + else: + return HttpResponse("OK") + else: + form = UpdateIanaForm() + + return render_to_response('sync/update.html', + dict(form=form, + org="IANA", + ), + context_instance=RequestContext(request)) + + +class UpdateRFCEditorForm(forms.Form): + queue = forms.BooleanField(initial=False, required=False, help_text="For when queue2.xml has been updated" % rfceditor.QUEUE_URL) + index = forms.BooleanField(initial=False, required=False, help_text="For when rfc-index.xml has been updated" % rfceditor.INDEX_URL) + +def update_rfc_editor(request): + if request.method == 'POST': + form = UpdateRFCEditorForm(request.POST) + if form.is_valid(): + failed = False + if form.cleaned_data["queue"]: + failed = failed or subprocess.call(["python", os.path.join(SYNC_BIN_PATH, "rfc-editor-queue-updates")]) + if form.cleaned_data["index"]: + failed = failed or subprocess.call(["python", os.path.join(SYNC_BIN_PATH, "rfc-editor-index-updates")]) + + if failed: + return HttpResponse("FAIL") + else: + return HttpResponse("OK") + else: + form = UpdateRFCEditorForm() + + return render_to_response('sync/update.html', + dict(form=form, + org="RFC Editor", + ), + context_instance=RequestContext(request)) diff --git a/ietf/templates/sync/discrepancies.html b/ietf/templates/sync/discrepancies.html new file mode 100644 index 000000000..fdf966e0e --- /dev/null +++ b/ietf/templates/sync/discrepancies.html @@ -0,0 +1,37 @@ +{% extends "base.html" %} + +{% block title %}Sync discrepancies{% endblock %} + +{% block morecss %} +table.discrepancies td { padding-right: 0.2em; } +{% endblock %} + +{% block content %} +

Sync discrepancies

+ +{% for title, docs in sections %} +

{{ title }}

+ +{% if docs %} + + + + + + + +{% for d in docs %} + + + + + + +{% endfor %} +
Draft NameIESG stateRFC Editor stateIANA Action state
{{ d.name }}{{ d.iesg_state|default:"-" }}{{ d.rfc_state|default:"-" }}{{ d.iana_action_state|default:"-" }}
+{% else %} +

None found.

+{% endif %} + +{% endfor %} +{% endblock %} diff --git a/ietf/templates/sync/discrepancies_report.txt b/ietf/templates/sync/discrepancies_report.txt new file mode 100644 index 000000000..0fb324668 --- /dev/null +++ b/ietf/templates/sync/discrepancies_report.txt @@ -0,0 +1,18 @@ +{% autoescape off %} +This is an automated report of current Datatracker sync discrepancies, +also available at: + +{{ url }} +{% for title, docs in sections %} + +{{ title|wordwrap:73 }} +{% if docs %}{% for d in docs %} + {{ d.name }} + IESG: {{ d.iesg_state|default:"-" }} + RFC Ed: {{ d.rfc_state|default:"-" }} + IANA: {{ d.iana_action_state|default:"-" }} + {{ base_url }}{{ d.get_absolute_url }} +{% endfor %}{% else %} +None found. +{% endif %}{% endfor %} +{% endautoescape %} diff --git a/ietf/templates/sync/update.html b/ietf/templates/sync/update.html new file mode 100644 index 000000000..86b6a73f8 --- /dev/null +++ b/ietf/templates/sync/update.html @@ -0,0 +1,27 @@ +{% extends "base.html" %} + +{% block title %}Trigger sync for {{ org }}{% endblock %} + +{% block morecss %} +.sync-form .help { font-style: italic; padding-left: 2em; } +.sync-form input[type=submit] { margin-top: 1em; } +{% endblock %} + + +{% block content %} +

Trigger sync for {{ org }}

+ +

Update the Datatracker with information from {{ org }}. Select +which parts to trigger a sync for:

+ +
+{% for field in form %} +
+ {{ field }} + {{ field.label_tag }} + {% if field.help_text %}{{ field.help_text|safe }}{% endif %} +
+{% endfor %} + +
+{% endblock %} diff --git a/ietf/urls.py b/ietf/urls.py index 3edfa9388..0f2bce922 100644 --- a/ietf/urls.py +++ b/ietf/urls.py @@ -66,6 +66,7 @@ urlpatterns = patterns('', (r'^submit/', include('ietf.submit.urls')), (r'^streams/', include('ietf.ietfworkflows.urls')), (r'^community/', include('ietf.community.urls')), + (r'^sync/', include('ietf.sync.urls')), (r'^$', 'ietf.idrfc.views.main'), (r'^admin/doc/', include('django.contrib.admindocs.urls')), diff --git a/ietf/utils/test_data.py b/ietf/utils/test_data.py index 7e22f88cf..1c8fd9bfe 100644 --- a/ietf/utils/test_data.py +++ b/ietf/utils/test_data.py @@ -114,6 +114,22 @@ def make_test_data(): ascii="(System)", address="", ) + + # IANA and RFC Editor groups + iana = Group.objects.create( + name="IANA", + acronym="iana", + state_id="active", + type_id="ietf", + parent=None, + ) + rfc_editor = Group.objects.create( + name="RFC Editor", + acronym="rfc-edit", + state_id="active", + type_id="ietf", + parent=None, + ) if system_person.id != 0: # work around bug in Django Person.objects.filter(id=system_person.id).update(id=0) @@ -248,6 +264,42 @@ def make_test_data(): email=email, ) + # IANA user + u = User.objects.create(username="iana") + p = Person.objects.create( + name="Ina Iana", + ascii="Ina Iana", + user=u) + Alias.objects.create( + name=p.name, + person=p) + email = Email.objects.create( + address="iana@ia.na", + person=p) + Role.objects.create( + name_id="auth", + group=iana, + email=email, + person=p, + ) + + # RFC Editor user + u = User.objects.create(username="rfc") + p = Person.objects.create( + name="Rfc Editor", + ascii="Rfc Editor", + user=u) + email = Email.objects.create( + address="rfc@edit.or", + person=p) + Role.objects.create( + name_id="auth", + group=rfc_editor, + email=email, + person=p, + ) + + # draft draft = Document.objects.create( name="draft-ietf-mars-test",